diff --git a/gradle.properties b/gradle.properties index edf28a845..b671044c7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version=1.19.4 yarn_mappings=1.19.4+build.2 loader_version=0.14.21 # Mod Properties -mod_version=1.0.1 +mod_version=1.0.2 maven_group=be.uantwerpen mod_id=minelabs archives_base_name=minelabs diff --git a/src/client/java/be/minelabs/client/mixin/ItemRendererAccessor.java b/src/client/java/be/minelabs/client/mixin/ItemRendererAccessor.java new file mode 100644 index 000000000..dda2b23e2 --- /dev/null +++ b/src/client/java/be/minelabs/client/mixin/ItemRendererAccessor.java @@ -0,0 +1,12 @@ +package be.minelabs.client.mixin; + +import net.minecraft.client.render.item.ItemModels; +import net.minecraft.client.render.item.ItemRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ItemRenderer.class) +public interface ItemRendererAccessor { + @Accessor("models") + ItemModels getModels(); +} diff --git a/src/client/java/be/minelabs/client/mixin/ItemRendererMixin.java b/src/client/java/be/minelabs/client/mixin/ItemRendererMixin.java new file mode 100644 index 000000000..bdd16394c --- /dev/null +++ b/src/client/java/be/minelabs/client/mixin/ItemRendererMixin.java @@ -0,0 +1,30 @@ +package be.minelabs.client.mixin; + +import be.minelabs.Minelabs; +import be.minelabs.item.Items; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.json.ModelTransformationMode; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(ItemRenderer.class) +public abstract class ItemRendererMixin { + @ModifyVariable(method = "renderItem", at = @At(value = "HEAD"), argsOnly = true) + public BakedModel useBalloonModel(BakedModel value, ItemStack stack, ModelTransformationMode renderMode, + boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, + int light, int overlay) { + + // Do not render the balloon in 3D if it is in the GUI, on the ground, or in an item frame/armor stand + boolean bl = renderMode == ModelTransformationMode.GUI || renderMode == ModelTransformationMode.GROUND || renderMode == ModelTransformationMode.FIXED; + if (stack.isOf(Items.BALLOON) && !bl) { + return ((ItemRendererAccessor) this).getModels().getModelManager().getModel(new ModelIdentifier(Minelabs.MOD_ID, "balloon_3d", "inventory")); + } + return value; + } +} diff --git a/src/client/java/be/minelabs/client/mixin/ModelLoaderMixin.java b/src/client/java/be/minelabs/client/mixin/ModelLoaderMixin.java new file mode 100644 index 000000000..795ca9270 --- /dev/null +++ b/src/client/java/be/minelabs/client/mixin/ModelLoaderMixin.java @@ -0,0 +1,28 @@ +package be.minelabs.client.mixin; + +import be.minelabs.Minelabs; +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Map; + +@Mixin(ModelLoader.class) +public abstract class ModelLoaderMixin { + @Shadow + protected abstract void addModel(ModelIdentifier modelId); + + @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/ModelLoader;addModel(Lnet/minecraft/client/util/ModelIdentifier;)V", ordinal = 3, shift = At.Shift.AFTER)) + public void addBalloon(BlockColors blockColors, Profiler profiler, Map jsonUnbakedModels, Map> blockStates, CallbackInfo ci) { + this.addModel(new ModelIdentifier(Minelabs.MOD_ID, "balloon_3d", "inventory")); + } +} diff --git a/src/client/java/be/minelabs/client/renderer/entity/BalloonEntityRenderer.java b/src/client/java/be/minelabs/client/renderer/entity/BalloonEntityRenderer.java index 3a496acb1..904b87430 100644 --- a/src/client/java/be/minelabs/client/renderer/entity/BalloonEntityRenderer.java +++ b/src/client/java/be/minelabs/client/renderer/entity/BalloonEntityRenderer.java @@ -6,17 +6,13 @@ import be.minelabs.entity.mob.BalloonEntity; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.render.entity.MobEntityRenderer; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; -import net.minecraft.util.math.RotationAxis; -import org.joml.Quaternionf; @Environment(EnvType.CLIENT) public class BalloonEntityRenderer extends MobEntityRenderer { - + private static final Identifier TEXTURE = new Identifier(Minelabs.MOD_ID, "textures/entity/balloon/balloon.png"); public BalloonEntityRenderer(EntityRendererFactory.Context context) { super(context, new BalloonEntityModel(context.getPart(EntityModelLayers.BALLOON_MODEL)), 0.7F); @@ -24,13 +20,6 @@ public BalloonEntityRenderer(EntityRendererFactory.Context context) { @Override public Identifier getTexture(BalloonEntity entity) { - return new Identifier(Minelabs.MOD_ID, "textures/entity/balloon/balloon.png"); - } - - @Override - public void render(BalloonEntity mobEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i) { - Quaternionf rot = RotationAxis.POSITIVE_Y.rotationDegrees(mobEntity.getRotationY()); - matrixStack.multiply(rot); - super.render(mobEntity, f, g, matrixStack, vertexConsumerProvider, i); + return TEXTURE; } } \ No newline at end of file diff --git a/src/client/resources/minelabs.client.mixins.json b/src/client/resources/minelabs.client.mixins.json index b8c420a33..c91bce5fc 100644 --- a/src/client/resources/minelabs.client.mixins.json +++ b/src/client/resources/minelabs.client.mixins.json @@ -3,7 +3,10 @@ "package": "be.minelabs.client.mixin", "compatibilityLevel": "JAVA_17", "client": [ - "LabCoatArmorItemMixin" + "ItemRendererAccessor", + "ItemRendererMixin", + "LabCoatArmorItemMixin", + "ModelLoaderMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/java/be/minelabs/block/ExtraDispenserBehavior.java b/src/main/java/be/minelabs/block/ExtraDispenserBehavior.java index 7265cc5eb..42732cee9 100644 --- a/src/main/java/be/minelabs/block/ExtraDispenserBehavior.java +++ b/src/main/java/be/minelabs/block/ExtraDispenserBehavior.java @@ -2,22 +2,24 @@ import be.minelabs.entity.projectile.thrown.ParticleEntity; import be.minelabs.item.Items; +import be.minelabs.item.items.BalloonItem; import net.minecraft.block.DispenserBlock; import net.minecraft.block.dispenser.ItemDispenserBehavior; import net.minecraft.block.dispenser.ProjectileDispenserBehavior; import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.projectile.ProjectileEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.SpawnEggItem; -import net.minecraft.util.math.BlockPointer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Position; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.util.math.*; import net.minecraft.world.World; import net.minecraft.world.event.GameEvent; +import java.util.List; + public class ExtraDispenserBehavior { /** * Main class method @@ -61,6 +63,23 @@ protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { return stack; } }); + + DispenserBlock.registerBehavior(Items.BALLOON, new ItemDispenserBehavior() { + protected ItemStack dispenseSilently(BlockPointer pointer, ItemStack stack) { + if(stack.getItem() instanceof BalloonItem bi) { + BlockPos blockPos = pointer.getPos().offset(pointer.getBlockState().get(DispenserBlock.FACING)); + List list = pointer.getWorld().getEntitiesByClass(LivingEntity.class, new Box(blockPos), EntityPredicates.EXCEPT_SPECTATOR); + if (list.isEmpty()) { + return super.dispenseSilently(pointer, stack); + } else { + LivingEntity livingEntity = list.get(0); + bi.useOnEntity(stack, null, livingEntity, null); + return stack; + } + } + return super.dispenseSilently(pointer, stack); + } + }); } /** diff --git a/src/main/java/be/minelabs/entity/Entities.java b/src/main/java/be/minelabs/entity/Entities.java index 31a00cbb7..be5f85208 100644 --- a/src/main/java/be/minelabs/entity/Entities.java +++ b/src/main/java/be/minelabs/entity/Entities.java @@ -36,7 +36,7 @@ public class Entities { .dimensions(EntityDimensions.fixed(0.6f, 1.7f)).build(), "entropy_creeper"); public static final EntityType BALLOON = register(FabricEntityTypeBuilder.create(SpawnGroup.MISC, BalloonEntity::new) - .dimensions(EntityDimensions.fixed(1.0f, 1.0f)).build(), "balloon"); + .dimensions(EntityDimensions.fixed(0.8f, 1.0f)).disableSummon().build(), "balloon"); public static final EntityType CORROSIVE_ENTITY = register(FabricEntityTypeBuilder.create(SpawnGroup.MISC, CorrosiveEntity::new) .dimensions(EntityDimensions.fixed(1.0f, 1.0f)).disableSummon().fireImmune().build(), "corrosive"); diff --git a/src/main/java/be/minelabs/entity/mob/BalloonEntity.java b/src/main/java/be/minelabs/entity/mob/BalloonEntity.java index 097a4e228..795b92bfd 100644 --- a/src/main/java/be/minelabs/entity/mob/BalloonEntity.java +++ b/src/main/java/be/minelabs/entity/mob/BalloonEntity.java @@ -2,51 +2,72 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.goal.SwimGoal; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.decoration.LeashKnotEntity; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; public class BalloonEntity extends MobEntity { private static final double MAX_DISTANCE = 10.0; private static final double MAX_HEIGHT = 320.0; + public static final double LEVITATION_SPEED = 0.11; - private float rotationY; private boolean helium = true; private double max_mob_distance = 2.0; - private MobEntity target = null; + private Entity owner = null; + + @Nullable + private NbtCompound ownerNbt; public BalloonEntity(EntityType entityType, World world) { super(entityType, world); - rotationY = getRandom().nextFloat() * 360.0F; - setHealth(1.0f); + setHealth(1.0F); setNoGravity(true); + setPersistent(); } - // GENERAL + // GETTERS AND SETTERS + public void setHelium(boolean new_value) { + helium = new_value; + } - public float getRotationY() { - return rotationY; + public boolean getHelium() { + return helium; } - @Override - public void onDeath(DamageSource source) { - detachLeash(true, false); - super.onDeath(source); + public Entity getOwner() { + return owner; } - public void setHelium(boolean new_value) { - helium = new_value; + + // GENERAL + public void heal(float amount) {} + + @Override + public boolean canPickupItem(ItemStack stack) { + return false; } - public boolean getHelium() { - return helium; + @Override + public void onDeath(DamageSource source) { + detachOwner(); + super.onDeath(source); } protected void initGoals() { @@ -69,69 +90,74 @@ protected boolean canStartRiding(Entity entity) { @Override protected void removeFromDimension() { super.removeFromDimension(); - this.detachLeash(true, false); + this.detachOwner(); } @Override public void tick() { super.tick(); - addStatusEffect(new StatusEffectInstance(StatusEffects.LEVITATION, 10, 3, false, false)); - MobEntity target = this.target; // Prevent crashes when the target becomes null during the tick + if (!this.world.isClient) { + updateOwner(); + } + + if(helium) { + setVelocity(new Vec3d(0.0, LEVITATION_SPEED, 0.0)); + } + Entity target = owner; // Prevent crashes when the target becomes null during the tick if(target != null) { - if(!target.isAlive()) { - kill(); - } - Vec3d mpos = getPos(); - if(mpos.getY() >= MAX_HEIGHT) { + if(getY() >= MAX_HEIGHT) { // POP GOES THE BALLOON kill(); return; } Vec3d tpos = target.getPos(); - double distance = tpos.distanceTo(mpos); + double distance = tpos.distanceTo(getPos()); if(distance >= MAX_DISTANCE) { // Fixes Issue #406 kill(); return; } + + // Stop the jiggle + double x = tpos.getX() - this.getX(); + double z = tpos.getZ() - this.getZ(); + double d2 = Math.sqrt(x*x+z*z); + double factor; + if(d2 < 0.2) { + factor = 0; + } else if(d2 <= 1) { + factor = d2 * d2; + } else { + factor = Math.sqrt(d2); + } + Vec3d xz = new Vec3d(x, 0.0, z).normalize().multiply(factor/10); + addVelocity(xz); + if(distance >= max_mob_distance) { - double x = (tpos.getX() - this.getX()) / (double)distance; - double y = (tpos.getY() - this.getY()) / (double)distance; - double z = (tpos.getZ() - this.getZ()) / (double)distance; - - // Fixes Issue #401 - Vec3d xz = new Vec3d(x, 0.0, z).normalize().multiply(-0.1); - Vec3d vertical = new Vec3d(0.0, Math.copySign(y * y, y), 0.0).normalize(); - if(target.isAiDisabled() || !helium) { - this.setVelocity(xz.add(vertical.multiply(0.1))); - } else if(this.target != null) { - this.target.setVelocity(xz.add(vertical.multiply(-0.18))); + if(!(target instanceof LeashKnotEntity) && !(target instanceof MobEntity && ((MobEntity) target).isAiDisabled())) { + // So apparently untamed rideables cannot be lifted in the air once saddled + // There is no reason for this, they just... can't... + // Let's just call this a feature for now... + if(getY() - tpos.getY() > max_mob_distance / 2.0) { + target.setVelocity(new Vec3d(0.0, LEVITATION_SPEED, 0.0)); + target.onLanding(); // prevent infinite fall damage accumulation + } + } else { + // Don't go up anymore + Vec3d vel = getVelocity(); + setVelocity(vel.x, 0.0, vel.z); } } + } else { + kill(); } } - // LEASH - - - @Override - public void attachLeash(Entity entity, boolean sendPacket) { - // By attaching a leash to the target, all positions appear to be valid - // Fixes Issues #404 and #399 - target = ((MobEntity) entity); - target.attachLeash(this, sendPacket); - Box bbox = entity.getBoundingBox(); - double a = bbox.getXLength(); - double b = bbox.getYLength(); - double c = bbox.getZLength(); - max_mob_distance = Math.sqrt(a * a + b * b + c * c); - } - + // LEASH & OWNER @Override - public void detachLeash(boolean sendPacket, boolean dropItem) { - super.detachLeash(sendPacket, dropItem); - target = null; + public boolean canBeLeashedBy(PlayerEntity player) { + return false; } public Vec3d getLeashOffset() { @@ -143,9 +169,75 @@ public boolean isLeashed() { return true; } + protected void updateOwner() { + if (ownerNbt != null) { + readOwnerNbt(); + } + + if(getHoldingEntity() != null) { + owner = getHoldingEntity(); + updateLeash(); + } + + if (owner != null) { + if (!isAlive() || !owner.isAlive()) { + detachOwner(); + kill(); + } + } + } + + public void attachOwner(Entity entity) { + // By attaching a leash to the target, all positions appear to be valid + // Fixes Issues #404 and #399 + owner = entity; + if (!this.world.isClient && world instanceof ServerWorld) { + // Send this to update all data? + attachLeash(owner, true); + } + + if(owner != null) { + ownerNbt = null; + if(owner instanceof LeashKnotEntity) { + max_mob_distance = 2.0; + } else if (owner instanceof MobEntity) { + // Longer leashes for larger mobs + Box bbox = entity.getBoundingBox(); + double a = bbox.getXLength(); + double b = bbox.getYLength(); + double c = bbox.getZLength(); + max_mob_distance = 1.3 * Math.sqrt(a * a + b * b + c * c); + } + } + } + + public void detachOwner() { + detachLeash(true, false); + if(owner != null && owner instanceof LeashKnotEntity) { + owner.discard(); + } + owner = null; + } + public void writeCustomDataToNbt(NbtCompound nbt) { super.writeCustomDataToNbt(nbt); nbt.putBoolean("CanLiftOff", helium); + if(owner != null) { + NbtCompound nbtCompound = new NbtCompound(); + if(owner instanceof LeashKnotEntity) { + BlockPos blockPos = ((LeashKnotEntity)owner).getDecorationBlockPos(); + nbtCompound.putInt("X", blockPos.getX()); + nbtCompound.putInt("Y", blockPos.getY()); + nbtCompound.putInt("Z", blockPos.getZ()); + } else { + UUID uUID = owner.getUuid(); + nbtCompound.putUuid("UUID", uUID); + } + + nbt.put("Owner", nbtCompound); + } else if (ownerNbt != null) { + nbt.put("Owner", ownerNbt.copy()); + } } public void readCustomDataFromNbt(NbtCompound nbt) { @@ -153,5 +245,23 @@ public void readCustomDataFromNbt(NbtCompound nbt) { if(nbt.contains("CanLiftOff")) { helium = nbt.getBoolean("CanLiftOff"); } + if(nbt.contains("Owner", 10)) { + ownerNbt = nbt.getCompound("Owner"); + } + } + + private void readOwnerNbt() { + if (ownerNbt != null && this.world instanceof ServerWorld) { + if (ownerNbt.containsUuid("UUID")) { + UUID uUID = ownerNbt.getUuid("UUID"); + Entity entity = ((ServerWorld)this.world).getEntity(uUID); + if (entity != null) { + attachOwner(entity); + } + } else if (ownerNbt.contains("X", 99) && ownerNbt.contains("Y", 99) && ownerNbt.contains("Z", 99)) { + BlockPos blockPos = NbtHelper.toBlockPos(ownerNbt); + attachOwner(LeashKnotEntity.getOrCreate(this.world, blockPos)); + } + } } } \ No newline at end of file diff --git a/src/main/java/be/minelabs/item/items/BalloonItem.java b/src/main/java/be/minelabs/item/items/BalloonItem.java index bb9e35ef8..1b8faeb3c 100644 --- a/src/main/java/be/minelabs/item/items/BalloonItem.java +++ b/src/main/java/be/minelabs/item/items/BalloonItem.java @@ -2,62 +2,104 @@ import be.minelabs.entity.mob.BalloonEntity; import be.minelabs.entity.Entities; +import net.minecraft.block.BlockState; +import net.minecraft.block.DispenserBlock; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.effect.StatusEffectInstance; -import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.entity.decoration.LeashKnotEntity; +import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; +import net.minecraft.item.*; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.util.*; +import net.minecraft.util.math.*; import net.minecraft.world.World; +import net.minecraft.world.event.GameEvent; +import java.util.List; public class BalloonItem extends Item { + public BalloonItem(Item.Settings settings) { super(settings); } - private BalloonEntity summon(World world, LivingEntity entity) { + private BalloonEntity summon(World world, Entity entity) { BalloonEntity balloon = Entities.BALLOON.create(world); - balloon.refreshPositionAndAngles(entity.getX(), entity.getY(), entity.getZ(), 0.0F, 0.0F); + balloon.refreshPositionAndAngles(entity.getX(), entity.getY() + 1.0, entity.getZ(), 0.0F, 0.0F); world.spawnEntity(balloon); - balloon.attachLeash(entity, true); return balloon; } @Override - public void inventoryTick(ItemStack itemStack, World world, Entity entity, int slot, boolean selected) { - super.inventoryTick(itemStack, world, entity, slot, selected); - if (!world.isClient()) { - if (entity instanceof PlayerEntity pe) { - if (!(pe.getAbilities().creativeMode)) { - if (pe.getOffHandStack().getItem() instanceof BalloonItem - || pe.getMainHandStack().getItem() instanceof BalloonItem) { - // TODO FIX custom effect (temp fix: use levitation) - //System.out.println("Try to fly: call effect"); - //StatusEffectInstance sei = new StatusEffectInstance(Effects.FLYING, 2, 2, false, false); - StatusEffectInstance sei = new StatusEffectInstance(StatusEffects.LEVITATION, 4, 2, false, false); - pe.addStatusEffect(sei); - } - } - } + public void usageTick(World world, LivingEntity user, ItemStack stack, int remainingUseTicks) { + if (user instanceof PlayerEntity pe) { + Vec3d vel = pe.getVelocity(); + user.setVelocity(vel.x * 1.02, BalloonEntity.LEVITATION_SPEED, vel.z * 1.02); + user.onLanding(); } } + public int getMaxUseTime(ItemStack stack) { + return 7200; + } + + @Override + public TypedActionResult use(World world, PlayerEntity user, Hand hand) { + user.setCurrentHand(hand); + return TypedActionResult.pass(user.getStackInHand(hand)); + } + @Override public ActionResult useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand) { if(!(entity instanceof PlayerEntity) && !(entity instanceof BalloonEntity)) { - World world = user.getWorld(); + World world = entity.getWorld(); if(!world.isClient) { - summon(world, entity); - stack.decrement(1); + List list = entity.getWorld().getEntitiesByClass(BalloonEntity.class, + new Box(entity.getBlockPos()).expand(10), EntityPredicates.EXCEPT_SPECTATOR); + boolean isLeashed = false; + for(BalloonEntity be: list) { + if(be.getOwner().equals(entity)) { + isLeashed = true; + break; + } + } + + if(!isLeashed) { + BalloonEntity be = summon(world, entity); + be.attachOwner(entity); + stack.decrement(1); + return ActionResult.success(true); + } } - return ActionResult.success(world.isClient); + return ActionResult.success(false); } - return ActionResult.PASS; + return super.useOnEntity(stack, user, entity, hand); } - // TODO: connect to fence? => see LeadItem + public ActionResult useOnBlock(ItemUsageContext context) { + World world = context.getWorld(); + BlockPos blockPos = context.getBlockPos(); + BlockState blockState = world.getBlockState(blockPos); + if (blockState.isIn(BlockTags.FENCES)) { + PlayerEntity playerEntity = context.getPlayer(); + if (!world.isClient && playerEntity != null) { + LeashKnotEntity leashKnotEntity = LeashKnotEntity.getOrCreate(world, blockPos); + leashKnotEntity.onPlace(); + BalloonEntity be = summon(world, leashKnotEntity); + be.attachOwner(leashKnotEntity); + + context.getStack().decrement(1); + + world.emitGameEvent(GameEvent.BLOCK_ATTACH, blockPos, GameEvent.Emitter.of(playerEntity)); + + return ActionResult.CONSUME; + } + + return ActionResult.success(world.isClient); + } else { + return super.useOnBlock(context); + } + } } diff --git a/src/main/java/be/minelabs/mixin/MobEntityMixin.java b/src/main/java/be/minelabs/mixin/MobEntityMixin.java new file mode 100644 index 000000000..b66c38a04 --- /dev/null +++ b/src/main/java/be/minelabs/mixin/MobEntityMixin.java @@ -0,0 +1,25 @@ +package be.minelabs.mixin; + +import be.minelabs.item.Items; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(MobEntity.class) +public abstract class MobEntityMixin { + @Inject(method="interactWithItem", at=@At(value="HEAD"), cancellable = true) + private void interactItemInject(PlayerEntity player, Hand hand, CallbackInfoReturnable ci) { + // Makes sure that Untamed Horses, Donkeys, Llamas and Camels are also balloonable + ItemStack itemStack = player.getStackInHand(hand); + if (itemStack.isOf(Items.BALLOON)) { + ActionResult ar = itemStack.getItem().useOnEntity(itemStack, player, ((MobEntity)((Object)(this))), hand); + ci.setReturnValue(ar); + } + } +} diff --git a/src/main/resources/assets/minelabs/models/item/balloon.json b/src/main/resources/assets/minelabs/models/item/balloon.json index a66f5ea10..2cfdd5e2a 100644 --- a/src/main/resources/assets/minelabs/models/item/balloon.json +++ b/src/main/resources/assets/minelabs/models/item/balloon.json @@ -1,127 +1,6 @@ { - "credit": "Made with Blockbench", - "texture_size": [32, 32], + "parent": "item/generated", "textures": { - "0": "minelabs:item/balloon_string", - "1": "minelabs:item/balloon", - "particle": "minelabs:item/balloon" - }, - "elements": [ - { - "from": [5, 11, 4], - "to": [11, 19, 10], - "faces": { - "north": {"uv": [0, 0, 3, 4], "texture": "#1"}, - "east": {"uv": [3, 0, 6, 4], "texture": "#1"}, - "south": {"uv": [0, 4, 3, 8], "texture": "#1"}, - "west": {"uv": [3, 4, 6, 8], "texture": "#1"}, - "up": {"uv": [9, 3, 6, 0], "texture": "#1"}, - "down": {"uv": [9, 3, 6, 6], "texture": "#1"} - } - }, - { - "from": [6, 13, 10], - "to": [10, 18, 11], - "faces": { - "east": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "south": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, - "west": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "up": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "down": {"uv": [0, 0, 2, 0.5], "texture": "#1"} - } - }, - { - "from": [4, 13, 5], - "to": [5, 18, 9], - "faces": { - "north": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "south": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "west": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, - "up": {"uv": [0, 0, 2, 0.5], "rotation": 270, "texture": "#1"}, - "down": {"uv": [0, 0, 2, 0.5], "rotation": 90, "texture": "#1"} - } - }, - { - "from": [6, 13, 3], - "to": [10, 18, 4], - "faces": { - "north": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, - "east": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "west": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "up": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "down": {"uv": [0, 0, 2, 0.5], "texture": "#1"} - } - }, - { - "from": [11, 13, 5], - "to": [12, 18, 9], - "faces": { - "north": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "east": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, - "south": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, - "west": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, - "up": {"uv": [0, 0, 2, 0.5], "rotation": 270, "texture": "#1"}, - "down": {"uv": [0, 0, 2, 0.5], "rotation": 90, "texture": "#1"} - } - }, - { - "from": [6, 10, 5], - "to": [10, 11, 9], - "faces": { - "north": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "east": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "south": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "west": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, - "down": {"uv": [0, 0, 2, 2], "texture": "#1"} - } - }, - { - "from": [7, 19, 6], - "to": [9, 20, 8], - "faces": { - "north": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, - "east": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, - "south": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, - "west": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, - "up": {"uv": [0, 0, 1, 1], "texture": "#1"}, - "down": {"uv": [0, 0, 1, 1], "texture": "#1"} - } - }, - { - "from": [8, -9, 7], - "to": [8.25, 10.25, 7.25], - "faces": { - "north": {"uv": [0, 0, 1, 11], "texture": "#0"}, - "east": {"uv": [1, 0, 2, 11], "texture": "#0"}, - "south": {"uv": [2, 0, 3, 11], "texture": "#0"}, - "west": {"uv": [3, 0, 4, 11], "texture": "#0"}, - "up": {"uv": [5, 1, 4, 0], "texture": "#0"}, - "down": {"uv": [5, 1, 4, 2], "texture": "#0"} - } - } - ], - "display": { - "thirdperson_righthand": { - "rotation": [69, 0, 0], - "translation": [0, 5.5, 17] - }, - "thirdperson_lefthand": { - "rotation": [69, 0, 0], - "translation": [0, 5.5, 17] - }, - "firstperson_righthand": { - "translation": [0, 17, 0], - "scale": [1, 1.1, 1] - }, - "firstperson_lefthand": { - "translation": [0, 17, 0], - "scale": [1, 1.1, 1] - }, - "gui": { - "translation": [0, -6.25, 0] - }, - "fixed": { - "translation": [0, -3.75, 0] - } + "layer0": "minelabs:item/balloon" } } \ No newline at end of file diff --git a/src/main/resources/assets/minelabs/models/item/balloon_3d.json b/src/main/resources/assets/minelabs/models/item/balloon_3d.json new file mode 100644 index 000000000..ecd6bf91a --- /dev/null +++ b/src/main/resources/assets/minelabs/models/item/balloon_3d.json @@ -0,0 +1,127 @@ +{ + "credit": "Made with Blockbench", + "texture_size": [32, 32], + "textures": { + "0": "minelabs:item/balloon_string", + "1": "minelabs:item/balloon_3d", + "particle": "minelabs:item/balloon" + }, + "elements": [ + { + "from": [5, 11, 4], + "to": [11, 19, 10], + "faces": { + "north": {"uv": [0, 0, 3, 4], "texture": "#1"}, + "east": {"uv": [3, 0, 6, 4], "texture": "#1"}, + "south": {"uv": [0, 4, 3, 8], "texture": "#1"}, + "west": {"uv": [3, 4, 6, 8], "texture": "#1"}, + "up": {"uv": [9, 3, 6, 0], "texture": "#1"}, + "down": {"uv": [9, 3, 6, 6], "texture": "#1"} + } + }, + { + "from": [6, 13, 10], + "to": [10, 18, 11], + "faces": { + "east": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "south": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, + "west": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "down": {"uv": [0, 0, 2, 0.5], "texture": "#1"} + } + }, + { + "from": [4, 13, 5], + "to": [5, 18, 9], + "faces": { + "north": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "south": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "west": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 0.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 0, 2, 0.5], "rotation": 90, "texture": "#1"} + } + }, + { + "from": [6, 13, 3], + "to": [10, 18, 4], + "faces": { + "north": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, + "east": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "west": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "down": {"uv": [0, 0, 2, 0.5], "texture": "#1"} + } + }, + { + "from": [11, 13, 5], + "to": [12, 18, 9], + "faces": { + "north": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "east": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, + "south": {"uv": [0, 0, 0.5, 2.5], "texture": "#1"}, + "west": {"uv": [0, 0, 2, 2.5], "texture": "#1"}, + "up": {"uv": [0, 0, 2, 0.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 0, 2, 0.5], "rotation": 90, "texture": "#1"} + } + }, + { + "from": [6, 10, 5], + "to": [10, 11, 9], + "faces": { + "north": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "east": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "south": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "west": {"uv": [0, 0, 2, 0.5], "texture": "#1"}, + "down": {"uv": [0, 0, 2, 2], "texture": "#1"} + } + }, + { + "from": [7, 19, 6], + "to": [9, 20, 8], + "faces": { + "north": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, + "east": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, + "south": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, + "west": {"uv": [0, 0, 1, 0.5], "texture": "#1"}, + "up": {"uv": [0, 0, 1, 1], "texture": "#1"}, + "down": {"uv": [0, 0, 1, 1], "texture": "#1"} + } + }, + { + "from": [8, -9, 7], + "to": [8.25, 10.25, 7.25], + "faces": { + "north": {"uv": [0, 0, 1, 11], "texture": "#0"}, + "east": {"uv": [1, 0, 2, 11], "texture": "#0"}, + "south": {"uv": [2, 0, 3, 11], "texture": "#0"}, + "west": {"uv": [3, 0, 4, 11], "texture": "#0"}, + "up": {"uv": [5, 1, 4, 0], "texture": "#0"}, + "down": {"uv": [5, 1, 4, 2], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [69, 0, 0], + "translation": [0, 5.5, 17] + }, + "thirdperson_lefthand": { + "rotation": [69, 0, 0], + "translation": [0, 5.5, 17] + }, + "firstperson_righthand": { + "translation": [0, 17, 0], + "scale": [1, 1.1, 1] + }, + "firstperson_lefthand": { + "translation": [0, 17, 0], + "scale": [1, 1.1, 1] + }, + "gui": { + "translation": [0, -6.25, 0] + }, + "fixed": { + "translation": [0, -3.75, 0] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/minelabs/textures/item/balloon.png b/src/main/resources/assets/minelabs/textures/item/balloon.png index d7cd51f29..dd5adee0d 100644 Binary files a/src/main/resources/assets/minelabs/textures/item/balloon.png and b/src/main/resources/assets/minelabs/textures/item/balloon.png differ diff --git a/src/main/resources/assets/minelabs/textures/item/balloon_3d.png b/src/main/resources/assets/minelabs/textures/item/balloon_3d.png new file mode 100644 index 000000000..d7cd51f29 Binary files /dev/null and b/src/main/resources/assets/minelabs/textures/item/balloon_3d.png differ diff --git a/src/main/resources/data/minelabs/loot_tables/entities/balloon.json b/src/main/resources/data/minelabs/loot_tables/entities/balloon.json new file mode 100644 index 000000000..5179639b0 --- /dev/null +++ b/src/main/resources/data/minelabs/loot_tables/entities/balloon.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:entity", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:leather", + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "min": 0, + "max": 1 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:string" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/minelabs.mixins.json b/src/main/resources/minelabs.mixins.json index dd0fa25b9..cac01e688 100644 --- a/src/main/resources/minelabs.mixins.json +++ b/src/main/resources/minelabs.mixins.json @@ -16,6 +16,7 @@ "FireMixin", "FishingBobberEntityAccessor", "IceBlockMixin", - "LivingEntityMixin" + "LivingEntityMixin", + "MobEntityMixin" ] } \ No newline at end of file