diff --git a/src/main/java/net/Chipperfluff/chipi/item/armor/ProtectionAuraHandler.java b/src/main/java/net/Chipperfluff/chipi/item/armor/ProtectionAuraHandler.java index 3b59af5..4f4ca7c 100644 --- a/src/main/java/net/Chipperfluff/chipi/item/armor/ProtectionAuraHandler.java +++ b/src/main/java/net/Chipperfluff/chipi/item/armor/ProtectionAuraHandler.java @@ -8,6 +8,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import net.minecraft.state.property.Properties; +import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -19,10 +20,13 @@ public final class ProtectionAuraHandler { private static final float BASE_RECHARGE_RATE = 0.0005f; private static final float BASE_DRAIN_RATE = 0.0008f; + private static final float CASCADE_LOSS = 0.30f; // 30% relative + private static final int CRITICAL_PCT = 10; + private ProtectionAuraHandler() {} /* ========================================================== - ENTRY POINT — CALLED SERVER SIDE + ENTRY POINT — SERVER SIDE ========================================================== */ public static void tick(PlayerEntity player) { @@ -40,22 +44,14 @@ public final class ProtectionAuraHandler { float rechargeRate = BASE_RECHARGE_RATE * durabilityFactor; float drainRate = BASE_DRAIN_RATE * (1.0f + (1.0f - durabilityFactor)); - float next = value; - - if (!inChipi || protectedAura) { - next = Math.min(1.0f, value + rechargeRate); - } else { - next = Math.max(0.0f, value - drainRate); - } + float next = (!inChipi || protectedAura) + ? Math.min(1.0f, value + rechargeRate) + : Math.max(0.0f, value - drainRate); if (next == value) return; - // ───────────────────────────────────────────── - // PERCENT-BASED DURABILITY LOGIC - // ───────────────────────────────────────────── - int oldPercent = (int) Math.floor(value * 100f); - int newPercent = (int) Math.floor(next * 100f); - + int oldPercent = (int) (value * 100f); + int newPercent = (int) (next * 100f); int delta = Math.abs(newPercent - oldPercent); if (delta > 0) { @@ -82,26 +78,113 @@ public final class ProtectionAuraHandler { ========================================================== */ private static float getDurabilityFactor(PlayerEntity player) { - int max = 0; - int current = 0; + int max = 0, current = 0; for (ItemStack stack : player.getArmorItems()) { if (!stack.isEmpty()) { max += stack.getMaxDamage(); - current += (stack.getMaxDamage() - stack.getDamage()); + current += stack.getMaxDamage() - stack.getDamage(); } } if (max <= 0) return 0f; - return Math.max(0f, Math.min(1f, (float) current / max)); + return Math.min(1f, (float) current / max); } private static void damageArmor(PlayerEntity player, int amount) { + int[] before = getArmorPercents(player); + boolean broke = false; + for (ItemStack stack : player.getArmorItems()) { if (!stack.isEmpty()) { stack.damage(amount, player, p -> {}); + if (stack.getDamage() >= stack.getMaxDamage()) { + broke = true; + } } } + + if (broke) { + applyCascadeDamage(player, before); + } else { + sendWarningIfNeeded(player, before, getArmorPercents(player)); + } + } + + private static void applyCascadeDamage(PlayerEntity player, int[] before) { + for (ItemStack stack : player.getArmorItems()) { + if (stack.isEmpty()) continue; + + int max = stack.getMaxDamage(); + int current = max - stack.getDamage(); + if (current <= 0) continue; + + int extra = Math.round(current * CASCADE_LOSS); + stack.setDamage(Math.min(max, stack.getDamage() + extra)); + } + + player.getDataTracker().set(CHIPI_ENERGY, 0f); + sendCollapseMessage(player, before, getArmorPercents(player)); + } + + /* ========================================================== + CHAT MESSAGES (PLAYER ONLY) + ========================================================== */ + + private static void sendWarningIfNeeded(PlayerEntity player, int[] before, int[] after) { + boolean trigger = false; + for (int i = 0; i < 4; i++) { + if (before[i] > CRITICAL_PCT && after[i] <= CRITICAL_PCT) { + trigger = true; + } + } + if (!trigger) return; + + player.sendMessage(Text.literal(buildWarningMessage(after)), false); + } + + private static void sendCollapseMessage(PlayerEntity player, int[] before, int[] after) { + StringBuilder sb = new StringBuilder("§c[AURA COLLAPSE]\n"); + appendPiece(sb, "Head", before[3], after[3]); + appendPiece(sb, "Body", before[2], after[2]); + appendPiece(sb, "Legs", before[1], after[1]); + appendPiece(sb, "Feet", before[0], after[0]); + player.sendMessage(Text.literal(sb.toString()), false); + } + + private static String buildWarningMessage(int[] pct) { + StringBuilder sb = new StringBuilder("§6[AURA WARNING]\n"); + appendPiece(sb, "Head", pct[3]); + appendPiece(sb, "Body", pct[2]); + appendPiece(sb, "Legs", pct[1]); + appendPiece(sb, "Feet", pct[0]); + return sb.toString(); + } + + private static void appendPiece(StringBuilder sb, String name, int pct) { + sb.append("§7").append(name).append(": ").append(pct).append("%"); + if (pct <= CRITICAL_PCT) sb.append(" §c!!!"); + sb.append("\n"); + } + + private static void appendPiece(StringBuilder sb, String name, int before, int after) { + sb.append("§7").append(name).append(": ") + .append(after).append("% (was ").append(before).append("%)\n"); + } + + private static int[] getArmorPercents(PlayerEntity player) { + int[] out = new int[4]; + int i = 0; + for (ItemStack stack : player.getArmorItems()) { + if (stack.isEmpty()) { + out[i++] = 0; + } else { + int max = stack.getMaxDamage(); + int cur = max - stack.getDamage(); + out[i++] = Math.max(0, (int) ((cur / (float) max) * 100f)); + } + } + return out; } /* ========================================================== @@ -126,10 +209,7 @@ public final class ProtectionAuraHandler { public static boolean hasAura(PlayerEntity player) { if (!hasFullChipperArmor(player)) return false; - Float value = player.getDataTracker().get(CHIPI_ENERGY); - if (value == null || value <= 0f) return false; - - return getDurabilityFactor(player) > 0f; + return value != null && value > 0f && getDurabilityFactor(player) > 0f; } }