Implement Protection Aura and adjust MepEntity behavior; refactor armor items
This commit is contained in:
parent
e3166bbf1a
commit
ef64fd61f6
@ -14,6 +14,7 @@ public class ChipiStatusBar {
|
|||||||
private static final int ORANGE = 0xFFFF8000;
|
private static final int ORANGE = 0xFFFF8000;
|
||||||
private static final int OUTLINE_LIGHT = 0xFFB0B0B0;
|
private static final int OUTLINE_LIGHT = 0xFFB0B0B0;
|
||||||
private static final int OUTLINE_DARK = 0xFF404040;
|
private static final int OUTLINE_DARK = 0xFF404040;
|
||||||
|
private static final int TEXT_COLOR = 0xFFFFFFFF;
|
||||||
private static final int BAR_HEIGHT = 6;
|
private static final int BAR_HEIGHT = 6;
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
@ -33,17 +34,33 @@ public class ChipiStatusBar {
|
|||||||
int screenWidth = client.getWindow().getScaledWidth();
|
int screenWidth = client.getWindow().getScaledWidth();
|
||||||
int screenHeight = client.getWindow().getScaledHeight();
|
int screenHeight = client.getWindow().getScaledHeight();
|
||||||
|
|
||||||
int barWidth = (int)(80 * 0.8f);
|
|
||||||
int centerX = screenWidth / 2;
|
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);
|
int fillWidth = (int)(barWidth * value);
|
||||||
|
|
||||||
float alpha = value >= 1f ? 0.65f :
|
// ============================================================
|
||||||
|
// Alpha behavior
|
||||||
|
// ============================================================
|
||||||
|
float alpha =
|
||||||
|
value >= 1f ? 0.65f :
|
||||||
value < 0.10f
|
value < 0.10f
|
||||||
? (float)(0.6f + 0.4f * Math.sin(System.currentTimeMillis() / 120.0))
|
? (float)(0.6f + 0.4f * Math.sin(System.currentTimeMillis() / 120.0))
|
||||||
: 1f;
|
: 1f;
|
||||||
@ -51,19 +68,36 @@ public class ChipiStatusBar {
|
|||||||
int outlineDark = applyAlpha(OUTLINE_DARK, alpha);
|
int outlineDark = applyAlpha(OUTLINE_DARK, alpha);
|
||||||
int outlineLight = applyAlpha(OUTLINE_LIGHT, alpha);
|
int outlineLight = applyAlpha(OUTLINE_LIGHT, alpha);
|
||||||
int fillColor = applyAlpha(ORANGE, 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);
|
// Draw outline
|
||||||
ctx.fill(left - 1, barY, left, barY + BAR_HEIGHT, outlineDark);
|
// ============================================================
|
||||||
ctx.fill(right, barY, right + 1, barY + BAR_HEIGHT, outlineLight);
|
ctx.fill(hungerLeft - 1, barY - 1, hungerRight + 1, barY, outlineDark);
|
||||||
ctx.fill(left, barY, left + fillWidth, barY + BAR_HEIGHT, fillColor);
|
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(
|
ctx.drawText(
|
||||||
client.textRenderer,
|
client.textRenderer,
|
||||||
String.format("%03d%%", Math.round(value * 100f)),
|
text,
|
||||||
right + 6,
|
textX,
|
||||||
barY - 2,
|
textY,
|
||||||
fillColor,
|
textColor,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,7 @@ import net.minecraft.block.SlabBlock;
|
|||||||
import net.minecraft.block.StairsBlock;
|
import net.minecraft.block.StairsBlock;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.ai.goal.ActiveTargetGoal;
|
import net.minecraft.entity.ai.goal.*;
|
||||||
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.attribute.DefaultAttributeContainer;
|
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
import net.minecraft.entity.attribute.EntityAttributes;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
@ -25,6 +20,7 @@ import net.minecraft.util.Hand;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
|
||||||
import net.Chipperfluff.chipi.item.ModItems;
|
import net.Chipperfluff.chipi.item.ModItems;
|
||||||
import net.Chipperfluff.chipi.sound.ModSounds;
|
import net.Chipperfluff.chipi.sound.ModSounds;
|
||||||
import net.Chipperfluff.chipi.util.TickScheduler;
|
import net.Chipperfluff.chipi.util.TickScheduler;
|
||||||
@ -32,6 +28,7 @@ import net.Chipperfluff.chipi.util.TickScheduler;
|
|||||||
public class MepEntity extends PathAwareEntity {
|
public class MepEntity extends PathAwareEntity {
|
||||||
|
|
||||||
private static final int FORGET_TARGET_AFTER_TICKS = 100;
|
private static final int FORGET_TARGET_AFTER_TICKS = 100;
|
||||||
|
private static final int DESPAWN_DISTANCE = 300;
|
||||||
|
|
||||||
private boolean angryAtPlayer = false;
|
private boolean angryAtPlayer = false;
|
||||||
private int ticksSinceLastSeen = 0;
|
private int ticksSinceLastSeen = 0;
|
||||||
@ -56,7 +53,8 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
this,
|
this,
|
||||||
PlayerEntity.class,
|
PlayerEntity.class,
|
||||||
true,
|
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);
|
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
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
|
||||||
|
// --- DISTANCE DESPAWN ONLY ---
|
||||||
|
if (!this.getWorld().isClient && isTooFarFromAllPlayers()) {
|
||||||
|
this.discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LivingEntity target = this.getTarget();
|
LivingEntity target = this.getTarget();
|
||||||
|
|
||||||
if (!(target instanceof PlayerEntity player)) {
|
if (!(target instanceof PlayerEntity player)) {
|
||||||
@ -92,7 +108,7 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlayerProtected(player)) {
|
if (isHardIgnored(player)) {
|
||||||
clearTarget();
|
clearTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -118,7 +134,27 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
this.getNavigation().stop();
|
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();
|
BlockPos pos = player.getBlockPos();
|
||||||
BlockState state = player.getWorld().getBlockState(pos);
|
BlockState state = player.getWorld().getBlockState(pos);
|
||||||
|
|
||||||
@ -130,19 +166,7 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
|| state.getBlock() instanceof SlabBlock;
|
|| state.getBlock() instanceof SlabBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Despawn prevention ===
|
// === MILKING ===
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean cannotDespawn() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canImmediatelyDespawn(double distanceSquared) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === MILKING (FEVER DREAM EDITION) ===
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionResult interactMob(PlayerEntity player, Hand hand) {
|
public ActionResult interactMob(PlayerEntity player, Hand hand) {
|
||||||
@ -154,7 +178,6 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
stack.decrement(1);
|
stack.decrement(1);
|
||||||
player.giveItemStack(new ItemStack(ModItems.MEP_MILK));
|
player.giveItemStack(new ItemStack(ModItems.MEP_MILK));
|
||||||
|
|
||||||
// ---- base sound ----
|
|
||||||
float basePitch = 0.3f + this.random.nextFloat() * 1.9f;
|
float basePitch = 0.3f + this.random.nextFloat() * 1.9f;
|
||||||
float baseVolume = 0.9f + this.random.nextFloat() * 0.6f;
|
float baseVolume = 0.9f + this.random.nextFloat() * 0.6f;
|
||||||
|
|
||||||
@ -167,10 +190,8 @@ public class MepEntity extends PathAwareEntity {
|
|||||||
basePitch
|
basePitch
|
||||||
);
|
);
|
||||||
|
|
||||||
// ---- single delayed echo (10% chance) ----
|
|
||||||
if (this.random.nextFloat() < 0.10f) {
|
if (this.random.nextFloat() < 0.10f) {
|
||||||
|
int delay = 10 + this.random.nextInt(21);
|
||||||
int delay = 10 + this.random.nextInt(21); // 10–30 ticks
|
|
||||||
|
|
||||||
float echoPitch = basePitch * 0.5f;
|
float echoPitch = basePitch * 0.5f;
|
||||||
float echoVolume = baseVolume * 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.Heightmap;
|
||||||
import net.minecraft.world.ServerWorldAccess;
|
import net.minecraft.world.ServerWorldAccess;
|
||||||
import net.minecraft.world.biome.Biome;
|
import net.minecraft.world.biome.Biome;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
|
|
||||||
|
|
||||||
public final class SpawnLogic {
|
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 =
|
private static final RegistryKey<Biome> VOID_BIOME =
|
||||||
RegistryKey.of(RegistryKeys.BIOME, new Identifier("chipi", "void"));
|
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();
|
int y = pos.getY();
|
||||||
if (y < 87 || y > 90) return false;
|
if (y < 87 || y > 90) return false;
|
||||||
if (pos.getZ() < 18) return false;
|
if (pos.getZ() < 18) return false;
|
||||||
|
|
||||||
|
// --- BLOCK CHECK ---
|
||||||
BlockState below = world.getBlockState(pos.down());
|
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.minecraft.util.Identifier;
|
||||||
import net.Chipperfluff.chipi.item.tool.ChipperToolMaterial;
|
import net.Chipperfluff.chipi.item.tool.ChipperToolMaterial;
|
||||||
import net.Chipperfluff.chipi.item.MepMilkItem;
|
import net.Chipperfluff.chipi.item.MepMilkItem;
|
||||||
import net.minecraft.item.Items;
|
|
||||||
import net.Chipperfluff.chipi.item.armor.ChipperArmorItem;
|
|
||||||
|
|
||||||
public class ModItems {
|
public class ModItems {
|
||||||
|
|
||||||
@ -95,25 +94,25 @@ public class ModItems {
|
|||||||
public static final Item CHIPPER_HELMET = Registry.register(
|
public static final Item CHIPPER_HELMET = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(ChipiMod.MOD_ID, "chipper_helmet"),
|
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(
|
public static final Item CHIPPER_CHESTPLATE = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(ChipiMod.MOD_ID, "chipper_chestplate"),
|
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(
|
public static final Item CHIPPER_LEGGINGS = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(ChipiMod.MOD_ID, "chipper_leggings"),
|
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(
|
public static final Item CHIPPER_BOOTS = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(ChipiMod.MOD_ID, "chipper_boots"),
|
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 =====
|
// ===== 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
|
@Override
|
||||||
public int getDurability(ArmorItem.Type type) {
|
public int getDurability(ArmorItem.Type type) {
|
||||||
return 10_000;
|
return 1_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -0,0 +1,135 @@
|
|||||||
|
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.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 ProtectionAuraHandler() {}
|
||||||
|
|
||||||
|
/* ==========================================================
|
||||||
|
ENTRY POINT — CALLED SERVER SIDE
|
||||||
|
========================================================== */
|
||||||
|
|
||||||
|
public static void tick(PlayerEntity player) {
|
||||||
|
if (!hasFullChipperArmor(player)) return;
|
||||||
|
|
||||||
|
Float value = player.getDataTracker().get(CHIPI_ENERGY);
|
||||||
|
if (value == null) return;
|
||||||
|
|
||||||
|
float durabilityFactor = getDurabilityFactor(player);
|
||||||
|
if (durabilityFactor <= 0f) return;
|
||||||
|
|
||||||
|
boolean inChipi = isInChipi(player.getWorld());
|
||||||
|
boolean protectedAura = isOnProtectedBlock(player);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == value) return;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
// PERCENT-BASED DURABILITY LOGIC
|
||||||
|
// ─────────────────────────────────────────────
|
||||||
|
int oldPercent = (int) Math.floor(value * 100f);
|
||||||
|
int newPercent = (int) Math.floor(next * 100f);
|
||||||
|
|
||||||
|
int delta = Math.abs(newPercent - oldPercent);
|
||||||
|
|
||||||
|
if (delta > 0) {
|
||||||
|
damageArmor(player, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.getDataTracker().set(CHIPI_ENERGY, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================
|
||||||
|
ARMOR CHECK
|
||||||
|
========================================================== */
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================
|
||||||
|
DURABILITY LOGIC
|
||||||
|
========================================================== */
|
||||||
|
|
||||||
|
private static float getDurabilityFactor(PlayerEntity player) {
|
||||||
|
int max = 0;
|
||||||
|
int current = 0;
|
||||||
|
|
||||||
|
for (ItemStack stack : player.getArmorItems()) {
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
max += stack.getMaxDamage();
|
||||||
|
current += (stack.getMaxDamage() - stack.getDamage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max <= 0) return 0f;
|
||||||
|
return Math.max(0f, Math.min(1f, (float) current / max));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void damageArmor(PlayerEntity player, int amount) {
|
||||||
|
for (ItemStack stack : player.getArmorItems()) {
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
stack.damage(amount, player, p -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================
|
||||||
|
AURA CONDITIONS
|
||||||
|
========================================================== */
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 value = player.getDataTracker().get(CHIPI_ENERGY);
|
||||||
|
if (value == null || value <= 0f) return false;
|
||||||
|
|
||||||
|
return getDurabilityFactor(player) > 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,13 @@
|
|||||||
package net.Chipperfluff.chipi.server;
|
package net.Chipperfluff.chipi.server;
|
||||||
|
|
||||||
import net.Chipperfluff.chipi.SpawnPlacedState;
|
import net.Chipperfluff.chipi.SpawnPlacedState;
|
||||||
|
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
|
||||||
import net.Chipperfluff.chipi.block.ChipperPortalBlock;
|
import net.Chipperfluff.chipi.block.ChipperPortalBlock;
|
||||||
import net.Chipperfluff.chipi.item.ModItems;
|
|
||||||
import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator;
|
import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
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.ServerTickEvents;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
|
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.PlayerEntity;
|
||||||
import net.minecraft.entity.player.PlayerInventory;
|
|
||||||
import net.minecraft.registry.RegistryKey;
|
import net.minecraft.registry.RegistryKey;
|
||||||
import net.minecraft.registry.RegistryKeys;
|
import net.minecraft.registry.RegistryKeys;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@ -19,24 +15,18 @@ import net.minecraft.server.network.ServerPlayerEntity;
|
|||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.structure.StructurePlacementData;
|
import net.minecraft.structure.StructurePlacementData;
|
||||||
import net.minecraft.structure.StructureTemplate;
|
import net.minecraft.structure.StructureTemplate;
|
||||||
import net.minecraft.state.property.Properties;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.GameRules;
|
import net.minecraft.world.GameRules;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
|
|
||||||
|
|
||||||
public final class ChipiServerEvents {
|
public final class ChipiServerEvents {
|
||||||
|
|
||||||
public static final RegistryKey<World> CHIPI_DIMENSION_KEY =
|
public static final RegistryKey<World> CHIPI_DIMENSION_KEY =
|
||||||
RegistryKey.of(RegistryKeys.WORLD, new Identifier("chipi", "chipi_dimension"));
|
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;
|
|
||||||
|
|
||||||
private static final Identifier SPAWN_STRUCTURE =
|
private static final Identifier SPAWN_STRUCTURE =
|
||||||
new Identifier("chipi", "spawn");
|
new Identifier("chipi", "spawn");
|
||||||
|
|
||||||
private static MinecraftServer SERVER;
|
private static MinecraftServer SERVER;
|
||||||
|
|
||||||
@ -44,33 +34,14 @@ public final class ChipiServerEvents {
|
|||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server);
|
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);
|
ServerTickEvents.END_WORLD_TICK.register(ChipiServerEvents::handleVoidFailsafe);
|
||||||
ServerWorldEvents.LOAD.register(ChipiServerEvents::onWorldLoad);
|
ServerWorldEvents.LOAD.register(ChipiServerEvents::onWorldLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tickEnergy(MinecraftServer server) {
|
private static void tickPlayers(MinecraftServer server) {
|
||||||
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
|
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
|
||||||
|
ProtectionAuraHandler.tick(player);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,31 +62,23 @@ public final class ChipiServerEvents {
|
|||||||
world.getGameRules().get(GameRules.DO_DAYLIGHT_CYCLE).set(false, server);
|
world.getGameRules().get(GameRules.DO_DAYLIGHT_CYCLE).set(false, server);
|
||||||
|
|
||||||
SpawnPlacedState state = world.getPersistentStateManager().getOrCreate(
|
SpawnPlacedState state = world.getPersistentStateManager().getOrCreate(
|
||||||
SpawnPlacedState::fromNbt,
|
SpawnPlacedState::fromNbt,
|
||||||
SpawnPlacedState::new,
|
SpawnPlacedState::new,
|
||||||
"chipi_spawn"
|
"chipi_spawn"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (state.placed) return;
|
if (state.placed) return;
|
||||||
|
|
||||||
StructureTemplate spawnTemplate =
|
StructureTemplate template =
|
||||||
world.getStructureTemplateManager().getTemplate(SPAWN_STRUCTURE).orElse(null);
|
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(
|
template.place(world, spawn, spawn, new StructurePlacementData(), world.getRandom(), 2);
|
||||||
world,
|
world.setSpawnPos(spawn.up(), 0f);
|
||||||
spawnCenter,
|
ChipiDungeonGenerator.generateInitialLayout(world, spawn);
|
||||||
spawnCenter,
|
|
||||||
new StructurePlacementData(),
|
|
||||||
world.getRandom(),
|
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
world.setSpawnPos(spawnCenter.up(), 0.0f);
|
|
||||||
ChipiDungeonGenerator.generateInitialLayout(world, spawnCenter);
|
|
||||||
|
|
||||||
state.placed = true;
|
state.placed = true;
|
||||||
state.markDirty();
|
state.markDirty();
|
||||||
@ -124,24 +87,4 @@ public final class ChipiServerEvents {
|
|||||||
public static ServerWorld getChipiWorld() {
|
public static ServerWorld getChipiWorld() {
|
||||||
return SERVER == null ? null : SERVER.getWorld(CHIPI_DIMENSION_KEY);
|
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