package com.atsuishio.superbwarfare.entity; import com.atsuishio.superbwarfare.Mod; import com.atsuishio.superbwarfare.component.ModDataComponents; import com.atsuishio.superbwarfare.entity.projectile.MortarShellEntity; import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity; import com.atsuishio.superbwarfare.init.ModEntities; import com.atsuishio.superbwarfare.init.ModItems; import com.atsuishio.superbwarfare.init.ModSounds; import com.atsuishio.superbwarfare.item.common.ammo.MortarShell; import com.atsuishio.superbwarfare.tools.RangeTool; import net.minecraft.ChatFormatting; import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.items.ItemHandlerHelper; import org.jetbrains.annotations.NotNull; import org.joml.Math; import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.animation.*; import software.bernie.geckolib.util.GeckoLibUtil; public class MortarEntity extends VehicleEntity implements GeoEntity { public static final EntityDataAccessor FIRE_TIME = SynchedEntityData.defineId(MortarEntity.class, EntityDataSerializers.INT); public static final EntityDataAccessor PITCH = SynchedEntityData.defineId(MortarEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor YAW = SynchedEntityData.defineId(MortarEntity.class, EntityDataSerializers.FLOAT); private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); public MortarEntity(EntityType type, Level level) { super(type, level); } public MortarEntity(Level level, float yRot) { super(ModEntities.MORTAR.get(), level); this.setYRot(yRot); this.entityData.set(YAW, yRot); } @Override protected void defineSynchedData(SynchedEntityData.Builder builder) { super.defineSynchedData(builder); builder.define(FIRE_TIME, 0) .define(PITCH, -70f) .define(YAW, this.getYRot()); } @Override public boolean canBeCollidedWith() { return false; } @Override public boolean isPickable() { return super.isPickable(); } @Override public double getEyeY() { return 0.2F; } @Override public boolean sendFireStarParticleOnHurt() { return false; } @Override public void addAdditionalSaveData(CompoundTag compound) { super.addAdditionalSaveData(compound); compound.putFloat("Pitch", this.entityData.get(PITCH)); compound.putFloat("Yaw", this.entityData.get(YAW)); } @Override public void readAdditionalSaveData(CompoundTag compound) { super.readAdditionalSaveData(compound); if (compound.contains("Pitch")) { this.entityData.set(PITCH, compound.getFloat("Pitch")); } if (compound.contains("Yaw")) { this.entityData.set(YAW, compound.getFloat("Yaw")); } } @Override public @NotNull InteractionResult interact(Player player, @NotNull InteractionHand hand) { ItemStack stack = player.getMainHandItem(); if (stack.getItem() instanceof MortarShell shell && !player.isShiftKeyDown() && this.entityData.get(FIRE_TIME) == 0) { this.entityData.set(FIRE_TIME, 25); if (!player.isCreative()) { stack.shrink(1); } if (!this.level().isClientSide()) { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), ModSounds.MORTAR_LOAD.get(), SoundSource.PLAYERS, 1f, 1f); this.level().playSound(null, this.getX(), this.getY(), this.getZ(), ModSounds.MORTAR_FIRE.get(), SoundSource.PLAYERS, 8f, 1f); this.level().playSound(null, this.getX(), this.getY(), this.getZ(), ModSounds.MORTAR_DISTANT.get(), SoundSource.PLAYERS, 32f, 1f); } Mod.queueServerWork(20, () -> { Level level = this.level(); if (level instanceof ServerLevel server) { MortarShellEntity entityToSpawn = shell.createShell(player, level, stack); entityToSpawn.setPos(this.getX(), this.getY() + this.getEyeY(), this.getZ()); entityToSpawn.shoot(this.getLookAngle().x, this.getLookAngle().y, this.getLookAngle().z, 11.4f, (float) 0.1); level.addFreshEntity(entityToSpawn); server.sendParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, (this.getX() + 3 * this.getLookAngle().x), (this.getY() + 0.1 + 3 * this.getLookAngle().y), (this.getZ() + 3 * this.getLookAngle().z), 8, 0.4, 0.4, 0.4, 0.007); server.sendParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, this.getX(), this.getY(), this.getZ(), 50, 2, 0.02, 2, 0.0005); } }); } if (player.getMainHandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) { if (setTarget(player.getMainHandItem())) { player.swing(InteractionHand.MAIN_HAND); return InteractionResult.SUCCESS; } else { player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true); return InteractionResult.FAIL; } } if (player.getOffhandItem().getItem() == ModItems.FIRING_PARAMETERS.get()) { if (setTarget(player.getOffhandItem())) { player.swing(InteractionHand.OFF_HAND); return InteractionResult.SUCCESS; } else { player.displayClientMessage(Component.translatable("tips.superbwarfare.mortar.warn").withStyle(ChatFormatting.RED), true); return InteractionResult.FAIL; } } if (player.isShiftKeyDown()) { if (stack.getItem() == ModItems.CROWBAR.get()) { this.discard(); ItemHandlerHelper.giveItemToPlayer(player, new ItemStack(ModItems.MORTAR_DEPLOYER.get())); return InteractionResult.SUCCESS; } entityData.set(YAW, player.getYRot()); } return InteractionResult.SUCCESS; } public boolean setTarget(ItemStack stack) { var parameters = stack.get(ModDataComponents.FIRING_PARAMETERS); if (parameters == null) return false; var pos = parameters.pos(); double targetX = pos.getX(); double targetY = pos.getY(); double targetZ = pos.getZ(); var isDepressed = parameters.isDepressed(); if (!RangeTool.canReach(11.4, 0.146, this.getEyePosition(), new Vec3(targetX, targetY, targetZ), 20, 89, isDepressed)) { return false; } this.look(new Vec3(targetX, targetY, targetZ)); entityData.set(PITCH, (float) -RangeTool.calculateAngle( 11.4, 0.146, this.getEyePosition(), new Vec3(targetX, targetY, targetZ), parameters.isDepressed() )); return true; } private void look(Vec3 pTarget) { Vec3 vec3 = EntityAnchorArgument.Anchor.EYES.apply(this); double d0 = (pTarget.x - vec3.x) * 0.2; double d2 = (pTarget.z - vec3.z) * 0.2; entityData.set(YAW, Mth.wrapDegrees((float) (Mth.atan2(d2, d0) * 57.2957763671875) - 90.0F)); } @Override public @NotNull Vec3 getDeltaMovement() { return new Vec3(0, Math.min(super.getDeltaMovement().y, 0), 0); } @Override public void baseTick() { super.baseTick(); if (this.entityData.get(FIRE_TIME) > 0) { this.entityData.set(FIRE_TIME, this.entityData.get(FIRE_TIME) - 1); } this.move(MoverType.SELF, this.getDeltaMovement()); if (this.onGround()) { this.setDeltaMovement(Vec3.ZERO); } else { this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04, 0.0)); } } @Override public void handleClientSync() { if (isControlledByLocalInstance()) { interpolationSteps = 0; syncPacketPositionCodec(getX(), getY(), getZ()); } if (interpolationSteps <= 0) { return; } double interpolatedYaw = Mth.wrapDegrees(serverYRot - (double) getYRot()); setYRot(getYRot() + (float) interpolatedYaw / (float) interpolationSteps); setXRot(getXRot() + (float) (serverXRot - (double) getXRot()) / (float) interpolationSteps); setRot(getYRot(), getXRot()); } @Override public void lerpTo(double x, double y, double z, float yaw, float pitch, int interpolationSteps) { serverYRot = yaw; serverXRot = pitch; this.interpolationSteps = 10; } @Override public void travel() { float diffY = Mth.wrapDegrees(entityData.get(YAW) - this.getYRot()); float diffX = Mth.wrapDegrees(entityData.get(PITCH) - this.getXRot()); this.setYRot(this.getYRot() + Mth.clamp(0.5f * diffY, -20f, 20f)); this.setXRot(Mth.clamp(this.getXRot() + Mth.clamp(0.5f * diffX, -20f, 20f), -89, -20)); } private PlayState movementPredicate(AnimationState event) { if (this.entityData.get(FIRE_TIME) > 0) { return event.setAndContinue(RawAnimation.begin().thenLoop("animation.mortar.fire")); } return event.setAndContinue(RawAnimation.begin().thenLoop("animation.mortar.idle")); } @Override public void destroy() { if (this.level() instanceof ServerLevel level) { var x = this.getX(); var y = this.getY(); var z = this.getZ(); level.explode(null, x, y, z, 0, Level.ExplosionInteraction.NONE); ItemEntity mortar = new ItemEntity(level, x, (y + 1), z, new ItemStack(ModItems.MORTAR_DEPLOYER.get())); mortar.setPickUpDelay(10); level.addFreshEntity(mortar); } super.destroy(); } @Override public float getMaxHealth() { return 100; } @Override public void registerControllers(AnimatableManager.ControllerRegistrar data) { data.add(new AnimationController<>(this, "movement", 0, this::movementPredicate)); } @Override public AnimatableInstanceCache getAnimatableInstanceCache() { return this.cache; } }