diff --git a/gradle.properties b/gradle.properties index 1d23e14..c7308b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loader_version=0.18.3 # Mod Properties -mod_version=26.4.16 +mod_version=26.4.17 maven_group=dev.tggamesyt archives_base_name=szar # Dependencies diff --git a/src/main/java/dev/tggamesyt/szar/Szar.java b/src/main/java/dev/tggamesyt/szar/Szar.java index 6994809..d6985b0 100644 --- a/src/main/java/dev/tggamesyt/szar/Szar.java +++ b/src/main/java/dev/tggamesyt/szar/Szar.java @@ -50,6 +50,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.*; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; +import net.minecraft.particle.ParticleTypes; import net.minecraft.registry.*; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.registry.tag.BiomeTags; @@ -1466,23 +1467,36 @@ public class Szar implements ModInitializer { ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, source, amount) -> { if (!(entity instanceof PlayerEntity player)) return true; - // Check if wearing full Ender armor (optional — remove if you want any piece to trigger) - boolean wearingFullSet = - player.getInventory().getArmorStack(0).isOf(ENDER_BOOTS) && - player.getInventory().getArmorStack(1).isOf(ENDER_LEGGINGS) && - player.getInventory().getArmorStack(2).isOf(ENDER_CHESTPLATE) && - player.getInventory().getArmorStack(3).isOf(ENDER_HELMET); + int piecesWorn = 0; + if (player.getInventory().getArmorStack(0).isOf(ENDER_BOOTS)) piecesWorn++; + if (player.getInventory().getArmorStack(1).isOf(ENDER_LEGGINGS)) piecesWorn++; + if (player.getInventory().getArmorStack(2).isOf(ENDER_CHESTPLATE)) piecesWorn++; + if (player.getInventory().getArmorStack(3).isOf(ENDER_HELMET)) piecesWorn++; - 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) - if (player.getHealth() - amount <= 2.0F) { + // armorFactor: 1 piece = 0.25, 2 = 0.5, 3 = 0.75, 4 = 1.0 + float armorFactor = piecesWorn / 4.0F; - if (player.getWorld() instanceof ServerWorld world) { - teleportRandomly(player, world); + // healthFactor: 4 HP (2 hearts) = 0.0, 0.5 HP (quarter heart) = ~0.875, 0 HP = 1.0 + // 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; @@ -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( FabricBlockSettings.copyOf(Blocks.BEACON).luminance(15) );