diff --git a/src/main/java/ru/betterend/client/render/ArmoredElytraLayer.java b/src/main/java/ru/betterend/client/render/ArmoredElytraLayer.java new file mode 100644 index 00000000..a03fc81e --- /dev/null +++ b/src/main/java/ru/betterend/client/render/ArmoredElytraLayer.java @@ -0,0 +1,52 @@ +package ru.betterend.client.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.entity.RenderLayerParent; +import net.minecraft.client.renderer.entity.layers.ElytraLayer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.PlayerModelPart; +import net.minecraft.world.item.ItemStack; +import ru.betterend.BetterEnd; +import ru.betterend.item.ArmoredElytra; +import ru.betterend.item.model.ArmoredElytraModel; +import ru.betterend.registry.EndItems; + +public class ArmoredElytraLayer> extends ElytraLayer { + private final ArmoredElytraModel elytraModel = new ArmoredElytraModel<>(); + + public ArmoredElytraLayer(RenderLayerParent renderLayerParent) { + super(renderLayerParent); + } + + public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, float f, float g, float h, float j, float k, float l) { + ItemStack itemStack = livingEntity.getItemBySlot(EquipmentSlot.CHEST); + if (itemStack.getItem() instanceof ArmoredElytra) { + ResourceLocation wingsTexture = ((ArmoredElytra) itemStack.getItem()).getWingTexture(); + if (livingEntity instanceof AbstractClientPlayer) { + AbstractClientPlayer abstractClientPlayer = (AbstractClientPlayer) livingEntity; + if (abstractClientPlayer.isElytraLoaded() && abstractClientPlayer.getElytraTextureLocation() != null) { + wingsTexture = abstractClientPlayer.getElytraTextureLocation(); + } else if (abstractClientPlayer.isCapeLoaded() && abstractClientPlayer.getCloakTextureLocation() != null && abstractClientPlayer.isModelPartShown(PlayerModelPart.CAPE)) { + wingsTexture = abstractClientPlayer.getCloakTextureLocation(); + } + } + + poseStack.pushPose(); + poseStack.translate(0.0D, 0.0D, 0.125D); + getParentModel().copyPropertiesTo(elytraModel); + elytraModel.setupAnim(livingEntity, f, g, j, k, l); + VertexConsumer vertexConsumer = ItemRenderer.getArmorFoilBuffer(multiBufferSource, RenderType.armorCutoutNoCull(wingsTexture), false, itemStack.hasFoil()); + elytraModel.renderToBuffer(poseStack, vertexConsumer, i, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + } +} diff --git a/src/main/java/ru/betterend/interfaces/BreakableItem.java b/src/main/java/ru/betterend/interfaces/BreakableItem.java new file mode 100644 index 00000000..7af4687b --- /dev/null +++ b/src/main/java/ru/betterend/interfaces/BreakableItem.java @@ -0,0 +1,5 @@ +package ru.betterend.interfaces; + +public interface BreakableItem { + void registerBrokenItem(); +} diff --git a/src/main/java/ru/betterend/item/ArmoredElytra.java b/src/main/java/ru/betterend/item/ArmoredElytra.java new file mode 100644 index 00000000..3308306d --- /dev/null +++ b/src/main/java/ru/betterend/item/ArmoredElytra.java @@ -0,0 +1,57 @@ +package ru.betterend.item; + +import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider; +import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry; +import net.minecraft.client.renderer.item.ItemPropertyFunction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.*; +import ru.betterend.BetterEnd; +import ru.betterend.interfaces.BreakableItem; +import ru.betterend.patterns.Patterned; +import ru.betterend.patterns.Patterns; +import ru.betterend.registry.EndItems; + +public class ArmoredElytra extends ElytraItem implements EquipmentSlotProvider, BreakableItem, Patterned { + + private final ResourceLocation wingTexture; + private final Item repairItem; + private final double movementFactor; + + public ArmoredElytra(String name, Item repairItem, int durability, double movementFactor, boolean fireproof) { + super(fireproof ? EndItems.makeItemSettings().durability(durability).rarity(Rarity.RARE).fireResistant() : + EndItems.makeItemSettings().durability(durability).rarity(Rarity.RARE)); + this.wingTexture = BetterEnd.makeID("textures/entity/" + name + ".png"); + this.repairItem = repairItem; + this.movementFactor = movementFactor; + } + + public double getMovementFactor() { + return movementFactor; + } + + public ResourceLocation getWingTexture() { + return wingTexture; + } + + @Override + public boolean isValidRepairItem(ItemStack itemStack, ItemStack itemStack2) { + return itemStack2.getItem() == repairItem; + } + + @Override + public void registerBrokenItem() { + FabricModelPredicateProviderRegistry.register(this, new ResourceLocation("broken"), + (itemStack, clientLevel, livingEntity) -> ElytraItem.isFlyEnabled(itemStack) ? 0.0F : 1.0F); + } + + @Override + public String getModelPattern(String name) { + return Patterns.createItemGenerated(name); + } + + @Override + public EquipmentSlot getPreferredEquipmentSlot(ItemStack stack) { + return EquipmentSlot.CHEST; + } +} diff --git a/src/main/java/ru/betterend/item/PatternedItem.java b/src/main/java/ru/betterend/item/PatternedItem.java index c25d9c6d..2d5dce7a 100644 --- a/src/main/java/ru/betterend/item/PatternedItem.java +++ b/src/main/java/ru/betterend/item/PatternedItem.java @@ -11,6 +11,6 @@ public class PatternedItem extends Item implements Patterned { @Override public String getModelPattern(String name) { - return Patterns.createJson(Patterns.ITEM_GENERATED, name); + return Patterns.createItemGenerated(name); } } diff --git a/src/main/java/ru/betterend/item/model/ArmoredElytraModel.java b/src/main/java/ru/betterend/item/model/ArmoredElytraModel.java new file mode 100644 index 00000000..3e6d785a --- /dev/null +++ b/src/main/java/ru/betterend/item/model/ArmoredElytraModel.java @@ -0,0 +1,73 @@ +package ru.betterend.item.model; + +import com.google.common.collect.ImmutableList; +import net.minecraft.client.model.AgeableListModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.phys.Vec3; + +public class ArmoredElytraModel extends AgeableListModel { + private final ModelPart rightWing; + private final ModelPart leftWing; + + public ArmoredElytraModel() { + this.leftWing = new ModelPart(this, 22, 0); + this.leftWing.addBox(-10.0F, 0.0F, 0.0F, 10.0F, 20.0F, 2.0F, 1.0F); + this.rightWing = new ModelPart(this, 22, 0); + this.rightWing.mirror = true; + this.rightWing.addBox(0.0F, 0.0F, 0.0F, 10.0F, 20.0F, 2.0F, 1.0F); + } + + protected Iterable headParts() { + return ImmutableList.of(); + } + + protected Iterable bodyParts() { + return ImmutableList.of(leftWing, rightWing); + } + + public void setupAnim(T livingEntity, float f, float g, float h, float i, float j) { + float rotX = 0.2617994F; + float rotZ = -0.2617994F; + float rotY = 0.0F; + float wingY = 0.0F; + if (livingEntity.isFallFlying()) { + float coef = 1.0F; + Vec3 vec3 = livingEntity.getDeltaMovement(); + if (vec3.y < 0.0D) { + Vec3 normalized = vec3.normalize(); + coef = 1.0F - (float) Math.pow(-normalized.y, 2.5D); + } + rotX = coef * 0.34906584F + (1.0F - coef) * rotX; + rotZ = coef * -1.5707964F + (1.0F - coef) * rotZ; + } else if (livingEntity.isCrouching()) { + rotX = 0.6981317F; + rotZ = -0.7853982F; + rotY = 0.08726646F; + wingY = 3.0F; + } + + leftWing.x = 5.0F; + leftWing.y = wingY; + if (livingEntity instanceof AbstractClientPlayer) { + AbstractClientPlayer abstractClientPlayer = (AbstractClientPlayer) livingEntity; + abstractClientPlayer.elytraRotX = (float) ((double) abstractClientPlayer.elytraRotX + (double) (rotX - abstractClientPlayer.elytraRotX) * 0.1D); + abstractClientPlayer.elytraRotY = (float) ((double) abstractClientPlayer.elytraRotY + (double) (rotY - abstractClientPlayer.elytraRotY) * 0.1D); + abstractClientPlayer.elytraRotZ = (float) ((double) abstractClientPlayer.elytraRotZ + (double) (rotZ - abstractClientPlayer.elytraRotZ) * 0.1D); + leftWing.xRot = abstractClientPlayer.elytraRotX; + leftWing.yRot = abstractClientPlayer.elytraRotY; + leftWing.zRot = abstractClientPlayer.elytraRotZ; + } else { + leftWing.xRot = rotX; + leftWing.zRot = rotZ; + leftWing.yRot = rotY; + } + + rightWing.x = -leftWing.x; + rightWing.yRot = -leftWing.yRot; + rightWing.y = leftWing.y; + rightWing.xRot = leftWing.xRot; + rightWing.zRot = -leftWing.zRot; + } +} diff --git a/src/main/java/ru/betterend/mixin/client/CapeLayerMixin.java b/src/main/java/ru/betterend/mixin/client/CapeLayerMixin.java new file mode 100644 index 00000000..22a68c0e --- /dev/null +++ b/src/main/java/ru/betterend/mixin/client/CapeLayerMixin.java @@ -0,0 +1,25 @@ +package ru.betterend.mixin.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.layers.CapeLayer; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +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.CallbackInfo; +import ru.betterend.item.ArmoredElytra; + +@Mixin(CapeLayer.class) +public class CapeLayerMixin { + + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + public void be_checkCustomElytra(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, AbstractClientPlayer abstractClientPlayer, float f, float g, float h, float j, float k, float l, CallbackInfo info) { + ItemStack itemStack = abstractClientPlayer.getItemBySlot(EquipmentSlot.CHEST); + if (itemStack.getItem() instanceof ArmoredElytra) { + info.cancel(); + } + } +} diff --git a/src/main/java/ru/betterend/mixin/client/LocalPlayerMixin.java b/src/main/java/ru/betterend/mixin/client/LocalPlayerMixin.java new file mode 100644 index 00000000..10029dee --- /dev/null +++ b/src/main/java/ru/betterend/mixin/client/LocalPlayerMixin.java @@ -0,0 +1,45 @@ +package ru.betterend.mixin.client; + +import com.mojang.authlib.GameProfile; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.player.Input; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ElytraItem; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Final; +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.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import ru.betterend.item.ArmoredElytra; +import ru.betterend.registry.EndItems; + +import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER; + +@Mixin(LocalPlayer.class) +public abstract class LocalPlayerMixin extends AbstractClientPlayer { + + public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) { + super(clientLevel, gameProfile); + } + + @Final + @Shadow + public ClientPacketListener connection; + + @Inject(method = "aiStep", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/player/LocalPlayer;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;", + shift = Shift.AFTER)) + public void be_aiStep(CallbackInfo info) { + ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST); + if (itemStack.getItem() instanceof ArmoredElytra && ElytraItem.isFlyEnabled(itemStack) && tryToStartFallFlying()) { + connection.send(new ServerboundPlayerCommandPacket(LocalPlayer.class.cast(this), ServerboundPlayerCommandPacket.Action.START_FALL_FLYING)); + } + } +} diff --git a/src/main/java/ru/betterend/mixin/client/PlayerRendererMixin.java b/src/main/java/ru/betterend/mixin/client/PlayerRendererMixin.java new file mode 100644 index 00000000..176159f2 --- /dev/null +++ b/src/main/java/ru/betterend/mixin/client/PlayerRendererMixin.java @@ -0,0 +1,25 @@ +package ru.betterend.mixin.client; + +import net.minecraft.client.model.PlayerModel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +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.CallbackInfo; +import ru.betterend.client.render.ArmoredElytraLayer; + +@Mixin(PlayerRenderer.class) +public abstract class PlayerRendererMixin extends LivingEntityRenderer> { + + public PlayerRendererMixin(EntityRenderDispatcher entityRenderDispatcher, PlayerModel entityModel, float f) { + super(entityRenderDispatcher, entityModel, f); + } + + @Inject(method = "(Lnet/minecraft/client/renderer/entity/EntityRenderDispatcher;Z)V", at = @At("TAIL")) + public void be_addCustomLayer(EntityRenderDispatcher entityRenderDispatcher, boolean bl, CallbackInfo info) { + addLayer(new ArmoredElytraLayer<>(PlayerRenderer.class.cast(this))); + } +} diff --git a/src/main/java/ru/betterend/mixin/common/LivingEntityMixin.java b/src/main/java/ru/betterend/mixin/common/LivingEntityMixin.java index 71777ea6..8dab1946 100644 --- a/src/main/java/ru/betterend/mixin/common/LivingEntityMixin.java +++ b/src/main/java/ru/betterend/mixin/common/LivingEntityMixin.java @@ -2,24 +2,59 @@ package ru.betterend.mixin.common; import java.util.Collection; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.animal.FlyingAnimal; +import net.minecraft.world.item.ElytraItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; 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.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.item.Item; +import ru.betterend.item.ArmoredElytra; +import ru.betterend.registry.EndItems; @Mixin(LivingEntity.class) -public abstract class LivingEntityMixin { +public abstract class LivingEntityMixin extends Entity { + + public LivingEntityMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + @Shadow + protected int fallFlyTicks; + + @Shadow + public abstract boolean hasEffect(MobEffect mobEffect); + + @Shadow + public abstract ItemStack getItemBySlot(EquipmentSlot equipmentSlot); + + @Shadow + public abstract void calculateEntityAnimation(LivingEntity livingEntity, boolean b); + + @Shadow + protected abstract SoundEvent getFallDamageSound(int i); + + @Shadow + public abstract boolean isFallFlying(); + private Entity lastAttacker; - + @Inject(method = "hurt", at = @At("HEAD")) public void be_hurt(DamageSource source, float amount, CallbackInfoReturnable info) { this.lastAttacker = source.getEntity(); @@ -33,7 +68,90 @@ public abstract class LivingEntityMixin { } return value; } - + + @Inject(method = "updateFallFlying", at = @At("HEAD"), cancellable = true) + private void be_updateFallFlying(CallbackInfo info) { + ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST); + if (!level.isClientSide && itemStack.getItem() instanceof ArmoredElytra) { + boolean isFlying = getSharedFlag(7); + if (isFlying && !onGround && !isPassenger() && !hasEffect(MobEffects.LEVITATION)) { + if (ElytraItem.isFlyEnabled(itemStack)) { + if ((fallFlyTicks + 1) % 20 == 0) { + itemStack.hurtAndBreak(1, LivingEntity.class.cast(this), (livingEntity) -> { + livingEntity.broadcastBreakEvent(EquipmentSlot.CHEST); + }); + } + isFlying = true; + } else { + isFlying = false; + } + } else { + isFlying = false; + } + setSharedFlag(7, isFlying); + info.cancel(); + } + } + + @Inject(method = "travel", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;isFallFlying()Z", + shift = Shift.AFTER), cancellable = true) + public void be_travel(Vec3 vec3, CallbackInfo info) { + ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST); + if (isFallFlying() && itemStack.getItem() instanceof ArmoredElytra) { + Vec3 moveDelta = getDeltaMovement(); + if (moveDelta.y > -0.5D) { + fallDistance = 1.0F; + } + + Vec3 lookAngle = getLookAngle(); + double d = 0.08D; + float rotX = xRot * 0.017453292F; + double k = Math.sqrt(lookAngle.x * lookAngle.x + lookAngle.z * lookAngle.z); + double l = Math.sqrt(getHorizontalDistanceSqr(moveDelta)); + double lookLen = lookAngle.length(); + float n = Mth.cos(rotX); + n = (float) (n * n * Math.min(1.0D, lookLen / 0.4D)); + moveDelta = getDeltaMovement().add(0.0D, d * (-1.0D + (double) n * 0.75D), 0.0D); + double coef; + if (moveDelta.y < 0.0D && k > 0.0D) { + coef = moveDelta.y * -0.1D * (double) n; + moveDelta = moveDelta.add(lookAngle.x * coef / k, coef, lookAngle.z * coef / k); + } + + if (rotX < 0.0F && k > 0.0D) { + coef = l * (double) (-Mth.sin(rotX)) * 0.04D; + moveDelta = moveDelta.add(-lookAngle.x * coef / k, coef * 3.2D, -lookAngle.z * coef / k); + } + + if (k > 0.0D) { + moveDelta = moveDelta.add((lookAngle.x / k * l - moveDelta.x) * 0.1D, 0.0D, (lookAngle.z / k * l - moveDelta.z) * 0.1D); + } + moveDelta = moveDelta.multiply(0.9900000095367432D, 0.9800000190734863D, 0.9900000095367432D); + double movementFactor = ((ArmoredElytra) itemStack.getItem()).getMovementFactor(); + moveDelta = moveDelta.multiply(movementFactor, 1.0D, movementFactor); + setDeltaMovement(moveDelta); + move(MoverType.SELF, moveDelta); + if (!level.isClientSide) { + if (horizontalCollision) { + coef = Math.sqrt(getHorizontalDistanceSqr(moveDelta)); + double r = l - coef; + float dmg = (float) (r * 10.0D - 3.0D); + if (dmg > 0.0F) { + playSound(getFallDamageSound((int) dmg), 1.0F, 1.0F); + hurt(DamageSource.FLY_INTO_WALL, dmg); + } + } + if (onGround) { + setSharedFlag(7, false); + } + } + + calculateEntityAnimation(LivingEntity.class.cast(this), this instanceof FlyingAnimal); + info.cancel(); + } + } + private double be_getKnockback(Item tool) { if (tool == null) return 0.0D; Collection modifiers = tool.getDefaultAttributeModifiers(EquipmentSlot.MAINHAND).get(Attributes.ATTACK_KNOCKBACK); diff --git a/src/main/java/ru/betterend/mixin/common/PlayerMixin.java b/src/main/java/ru/betterend/mixin/common/PlayerMixin.java index 7b177e3a..e2a2fca3 100644 --- a/src/main/java/ru/betterend/mixin/common/PlayerMixin.java +++ b/src/main/java/ru/betterend/mixin/common/PlayerMixin.java @@ -2,7 +2,18 @@ package ru.betterend.mixin.common; import java.util.Optional; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ElytraItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; 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.CallbackInfoReturnable; @@ -15,14 +26,21 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import ru.betterend.blocks.BlockProperties; import ru.betterend.blocks.BlockProperties.TripleShape; +import ru.betterend.item.ArmoredElytra; import ru.betterend.registry.EndBlocks; +import ru.betterend.registry.EndItems; import ru.betterend.util.BlocksHelper; import ru.betterend.util.MHelper; @Mixin(Player.class) -public abstract class PlayerMixin { +public abstract class PlayerMixin extends LivingEntity { + + protected PlayerMixin(EntityType entityType, Level level) { + super(entityType, level); + } + private static Direction[] horizontal; - + @Inject(method = "findRespawnPositionAndUseSpawnBlock", at = @At(value = "HEAD"), cancellable = true) private static void be_findRespawnPositionAndUseSpawnBlock(ServerLevel world, BlockPos pos, float f, boolean bl, boolean bl2, CallbackInfoReturnable> info) { BlockState blockState = world.getBlockState(pos); @@ -32,6 +50,17 @@ public abstract class PlayerMixin { } } + @Inject(method = "tryToStartFallFlying", at = @At("HEAD"), cancellable = true) + public void be_tryToStartFlying(CallbackInfoReturnable info) { + if (!onGround && !isFallFlying() && !isInWater() && !hasEffect(MobEffects.LEVITATION)) { + ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST); + if (itemStack.getItem() instanceof ArmoredElytra && ElytraItem.isFlyEnabled(itemStack)) { + setSharedFlag(7, true); + info.setReturnValue(true); + } + } + } + private static Optional be_obeliskRespawnPosition(ServerLevel world, BlockPos pos, BlockState state) { if (state.getValue(BlockProperties.TRIPLE_SHAPE) == TripleShape.TOP) { pos = pos.below(2); diff --git a/src/main/java/ru/betterend/patterns/Patterns.java b/src/main/java/ru/betterend/patterns/Patterns.java index 53851122..f3f452e0 100644 --- a/src/main/java/ru/betterend/patterns/Patterns.java +++ b/src/main/java/ru/betterend/patterns/Patterns.java @@ -116,6 +116,10 @@ public class Patterns { public final static ResourceLocation ITEM_GENERATED = BetterEnd.makeID("patterns/item/pattern_item_generated.json"); public final static ResourceLocation ITEM_HANDHELD = BetterEnd.makeID("patterns/item/pattern_item_handheld.json"); public final static ResourceLocation ITEM_SPAWN_EGG = BetterEnd.makeID("patterns/item/pattern_item_spawn_egg.json"); + + public static String createItemGenerated(String name) { + return createJson(ITEM_GENERATED, name); + } public static String createJson(Reader data, String parent, String block) { try (BufferedReader buffer = new BufferedReader(data)) { diff --git a/src/main/java/ru/betterend/recipe/SmithingRecipes.java b/src/main/java/ru/betterend/recipe/SmithingRecipes.java index 0da63634..4f1117c2 100644 --- a/src/main/java/ru/betterend/recipe/SmithingRecipes.java +++ b/src/main/java/ru/betterend/recipe/SmithingRecipes.java @@ -73,15 +73,20 @@ public class SmithingRecipes { .build(); SmithingTableRecipe.create("thallasium_anvil_updrade") - .setResult(EndBlocks.TERMINITE.anvil) - .setBase(EndBlocks.THALLASIUM.anvil) - .setAddition(EndBlocks.TERMINITE.block) - .build(); - + .setResult(EndBlocks.TERMINITE.anvil) + .setBase(EndBlocks.THALLASIUM.anvil) + .setAddition(EndBlocks.TERMINITE.block) + .build(); SmithingTableRecipe.create("terminite_anvil_updrade") - .setResult(EndBlocks.AETERNIUM_ANVIL) - .setBase(EndBlocks.TERMINITE.anvil) - .setAddition(EndBlocks.AETERNIUM_BLOCK) - .build(); + .setResult(EndBlocks.AETERNIUM_ANVIL) + .setBase(EndBlocks.TERMINITE.anvil) + .setAddition(EndBlocks.AETERNIUM_BLOCK) + .build(); + + SmithingTableRecipe.create("armored_elytra") + .setResult(EndItems.ARMORED_ELYTRA) + .setBase(Items.ELYTRA) + .setAddition(EndItems.AETERNIUM_INGOT) + .build(); } } diff --git a/src/main/java/ru/betterend/registry/EndItems.java b/src/main/java/ru/betterend/registry/EndItems.java index d3fbe6cf..dedae143 100644 --- a/src/main/java/ru/betterend/registry/EndItems.java +++ b/src/main/java/ru/betterend/registry/EndItems.java @@ -4,6 +4,7 @@ import java.util.List; import com.google.common.collect.Lists; +import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry; import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; import net.minecraft.core.BlockSource; import net.minecraft.core.Direction; @@ -19,28 +20,13 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.food.FoodProperties; import net.minecraft.world.food.Foods; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; +import net.minecraft.world.item.*; import net.minecraft.world.item.Item.Properties; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.Rarity; -import net.minecraft.world.item.ShovelItem; -import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.item.SwordItem; -import net.minecraft.world.item.TieredItem; -import net.minecraft.world.item.Tiers; import net.minecraft.world.level.block.DispenserBlock; import ru.betterend.BetterEnd; import ru.betterend.config.Configs; -import ru.betterend.item.DrinkItem; -import ru.betterend.item.EnchantedPetalItem; -import ru.betterend.item.EndArmorItem; -import ru.betterend.item.EndBucketItem; -import ru.betterend.item.EndSpawnEggItem; -import ru.betterend.item.EternalCrystalItem; -import ru.betterend.item.PatternedDiscItem; -import ru.betterend.item.PatternedItem; +import ru.betterend.interfaces.BreakableItem; +import ru.betterend.item.*; import ru.betterend.item.material.EndArmorMaterial; import ru.betterend.item.material.EndToolMaterial; import ru.betterend.item.tool.EndAxeItem; @@ -89,7 +75,8 @@ public class EndItems { public static final Item CRYSTALITE_CHESTPLATE = registerItem("crystalite_chestplate", new EndArmorItem(EndArmorMaterial.CRYSTALITE, EquipmentSlot.CHEST, makeItemSettings().rarity(Rarity.UNCOMMON))); public static final Item CRYSTALITE_LEGGINGS = registerItem("crystalite_leggings", new EndArmorItem(EndArmorMaterial.CRYSTALITE, EquipmentSlot.LEGS, makeItemSettings().rarity(Rarity.UNCOMMON))); public static final Item CRYSTALITE_BOOTS = registerItem("crystalite_boots", new EndArmorItem(EndArmorMaterial.CRYSTALITE, EquipmentSlot.FEET, makeItemSettings().rarity(Rarity.UNCOMMON))); - + public static final Item ARMORED_ELYTRA = registerItem("elytra_armored", new ArmoredElytra("elytra_armored", AETERNIUM_INGOT, 700, 0.96D, true)); + // Tools // public static final TieredItem AETERNIUM_SHOVEL = registerTool("aeternium_shovel", new EndShovelItem(EndToolMaterial.AETERNIUM, 1.5F, -3.0F, makeItemSettings().fireResistant())); public static final TieredItem AETERNIUM_SWORD = registerTool("aeternium_sword", new EndSwordItem(EndToolMaterial.AETERNIUM, 3, -2.4F, makeItemSettings().fireResistant())); @@ -152,6 +139,9 @@ public class EndItems { return item; } registerItem(id, item, MOD_ITEMS); + if (item instanceof BreakableItem) { + ((BreakableItem) item).registerBrokenItem(); + } return item; } @@ -193,7 +183,7 @@ public class EndItems { } else if (item instanceof EndHoeItem) { TagHelper.addTag((Tag.Named) FabricToolTags.HOES, item); } else if (item instanceof EndHammerItem) { - TagHelper.addTag((Tag.Named) EndTags.HAMMERS, item); + TagHelper.addTag(EndTags.HAMMERS, item); } return item; diff --git a/src/main/resources/assets/betterend/lang/en_us.json b/src/main/resources/assets/betterend/lang/en_us.json index a7f86e22..7bf6793f 100644 --- a/src/main/resources/assets/betterend/lang/en_us.json +++ b/src/main/resources/assets/betterend/lang/en_us.json @@ -75,6 +75,7 @@ "item.betterend.aeternium_sword_handle": "Aeternium Sword Handle", "item.betterend.leather_stripe": "Leather Stripe", "item.betterend.leather_wrapped_stick": "Leather Wrapped Stick", + "item.betterend.elytra_armored": "Armored Elytra", "effect.betterend.end_veil": "End Veil", "enchantment.betterend.end_veil": "End Veil", diff --git a/src/main/resources/assets/betterend/lang/ru_ru.json b/src/main/resources/assets/betterend/lang/ru_ru.json index c5366cb6..acb9df25 100644 --- a/src/main/resources/assets/betterend/lang/ru_ru.json +++ b/src/main/resources/assets/betterend/lang/ru_ru.json @@ -75,6 +75,7 @@ "item.betterend.aeternium_sword_handle": "Рукоятка этериевого меча", "item.betterend.leather_stripe": "Полоска кожи", "item.betterend.leather_wrapped_stick": "Обернутая кожей палка", + "item.betterend.elytra_armored": "Армированная Элитра", "effect.betterend.end_veil": "Вуаль Края", "enchantment.betterend.end_veil": "Вуаль Края", diff --git a/src/main/resources/assets/betterend/textures/entity/elytra_armored.png b/src/main/resources/assets/betterend/textures/entity/elytra_armored.png new file mode 100644 index 00000000..c5540b5d Binary files /dev/null and b/src/main/resources/assets/betterend/textures/entity/elytra_armored.png differ diff --git a/src/main/resources/assets/betterend/textures/item/elytra_armored.png b/src/main/resources/assets/betterend/textures/item/elytra_armored.png new file mode 100644 index 00000000..3d13a928 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/item/elytra_armored.png differ diff --git a/src/main/resources/betterend.mixins.client.json b/src/main/resources/betterend.mixins.client.json index 7da8b81b..de7b097c 100644 --- a/src/main/resources/betterend.mixins.client.json +++ b/src/main/resources/betterend.mixins.client.json @@ -13,11 +13,14 @@ "ModelVariantMapMixin", "MinecraftClientMixin", "ContextGsonAccessor", + "PlayerRendererMixin", "WorldRendererMixin", "MusicTrackerMixin", "AnvilScreenMixin", "BiomeColorsMixin", - "ModelLoaderMixin" + "ModelLoaderMixin", + "LocalPlayerMixin", + "CapeLayerMixin" ], "injectors": { "defaultRequire": 1