fix(D-01/D): checkup cleanup — 5 issues resolved
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
This commit is contained in:
284
docs/plans/D01-branch-A-bridge-utilities.md
Normal file
284
docs/plans/D01-branch-A-bridge-utilities.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# D-01 Branch A : Bridge Utilities
|
||||
|
||||
> **Prérequis :** Phase 1 (component system) mergée dans develop.
|
||||
> **Branche :** `feature/d01-branch-a-bridge`
|
||||
> **Objectif :** 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 `baseResistance` par `resistanceId` (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 via `GagMaterial.valueOf(material).getTalkRange()`
|
||||
- `getMaterial()` : expose le `GagMaterial` enum pour `GagTalkManager`
|
||||
- Garder les champs `comprehension`/`range` comme 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.java`
|
||||
- `src/main/java/com/tiedup/remake/v2/bondage/component/ComponentHolder.java`
|
||||
- `src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java`
|
||||
|
||||
Ajouter hook tooltip pour que chaque composant contribue des lignes :
|
||||
|
||||
```java
|
||||
// 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 gag
|
||||
- `BlindingComponent` : rien (pas d'info utile)
|
||||
- `ShockComponent` : affiche "Shock: Manual" ou "Shock: Auto (Xs)"
|
||||
- `GpsComponent` : affiche "GPS Tracking" + zone radius
|
||||
- `ChokingComponent` : 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 poseType`
|
||||
- `src/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`
|
||||
|
||||
```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 mode
|
||||
- `getBindModeTranslationKey(ItemStack)` → clé i18n
|
||||
- `isBindItem(ItemStack)` → true si l'item a la région ARMS dans sa definition V2, OU est `instanceof 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)` → Set<UUID>
|
||||
- `addOwner(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 ItemCollar
|
||||
- `getNickname(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 ItemShockCollar
|
||||
- `isPublic(ItemStack)`, `setPublic(ItemStack, boolean)`
|
||||
- `getShockInterval(ItemStack)` → depuis ShockComponent ou ItemShockCollarAuto
|
||||
|
||||
**GPS :**
|
||||
- `hasGPS(ItemStack)` → check GpsComponent presence OR instanceof ItemGpsCollar
|
||||
- `hasPublicTracking(ItemStack)`, `setPublicTracking(ItemStack, boolean)`
|
||||
- `getSafeSpots(ItemStack)`, `addSafeSpot(ItemStack, ...)`, `removeSafeSpot(ItemStack, int)`
|
||||
- `isActive(ItemStack)`, `setActive(ItemStack, boolean)`
|
||||
|
||||
**Choke :**
|
||||
- `isChokeCollar(ItemStack)` → check ChokingComponent presence OR instanceof ItemChokeCollar
|
||||
- `isChoking(ItemStack)`, `setChoking(ItemStack, boolean)`
|
||||
- `isPetPlayMode(ItemStack)`, `setPetPlayMode(ItemStack, boolean)`
|
||||
|
||||
**Alert suppression :**
|
||||
- `runWithSuppressedAlert(Runnable)` — ThreadLocal, délègue à `ItemCollar` pendant la coexistence
|
||||
- `isRemovalAlertSuppressed()` → 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 de `ItemCollar.registerCollarInRegistry`). **Note :** le owner initial (le player qui equip) n'est pas dans la signature `onEquipped(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 override `dropLockOnUnlock()` 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)
|
||||
|
||||
---
|
||||
|
||||
### A9. Réécrire StruggleBinds/StruggleCollar pour V2
|
||||
|
||||
**Fichiers :**
|
||||
- `src/main/java/com/tiedup/remake/state/struggle/StruggleBinds.java`
|
||||
- `src/main/java/com/tiedup/remake/state/struggle/StruggleCollar.java`
|
||||
|
||||
`StruggleBinds.canStruggle()` :
|
||||
- Actuellement : `instanceof ItemBind` → rejette V2
|
||||
- Fix : accepter `instanceof IV2BondageItem` avec region ARMS, OU `instanceof ItemBind`
|
||||
- Résistance : si V2 → `ResistanceComponent.getBaseResistance()`. Si V1 → existant via `IHasResistance`.
|
||||
|
||||
`StruggleCollar` :
|
||||
- Actuellement : `instanceof ItemCollar` → rejette V2
|
||||
- Fix : accepter items avec `OwnershipComponent` (via `CollarHelper.isCollar(stack)`)
|
||||
- Résistance collar : via `ResistanceComponent` ou `IHasResistance`
|
||||
|
||||
---
|
||||
|
||||
### 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 :
|
||||
|
||||
```java
|
||||
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
|
||||
Reference in New Issue
Block a user