Mechanical Arm Implementation

- The arm blockitem can now be used to select inputs & outputs
- Arms now transfer items between inputs and outputs
- Arm support for Belts, Depots and Funnels
- Some safety checks in net code
- Minor refactor to NBTHelper
This commit is contained in:
simibubi 2020-07-01 22:02:00 +02:00
parent 5411bc3565
commit f820e2be27
44 changed files with 1104 additions and 129 deletions

View file

@ -29,7 +29,7 @@ public class AllBlockPartials {
private static List<AllBlockPartials> all = new ArrayList<>(); private static List<AllBlockPartials> all = new ArrayList<>();
public static final AllBlockPartials public static final AllBlockPartials
SCHEMATICANNON_CONNECTOR = get("schematicannon/connector"), SCHEMATICANNON_CONNECTOR = get("schematicannon/connector"),
SCHEMATICANNON_PIPE = get("schematicannon/pipe"), SCHEMATICANNON_PIPE = get("schematicannon/pipe"),
SHAFTLESS_COGWHEEL = get("cogwheel_shaftless"), SHAFTLESS_COGWHEEL = get("cogwheel_shaftless"),
@ -92,6 +92,11 @@ public class AllBlockPartials {
ARM_CLAW_BASE = get("mechanical_arm/claw_base"), ARM_CLAW_BASE = get("mechanical_arm/claw_base"),
ARM_CLAW_GRIP = get("mechanical_arm/claw_grip"), ARM_CLAW_GRIP = get("mechanical_arm/claw_grip"),
FLAG_SHORT_IN = get("mechanical_arm/flag/short_in"),
FLAG_SHORT_OUT = get("mechanical_arm/flag/short_out"),
FLAG_LONG_IN = get("mechanical_arm/flag/long_in"),
FLAG_LONG_OUT = get("mechanical_arm/flag/long_out"),
MECHANICAL_PUMP_ARROW = get("mechanical_pump/arrow"), MECHANICAL_PUMP_ARROW = get("mechanical_pump/arrow"),
MECHANICAL_PUMP_COG = get("mechanical_pump/cog"), MECHANICAL_PUMP_COG = get("mechanical_pump/cog"),
FLUID_PIPE_CASING = get("fluid_pipe/casing"); FLUID_PIPE_CASING = get("fluid_pipe/casing");

View file

@ -96,6 +96,7 @@ import com.simibubi.create.content.logistics.block.funnel.VerticalFunnelGenerato
import com.simibubi.create.content.logistics.block.inventories.AdjustableCrateBlock; import com.simibubi.create.content.logistics.block.inventories.AdjustableCrateBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateBlock; import com.simibubi.create.content.logistics.block.inventories.CreativeCrateBlock;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmBlock; import com.simibubi.create.content.logistics.block.mechanicalArm.ArmBlock;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmItem;
import com.simibubi.create.content.logistics.block.packager.PackagerBlock; import com.simibubi.create.content.logistics.block.packager.PackagerBlock;
import com.simibubi.create.content.logistics.block.realityFunnel.BeltFunnelBlock; import com.simibubi.create.content.logistics.block.realityFunnel.BeltFunnelBlock;
import com.simibubi.create.content.logistics.block.realityFunnel.BeltFunnelGenerator; import com.simibubi.create.content.logistics.block.realityFunnel.BeltFunnelGenerator;
@ -781,7 +782,7 @@ public class AllBlocks {
.initialProperties(SharedProperties::softMetal) .initialProperties(SharedProperties::softMetal)
.blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p))) .blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p)))
.transform(StressConfigDefaults.setImpact(8.0)) .transform(StressConfigDefaults.setImpact(8.0))
.item() .item(ArmItem::new)
.transform(customItemModel()) .transform(customItemModel())
.register(); .register();

View file

@ -109,7 +109,6 @@ public class ClientEvents {
return; return;
double delta = event.getScrollDelta(); double delta = event.getScrollDelta();
boolean cancelled = CreateClient.schematicHandler.mouseScrolled(delta) boolean cancelled = CreateClient.schematicHandler.mouseScrolled(delta)
|| CreateClient.schematicAndQuillHandler.mouseScrolled(delta) || FilteringHandler.onScroll(delta) || CreateClient.schematicAndQuillHandler.mouseScrolled(delta) || FilteringHandler.onScroll(delta)
|| ScrollValueHandler.onScroll(delta); || ScrollValueHandler.onScroll(delta);

View file

@ -14,6 +14,7 @@ import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler;
import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler; import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.content.curiosities.zapper.blockzapper.BlockzapperRenderHandler; import com.simibubi.create.content.curiosities.zapper.blockzapper.BlockzapperRenderHandler;
import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler; import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPointHandler;
import com.simibubi.create.content.schematics.ClientSchematicLoader; import com.simibubi.create.content.schematics.ClientSchematicLoader;
import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler; import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler;
import com.simibubi.create.content.schematics.client.SchematicHandler; import com.simibubi.create.content.schematics.client.SchematicHandler;
@ -109,6 +110,7 @@ public class CreateClient {
KineticDebugger.tick(); KineticDebugger.tick();
ZapperRenderHandler.tick(); ZapperRenderHandler.tick();
ExtendoGripRenderHandler.tick(); ExtendoGripRenderHandler.tick();
ArmInteractionPointHandler.tick();
outliner.tickOutlines(); outliner.tickOutlines();
} }

View file

@ -76,7 +76,7 @@ public class CuckooClockRenderer extends KineticTileEntityRenderer {
.renderInto(ms, vb); .renderInto(ms, vb);
// Figure // Figure
if (clock.animationType != null) { if (clock.animationType != Animation.NONE) {
offset = -(angle / 135) * 1 / 2f + 10 / 16f; offset = -(angle / 135) * 1 / 2f + 10 / 16f;
SuperByteBuffer figure = SuperByteBuffer figure =
(clock.animationType == Animation.PIG ? AllBlockPartials.CUCKOO_PIG : AllBlockPartials.CUCKOO_CREEPER) (clock.animationType == Animation.PIG ? AllBlockPartials.CUCKOO_PIG : AllBlockPartials.CUCKOO_CREEPER)

View file

@ -32,17 +32,18 @@ public class CuckooClockTileEntity extends KineticTileEntity {
private boolean sendAnimationUpdate; private boolean sendAnimationUpdate;
enum Animation { enum Animation {
PIG, CREEPER, SURPRISE; PIG, CREEPER, SURPRISE, NONE;
} }
public CuckooClockTileEntity(TileEntityType<? extends CuckooClockTileEntity> type) { public CuckooClockTileEntity(TileEntityType<? extends CuckooClockTileEntity> type) {
super(type); super(type);
animationType = Animation.NONE;
} }
@Override @Override
public CompoundNBT writeToClient(CompoundNBT compound) { public CompoundNBT writeToClient(CompoundNBT compound) {
if (sendAnimationUpdate) if (sendAnimationUpdate)
compound.putString("Animation", animationType == null ? "none" : NBTHelper.writeEnum(animationType)); NBTHelper.writeEnum(compound, "Animation", animationType);
sendAnimationUpdate = false; sendAnimationUpdate = false;
return super.writeToClient(compound); return super.writeToClient(compound);
} }
@ -50,11 +51,7 @@ public class CuckooClockTileEntity extends KineticTileEntity {
@Override @Override
public void readClientUpdate(CompoundNBT tag) { public void readClientUpdate(CompoundNBT tag) {
if (tag.contains("Animation")) { if (tag.contains("Animation")) {
String string = tag.getString("Animation"); animationType = NBTHelper.readEnum(tag, "Animation", Animation.class);
if ("none".equals(string))
animationType = null;
else
animationType = NBTHelper.readEnum(string, Animation.class);
animationProgress.lastValue = 0; animationProgress.lastValue = 0;
animationProgress.value = 0; animationProgress.value = 0;
} }
@ -72,7 +69,7 @@ public class CuckooClockTileEntity extends KineticTileEntity {
int minutes = (dayTime % 1000) * 60 / 1000; int minutes = (dayTime % 1000) * 60 / 1000;
if (!world.isRemote) { if (!world.isRemote) {
if (animationType == null) { if (animationType == Animation.NONE) {
if (hours == 12 && minutes < 5) if (hours == 12 && minutes < 5)
startAnimation(Animation.PIG); startAnimation(Animation.PIG);
if (hours == 18 && minutes < 36 && minutes > 31) if (hours == 18 && minutes < 36 && minutes > 31)
@ -81,13 +78,13 @@ public class CuckooClockTileEntity extends KineticTileEntity {
float value = animationProgress.value; float value = animationProgress.value;
animationProgress.set(value + 1); animationProgress.set(value + 1);
if (value > 100) if (value > 100)
animationType = null; animationType = Animation.NONE;
if (animationType == Animation.SURPRISE && animationProgress.value == 50) { if (animationType == Animation.SURPRISE && animationProgress.value == 50) {
Vec3d center = VecHelper.getCenterOf(pos); Vec3d center = VecHelper.getCenterOf(pos);
world.destroyBlock(pos, false); world.destroyBlock(pos, false);
world.createExplosion(null, CUCKOO_SURPRISE, center.x, center.y, center.z, 3, false, world.createExplosion(null, CUCKOO_SURPRISE, center.x, center.y, center.z, 3, false,
Explosion.Mode.BREAK); Explosion.Mode.BREAK);
} }
} }
@ -96,7 +93,7 @@ public class CuckooClockTileEntity extends KineticTileEntity {
if (world.isRemote) { if (world.isRemote) {
moveHands(hours, minutes); moveHands(hours, minutes);
if (animationType == null) { if (animationType == Animation.NONE) {
if (AnimationTickHolder.ticks % 32 == 0) if (AnimationTickHolder.ticks % 32 == 0)
playSound(SoundEvents.BLOCK_NOTE_BLOCK_HAT, 1 / 16f, 2f); playSound(SoundEvents.BLOCK_NOTE_BLOCK_HAT, 1 / 16f, 2f);
else if (AnimationTickHolder.ticks % 16 == 0) else if (AnimationTickHolder.ticks % 16 == 0)

View file

@ -162,7 +162,7 @@ public class DeployerMovementBehaviour extends MovementBehaviour {
} }
private Mode getMode(MovementContext context) { private Mode getMode(MovementContext context) {
return NBTHelper.readEnum(context.tileData.getString("Mode"), Mode.class); return NBTHelper.readEnum(context.tileData, "Mode", Mode.class);
} }
@Override @Override

View file

@ -160,7 +160,7 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
IVertexBuilder builder = buffer.getBuffer(RenderType.getSolid()); IVertexBuilder builder = buffer.getBuffer(RenderType.getSolid());
BlockState blockState = context.state; BlockState blockState = context.state;
BlockPos pos = BlockPos.ZERO; BlockPos pos = BlockPos.ZERO;
Mode mode = NBTHelper.readEnum(context.tileData.getString("Mode"), Mode.class); Mode mode = NBTHelper.readEnum(context.tileData, "Mode", Mode.class);
World world = context.world; World world = context.world;
AllBlockPartials handPose = AllBlockPartials handPose =
mode == Mode.PUNCH ? AllBlockPartials.DEPLOYER_HAND_PUNCHING : AllBlockPartials.DEPLOYER_HAND_POINTING; mode == Mode.PUNCH ? AllBlockPartials.DEPLOYER_HAND_PUNCHING : AllBlockPartials.DEPLOYER_HAND_POINTING;

View file

@ -333,8 +333,8 @@ public class DeployerTileEntity extends KineticTileEntity {
@Override @Override
public void read(CompoundNBT compound) { public void read(CompoundNBT compound) {
state = NBTHelper.readEnum(compound.getString("State"), State.class); state = NBTHelper.readEnum(compound, "State", State.class);
mode = NBTHelper.readEnum(compound.getString("Mode"), Mode.class); mode = NBTHelper.readEnum(compound, "Mode", Mode.class);
timer = compound.getInt("Timer"); timer = compound.getInt("Timer");
deferredInventoryList = compound.getList("Inventory", NBT.TAG_COMPOUND); deferredInventoryList = compound.getList("Inventory", NBT.TAG_COMPOUND);
overflowItems = NBTHelper.readItemList(compound.getList("Overflow", NBT.TAG_COMPOUND)); overflowItems = NBTHelper.readItemList(compound.getList("Overflow", NBT.TAG_COMPOUND));
@ -345,8 +345,8 @@ public class DeployerTileEntity extends KineticTileEntity {
@Override @Override
public CompoundNBT write(CompoundNBT compound) { public CompoundNBT write(CompoundNBT compound) {
compound.putString("Mode", NBTHelper.writeEnum(mode)); NBTHelper.writeEnum(compound, "Mode", mode);
compound.putString("State", NBTHelper.writeEnum(state)); NBTHelper.writeEnum(compound, "State", state);
compound.putInt("Timer", timer); compound.putInt("Timer", timer);
if (player != null) { if (player != null) {
compound.put("HeldItem", player.getHeldItemMainhand().serializeNBT()); compound.put("HeldItem", player.getHeldItemMainhand().serializeNBT());

View file

@ -24,6 +24,8 @@ public class CancelPlayerFallPacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity sender = context.get().getSender(); ServerPlayerEntity sender = context.get().getSender();
if (sender == null)
return;
sender.handleFallDamage(sender.fallDistance, 1.0F); sender.handleFallDamage(sender.fallDistance, 1.0F);
sender.fallDistance = 0; sender.fallDistance = 0;
sender.onGround = true; sender.onGround = true;

View file

@ -98,14 +98,14 @@ public class ClockworkContraption extends Contraption {
CompoundNBT tag = super.writeNBT(); CompoundNBT tag = super.writeNBT();
tag.putInt("facing", facing.getIndex()); tag.putInt("facing", facing.getIndex());
tag.putInt("offset", offset); tag.putInt("offset", offset);
tag.putString("HandType", NBTHelper.writeEnum(handType)); NBTHelper.writeEnum(tag, "HandType", handType);
return tag; return tag;
} }
@Override @Override
public void readNBT(World world, CompoundNBT tag) { public void readNBT(World world, CompoundNBT tag) {
facing = Direction.byIndex(tag.getInt("Facing")); facing = Direction.byIndex(tag.getInt("Facing"));
handType = NBTHelper.readEnum(tag.getString("HandType"), HandType.class); handType = NBTHelper.readEnum(tag, "HandType", HandType.class);
offset = tag.getInt("offset"); offset = tag.getInt("offset");
super.readNBT(world, tag); super.readNBT(world, tag);
} }

View file

@ -80,13 +80,13 @@ public class MountedContraption extends Contraption {
@Override @Override
public CompoundNBT writeNBT() { public CompoundNBT writeNBT() {
CompoundNBT writeNBT = super.writeNBT(); CompoundNBT writeNBT = super.writeNBT();
writeNBT.putString("RotationMode", NBTHelper.writeEnum(rotationMode)); NBTHelper.writeEnum(writeNBT, "RotationMode", rotationMode);
return writeNBT; return writeNBT;
} }
@Override @Override
public void readNBT(World world, CompoundNBT nbt) { public void readNBT(World world, CompoundNBT nbt) {
rotationMode = NBTHelper.readEnum(nbt.getString("RotationMode"), CartMovementMode.class); rotationMode = NBTHelper.readEnum(nbt, "RotationMode", CartMovementMode.class);
super.readNBT(world, nbt); super.readNBT(world, nbt);
} }

View file

@ -88,16 +88,16 @@ public class Instruction {
CompoundNBT serialize() { CompoundNBT serialize() {
CompoundNBT tag = new CompoundNBT(); CompoundNBT tag = new CompoundNBT();
tag.putString("Type", NBTHelper.writeEnum(instruction)); NBTHelper.writeEnum(tag, "Type", instruction);
tag.putString("Modifier", NBTHelper.writeEnum(speedModifier)); NBTHelper.writeEnum(tag, "Modifier", speedModifier);
tag.putInt("Value", value); tag.putInt("Value", value);
return tag; return tag;
} }
static Instruction deserialize(CompoundNBT tag) { static Instruction deserialize(CompoundNBT tag) {
Instruction instruction = Instruction instruction =
new Instruction(NBTHelper.readEnum(tag.getString("Type"), SequencerInstructions.class)); new Instruction(NBTHelper.readEnum(tag, "Type", SequencerInstructions.class));
instruction.speedModifier = NBTHelper.readEnum(tag.getString("Modifier"), InstructionSpeedModifiers.class); instruction.speedModifier = NBTHelper.readEnum(tag, "Modifier", InstructionSpeedModifiers.class);
instruction.value = tag.getInt("Value"); instruction.value = tag.getInt("Value");
return instruction; return instruction;
} }

View file

@ -352,8 +352,8 @@ public class BeltInventory {
} }
public TransportedItemStack getStackAtOffset(int offset) { public TransportedItemStack getStackAtOffset(int offset) {
float min = offset + .5f - (SEGMENT_WINDOW / 2); float min = offset;
float max = offset + .5f + (SEGMENT_WINDOW / 2); float max = offset + 1;
for (TransportedItemStack stack : items) { for (TransportedItemStack stack : items) {
if (stack.beltPosition > max) if (stack.beltPosition > max)
continue; continue;

View file

@ -57,6 +57,8 @@ public class ExtendoGripInteractionPacket extends SimplePacketBase {
.enqueueWork(() -> { .enqueueWork(() -> {
ServerPlayerEntity sender = context.get() ServerPlayerEntity sender = context.get()
.getSender(); .getSender();
if (sender == null)
return;
Entity entityByID = sender.getServerWorld() Entity entityByID = sender.getServerWorld()
.getEntityByID(target); .getEntityByID(target);
if (entityByID != null && ExtendoGripItem.isHoldingExtendoGrip(sender)) { if (entityByID != null && ExtendoGripItem.isHoldingExtendoGrip(sender)) {

View file

@ -351,17 +351,16 @@ public class BlockzapperItem extends ZapperItem {
.contains(component.name())) .contains(component.name()))
stack.getOrCreateTag() stack.getOrCreateTag()
.putString(component.name(), ComponentTier.None.name()); .putString(component.name(), ComponentTier.None.name());
return NBTHelper.readEnum(stack.getTag() return NBTHelper.readEnum(stack.getTag(), component.name(), ComponentTier.class);
.getString(component.name()), ComponentTier.class);
} }
public static void setTier(Components component, ComponentTier tier, ItemStack stack) { public static void setTier(Components component, ComponentTier tier, ItemStack stack) {
stack.getOrCreateTag() NBTHelper.writeEnum(stack.getOrCreateTag(), component.name(), tier);
.putString(component.name(), NBTHelper.writeEnum(tier));
} }
public static enum ComponentTier { public static enum ComponentTier {
None(TextFormatting.DARK_GRAY), Brass(TextFormatting.GOLD), Chromatic(TextFormatting.LIGHT_PURPLE); None(TextFormatting.DARK_GRAY), Brass(TextFormatting.GOLD), Chromatic(TextFormatting.LIGHT_PURPLE);
public TextFormatting color; public TextFormatting color;
private ComponentTier(TextFormatting color) { private ComponentTier(TextFormatting color) {

View file

@ -53,7 +53,7 @@ public class WorldshaperItem extends ZapperItem {
@Override @Override
protected boolean canActivateWithoutSelectedBlock(ItemStack stack) { protected boolean canActivateWithoutSelectedBlock(ItemStack stack) {
CompoundNBT tag = stack.getOrCreateTag(); CompoundNBT tag = stack.getOrCreateTag();
TerrainTools tool = NBTHelper.readEnum(tag.getString("Tool"), TerrainTools.class); TerrainTools tool = NBTHelper.readEnum(tag, "Tool", TerrainTools.class);
return !tool.requiresSelectedBlock(); return !tool.requiresSelectedBlock();
} }
@ -65,11 +65,11 @@ public class WorldshaperItem extends ZapperItem {
List<BlockPos> affectedPositions = new ArrayList<>(); List<BlockPos> affectedPositions = new ArrayList<>();
CompoundNBT tag = stack.getOrCreateTag(); CompoundNBT tag = stack.getOrCreateTag();
Brush brush = NBTHelper.readEnum(tag.getString("Brush"), TerrainBrushes.class) Brush brush = NBTHelper.readEnum(tag, "Brush", TerrainBrushes.class)
.get(); .get();
BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams")); BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams"));
PlacementOptions option = NBTHelper.readEnum(tag.getString("Placement"), PlacementOptions.class); PlacementOptions option = NBTHelper.readEnum(tag, "Placement", PlacementOptions.class);
TerrainTools tool = NBTHelper.readEnum(tag.getString("Tool"), TerrainTools.class); TerrainTools tool = NBTHelper.readEnum(tag, "Tool", TerrainTools.class);
brush.set(params.getX(), params.getY(), params.getZ()); brush.set(params.getX(), params.getY(), params.getZ());
targetPos = targetPos.add(brush.getOffset(player.getLookVec(), raytrace.getFace(), option)); targetPos = targetPos.add(brush.getOffset(player.getLookVec(), raytrace.getFace(), option));

View file

@ -69,9 +69,9 @@ public class WorldshaperRenderHandler {
return; return;
} }
Brush brush = NBTHelper.readEnum(tag.getString("Brush"), TerrainBrushes.class) Brush brush = NBTHelper.readEnum(tag, "Brush", TerrainBrushes.class)
.get(); .get();
PlacementOptions placement = NBTHelper.readEnum(tag.getString("Placement"), PlacementOptions.class); PlacementOptions placement = NBTHelper.readEnum(tag, "Placement", PlacementOptions.class);
BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams")); BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams"));
brush.set(params.getX(), params.getY(), params.getZ()); brush.set(params.getX(), params.getY(), params.getZ());
renderedShape = brush.getIncludedPositions(); renderedShape = brush.getIncludedPositions();

View file

@ -53,9 +53,12 @@ public class WorldshaperScreen extends ZapperScreen {
brushLabel = new Label(i + 58, j + 28, "").withShadow(); brushLabel = new Label(i + 58, j + 28, "").withShadow();
brushInput = new SelectionScrollInput(i + 55, j + 25, 78, 14).forOptions(brushOptions) brushInput = new SelectionScrollInput(i + 55, j + 25, 78, 14).forOptions(brushOptions)
.titled(Lang.translate("gui.terrainzapper.brush")).writingTo(brushLabel).calling(this::brushChanged); .titled(Lang.translate("gui.terrainzapper.brush"))
.writingTo(brushLabel)
.calling(this::brushChanged);
if (nbt.contains("Brush")) if (nbt.contains("Brush"))
brushInput.setState(NBTHelper.readEnum(nbt.getString("Brush"), TerrainBrushes.class).ordinal()); brushInput.setState(NBTHelper.readEnum(nbt, "Brush", TerrainBrushes.class)
.ordinal());
widgets.add(brushLabel); widgets.add(brushLabel);
widgets.add(brushInput); widgets.add(brushInput);
@ -66,11 +69,13 @@ public class WorldshaperScreen extends ZapperScreen {
for (int id = 0; id < toolValues.length; id++) { for (int id = 0; id < toolValues.length; id++) {
TerrainTools tool = toolValues[id]; TerrainTools tool = toolValues[id];
toolButtons.add(new IconButton(i + 8 + id * 18, j + 76, tool.icon)); toolButtons.add(new IconButton(i + 8 + id * 18, j + 76, tool.icon));
toolButtons.get(id).setToolTip(Lang.translate("gui.terrainzapper.tool." + tool.translationKey)); toolButtons.get(id)
.setToolTip(Lang.translate("gui.terrainzapper.tool." + tool.translationKey));
} }
if (nbt.contains("Tool")) if (nbt.contains("Tool"))
toolButtons.get(NBTHelper.readEnum(nbt.getString("Tool"), TerrainTools.class).ordinal()).active = false; toolButtons.get(NBTHelper.readEnum(nbt, "Tool", TerrainTools.class)
.ordinal()).active = false;
widgets.addAll(toolButtons); widgets.addAll(toolButtons);
placementButtons = new Vector<>(3); placementButtons = new Vector<>(3);
@ -78,21 +83,25 @@ public class WorldshaperScreen extends ZapperScreen {
for (int id = 0; id < placementValues.length; id++) { for (int id = 0; id < placementValues.length; id++) {
PlacementOptions option = placementValues[id]; PlacementOptions option = placementValues[id];
placementButtons.add(new IconButton(i + 147 + id * 18, j + 76, option.icon)); placementButtons.add(new IconButton(i + 147 + id * 18, j + 76, option.icon));
placementButtons.get(id).setToolTip(Lang.translate("gui.terrainzapper.placement." + option.translationKey)); placementButtons.get(id)
.setToolTip(Lang.translate("gui.terrainzapper.placement." + option.translationKey));
} }
if (nbt.contains("Placement")) if (nbt.contains("Placement"))
placementButtons placementButtons.get(NBTHelper.readEnum(nbt, "Placement", PlacementOptions.class)
.get(NBTHelper.readEnum(nbt.getString("Placement"), PlacementOptions.class).ordinal()).active = .ordinal()).active = false;
false;
widgets.addAll(placementButtons); widgets.addAll(placementButtons);
} }
public void initBrushParams() { public void initBrushParams() {
if (brushParams != null) { if (brushParams != null) {
nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0).getState(), nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0)
brushParams.get(1).getState(), brushParams.get(2).getState()))); .getState(),
brushParams.get(1)
.getState(),
brushParams.get(2)
.getState())));
widgets.removeAll(brushParamLabels); widgets.removeAll(brushParamLabels);
widgets.removeAll(brushParams); widgets.removeAll(brushParams);
@ -109,10 +118,12 @@ public class WorldshaperScreen extends ZapperScreen {
brushParamLabels.add(label); brushParamLabels.add(label);
int indexFinal = index; int indexFinal = index;
ScrollInput input = new ScrollInput(i + 55 + 18 * index, j + 43, 14, 14) ScrollInput input = new ScrollInput(i + 55 + 18 * index, j + 43, 14, 14)
.withRange(currentBrush.getMin(index), currentBrush.getMax(index) + 1).writingTo(label) .withRange(currentBrush.getMin(index), currentBrush.getMax(index) + 1)
.titled(currentBrush.getParamLabel(index)).calling(state -> { .writingTo(label)
label.x = i + 62 + 18 * indexFinal - font.getStringWidth(label.text) / 2; .titled(currentBrush.getParamLabel(index))
}); .calling(state -> {
label.x = i + 62 + 18 * indexFinal - font.getStringWidth(label.text) / 2;
});
input.setState(params[index]); input.setState(params[index]);
input.onChanged(); input.onChanged();
if (index >= currentBrush.amtParams) { if (index >= currentBrush.amtParams) {
@ -140,7 +151,8 @@ public class WorldshaperScreen extends ZapperScreen {
if (placementButton.isHovered()) { if (placementButton.isHovered()) {
placementButtons.forEach(b -> b.active = true); placementButtons.forEach(b -> b.active = true);
placementButton.active = false; placementButton.active = false;
placementButton.playDownSound(Minecraft.getInstance().getSoundHandler()); placementButton.playDownSound(Minecraft.getInstance()
.getSoundHandler());
nbt.putString("Placement", PlacementOptions.values()[placementButtons.indexOf(placementButton)].name()); nbt.putString("Placement", PlacementOptions.values()[placementButtons.indexOf(placementButton)].name());
} }
} }
@ -149,7 +161,8 @@ public class WorldshaperScreen extends ZapperScreen {
if (toolButton.isHovered()) { if (toolButton.isHovered()) {
toolButtons.forEach(b -> b.active = true); toolButtons.forEach(b -> b.active = true);
toolButton.active = false; toolButton.active = false;
toolButton.playDownSound(Minecraft.getInstance().getSoundHandler()); toolButton.playDownSound(Minecraft.getInstance()
.getSoundHandler());
nbt.putString("Tool", TerrainTools.values()[toolButtons.indexOf(toolButton)].name()); nbt.putString("Tool", TerrainTools.values()[toolButtons.indexOf(toolButton)].name());
} }
} }
@ -173,9 +186,13 @@ public class WorldshaperScreen extends ZapperScreen {
@Override @Override
protected void writeAdditionalOptions(CompoundNBT nbt) { protected void writeAdditionalOptions(CompoundNBT nbt) {
super.writeAdditionalOptions(nbt); super.writeAdditionalOptions(nbt);
nbt.putString("Brush", NBTHelper.writeEnum(TerrainBrushes.values()[brushInput.getState()])); NBTHelper.writeEnum(nbt, "Brush", TerrainBrushes.values()[brushInput.getState()]);
nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0).getState(), nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0)
brushParams.get(1).getState(), brushParams.get(2).getState()))); .getState(),
brushParams.get(1)
.getState(),
brushParams.get(2)
.getState())));
} }
} }

View file

@ -39,12 +39,12 @@ public class DepotRenderer extends SafeTileEntityRenderer<DepotTileEntity> {
msr.nudge(0); msr.nudge(0);
float offset = MathHelper.lerp(partialTicks, transported.prevBeltPosition, transported.beltPosition); float offset = MathHelper.lerp(partialTicks, transported.prevBeltPosition, transported.beltPosition);
float sideOffset = MathHelper.lerp(partialTicks, transported.prevSideOffset, transported.sideOffset); float sideOffset = MathHelper.lerp(partialTicks, transported.prevSideOffset, transported.sideOffset);
Vec3d offsetVec = new Vec3d(transported.insertedFrom.getOpposite()
.getDirectionVec()).scale(.5f - offset);
ms.translate(offsetVec.x, offsetVec.y, offsetVec.z);
if (transported.insertedFrom.getAxis() if (transported.insertedFrom.getAxis()
.isHorizontal()) { .isHorizontal()) {
Vec3d offsetVec = new Vec3d(transported.insertedFrom.getOpposite()
.getDirectionVec()).scale(.5f - offset);
ms.translate(offsetVec.x, offsetVec.y, offsetVec.z);
boolean alongX = transported.insertedFrom.rotateY() boolean alongX = transported.insertedFrom.rotateY()
.getAxis() == Axis.X; .getAxis() == Axis.X;
if (!alongX) if (!alongX)

View file

@ -84,6 +84,13 @@ public class DepotTileEntity extends SmartTileEntity {
if (heldItem.locked != wasLocked || !previousItem.equals(heldItem.stack, false)) if (heldItem.locked != wasLocked || !previousItem.equals(heldItem.stack, false))
sendData(); sendData();
} }
@Override
public void remove() {
super.remove();
if (lazyItemHandler != null)
lazyItemHandler.invalidate();
}
@Override @Override
public CompoundNBT write(CompoundNBT compound) { public CompoundNBT write(CompoundNBT compound) {

View file

@ -0,0 +1,77 @@
package com.simibubi.create.content.logistics.block.mechanicalArm;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class ArmAngleTarget {
static ArmAngleTarget NO_TARGET = new ArmAngleTarget();
float baseAngle;
float lowerArmAngle;
float upperArmAngle;
float headAngle;
private ArmAngleTarget() {
lowerArmAngle = 155;
upperArmAngle = 60;
headAngle = -15;
}
public ArmAngleTarget(BlockPos armPos, Vec3d pointTarget, Direction clawFacing) {
Vec3d target = pointTarget;
Vec3d origin = VecHelper.getCenterOf(armPos)
.add(0, 4 / 16f, 0);
Vec3d clawTarget = target;
target = target.add(new Vec3d(clawFacing.getOpposite()
.getDirectionVec()).scale(.5f));
Vec3d diff = target.subtract(origin);
float horizontalDistance = (float) diff.mul(1, 0, 1)
.length();
float baseAngle = AngleHelper.deg(MathHelper.atan2(diff.x, diff.z)) + 180;
float alphaOffset = AngleHelper.deg(MathHelper.atan2(diff.y, horizontalDistance));
float a = 18 / 16f; // lower arm length
float a2 = a * a;
float b = 17 / 16f; // upper arm length
float b2 = b * b;
float diffLength =
MathHelper.clamp(MathHelper.sqrt(diff.y * diff.y + horizontalDistance * horizontalDistance), 1 / 8f, a + b);
float diffLength2 = diffLength * diffLength;
float alphaRatio = (-b2 + a2 + diffLength2) / (2 * a * diffLength);
float alpha = AngleHelper.deg(Math.acos(alphaRatio)) + alphaOffset;
float betaRatio = (-diffLength2 + a2 + b2) / (2 * b * a);
float beta = AngleHelper.deg(Math.acos(betaRatio));
if (Float.isNaN(alpha))
alpha = 0;
if (Float.isNaN(beta))
beta = 0;
Vec3d headPos = new Vec3d(0, 0, 0);
headPos = VecHelper.rotate(headPos.add(0, b, 0), beta + 180, Axis.X);
headPos = VecHelper.rotate(headPos.add(0, a, 0), alpha - 90, Axis.X);
headPos = VecHelper.rotate(headPos, baseAngle, Axis.Y);
headPos = headPos.add(origin);
Vec3d headDiff = clawTarget.subtract(headPos);
float horizontalHeadDistance = (float) headDiff.mul(1, 0, 1)
.length();
float headAngle =
(float) (alpha + beta + 135 - AngleHelper.deg(MathHelper.atan2(headDiff.y, horizontalHeadDistance)));
this.lowerArmAngle = alpha;
this.upperArmAngle = beta;
this.headAngle = -headAngle;
this.baseAngle = baseAngle;
}
}

View file

@ -6,18 +6,13 @@ import com.simibubi.create.content.contraptions.base.KineticBlock;
import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.ITE;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader; import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
public class ArmBlock extends KineticBlock implements ITE<ArmTileEntity> { public class ArmBlock extends KineticBlock implements ITE<ArmTileEntity> {
@ -30,14 +25,6 @@ public class ArmBlock extends KineticBlock implements ITE<ArmTileEntity> {
return true; return true;
} }
@Override
public ActionResultType onUse(BlockState p_225533_1_, World p_225533_2_, BlockPos p_225533_3_,
PlayerEntity p_225533_4_, Hand p_225533_5_, BlockRayTraceResult p_225533_6_) {
withTileEntityDo(p_225533_2_, p_225533_3_, ArmTileEntity::toggleRave);
return ActionResultType.SUCCESS;
}
@Override @Override
public VoxelShape getShape(BlockState p_220053_1_, IBlockReader p_220053_2_, BlockPos p_220053_3_, public VoxelShape getShape(BlockState p_220053_1_, IBlockReader p_220053_2_, BlockPos p_220053_3_,
ISelectionContext p_220053_4_) { ISelectionContext p_220053_4_) {

View file

@ -0,0 +1,234 @@
package com.simibubi.create.content.logistics.block.mechanicalArm;
import javax.annotation.Nullable;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.logistics.block.realityFunnel.RealityFunnelBlock;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InsertingBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
public abstract class ArmInteractionPoint {
static enum Mode {
DEPOSIT, TAKE
}
BlockPos pos;
BlockState state;
Mode mode;
private LazyOptional<IItemHandler> cachedHandler;
private ArmAngleTarget cachedAngles;
public ArmInteractionPoint() {
cachedHandler = LazyOptional.empty();
}
@OnlyIn(Dist.CLIENT)
void transformFlag(MatrixStack stack) {}
AllBlockPartials getFlagType() {
return mode == Mode.TAKE ? AllBlockPartials.FLAG_LONG_OUT : AllBlockPartials.FLAG_LONG_IN;
}
void cycleMode() {
mode = mode == Mode.DEPOSIT ? Mode.TAKE : Mode.DEPOSIT;
}
Vec3d getInteractionPositionVector() {
return VecHelper.getCenterOf(pos);
}
Direction getInteractionDirection() {
return Direction.DOWN;
}
abstract boolean isValid(BlockState state);
static boolean isInteractable(BlockState state) {
return AllBlocks.DEPOT.has(state) || AllBlocks.BELT.has(state) || AllBlocks.CHUTE.has(state)
|| AllBlocks.REALITY_FUNNEL.has(state);
}
ArmAngleTarget getTargetAngles(BlockPos armPos) {
if (cachedAngles == null)
cachedAngles = new ArmAngleTarget(armPos, getInteractionPositionVector(), getInteractionDirection());
return cachedAngles;
}
@Nullable
IItemHandler getHandler(World world) {
if (!cachedHandler.isPresent()) {
TileEntity te = world.getTileEntity(pos);
if (te == null)
return null;
cachedHandler = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.UP);
}
return cachedHandler.orElse(null);
}
ItemStack insert(World world, ItemStack stack, boolean simulate) {
IItemHandler handler = getHandler(world);
if (handler == null)
return stack;
return ItemHandlerHelper.insertItem(handler, stack, simulate);
}
ItemStack extract(World world, int slot, int amount, boolean simulate) {
IItemHandler handler = getHandler(world);
if (handler == null)
return ItemStack.EMPTY;
return handler.extractItem(slot, amount, simulate);
}
ItemStack extract(World world, int slot, boolean simulate) {
return extract(world, slot, 64, simulate);
}
int getSlotCount(World world) {
IItemHandler handler = getHandler(world);
if (handler == null)
return 0;
return handler.getSlots();
}
@Nullable
static ArmInteractionPoint createAt(IBlockReader world, BlockPos pos) {
BlockState state = world.getBlockState(pos);
ArmInteractionPoint point = null;
if (AllBlocks.DEPOT.has(state))
point = new Depot();
if (AllBlocks.BELT.has(state))
point = new Belt();
if (AllBlocks.CHUTE.has(state))
point = new Chute();
if (AllBlocks.REALITY_FUNNEL.has(state))
point = new Funnel();
if (point != null) {
point.state = state;
point.pos = pos;
point.mode = Mode.DEPOSIT;
}
return point;
}
CompoundNBT serialize() {
CompoundNBT nbt = new CompoundNBT();
nbt.put("Pos", NBTUtil.writeBlockPos(pos));
NBTHelper.writeEnum(nbt, "Mode", mode);
return nbt;
}
static ArmInteractionPoint deserialize(IBlockReader world, CompoundNBT nbt) {
BlockPos pos = NBTUtil.readBlockPos(nbt.getCompound("Pos"));
ArmInteractionPoint interactionPoint = createAt(world, pos);
interactionPoint.mode = NBTHelper.readEnum(nbt, "Mode", Mode.class);
return interactionPoint;
}
static class Depot extends ArmInteractionPoint {
@Override
Vec3d getInteractionPositionVector() {
return new Vec3d(pos).add(.5f, 14 / 16f, .5f);
}
@Override
boolean isValid(BlockState state) {
return AllBlocks.DEPOT.has(state);
}
}
static class Belt extends Depot {
@Override
boolean isValid(BlockState state) {
return AllBlocks.BELT.has(state);
}
}
static class Chute extends ArmInteractionPoint {
@Override
Vec3d getInteractionPositionVector() {
return new Vec3d(pos).add(.5f, 1, .5f);
}
@Override
boolean isValid(BlockState state) {
return AllBlocks.CHUTE.has(state);
}
}
static class Funnel extends ArmInteractionPoint {
@Override
Vec3d getInteractionPositionVector() {
return VecHelper.getCenterOf(pos)
.add(new Vec3d(RealityFunnelBlock.getFunnelFacing(state)
.getDirectionVec()).scale(.5f));
}
@Override
int getSlotCount(World world) {
return 0;
}
@Override
ItemStack extract(World world, int slot, int amount, boolean simulate) {
return ItemStack.EMPTY;
}
@Override
Direction getInteractionDirection() {
return RealityFunnelBlock.getFunnelFacing(state).getOpposite();
}
@Override
ItemStack insert(World world, ItemStack stack, boolean simulate) {
FilteringBehaviour filtering = TileEntityBehaviour.get(world, pos, FilteringBehaviour.TYPE);
InsertingBehaviour inserter = TileEntityBehaviour.get(world, pos, InsertingBehaviour.TYPE);
if (inserter == null)
return stack;
if (filtering != null && !filtering.test(stack))
return stack;
return inserter.insert(stack, simulate);
}
@Override
boolean isValid(BlockState state) {
return AllBlocks.REALITY_FUNNEL.has(state);
}
@Override
void cycleMode() {}
}
}

View file

@ -0,0 +1,115 @@
package com.simibubi.create.content.logistics.block.mechanicalArm;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPoint.Mode;
import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber(value = Dist.CLIENT)
public class ArmInteractionPointHandler {
static Map<BlockPos, ArmInteractionPoint> currentSelection = new HashMap<>();
static ItemStack currentItem;
@SubscribeEvent
public static void rightClickingBlocksSelectsThem(PlayerInteractEvent.RightClickBlock event) {
if (currentItem == null)
return;
BlockPos pos = event.getPos();
World world = event.getWorld();
if (!world.isRemote)
return;
if (!currentSelection.containsKey(pos)) {
ArmInteractionPoint point = ArmInteractionPoint.createAt(world, pos);
if (point == null)
return;
currentSelection.put(pos, point);
}
currentSelection.get(pos)
.cycleMode();
event.setCanceled(true);
event.setCancellationResult(ActionResultType.SUCCESS);
}
@SubscribeEvent
public static void leftClickingBlocksDeselectsThem(PlayerInteractEvent.LeftClickBlock event) {
if (currentItem == null)
return;
if (!event.getWorld().isRemote)
return;
BlockPos pos = event.getPos();
if (currentSelection.remove(pos) != null) {
event.setCanceled(true);
event.setCancellationResult(ActionResultType.SUCCESS);
}
}
public static void flushSettings(BlockPos pos) {
if (currentItem == null)
return;
AllPackets.channel.sendToServer(new ArmPlacementPacket(currentSelection.values(), pos));
currentSelection.clear();
currentItem = null;
}
public static void tick() {
PlayerEntity player = Minecraft.getInstance().player;
World world = Minecraft.getInstance().world;
if (player == null)
return;
ItemStack heldItemMainhand = player.getHeldItemMainhand();
if (!AllBlocks.MECHANICAL_ARM.isIn(heldItemMainhand)) {
currentItem = null;
return;
}
if (heldItemMainhand != currentItem) {
currentSelection.clear();
currentItem = heldItemMainhand;
}
for (Iterator<Entry<BlockPos, ArmInteractionPoint>> iterator = currentSelection.entrySet()
.iterator(); iterator.hasNext();) {
Entry<BlockPos, ArmInteractionPoint> entry = iterator.next();
BlockPos pos = entry.getKey();
BlockState state = world.getBlockState(pos);
ArmInteractionPoint point = entry.getValue();
if (!point.isValid(state)) {
iterator.remove();
continue;
}
VoxelShape shape = state.getShape(world, pos);
if (shape.isEmpty())
continue;
int color = point.mode == Mode.DEPOSIT ? 0xffcb74 : 0x4f8a8b;
CreateClient.outliner.showAABB(point, shape.getBoundingBox()
.offset(pos))
.colored(color)
.lineWidth(1 / 16f);
}
}
}

View file

@ -0,0 +1,45 @@
package com.simibubi.create.content.logistics.block.mechanicalArm;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class ArmItem extends BlockItem {
public ArmItem(Block p_i48527_1_, Properties p_i48527_2_) {
super(p_i48527_1_, p_i48527_2_);
}
@Override
public ActionResultType onItemUse(ItemUseContext ctx) {
if (ArmInteractionPoint.isInteractable(ctx.getWorld()
.getBlockState(ctx.getPos())))
return ActionResultType.SUCCESS;
return super.onItemUse(ctx);
}
@Override
protected boolean onBlockPlaced(BlockPos pos, World world, PlayerEntity p_195943_3_, ItemStack p_195943_4_,
BlockState p_195943_5_) {
if (world.isRemote)
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> ArmInteractionPointHandler.flushSettings(pos));
return super.onBlockPlaced(pos, world, p_195943_3_, p_195943_4_, p_195943_5_);
}
@Override
public boolean canPlayerBreakBlockWhileHolding(BlockState state, World p_195938_2_, BlockPos p_195938_3_,
PlayerEntity p_195938_4_) {
return !ArmInteractionPoint.isInteractable(state);
}
}

View file

@ -0,0 +1,70 @@
package com.simibubi.create.content.logistics.block.mechanicalArm;
import java.util.Collection;
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class ArmPlacementPacket extends SimplePacketBase {
private Collection<ArmInteractionPoint> points;
private ListNBT receivedTag;
private BlockPos pos;
public ArmPlacementPacket(Collection<ArmInteractionPoint> points, BlockPos pos) {
this.points = points;
this.pos = pos;
}
public ArmPlacementPacket(PacketBuffer buffer) {
CompoundNBT nbt = buffer.readCompoundTag();
receivedTag = nbt.getList("Points", NBT.TAG_COMPOUND);
pos = buffer.readBlockPos();
}
@Override
public void write(PacketBuffer buffer) {
CompoundNBT nbt = new CompoundNBT();
ListNBT pointsNBT = new ListNBT();
points.stream()
.map(ArmInteractionPoint::serialize)
.forEach(pointsNBT::add);
nbt.put("Points", pointsNBT);
buffer.writeCompoundTag(nbt);
buffer.writeBlockPos(pos);
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
ServerPlayerEntity player = context.get()
.getSender();
if (player == null)
return;
World world = player.world;
if (world == null)
return;
TileEntity tileEntity = world.getTileEntity(pos);
if (tileEntity == null || !(tileEntity instanceof ArmTileEntity))
return;
ArmTileEntity arm = (ArmTileEntity) tileEntity;
arm.interactionPointTag = receivedTag;
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -5,17 +5,19 @@ import com.mojang.blaze3d.vertex.IVertexBuilder;
import com.simibubi.create.AllBlockPartials; import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.MatrixStacker; import com.simibubi.create.foundation.utility.MatrixStacker;
import com.simibubi.create.foundation.utility.SuperByteBuffer; import com.simibubi.create.foundation.utility.SuperByteBuffer;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.util.math.MathHelper; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
public class ArmRenderer extends KineticTileEntityRenderer { public class ArmRenderer extends KineticTileEntityRenderer {
@ -24,25 +26,15 @@ public class ArmRenderer extends KineticTileEntityRenderer {
} }
@Override @Override
protected void renderSafe(KineticTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, protected void renderSafe(KineticTileEntity te, float pt, MatrixStack ms, IRenderTypeBuffer buffer, int light,
int light, int overlay) { int overlay) {
super.renderSafe(te, partialTicks, ms, buffer, light, overlay); super.renderSafe(te, pt, ms, buffer, light, overlay);
ArmTileEntity arm = (ArmTileEntity) te;
IVertexBuilder builder = buffer.getBuffer(RenderType.getSolid()); IVertexBuilder builder = buffer.getBuffer(RenderType.getSolid());
BlockState blockState = te.getBlockState(); BlockState blockState = te.getBlockState();
MatrixStacker msr = MatrixStacker.of(ms); MatrixStacker msr = MatrixStacker.of(ms);
float angle = 0;
float clawAngle = -25;
float otherAngle = 0;
int color = 0xFFFFFF; int color = 0xFFFFFF;
boolean rave = te instanceof ArmTileEntity && ((ArmTileEntity) te).debugRave;
if (rave) {
clawAngle = angle = MathHelper.lerp((MathHelper.sin(AnimationTickHolder.getRenderTick() / 2) + 1) / 2, -45, 15);
otherAngle = MathHelper.lerp((MathHelper.sin(AnimationTickHolder.getRenderTick() / 4) + 1) / 2, -95, 95);
color = ColorHelper.rainbowColor(AnimationTickHolder.ticks * 100);
}
ms.push(); ms.push();
SuperByteBuffer base = AllBlockPartials.ARM_BASE.renderOn(blockState); SuperByteBuffer base = AllBlockPartials.ARM_BASE.renderOn(blockState);
@ -55,33 +47,51 @@ public class ArmRenderer extends KineticTileEntityRenderer {
msr.centre(); msr.centre();
ms.translate(0, 4 / 16d, 0); ms.translate(0, 4 / 16d, 0);
msr.rotateY(rave ? AnimationTickHolder.getRenderTick() * 10 : 0); msr.rotateY(arm.baseAngle.get(pt));
base.renderInto(ms, builder); base.renderInto(ms, builder);
ms.translate(0, 1 / 16d, -2 / 16d); ms.translate(0, 1 / 16d, -2 / 16d);
msr.rotateX(angle); msr.rotateX(arm.lowerArmAngle.get(pt) - 135);
ms.translate(0, -1 / 16d, 0); ms.translate(0, -1 / 16d, 0);
lowerBody.color(color).renderInto(ms, builder); lowerBody.color(color)
.renderInto(ms, builder);
ms.translate(0, 12 / 16d, 12 / 16d); ms.translate(0, 12 / 16d, 12 / 16d);
msr.rotateX(-otherAngle / 2f); msr.rotateX(arm.upperArmAngle.get(pt) - 90);
upperBody.color(color).renderInto(ms, builder); upperBody.color(color)
.renderInto(ms, builder);
ms.translate(0, 11 / 16d, -11 / 16d); ms.translate(0, 11 / 16d, -11 / 16d);
msr.rotateX(-angle); msr.rotateX(arm.headAngle.get(pt));
head.renderInto(ms, builder); head.renderInto(ms, builder);
ms.translate(0, 0, -4 / 16d); ms.translate(0, 0, -4 / 16d);
claw.renderInto(ms, builder); claw.renderInto(ms, builder);
ItemStack item = arm.heldItem;
ItemRenderer itemRenderer = Minecraft.getInstance()
.getItemRenderer();
boolean hasItem = !item.isEmpty();
boolean isBlockItem = hasItem && (item.getItem() instanceof BlockItem)
&& itemRenderer.getItemModelWithOverrides(item, Minecraft.getInstance().world, null)
.isGui3d();
for (int flip : Iterate.positiveAndNegative) { for (int flip : Iterate.positiveAndNegative) {
ms.push(); ms.push();
ms.translate(0, flip * 3 / 16d, -1 / 16d); ms.translate(0, flip * 3 / 16d, -1 / 16d);
msr.rotateX(flip * clawAngle); msr.rotateX(flip * (hasItem ? isBlockItem ? 0 : -35 : 0));
clawGrip.renderInto(ms, builder); clawGrip.renderInto(ms, builder);
ms.pop(); ms.pop();
} }
if (hasItem) {
float itemScale = isBlockItem ? .5f : .625f;
msr.rotateX(90);
ms.translate(0, -4 / 16f, 0);
ms.scale(itemScale, itemScale, itemScale);
itemRenderer
.renderItem(item, TransformType.FIXED, light, overlay, ms, buffer);
}
ms.pop(); ms.pop();
} }

View file

@ -1,45 +1,297 @@
package com.simibubi.create.content.logistics.block.mechanicalArm; package com.simibubi.create.content.logistics.block.mechanicalArm;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPoint.Mode;
import com.simibubi.create.foundation.gui.widgets.InterpolatedAngle;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.common.util.Constants.NBT;
public class ArmTileEntity extends KineticTileEntity { public class ArmTileEntity extends KineticTileEntity {
boolean debugRave; // Server
List<ArmInteractionPoint> inputs;
public ArmTileEntity(TileEntityType<?> typeIn) { List<ArmInteractionPoint> outputs;
super(typeIn); ListNBT interactionPointTag;
}
// Both
@Override float chasedPointProgress;
public void lazyTick() { int chasedPointIndex;
if (hasWorld()) ItemStack heldItem;
if (world.rand.nextInt(100) == 0) Phase phase;
toggleRave();
super.lazyTick(); // Client
ArmAngleTarget previousTarget;
InterpolatedAngle lowerArmAngle;
InterpolatedAngle upperArmAngle;
InterpolatedAngle baseAngle;
InterpolatedAngle headAngle;
InterpolatedAngle clawAngle;
float previousBaseAngle;
boolean updateInteractionPoints;
enum Phase {
SEARCH_INPUTS, MOVE_TO_INPUT, SEARCH_OUTPUTS, MOVE_TO_OUTPUT, IDLE
} }
public void toggleRave() { public ArmTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
inputs = new ArrayList<>();
outputs = new ArrayList<>();
heldItem = ItemStack.EMPTY;
phase = Phase.SEARCH_INPUTS;
baseAngle = new InterpolatedAngle();
lowerArmAngle = new InterpolatedAngle();
upperArmAngle = new InterpolatedAngle();
headAngle = new InterpolatedAngle();
clawAngle = new InterpolatedAngle();
previousTarget = ArmAngleTarget.NO_TARGET;
previousBaseAngle = previousTarget.baseAngle;
updateInteractionPoints = true;
}
@Override
public void tick() {
super.tick();
initInteractionPoints();
tickMovementProgress();
if (world.isRemote) if (world.isRemote)
return; return;
debugRave = !debugRave; if (chasedPointProgress < 1)
return;
if (phase == Phase.MOVE_TO_INPUT)
collectItem();
if (phase == Phase.MOVE_TO_OUTPUT)
depositItem();
}
@Override
public void lazyTick() {
super.lazyTick();
if (world.isRemote)
return;
if (chasedPointProgress < .5f)
return;
if (phase == Phase.SEARCH_INPUTS)
searchForItem();
if (phase == Phase.SEARCH_OUTPUTS)
searchForDestination();
}
private void tickMovementProgress() {
chasedPointProgress += Math.min(256, Math.abs(getSpeed())) / 1024f;
if (chasedPointProgress > 1)
chasedPointProgress = 1;
if (!world.isRemote)
return;
ArmInteractionPoint targetedInteractionPoint = getTargetedInteractionPoint();
ArmAngleTarget previousTarget = this.previousTarget;
ArmAngleTarget target =
targetedInteractionPoint == null ? ArmAngleTarget.NO_TARGET : targetedInteractionPoint.getTargetAngles(pos);
baseAngle.set(AngleHelper.angleLerp(chasedPointProgress, previousBaseAngle,
target == ArmAngleTarget.NO_TARGET ? previousBaseAngle : target.baseAngle));
// Arm's angles first backup to resting position and then continue
if (chasedPointProgress < .5f)
target = ArmAngleTarget.NO_TARGET;
else
previousTarget = ArmAngleTarget.NO_TARGET;
float progress = chasedPointProgress == 1 ? 1 : (chasedPointProgress % .5f) * 2;
lowerArmAngle.set(MathHelper.lerp(progress, previousTarget.lowerArmAngle, target.lowerArmAngle));
upperArmAngle.set(MathHelper.lerp(progress, previousTarget.upperArmAngle, target.upperArmAngle));
headAngle.set(AngleHelper.angleLerp(progress, previousTarget.headAngle, target.headAngle));
}
@Nullable
private ArmInteractionPoint getTargetedInteractionPoint() {
if (chasedPointIndex == -1)
return null;
if (phase == Phase.MOVE_TO_INPUT && chasedPointIndex < inputs.size())
return inputs.get(chasedPointIndex);
if (phase == Phase.MOVE_TO_OUTPUT && chasedPointIndex < outputs.size())
return outputs.get(chasedPointIndex);
return null;
}
protected void searchForItem() {
for (int index = 0; index < inputs.size(); index++) {
ArmInteractionPoint armInteractionPoint = inputs.get(index);
for (int i = 0; i < armInteractionPoint.getSlotCount(world); i++) {
if (getDistributableAmount(armInteractionPoint, i) == 0)
continue;
phase = Phase.MOVE_TO_INPUT;
chasedPointIndex = index;
chasedPointProgress = 0;
sendData();
markDirty();
return;
}
}
}
protected void searchForDestination() {
ItemStack held = heldItem.copy();
for (int index = 0; index < outputs.size(); index++) {
ArmInteractionPoint armInteractionPoint = outputs.get(index);
ItemStack remainder = armInteractionPoint.insert(world, held, true);
if (remainder.equals(heldItem, false))
continue;
phase = Phase.MOVE_TO_OUTPUT;
chasedPointIndex = index;
chasedPointProgress = 0;
sendData();
markDirty();
return;
}
}
protected int getDistributableAmount(ArmInteractionPoint armInteractionPoint, int i) {
ItemStack stack = armInteractionPoint.extract(world, i, true);
ItemStack remainder = simulateInsertion(stack);
return stack.getCount() - remainder.getCount();
}
protected void depositItem() {
ArmInteractionPoint armInteractionPoint = getTargetedInteractionPoint();
ItemStack toInsert = heldItem.copy();
ItemStack remainder = armInteractionPoint.insert(world, toInsert, false);
heldItem = remainder;
phase = heldItem.isEmpty() ? Phase.SEARCH_INPUTS : Phase.SEARCH_OUTPUTS;
chasedPointProgress = 0;
chasedPointIndex = -1;
sendData();
markDirty();
}
protected void collectItem() {
ArmInteractionPoint armInteractionPoint = getTargetedInteractionPoint();
for (int i = 0; i < armInteractionPoint.getSlotCount(world); i++) {
int amountExtracted = getDistributableAmount(armInteractionPoint, i);
if (amountExtracted == 0)
continue;
heldItem = armInteractionPoint.extract(world, i, amountExtracted, false);
phase = Phase.SEARCH_OUTPUTS;
chasedPointProgress = 0;
chasedPointIndex = -1;
sendData();
markDirty();
return;
}
phase = Phase.SEARCH_INPUTS;
chasedPointProgress = 0;
chasedPointIndex = -1;
sendData();
markDirty();
}
private ItemStack simulateInsertion(ItemStack stack) {
for (ArmInteractionPoint armInteractionPoint : outputs) {
stack = armInteractionPoint.insert(world, stack, true);
if (stack.isEmpty())
break;
}
return stack;
}
protected void initInteractionPoints() {
if (interactionPointTag == null)
return;
inputs.clear();
outputs.clear();
for (INBT inbt : interactionPointTag) {
ArmInteractionPoint point = ArmInteractionPoint.deserialize(world, (CompoundNBT) inbt);
if (point.mode == Mode.DEPOSIT)
outputs.add(point);
if (point.mode == Mode.TAKE)
inputs.add(point);
}
interactionPointTag = null;
markDirty(); markDirty();
sendData(); sendData();
} }
@Override @Override
public CompoundNBT write(CompoundNBT compound) { public CompoundNBT write(CompoundNBT compound) {
super.write(compound); super.write(compound);
compound.putBoolean("DebugRave", debugRave);
ListNBT pointsNBT = new ListNBT();
inputs.stream()
.map(ArmInteractionPoint::serialize)
.forEach(pointsNBT::add);
outputs.stream()
.map(ArmInteractionPoint::serialize)
.forEach(pointsNBT::add);
NBTHelper.writeEnum(compound, "Phase", phase);
compound.put("InterationPoints", pointsNBT);
compound.put("HeldItem", heldItem.serializeNBT());
compound.putInt("TargetPointIndex", chasedPointIndex);
compound.putFloat("MovementProgress", chasedPointProgress);
return compound; return compound;
} }
@Override
public CompoundNBT writeToClient(CompoundNBT compound) {
super.writeToClient(compound);
if (interactionPointTag != null)
compound.put("InitialInterationPoints", interactionPointTag);
return compound;
}
@Override @Override
public void read(CompoundNBT compound) { public void read(CompoundNBT compound) {
super.read(compound); super.read(compound);
debugRave = compound.getBoolean("DebugRave"); heldItem = ItemStack.read(compound.getCompound("HeldItem"));
phase = NBTHelper.readEnum(compound, "Phase", Phase.class);
chasedPointIndex = compound.getInt("TargetPointIndex");
chasedPointProgress = compound.getFloat("MovementProgress");
if (!hasWorld() || !world.isRemote || updateInteractionPoints)
interactionPointTag = compound.getList("InterationPoints", NBT.TAG_COMPOUND);
updateInteractionPoints = false;
}
@Override
public void readClientUpdate(CompoundNBT tag) {
int previousIndex = chasedPointIndex;
Phase previousPhase = phase;
super.readClientUpdate(tag);
if (previousIndex != chasedPointIndex || (previousPhase != phase)) {
ArmInteractionPoint previousPoint = null;
if (previousPhase == Phase.MOVE_TO_INPUT && previousIndex < inputs.size())
previousPoint = inputs.get(previousIndex);
if (previousPhase == Phase.MOVE_TO_OUTPUT && previousIndex < outputs.size())
previousPoint = outputs.get(previousIndex);
previousTarget = previousPoint == null ? ArmAngleTarget.NO_TARGET : previousPoint.getTargetAngles(pos);
if (previousPoint != null)
previousBaseAngle = previousPoint.getTargetAngles(pos).baseAngle;
}
if (tag.contains("InitialInterationPoints"))
interactionPointTag = tag.getList("InitialInterationPoints", NBT.TAG_COMPOUND);
} }
} }

View file

@ -43,7 +43,9 @@ public class FilterScreenPacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null)
return;
if (player.openContainer instanceof AbstractFilterContainer) { if (player.openContainer instanceof AbstractFilterContainer) {
AbstractFilterContainer c = (AbstractFilterContainer) player.openContainer; AbstractFilterContainer c = (AbstractFilterContainer) player.openContainer;
if (option == Option.CLEAR) { if (option == Option.CLEAR) {

View file

@ -49,6 +49,8 @@ public class ConfigureSchematicannonPacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null)
return;
World world = player.world; World world = player.world;
if (world == null) if (world == null)
return; return;

View file

@ -32,6 +32,8 @@ public class SchematicPlacePacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null)
return;
Template t = SchematicItem.loadSchematic(stack); Template t = SchematicItem.loadSchematic(stack);
PlacementSettings settings = SchematicItem.getSettings(stack); PlacementSettings settings = SchematicItem.getSettings(stack);
settings.setIgnoreEntities(false); settings.setIgnoreEntities(false);

View file

@ -68,6 +68,8 @@ public class SchematicUploadPacket extends SimplePacketBase {
.enqueueWork(() -> { .enqueueWork(() -> {
ServerPlayerEntity player = context.get() ServerPlayerEntity player = context.get()
.getSender(); .getSender();
if (player == null)
return;
if (code == BEGIN) { if (code == BEGIN) {
BlockPos pos = ((SchematicTableContainer) player.openContainer).getTileEntity() BlockPos pos = ((SchematicTableContainer) player.openContainer).getTileEntity()
.getPos(); .getPos();

View file

@ -0,0 +1,11 @@
package com.simibubi.create.foundation.gui.widgets;
import com.simibubi.create.foundation.utility.AngleHelper;
public class InterpolatedAngle extends InterpolatedValue {
public float get(float partialTicks) {
return AngleHelper.angleLerp(partialTicks, lastValue, value);
}
}

View file

@ -4,6 +4,10 @@ import com.simibubi.create.foundation.utility.AngleHelper;
public class InterpolatedChasingAngle extends InterpolatedChasingValue { public class InterpolatedChasingAngle extends InterpolatedChasingValue {
public float get(float partialTicks) {
return AngleHelper.angleLerp(partialTicks, lastValue, value);
}
@Override @Override
protected float getCurrentDiff() { protected float getCurrentDiff() {
return AngleHelper.getShortestAngleDiff(value, getTarget()); return AngleHelper.getShortestAngleDiff(value, getTarget());

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Config
import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket; import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket;
import com.simibubi.create.content.curiosities.tools.ExtendoGripInteractionPacket; import com.simibubi.create.content.curiosities.tools.ExtendoGripInteractionPacket;
import com.simibubi.create.content.curiosities.zapper.ZapperBeamPacket; import com.simibubi.create.content.curiosities.zapper.ZapperBeamPacket;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmPlacementPacket;
import com.simibubi.create.content.logistics.item.filter.FilterScreenPacket; import com.simibubi.create.content.logistics.item.filter.FilterScreenPacket;
import com.simibubi.create.content.logistics.packet.ConfigureFlexcratePacket; import com.simibubi.create.content.logistics.packet.ConfigureFlexcratePacket;
import com.simibubi.create.content.logistics.packet.ConfigureStockswitchPacket; import com.simibubi.create.content.logistics.packet.ConfigureStockswitchPacket;
@ -44,6 +45,7 @@ public enum AllPackets {
CONFIGURE_SCROLLABLE(ScrollValueUpdatePacket.class, ScrollValueUpdatePacket::new), CONFIGURE_SCROLLABLE(ScrollValueUpdatePacket.class, ScrollValueUpdatePacket::new),
CANCEL_FALL(CancelPlayerFallPacket.class, CancelPlayerFallPacket::new), CANCEL_FALL(CancelPlayerFallPacket.class, CancelPlayerFallPacket::new),
EXTENDO_INTERACT(ExtendoGripInteractionPacket.class, ExtendoGripInteractionPacket::new), EXTENDO_INTERACT(ExtendoGripInteractionPacket.class, ExtendoGripInteractionPacket::new),
PLACE_ARM(ArmPlacementPacket.class, ArmPlacementPacket::new),
// Server to Client // Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new), SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new),

View file

@ -40,6 +40,8 @@ public class NbtPacket extends SimplePacketBase {
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null)
return;
if (slot == -1) { if (slot == -1) {
ItemStack heldItem = player.getHeldItem(hand); ItemStack heldItem = player.getHeldItem(hand);

View file

@ -35,6 +35,8 @@ public abstract class TileEntityConfigurationPacket<TE extends SyncedTileEntity>
public void handle(Supplier<Context> context) { public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> { context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null)
return;
World world = player.world; World world = player.world;
if (world == null || world.getTileEntity(pos) == null) if (world == null || world.getTileEntity(pos) == null)

View file

@ -12,8 +12,9 @@ import net.minecraft.util.math.AxisAlignedBB;
public class NBTHelper { public class NBTHelper {
public static <T extends Enum<?>> T readEnum(String name, Class<T> enumClass) { public static <T extends Enum<?>> T readEnum(CompoundNBT nbt, String key, Class<T> enumClass) {
T[] enumConstants = enumClass.getEnumConstants(); T[] enumConstants = enumClass.getEnumConstants();
String name = nbt.getString(key);
if (enumConstants == null) if (enumConstants == null)
throw new IllegalArgumentException("Non-Enum class passed to readEnum(): " + enumClass.getName()); throw new IllegalArgumentException("Non-Enum class passed to readEnum(): " + enumClass.getName());
for (T t : enumConstants) { for (T t : enumConstants) {
@ -22,11 +23,11 @@ public class NBTHelper {
} }
return enumConstants[0]; return enumConstants[0];
} }
public static <T extends Enum<?>> String writeEnum(T enumConstant) { public static <T extends Enum<?>> void writeEnum(CompoundNBT nbt, String key, T enumConstant) {
return enumConstant.name(); nbt.putString(key, enumConstant.name());
} }
public static <T> ListNBT writeCompoundList(List<T> list, Function<T, CompoundNBT> serializer) { public static <T> ListNBT writeCompoundList(List<T> list, Function<T, CompoundNBT> serializer) {
ListNBT listNBT = new ListNBT(); ListNBT listNBT = new ListNBT();
list.forEach(t -> listNBT.add(serializer.apply(t))); list.forEach(t -> listNBT.add(serializer.apply(t)));

View file

@ -0,0 +1,31 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"4": "create:block/marker_flag"
},
"elements": [
{
"from": [0, 0, 0],
"to": [1, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"north": {"uv": [1, 0, 2, 16], "texture": "#4"},
"east": {"uv": [0, 0, 1, 16], "texture": "#4"},
"south": {"uv": [1, 0, 2, 16], "texture": "#4"},
"west": {"uv": [0, 0, 1, 16], "texture": "#4"},
"up": {"uv": [0, 0, 1, 1], "texture": "#4"},
"down": {"uv": [0, 0, 1, 1], "texture": "#4"}
}
},
{
"from": [0.4, 7.5, 1],
"to": [0.4, 15.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"east": {"uv": [16, 0, 8, 8], "texture": "#4", "tintindex": 0},
"west": {"uv": [8, 0, 16, 8], "texture": "#4", "tintindex": 0}
}
}
]
}

View file

@ -0,0 +1,31 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"4": "create:block/marker_flag"
},
"elements": [
{
"from": [0, 0, 0],
"to": [1, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"north": {"uv": [1, 0, 2, 16], "texture": "#4"},
"east": {"uv": [0, 0, 1, 16], "texture": "#4"},
"south": {"uv": [1, 0, 2, 16], "texture": "#4"},
"west": {"uv": [0, 0, 1, 16], "texture": "#4"},
"up": {"uv": [0, 0, 1, 1], "texture": "#4"},
"down": {"uv": [0, 0, 1, 1], "texture": "#4"}
}
},
{
"from": [0.4, 7.5, 1],
"to": [0.4, 15.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"east": {"uv": [16, 8, 8, 16], "texture": "#4", "tintindex": 0},
"west": {"uv": [8, 8, 16, 16], "texture": "#4", "tintindex": 0}
}
}
]
}

View file

@ -0,0 +1,31 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"4": "create:block/marker_flag"
},
"elements": [
{
"from": [0, 0, 0],
"to": [1, 8, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"north": {"uv": [1, 8, 2, 16], "texture": "#4"},
"east": {"uv": [0, 0, 1, 8], "texture": "#4"},
"south": {"uv": [1, 0, 2, 8], "texture": "#4"},
"west": {"uv": [0, 0, 1, 8], "texture": "#4"},
"up": {"uv": [0, 0, 1, 1], "texture": "#4"},
"down": {"uv": [0, 0, 1, 1], "texture": "#4"}
}
},
{
"from": [0.4, -0.5, 1],
"to": [0.4, 7.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"east": {"uv": [16, 0, 8, 8], "texture": "#4", "tintindex": 0},
"west": {"uv": [8, 0, 16, 8], "texture": "#4", "tintindex": 0}
}
}
]
}

View file

@ -0,0 +1,31 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"4": "create:block/marker_flag"
},
"elements": [
{
"from": [0, 0, 0],
"to": [1, 8, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"north": {"uv": [1, 8, 2, 16], "texture": "#4"},
"east": {"uv": [0, 0, 1, 8], "texture": "#4"},
"south": {"uv": [1, 0, 2, 8], "texture": "#4"},
"west": {"uv": [0, 0, 1, 8], "texture": "#4"},
"up": {"uv": [0, 0, 1, 1], "texture": "#4"},
"down": {"uv": [0, 0, 1, 1], "texture": "#4"}
}
},
{
"from": [0.4, -0.5, 1],
"to": [0.4, 7.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -3, 10]},
"faces": {
"east": {"uv": [16, 8, 8, 16], "texture": "#4", "tintindex": 0},
"west": {"uv": [8, 8, 16, 16], "texture": "#4", "tintindex": 0}
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B