Started working on the robbit.
This commit is contained in:
@@ -6,11 +6,13 @@ import li.cil.oc2.client.gui.ComputerContainerScreen;
|
||||
import li.cil.oc2.client.item.CustomItemModelProperties;
|
||||
import li.cil.oc2.client.model.BusCableModelLoader;
|
||||
import li.cil.oc2.client.renderer.NetworkCableRenderer;
|
||||
import li.cil.oc2.client.renderer.entity.RobotEntityRenderer;
|
||||
import li.cil.oc2.client.renderer.tileentity.ComputerTileEntityRenderer;
|
||||
import li.cil.oc2.client.renderer.tileentity.NetworkConnectorTileEntityRenderer;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.device.DeviceTypes;
|
||||
import li.cil.oc2.common.container.Containers;
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.tileentity.TileEntities;
|
||||
import net.minecraft.client.gui.ScreenManager;
|
||||
import net.minecraft.inventory.container.PlayerContainer;
|
||||
@@ -20,6 +22,7 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
public final class ClientSetup {
|
||||
@@ -32,6 +35,8 @@ public final class ClientSetup {
|
||||
|
||||
ClientRegistry.bindTileEntityRenderer(TileEntities.COMPUTER_TILE_ENTITY.get(), ComputerTileEntityRenderer::new);
|
||||
ClientRegistry.bindTileEntityRenderer(TileEntities.NETWORK_CONNECTOR_TILE_ENTITY.get(), NetworkConnectorTileEntityRenderer::new);
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler(Entities.ROBOT.get(), RobotEntityRenderer::new);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package li.cil.oc2.client.renderer.entity;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||
import net.minecraft.client.renderer.entity.model.EntityModel;
|
||||
import net.minecraft.client.renderer.model.ModelRenderer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.EntityRayTraceResult;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraftforge.common.model.TransformationHelper;
|
||||
|
||||
public final class RobotEntityRenderer extends EntityRenderer<RobotEntity> {
|
||||
private final RobotModel model = new RobotModel();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotEntityRenderer(final EntityRendererManager renderManager) {
|
||||
super(renderManager);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public ResourceLocation getEntityTexture(final RobotEntity entity) {
|
||||
return new ResourceLocation(API.MOD_ID, "textures/entity/robot/robot.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final RobotEntity entity, final float entityYaw, final float partialTicks, final MatrixStack matrixStack, final IRenderTypeBuffer buffer, final int packedLight) {
|
||||
final RobotEntity.AnimationState state = entity.getAnimationState();
|
||||
state.update(partialTicks, entity.world.rand);
|
||||
|
||||
matrixStack.push();
|
||||
// NB: we don't entityYaw given to use because that uses a plain lerp which can lead to ugly
|
||||
// jumps in case we get a wrapped rotationYaw synced from the server (leading to ~360
|
||||
// degree delta to the last known previous rotation). Haven't figured out where to
|
||||
// alternatively prevent this wrapping or patch the prev value instead.
|
||||
final float partialRotation = MathHelper.degreesDifferenceAbs(entity.prevRotationYaw, entity.rotationYaw) * partialTicks;
|
||||
final float rotation = MathHelper.approachDegrees(entity.prevRotationYaw, entity.rotationYaw, partialRotation);
|
||||
matrixStack.rotate(Vector3f.YN.rotationDegrees(rotation));
|
||||
|
||||
model.setRotationAngles(entity, 0, 0, 0, 0, 0);
|
||||
|
||||
final IVertexBuilder builder = buffer.getBuffer(model.getRenderType(getEntityTexture(entity)));
|
||||
model.render(matrixStack, builder, packedLight, OverlayTexture.NO_OVERLAY, 1, 1, 1, 1);
|
||||
|
||||
matrixStack.pop();
|
||||
|
||||
final RayTraceResult hit = Minecraft.getInstance().objectMouseOver;
|
||||
if (hit instanceof EntityRayTraceResult && entity == ((EntityRayTraceResult) hit).getEntity()) {
|
||||
super.renderName(entity, new StringTextComponent("hi"), matrixStack, buffer, packedLight);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class RobotModel extends EntityModel<RobotEntity> {
|
||||
private final ModelRenderer topRenderer;
|
||||
private final ModelRenderer baseRenderer;
|
||||
private final ModelRenderer coreRenderer;
|
||||
private float baseY, topY;
|
||||
private final float[] topRotation = new float[3];
|
||||
|
||||
public RobotModel() {
|
||||
topRenderer = new ModelRenderer(this, 1, 1)
|
||||
.setTextureSize(64, 64)
|
||||
.addBox(-7, 8, -7, 14, 6, 14);
|
||||
baseRenderer = new ModelRenderer(this, 1, 23)
|
||||
.setTextureSize(64, 64)
|
||||
.addBox(-7, 0, -7, 14, 7, 14);
|
||||
coreRenderer = new ModelRenderer(this, 1, 34)
|
||||
.setTextureSize(64, 64)
|
||||
.addBox(-6, 7, -6, 12, 1, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRotationAngles(final RobotEntity entity, final float limbSwing, final float limbSwingAmount, final float ageInTicks, final float netHeadYaw, final float headPitch) {
|
||||
final RobotEntity.AnimationState state = entity.getAnimationState();
|
||||
baseY = state.baseRenderOffsetY;
|
||||
topY = state.topRenderOffsetY;
|
||||
topRotation[1] = state.topRenderRotationY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final MatrixStack matrixStack, final IVertexBuilder buffer, final int packedLight, final int packedOverlay, final float red, final float green, final float blue, final float alpha) {
|
||||
matrixStack.push();
|
||||
matrixStack.translate(0, topY, 0);
|
||||
matrixStack.rotate(TransformationHelper.quatFromXYZ(topRotation, true));
|
||||
topRenderer.render(matrixStack, buffer, packedLight, packedOverlay);
|
||||
matrixStack.pop();
|
||||
|
||||
matrixStack.push();
|
||||
matrixStack.translate(0, baseY, 0);
|
||||
baseRenderer.render(matrixStack, buffer, packedLight, packedOverlay);
|
||||
coreRenderer.render(matrixStack, buffer, LightTexture.packLight(15, 15), packedOverlay);
|
||||
matrixStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.client.renderer.entity;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -41,6 +41,10 @@ public final class Constants {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String ROBOT_ENTITY_NAME = "robot";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String CONFIG_MAX_ALLOCATED_MEMORY = "config.oc2.maxAllocatedMemory";
|
||||
public static final String CONFIG_MAX_MEMORY_SIZE = "config.oc2.maxMemorySize";
|
||||
public static final String CONFIG_MAX_HARD_DRIVE_SIZE = "config.oc2.maxHardDriveSize";
|
||||
|
||||
@@ -9,6 +9,7 @@ import li.cil.oc2.common.bus.device.data.BaseBlockDevices;
|
||||
import li.cil.oc2.common.bus.device.data.Firmwares;
|
||||
import li.cil.oc2.common.bus.device.provider.Providers;
|
||||
import li.cil.oc2.common.container.Containers;
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.serialization.serializers.Serializers;
|
||||
import li.cil.oc2.common.tileentity.TileEntities;
|
||||
@@ -28,6 +29,7 @@ public final class Main {
|
||||
Items.initialize();
|
||||
Blocks.initialize();
|
||||
TileEntities.initialize();
|
||||
Entities.initialize();
|
||||
Containers.initialize();
|
||||
Providers.initialize();
|
||||
DeviceTypes.initialize();
|
||||
|
||||
33
src/main/java/li/cil/oc2/common/entity/Entities.java
Normal file
33
src/main/java/li/cil/oc2/common/entity/Entities.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package li.cil.oc2.common.entity;
|
||||
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityClassification;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraftforge.fml.RegistryObject;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class Entities {
|
||||
private static final DeferredRegister<EntityType<?>> ENTITIES = DeferredRegister.create(ForgeRegistries.ENTITIES, API.MOD_ID);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final RegistryObject<EntityType<RobotEntity>> ROBOT = register(Constants.ROBOT_ENTITY_NAME, RobotEntity::new, EntityClassification.MISC, b -> b.size(14f / 16f, 14f / 16f).immuneToFire().disableSummoning());
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
ENTITIES.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static <T extends Entity> RegistryObject<EntityType<T>> register(final String name, final EntityType.IFactory<T> factory, final EntityClassification classification, final Function<EntityType.Builder<T>, EntityType.Builder<T>> customizer) {
|
||||
return ENTITIES.register(name, () -> customizer.apply(EntityType.Builder.create(factory, classification)).build(name));
|
||||
}
|
||||
}
|
||||
323
src/main/java/li/cil/oc2/common/entity/RobotEntity.java
Normal file
323
src/main/java/li/cil/oc2/common/entity/RobotEntity.java
Normal file
@@ -0,0 +1,323 @@
|
||||
package li.cil.oc2.common.entity;
|
||||
|
||||
import li.cil.oc2.common.entity.robot.*;
|
||||
import li.cil.oc2.common.integration.Wrenches;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.WorldUtils;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.network.IPacket;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkHooks;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
|
||||
public final class RobotEntity extends Entity {
|
||||
public static int MAX_QUEUED_ACTIONS = 16;
|
||||
|
||||
private static final String COMMAND_PROCESSOR_TAG_NAME = "commands";
|
||||
|
||||
private static final DataParameter<Boolean> IS_RUNNING = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BOOLEAN);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final AnimationState animationState = new AnimationState();
|
||||
private final CommandProcessor commandProcessor = new CommandProcessor();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotEntity(final EntityType<?> type, final World world) {
|
||||
super(type, world);
|
||||
this.preventEntitySpawning = true;
|
||||
setNoGravity(true);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public AnimationState getAnimationState() {
|
||||
return animationState;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return dataManager.get(IS_RUNNING);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
// todo start vm
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// todo stop vm
|
||||
|
||||
commandProcessor.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
commandProcessor.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType processInitialInteract(final PlayerEntity player, final Hand hand) {
|
||||
final ItemStack stack = player.getHeldItem(hand);
|
||||
if (Wrenches.isWrench(stack)) {
|
||||
if (!world.isRemote()) {
|
||||
if (player.isSneaking()) {
|
||||
remove();
|
||||
WorldUtils.playSound(world, getPosition(), SoundType.METAL, SoundType::getBreakSound);
|
||||
} else {
|
||||
// todo open container
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (player.isSneaking()) {
|
||||
// TODO start machine
|
||||
if (!world.isRemote()) {
|
||||
dataManager.set(IS_RUNNING, !dataManager.get(IS_RUNNING));
|
||||
}
|
||||
} else {
|
||||
// if (rand.nextBoolean()) {
|
||||
// commandProcessor.move(MovementDirection.values()[rand.nextInt(MovementDirection.values().length)]);
|
||||
// } else {
|
||||
// commandProcessor.rotate(rand.nextBoolean() ? RotationDirection.LEFT : RotationDirection.RIGHT);
|
||||
commandProcessor.rotate(RotationDirection.RIGHT);
|
||||
// }
|
||||
// TODO open terminal + inventory screen
|
||||
}
|
||||
}
|
||||
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPacket<?> createSpawnPacket() {
|
||||
return NetworkHooks.getEntitySpawningPacket(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCollidedWith() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCollide(final Entity entity) {
|
||||
return entity != this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyEntityCollision(final Entity entity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean func_241845_aY() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSpawnRunningEffects() {
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void registerData() {
|
||||
getDataManager().register(IS_RUNNING, false);
|
||||
RobotActions.registerData(getDataManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeAdditional(final CompoundNBT compound) {
|
||||
compound.put(COMMAND_PROCESSOR_TAG_NAME, commandProcessor.serialize());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readAdditional(final CompoundNBT compound) {
|
||||
commandProcessor.deserialize(compound.getCompound(COMMAND_PROCESSOR_TAG_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canTriggerWalking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBlockCollisions() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static float lerpClamped(final float from, final float to, final float delta) {
|
||||
if (from < to) {
|
||||
return Math.min(from + delta, to);
|
||||
} else if (from > to) {
|
||||
return Math.max(from - delta, to);
|
||||
} else {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
private static float remapFrom01To(final float x, final float a1, final float b1) {
|
||||
if (a1 == b1) {
|
||||
return a1;
|
||||
} else {
|
||||
return x * (b1 - a1) + a1;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public final class AnimationState {
|
||||
private static final float TOP_IDLE_Y = -2f / 16f;
|
||||
private static final float BASE_IDLE_Y = -1f / 16f;
|
||||
|
||||
private static final float TRANSLATION_SPEED = 0.005f;
|
||||
private static final float ROTATION_SPEED = 1f;
|
||||
private static final float MAX_ROTATION = 5f;
|
||||
private static final float MIN_ROTATION_SPEED = 0.055f;
|
||||
private static final float MAX_ROTATION_SPEED = 0.060f;
|
||||
private static final float HOVER_ANIMATION_SPEED = 0.01f;
|
||||
|
||||
public float topRenderOffsetY = TOP_IDLE_Y;
|
||||
public float baseRenderOffsetY = BASE_IDLE_Y;
|
||||
public float topRenderRotationY;
|
||||
public float topRenderTargetRotationY;
|
||||
public float topRenderRotationSpeed;
|
||||
public float topRenderHover = -(hashCode() & 0xFFFF); // init to "random" to avoid synchronous hovering
|
||||
|
||||
public void update(final float deltaTime, final Random random) {
|
||||
if (isRunning() || commandProcessor.hasQueuedActions()) {
|
||||
topRenderHover = topRenderHover + deltaTime * HOVER_ANIMATION_SPEED;
|
||||
final float topOffsetY = MathHelper.sin(topRenderHover) / 32f;
|
||||
|
||||
topRenderOffsetY = lerpClamped(topRenderOffsetY, topOffsetY, deltaTime * TRANSLATION_SPEED);
|
||||
baseRenderOffsetY = lerpClamped(baseRenderOffsetY, topOffsetY, deltaTime * TRANSLATION_SPEED);
|
||||
|
||||
topRenderRotationY = lerpClamped(topRenderRotationY, topRenderTargetRotationY, deltaTime * topRenderRotationSpeed);
|
||||
if (topRenderRotationY == topRenderTargetRotationY) {
|
||||
topRenderTargetRotationY = remapFrom01To(random.nextFloat(), -MAX_ROTATION, MAX_ROTATION);
|
||||
topRenderRotationSpeed = remapFrom01To(random.nextFloat(), MIN_ROTATION_SPEED, MAX_ROTATION_SPEED);
|
||||
}
|
||||
} else {
|
||||
topRenderOffsetY = lerpClamped(topRenderOffsetY, TOP_IDLE_Y, deltaTime * TRANSLATION_SPEED * 2);
|
||||
baseRenderOffsetY = lerpClamped(baseRenderOffsetY, BASE_IDLE_Y, deltaTime * TRANSLATION_SPEED);
|
||||
|
||||
topRenderRotationY = lerpClamped(topRenderRotationY, 0, deltaTime * ROTATION_SPEED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CommandProcessor {
|
||||
private static final String QUEUE_TAG_NAME = "queue";
|
||||
private static final String ACTION_TAG_NAME = "action";
|
||||
|
||||
private final Queue<AbstractRobotAction> queue = new ArrayDeque<>(MAX_QUEUED_ACTIONS);
|
||||
@Nullable private AbstractRobotAction action;
|
||||
|
||||
public boolean hasQueuedActions() {
|
||||
return action != null || !queue.isEmpty();
|
||||
}
|
||||
|
||||
public boolean move(final MovementDirection direction) {
|
||||
return addAction(new RobotMovementAction(direction));
|
||||
}
|
||||
|
||||
public boolean rotate(final RotationDirection direction) {
|
||||
return addAction(new RobotRotationAction(direction));
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (getEntityWorld().isRemote()) {
|
||||
RobotActions.performClient(RobotEntity.this);
|
||||
} else {
|
||||
if (action != null) {
|
||||
if (action.perform(RobotEntity.this)) {
|
||||
action = null;
|
||||
}
|
||||
}
|
||||
if (action == null) {
|
||||
action = queue.poll();
|
||||
if (action != null) {
|
||||
action.initialize(RobotEntity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
public CompoundNBT serialize() {
|
||||
final CompoundNBT tag = new CompoundNBT();
|
||||
|
||||
final ListNBT queueTag = new ListNBT();
|
||||
for (final AbstractRobotAction action : queue) {
|
||||
queueTag.add(RobotActions.serialize(action));
|
||||
}
|
||||
tag.put(QUEUE_TAG_NAME, queueTag);
|
||||
|
||||
if (action != null) {
|
||||
tag.put(ACTION_TAG_NAME, RobotActions.serialize(action));
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void deserialize(final CompoundNBT tag) {
|
||||
queue.clear();
|
||||
action = null;
|
||||
|
||||
final ListNBT queueTag = tag.getList(QUEUE_TAG_NAME, NBTTagIds.TAG_COMPOUND);
|
||||
for (int i = 0; i < queueTag.size(); i++) {
|
||||
final AbstractRobotAction action = RobotActions.deserialize(queueTag.getCompound(i));
|
||||
if (action != null) {
|
||||
queue.add(action);
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(ACTION_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
action = RobotActions.deserialize(tag.getCompound(ACTION_TAG_NAME));
|
||||
action.initialize(RobotEntity.this);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean addAction(final AbstractRobotAction action) {
|
||||
if (getEntityWorld().isRemote()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue.size() < MAX_QUEUED_ACTIONS) {
|
||||
queue.add(action);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/main/java/li/cil/oc2/common/entity/package-info.java
Normal file
7
src/main/java/li/cil/oc2/common/entity/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.entity;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -0,0 +1,39 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
|
||||
public abstract class AbstractRobotAction {
|
||||
private final AbstractRobotActionType type;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public AbstractRobotAction(final AbstractRobotActionType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public AbstractRobotAction(final AbstractRobotActionType type, final CompoundNBT tag) {
|
||||
this(type);
|
||||
deserialize(tag);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public AbstractRobotActionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void initialize(final RobotEntity robot) {
|
||||
}
|
||||
|
||||
public abstract boolean perform(RobotEntity robot);
|
||||
|
||||
public CompoundNBT serialize() {
|
||||
return new CompoundNBT();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected void deserialize(final CompoundNBT tag) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
|
||||
public abstract class AbstractRobotActionType {
|
||||
private final int id;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected AbstractRobotActionType(final int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void registerData(final EntityDataManager dataManager) {
|
||||
}
|
||||
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
}
|
||||
|
||||
public void performClient(final RobotEntity robot) {
|
||||
}
|
||||
|
||||
public abstract AbstractRobotAction deserialize(final CompoundNBT tag);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
public enum MovementDirection {
|
||||
UP,
|
||||
DOWN,
|
||||
FORWARD,
|
||||
BACKWARD,
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public final class RobotActions {
|
||||
private static final String ACTION_TYPE_TAG_NAME = "action_type";
|
||||
|
||||
private static final ArrayList<AbstractRobotActionType> ACTIONS = new ArrayList<>();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final AbstractRobotActionType MOVEMENT = register(RobotMovementActionType::new);
|
||||
public static final AbstractRobotActionType ROTATION = register(RobotRotationActionType::new);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void registerData(final EntityDataManager dataManager) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.registerData(dataManager);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initializeData(final RobotEntity robot) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.initializeData(robot);
|
||||
}
|
||||
}
|
||||
|
||||
public static void performClient(final RobotEntity robot) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.performClient(robot);
|
||||
}
|
||||
}
|
||||
|
||||
public static CompoundNBT serialize(final AbstractRobotAction action) {
|
||||
final CompoundNBT actionTag = action.serialize();
|
||||
actionTag.putInt(ACTION_TYPE_TAG_NAME, action.getType().getId());
|
||||
return actionTag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AbstractRobotAction deserialize(final CompoundNBT tag) {
|
||||
final int type = tag.getInt(ACTION_TYPE_TAG_NAME);
|
||||
if (type < 1 || type > ACTIONS.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AbstractRobotActionType actionType = ACTIONS.get(type - 1);
|
||||
return actionType.deserialize(tag);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static AbstractRobotActionType register(final IntFunction<? extends AbstractRobotActionType> factory) {
|
||||
final AbstractRobotActionType type = factory.apply(ACTIONS.size() + 1);
|
||||
ACTIONS.add(type);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class RobotMovementAction extends AbstractRobotAction {
|
||||
public static final DataParameter<BlockPos> TARGET_POSITION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BLOCK_POS);
|
||||
public static final double TARGET_EPSILON = 0.0001;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final float MOVEMENT_SPEED = 0.5f / Constants.TICK_SECONDS; // In blocks per second.
|
||||
|
||||
private static final String DIRECTION_TAG_NAME = "direction";
|
||||
private static final String START_TAG_NAME = "start";
|
||||
private static final String TARGET_TAG_NAME = "start";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private MovementDirection direction;
|
||||
@Nullable private BlockPos start;
|
||||
@Nullable private Vector3d target;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotMovementAction(final MovementDirection direction) {
|
||||
super(RobotActions.MOVEMENT);
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
RobotMovementAction(final CompoundNBT tag) {
|
||||
super(RobotActions.MOVEMENT);
|
||||
deserialize(tag);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Vector3d getTargetPositionInBlock(final BlockPos position) {
|
||||
return Vector3d.copyCenteredHorizontally(position).add(0, 0.5f * (1 - Entities.ROBOT.get().getHeight()), 0);
|
||||
}
|
||||
|
||||
public static void moveTowards(final RobotEntity robot, final Vector3d targetPosition) {
|
||||
Vector3d delta = targetPosition.subtract(robot.getPositionVec());
|
||||
if (delta.lengthSquared() > MOVEMENT_SPEED * MOVEMENT_SPEED) {
|
||||
delta = delta.normalize().scale(MOVEMENT_SPEED);
|
||||
}
|
||||
|
||||
robot.move(MoverType.SELF, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final RobotEntity robot) {
|
||||
if (target == null) {
|
||||
start = robot.getPosition();
|
||||
BlockPos targetPosition = start;
|
||||
switch (direction) {
|
||||
case UP:
|
||||
targetPosition = targetPosition.offset(Direction.UP);
|
||||
break;
|
||||
case DOWN:
|
||||
targetPosition = targetPosition.offset(Direction.DOWN);
|
||||
break;
|
||||
case FORWARD:
|
||||
targetPosition = targetPosition.offset(robot.getHorizontalFacing());
|
||||
break;
|
||||
case BACKWARD:
|
||||
targetPosition = targetPosition.offset(robot.getHorizontalFacing().getOpposite());
|
||||
break;
|
||||
}
|
||||
|
||||
target = getTargetPositionInBlock(targetPosition);
|
||||
}
|
||||
|
||||
robot.getDataManager().set(TARGET_POSITION, new BlockPos(target));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(final RobotEntity robot) {
|
||||
if (target == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
moveTowards(robot, target);
|
||||
|
||||
final boolean didCollide = robot.collidedHorizontally || robot.collidedVertically;
|
||||
if (didCollide && !robot.getEntityWorld().isRemote()) {
|
||||
if (start != null) {
|
||||
target = getTargetPositionInBlock(start);
|
||||
robot.getDataManager().set(TARGET_POSITION, start);
|
||||
|
||||
start = null;
|
||||
} else {
|
||||
// todo if it's a block, try to break it. or just drop ourselves?
|
||||
}
|
||||
}
|
||||
|
||||
return robot.getPositionVec().squareDistanceTo(target) < TARGET_EPSILON;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public CompoundNBT serialize() {
|
||||
final CompoundNBT tag = super.serialize();
|
||||
|
||||
NBTUtils.putEnum(tag, DIRECTION_TAG_NAME, direction);
|
||||
if (start != null) {
|
||||
NBTUtils.putBlockPos(tag, START_TAG_NAME, start);
|
||||
}
|
||||
if (target != null) {
|
||||
NBTUtils.putVector3d(tag, TARGET_TAG_NAME, target);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deserialize(final CompoundNBT tag) {
|
||||
super.deserialize(tag);
|
||||
|
||||
direction = NBTUtils.getEnum(tag, DIRECTION_TAG_NAME, MovementDirection.class);
|
||||
if (tag.contains(START_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
start = NBTUtils.getBlockPos(tag, START_TAG_NAME);
|
||||
}
|
||||
if (tag.contains(TARGET_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
target = NBTUtils.getVector3d(tag, TARGET_TAG_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
|
||||
public final class RobotMovementActionType extends AbstractRobotActionType {
|
||||
public RobotMovementActionType(final int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void registerData(final EntityDataManager dataManager) {
|
||||
dataManager.register(RobotMovementAction.TARGET_POSITION, BlockPos.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
robot.getDataManager().set(RobotMovementAction.TARGET_POSITION, robot.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performClient(final RobotEntity robot) {
|
||||
final Vector3d target = RobotMovementAction.getTargetPositionInBlock(robot.getDataManager().get(RobotMovementAction.TARGET_POSITION));
|
||||
if (robot.getPositionVec().squareDistanceTo(target) > RobotMovementAction.TARGET_EPSILON) {
|
||||
RobotMovementAction.moveTowards(robot, target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractRobotAction deserialize(final CompoundNBT tag) {
|
||||
return new RobotMovementAction(tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.DataParameter;
|
||||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class RobotRotationAction extends AbstractRobotAction {
|
||||
public static final DataParameter<Direction> TARGET_DIRECTION = EntityDataManager.createKey(RobotEntity.class, DataSerializers.DIRECTION);
|
||||
public static final float TARGET_EPSILON = 0.0001f;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final float ROTATION_SPEED = 45f / Constants.TICK_SECONDS; // In degrees per second.
|
||||
|
||||
private static final String DIRECTION_TAG_NAME = "direction";
|
||||
private static final String TARGET_TAG_NAME = "start";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private RotationDirection direction;
|
||||
@Nullable private Direction target;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotRotationAction(final RotationDirection direction) {
|
||||
super(RobotActions.ROTATION);
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
RobotRotationAction(final CompoundNBT tag) {
|
||||
super(RobotActions.ROTATION);
|
||||
deserialize(tag);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void rotateTowards(final RobotEntity robot, final Direction targetRotation) {
|
||||
robot.rotationYaw = MathHelper.approachDegrees(robot.rotationYaw, targetRotation.getHorizontalAngle(), ROTATION_SPEED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final RobotEntity robot) {
|
||||
if (target == null) {
|
||||
target = robot.getHorizontalFacing();
|
||||
switch (direction) {
|
||||
case LEFT:
|
||||
target = target.rotateYCCW();
|
||||
break;
|
||||
case RIGHT:
|
||||
target = target.rotateY();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
robot.getDataManager().set(TARGET_DIRECTION, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(final RobotEntity robot) {
|
||||
if (target == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
rotateTowards(robot, target);
|
||||
|
||||
return MathHelper.degreesDifferenceAbs(robot.rotationYaw, target.getHorizontalAngle()) < TARGET_EPSILON;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public CompoundNBT serialize() {
|
||||
final CompoundNBT tag = super.serialize();
|
||||
|
||||
NBTUtils.putEnum(tag, DIRECTION_TAG_NAME, direction);
|
||||
if (target != null) {
|
||||
NBTUtils.putEnum(tag, TARGET_TAG_NAME, target);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deserialize(final CompoundNBT tag) {
|
||||
super.deserialize(tag);
|
||||
|
||||
direction = NBTUtils.getEnum(tag, DIRECTION_TAG_NAME, RotationDirection.class);
|
||||
if (tag.contains(TARGET_TAG_NAME, NBTTagIds.TAG_INT)) {
|
||||
target = NBTUtils.getEnum(tag, TARGET_TAG_NAME, Direction.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public final class RobotRotationActionType extends AbstractRobotActionType {
|
||||
public RobotRotationActionType(final int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void registerData(final EntityDataManager dataManager) {
|
||||
dataManager.register(RobotRotationAction.TARGET_DIRECTION, Direction.NORTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
robot.getDataManager().set(RobotRotationAction.TARGET_DIRECTION, robot.getHorizontalFacing());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performClient(final RobotEntity robot) {
|
||||
final Direction target = robot.getDataManager().get(RobotRotationAction.TARGET_DIRECTION);
|
||||
if (MathHelper.degreesDifferenceAbs(robot.rotationYaw, target.getHorizontalAngle()) > RobotRotationAction.TARGET_EPSILON) {
|
||||
RobotRotationAction.rotateTowards(robot, target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractRobotAction deserialize(final CompoundNBT tag) {
|
||||
return new RobotRotationAction(tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
public enum RotationDirection {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -30,6 +30,7 @@ public final class Items {
|
||||
|
||||
public static final RegistryObject<Item> BUS_INTERFACE_ITEM = register(Constants.BUS_INTERFACE_ITEM_NAME, BusInterfaceItem::new);
|
||||
public static final RegistryObject<Item> NETWORK_CABLE_ITEM = register(Constants.NETWORK_CABLE_ITEM_NAME, NetworkCableItem::new);
|
||||
public static final RegistryObject<Item> ROBOT_ITEM = register(Constants.ROBOT_ENTITY_NAME, RobotItem::new);
|
||||
|
||||
public static final RegistryObject<Item> MEMORY_ITEM = register(Constants.MEMORY_ITEM_NAME, MemoryItem::new, new Item.Properties());
|
||||
public static final RegistryObject<Item> HARD_DRIVE_ITEM = register(Constants.HARD_DRIVE_ITEM_NAME, HardDriveItem::new, new Item.Properties());
|
||||
|
||||
56
src/main/java/li/cil/oc2/common/item/RobotItem.java
Normal file
56
src/main/java/li/cil/oc2/common/item/RobotItem.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package li.cil.oc2.common.item;
|
||||
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.robot.RobotActions;
|
||||
import li.cil.oc2.common.util.WorldUtils;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemUseContext;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public final class RobotItem extends Item {
|
||||
public RobotItem(final Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType onItemUse(final ItemUseContext context) {
|
||||
final World world = context.getWorld();
|
||||
final BlockPos pos = context.getPos();
|
||||
|
||||
final Vector3d position;
|
||||
if (world.getBlockState(pos).isReplaceable(new BlockItemUseContext(context))) {
|
||||
position = Vector3d.copyCentered(pos);
|
||||
} else {
|
||||
position = Vector3d.copyCentered(pos.offset(context.getFace()));
|
||||
}
|
||||
|
||||
final RobotEntity robot = Entities.ROBOT.get().create(context.getWorld());
|
||||
robot.setLocationAndAngles(position.getX(), position.getY() - robot.getHeight() * 0.5f, position.getZ(),
|
||||
Direction.fromAngle(context.getPlacementYaw()).getOpposite().getHorizontalAngle(), 0);
|
||||
if (!world.hasNoCollisions(robot)) {
|
||||
return super.onItemUse(context);
|
||||
}
|
||||
|
||||
RobotActions.initializeData(robot);
|
||||
|
||||
if (!world.isRemote()) {
|
||||
WorldUtils.playSound(world, new BlockPos(position), SoundType.METAL, SoundType::getPlaceSound);
|
||||
world.addEntity(robot);
|
||||
if (!context.getPlayer().isCreative()) {
|
||||
context.getItem().shrink(1);
|
||||
}
|
||||
}
|
||||
|
||||
context.getPlayer().addStat(Stats.ITEM_USED.get(this));
|
||||
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package li.cil.oc2.common.util;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraftforge.items.ItemStackHandler;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -27,6 +29,40 @@ public final class NBTUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void putBlockPos(final CompoundNBT tag, final String key, final BlockPos value) {
|
||||
final CompoundNBT valueTag = new CompoundNBT();
|
||||
valueTag.putInt("x", value.getX());
|
||||
valueTag.putInt("y", value.getY());
|
||||
valueTag.putInt("z", value.getZ());
|
||||
tag.put(key, valueTag);
|
||||
}
|
||||
|
||||
public static BlockPos getBlockPos(final CompoundNBT tag, final String key) {
|
||||
final CompoundNBT valueTag = tag.getCompound(key);
|
||||
return new BlockPos(
|
||||
valueTag.getInt("x"),
|
||||
valueTag.getInt("y"),
|
||||
valueTag.getInt("z")
|
||||
);
|
||||
}
|
||||
|
||||
public static void putVector3d(final CompoundNBT tag, final String key, final Vector3d value) {
|
||||
final CompoundNBT valueTag = new CompoundNBT();
|
||||
valueTag.putDouble("x", value.getX());
|
||||
valueTag.putDouble("y", value.getY());
|
||||
valueTag.putDouble("z", value.getZ());
|
||||
tag.put(key, valueTag);
|
||||
}
|
||||
|
||||
public static Vector3d getVector3d(final CompoundNBT tag, final String key) {
|
||||
final CompoundNBT valueTag = tag.getCompound(key);
|
||||
return new Vector3d(
|
||||
valueTag.getDouble("x"),
|
||||
valueTag.getDouble("y"),
|
||||
valueTag.getDouble("z")
|
||||
);
|
||||
}
|
||||
|
||||
public static CompoundNBT makeInventoryTag(final ItemStack... items) {
|
||||
final ItemStackHandler itemStackHandler = new ItemStackHandler(items.length);
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
|
||||
BIN
src/main/resources/assets/oc2/textures/entity/robot/robot.png
Normal file
BIN
src/main/resources/assets/oc2/textures/entity/robot/robot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Reference in New Issue
Block a user