Merge pull request 'continue/armor_aura' (#1) from continue/armor_aura into main
Reviewed-on: #1
This commit is contained in:
commit
5d095fbba2
@ -14,6 +14,7 @@ public class ChipiStatusBar {
|
||||
private static final int ORANGE = 0xFFFF8000;
|
||||
private static final int OUTLINE_LIGHT = 0xFFB0B0B0;
|
||||
private static final int OUTLINE_DARK = 0xFF404040;
|
||||
private static final int TEXT_COLOR = 0xFFFFFFFF;
|
||||
private static final int BAR_HEIGHT = 6;
|
||||
|
||||
public static void register() {
|
||||
@ -33,17 +34,33 @@ public class ChipiStatusBar {
|
||||
int screenWidth = client.getWindow().getScaledWidth();
|
||||
int screenHeight = client.getWindow().getScaledHeight();
|
||||
|
||||
int barWidth = (int)(80 * 0.8f);
|
||||
int centerX = screenWidth / 2;
|
||||
int left = centerX - barWidth / 2;
|
||||
int right = left + barWidth;
|
||||
|
||||
int heartsY = screenHeight - 39;
|
||||
int barY = heartsY - BAR_HEIGHT - 4;
|
||||
// ============================================================
|
||||
// EXACT vanilla hunger bar bounds (pixel-perfect)
|
||||
// ============================================================
|
||||
int hungerRight = centerX + 91;
|
||||
int hungerLeft = hungerRight - 81; // vanilla width (10 * 8 + 1)
|
||||
int barWidth = hungerRight - hungerLeft;
|
||||
|
||||
// Base hunger Y
|
||||
int hungerY = screenHeight - 39;
|
||||
|
||||
// While air bar is visible, hunger (and us) move up
|
||||
if (client.player.getAir() < client.player.getMaxAir()) {
|
||||
hungerY -= 10;
|
||||
}
|
||||
|
||||
// Our bar sits directly above hunger
|
||||
int barY = hungerY - BAR_HEIGHT - 2;
|
||||
|
||||
int fillWidth = (int)(barWidth * value);
|
||||
|
||||
float alpha = value >= 1f ? 0.65f :
|
||||
// ============================================================
|
||||
// Alpha behavior
|
||||
// ============================================================
|
||||
float alpha =
|
||||
value >= 1f ? 0.65f :
|
||||
value < 0.10f
|
||||
? (float)(0.6f + 0.4f * Math.sin(System.currentTimeMillis() / 120.0))
|
||||
: 1f;
|
||||
@ -51,19 +68,36 @@ public class ChipiStatusBar {
|
||||
int outlineDark = applyAlpha(OUTLINE_DARK, alpha);
|
||||
int outlineLight = applyAlpha(OUTLINE_LIGHT, alpha);
|
||||
int fillColor = applyAlpha(ORANGE, alpha);
|
||||
int textColor = applyAlpha(TEXT_COLOR, alpha);
|
||||
|
||||
ctx.fill(left - 1, barY - 1, right + 1, barY, outlineDark);
|
||||
ctx.fill(left - 1, barY + BAR_HEIGHT, right + 1, barY + BAR_HEIGHT + 1, outlineLight);
|
||||
ctx.fill(left - 1, barY, left, barY + BAR_HEIGHT, outlineDark);
|
||||
ctx.fill(right, barY, right + 1, barY + BAR_HEIGHT, outlineLight);
|
||||
ctx.fill(left, barY, left + fillWidth, barY + BAR_HEIGHT, fillColor);
|
||||
// ============================================================
|
||||
// Draw outline
|
||||
// ============================================================
|
||||
ctx.fill(hungerLeft - 1, barY - 1, hungerRight + 1, barY, outlineDark);
|
||||
ctx.fill(hungerLeft - 1, barY + BAR_HEIGHT, hungerRight + 1, barY + BAR_HEIGHT + 1, outlineLight);
|
||||
ctx.fill(hungerLeft - 1, barY, hungerLeft, barY + BAR_HEIGHT, outlineDark);
|
||||
ctx.fill(hungerRight, barY, hungerRight + 1, barY + BAR_HEIGHT, outlineLight);
|
||||
|
||||
// ============================================================
|
||||
// Draw fill
|
||||
// ============================================================
|
||||
ctx.fill(hungerLeft, barY, hungerLeft + fillWidth, barY + BAR_HEIGHT, fillColor);
|
||||
|
||||
// ============================================================
|
||||
// Percent text INSIDE bar, centered
|
||||
// ============================================================
|
||||
String text = String.format("%03d%%", Math.round(value * 100f));
|
||||
int textWidth = client.textRenderer.getWidth(text);
|
||||
|
||||
int textX = hungerLeft + (barWidth - textWidth) / 2;
|
||||
int textY = barY - 1;
|
||||
|
||||
ctx.drawText(
|
||||
client.textRenderer,
|
||||
String.format("%03d%%", Math.round(value * 100f)),
|
||||
right + 6,
|
||||
barY - 2,
|
||||
fillColor,
|
||||
text,
|
||||
textX,
|
||||
textY,
|
||||
textColor,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,12 +5,7 @@ import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.block.StairsBlock;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.ai.goal.ActiveTargetGoal;
|
||||
import net.minecraft.entity.ai.goal.LookAroundGoal;
|
||||
import net.minecraft.entity.ai.goal.LookAtEntityGoal;
|
||||
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.entity.ai.goal.SwimGoal;
|
||||
import net.minecraft.entity.ai.goal.WanderAroundFarGoal;
|
||||
import net.minecraft.entity.ai.goal.*;
|
||||
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||
import net.minecraft.entity.attribute.EntityAttributes;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
@ -25,6 +20,7 @@ import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
|
||||
import net.Chipperfluff.chipi.item.ModItems;
|
||||
import net.Chipperfluff.chipi.sound.ModSounds;
|
||||
import net.Chipperfluff.chipi.util.TickScheduler;
|
||||
@ -32,6 +28,7 @@ import net.Chipperfluff.chipi.util.TickScheduler;
|
||||
public class MepEntity extends PathAwareEntity {
|
||||
|
||||
private static final int FORGET_TARGET_AFTER_TICKS = 100;
|
||||
private static final int DESPAWN_DISTANCE = 300;
|
||||
|
||||
private boolean angryAtPlayer = false;
|
||||
private int ticksSinceLastSeen = 0;
|
||||
@ -56,7 +53,8 @@ public class MepEntity extends PathAwareEntity {
|
||||
this,
|
||||
PlayerEntity.class,
|
||||
true,
|
||||
target -> target instanceof PlayerEntity player && !isPlayerProtected(player)
|
||||
target -> target instanceof PlayerEntity player
|
||||
&& !isHardIgnored(player)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -80,10 +78,28 @@ public class MepEntity extends PathAwareEntity {
|
||||
return super.damage(source, amount);
|
||||
}
|
||||
|
||||
// === ATTACK OVERRIDE (AURA IMMUNITY) ===
|
||||
|
||||
@Override
|
||||
public boolean tryAttack(net.minecraft.entity.Entity target) {
|
||||
if (target instanceof PlayerEntity player) {
|
||||
if (ProtectionAuraHandler.hasAura(player)) {
|
||||
return false; // chase but never hit
|
||||
}
|
||||
}
|
||||
return super.tryAttack(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
// --- DISTANCE DESPAWN ONLY ---
|
||||
if (!this.getWorld().isClient && isTooFarFromAllPlayers()) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
|
||||
LivingEntity target = this.getTarget();
|
||||
|
||||
if (!(target instanceof PlayerEntity player)) {
|
||||
@ -92,7 +108,7 @@ public class MepEntity extends PathAwareEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPlayerProtected(player)) {
|
||||
if (isHardIgnored(player)) {
|
||||
clearTarget();
|
||||
return;
|
||||
}
|
||||
@ -118,7 +134,27 @@ public class MepEntity extends PathAwareEntity {
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
|
||||
private static boolean isPlayerProtected(PlayerEntity player) {
|
||||
// === DISTANCE CHECK ===
|
||||
|
||||
private boolean isTooFarFromAllPlayers() {
|
||||
double maxSq = DESPAWN_DISTANCE * DESPAWN_DISTANCE;
|
||||
|
||||
for (PlayerEntity player : this.getWorld().getPlayers()) {
|
||||
if (this.squaredDistanceTo(player) <= maxSq) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// === HARD IGNORE RULES ===
|
||||
|
||||
private static boolean isHardIgnored(PlayerEntity player) {
|
||||
if (player.getZ() < 18) return true;
|
||||
return isOnProtectedBlock(player);
|
||||
}
|
||||
|
||||
private static boolean isOnProtectedBlock(PlayerEntity player) {
|
||||
BlockPos pos = player.getBlockPos();
|
||||
BlockState state = player.getWorld().getBlockState(pos);
|
||||
|
||||
@ -130,19 +166,7 @@ public class MepEntity extends PathAwareEntity {
|
||||
|| state.getBlock() instanceof SlabBlock;
|
||||
}
|
||||
|
||||
// === Despawn prevention ===
|
||||
|
||||
@Override
|
||||
public boolean cannotDespawn() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImmediatelyDespawn(double distanceSquared) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// === MILKING (FEVER DREAM EDITION) ===
|
||||
// === MILKING ===
|
||||
|
||||
@Override
|
||||
public ActionResult interactMob(PlayerEntity player, Hand hand) {
|
||||
@ -154,7 +178,6 @@ public class MepEntity extends PathAwareEntity {
|
||||
stack.decrement(1);
|
||||
player.giveItemStack(new ItemStack(ModItems.MEP_MILK));
|
||||
|
||||
// ---- base sound ----
|
||||
float basePitch = 0.3f + this.random.nextFloat() * 1.9f;
|
||||
float baseVolume = 0.9f + this.random.nextFloat() * 0.6f;
|
||||
|
||||
@ -167,10 +190,8 @@ public class MepEntity extends PathAwareEntity {
|
||||
basePitch
|
||||
);
|
||||
|
||||
// ---- single delayed echo (10% chance) ----
|
||||
if (this.random.nextFloat() < 0.10f) {
|
||||
|
||||
int delay = 10 + this.random.nextInt(21); // 10–30 ticks
|
||||
int delay = 10 + this.random.nextInt(21);
|
||||
|
||||
float echoPitch = basePitch * 0.5f;
|
||||
float echoVolume = baseVolume * 0.5f;
|
||||
|
||||
@ -18,9 +18,15 @@ import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.ServerWorldAccess;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.Box;
|
||||
|
||||
|
||||
public final class SpawnLogic {
|
||||
|
||||
private static final int MAX_MEPS = 60;
|
||||
private static final int SPAWN_RADIUS = 150;
|
||||
|
||||
private static final RegistryKey<Biome> VOID_BIOME =
|
||||
RegistryKey.of(RegistryKeys.BIOME, new Identifier("chipi", "void"));
|
||||
|
||||
@ -46,16 +52,48 @@ public final class SpawnLogic {
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean canSpawn(EntityType<? extends MobEntity> type, ServerWorldAccess world, SpawnReason reason, BlockPos pos, Random random) {
|
||||
private static boolean canSpawn(
|
||||
EntityType<? extends MobEntity> type,
|
||||
ServerWorldAccess world,
|
||||
SpawnReason reason,
|
||||
BlockPos pos,
|
||||
Random random
|
||||
) {
|
||||
// --- HEIGHT & Z RULES ---
|
||||
int y = pos.getY();
|
||||
if (y < 87 || y > 90) return false;
|
||||
if (pos.getZ() < 18) return false;
|
||||
|
||||
// --- BLOCK CHECK ---
|
||||
BlockState below = world.getBlockState(pos.down());
|
||||
boolean can_spawn = below.isOf(Blocks.POLISHED_BLACKSTONE_BRICKS);
|
||||
if (!below.isOf(Blocks.POLISHED_BLACKSTONE_BRICKS)) return false;
|
||||
|
||||
System.out.println("[MEP ENTITY] spawn check pos=" + pos + " below=" + below.getBlock());
|
||||
// --- GLOBAL CAP (DENY SPAWN, DO NOT DESPAWN) ---
|
||||
int mepCount = world.getEntitiesByClass(
|
||||
MepEntity.class,
|
||||
new Box(
|
||||
pos.getX() - 512, pos.getY() - 512, pos.getZ() - 512,
|
||||
pos.getX() + 512, pos.getY() + 512, pos.getZ() + 512
|
||||
),
|
||||
e -> true
|
||||
).size();
|
||||
|
||||
return can_spawn;
|
||||
if (mepCount >= MAX_MEPS) return false;
|
||||
|
||||
// --- PLAYER PROXIMITY ---
|
||||
double maxSq = SPAWN_RADIUS * SPAWN_RADIUS;
|
||||
|
||||
for (PlayerEntity player : world.getPlayers()) {
|
||||
if (player.squaredDistanceTo(
|
||||
pos.getX() + 0.5,
|
||||
pos.getY(),
|
||||
pos.getZ() + 0.5
|
||||
) <= maxSq) {
|
||||
return true; // ✔ valid spawn
|
||||
}
|
||||
}
|
||||
|
||||
// No nearby player → no spawn
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,8 +11,7 @@ import net.minecraft.registry.Registry;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.Chipperfluff.chipi.item.tool.ChipperToolMaterial;
|
||||
import net.Chipperfluff.chipi.item.MepMilkItem;
|
||||
import net.minecraft.item.Items;
|
||||
import net.Chipperfluff.chipi.item.armor.ChipperArmorItem;
|
||||
|
||||
|
||||
public class ModItems {
|
||||
|
||||
@ -95,25 +94,25 @@ public class ModItems {
|
||||
public static final Item CHIPPER_HELMET = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(ChipiMod.MOD_ID, "chipper_helmet"),
|
||||
new ChipperArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.HELMET, new Item.Settings())
|
||||
new ArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.HELMET, new Item.Settings())
|
||||
);
|
||||
|
||||
public static final Item CHIPPER_CHESTPLATE = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(ChipiMod.MOD_ID, "chipper_chestplate"),
|
||||
new ChipperArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.CHESTPLATE, new Item.Settings())
|
||||
new ArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.CHESTPLATE, new Item.Settings())
|
||||
);
|
||||
|
||||
public static final Item CHIPPER_LEGGINGS = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(ChipiMod.MOD_ID, "chipper_leggings"),
|
||||
new ChipperArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.LEGGINGS, new Item.Settings())
|
||||
new ArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.LEGGINGS, new Item.Settings())
|
||||
);
|
||||
|
||||
public static final Item CHIPPER_BOOTS = Registry.register(
|
||||
Registries.ITEM,
|
||||
new Identifier(ChipiMod.MOD_ID, "chipper_boots"),
|
||||
new ChipperArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.BOOTS, new Item.Settings())
|
||||
new ArmorItem(ChipperArmorMaterial.INSTANCE, ArmorItem.Type.BOOTS, new Item.Settings())
|
||||
);
|
||||
|
||||
// ===== TOOLS =====
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
package net.Chipperfluff.chipi.item.armor;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.ArmorItem;
|
||||
import net.minecraft.item.ArmorMaterial;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ChipperArmorItem extends ArmorItem {
|
||||
|
||||
public ChipperArmorItem(
|
||||
ArmorMaterial material,
|
||||
Type type,
|
||||
Settings settings
|
||||
) {
|
||||
super(material, type, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(
|
||||
ItemStack stack,
|
||||
World world,
|
||||
Entity entity,
|
||||
int slot,
|
||||
boolean selected
|
||||
) {
|
||||
if (slot >= EquipmentSlot.FEET.getEntitySlotId()
|
||||
&& slot <= EquipmentSlot.HEAD.getEntitySlotId()) {
|
||||
|
||||
if (stack.getDamage() != 0) {
|
||||
stack.setDamage(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ public class ChipperArmorMaterial implements ArmorMaterial {
|
||||
|
||||
@Override
|
||||
public int getDurability(ArmorItem.Type type) {
|
||||
return 10_000;
|
||||
return 1_000;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,240 @@
|
||||
package net.Chipperfluff.chipi.armor;
|
||||
|
||||
import net.Chipperfluff.chipi.item.ModItems;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.block.StairsBlock;
|
||||
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;
|
||||
|
||||
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
|
||||
import static net.Chipperfluff.chipi.server.ChipiServerEvents.CHIPI_DIMENSION_KEY;
|
||||
|
||||
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;
|
||||
private static final int CRITICAL_PCT = 10;
|
||||
|
||||
private ProtectionAuraHandler() {}
|
||||
|
||||
/* ==========================================================
|
||||
ENTRY POINT
|
||||
========================================================== */
|
||||
|
||||
public static void tick(PlayerEntity player) {
|
||||
if (!hasFullChipperArmor(player)) return;
|
||||
|
||||
Float energy = player.getDataTracker().get(CHIPI_ENERGY);
|
||||
if (energy == null) return;
|
||||
|
||||
float durabilityFactor = getDurabilityFactor(player);
|
||||
if (durabilityFactor <= 0f) return;
|
||||
|
||||
float nextEnergy = calculateNextEnergy(player, energy, durabilityFactor);
|
||||
if (nextEnergy == energy) return;
|
||||
|
||||
applyEnergyDelta(player, energy, nextEnergy);
|
||||
player.getDataTracker().set(CHIPI_ENERGY, nextEnergy);
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
ENERGY
|
||||
========================================================== */
|
||||
|
||||
private static float calculateNextEnergy(PlayerEntity player, float current, float durabilityFactor) {
|
||||
boolean recharge = !isInChipi(player.getWorld()) || isOnProtectedBlock(player);
|
||||
|
||||
float rate = recharge
|
||||
? BASE_RECHARGE_RATE * durabilityFactor
|
||||
: BASE_DRAIN_RATE * (1.0f + (1.0f - durabilityFactor));
|
||||
|
||||
return recharge
|
||||
? Math.min(1.0f, current + rate)
|
||||
: Math.max(0.0f, current - rate);
|
||||
}
|
||||
|
||||
private static void applyEnergyDelta(PlayerEntity player, float before, float after) {
|
||||
int oldPct = (int) (before * 100f);
|
||||
int newPct = (int) (after * 100f);
|
||||
int delta = Math.abs(newPct - oldPct);
|
||||
|
||||
if (delta > 0) {
|
||||
applyArmorDamage(player, delta);
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
ARMOR DAMAGE
|
||||
========================================================== */
|
||||
|
||||
private static void applyArmorDamage(PlayerEntity player, int amount) {
|
||||
int[] before = getArmorPercents(player);
|
||||
boolean broke = damageAllPieces(player, amount);
|
||||
|
||||
if (broke) {
|
||||
handleCascade(player, before);
|
||||
} else {
|
||||
handleWarnings(player, before, getArmorPercents(player));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean damageAllPieces(PlayerEntity player, int amount) {
|
||||
boolean broke = false;
|
||||
|
||||
for (ItemStack stack : player.getArmorItems()) {
|
||||
if (stack.isEmpty()) continue;
|
||||
|
||||
stack.damage(amount, player, p -> {});
|
||||
if (stack.getDamage() >= stack.getMaxDamage()) {
|
||||
broke = true;
|
||||
}
|
||||
}
|
||||
return broke;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
CASCADE
|
||||
========================================================== */
|
||||
|
||||
private static void handleCascade(PlayerEntity player, int[] before) {
|
||||
applyCascadeLoss(player);
|
||||
player.getDataTracker().set(CHIPI_ENERGY, 0f);
|
||||
sendCollapseMessage(player, before, getArmorPercents(player));
|
||||
}
|
||||
|
||||
private static void applyCascadeLoss(PlayerEntity player) {
|
||||
for (ItemStack stack : player.getArmorItems()) {
|
||||
if (stack.isEmpty()) continue;
|
||||
|
||||
int max = stack.getMaxDamage();
|
||||
int remaining = max - stack.getDamage();
|
||||
if (remaining <= 0) continue;
|
||||
|
||||
int extra = Math.round(remaining * CASCADE_LOSS);
|
||||
stack.setDamage(Math.min(max, stack.getDamage() + extra));
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
WARNINGS
|
||||
========================================================== */
|
||||
|
||||
private static void handleWarnings(PlayerEntity player, int[] before, int[] after) {
|
||||
if (!enteredCritical(before, after)) return;
|
||||
player.sendMessage(Text.literal(buildWarningMessage(after)), false);
|
||||
}
|
||||
|
||||
private static boolean enteredCritical(int[] before, int[] after) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (before[i] > CRITICAL_PCT && after[i] <= CRITICAL_PCT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
CHAT BUILDERS
|
||||
========================================================== */
|
||||
|
||||
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 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 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");
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
UTIL
|
||||
========================================================== */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static float getDurabilityFactor(PlayerEntity player) {
|
||||
int max = 0, cur = 0;
|
||||
|
||||
for (ItemStack stack : player.getArmorItems()) {
|
||||
if (!stack.isEmpty()) {
|
||||
max += stack.getMaxDamage();
|
||||
cur += stack.getMaxDamage() - stack.getDamage();
|
||||
}
|
||||
}
|
||||
return max <= 0 ? 0f : Math.min(1f, (float) cur / max);
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
CONDITIONS
|
||||
========================================================== */
|
||||
|
||||
public static boolean hasFullChipperArmor(PlayerEntity player) {
|
||||
PlayerInventory inv = player.getInventory();
|
||||
return inv.getArmorStack(3).isOf(ModItems.CHIPPER_HELMET)
|
||||
&& inv.getArmorStack(2).isOf(ModItems.CHIPPER_CHESTPLATE)
|
||||
&& inv.getArmorStack(1).isOf(ModItems.CHIPPER_LEGGINGS)
|
||||
&& inv.getArmorStack(0).isOf(ModItems.CHIPPER_BOOTS);
|
||||
}
|
||||
|
||||
private static boolean isOnProtectedBlock(PlayerEntity player) {
|
||||
BlockPos pos = player.getBlockPos();
|
||||
BlockState state = player.getWorld().getBlockState(pos);
|
||||
|
||||
return state.contains(Properties.WATERLOGGED)
|
||||
&& state.get(Properties.WATERLOGGED)
|
||||
&& (state.getBlock() instanceof StairsBlock
|
||||
|| state.getBlock() instanceof SlabBlock);
|
||||
}
|
||||
|
||||
private static boolean isInChipi(World world) {
|
||||
return world.getRegistryKey().equals(CHIPI_DIMENSION_KEY);
|
||||
}
|
||||
|
||||
public static boolean hasAura(PlayerEntity player) {
|
||||
if (!hasFullChipperArmor(player)) return false;
|
||||
Float v = player.getDataTracker().get(CHIPI_ENERGY);
|
||||
return v != null && v > 0f && getDurabilityFactor(player) > 0f;
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,13 @@
|
||||
package net.Chipperfluff.chipi.server;
|
||||
|
||||
import net.Chipperfluff.chipi.SpawnPlacedState;
|
||||
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
|
||||
import net.Chipperfluff.chipi.block.ChipperPortalBlock;
|
||||
import net.Chipperfluff.chipi.item.ModItems;
|
||||
import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.block.StairsBlock;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@ -19,24 +15,18 @@ import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.structure.StructurePlacementData;
|
||||
import net.minecraft.structure.StructureTemplate;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.GameRules;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
|
||||
|
||||
public final class ChipiServerEvents {
|
||||
|
||||
public static final RegistryKey<World> CHIPI_DIMENSION_KEY =
|
||||
RegistryKey.of(RegistryKeys.WORLD, new Identifier("chipi", "chipi_dimension"));
|
||||
|
||||
private static final float RECHARGE_RATE = 0.0005f;
|
||||
private static final float DRAIN_RATE = 0.0008f;
|
||||
RegistryKey.of(RegistryKeys.WORLD, new Identifier("chipi", "chipi_dimension"));
|
||||
|
||||
private static final Identifier SPAWN_STRUCTURE =
|
||||
new Identifier("chipi", "spawn");
|
||||
new Identifier("chipi", "spawn");
|
||||
|
||||
private static MinecraftServer SERVER;
|
||||
|
||||
@ -44,33 +34,14 @@ public final class ChipiServerEvents {
|
||||
|
||||
public static void register() {
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server);
|
||||
ServerTickEvents.END_SERVER_TICK.register(ChipiServerEvents::tickEnergy);
|
||||
ServerTickEvents.END_SERVER_TICK.register(ChipiServerEvents::tickPlayers);
|
||||
ServerTickEvents.END_WORLD_TICK.register(ChipiServerEvents::handleVoidFailsafe);
|
||||
ServerWorldEvents.LOAD.register(ChipiServerEvents::onWorldLoad);
|
||||
}
|
||||
|
||||
private static void tickEnergy(MinecraftServer server) {
|
||||
private static void tickPlayers(MinecraftServer server) {
|
||||
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
|
||||
|
||||
if (!hasFullChipperArmor(player)) continue;
|
||||
|
||||
Float value = player.getDataTracker().get(CHIPI_ENERGY);
|
||||
if (value == null) continue; // ultra-safe, but should never happen now
|
||||
|
||||
boolean inChipi = player.getWorld().getRegistryKey().equals(CHIPI_DIMENSION_KEY);
|
||||
boolean onProtected = isOnProtectedBlock(player);
|
||||
|
||||
float next = value;
|
||||
|
||||
if (!inChipi || onProtected) {
|
||||
next = Math.min(1.0f, value + RECHARGE_RATE);
|
||||
} else {
|
||||
next = Math.max(0.0f, value - DRAIN_RATE);
|
||||
}
|
||||
|
||||
if (next != value) {
|
||||
player.getDataTracker().set(CHIPI_ENERGY, next);
|
||||
}
|
||||
ProtectionAuraHandler.tick(player);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,31 +62,23 @@ public final class ChipiServerEvents {
|
||||
world.getGameRules().get(GameRules.DO_DAYLIGHT_CYCLE).set(false, server);
|
||||
|
||||
SpawnPlacedState state = world.getPersistentStateManager().getOrCreate(
|
||||
SpawnPlacedState::fromNbt,
|
||||
SpawnPlacedState::new,
|
||||
"chipi_spawn"
|
||||
SpawnPlacedState::fromNbt,
|
||||
SpawnPlacedState::new,
|
||||
"chipi_spawn"
|
||||
);
|
||||
|
||||
if (state.placed) return;
|
||||
|
||||
StructureTemplate spawnTemplate =
|
||||
world.getStructureTemplateManager().getTemplate(SPAWN_STRUCTURE).orElse(null);
|
||||
StructureTemplate template =
|
||||
world.getStructureTemplateManager().getTemplate(SPAWN_STRUCTURE).orElse(null);
|
||||
|
||||
if (spawnTemplate == null) return;
|
||||
if (template == null) return;
|
||||
|
||||
BlockPos spawnCenter = new BlockPos(0, 80, 0);
|
||||
BlockPos spawn = new BlockPos(0, 80, 0);
|
||||
|
||||
spawnTemplate.place(
|
||||
world,
|
||||
spawnCenter,
|
||||
spawnCenter,
|
||||
new StructurePlacementData(),
|
||||
world.getRandom(),
|
||||
2
|
||||
);
|
||||
|
||||
world.setSpawnPos(spawnCenter.up(), 0.0f);
|
||||
ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter);
|
||||
template.place(world, spawn, spawn, new StructurePlacementData(), world.getRandom(), 2);
|
||||
world.setSpawnPos(spawn.up(), 0f);
|
||||
ChipiDungeonGenerator.generateInitialLayout(world, spawn);
|
||||
|
||||
state.placed = true;
|
||||
state.markDirty();
|
||||
@ -124,24 +87,4 @@ public final class ChipiServerEvents {
|
||||
public static ServerWorld getChipiWorld() {
|
||||
return SERVER == null ? null : SERVER.getWorld(CHIPI_DIMENSION_KEY);
|
||||
}
|
||||
|
||||
private static boolean hasFullChipperArmor(PlayerEntity player) {
|
||||
PlayerInventory inv = player.getInventory();
|
||||
return inv.getArmorStack(3).isOf(ModItems.CHIPPER_HELMET)
|
||||
&& inv.getArmorStack(2).isOf(ModItems.CHIPPER_CHESTPLATE)
|
||||
&& inv.getArmorStack(1).isOf(ModItems.CHIPPER_LEGGINGS)
|
||||
&& inv.getArmorStack(0).isOf(ModItems.CHIPPER_BOOTS);
|
||||
}
|
||||
|
||||
private static boolean isOnProtectedBlock(PlayerEntity player) {
|
||||
BlockPos pos = player.getBlockPos();
|
||||
BlockState state = player.getWorld().getBlockState(pos);
|
||||
|
||||
if (!state.contains(Properties.WATERLOGGED) || !state.get(Properties.WATERLOGGED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return state.getBlock() instanceof StairsBlock
|
||||
|| state.getBlock() instanceof SlabBlock;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user