Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
jei_version=19.27.0.340
detail_armor_bar_version=5.0.2
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 ─────────────────────────────────────────────────
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;

/**
* 宿主物品上的附件数据。
* <p>
* 用 {@link Map}{@code <SlotType, ItemStack>} 存储,只存非空值。
* 不同宿主物品(头盔、胸甲、锯剑)各自只存自己支持的槽位子集,
* 没有"定长 N"的限制。
* <p>
* 不可变 —— {@link #with} / {@link #without} 返回新实例。
* 宿主物品上的附件数据。自定义 equals/hashCode 用 ItemStack.matches 做值比较。
*/
public record AttachmentsData(Map<SlotType, ItemStack> slots) {
public final class AttachmentsData {

private final Map<SlotType, ItemStack> slots;

public static final AttachmentsData EMPTY = new AttachmentsData(Map.of());

/** 防御性拷贝,确保不可变 */
public AttachmentsData {
slots = Map.copyOf(slots);
public AttachmentsData(Map<SlotType, ItemStack> slots) {
this.slots = Map.copyOf(slots);
}

public Map<SlotType, ItemStack> 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 ──────────────────────────────────────────────────
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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) {
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -203,4 +204,43 @@ private static boolean hasImmunity(Player player, Holder<MobEffect> 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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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

Expand Down
Loading
Loading