From 777cae5e603fb04dc224007eb9f55759980c20e3 Mon Sep 17 00:00:00 2001 From: Chemiofitor4096 Date: Wed, 24 Jun 2026 12:49:43 +0800 Subject: [PATCH] update v1.1.0 --- build.gradle | 7 +- gradle.properties | 5 +- .../ProtectionEngineering.java | 10 +++ .../api/AttachmentsData.java | 46 +++++++++---- .../block/WorkbenchBlock.java | 12 ++++ .../client/PEHormoneOverlay.java | 5 +- .../compat/DetailArmorBarCompat.java | 35 ++++++++++ .../entity/MissileEntity.java | 4 ++ .../entity/ThrustEntity.java | 16 ++--- .../event/PEGameEvents.java | 40 ++++++++++++ .../event/PENetworkEvents.java | 6 +- .../item/AttachmentHostArmorItem.java | 61 +++--------------- .../item/AttachmentItem.java | 19 ++++-- .../menu/WorkbenchMenu.java | 13 +--- .../registry/PEArmorMaterials.java | 2 +- .../assets/protectionengineering/sounds.json | 7 +- .../textures/gui/engineer_armor_bar.png | Bin 0 -> 956 bytes 17 files changed, 184 insertions(+), 104 deletions(-) create mode 100644 src/main/java/com/chemiofitor/protection_engineering/compat/DetailArmorBarCompat.java create mode 100644 src/main/resources/assets/protectionengineering/textures/gui/engineer_armor_bar.png diff --git a/build.gradle b/build.gradle index a7f8f22..e09df1e 100644 --- a/build.gradle +++ b/build.gradle @@ -119,8 +119,11 @@ configurations { repositories { maven { url "https://maven.createmod.net" } // Ponder, Flywheel - maven { url = "https://maven.ithundxr.dev/snapshots" } // Registrate + maven { url "https://maven.ithundxr.dev/snapshots" } // Registrate maven { url "https://maven.blamejared.com" } // JEI + maven { + url "https://api.modrinth.com/maven" + } } dependencies { @@ -134,6 +137,8 @@ dependencies { compileOnly("mezz.jei:jei-${minecraft_version}-neoforge-api:${jei_version}") runtimeOnly("mezz.jei:jei-${minecraft_version}-neoforge:${jei_version}") + + implementation("maven.modrinth:detail-armor-bar-reconstructed:${detail_armor_bar_version}") } // This block of code expands all declared replace properties in the specified resource targets. diff --git a/gradle.properties b/gradle.properties index efa8e9c..d30ca52 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ mod_name=Protection Engineering # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=MIT # The mod version. See https://semver.org/ -mod_version=1.0.4 +mod_version=1.1.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html @@ -45,4 +45,5 @@ create_version_range=[6.0.8,) ponder_version=1.0.82 flywheel_version=1.0.6 registrate_version=MC1.21-1.3.0+67 -jei_version=19.27.0.340 \ No newline at end of file +jei_version=19.27.0.340 +detail_armor_bar_version=5.0.2 \ No newline at end of file diff --git a/src/main/java/com/chemiofitor/protection_engineering/ProtectionEngineering.java b/src/main/java/com/chemiofitor/protection_engineering/ProtectionEngineering.java index 1865767..81dd545 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/ProtectionEngineering.java +++ b/src/main/java/com/chemiofitor/protection_engineering/ProtectionEngineering.java @@ -1,5 +1,6 @@ package com.chemiofitor.protection_engineering; +import com.chemiofitor.protection_engineering.compat.DetailArmorBarCompat; import com.chemiofitor.protection_engineering.config.PEConfig; import com.chemiofitor.protection_engineering.config.PEServerConfig; import com.chemiofitor.protection_engineering.data.PEDataGen; @@ -12,11 +13,13 @@ import com.chemiofitor.protection_engineering.registry.PEWorkbench; import com.mojang.logging.LogUtils; import com.tterrag.registrate.Registrate; +import net.neoforged.neoforge.common.NeoForge; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; +import net.neoforged.fml.ModList; import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfig; import org.slf4j.Logger; @@ -56,6 +59,13 @@ public ProtectionEngineering(IEventBus modEventBus, ModContainer modContainer) { modEventBus.addListener(EventPriority.HIGHEST, PEDataGen::gatherDataHighPriority); modEventBus.addListener(EventPriority.LOWEST, PEDataGen::gatherData); + + if (ModList.get().isLoaded("detailab")) { + modEventBus.addListener(DetailArmorBarCompat::onClientSetup); + } + + NeoForge.EVENT_BUS.register( + new com.chemiofitor.protection_engineering.event.PEGameEvents.EquipmentHandler()); } // ── Helpers ───────────────────────────────────────────────── diff --git a/src/main/java/com/chemiofitor/protection_engineering/api/AttachmentsData.java b/src/main/java/com/chemiofitor/protection_engineering/api/AttachmentsData.java index f4bcb13..e450eae 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/api/AttachmentsData.java +++ b/src/main/java/com/chemiofitor/protection_engineering/api/AttachmentsData.java @@ -6,26 +6,44 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; /** - * 宿主物品上的附件数据。 - *

- * 用 {@link Map}{@code } 存储,只存非空值。 - * 不同宿主物品(头盔、胸甲、锯剑)各自只存自己支持的槽位子集, - * 没有"定长 N"的限制。 - *

- * 不可变 —— {@link #with} / {@link #without} 返回新实例。 + * 宿主物品上的附件数据。自定义 equals/hashCode 用 ItemStack.matches 做值比较。 */ -public record AttachmentsData(Map slots) { +public final class AttachmentsData { + + private final Map slots; public static final AttachmentsData EMPTY = new AttachmentsData(Map.of()); - /** 防御性拷贝,确保不可变 */ - public AttachmentsData { - slots = Map.copyOf(slots); + public AttachmentsData(Map slots) { + this.slots = Map.copyOf(slots); + } + + public Map slots() { return slots; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AttachmentsData other)) return false; + if (slots.size() != other.slots.size()) return false; + for (var entry : slots.entrySet()) { + ItemStack otherStack = other.slots.get(entry.getKey()); + if (otherStack == null) return false; + // 仅比较物品类型,忽略组件变化(防止状态更新触发装备音效) + if (!ItemStack.matches(entry.getValue(), otherStack)) return false; + } + return true; + } + + @Override + public int hashCode() { + int h = 0; + for (var entry : slots.entrySet()) { + h += Objects.hash(entry.getKey(), ItemStack.hashItemAndComponents(entry.getValue())); + } + return h; } // ── Codec ────────────────────────────────────────────────── diff --git a/src/main/java/com/chemiofitor/protection_engineering/block/WorkbenchBlock.java b/src/main/java/com/chemiofitor/protection_engineering/block/WorkbenchBlock.java index 61daddb..9f33fc5 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/block/WorkbenchBlock.java +++ b/src/main/java/com/chemiofitor/protection_engineering/block/WorkbenchBlock.java @@ -8,6 +8,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; @@ -16,6 +17,9 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; import javax.annotation.Nullable; @@ -80,4 +84,12 @@ protected void onRemove(BlockState state, Level level, BlockPos pos, super.onRemove(state, level, pos, newState, moved); } } + + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return Shapes.or( + Shapes.box(0.0D / 16, 0.0D / 16, 0.0D / 16, 16.0D / 16, 9.0D / 16, 16.0D / 16), + Shapes.box(1.0D / 16, 9.0D / 16, 1.0D / 16, 15.0D / 16, 11.0D / 16, 15.0D / 16) + ); + } } diff --git a/src/main/java/com/chemiofitor/protection_engineering/client/PEHormoneOverlay.java b/src/main/java/com/chemiofitor/protection_engineering/client/PEHormoneOverlay.java index 4ad0174..2ea3ac0 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/client/PEHormoneOverlay.java +++ b/src/main/java/com/chemiofitor/protection_engineering/client/PEHormoneOverlay.java @@ -3,6 +3,7 @@ import com.chemiofitor.protection_engineering.ProtectionEngineering; import com.chemiofitor.protection_engineering.api.IAttachment; import com.chemiofitor.protection_engineering.api.IAttachmentHost; +import com.chemiofitor.protection_engineering.config.PEServerConfig; import com.chemiofitor.protection_engineering.item.SpyglassItem; import com.chemiofitor.protection_engineering.registry.PEDataComponents; import com.chemiofitor.protection_engineering.registry.PEItems; @@ -121,7 +122,7 @@ private static float getOverlayAlpha(LocalPlayer player) { long cooldownEnd = getHormoneCooldownEnd(player); if (cooldownEnd <= 0) return 0; - long activatedAt = cooldownEnd - 1200; // COOLDOWN_TICKS + long activatedAt = cooldownEnd - PEServerConfig.HORMONE_COOLDOWN_TICKS.get(); long elapsed = now - activatedAt; if (elapsed < 0 || elapsed >= 200) return 0; // EFFECT_DURATION = 200 @@ -156,7 +157,7 @@ private static float getFovBonus(LocalPlayer player) { long cooldownEnd = getHormoneCooldownEnd(player); if (cooldownEnd <= 0) return 0; - long activatedAt = cooldownEnd - 1200; + long activatedAt = cooldownEnd - PEServerConfig.HORMONE_COOLDOWN_TICKS.get(); long elapsed = now - activatedAt; if (elapsed < 0 || elapsed >= 60) return 0; diff --git a/src/main/java/com/chemiofitor/protection_engineering/compat/DetailArmorBarCompat.java b/src/main/java/com/chemiofitor/protection_engineering/compat/DetailArmorBarCompat.java new file mode 100644 index 0000000..5d09382 --- /dev/null +++ b/src/main/java/com/chemiofitor/protection_engineering/compat/DetailArmorBarCompat.java @@ -0,0 +1,35 @@ +package com.chemiofitor.protection_engineering.compat; + +import com.chemiofitor.protection_engineering.ProtectionEngineering; +import com.chemiofitor.protection_engineering.registry.PEItems; +import com.redlimerl.detailab.api.DetailArmorBarAPI; +import com.redlimerl.detailab.api.render.ArmorBarRenderManager; +import com.redlimerl.detailab.api.render.BarRenderManager; +import com.redlimerl.detailab.api.render.TextureOffset; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; + +import java.awt.*; + +public class DetailArmorBarCompat { + + private static final ResourceLocation TEX = ResourceLocation.fromNamespaceAndPath( + ProtectionEngineering.MODID, "textures/gui/engineer_armor_bar.png"); + // 纹理 36×9: 半格(0,0) 满格(9,0) 半轮廓(18,0) 全轮廓(27,0) + + public static void onClientSetup(FMLClientSetupEvent event) { + event.enqueueWork(() -> DetailArmorBarAPI.customArmorBarBuilder() + .armor(PEItems.ENGINEER_HOOD.get(), + PEItems.ENGINEER_CHESTPLATE.get(), + PEItems.ENGINEER_LEGGINGS.get(), + PEItems.ENGINEER_BOOTS.get()) + .render(stack -> new ArmorBarRenderManager( + new BarRenderManager.Texture(TEX, 64, 32, new TextureOffset(9, 0)), + new BarRenderManager.Texture(TEX, 64, 32, new TextureOffset(0, 0)), + new BarRenderManager.Texture(TEX, 64, 32, new TextureOffset(27, 0)), + new BarRenderManager.Texture(TEX, 64, 32, new TextureOffset(18, 0)), + Color.WHITE + )) + .register()); + } +} diff --git a/src/main/java/com/chemiofitor/protection_engineering/entity/MissileEntity.java b/src/main/java/com/chemiofitor/protection_engineering/entity/MissileEntity.java index 83b0123..4c7cc88 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/entity/MissileEntity.java +++ b/src/main/java/com/chemiofitor/protection_engineering/entity/MissileEntity.java @@ -358,6 +358,8 @@ protected void defineSynchedData(SynchedEntityData.Builder builder) { protected void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.life = tag.getInt("Life"); + this.engineSoundTimer = tag.getInt("EngineSound"); + this.warningSoundTimer = tag.getInt("WarningSound"); if (tag.hasUUID("TargetUUID")) { this.targetEntityUUID = tag.getUUID("TargetUUID"); } @@ -376,6 +378,8 @@ protected void readAdditionalSaveData(CompoundTag tag) { protected void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.putInt("Life", life); + tag.putInt("EngineSound", engineSoundTimer); + tag.putInt("WarningSound", warningSoundTimer); if (targetEntityUUID != null) { tag.putUUID("TargetUUID", targetEntityUUID); } diff --git a/src/main/java/com/chemiofitor/protection_engineering/entity/ThrustEntity.java b/src/main/java/com/chemiofitor/protection_engineering/entity/ThrustEntity.java index 447102a..a2bd877 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/entity/ThrustEntity.java +++ b/src/main/java/com/chemiofitor/protection_engineering/entity/ThrustEntity.java @@ -53,13 +53,17 @@ public void init(LivingEntity target, double targetSpeed, boolean soulFire, int @Override public boolean shouldRender(double x, double y, double z) { return false; } + @Override + public void remove(RemovalReason reason) { + if (target != null) ACTIVE.remove(target.getUUID()); + super.remove(reason); + } + @Override public void tick() { super.tick(); if (target == null || !target.isAlive() || life >= maxLife) { - ProtectionEngineering.LOGGER.info("Thrust DISCARD life={}/{} targetNull={} targetDead={}", - life, maxLife, target == null, target != null && !target.isAlive()); - if (target != null) ACTIVE.remove(target.getUUID()); + ProtectionEngineering.LOGGER.debug("Thrust DISCARD life={}/{}", life, maxLife); this.discard(); return; } @@ -75,11 +79,6 @@ public void tick() { look.x * 0.1 + (look.x * targetSpeed - vel.x) * 0.5, look.y * 0.1 + (look.y * targetSpeed - vel.y) * 0.5, look.z * 0.1 + (look.z * targetSpeed - vel.z) * 0.5)); - if (life % 20 == 0) { - Vec3 v = target.getDeltaMovement(); - ProtectionEngineering.LOGGER.info("Thrust life={}/{} thrust={} vx={} vy={} vz={} fly={}", - life, maxLife, targetSpeed, v.x, v.y, v.z, target.isFallFlying()); - } // 粒子 if (target.level() instanceof ServerLevel sl) { @@ -89,7 +88,6 @@ public void tick() { pos.x, pos.y, pos.z, 3, 0.3, 0.05, 0.3, 0.02); } } else { - ACTIVE.remove(target.getUUID()); this.discard(); return; } diff --git a/src/main/java/com/chemiofitor/protection_engineering/event/PEGameEvents.java b/src/main/java/com/chemiofitor/protection_engineering/event/PEGameEvents.java index 741b4dd..310edc6 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/event/PEGameEvents.java +++ b/src/main/java/com/chemiofitor/protection_engineering/event/PEGameEvents.java @@ -15,6 +15,7 @@ import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.item.ItemStack; @@ -203,4 +204,43 @@ private static boolean hasImmunity(Player player, Holder effect) { } return false; } + + // ── 装备变更(LivingEquipmentChangeEvent) ────────────────── + + /** 由 ProtectionEngineering 构造器注册到 NeoForge.EVENT_BUS */ + public static final class EquipmentHandler { + @SubscribeEvent + public void onEquipmentChange(net.neoforged.neoforge.event.entity.living.LivingEquipmentChangeEvent event) { + var slot = event.getSlot(); + if (slot.getType() != EquipmentSlot.Type.HUMANOID_ARMOR) return; + + ItemStack from = event.getFrom(); + ItemStack to = event.getTo(); + + + LivingEntity entity = event.getEntity(); + + // 卸下 + if (from.getItem() instanceof IAttachmentHost) { + for (var entry : ((IAttachmentHost) from.getItem()).getAttachments(from).slots().entrySet()) { + if (entry.getValue().getItem() instanceof IAttachment att) { + att.onUnequip(entry.getValue(), from, entity); + } + } + } + + // 装备 + if (to.getItem() instanceof IAttachmentHost host) { + var data = host.getAttachments(to); + for (var entry : data.slots().entrySet()) { + var attStack = entry.getValue(); + if (attStack.getItem() instanceof IAttachment att) { + att.onEquip(attStack, to, entity); + data = data.with(entry.getKey(), attStack); + } + } + host.setAttachments(to, data); + } + } + } } diff --git a/src/main/java/com/chemiofitor/protection_engineering/event/PENetworkEvents.java b/src/main/java/com/chemiofitor/protection_engineering/event/PENetworkEvents.java index e65cdf2..3ec82b1 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/event/PENetworkEvents.java +++ b/src/main/java/com/chemiofitor/protection_engineering/event/PENetworkEvents.java @@ -31,7 +31,9 @@ public static void registerPayloads(RegisterPayloadHandlersEvent event) { (payload, context) -> context.enqueueWork(() -> { var player = context.player(); - EquipmentSlot slot = EquipmentSlot.values()[payload.armorIndex()]; + int idx = payload.armorIndex(); + if (idx < 0 || idx >= EquipmentSlot.values().length) return; + EquipmentSlot slot = EquipmentSlot.values()[idx]; ItemStack armor = player.getItemBySlot(slot); if (!(armor.getItem() instanceof IAttachmentHost host)) return; @@ -44,8 +46,6 @@ public static void registerPayloads(RegisterPayloadHandlersEvent event) { attItem.onActivatePress(attachment, armor, player); } host.setAttachments(armor, data.with(type, attachment)); - // 装备数据修改后须写回槽位,触发客户端同步 - player.setItemSlot(slot, armor); }) ); diff --git a/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentHostArmorItem.java b/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentHostArmorItem.java index 86de53a..4dce033 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentHostArmorItem.java +++ b/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentHostArmorItem.java @@ -8,7 +8,6 @@ import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ArmorItem; @@ -17,9 +16,7 @@ import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; -import java.util.IdentityHashMap; import java.util.List; -import java.util.Map; import java.util.Set; import static com.chemiofitor.protection_engineering.registry.PEDataComponents.ATTACHMENTS; @@ -69,71 +66,29 @@ public AttachmentsData getAttachments(ItemStack host) { public void setAttachments(ItemStack host, AttachmentsData data) { host.set(ATTACHMENTS.get(), data); } - // ── Tick ──────────────────────────────────────────────────── - private static final Map WORN_TRACKER = new IdentityHashMap<>(); - @Override public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) { if (!(entity instanceof LivingEntity living)) return; - - boolean isWorn = living.getItemBySlot(getEquipmentSlot()) == stack; - boolean wasWorn = WORN_TRACKER.getOrDefault(stack, false); - - if (!wasWorn && isWorn) { - // 刚穿上 → 通知所有附件 - AttachmentsData data = getAttachments(stack); - for (var entry : data.slots().entrySet()) { - ItemStack attached = entry.getValue(); - if (attached.getItem() instanceof IAttachment att) { - att.onEquip(attached, stack, living); - data = data.with(entry.getKey(), attached); - } - } - setAttachments(stack, data); - syncEquipmentIfPlayer(living, stack); - } else if (wasWorn && !isWorn) { - // 刚脱下 → 通知所有附件 - AttachmentsData data = getAttachments(stack); - for (var entry : data.slots().entrySet()) { - ItemStack attached = entry.getValue(); - if (attached.getItem() instanceof IAttachment att) { - att.onUnequip(attached, stack, living); - } - } - } - - WORN_TRACKER.put(stack, isWorn); - - if (!isWorn) return; - - // 服务端 tick + 写回;客户端自动从 networkSynchronized 数据组件同步 + if (living.getItemBySlot(getEquipmentSlot()) != stack) return; if (level.isClientSide()) return; AttachmentsData data = getAttachments(stack); boolean mutated = false; for (var entry : data.slots().entrySet()) { - ItemStack attachmentStack = entry.getValue(); + var key = entry.getKey(); + ItemStack attachmentStack = entry.getValue().copy(); if (attachmentStack.getItem() instanceof IAttachment attachment) { - attachment.onTick(attachmentStack, stack, living, entry.getKey()); - data = data.with(entry.getKey(), attachmentStack); - mutated = true; + attachment.onTick(attachmentStack, stack, living, key); + if (!ItemStack.matches(entry.getValue(), attachmentStack)) { + data = data.with(key, attachmentStack); + mutated = true; + } } } if (mutated) { setAttachments(stack, data); - syncEquipmentIfPlayer(living, stack); - } - } - - /** 数据组件变更后强制同步到客户端 */ - private void syncEquipmentIfPlayer(LivingEntity living, ItemStack stack) { - if (living instanceof ServerPlayer sp) { - sp.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket( - sp.getId(), List.of( - com.mojang.datafixers.util.Pair.of(getEquipmentSlot(), stack.copy()) - ))); } } diff --git a/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentItem.java b/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentItem.java index a13ef99..ddf5cc1 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentItem.java +++ b/src/main/java/com/chemiofitor/protection_engineering/item/AttachmentItem.java @@ -82,9 +82,12 @@ private int migrateState(ItemStack stack) { return getControlPattern() == ControlPattern.ALWAYS_ON ? STATE_READY : STATE_DISABLED; } switch (getControlPattern()) { - case ACTIVE_COOLDOWN: - // 保守迁移为 READY,首个 tick 修正 - return STATE_READY; + case ACTIVE_COOLDOWN: { + // 有旧冷却计时器 → 迁移到 COOLING + long oldTimer = stack.getOrDefault(ATTACHMENT_COOLDOWN.get(), 0L); + if (!active && oldTimer > 0) return STATE_COOLING; + return STATE_READY; // 激活窗口中或就绪,下个 tick 修正 + } case ONE_SHOT_COOLDOWN: return active ? STATE_READY : STATE_COOLING; case FREE_TOGGLE: @@ -159,12 +162,17 @@ public void onTick(ItemStack attachment, ItemStack host, LivingEntity entity, Sl long timer = getTimer(attachment); int state = getState(attachment); - if (timer == 0) return; + // 损坏恢复:需要计时器但没有 → 回到就绪 + if (timer == 0) { + if (state == STATE_ACTIVE || state == STATE_COOLING) { + setState(attachment, STATE_READY); + } + return; + } switch (state) { case STATE_ACTIVE: if (now >= timer) { - // 激活窗口到期 → 进入冷却 setState(attachment, STATE_COOLING); setTimer(attachment, now + getCooldownDuration()); onStateExit(attachment, STATE_ACTIVE, entity); @@ -173,7 +181,6 @@ public void onTick(ItemStack attachment, ItemStack host, LivingEntity entity, Sl case STATE_COOLING: if (now >= timer) { - // 冷却到期 → 回到就绪 setState(attachment, STATE_READY); onStateEnter(attachment, STATE_READY, entity); } diff --git a/src/main/java/com/chemiofitor/protection_engineering/menu/WorkbenchMenu.java b/src/main/java/com/chemiofitor/protection_engineering/menu/WorkbenchMenu.java index 1f5386d..596c80c 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/menu/WorkbenchMenu.java +++ b/src/main/java/com/chemiofitor/protection_engineering/menu/WorkbenchMenu.java @@ -126,7 +126,7 @@ private void loadFromArmor() { } private void playAttachSound() { - if (be == null) return; + if (be == null || be.getLevel() == null) return; var sound = RandomSource.create().nextBoolean() ? PESounds.ATTACH_1.get() : PESounds.ATTACH_2.get(); be.getLevel().playSound(null, be.getBlockPos(), sound, SoundSource.BLOCKS, 0.8F, 1.0F); @@ -144,17 +144,6 @@ private void applyToArmor() { syncing = false; } - /** 菜单关闭 → 写回 BE 并清空 */ - public void onClosed() { - if (be == null) return; - // 附件写入护甲 - for (int i = 0; i < WorkbenchBlockEntity.SLOT_COUNT; i++) - be.setItem(i, container.getItem(i).copy()); - be.applyAttachments(); - // 清空 BE 物品栏(不持久化) - be.clearContent(); - } - // ── Screen ───────────────────────────────────────────────── public List getActiveSlots() { diff --git a/src/main/java/com/chemiofitor/protection_engineering/registry/PEArmorMaterials.java b/src/main/java/com/chemiofitor/protection_engineering/registry/PEArmorMaterials.java index 672cb0b..1ad8fc6 100644 --- a/src/main/java/com/chemiofitor/protection_engineering/registry/PEArmorMaterials.java +++ b/src/main/java/com/chemiofitor/protection_engineering/registry/PEArmorMaterials.java @@ -36,7 +36,7 @@ public class PEArmorMaterials { ArmorItem.Type.BOOTS, 3 ), 15, // 附魔值同下界合金 - PESounds.EQUIP_ENGINEER_ARMOR, // 工程师护甲音效(复用下界合金音频) + PESounds.EQUIP_ENGINEER_ARMOR, // 工程师护甲音效 () -> Ingredient.of(Items.IRON_INGOT), List.of(new ArmorMaterial.Layer(ProtectionEngineering.asResource("engineer"))), 3.0F, // 韧性同下界合金 diff --git a/src/main/resources/assets/protectionengineering/sounds.json b/src/main/resources/assets/protectionengineering/sounds.json index 5f8530b..cc2c2a4 100644 --- a/src/main/resources/assets/protectionengineering/sounds.json +++ b/src/main/resources/assets/protectionengineering/sounds.json @@ -6,7 +6,12 @@ "sounds": ["protectionengineering:attach_2"] }, "equip_engineer_armor": { - "sounds": [] + "sounds": [ + "item/armor/equip_netherite1", + "item/armor/equip_netherite2", + "item/armor/equip_netherite3", + "item/armor/equip_netherite4" + ] }, "night_vision_on": { "sounds": ["protectionengineering:night_vision_on"] diff --git a/src/main/resources/assets/protectionengineering/textures/gui/engineer_armor_bar.png b/src/main/resources/assets/protectionengineering/textures/gui/engineer_armor_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..d78a962ed8f4567050d91546701bb6b4c6c27f18 GIT binary patch literal 956 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`4+QvxxQelJ{Qv*IT36@O*{=Jq-(;w1sPPHhSywCFw%i zZ)sy`Y7rR}Rg_W`85C({YGGz#9upc69QVip@3U7lGQ8yX`nC}^suV{T$$U|^7! zmlqisX=Y{?78VvB9*~f)XXJ5hcO-X(i=}MX3w{ ziJ5sNdVa1U3Z{C7dX_nPx9oswwxvdRrg?g5F>nAmtPE0&tPG4mmKP99L)oBM(qLo; zi!%Y)hKx)M0zf(nh%?(+z~WgzHVE7-V`O*%j7%7fW+?+uY63e03s9wjk+A{e0*I*~ z8(9}XOqv2@g8&oITqdx}AWI7%3#!Y|zyK_3)NxP5p~?)HT!KAa978<3-%g4SKIFjD zdihe`dhUBWD;PyZSi8fbwKRVJ|37u|-7U>4nl_4Asr)XqwK8G$ zzXyt{#Y4|2`J_-J{#yBHxlSw-BGuJGJ&q}qv-In4Do+No1>rBf(&1&4=V596X z@2t@~7U_oXQ4OI^4WX0Nwe1p~w{KbekI}1^;rzy3&z9BIuLA|Or>mdKI;Vst0H!%p AKmY&$ literal 0 HcmV?d00001