1. LockableComponent: remove duplicate "Lockable" tooltip line (ILockable.appendLockTooltip already handles lock status display) 2. ILockable/IHasResistance Javadoc: update @link refs from deleted V1 classes to V2 AbstractV2BondageItem/DataDrivenBondageItem 3. SettingsAccessor Javadoc: remove stale BindVariant @link references 4. DataDrivenBondageItem: update NECK block comment (remove branch ref) 5. Delete empty bondage3d/gags/ directory
14 KiB
D-01 Branch A : Bridge Utilities
Prérequis : Phase 1 (component system) mergée dans develop. Branche :
feature/d01-branch-a-bridgeObjectif : Créer les utilitaires V2 qui remplacent la logique V1, SANS supprimer de code V1. À la fin de cette branche, le mod compile, les items V1 et V2 coexistent, le struggle fonctionne pour les deux, et les nouveaux helpers sont prêts pour la migration des consommateurs.
Décisions actées
- Stack size : Stacks de 1 pour tout. Régression acceptée.
- Save compat : Breaking change. Pas de migration. Mod en alpha.
- Résistance : Config-driven via
resistanceId, pas hardcodé en JSON. - Comprehension/range gags : Config-driven via
gagMaterial, délègue à ModConfig au runtime. - IHasGaggingEffect/IHasBlindingEffect : DataDrivenBondageItem les implémente en checkant les composants.
Tâches
A1. Modifier ResistanceComponent — config-driven
Fichier : src/main/java/com/tiedup/remake/v2/bondage/component/ResistanceComponent.java
Actuellement stocke un int baseResistance hardcodé. Doit stocker un String resistanceId et déléguer à SettingsAccessor.getBindResistance(resistanceId) au runtime.
- Remplacer le champ
baseResistanceparresistanceId(String) fromJson(): parse"id"au lieu de"base"—"resistance": {"id": "rope"}getBaseResistance():return SettingsAccessor.getBindResistance(resistanceId);- Garder un fallback
"base"pour backward compat avec test_component_gag.json (ou le mettre à jour)
Référence : SettingsAccessor.getBindResistance(String) dans core/SettingsAccessor.java — normalise les clés et lit depuis ModConfig.
A2. Modifier GaggingComponent — config-driven + GagMaterial
Fichier : src/main/java/com/tiedup/remake/v2/bondage/component/GaggingComponent.java
Actuellement stocke comprehension et range hardcodés. Doit stocker un String material et déléguer à GagMaterial/ModConfig au runtime.
- Ajouter champ
@Nullable String material fromJson(): parse"material"—"gagging": {"material": "ball"}getComprehension(): si material != null →GagMaterial.valueOf(material).getComprehension()(lit ModConfig). Sinon → fallback au champ hardcodé (compat).getRange(): idem viaGagMaterial.valueOf(material).getTalkRange()getMaterial(): expose leGagMaterialenum pourGagTalkManager- Garder les champs
comprehension/rangecomme overrides optionnels (si présents dans JSON, ils prennent priorité sur le material)
Référence : GagMaterial enum dans items/base/GagVariant.java ou util/GagMaterial.java.
A3. Ajouter appendTooltip() à IItemComponent + ComponentHolder
Fichiers :
src/main/java/com/tiedup/remake/v2/bondage/component/IItemComponent.javasrc/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.javasrc/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java
Ajouter hook tooltip pour que chaque composant contribue des lignes :
// IItemComponent
default void appendTooltip(ItemStack stack, @Nullable Level level, List<Component> tooltip, TooltipFlag flag) {}
// ComponentHolder
public void appendTooltip(ItemStack stack, @Nullable Level level, List<Component> tooltip, TooltipFlag flag) {
for (IItemComponent c : components.values()) c.appendTooltip(stack, level, tooltip, flag);
}
Dans DataDrivenBondageItem.appendHoverText() : appeler holder.appendTooltip(...).
Implémenter appendTooltip dans chaque composant existant :
LockableComponent: affiche "Locked" / "Lockable"ResistanceComponent: affiche la résistance en advanced mode (F3+H)GaggingComponent: affiche le type de gagBlindingComponent: rien (pas d'info utile)ShockComponent: affiche "Shock: Manual" ou "Shock: Auto (Xs)"GpsComponent: affiche "GPS Tracking" + zone radiusChokingComponent: affiche "Choking Effect"AdjustableComponent: rien (ajustement est visuel)
A4. Champ pose_type + PoseTypeHelper
Fichiers :
src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemDefinition.java— ajouter@Nullable String poseTypesrc/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemParser.java— parser"pose_type"- Créer
src/main/java/com/tiedup/remake/v2/bondage/PoseTypeHelper.java
public static PoseType getPoseType(ItemStack stack) {
// V2: read from data-driven definition
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
if (def != null && def.poseType() != null) {
try { return PoseType.valueOf(def.poseType()); }
catch (IllegalArgumentException e) { return PoseType.STANDARD; }
}
// V1 fallback: instanceof ItemBind
if (stack.getItem() instanceof ItemBind bind) {
return bind.getPoseType();
}
return PoseType.STANDARD;
}
Note mixin : Les mixins (MixinPlayerModel, MixinCamera, etc.) appellent itemBind.getPoseType(). Ils devront migrer vers PoseTypeHelper.getPoseType(stack) en Branch C. Le helper doit être dans un package chargé tôt — v2/bondage/ est OK car le mod est chargé avant les mixins client.
A5. BindModeHelper
Fichier : Créer src/main/java/com/tiedup/remake/v2/bondage/BindModeHelper.java
Méthodes statiques pures NBT (clé "bindMode") :
hasArmsBound(ItemStack)→ true si mode "arms" ou "full"hasLegsBound(ItemStack)→ true si mode "legs" ou "full"getBindModeId(ItemStack)→ "full", "arms", ou "legs"cycleBindModeId(ItemStack)→ full→arms→legs→full, retourne le nouveau modegetBindModeTranslationKey(ItemStack)→ clé i18nisBindItem(ItemStack)→ true si l'item a la région ARMS dans sa definition V2, OU estinstanceof ItemBind
Référence : ItemBind.java lignes 64-160 pour les méthodes statiques existantes.
A6. CollarHelper (complet)
Fichier : Créer src/main/java/com/tiedup/remake/v2/bondage/CollarHelper.java
Extraire TOUTES les méthodes NBT de ItemCollar (1407 lignes) + 5 sous-classes en méthodes statiques. Sections :
Ownership :
isOwner(ItemStack, Player),isOwner(ItemStack, UUID)getOwners(ItemStack)→ SetaddOwner(ItemStack, UUID, String name),removeOwner(ItemStack, UUID)getBlacklist(ItemStack),addToBlacklist(ItemStack, UUID),removeFromBlacklist(ItemStack, UUID)getWhitelist(ItemStack),addToWhitelist(ItemStack, UUID),removeFromWhitelist(ItemStack, UUID)
Collar features :
isCollar(ItemStack)→ check OwnershipComponent presence OR instanceof ItemCollargetNickname(ItemStack),setNickname(ItemStack, String)isKidnappingModeEnabled(ItemStack),setKidnappingModeEnabled(ItemStack, boolean)getCellId(ItemStack),setCellId(ItemStack, UUID)shouldTieToPole(ItemStack),setShouldTieToPole(ItemStack, boolean)shouldWarnMasters(ItemStack),setShouldWarnMasters(ItemStack, boolean)isBondageServiceEnabled(ItemStack),setBondageServiceEnabled(ItemStack, boolean)getServiceSentence(ItemStack),setServiceSentence(ItemStack, String)
Shock :
canShock(ItemStack)→ check ShockComponent presence OR instanceof ItemShockCollarisPublic(ItemStack),setPublic(ItemStack, boolean)getShockInterval(ItemStack)→ depuis ShockComponent ou ItemShockCollarAuto
GPS :
hasGPS(ItemStack)→ check GpsComponent presence OR instanceof ItemGpsCollarhasPublicTracking(ItemStack),setPublicTracking(ItemStack, boolean)getSafeSpots(ItemStack),addSafeSpot(ItemStack, ...),removeSafeSpot(ItemStack, int)isActive(ItemStack),setActive(ItemStack, boolean)
Choke :
isChokeCollar(ItemStack)→ check ChokingComponent presence OR instanceof ItemChokeCollarisChoking(ItemStack),setChoking(ItemStack, boolean)isPetPlayMode(ItemStack),setPetPlayMode(ItemStack, boolean)
Alert suppression :
runWithSuppressedAlert(Runnable)— ThreadLocal, délègue àItemCollarpendant la coexistenceisRemovalAlertSuppressed()→ lit le ThreadLocal
Référence : ItemCollar.java (1407 lignes), ItemShockCollar.java (133 lignes), ItemGpsCollar.java (369 lignes), ItemChokeCollar.java (154 lignes), ItemShockCollarAuto.java (58 lignes).
A7. OwnershipComponent (complet)
Fichier : Créer src/main/java/com/tiedup/remake/v2/bondage/component/OwnershipComponent.java
Modifier : ComponentType.java — ajouter OWNERSHIP
JSON : "ownership": {}
Lifecycle hooks :
onEquipped(stack, entity): enregistrer dans CollarRegistry (extraire deItemCollar.registerCollarInRegistry). Note : le owner initial (le player qui equip) n'est pas dans la signatureonEquipped(stack, entity). Options : lire le owner depuis le NBT du stack (déjà écrit par l'interaction flow), ou passer par un tag temporaire.onUnequipped(stack, entity): alerter les owners (si pas supprimé via ThreadLocal), désenregistrer du CollarRegistry, reset auto-shock timer.appendTooltip: nickname, lock status, kidnapping mode, cell ID, bondage service, shock status, GPS status.blocksUnequip: si locked via ILockable.- Override
dropLockOnUnlock(): retourner false pour les collars (pas de padlock drop). Note : ceci doit être sur DataDrivenBondageItem, pas sur le composant (ILockable est sur l'Item, pas sur le composant). → DataDrivenBondageItem overridedropLockOnUnlock()quand OwnershipComponent est présent.
A8. TyingInteractionHelper + DataDrivenBondageItem extensions
Fichier : Créer src/main/java/com/tiedup/remake/v2/bondage/TyingInteractionHelper.java
Modifier : src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java
Extraire le flow tying de ItemBind.interactLivingEntity() dans TyingInteractionHelper :
- Accepte
(ServerPlayer player, LivingEntity target, ItemStack stack, InteractionHand hand) - Crée TyingPlayerTask, gère la progress bar, consume l'item, equipe via V2
Dans DataDrivenBondageItem :
use(): si regions contient ARMS → shift+click cycle bind mode (son + message action bar). Server-side only.interactLivingEntity(): routing par région :- ARMS →
TyingInteractionHelper(tying task flow) - NECK → collar equip flow (add owner, register CollarRegistry, play sound) — extraire de
ItemCollar.interactLivingEntity() - MOUTH/EYES/EARS/HANDS → instant equip (existant via AbstractV2BondageItem)
- ARMS →
A9. Réécrire StruggleBinds/StruggleCollar pour V2
Fichiers :
src/main/java/com/tiedup/remake/state/struggle/StruggleBinds.javasrc/main/java/com/tiedup/remake/state/struggle/StruggleCollar.java
StruggleBinds.canStruggle() :
- Actuellement :
instanceof ItemBind→ rejette V2 - Fix : accepter
instanceof IV2BondageItemavec region ARMS, OUinstanceof ItemBind - Résistance : si V2 →
ResistanceComponent.getBaseResistance(). Si V1 → existant viaIHasResistance.
StruggleCollar :
- Actuellement :
instanceof ItemCollar→ rejette V2 - Fix : accepter items avec
OwnershipComponent(viaCollarHelper.isCollar(stack)) - Résistance collar : via
ResistanceComponentouIHasResistance
A10. DataDrivenBondageItem implémente IHasGaggingEffect/IHasBlindingEffect
Fichier : src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java
Ajouter implements IHasGaggingEffect, IHasBlindingEffect sur la classe. Les 7+ call sites qui font instanceof IHasGaggingEffect continueront de fonctionner.
Problème : Ces interfaces sont des markers (pas de méthodes). La simple présence de l'interface signifie "a l'effet". Mais DataDrivenBondageItem est un singleton — TOUS les items data-driven auront ces interfaces.
Solution : Ne PAS implémenter les interfaces marker sur la classe. À la place, lors de la migration Branch C, convertir les call sites vers des component checks. C'est plus propre.
→ A10 annulé. Les call sites migreront en Branch C vers DataDrivenBondageItem.getComponent(stack, GAGGING, ...) != null.
A11. Fix PacketSelfBondage — routing par région
Fichier : src/main/java/com/tiedup/remake/network/selfbondage/PacketSelfBondage.java
Dans handleV2SelfBondage(), ajouter du routing par région :
Set<BodyRegionV2> regions = v2Item.getOccupiedRegions(stack);
if (regions.contains(BodyRegionV2.NECK)) {
// Cannot self-collar
return;
}
if (regions.contains(BodyRegionV2.ARMS)) {
// Tying task (existing flow)
handleV2SelfBind(player, stack, v2Item, state);
} else {
// Accessories: instant equip (no tying delay)
handleV2SelfAccessory(player, stack, v2Item, state);
}
Créer handleV2SelfAccessory() basé sur le pattern V1 handleSelfAccessory() (instant equip, swap si déjà équipé, locked check).
A12. NPC speed reduction
Fichier : À déterminer (composant ou event handler)
ItemBind.onEquipped() appelle RestraintEffectUtils.applyBindSpeedReduction(entity) pour les NPCs. Cette logique doit survivre à la migration.
Option : Ajouter dans DataDrivenBondageItem.onEquipped() (après le component dispatch) un check : si entity n'est PAS un Player ET l'item a la région ARMS → appeler RestraintEffectUtils.applyBindSpeedReduction(entity).
Vérification
make build— compilation clean- Struggle fonctionne avec V1 items (ropes, chain, collar)
- Struggle fonctionne avec V2 items (test_component_gag.json ou nouveau test item)
- Self-bondage V2 : ARMS → tying delay, MOUTH → instant, NECK → rejeté
- Tooltips : composants contribuent des lignes
- PoseTypeHelper résout V1 (ItemBind) et V2 (definition.poseType)
- CollarHelper.isOwner() fonctionne sur V1 ET V2 collars
- MCP reindex après la branche