From b81d3eed95b2eb58a7fa162ec18befeea92df802 Mon Sep 17 00:00:00 2001 From: NotEvil Date: Tue, 14 Apr 2026 15:05:48 +0200 Subject: [PATCH] feat(D-01/A): config-driven components + tooltip hook (A1, A2, A3) - ResistanceComponent: resistanceId delegates to SettingsAccessor at runtime, fallback to hardcoded base for backward compat - GaggingComponent: material field delegates to GagMaterial enum from ModConfig, explicit comprehension/range overrides take priority - IItemComponent: add default appendTooltip() method - ComponentHolder: iterate components for tooltip contribution - 6 components implement appendTooltip (lockable, resistance, gagging, shock, gps, choking) - DataDrivenBondageItem: call holder.appendTooltip() in appendHoverText() --- .../bondage/component/ChokingComponent.java | 12 +++ .../v2/bondage/component/ComponentHolder.java | 10 +++ .../bondage/component/GaggingComponent.java | 79 +++++++++++++++---- .../v2/bondage/component/GpsComponent.java | 17 ++++ .../v2/bondage/component/IItemComponent.java | 7 ++ .../bondage/component/LockableComponent.java | 16 ++++ .../component/ResistanceComponent.java | 53 +++++++++++-- .../v2/bondage/component/ShockComponent.java | 19 +++++ .../datadriven/DataDrivenBondageItem.java | 6 ++ 9 files changed, 196 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/ChokingComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/ChokingComponent.java index 5860159..5cc4d74 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/ChokingComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/ChokingComponent.java @@ -1,6 +1,13 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: choking effect for data-driven items. @@ -43,4 +50,9 @@ public class ChokingComponent implements IItemComponent { public boolean isNonLethalForMaster() { return nonLethalForMaster; } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + tooltip.add(Component.translatable("item.tiedup.tooltip.choking").withStyle(ChatFormatting.DARK_PURPLE)); + } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.java b/src/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.java index 6a465ae..d6fcdfb 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.java @@ -2,9 +2,13 @@ package com.tiedup.remake.v2.bondage.component; import java.util.Collections; import java.util.EnumMap; +import java.util.List; import java.util.Map; +import net.minecraft.network.chat.Component; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; public final class ComponentHolder { @@ -58,6 +62,12 @@ public final class ComponentHolder { return false; } + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + for (IItemComponent c : components.values()) { + c.appendTooltip(stack, level, tooltip, flag); + } + } + public boolean isEmpty() { return components.isEmpty(); } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/GaggingComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/GaggingComponent.java index 0e401a5..e6af614 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/GaggingComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/GaggingComponent.java @@ -1,45 +1,94 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import com.tiedup.remake.core.TiedUpMod; +import com.tiedup.remake.util.GagMaterial; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: gagging behavior for data-driven items. * - * JSON config: {@code "gagging": {"comprehension": 0.2, "range": 10.0}} + *

Config-driven: {@code "gagging": {"material": "ball"}} delegates to + * {@link GagMaterial} for comprehension/range from ModConfig at runtime.

+ * + *

Override: {@code "gagging": {"comprehension": 0.15, "range": 8.0}} uses + * explicit values that take priority over the material lookup.

*/ public class GaggingComponent implements IItemComponent { - private final double comprehension; - private final double range; + private final @Nullable String material; + private final double comprehensionOverride; + private final double rangeOverride; - private GaggingComponent(double comprehension, double range) { - this.comprehension = comprehension; - this.range = range; + private GaggingComponent(@Nullable String material, double comprehensionOverride, double rangeOverride) { + this.material = material; + this.comprehensionOverride = comprehensionOverride; + this.rangeOverride = rangeOverride; } public static IItemComponent fromJson(JsonObject config) { - double comprehension = 0.2; - double range = 10.0; + String material = null; + double comprehension = -1; + double range = -1; if (config != null) { + if (config.has("material")) { + material = config.get("material").getAsString(); + } if (config.has("comprehension")) { - comprehension = config.get("comprehension").getAsDouble(); + comprehension = Math.max(0.0, Math.min(1.0, config.get("comprehension").getAsDouble())); } if (config.has("range")) { - range = config.get("range").getAsDouble(); + range = Math.max(0.0, config.get("range").getAsDouble()); } } - comprehension = Math.max(0.0, Math.min(1.0, comprehension)); - range = Math.max(0.0, range); - return new GaggingComponent(comprehension, range); + return new GaggingComponent(material, comprehension, range); } /** How much of the gagged speech is comprehensible (0.0 = nothing, 1.0 = full). */ public double getComprehension() { - return comprehension; + if (comprehensionOverride >= 0) return comprehensionOverride; + GagMaterial gag = getMaterial(); + if (gag != null) return gag.getComprehension(); + return 0.2; } /** Maximum range in blocks where muffled speech can be heard. */ public double getRange() { - return range; + if (rangeOverride >= 0) return rangeOverride; + GagMaterial gag = getMaterial(); + if (gag != null) return gag.getTalkRange(); + return 10.0; + } + + /** The gag material enum, or null if not configured or invalid. */ + public @Nullable GagMaterial getMaterial() { + if (material == null) return null; + try { + return GagMaterial.valueOf(material.toUpperCase()); + } catch (IllegalArgumentException e) { + TiedUpMod.LOGGER.warn("[GaggingComponent] Unknown gag material: {}", material); + return null; + } + } + + /** The raw material string from JSON, or null. */ + public @Nullable String getMaterialName() { + return material; + } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + if (material != null) { + tooltip.add(Component.translatable("item.tiedup.tooltip.gag_material", material) + .withStyle(ChatFormatting.RED)); + } else { + tooltip.add(Component.translatable("item.tiedup.tooltip.gagging").withStyle(ChatFormatting.RED)); + } } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/GpsComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/GpsComponent.java index 272792a..3eaf778 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/GpsComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/GpsComponent.java @@ -1,6 +1,13 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: GPS tracking and safe zone for data-driven items. @@ -42,4 +49,14 @@ public class GpsComponent implements IItemComponent { public boolean isPublicTracking() { return publicTracking; } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + tooltip.add(Component.translatable("item.tiedup.tooltip.gps_tracking") + .withStyle(ChatFormatting.AQUA)); + if (safeZoneRadius > 0) { + tooltip.add(Component.translatable("item.tiedup.tooltip.gps_zone_radius", safeZoneRadius) + .withStyle(ChatFormatting.DARK_AQUA)); + } + } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/IItemComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/IItemComponent.java index 4af99c0..f051abc 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/IItemComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/IItemComponent.java @@ -1,7 +1,12 @@ package com.tiedup.remake.v2.bondage.component; +import java.util.List; +import net.minecraft.network.chat.Component; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * A reusable behavior module for data-driven bondage items. @@ -16,4 +21,6 @@ public interface IItemComponent { default boolean blocksUnequip(ItemStack stack, LivingEntity entity) { return false; } + + default void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) {} } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/LockableComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/LockableComponent.java index 3a57c71..ef8ddcd 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/LockableComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/LockableComponent.java @@ -1,6 +1,13 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: lockable behavior for data-driven items. @@ -41,4 +48,13 @@ public class LockableComponent implements IItemComponent { public int getLockResistance() { return lockResistance; } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + tooltip.add(Component.translatable("item.tiedup.tooltip.lockable").withStyle(ChatFormatting.GOLD)); + if (flag.isAdvanced()) { + tooltip.add(Component.translatable("item.tiedup.tooltip.lock_resistance", lockResistance) + .withStyle(ChatFormatting.DARK_GRAY)); + } + } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/ResistanceComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/ResistanceComponent.java index d59f838..4288596 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/ResistanceComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/ResistanceComponent.java @@ -1,33 +1,70 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import com.tiedup.remake.core.SettingsAccessor; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: struggle resistance for data-driven items. * - * JSON config: {@code "resistance": {"base": 150}} + *

Config-driven: {@code "resistance": {"id": "rope"}} delegates to + * {@link SettingsAccessor#getBindResistance(String)} at runtime.

+ * + *

Legacy/override: {@code "resistance": {"base": 150}} uses a hardcoded value.

*/ public class ResistanceComponent implements IItemComponent { - private final int baseResistance; + private final @Nullable String resistanceId; + private final int fallbackBase; - private ResistanceComponent(int baseResistance) { - this.baseResistance = baseResistance; + private ResistanceComponent(@Nullable String resistanceId, int fallbackBase) { + this.resistanceId = resistanceId; + this.fallbackBase = fallbackBase; } public static IItemComponent fromJson(JsonObject config) { + String id = null; int base = 100; - if (config != null && config.has("base")) { - base = config.get("base").getAsInt(); + if (config != null) { + if (config.has("id")) { + id = config.get("id").getAsString(); + } + if (config.has("base")) { + base = config.get("base").getAsInt(); + } } base = Math.max(0, base); - return new ResistanceComponent(base); + return new ResistanceComponent(id, base); } /** * Get the base resistance for this item. + * If a {@code resistanceId} is configured, delegates to server config at runtime. + * Otherwise returns the hardcoded fallback value. */ public int getBaseResistance() { - return baseResistance; + if (resistanceId != null) { + return SettingsAccessor.getBindResistance(resistanceId); + } + return fallbackBase; + } + + /** The config key used for runtime resistance lookup, or null if hardcoded. */ + public @Nullable String getResistanceId() { + return resistanceId; + } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + if (flag.isAdvanced()) { + tooltip.add(Component.translatable("item.tiedup.tooltip.resistance", getBaseResistance()) + .withStyle(ChatFormatting.DARK_GRAY)); + } } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/component/ShockComponent.java b/src/main/java/com/tiedup/remake/v2/bondage/component/ShockComponent.java index 1c6d867..e54b8de 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/component/ShockComponent.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/component/ShockComponent.java @@ -1,6 +1,13 @@ package com.tiedup.remake.v2.bondage.component; import com.google.gson.JsonObject; +import java.util.List; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; /** * Component: shock collar behavior for data-driven items. @@ -49,4 +56,16 @@ public class ShockComponent implements IItemComponent { public boolean hasAutoShock() { return autoInterval > 0; } + + @Override + public void appendTooltip(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag) { + if (hasAutoShock()) { + float seconds = autoInterval / 20.0f; + tooltip.add(Component.translatable("item.tiedup.tooltip.shock_auto", String.format("%.1f", seconds)) + .withStyle(ChatFormatting.DARK_RED)); + } else { + tooltip.add(Component.translatable("item.tiedup.tooltip.shock_manual") + .withStyle(ChatFormatting.DARK_RED)); + } + } } diff --git a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java index 56d6738..b37d114 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java @@ -246,6 +246,12 @@ public class DataDrivenBondageItem extends AbstractV2BondageItem { } } + // Component tooltips + ComponentHolder holder = DataDrivenItemRegistry.getComponents(stack); + if (holder != null) { + holder.appendTooltip(stack, level, tooltip, flag); + } + // Lock status + escape difficulty (from AbstractV2BondageItem) super.appendHoverText(stack, level, tooltip, flag);