package de.ellpeck.naturesaura.items; import de.ellpeck.naturesaura.NaturesAura; import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk; import de.ellpeck.naturesaura.packet.PacketHandler; import de.ellpeck.naturesaura.packet.PacketParticles; import net.minecraft.ChatFormatting; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.living.LivingDeathEvent; import net.neoforged.neoforge.event.entity.living.LivingEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; public class ItemPetReviver extends ItemImpl { public ItemPetReviver() { super("pet_reviver"); NeoForge.EVENT_BUS.register(new Events()); } private static class Events { @SubscribeEvent public void onEntityTick(LivingEvent.LivingTickEvent event) { var entity = event.getEntity(); if (entity.level().isClientSide || entity.level().getGameTime() % 20 != 0 || !(entity instanceof TamableAnimal tameable)) return; if (!tameable.isTame() || !tameable.getPersistentData().getBoolean(NaturesAura.MOD_ID + ":pet_reviver")) return; var owner = tameable.getOwner(); if (owner == null || owner.distanceToSqr(tameable) > 5 * 5) return; if (entity.level().random.nextFloat() >= 0.65F) { ((ServerLevel) entity.level()).sendParticles(ParticleTypes.HEART, entity.getX() + entity.level().random.nextGaussian() * 0.25F, entity.getEyeY() + entity.level().random.nextGaussian() * 0.25F, entity.getZ() + entity.level().random.nextGaussian() * 0.25F, entity.level().random.nextInt(2) + 1, 0, 0, 0, 0); } } // we need to use the event since the item doesn't receive the interaction for tamed pets... @SubscribeEvent public void onEntityInteract(PlayerInteractEvent.EntityInteractSpecific event) { var target = event.getTarget(); if (!(target instanceof TamableAnimal) || !((TamableAnimal) target).isTame()) return; if (target.getPersistentData().getBoolean(NaturesAura.MOD_ID + ":pet_reviver")) return; var stack = event.getEntity().getItemInHand(event.getHand()); if (stack.getItem() != ModItems.PET_REVIVER) return; target.getPersistentData().putBoolean(NaturesAura.MOD_ID + ":pet_reviver", true); if (!target.level().isClientSide) stack.shrink(1); event.setCancellationResult(InteractionResult.SUCCESS); event.setCanceled(true); } // we want to be sure that the pet is really dying, so we want to receive the event last @SubscribeEvent(priority = EventPriority.LOWEST) public void onLivingDeath(LivingDeathEvent event) { var entity = event.getEntity(); if (entity.level().isClientSide || !(entity instanceof TamableAnimal tameable)) return; if (!tameable.isTame() || !tameable.getPersistentData().getBoolean(NaturesAura.MOD_ID + ":pet_reviver")) return; // get the overworld, and the overworld's spawn point, by default var spawnLevel = tameable.level().getServer().overworld(); var spawn = Vec3.atBottomCenterOf(spawnLevel.getSharedSpawnPos()); // check if the owner is online, and respawn at the bed if they are var owner = tameable.getOwner(); if (owner instanceof ServerPlayer player) { var pos = player.getRespawnPosition(); if (pos != null) { var f = player.getRespawnAngle(); var b = player.isRespawnForced(); var bed = Player.findRespawnPositionAndUseSpawnBlock((ServerLevel) player.level(), pos, f, b, false); if (bed.isPresent()) { spawnLevel = (ServerLevel) player.level(); spawn = bed.get(); } } } PacketHandler.sendToAllAround(tameable.level(), tameable.blockPosition(), 32, new PacketParticles((float) tameable.getX(), (float) tameable.getEyeY(), (float) tameable.getZ(), PacketParticles.Type.PET_REVIVER, 0xc2461d)); var spawnedPet = tameable; if (tameable.level() != spawnLevel) { tameable.remove(Entity.RemovalReason.DISCARDED); spawnedPet = (TamableAnimal) tameable.getType().create(spawnLevel); } // respawn (a copy of) the pet spawnedPet.restoreFrom(tameable); spawnedPet.setDeltaMovement(0, 0, 0); spawnedPet.moveTo(spawn.x, spawn.y, spawn.z, tameable.getYRot(), tameable.getXRot()); while (!spawnLevel.noCollision(spawnedPet)) spawnedPet.setPos(spawnedPet.getX(), spawnedPet.getY() + 1, spawnedPet.getZ()); spawnedPet.setHealth(spawnedPet.getMaxHealth()); spawnedPet.getNavigation().stop(); // sit down (on the server side!) spawnedPet.setInSittingPose(true); spawnedPet.setJumping(false); spawnedPet.setTarget(null); if (tameable.level() != spawnLevel) { spawnLevel.addFreshEntity(spawnedPet); tameable.remove(Entity.RemovalReason.DISCARDED); } // drain aura var auraPos = IAuraChunk.getHighestSpot(spawnLevel, spawnedPet.blockPosition(), 35, spawnedPet.blockPosition()); IAuraChunk.getAuraChunk(spawnLevel, auraPos).drainAura(auraPos, 200000); PacketHandler.sendToAllAround(spawnedPet.level(), spawnedPet.blockPosition(), 32, new PacketParticles((float) spawnedPet.getX(), (float) spawnedPet.getEyeY(), (float) spawnedPet.getZ(), PacketParticles.Type.PET_REVIVER, 0x4dba2f)); if (owner instanceof Player) owner.sendSystemMessage(Component.translatable("info." + NaturesAura.MOD_ID + ".pet_reviver", spawnedPet.getDisplayName()).withStyle(ChatFormatting.ITALIC, ChatFormatting.GRAY)); event.setCanceled(true); } } }