diff --git a/bugs.md b/bugs.md
index a3f18ca..d53dda6 100644
--- a/bugs.md
+++ b/bugs.md
@@ -6,10 +6,27 @@ Les sujets dans FIXME doivent être corrigé, puis déplacé dans "DONE", puis c
# DONE
+## farah question
+
+```
+[farah]
+C'est la boite de l'ete ou Chenda a emmenage a cote.
+[farah]
+Vous vous passiez des mots dedans. Dans un sens et dans l'autre, par-dessus la cloture.
+```
+=> Pas un bug. L'idée de Chenda et du joueur échangeant des messages dans une boîte par-dessus la clôture est une métaphore originale de l'aventure Sentimental. Elle n'est pas directement inspirée d'un jeu spécifique — c'est une variation sur le thème des "boîtes à messages" entre voisins/amis d'enfance, un motif classique dans les récits nostalgiques. Si ça ressemble à un autre jeu, c'est probablement une convergence thématique !
+
## missing translation
```
Cheveux équipé : [MISSING:cosmetic.hair.stardustlegendary]
```
-=> Cause : le code construisait la clé de trad à partir de `cosmeticValue.ToLower()` (`stardustlegendary`) au lieu d'utiliser le `nameKey` de l'item (`cosmetic.hair.stardust`). Les tests existants vérifiaient les `nameKey` (qui existaient bien), mais pas la clé construite dynamiquement par le code.
-=> Fix : `ChangeAppearance()` recherche maintenant l'item par `(CosmeticSlot, CosmeticValue)` dans le registre et utilise son `nameKey` pour la traduction.
-=> Tests ajoutés : `CosmeticSlots_HaveLocalizationKeys` (vérifie les clés `cosmetic.slot.*`) et `CosmeticItems_CanBeResolvedBySlotAndValue` (vérifie qu'aucune paire slot+value n'est ambiguë).
+=> Fix : le code utilisait `cosmeticValue.ToLower()` pour construire la clé au lieu du `nameKey` de l'item. Corrigé avec lookup par `(CosmeticSlot, CosmeticValue)` dans le registre. Tests ajoutés.
+
+## Loot d'aventure
+=> Fix : Ajouté un `AdventureUnlockedEvent` émis par `MetaEngine` quand une aventure est débloquée. Affiché dans `RenderEvents()` avec le message localisé "🎉 Nouvelle aventure débloquée ! Découvre '{0}' dans « Partir à l'aventure » !"
+
+## Pacing meta et boites pas ouf
+=> Fix balancing dans boxes.json :
+- `box_of_boxes` : réduit poids de `box_not_great` de 10→7, augmenté `box_ok_tier` de 5→7 (ratio basic passe de 67%/33% à 50%/50%)
+- `box_not_great` : ajouté `box_ok_tier` (weight 2) et `box_meta_basics` (weight 1) dans le loot — les boîtes pas ouf peuvent maintenant dropper directement du mieux
+- `box_ok_tier` : augmenté poids de `box_meta_basics` de 1→2 (doublement des chances de meta)
diff --git a/content/data/boxes.json b/content/data/boxes.json
index 081c584..2bd56ed 100644
--- a/content/data/boxes.json
+++ b/content/data/boxes.json
@@ -21,8 +21,8 @@
"guaranteedRolls": [],
"rollCount": 1,
"entries": [
- {"itemDefinitionId": "box_not_great", "weight": 10},
- {"itemDefinitionId": "box_ok_tier", "weight": 5},
+ {"itemDefinitionId": "box_not_great", "weight": 7},
+ {"itemDefinitionId": "box_ok_tier", "weight": 7},
{"itemDefinitionId": "box_cool", "weight": 1},
{"itemDefinitionId": "box_epic", "weight": 1},
{"itemDefinitionId": "box_legendhair", "weight": 1},
@@ -57,7 +57,9 @@
{"itemDefinitionId": "cosmetic_legs_short", "weight": 2},
{"itemDefinitionId": "cosmetic_arms_regular", "weight": 2},
{"itemDefinitionId": "tint_light", "weight": 1},
- {"itemDefinitionId": "tint_dark", "weight": 1}
+ {"itemDefinitionId": "tint_dark", "weight": 1},
+ {"itemDefinitionId": "box_ok_tier", "weight": 2},
+ {"itemDefinitionId": "box_meta_basics", "weight": 1}
]
}
},
@@ -81,7 +83,7 @@
{"itemDefinitionId": "cosmetic_eyes_green", "weight": 2},
{"itemDefinitionId": "tint_cyan", "weight": 2},
{"itemDefinitionId": "tint_orange", "weight": 2},
- {"itemDefinitionId": "box_meta_basics", "weight": 1}
+ {"itemDefinitionId": "box_meta_basics", "weight": 2}
]
}
},
diff --git a/content/strings/en.json b/content/strings/en.json
index 055ccfa..7393b0b 100644
--- a/content/strings/en.json
+++ b/content/strings/en.json
@@ -440,5 +440,6 @@
"adventure.none_available": "No adventures available yet. Keep opening boxes!",
"adventure.coming_soon": "Adventure '{0}' is coming soon! The boxes are still being assembled.",
"adventure.done": "Done",
+ "adventure.unlocked": "🎉 New adventure unlocked! Discover '{0}' in the adventure menu!",
"misc.welcome": "Welcome, {0}!"
}
diff --git a/content/strings/fr.json b/content/strings/fr.json
index efedb89..d8fe6c2 100644
--- a/content/strings/fr.json
+++ b/content/strings/fr.json
@@ -440,5 +440,6 @@
"adventure.none_available": "Aucune aventure disponible. Continue à ouvrir des boîtes !",
"adventure.coming_soon": "L'aventure '{0}' arrive bientôt ! Les boîtes sont encore en cours d'assemblage.",
"adventure.done": "Terminée",
+ "adventure.unlocked": "🎉 Nouvelle aventure débloquée ! Découvre '{0}' dans « Partir à l'aventure » !",
"misc.welcome": "Bienvenue, {0} !"
}
diff --git a/src/OpenTheBox/Program.cs b/src/OpenTheBox/Program.cs
index 90b475a..e7542cf 100644
--- a/src/OpenTheBox/Program.cs
+++ b/src/OpenTheBox/Program.cs
@@ -432,6 +432,10 @@ public static class Program
_renderer.ShowMessage(_loc.Get("interaction.key_no_match"));
break;
+ case AdventureUnlockedEvent advUnlockedEvt:
+ _renderer.ShowMessage(_loc.Get("adventure.unlocked", advUnlockedEvt.Theme.ToString()));
+ break;
+
case AdventureStartedEvent advEvt:
await RunAdventure(advEvt.Theme);
break;
diff --git a/src/OpenTheBox/Simulation/Events/GameEvent.cs b/src/OpenTheBox/Simulation/Events/GameEvent.cs
index 0d8d781..bc9ef5e 100644
--- a/src/OpenTheBox/Simulation/Events/GameEvent.cs
+++ b/src/OpenTheBox/Simulation/Events/GameEvent.cs
@@ -46,6 +46,11 @@ public sealed record ResourceChangedEvent(ResourceType Type, int OldValue, int N
///
public sealed record CosmeticEquippedEvent(CosmeticSlot Slot, string NewValue) : GameEvent;
+///
+/// A new adventure was unlocked (from receiving an adventure-themed item).
+///
+public sealed record AdventureUnlockedEvent(AdventureTheme Theme) : GameEvent;
+
///
/// An adventure was started.
///
diff --git a/src/OpenTheBox/Simulation/MetaEngine.cs b/src/OpenTheBox/Simulation/MetaEngine.cs
index 093cd14..d3008c0 100644
--- a/src/OpenTheBox/Simulation/MetaEngine.cs
+++ b/src/OpenTheBox/Simulation/MetaEngine.cs
@@ -44,9 +44,10 @@ public class MetaEngine
}
// Unlock adventure if this item references a theme
- if (itemDef.AdventureTheme.HasValue)
+ if (itemDef.AdventureTheme.HasValue &&
+ state.UnlockedAdventures.Add(itemDef.AdventureTheme.Value))
{
- state.UnlockedAdventures.Add(itemDef.AdventureTheme.Value);
+ events.Add(new AdventureUnlockedEvent(itemDef.AdventureTheme.Value));
}
// Make stat type visible if this item references a stat