continue/armor_aura #1

Merged
Jack merged 3 commits from continue/armor_aura into main 2025-12-20 01:48:43 +00:00
8 changed files with 295 additions and 161 deletions
Showing only changes of commit ef64fd61f6 - Show all commits

View File

@ -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
); );
} }

View File

@ -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); // 1030 ticks
float echoPitch = basePitch * 0.5f; float echoPitch = basePitch * 0.5f;
float echoVolume = baseVolume * 0.5f; float echoVolume = baseVolume * 0.5f;

View File

@ -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;
} }
} }

View File

@ -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 =====

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
} }