chore: remove docs from branch D tracking

This commit is contained in:
NotEvil
2026-04-15 01:59:16 +02:00
parent 2504b7d657
commit c265206128
7 changed files with 0 additions and 2172 deletions

View File

@@ -1,284 +0,0 @@
# 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

View File

@@ -1,128 +0,0 @@
# D-01 Branch B : 46 JSON Item Definitions
> **Prérequis :** Branch A (bridge utilities) mergée.
> **Branche :** `feature/d01-branch-b-definitions`
> **Objectif :** Créer les définitions JSON data-driven pour les 46 items V1. Chaque item V1 a un équivalent JSON. Les valeurs sont config-driven (pas hardcodées). À la fin, les 46 items V2 existent en parallèle des V1.
---
## Extensions parser nécessaires
Avant de créer les JSON, vérifier que le parser supporte :
- `pose_type` (string, optionnel) — ajouté en Branch A4
- `can_attach_padlock` (boolean, default true) — **à ajouter si pas fait en A**
- GaggingComponent `material` — ajouté en Branch A2
- ResistanceComponent `id` — ajouté en Branch A1
**Fichiers :** `DataDrivenItemParser.java`, `DataDrivenItemDefinition.java`
---
## Items à créer
Tous dans `src/main/resources/data/tiedup/tiedup_items/`.
### Binds (16 fichiers)
| Fichier | display_name | resistance id | pose_type | Notes |
|---------|-------------|---------------|-----------|-------|
| `ropes.json` | Ropes | rope | STANDARD | supports_color: true |
| `armbinder.json` | Armbinder | armbinder | STANDARD | |
| `dogbinder.json` | Dogbinder | armbinder | DOG | movement_style: crawl |
| `chain.json` | Chains | chain | STANDARD | |
| `ribbon.json` | Ribbon | ribbon | STANDARD | supports_color: true |
| `slime.json` | Slime Bind | slime | STANDARD | can_attach_padlock: false |
| `vine_seed.json` | Vine Bind | vine | STANDARD | can_attach_padlock: false |
| `web_bind.json` | Web Bind | web | STANDARD | can_attach_padlock: false |
| `shibari.json` | Shibari | rope | STANDARD | supports_color: true |
| `leather_straps.json` | Leather Straps | armbinder | STANDARD | |
| `medical_straps.json` | Medical Straps | armbinder | STANDARD | |
| `beam_cuffs.json` | Beam Cuffs | chain | STANDARD | |
| `duct_tape.json` | Duct Tape | tape | STANDARD | can_attach_padlock: false |
| `straitjacket.json` | Straitjacket | straitjacket | STRAITJACKET | |
| `wrap.json` | Wrap | wrap | WRAP | |
| `latex_sack.json` | Latex Sack | latex_sack | LATEX_SACK | |
Tous ont : regions `["ARMS"]`, lockable component (sauf organiques), resistance component avec `id`.
**Référence :** `BindVariant.java` pour les registry names, pose types, supports_color. `ModConfig.ServerConfig` pour les resistance IDs (lignes 413-428).
### Gags (19 fichiers)
| Fichier | display_name | gag material | Notes |
|---------|-------------|-------------|-------|
| `cloth_gag.json` | Cloth Gag | cloth | |
| `ropes_gag.json` | Rope Gag | cloth | |
| `cleave_gag.json` | Cleave Gag | cloth | |
| `ribbon_gag.json` | Ribbon Gag | cloth | supports_color: true |
| `ball_gag.json` | Ball Gag | ball | supports_color: true |
| `ball_gag_strap.json` | Ball Gag (Strap) | ball | |
| `tape_gag.json` | Tape Gag | tape | can_attach_padlock: false |
| `wrap_gag.json` | Wrap Gag | tape | |
| `slime_gag.json` | Slime Gag | tape | can_attach_padlock: false |
| `vine_gag.json` | Vine Gag | tape | can_attach_padlock: false |
| `web_gag.json` | Web Gag | tape | can_attach_padlock: false |
| `panel_gag.json` | Panel Gag | panel | |
| `beam_panel_gag.json` | Beam Panel Gag | panel | |
| `chain_panel_gag.json` | Chain Panel Gag | panel | |
| `latex_gag.json` | Latex Gag | latex | |
| `tube_gag.json` | Tube Gag | stuffed | |
| `bite_gag.json` | Bite Gag | bite | |
| `sponge_gag.json` | Sponge Gag | sponge | |
| `baguette_gag.json` | Baguette Gag | baguette | |
Tous ont : regions `["MOUTH"]`, gagging component avec material, lockable, resistance `{"id": "gag"}`, adjustable.
**Référence :** `GagVariant.java` pour les materials et registry names. `ModConfig.ServerConfig.gagComprehension/gagRange` pour les valeurs runtime.
### Blindfolds (2 fichiers)
| Fichier | display_name |
|---------|-------------|
| `classic_blindfold.json` | Blindfold |
| `blindfold_mask.json` | Blindfold Mask |
Regions `["EYES"]`, components : blinding, lockable, resistance `{"id": "blindfold"}`, adjustable.
### Earplugs (1 fichier)
`classic_earplugs.json` — regions `["EARS"]`, lockable, resistance `{"id": "blindfold"}` (partage la résistance blindfold dans V1).
### Mittens (1 fichier)
`leather_mittens.json` — regions `["HANDS"]`, lockable.
### Collars (5 fichiers)
| Fichier | display_name | Components spécifiques |
|---------|-------------|----------------------|
| `classic_collar.json` | Classic Collar | ownership, lockable, resistance `{"id": "collar"}` |
| `shock_collar.json` | Shock Collar | + shock `{"damage": 2.0}` |
| `shock_collar_auto.json` | Auto Shock Collar | + shock `{"damage": 2.0, "auto_interval": 200}` |
| `gps_collar.json` | GPS Collar | + gps `{"safe_zone_radius": 50}` |
| `choke_collar.json` | Choke Collar | + choking |
### Combos (3 fichiers)
| Fichier | display_name | Regions | Components |
|---------|-------------|---------|-----------|
| `hood.json` | Hood | `["EYES"]` | blinding, gagging `{"material": "cloth"}`, lockable, blocked_regions: `["EYES", "EARS"]` |
| `medical_gag.json` | Medical Gag | `["MOUTH"]` | gagging `{"material": "stuffed"}`, blinding, lockable |
| `ball_gag_3d.json` | Ball Gag 3D | `["MOUTH"]` | gagging `{"material": "ball"}`, lockable, adjustable. Model 3D spécifique. |
---
## Supprimer le fichier test
Supprimer `src/main/resources/data/tiedup/tiedup_items/test_component_gag.json` (fichier de test Phase 1, plus nécessaire).
---
## Vérification
- [ ] `make build` — clean
- [ ] `make run` — les 46 items data-driven apparaissent (via `/tiedup give` ou creative tab section data-driven)
- [ ] Résistance = valeur config (changer dans config, vérifier que la résistance change)
- [ ] Gag comprehension = valeur config (changer dans config, vérifier)
- [ ] Collars ownership fonctionne via le nouveau OwnershipComponent
- [ ] Items organiques (slime, vine, web, tape) ne peuvent pas recevoir de padlock

View File

@@ -1,107 +0,0 @@
# D-01 Branch C : Consumer Migration (~97 fichiers)
> **Prérequis :** Branch A + B mergées.
> **Branche :** `feature/d01-branch-c-migration`
> **Objectif :** Remplacer TOUTES les références V1 (`instanceof ItemBind`, `ItemCollar.isOwner()`, `BindVariant.ROPES`, etc.) par les helpers/composants V2. Les classes V1 existent encore mais ne sont plus référencées. À la fin, `grep -r "instanceof ItemBind\|instanceof ItemGag\|instanceof ItemCollar\|instanceof ItemBlindfold\|instanceof ItemEarplugs\|instanceof ItemMittens\|BindVariant\|GagVariant" src/` retourne ZÉRO résultats (hors items/ lui-même).
---
## Pattern migration
| V1 | V2 | Notes |
|----|-----|-------|
| `instanceof ItemBind` | `BindModeHelper.isBindItem(stack)` | Ou `instanceof IV2BondageItem` si on a juste besoin de savoir que c'est un bondage item |
| `ItemBind.hasArmsBound(stack)` | `BindModeHelper.hasArmsBound(stack)` | Mêmes NBT keys |
| `ItemBind.hasLegsBound(stack)` | `BindModeHelper.hasLegsBound(stack)` | |
| `ItemBind.getBindModeId(stack)` | `BindModeHelper.getBindModeId(stack)` | |
| `itemBind.getPoseType()` | `PoseTypeHelper.getPoseType(stack)` | |
| `BindVariant.ROPES` / `ModItems.getBind(variant)` | `DataDrivenBondageItem.createStack(rl("tiedup:ropes"))` | **LAZY !** Ne pas appeler dans des static initializers |
| `instanceof ItemCollar` + methods | `CollarHelper.isCollar(stack)` + `CollarHelper.method(stack)` | |
| `instanceof ItemShockCollar` | `CollarHelper.canShock(stack)` | |
| `instanceof ItemGpsCollar` | `CollarHelper.hasGPS(stack)` | |
| `instanceof ItemChokeCollar` | `CollarHelper.isChokeCollar(stack)` | |
| `instanceof IHasGaggingEffect` | `DataDrivenBondageItem.getComponent(stack, GAGGING, GaggingComponent.class) != null` | Pour V2 items. V1 items gardent l'interface pendant la transition. |
| `instanceof IHasBlindingEffect` | `DataDrivenBondageItem.getComponent(stack, BLINDING, BlindingComponent.class) != null` | Idem |
| `instanceof ItemGag` + `getGagMaterial()` | `GaggingComponent comp = getComponent(stack, GAGGING, ...)` + `comp.getMaterial()` | |
| `PoseType` enum direct | Inchangé — l'enum est conservé | |
| `IHasResistance` methods | Inchangé — l'interface est conservée | |
| `ILockable` methods | Inchangé — l'interface est conservée | |
---
## Ordre de migration (critique d'abord)
### Phase 1 : State core (12 fichiers)
Ces fichiers sont la fondation — les migrer d'abord assure que le reste fonctionne.
| Fichier | Changements |
|---------|------------|
| `state/IBondageState.java` | `hasArmsBound()`/`hasLegsBound()``BindModeHelper` |
| `state/PlayerBindState.java` | `instanceof ItemCollar``CollarHelper`, `instanceof ItemBind``BindModeHelper` |
| `state/PlayerCaptorManager.java` | `instanceof ItemCollar``CollarHelper.isCollar() + CollarHelper.getOwners()` |
| `state/HumanChairHelper.java` | `PoseType` import OK (conservé) |
| `state/components/PlayerEquipment.java` | **Garder `equipInRegion()` V1 fallback** (migre en Branch D). Remplacer `instanceof ItemBind/ItemCollar` dans resistance methods. |
| `state/components/PlayerDataRetrieval.java` | `instanceof ItemCollar``CollarHelper.getNickname()` |
| `state/components/PlayerLifecycle.java` | imports V1 → V2 helpers |
| `state/components/PlayerShockCollar.java` | `instanceof ItemShockCollar``CollarHelper.canShock()` |
| `state/struggle/StruggleBinds.java` | Déjà migré en Branch A9 — vérifier |
| `state/struggle/StruggleCollar.java` | Déjà migré en Branch A9 — vérifier |
| `state/struggle/StruggleAccessory.java` | Vérifier |
### Phase 2 : Client animation/render (12 fichiers)
| Fichier | Changements |
|---------|------------|
| `client/animation/tick/AnimationTickHandler.java` | `instanceof ItemBind``PoseTypeHelper.getPoseType()` + `BindModeHelper` |
| `client/animation/tick/NpcAnimationTickHandler.java` | Idem |
| `client/animation/render/PlayerArmHideEventHandler.java` | `instanceof ItemBind``PoseTypeHelper` |
| `client/animation/render/DogPoseRenderHandler.java` | Idem |
| `client/animation/render/PetBedRenderHandler.java` | Idem |
| `client/animation/util/AnimationIdBuilder.java` | `PoseType` import OK (conservé) |
| `client/animation/StaticPoseApplier.java` | `PoseType` import OK |
| `client/model/DamselModel.java` | `PoseType` + `instanceof ItemBind` → helpers |
| `client/FirstPersonMittensRenderer.java` | `BindVariant.ROPES` → lazy createStack |
| `mixin/client/MixinPlayerModel.java` | `instanceof ItemBind``PoseTypeHelper` |
| `mixin/client/MixinCamera.java` | Idem |
| `mixin/client/MixinVillagerEntityBaseModelMCA.java` | Idem |
### Phase 3 : Entity AI goals (15 fichiers)
Principalement `instanceof ItemBind``BindModeHelper`, `ModItems.getBind(variant)``createStack(rl)`, `instanceof ItemCollar``CollarHelper`.
### Phase 4 : Network packets (14 fichiers)
`PacketSelfBondage` déjà migré en A11. Reste : `PacketSlaveAction`, `PacketMasterEquip`, `PacketAssignCellToCollar`, `PacketNpcCommand`, etc. → `CollarHelper`.
### Phase 5 : Events (8 fichiers)
`BondageItemRestrictionHandler`, `RestraintTaskTickHandler`, `PlayerEnslavementHandler`, `ChatEventHandler`, etc.
### Phase 6 : Commands (6 fichiers)
`BondageSubCommand` (1232 lignes) — le plus gros. `BindVariant``createStack()`. `NPCCommand`, `CollarCommand`, `KidnapSetCommand`.
### Phase 7 : Entity classes (15 fichiers)
`EntityKidnapper`, `KidnapperCaptureEquipment`, `KidnapperTheme`, `KidnapperItemSelector`, `KidnapperCollarConfig`, etc.
### Phase 8 : Compat MCA (5 fichiers)
`MCAKidnappedAdapter` (907 lignes) — `instanceof IHasGaggingEffect/IHasBlindingEffect` → component checks. `instanceof ItemCollar``CollarHelper`.
### Phase 9 : Autres (10 fichiers)
Dialogue (`GagTalkManager`, `PetRequestManager`), worldgen (`HangingCagePiece`), util (`RestraintApplicator`), blocks (`BondageItemBlockEntity`), dispenser, creative tab.
---
## Vérification
- [ ] `make build` — clean
- [ ] `grep -r "instanceof ItemBind\b" src/ --include="*.java" | grep -v "items/"` → 0 résultats
- [ ] `grep -r "instanceof ItemGag\b" src/ --include="*.java" | grep -v "items/"` → 0 résultats
- [ ] `grep -r "instanceof ItemCollar\b" src/ --include="*.java" | grep -v "items/"` → 0 résultats
- [ ] `grep -r "BindVariant\b" src/ --include="*.java" | grep -v "items/"` → 0 résultats
- [ ] `grep -r "GagVariant\b" src/ --include="*.java" | grep -v "items/"` → 0 résultats
- [ ] `make run` — le mod fonctionne normalement

View File

@@ -1,188 +0,0 @@
# D-01 Branch D : V1 Cleanup
> **Prérequis :** Branch A + B + C mergées. Zero références V1 hors du package `items/`.
> **Branche :** `feature/d01-branch-d-cleanup`
> **Objectif :** Supprimer toutes les classes V1 bondage. Migrer `equipInRegion()` vers le flow V2 complet. Réécrire le creative tab. À la fin, zéro classe V1 bondage dans le mod.
---
## Décisions
- **Save compat :** Breaking change. Les items V1 dans les inventaires existants seront perdus. Mod en alpha.
- **Pas de MissingMappingsEvent.** Simplement supprimer les registrations.
---
## Tâches
### D1. Migrer equipInRegion() → V2EquipmentHelper.equipItem()
**Fichier :** `src/main/java/com/tiedup/remake/state/components/PlayerEquipment.java`
Maintenant que tous les items sont `DataDrivenBondageItem` (qui implémente `IV2BondageItem`), le bypass direct `setInRegion()` n'est plus nécessaire.
Remplacer `equipInRegion()` par un appel à `V2EquipmentHelper.equipItem()` qui fait la conflict resolution complète (swap, supersede, blocked regions).
Vérifier que les méthodes `putBindOn()`, `putGagOn()`, `putCollarOn()`, etc. fonctionnent toujours via le nouveau path.
---
### D2. Supprimer les classes V1 (~30 fichiers)
**À supprimer :**
Abstract bases :
- `items/base/ItemBind.java` (637 lignes)
- `items/base/ItemGag.java` (93 lignes)
- `items/base/ItemBlindfold.java` (89 lignes)
- `items/base/ItemCollar.java` (1407 lignes)
- `items/base/ItemEarplugs.java` (90 lignes)
- `items/base/ItemMittens.java` (72 lignes)
Interfaces V1-only :
- `items/base/IBondageItem.java` (102 lignes)
- `items/base/IHasGaggingEffect.java` (33 lignes)
- `items/base/IHasBlindingEffect.java` (33 lignes)
- `items/base/IAdjustable.java` (49 lignes)
- `items/base/ItemOwnerTarget.java`
- `items/base/ItemColor.java`
Variant enums :
- `items/base/BindVariant.java` (90 lignes)
- `items/base/GagVariant.java` (163 lignes)
- `items/base/BlindfoldVariant.java` (48 lignes)
- `items/base/EarplugsVariant.java` (33 lignes)
- `items/base/MittensVariant.java` (35 lignes)
Factory classes :
- `items/GenericBind.java` (68 lignes)
- `items/GenericGag.java` (72 lignes)
- `items/GenericBlindfold.java` (37 lignes)
- `items/GenericEarplugs.java` (37 lignes)
- `items/GenericMittens.java` (37 lignes)
Collars :
- `items/ItemClassicCollar.java` (21 lignes)
- `items/ItemShockCollar.java` (133 lignes)
- `items/ItemShockCollarAuto.java` (58 lignes)
- `items/ItemGpsCollar.java` (369 lignes)
- `items/ItemChokeCollar.java` (154 lignes)
Combos :
- `items/ItemHood.java` (35 lignes)
- `items/ItemMedicalGag.java` (24 lignes)
- `items/bondage3d/gags/ItemBallGag3D.java` (78 lignes)
- `items/bondage3d/IHas3DModelConfig.java`
- `items/bondage3d/Model3DConfig.java`
**À CONSERVER :**
- `items/base/ILockable.java` — utilisé par V2 (AbstractV2BondageItem)
- `items/base/IHasResistance.java` — utilisé par V2 (DataDrivenBondageItem)
- `items/base/IKnife.java` — utilisé par GenericKnife (tool)
- `items/base/PoseType.java` — utilisé par animation system
- `items/base/KnifeVariant.java` — utilisé par GenericKnife (tool)
- `items/base/AdjustmentHelper.java` — utilisé par adjustment packets
- `items/GenericKnife.java` — tool, pas bondage
- `items/clothes/GenericClothes.java` — déjà V2
- `items/clothes/ClothesProperties.java`
- `items/ModItems.java` — garde les tools, supprime les bondage
- `items/ModCreativeTabs.java` — réécrit (voir D3)
- Tous les tool items (whip, padlock, key, lockpick, taser, etc.)
---
### D3. Réécrire ModItems — retirer les registrations V1
**Fichier :** `src/main/java/com/tiedup/remake/items/ModItems.java`
Supprimer :
- `BINDS` map + `registerAllBinds()`
- `GAGS` map + `registerAllGags()`
- `BLINDFOLDS` map + `registerAllBlindfolds()`
- `EARPLUGS` map + `registerAllEarplugs()`
- `MITTENS` map + `registerAllMittens()`
- `BALL_GAG_3D`, `MEDICAL_GAG`, `HOOD`
- `CLASSIC_COLLAR`, `SHOCK_COLLAR`, `SHOCK_COLLAR_AUTO`, `GPS_COLLAR`, `CHOKE_COLLAR`
- Les helper accessors `getBind()`, `getGag()`, etc.
Garder : CLOTHES, tous les tools (WHIP, PADLOCK, KEY, etc.), KNIVES, spawn eggs.
---
### D4. Réécrire ModCreativeTabs
**Fichier :** `src/main/java/com/tiedup/remake/items/ModCreativeTabs.java`
Remplacer l'itération par variant enums par :
```java
// Data-driven bondage items
for (DataDrivenItemDefinition def : DataDrivenItemRegistry.getAll()) {
output.accept(DataDrivenBondageItem.createStack(def.id()));
}
```
Pour l'ordre : ajouter un champ optionnel `"creative_tab_order"` aux definitions JSON, ou trier par catégorie (regions) puis par nom.
Pour les couleurs : si l'item a `supports_color`, ajouter les variantes colorées. Utiliser `tint_channels` du definition.
---
### D5. Cleanup PoseTypeHelper — retirer le fallback V1
**Fichier :** `src/main/java/com/tiedup/remake/v2/bondage/PoseTypeHelper.java`
Supprimer le fallback `instanceof ItemBind` dans `getPoseType()`. Ne garder que le path data-driven.
---
### D6. Cleanup CollarHelper — retirer les fallbacks V1
**Fichier :** `src/main/java/com/tiedup/remake/v2/bondage/CollarHelper.java`
Les méthodes comme `isCollar(stack)` checkent `instanceof ItemCollar` en fallback V1. Retirer ces checks.
---
### D7. Cleanup BindModeHelper — retirer le fallback V1
Idem — retirer `instanceof ItemBind` fallback dans `isBindItem()`.
---
### D8. Cleanup imports orphelins
Faire un pass sur tout le projet pour retirer les imports V1 orphelins.
```bash
grep -rn "import com.tiedup.remake.items.base.ItemBind" src/ --include="*.java"
grep -rn "import com.tiedup.remake.items.base.ItemCollar" src/ --include="*.java"
grep -rn "import com.tiedup.remake.items.base.IBondageItem" src/ --include="*.java"
# etc. — tout doit retourner 0
```
---
## Vérification finale
- [ ] `make build` — clean, zero errors
- [ ] `make run` — le mod démarre, les items apparaissent dans le creative tab
- [ ] `grep -r "items.base.ItemBind\|items.base.ItemGag\|items.base.ItemCollar\|items.base.ItemBlindfold\|items.base.ItemEarplugs\|items.base.ItemMittens\|items.base.IBondageItem\|BindVariant\|GagVariant\|BlindfoldVariant\|EarplugsVariant\|MittensVariant" src/main/java/ --include="*.java"`**0 résultats**
- [ ] Les items data-driven s'équipent/se déséquipent correctement
- [ ] Le struggle fonctionne (binds + collars)
- [ ] Le self-bondage fonctionne (routing par région)
- [ ] Les collars gardent leur ownership/shock/GPS après equip/unequip
- [ ] Les tooltips affichent toutes les infos composants
- [ ] `equipInRegion()` utilise V2EquipmentManager (conflict resolution active)
- [ ] MCP reindex final
---
## Résultat attendu
- **~6500 lignes de code V1 supprimées**
- **46 items = 46 fichiers JSON** (data-driven, extensible par resource packs)
- **1 seul Item singleton** (`DataDrivenBondageItem`)
- **8 composants** gèrent toute la logique gameplay
- **3 helpers** (`BindModeHelper`, `PoseTypeHelper`, `CollarHelper`) remplacent les anciennes classes
- **Zero couplage V1** dans le reste du mod

View File

@@ -1,183 +0,0 @@
# D-01 Branch E : Resistance & Lock System Rework
> **Prérequis :** Branch D (V1 cleanup) mergée.
> **Branche :** `feature/d01-branch-e-resistance`
> **Objectif :** Redesign complet du système de résistance/lock.
---
## Nouveau modèle
### Principes
1. **La résistance vient de l'item** — définie dans le JSON via `ResistanceComponent`, point final.
2. **Le lock est binaire** — on/off. Pas de "lock resistance" séparée. Le lock *active* la nécessité de struggle pour les items non-ARMS.
3. **ARMS = toujours actif** — un bind aux bras nécessite toujours un struggle pour s'en libérer soi-même, locké ou non.
4. **Non-ARMS + pas locké = libre** — un gag/blindfold/collar non-locké peut être retiré librement (par soi-même ou un autre joueur).
5. **Non-ARMS + locké = struggle requis** — le lock active la résistance de l'item.
6. **Un autre joueur peut aider** — retirer un item non-locké sur un autre joueur ne nécessite pas de struggle (aide).
### Matrice de struggle
| Région | Locké ? | Self-remove | Autre joueur remove |
|--------|---------|-------------|---------------------|
| ARMS | Non | Struggle (résistance item) | Libre (aide) |
| ARMS | Oui | Struggle (résistance item) | Struggle (résistance item) |
| Non-ARMS | Non | Libre | Libre |
| Non-ARMS | Oui | Struggle (résistance item) | Struggle (résistance item) |
### Items organiques (slime, vine, web, tape)
Ces items sont "lockés par nature" — pas de padlock possible mais impossible à retirer sans struggle.
**Option :** Nouveau composant `BuiltInLockComponent` dans le JSON :
```json
"components": {
"resistance": {"id": "slime"},
"built_in_lock": {}
}
```
`BuiltInLockComponent` :
- `blocksUnequip()` retourne `true` (comme un lock, mais sans padlock)
- `ILockable.canAttachPadlock()` retourne `false` (déjà le cas pour les organiques)
- L'item se comporte comme un ARMS bind : toujours struggle required
**Alternative :** Flag `"always_locked": true` sur la definition JSON. Plus simple, pas besoin de nouveau composant.
---
## Problèmes actuels que ce rework corrige
### P1. Singleton MAX scan
`DataDrivenBondageItem.getBaseResistance(LivingEntity)` retourne le MAX de tous les items data-driven équipés. Un gag résistance 50 hérite de la résistance 200 du chain bind.
**Fix :** Initialiser `currentResistance` dans le NBT à l'equip depuis `ResistanceComponent.getBaseResistance()`. Plus jamais de fallback au MAX scan runtime. Le `getBaseResistance(LivingEntity)` du singleton devient un no-op/fallback qui n'est plus utilisé par le struggle.
### P2. isItemLocked() dead code
`StruggleState.struggle()` ne call jamais `isItemLocked()`. Le x10 penalty n'est jamais appliqué.
**Fix :** Supprimer le concept de "locked penalty". Avec le nouveau modèle, le lock **active** le struggle, il ne le ralentit pas. Si l'item est locké, il faut struggle avec la résistance complète de l'item. Si non locké (et non-ARMS), pas de struggle du tout.
### P3. Lock resistance / item resistance déconnectés
`ILockable.getLockResistance()` vs `IHasResistance.getBaseResistance()` sont deux systèmes indépendants.
**Fix :** Supprimer `ILockable.getLockResistance()` / `getCurrentLockResistance()` / `setCurrentLockResistance()` / `initializeLockResistance()` / `clearLockResistance()`. La résistance du lockpick minigame utilise directement la résistance de l'item (ou un multiplicateur fixe).
### P4. Dice-roll ignore le lock
**Fix :** Avec le nouveau modèle, le dice-roll ne change pas. C'est `canStruggle()` qui gate l'accès :
```java
// StruggleBinds.canStruggle()
// ARMS: toujours struggle-able (self)
return true;
// StruggleCollar/StruggleAccessory.canStruggle()
// Non-ARMS: seulement si locké
return isLocked(stack) || hasBuiltInLock(stack);
```
---
## Bugs pré-existants à corriger dans cette branche
### B1. V1 ItemCollar.onUnequipped() — suppressed path skip unregister
Quand `isRemovalAlertSuppressed()` est true, `ItemCollar.onUnequipped()` return early SANS appeler `CollarRegistry.unregisterWearer()`. Entrées fantômes persistées.
**Fichier :** `items/base/ItemCollar.java` lignes 1382-1395
**Fix :** Ajouter `unregisterWearer()` dans le branch suppressed.
### B2. DataDrivenItemRegistry.clear() pas synchronisé
`clear()` écrit `SNAPSHOT = EMPTY` sans acquérir `RELOAD_LOCK`. Race avec `mergeAll()`.
**Fichier :** `v2/bondage/datadriven/DataDrivenItemRegistry.java` ligne 142
**Fix :** Synchroniser sur `RELOAD_LOCK`.
### B3. V2TyingPlayerTask.heldStack reference stale
Le held item peut être remplacé entre début et fin du tying → item dupliqué.
**Fichier :** `tasks/V2TyingPlayerTask.java` ligne 80
**Fix :** Valider `heldStack` non-vide et matching avant equip dans `onComplete()`.
### B4. PlayerShockCollar ignore complètement les V2 collars
`checkAutoShockCollar()` dispatche exclusivement sur `instanceof ItemShockCollarAuto` et `instanceof ItemGpsCollar`. Les V2 data-driven collars avec ShockComponent ou GpsComponent ne déclenchent jamais les auto-shocks ni l'enforcement de zones GPS.
**Fichier :** `state/components/PlayerShockCollar.java` lignes 139-189
**Fix :** Utiliser `CollarHelper.canShock()`, `CollarHelper.getShockInterval()`, `CollarHelper.hasGPS()` pour la détection, avec fallback V1 pour les méthodes V1-specific (`getSafeSpots()`).
### B5. EntityKidnapperMerchant.remove() memory leak
`remove()` appelle `tradingPlayers.clear()` mais ne nettoie PAS la `playerToMerchant` ConcurrentHashMap statique. Entrées stales accumulées sur les serveurs long-running.
**Fichier :** `entities/EntityKidnapperMerchant.java` ligne 966-981
**Fix :** Itérer `tradingPlayers` et appeler `playerToMerchant.remove(uuid)` avant le clear.
### B6. Timer division potentiellement inversée (auto-shock)
`PlayerShockCollar.java` lignes 153-155 : `collarShock.getInterval() / GameConstants.TICKS_PER_SECOND`. Si Timer attend des ticks, la division réduit l'intervalle de 20x (shock toutes les 0.25s au lieu de 5s).
**Fichier :** `state/components/PlayerShockCollar.java` lignes 153-155 et 179-182
**Fix :** Vérifier le contrat du constructeur `Timer`. Si il attend des ticks, supprimer la division.
### B7. StruggleState.isItemLocked() dead code
`StruggleState.struggle()` ne call JAMAIS `isItemLocked()`. Le penalty x10 pour les items padlockés n'est jamais appliqué.
**Fichier :** `state/struggle/StruggleState.java` ligne 53-133
**Fix :** Inclus dans le rework E2 (nouveau modèle resistance/lock).
---
## Tâches
### E1. Initialiser currentResistance à l'equip
Dans `DataDrivenBondageItem.onEquipped()` et les hooks V1 `onEquipped()` :
- Lire `ResistanceComponent.getBaseResistance()` (ou `IHasResistance.getBaseResistance()` pour V1)
- Écrire immédiatement dans le NBT via `setCurrentResistance(stack, base)`
- Élimine le MAX scan comme source d'initialisation
### E2. Refactor canStruggle() — nouveau modèle
- `StruggleBinds.canStruggle()` : ARMS → toujours true (self) si item existe
- Nouveau `StruggleAccessory` (ou refactor de StruggleCollar) : non-ARMS → true seulement si locké ou built-in lock
- Supprimer `isItemLocked()` penalty (dead code de toute façon)
### E3. "Aide" — remove non-locké par un autre joueur
Modifier `AbstractV2BondageItem.interactLivingEntity()` :
- Si clic sur un joueur qui porte l'item ET item non-locké ET clicker n'a pas l'item en main → retirer l'item (aide)
- Ou via un packet dédié (clic droit main vide sur joueur attaché)
### E4. BuiltInLockComponent ou flag `always_locked`
Pour les items organiques qui ne peuvent pas avoir de padlock mais nécessitent un struggle.
### E5. Cleanup ILockable — supprimer lock resistance
Supprimer : `getLockResistance()`, `getCurrentLockResistance()`, `setCurrentLockResistance()`, `initializeLockResistance()`, `clearLockResistance()`.
Le lockpick minigame utilise la résistance de l'item directement (ou un multiplicateur config).
### E6. Fix bugs pré-existants (B1, B2, B3)
---
## Vérification
- [ ] V2 bind résistance 50 + V2 gag résistance 80 : chacun a sa propre résistance (pas MAX)
- [ ] Gag non-locké → retirable sans struggle
- [ ] Gag locké → struggle avec résistance du gag
- [ ] Bind ARMS non-locké → self-struggle requis, autre joueur peut aider (libre)
- [ ] Bind ARMS locké → self-struggle requis, autre joueur aussi struggle
- [ ] Slime bind (built-in lock) → struggle obligatoire, pas de padlock possible
- [ ] `currentResistance` initialisé dans NBT dès l'equip
- [ ] CollarRegistry clean après removals légitimes
- [ ] Pas de duplication d'item via tying task