Armored Elytra
This commit is contained in:
parent
d5a93cb730
commit
bbf7169ccc
18 changed files with 472 additions and 39 deletions
25
src/main/java/ru/betterend/mixin/client/CapeLayerMixin.java
Normal file
25
src/main/java/ru/betterend/mixin/client/CapeLayerMixin.java
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
|
||||
|
||||
public PlayerRendererMixin(EntityRenderDispatcher entityRenderDispatcher, PlayerModel<AbstractClientPlayer> entityModel, float f) {
|
||||
super(entityRenderDispatcher, entityModel, f);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(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)));
|
||||
}
|
||||
}
|
|
@ -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<Boolean> 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<AttributeModifier> modifiers = tool.getDefaultAttributeModifiers(EquipmentSlot.MAINHAND).get(Attributes.ATTACK_KNOCKBACK);
|
||||
|
|
|
@ -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<? extends LivingEntity> 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<Optional<Vec3>> 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<Boolean> 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<Vec3> be_obeliskRespawnPosition(ServerLevel world, BlockPos pos, BlockState state) {
|
||||
if (state.getValue(BlockProperties.TRIPLE_SHAPE) == TripleShape.TOP) {
|
||||
pos = pos.below(2);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue