|
|
|
@@ -50,6 +50,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
|
|
|
import net.minecraft.item.*;
|
|
|
|
import net.minecraft.item.*;
|
|
|
|
import net.minecraft.nbt.NbtCompound;
|
|
|
|
import net.minecraft.nbt.NbtCompound;
|
|
|
|
import net.minecraft.network.PacketByteBuf;
|
|
|
|
import net.minecraft.network.PacketByteBuf;
|
|
|
|
|
|
|
|
import net.minecraft.particle.ParticleTypes;
|
|
|
|
import net.minecraft.registry.*;
|
|
|
|
import net.minecraft.registry.*;
|
|
|
|
import net.minecraft.registry.entry.RegistryEntry;
|
|
|
|
import net.minecraft.registry.entry.RegistryEntry;
|
|
|
|
import net.minecraft.registry.tag.BiomeTags;
|
|
|
|
import net.minecraft.registry.tag.BiomeTags;
|
|
|
|
@@ -1466,23 +1467,36 @@ public class Szar implements ModInitializer {
|
|
|
|
ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, source, amount) -> {
|
|
|
|
ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, source, amount) -> {
|
|
|
|
if (!(entity instanceof PlayerEntity player)) return true;
|
|
|
|
if (!(entity instanceof PlayerEntity player)) return true;
|
|
|
|
|
|
|
|
|
|
|
|
// Check if wearing full Ender armor (optional — remove if you want any piece to trigger)
|
|
|
|
int piecesWorn = 0;
|
|
|
|
boolean wearingFullSet =
|
|
|
|
if (player.getInventory().getArmorStack(0).isOf(ENDER_BOOTS)) piecesWorn++;
|
|
|
|
player.getInventory().getArmorStack(0).isOf(ENDER_BOOTS) &&
|
|
|
|
if (player.getInventory().getArmorStack(1).isOf(ENDER_LEGGINGS)) piecesWorn++;
|
|
|
|
player.getInventory().getArmorStack(1).isOf(ENDER_LEGGINGS) &&
|
|
|
|
if (player.getInventory().getArmorStack(2).isOf(ENDER_CHESTPLATE)) piecesWorn++;
|
|
|
|
player.getInventory().getArmorStack(2).isOf(ENDER_CHESTPLATE) &&
|
|
|
|
if (player.getInventory().getArmorStack(3).isOf(ENDER_HELMET)) piecesWorn++;
|
|
|
|
player.getInventory().getArmorStack(3).isOf(ENDER_HELMET);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!wearingFullSet) return true;
|
|
|
|
if (piecesWorn == 0) return true;
|
|
|
|
|
|
|
|
if (player.getHealth() > 4.0F) return true;
|
|
|
|
|
|
|
|
|
|
|
|
// If player would drop to <= 1 heart (2 HP)
|
|
|
|
// armorFactor: 1 piece = 0.25, 2 = 0.5, 3 = 0.75, 4 = 1.0
|
|
|
|
if (player.getHealth() - amount <= 2.0F) {
|
|
|
|
float armorFactor = piecesWorn / 4.0F;
|
|
|
|
|
|
|
|
|
|
|
|
if (player.getWorld() instanceof ServerWorld world) {
|
|
|
|
// healthFactor: 4 HP (2 hearts) = 0.0, 0.5 HP (quarter heart) = ~0.875, 0 HP = 1.0
|
|
|
|
teleportRandomly(player, world);
|
|
|
|
// Never reaches 1.0 in practice since player is alive
|
|
|
|
|
|
|
|
float healthFactor = 1.0F - (player.getHealth() / 4.0F);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Combined: ranges from ~1% (1 piece, 2 hearts) to 75% (full set, half heart)
|
|
|
|
|
|
|
|
// Formula: 0.75 * armorFactor * healthFactor
|
|
|
|
|
|
|
|
// Full set half heart (0.5 HP): 0.75 * 1.0 * 0.875 = 65.6%
|
|
|
|
|
|
|
|
// Full set 1 HP: 0.75 * 1.0 * 0.75 = 56.3%
|
|
|
|
|
|
|
|
// 1 piece 4 HP: 0.75 * 0.25 * 0.0 = 0%
|
|
|
|
|
|
|
|
// 1 piece 3.5 HP: 0.75 * 0.25 * 0.125 = 2.3%
|
|
|
|
|
|
|
|
float chance = 0.75F * armorFactor * healthFactor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (chance <= 0F || player.getRandom().nextFloat() > chance) return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (player.getWorld() instanceof ServerWorld world) {
|
|
|
|
|
|
|
|
if (teleportRandomlySafe(player, world)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false; // CANCEL DAMAGE
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
@@ -1549,6 +1563,74 @@ public class Szar implements ModInitializer {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean teleportRandomlySafe(PlayerEntity player, ServerWorld world) {
|
|
|
|
|
|
|
|
double origX = player.getX();
|
|
|
|
|
|
|
|
double origY = player.getY();
|
|
|
|
|
|
|
|
double origZ = player.getZ();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int attempt = 0; attempt < 32; attempt++) {
|
|
|
|
|
|
|
|
double x = origX + (player.getRandom().nextDouble() - 0.5) * 32.0;
|
|
|
|
|
|
|
|
double z = origZ + (player.getRandom().nextDouble() - 0.5) * 32.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find solid ground: start from player Y + 16, scan down
|
|
|
|
|
|
|
|
int startY = Math.min((int) origY + 16, world.getTopY() - 1);
|
|
|
|
|
|
|
|
BlockPos.Mutable mutable = new BlockPos.Mutable((int) x, startY, (int) z);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean foundGround = false;
|
|
|
|
|
|
|
|
for (int y = startY; y >= world.getBottomY() + 1; y--) {
|
|
|
|
|
|
|
|
mutable.setY(y);
|
|
|
|
|
|
|
|
BlockState below = world.getBlockState(mutable.down());
|
|
|
|
|
|
|
|
BlockState atFeet = world.getBlockState(mutable);
|
|
|
|
|
|
|
|
BlockState atHead = world.getBlockState(mutable.up());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ground must be solid + walkable, feet & head must be passable
|
|
|
|
|
|
|
|
if (below.isSolidBlock(world, mutable.down())
|
|
|
|
|
|
|
|
&& !below.isOf(Blocks.LAVA) && !below.isOf(Blocks.MAGMA_BLOCK)
|
|
|
|
|
|
|
|
&& !below.isOf(Blocks.CACTUS) && !below.isOf(Blocks.SWEET_BERRY_BUSH)
|
|
|
|
|
|
|
|
&& !atFeet.isSolidBlock(world, mutable)
|
|
|
|
|
|
|
|
&& !atFeet.isLiquid()
|
|
|
|
|
|
|
|
&& !atHead.isSolidBlock(world, mutable.up())
|
|
|
|
|
|
|
|
&& !atHead.isLiquid()) {
|
|
|
|
|
|
|
|
foundGround = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!foundGround) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double destX = mutable.getX() + 0.5;
|
|
|
|
|
|
|
|
double destY = mutable.getY();
|
|
|
|
|
|
|
|
double destZ = mutable.getZ() + 0.5;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Particles at origin
|
|
|
|
|
|
|
|
world.spawnParticles(ParticleTypes.PORTAL, origX, origY + 1, origZ, 32,
|
|
|
|
|
|
|
|
0.5, 1.0, 0.5, 0.5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sound at origin
|
|
|
|
|
|
|
|
world.playSound(null, origX, origY, origZ,
|
|
|
|
|
|
|
|
SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.PLAYERS,
|
|
|
|
|
|
|
|
1.0F, 1.0F);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Teleport
|
|
|
|
|
|
|
|
player.teleport(destX, destY, destZ);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Particles at destination
|
|
|
|
|
|
|
|
world.spawnParticles(ParticleTypes.PORTAL, destX, destY + 1, destZ, 32,
|
|
|
|
|
|
|
|
0.5, 1.0, 0.5, 0.5);
|
|
|
|
|
|
|
|
world.spawnParticles(ParticleTypes.REVERSE_PORTAL, destX, destY + 1, destZ, 16,
|
|
|
|
|
|
|
|
0.3, 0.5, 0.3, 0.1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sound at destination
|
|
|
|
|
|
|
|
world.playSound(null, destX, destY, destZ,
|
|
|
|
|
|
|
|
SoundEvents.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS,
|
|
|
|
|
|
|
|
1.0F, 1.0F);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; // all 32 attempts failed, no safe spot found
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static final Block SUPER_BEACON_BLOCK = new SuperBeaconBlock(
|
|
|
|
public static final Block SUPER_BEACON_BLOCK = new SuperBeaconBlock(
|
|
|
|
FabricBlockSettings.copyOf(Blocks.BEACON).luminance(15)
|
|
|
|
FabricBlockSettings.copyOf(Blocks.BEACON).luminance(15)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|