Complete TODO.md: remove event log, retouch Space adventure, add character name translation
- Remove ChatPanel/EventLog entirely (UIFeature, GameState, MetaEngine, SpectreRenderer, Program, ChatPanelTests, meta_chat item/box/strings) - Retouch Space adventure: expand AlienEncounter with 3 proper endings (RareBoxStory, AlienTrade, BoxContest with flair secret branch), expand SpaceExploration with fuel-gated choices and deeper branching - Add (NOUVEAU/NEW) prefix to adventure action when no adventure completed - Translate character names via localization keys (character.* in en/fr.json) - Replace hardcoded "(unavailable)" with localized hint or fallback text - Fix snapshot path to use CallerFilePath instead of fragile ../ chain - Add Workstations summary section to item_utility_report.txt
This commit is contained in:
parent
006ef1f94a
commit
4d8d5224e1
19 changed files with 413 additions and 241 deletions
27
TODO.md
27
TODO.md
|
|
@ -1,28 +1,3 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
Tous les items ont été traités. Ce fichier est conservé comme historique.
|
Tous les items ont été traités.
|
||||||
|
|
||||||
## Noms de personnages
|
|
||||||
|
|
||||||
Les noms des personnages dans les histoires ne sont pas traduits à l'affichage. Loreline ne propose a priori pas de système de traduction pour les noms de personnages donc il faudra des clés de traductions dédiées.
|
|
||||||
|
|
||||||
## Partir à l'aventure
|
|
||||||
|
|
||||||
Lorsqu'on débloque "Partir à l'aventure" mais qu'aucune aventure n'a été lancée il faut ajouter une incentive plus claire à lancer la première aventure. L'entrée devrait avec le préfix (new) ou (nouveau) et être en couleur (si la couleur est débloquée) tant qu'une aventure n'a pas été faite.
|
|
||||||
|
|
||||||
## Retouch aventure Space
|
|
||||||
|
|
||||||
Dans cette Space aventure, la fin tombe un peu abrute: le concours d'ouverture de boite n'a pas lieu, "Trade items with Zx'thorp" tourne court également.
|
|
||||||
|
|
||||||
Attendu: Il faudrait un fin un peu plus sympa ou couper plus tôt (l'aventure peu être plus courte si besoin: la perfection est atteinte non pas lorsqu'il n'y a plus rien à ajouter mais plus rien à enlever !)
|
|
||||||
|
|
||||||
Dans cette Space aventure le fuel semble être un sujet sauf qu'en réalité on ne peut pas tomber à court. Ce serait bien de rendre ce state vraiment utile (ex: si on utilise le fuel tôt on ne peut plus l'utiliser plus tard).
|
|
||||||
|
|
||||||
## Unavailable
|
|
||||||
|
|
||||||
Plutôt que d'avoir "unavailable" pour un choix à condition, ce sera mieux de donner un indice sur ce qu'il faut.
|
|
||||||
Ex: "(Si j'avais plus de force…)" ou "(Si j'avais des jambes adaptées…)"
|
|
||||||
|
|
||||||
## Journal d'événements
|
|
||||||
|
|
||||||
pas utile après test: à supprimer complètement.
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ Examiner la boîte
|
||||||
#opt-ignore // "Ignore it and continue"
|
#opt-ignore // "Ignore it and continue"
|
||||||
L'ignorer et continuer
|
L'ignorer et continuer
|
||||||
|
|
||||||
#opt-scan // "Run a deep scan first"
|
#opt-scan // "Run a deep scan first|||Fuel reserves insufficient for deep scan"
|
||||||
Lancer un scan approfondi d'abord
|
Lancer un scan approfondi d'abord|||Réserves de carburant insuffisantes pour un scan approfondi
|
||||||
|
|
||||||
#deepscan-init // "Deep scan initiated. Fuel reserves reduced."
|
#deepscan-init // "Deep scan initiated. Fuel reserves reduced."
|
||||||
Scan approfondi lancé. Réserves de carburant réduites.
|
Scan approfondi lancé. Réserves de carburant réduites.
|
||||||
|
|
@ -61,8 +61,8 @@ Ne me dites pas quels mots utiliser pour les boîtes, ARIA.
|
||||||
#opt-open-now // "Open it immediately"
|
#opt-open-now // "Open it immediately"
|
||||||
L'ouvrir immédiatement
|
L'ouvrir immédiatement
|
||||||
|
|
||||||
#opt-scan-first // "Scan it first"
|
#opt-scan-first // "Scan it first|||Not enough fuel for a scan"
|
||||||
La scanner d'abord
|
La scanner d'abord|||Pas assez de carburant pour un scan
|
||||||
|
|
||||||
#opt-poke // "Poke it with a stick"
|
#opt-poke // "Poke it with a stick"
|
||||||
La pousser avec un bâton
|
La pousser avec un bâton
|
||||||
|
|
@ -208,7 +208,7 @@ Ah, un autre appréciateur de boîtes ! Prenez ceci comme cadeau de ma collectio
|
||||||
#captain-wasbox // "The alien WAS the box?"
|
#captain-wasbox // "The alien WAS the box?"
|
||||||
L'extraterrestre ÉTAIT la boîte ?
|
L'extraterrestre ÉTAIT la boîte ?
|
||||||
|
|
||||||
#alien-intro // I'm Zx'thorp."
|
#alien-intro // "I'm Zx'thorp."
|
||||||
Je suis Zx'thorp.
|
Je suis Zx'thorp.
|
||||||
|
|
||||||
#open-normal // "Inside you find a Nebula Shard and what appears to be a map to more boxes."
|
#open-normal // "Inside you find a Nebula Shard and what appears to be a map to more boxes."
|
||||||
|
|
@ -238,27 +238,117 @@ J'apprécie les boîtes. Surtout celles avec du bon butin.
|
||||||
#opt-rare-box // "Ask about the galaxy's rarest box"
|
#opt-rare-box // "Ask about the galaxy's rarest box"
|
||||||
Demander quelle est la boîte la plus rare de la galaxie
|
Demander quelle est la boîte la plus rare de la galaxie
|
||||||
|
|
||||||
#alien-singularity // "The Singularity Box. It contains everything and nothing. Also a coupon."
|
|
||||||
La Boîte Singulière. Elle contient tout et rien. Et aussi un coupon.
|
|
||||||
|
|
||||||
#opt-trade // "Trade items with Zx'thorp"
|
#opt-trade // "Trade items with Zx'thorp"
|
||||||
Échanger des objets avec Zx'thorp
|
Échanger des objets avec Zx'thorp
|
||||||
|
|
||||||
#alien-trade // "I have a Space Helmet for you. In exchange, I want your sense of wonder."
|
|
||||||
J'ai un Casque Spatial pour vous. En échange, je veux votre sens de l'émerveillement.
|
|
||||||
|
|
||||||
#captain-deal // "Deal. Wait--"
|
|
||||||
Marché. Attendez--
|
|
||||||
|
|
||||||
#opt-contest // "Challenge the alien to a box-opening contest"
|
#opt-contest // "Challenge the alien to a box-opening contest"
|
||||||
Défier l'extraterrestre dans un concours d'ouverture de boîtes
|
Défier l'extraterrestre dans un concours d'ouverture de boîtes
|
||||||
|
|
||||||
|
#alien-singularity // "The Singularity Box. It contains everything and nothing. Also a coupon."
|
||||||
|
La Boîte Singulière. Elle contient tout et rien. Et aussi un coupon.
|
||||||
|
|
||||||
|
#captain-coupon // "A coupon? For what?"
|
||||||
|
Un coupon ? Pour quoi ?
|
||||||
|
|
||||||
|
#alien-coupon-answer // "For another Singularity Box. It's boxes all the way down."
|
||||||
|
Pour une autre Boîte Singulière. Ce sont des boîtes jusqu'au bout.
|
||||||
|
|
||||||
|
#captain-boxes-inside // "You're saying the universe is just boxes inside boxes?"
|
||||||
|
Vous dites que l'univers n'est qu'une boîte dans des boîtes ?
|
||||||
|
|
||||||
|
#alien-getting-it // "Now you're getting it, Captain."
|
||||||
|
Vous commencez à comprendre, Capitaine.
|
||||||
|
|
||||||
|
#ai-existential // "Commander, I'm detecting a slight existential crisis in your vitals."
|
||||||
|
Commandant, je détecte une légère crise existentielle dans vos signes vitaux.
|
||||||
|
|
||||||
|
#captain-fine // "I'm fine, ARIA. Just rethinking everything I know about reality."
|
||||||
|
Ça va, ARIA. Je remets juste en question tout ce que je sais sur la réalité.
|
||||||
|
|
||||||
|
#alien-trade-offer // "I have a Space Helmet for you. In exchange, I want... a story."
|
||||||
|
J'ai un Casque Spatial pour vous. En échange, je veux... une histoire.
|
||||||
|
|
||||||
|
#captain-story // "A story?"
|
||||||
|
Une histoire ?
|
||||||
|
|
||||||
|
#alien-first-box // "Tell me about the first box you ever opened."
|
||||||
|
Racontez-moi la première boîte que vous avez ouverte.
|
||||||
|
|
||||||
|
#captain-first-box-story // "It was small. Cardboard. My parents gave it to me. Inside was a toy rocket ship."
|
||||||
|
Elle était petite. En carton. Mes parents me l'avaient offerte. À l'intérieur, il y avait une fusée jouet.
|
||||||
|
|
||||||
|
#alien-pause // "..."
|
||||||
|
...
|
||||||
|
|
||||||
|
#alien-beautiful-trade // "That's the most beautiful thing I've ever heard."
|
||||||
|
C'est la plus belle chose que j'aie jamais entendue.
|
||||||
|
|
||||||
|
#alien-gives-helmet // "The alien removes its helmet and places it gently in your hands. Its eyes -- all seven of them -- are glistening."
|
||||||
|
L'extraterrestre retire son casque et le place délicatement dans vos mains. Ses yeux -- les sept -- brillent.
|
||||||
|
|
||||||
|
#captain-crying // "Are you... crying?"
|
||||||
|
Vous... pleurez ?
|
||||||
|
|
||||||
|
#alien-appreciation // "We don't cry. We leak appreciation fluid. It's different."
|
||||||
|
Nous ne pleurons pas. Nous sécrétons du fluide d'appréciation. C'est différent.
|
||||||
|
|
||||||
#alien-dare // "You dare? No one out-opens Zx'thorp!"
|
#alien-dare // "You dare? No one out-opens Zx'thorp!"
|
||||||
Vous osez ? Personne ne surpasse Zx'thorp à l'ouverture !
|
Vous osez ? Personne ne surpasse Zx'thorp à l'ouverture !
|
||||||
|
|
||||||
#alien-contest // "...fine. Best of three. You open first."
|
#alien-contest // "...fine. Best of three. You open first."
|
||||||
...très bien. Au meilleur des trois. Vous ouvrez en premier.
|
...très bien. Au meilleur des trois. Vous ouvrez en premier.
|
||||||
|
|
||||||
|
#opt-confident // "Open with confidence"
|
||||||
|
Ouvrir avec assurance
|
||||||
|
|
||||||
|
#opt-flair // "Open with theatrical flair"
|
||||||
|
Ouvrir avec panache
|
||||||
|
|
||||||
|
#flair-spin // "You crack your knuckles. You stretch your fingers. You do a little spin."
|
||||||
|
Vous faites craquer vos doigts. Vous étirez vos mains. Vous faites une petite pirouette.
|
||||||
|
|
||||||
|
#captain-watch // "Watch and learn, Zx'thorp."
|
||||||
|
Regardez et apprenez, Zx'thorp.
|
||||||
|
|
||||||
|
#flair-confetti // "You open the box with a flourish, confetti exploding from inside. Wait -- you didn't put confetti in there."
|
||||||
|
Vous ouvrez la boîte avec un geste théâtral, des confettis explosent de l'intérieur. Attendez -- vous n'avez pas mis de confettis dedans.
|
||||||
|
|
||||||
|
#alien-score-flair // "Theatrical. I approve. 8 out of 10."
|
||||||
|
Théâtral. J'approuve. 8 sur 10.
|
||||||
|
|
||||||
|
#ai-confetti // "Commander, where did the confetti come from?"
|
||||||
|
Commandant, d'où viennent les confettis ?
|
||||||
|
|
||||||
|
#captain-heart // "From the heart, ARIA. From the heart."
|
||||||
|
Du cœur, ARIA. Du cœur.
|
||||||
|
|
||||||
|
#contest-open // "You grip the box firmly and open it in one clean motion."
|
||||||
|
Vous saisissez la boîte fermement et l'ouvrez d'un geste net.
|
||||||
|
|
||||||
|
#alien-score // "Clean technique. 7 out of 10."
|
||||||
|
Technique propre. 7 sur 10.
|
||||||
|
|
||||||
|
#alien-produces // "Zx'thorp produces a box from somewhere. You try not to think about where."
|
||||||
|
Zx'thorp sort une boîte de quelque part. Vous essayez de ne pas penser d'où.
|
||||||
|
|
||||||
|
#alien-opens // "The alien opens it using three of its tentacles simultaneously. The lid flies off, does a triple backflip, and lands perfectly back on the box."
|
||||||
|
L'extraterrestre l'ouvre avec trois de ses tentacules simultanément. Le couvercle s'envole, fait un triple salto arrière et atterrit parfaitement sur la boîte.
|
||||||
|
|
||||||
|
#captain-impossible // "That's... that's not even possible."
|
||||||
|
C'est... c'est même pas possible.
|
||||||
|
|
||||||
|
#alien-score-2 // "10 out of 10. I win."
|
||||||
|
10 sur 10. Je gagne.
|
||||||
|
|
||||||
|
#ai-correct // "I'm afraid the alien is correct, Commander. That was objectively superior box-opening."
|
||||||
|
Je crains que l'extraterrestre ait raison, Commandant. C'était objectivement une ouverture de boîte supérieure.
|
||||||
|
|
||||||
|
#captain-rematch // "Rematch. One day. I'll be ready."
|
||||||
|
Revanche. Un jour. Je serai prêt.
|
||||||
|
|
||||||
|
#alien-waiting // "I look forward to it, Captain. Across the stars, I'll be waiting. With boxes."
|
||||||
|
J'ai hâte, Capitaine. À travers les étoiles, je vous attendrai. Avec des boîtes.
|
||||||
|
|
||||||
#explore-course // "Setting course for the nearest box. ETA: approximately one dramatic pause."
|
#explore-course // "Setting course for the nearest box. ETA: approximately one dramatic pause."
|
||||||
Cap vers la boîte la plus proche. Arrivée estimée : environ une pause dramatique.
|
Cap vers la boîte la plus proche. Arrivée estimée : environ une pause dramatique.
|
||||||
|
|
||||||
|
|
@ -274,20 +364,53 @@ La grande semble contenir les deux autres. Ce sont des boîtes jusqu'au bout.
|
||||||
#opt-big // "Open the biggest box first"
|
#opt-big // "Open the biggest box first"
|
||||||
Ouvrir la plus grande boîte en premier
|
Ouvrir la plus grande boîte en premier
|
||||||
|
|
||||||
|
#opt-all // "Open all three at once|||Not enough fuel for maximum box velocity"
|
||||||
|
Ouvrir les trois en même temps|||Pas assez de carburant pour la vitesse maximale de boîte
|
||||||
|
|
||||||
|
#opt-happy // "Leave them orbiting, they seem happy"
|
||||||
|
Les laisser en orbite, elles ont l'air heureuses
|
||||||
|
|
||||||
#big-open // "You open the biggest box. Inside: two medium boxes and a note."
|
#big-open // "You open the biggest box. Inside: two medium boxes and a note."
|
||||||
Vous ouvrez la plus grande boîte. À l'intérieur : deux boîtes moyennes et un mot.
|
Vous ouvrez la plus grande boîte. À l'intérieur : deux boîtes moyennes et un mot.
|
||||||
|
|
||||||
#big-note // "The note reads: \"Congratulations! You've found the Box Nebula. Population: boxes.\""
|
#big-note // "The note reads: \"Congratulations! You've found the Box Nebula. Population: boxes.\""
|
||||||
Le mot dit : \"Félicitations ! Vous avez trouvé la Nébuleuse des Boîtes. Population : des boîtes.\"
|
Le mot dit : \"Félicitations ! Vous avez trouvé la Nébuleuse des Boîtes. Population : des boîtes.\"
|
||||||
|
|
||||||
#opt-all // "Open all three at once"
|
#captain-nebula // "There's a whole nebula of boxes?"
|
||||||
Ouvrir les trois en même temps
|
Il y a une nébuleuse entière de boîtes ?
|
||||||
|
|
||||||
|
#ai-billion // "Scanners confirm it, Commander. Approximately 4.7 billion boxes, all orbiting a supermassive box."
|
||||||
|
Les scanners confirment, Commandant. Environ 4,7 milliards de boîtes, toutes en orbite autour d'une boîte supermassive.
|
||||||
|
|
||||||
|
#opt-deeper // "We must go deeper"
|
||||||
|
Il faut aller plus loin
|
||||||
|
|
||||||
|
#opt-enough // "We've seen enough boxes for one day"
|
||||||
|
On a vu assez de boîtes pour aujourd'hui
|
||||||
|
|
||||||
|
#deeper-open // "You open the two medium boxes. Each contains two smaller boxes."
|
||||||
|
Vous ouvrez les deux boîtes moyennes. Chacune contient deux boîtes plus petites.
|
||||||
|
|
||||||
|
#captain-how-many // "ARIA, how many boxes is this now?"
|
||||||
|
ARIA, ça fait combien de boîtes maintenant ?
|
||||||
|
|
||||||
|
#ai-crashed // "I've lost count, Commander. My box-tracking subroutine has crashed."
|
||||||
|
J'ai perdu le compte, Commandant. Mon sous-programme de suivi de boîtes a planté.
|
||||||
|
|
||||||
|
#captain-count // "Then let the boxes count themselves."
|
||||||
|
Alors laissons les boîtes se compter elles-mêmes.
|
||||||
|
|
||||||
|
#ai-not-how // "...that's not how boxes work, Commander."
|
||||||
|
...ce n'est pas comme ça que les boîtes fonctionnent, Commandant.
|
||||||
|
|
||||||
|
#captain-should-be // "Maybe it should be."
|
||||||
|
Peut-être que ça devrait l'être.
|
||||||
|
|
||||||
#captain-velocity // "All three, ARIA. Maximum box velocity."
|
#captain-velocity // "All three, ARIA. Maximum box velocity."
|
||||||
Les trois, ARIA. Vitesse maximale de boîte.
|
Les trois, ARIA. Vitesse maximale de boîte.
|
||||||
|
|
||||||
#ai-enthusiasm // "That's not a real measurement, but I admire your enthusiasm."
|
#ai-enthusiasm // "That's not a real measurement, but I admire your enthusiasm. Fuel reserves depleted for the maneuver."
|
||||||
Ce n'est pas une vraie mesure, mais j'admire votre enthousiasme.
|
Ce n'est pas une vraie mesure, mais j'admire votre enthousiasme. Réserves de carburant épuisées pour la manœuvre.
|
||||||
|
|
||||||
#all-open // "All three boxes burst open simultaneously. The contents mix together in zero gravity."
|
#all-open // "All three boxes burst open simultaneously. The contents mix together in zero gravity."
|
||||||
Les trois boîtes s'ouvrent simultanément. Le contenu se mélange en apesanteur.
|
Les trois boîtes s'ouvrent simultanément. Le contenu se mélange en apesanteur.
|
||||||
|
|
@ -298,8 +421,14 @@ Commandant, les objets forment... une plus grande boîte.
|
||||||
#captain-ofcourse // "Of course they are."
|
#captain-ofcourse // "Of course they are."
|
||||||
Évidemment.
|
Évidemment.
|
||||||
|
|
||||||
#opt-happy // "Leave them orbiting, they seem happy"
|
#ai-proud // "A bigger, better box. It's vibrating at a frequency I've never seen. I think it's... proud of itself."
|
||||||
Les laisser en orbite, elles ont l'air heureuses
|
Une plus grande, meilleure boîte. Elle vibre à une fréquence que je n'ai jamais vue. Je crois qu'elle est... fière d'elle-même.
|
||||||
|
|
||||||
|
#captain-everything // "A proud box. Now I've seen everything."
|
||||||
|
Une boîte fière. Maintenant j'ai tout vu.
|
||||||
|
|
||||||
|
#ai-doubt // "Commander, I doubt that very much."
|
||||||
|
Commandant, j'en doute fortement.
|
||||||
|
|
||||||
#captain-orbit // "Let them orbit in peace."
|
#captain-orbit // "Let them orbit in peace."
|
||||||
Laissons-les orbiter en paix.
|
Laissons-les orbiter en paix.
|
||||||
|
|
@ -307,9 +436,18 @@ Laissons-les orbiter en paix.
|
||||||
#ai-philosophical // "A surprisingly philosophical choice, Commander."
|
#ai-philosophical // "A surprisingly philosophical choice, Commander."
|
||||||
Un choix étonnamment philosophique, Commandant.
|
Un choix étonnamment philosophique, Commandant.
|
||||||
|
|
||||||
#ai-happy // "The boxes seem to orbit faster. I think they're happy."
|
#ai-happy-boxes // "The boxes seem to orbit faster. I think they're happy."
|
||||||
Les boîtes semblent orbiter plus vite. Je crois qu'elles sont heureuses.
|
Les boîtes semblent orbiter plus vite. Je crois qu'elles sont heureuses.
|
||||||
|
|
||||||
|
#captain-best-box // "See? Sometimes the best box is the one you don't open."
|
||||||
|
Vous voyez ? Parfois la meilleure boîte est celle qu'on n'ouvre pas.
|
||||||
|
|
||||||
|
#ai-wisest // "I'm going to write that down. That might be the wisest thing you've ever said."
|
||||||
|
Je vais noter ça. C'est peut-être la chose la plus sage que vous ayez jamais dite.
|
||||||
|
|
||||||
|
#captain-used-to-it // "Don't get used to it."
|
||||||
|
N'y prenez pas goût.
|
||||||
|
|
||||||
#ending-complete // "Adventure complete. Updating ship's log."
|
#ending-complete // "Adventure complete. Updating ship's log."
|
||||||
Aventure terminée. Mise à jour du journal de bord.
|
Aventure terminée. Mise à jour du journal de bord.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ beat Intro
|
||||||
-> InvestigateBox
|
-> InvestigateBox
|
||||||
Ignore it and continue #opt-ignore
|
Ignore it and continue #opt-ignore
|
||||||
-> IgnoreBox
|
-> IgnoreBox
|
||||||
Run a deep scan first #opt-scan if fuel > 20
|
Run a deep scan first|||Fuel reserves insufficient for deep scan #opt-scan if fuel > 20
|
||||||
fuel -= 20
|
fuel -= 20
|
||||||
-> DeepScan
|
-> DeepScan
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ beat InvestigateBox
|
||||||
choice
|
choice
|
||||||
Open it immediately #opt-open-now
|
Open it immediately #opt-open-now
|
||||||
-> OpenSpaceBox
|
-> OpenSpaceBox
|
||||||
Scan it first #opt-scan-first if fuel > 10
|
Scan it first|||Not enough fuel for a scan #opt-scan-first if fuel > 10
|
||||||
fuel -= 10
|
fuel -= 10
|
||||||
-> ScanThenOpen
|
-> ScanThenOpen
|
||||||
Poke it with a stick #opt-poke
|
Poke it with a stick #opt-poke
|
||||||
|
|
@ -73,23 +73,23 @@ beat BoxWhisperer
|
||||||
captain: I'm not doing anything, ARIA. I'm just listening. #captain-listening
|
captain: I'm not doing anything, ARIA. I'm just listening. #captain-listening
|
||||||
ai: The box has shared a direct route to its home system. No fuel cost. It's folding space around us like... #ai-folding
|
ai: The box has shared a direct route to its home system. No fuel cost. It's folding space around us like... #ai-folding
|
||||||
captain: Like folding a box? #captain-folding
|
captain: Like folding a box? #captain-folding
|
||||||
ai: I was going to say \"like origami,\" but yes. Like folding a box. #ai-origami
|
ai: I was going to say "like origami," but yes. Like folding a box. #ai-origami
|
||||||
trustAlien = true
|
trustAlien = true
|
||||||
discovered += 1
|
discovered += 1
|
||||||
-> OpenSpaceBox
|
-> OpenSpaceBox
|
||||||
|
|
||||||
beat PokeBox
|
beat PokeBox
|
||||||
You extend the ship's robotic arm and gently poke the box. #poke-arm
|
You extend the ship's robotic arm and gently poke the box. #poke-arm
|
||||||
The box rotates 90 degrees and reveals a label: \"THIS SIDE UP\" #poke-label
|
The box rotates 90 degrees and reveals a label: "THIS SIDE UP" #poke-label
|
||||||
ai: Commander, I don't think boxes in zero gravity have an \"up\". #ai-gravity
|
ai: Commander, I don't think boxes in zero gravity have an "up". #ai-gravity
|
||||||
captain: Maybe the box defines its own reality. #captain-reality
|
captain: Maybe the box defines its own reality. #captain-reality
|
||||||
ai: That's... philosophically concerning. #ai-philosophy
|
ai: That's... philosophically concerning. #ai-philosophy
|
||||||
-> OpenSpaceBox
|
-> OpenSpaceBox
|
||||||
|
|
||||||
beat ScanThenOpen
|
beat ScanThenOpen
|
||||||
ai: Scan reveals the box contains crystallized starlight and something called a \"Nebula Shard\". #scan-contents
|
ai: Scan reveals the box contains crystallized starlight and something called a "Nebula Shard". #scan-contents
|
||||||
ai: Also what appears to be a very small, very angry alien. #scan-alien
|
ai: Also what appears to be a very small, very angry alien. #scan-alien
|
||||||
captain: Define \"very small\". #captain-small
|
captain: Define "very small". #captain-small
|
||||||
ai: Approximately the size of a box. Commander, I think the alien IS a box. #ai-alienbox
|
ai: Approximately the size of a box. Commander, I think the alien IS a box. #ai-alienbox
|
||||||
-> OpenSpaceBox
|
-> OpenSpaceBox
|
||||||
|
|
||||||
|
|
@ -132,7 +132,7 @@ beat LeaveForGood
|
||||||
ai: I hope you're happy, Commander. #ai-happy
|
ai: I hope you're happy, Commander. #ai-happy
|
||||||
captain: I am. No weird space boxes for me today. #captain-happy
|
captain: I am. No weird space boxes for me today. #captain-happy
|
||||||
ai: Incoming transmission. It's... from the box. #ai-transmission
|
ai: Incoming transmission. It's... from the box. #ai-transmission
|
||||||
ai: It says \"you'll be back. they always come back.\" #ai-message
|
ai: It says "you'll be back. they always come back." #ai-message
|
||||||
captain: That's ominous. I love it. #captain-ominous
|
captain: That's ominous. I love it. #captain-ominous
|
||||||
-> Ending
|
-> Ending
|
||||||
|
|
||||||
|
|
@ -158,19 +158,71 @@ beat AlienEncounter
|
||||||
alien: I've been collecting boxes across the galaxy for centuries. #alien-collecting
|
alien: I've been collecting boxes across the galaxy for centuries. #alien-collecting
|
||||||
alien: Most species open them. Very few appreciate them. #alien-appreciate
|
alien: Most species open them. Very few appreciate them. #alien-appreciate
|
||||||
captain: I appreciate boxes. Especially ones with good loot. #captain-loot
|
captain: I appreciate boxes. Especially ones with good loot. #captain-loot
|
||||||
alien: \"Loot.\" What a crude word for cosmic treasures. #alien-crude
|
alien: "Loot." What a crude word for cosmic treasures. #alien-crude
|
||||||
|
|
||||||
choice
|
choice
|
||||||
Ask about the galaxy's rarest box #opt-rare-box
|
Ask about the galaxy's rarest box #opt-rare-box
|
||||||
alien: The Singularity Box. It contains everything and nothing. Also a coupon. #alien-singularity
|
-> RareBoxStory
|
||||||
-> Ending
|
|
||||||
Trade items with Zx'thorp #opt-trade
|
Trade items with Zx'thorp #opt-trade
|
||||||
alien: I have a Space Helmet for you. In exchange, I want your sense of wonder. #alien-trade
|
-> AlienTrade
|
||||||
captain: Deal. Wait-- #captain-deal
|
|
||||||
-> Ending
|
|
||||||
Challenge the alien to a box-opening contest #opt-contest
|
Challenge the alien to a box-opening contest #opt-contest
|
||||||
|
-> BoxContest
|
||||||
|
|
||||||
|
beat RareBoxStory
|
||||||
|
alien: The Singularity Box. It contains everything and nothing. Also a coupon. #alien-singularity
|
||||||
|
captain: A coupon? For what? #captain-coupon
|
||||||
|
alien: For another Singularity Box. It's boxes all the way down. #alien-coupon-answer
|
||||||
|
captain: You're saying the universe is just boxes inside boxes? #captain-boxes-inside
|
||||||
|
alien: Now you're getting it, Captain. #alien-getting-it
|
||||||
|
ai: Commander, I'm detecting a slight existential crisis in your vitals. #ai-existential
|
||||||
|
captain: I'm fine, ARIA. Just rethinking everything I know about reality. #captain-fine
|
||||||
|
-> Ending
|
||||||
|
|
||||||
|
beat AlienTrade
|
||||||
|
alien: I have a Space Helmet for you. In exchange, I want... a story. #alien-trade-offer
|
||||||
|
captain: A story? #captain-story
|
||||||
|
alien: Tell me about the first box you ever opened. #alien-first-box
|
||||||
|
captain: It was small. Cardboard. My parents gave it to me. Inside was a toy rocket ship. #captain-first-box-story
|
||||||
|
alien: ... #alien-pause
|
||||||
|
alien: That's the most beautiful thing I've ever heard. #alien-beautiful-trade
|
||||||
|
The alien removes its helmet and places it gently in your hands. Its eyes -- all seven of them -- are glistening. #alien-gives-helmet
|
||||||
|
captain: Are you... crying? #captain-crying
|
||||||
|
alien: We don't cry. We leak appreciation fluid. It's different. #alien-appreciation
|
||||||
|
-> Ending
|
||||||
|
|
||||||
|
beat BoxContest
|
||||||
alien: You dare? No one out-opens Zx'thorp! #alien-dare
|
alien: You dare? No one out-opens Zx'thorp! #alien-dare
|
||||||
alien: ...fine. Best of three. You open first. #alien-contest
|
alien: ...fine. Best of three. You open first. #alien-contest
|
||||||
|
|
||||||
|
choice
|
||||||
|
Open with confidence #opt-confident
|
||||||
|
-> ContestRoundOne
|
||||||
|
Open with theatrical flair #opt-flair
|
||||||
|
-> ContestFlair
|
||||||
|
|
||||||
|
beat ContestFlair
|
||||||
|
markSecretBranch("space_box_flair")
|
||||||
|
You crack your knuckles. You stretch your fingers. You do a little spin. #flair-spin
|
||||||
|
captain: Watch and learn, Zx'thorp. #captain-watch
|
||||||
|
You open the box with a flourish, confetti exploding from inside. Wait -- you didn't put confetti in there. #flair-confetti
|
||||||
|
alien: Theatrical. I approve. 8 out of 10. #alien-score-flair
|
||||||
|
ai: Commander, where did the confetti come from? #ai-confetti
|
||||||
|
captain: From the heart, ARIA. From the heart. #captain-heart
|
||||||
|
-> ContestAlienTurn
|
||||||
|
|
||||||
|
beat ContestRoundOne
|
||||||
|
You grip the box firmly and open it in one clean motion. #contest-open
|
||||||
|
alien: Clean technique. 7 out of 10. #alien-score
|
||||||
|
-> ContestAlienTurn
|
||||||
|
|
||||||
|
beat ContestAlienTurn
|
||||||
|
Zx'thorp produces a box from somewhere. You try not to think about where. #alien-produces
|
||||||
|
The alien opens it using three of its tentacles simultaneously. The lid flies off, does a triple backflip, and lands perfectly back on the box. #alien-opens
|
||||||
|
captain: That's... that's not even possible. #captain-impossible
|
||||||
|
alien: 10 out of 10. I win. #alien-score-2
|
||||||
|
ai: I'm afraid the alien is correct, Commander. That was objectively superior box-opening. #ai-correct
|
||||||
|
captain: Rematch. One day. I'll be ready. #captain-rematch
|
||||||
|
alien: I look forward to it, Captain. Across the stars, I'll be waiting. With boxes. #alien-waiting
|
||||||
-> Ending
|
-> Ending
|
||||||
|
|
||||||
beat SpaceExploration
|
beat SpaceExploration
|
||||||
|
|
@ -182,20 +234,52 @@ beat SpaceExploration
|
||||||
|
|
||||||
choice
|
choice
|
||||||
Open the biggest box first #opt-big
|
Open the biggest box first #opt-big
|
||||||
|
-> BigBox
|
||||||
|
Open all three at once|||Not enough fuel for maximum box velocity #opt-all if fuel >= 30
|
||||||
|
fuel -= 30
|
||||||
|
-> AllBoxes
|
||||||
|
Leave them orbiting, they seem happy #opt-happy
|
||||||
|
-> HappyBoxes
|
||||||
|
|
||||||
|
beat BigBox
|
||||||
You open the biggest box. Inside: two medium boxes and a note. #big-open
|
You open the biggest box. Inside: two medium boxes and a note. #big-open
|
||||||
The note reads: \"Congratulations! You've found the Box Nebula. Population: boxes.\" #big-note
|
The note reads: "Congratulations! You've found the Box Nebula. Population: boxes." #big-note
|
||||||
|
captain: There's a whole nebula of boxes? #captain-nebula
|
||||||
|
ai: Scanners confirm it, Commander. Approximately 4.7 billion boxes, all orbiting a supermassive box. #ai-billion
|
||||||
|
|
||||||
|
choice
|
||||||
|
We must go deeper #opt-deeper
|
||||||
|
-> GoDeeper
|
||||||
|
We've seen enough boxes for one day #opt-enough
|
||||||
-> Ending
|
-> Ending
|
||||||
Open all three at once #opt-all
|
|
||||||
|
beat GoDeeper
|
||||||
|
You open the two medium boxes. Each contains two smaller boxes. #deeper-open
|
||||||
|
captain: ARIA, how many boxes is this now? #captain-how-many
|
||||||
|
ai: I've lost count, Commander. My box-tracking subroutine has crashed. #ai-crashed
|
||||||
|
captain: Then let the boxes count themselves. #captain-count
|
||||||
|
ai: ...that's not how boxes work, Commander. #ai-not-how
|
||||||
|
captain: Maybe it should be. #captain-should-be
|
||||||
|
-> Ending
|
||||||
|
|
||||||
|
beat AllBoxes
|
||||||
captain: All three, ARIA. Maximum box velocity. #captain-velocity
|
captain: All three, ARIA. Maximum box velocity. #captain-velocity
|
||||||
ai: That's not a real measurement, but I admire your enthusiasm. #ai-enthusiasm
|
ai: That's not a real measurement, but I admire your enthusiasm. Fuel reserves depleted for the maneuver. #ai-enthusiasm
|
||||||
All three boxes burst open simultaneously. The contents mix together in zero gravity. #all-open
|
All three boxes burst open simultaneously. The contents mix together in zero gravity. #all-open
|
||||||
ai: Commander, the items are forming... a bigger box. #ai-biggerbox
|
ai: Commander, the items are forming... a bigger box. #ai-biggerbox
|
||||||
captain: Of course they are. #captain-ofcourse
|
captain: Of course they are. #captain-ofcourse
|
||||||
|
ai: A bigger, better box. It's vibrating at a frequency I've never seen. I think it's... proud of itself. #ai-proud
|
||||||
|
captain: A proud box. Now I've seen everything. #captain-everything
|
||||||
|
ai: Commander, I doubt that very much. #ai-doubt
|
||||||
-> Ending
|
-> Ending
|
||||||
Leave them orbiting, they seem happy #opt-happy
|
|
||||||
|
beat HappyBoxes
|
||||||
captain: Let them orbit in peace. #captain-orbit
|
captain: Let them orbit in peace. #captain-orbit
|
||||||
ai: A surprisingly philosophical choice, Commander. #ai-philosophical
|
ai: A surprisingly philosophical choice, Commander. #ai-philosophical
|
||||||
ai: The boxes seem to orbit faster. I think they're happy. #ai-happy
|
ai: The boxes seem to orbit faster. I think they're happy. #ai-happy-boxes
|
||||||
|
captain: See? Sometimes the best box is the one you don't open. #captain-best-box
|
||||||
|
ai: I'm going to write that down. That might be the wisest thing you've ever said. #ai-wisest
|
||||||
|
captain: Don't get used to it. #captain-used-to-it
|
||||||
-> Ending
|
-> Ending
|
||||||
|
|
||||||
beat Ending
|
beat Ending
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,6 @@
|
||||||
"rollCount": 1,
|
"rollCount": 1,
|
||||||
"entries": [
|
"entries": [
|
||||||
{"itemDefinitionId": "meta_completion", "weight": 3},
|
{"itemDefinitionId": "meta_completion", "weight": 3},
|
||||||
{"itemDefinitionId": "meta_chat", "weight": 3},
|
|
||||||
{"itemDefinitionId": "meta_crafting", "weight": 3},
|
{"itemDefinitionId": "meta_crafting", "weight": 3},
|
||||||
{"itemDefinitionId": "meta_extended_colors", "weight": 3},
|
{"itemDefinitionId": "meta_extended_colors", "weight": 3},
|
||||||
{"itemDefinitionId": "box_meta_resources", "weight": 1}
|
{"itemDefinitionId": "box_meta_resources", "weight": 1}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
{"id": "meta_resources", "nameKey": "meta.resources", "descriptionKey": "meta.resources.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "ResourcePanel"},
|
{"id": "meta_resources", "nameKey": "meta.resources", "descriptionKey": "meta.resources.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "ResourcePanel"},
|
||||||
{"id": "meta_stats", "nameKey": "meta.stats", "descriptionKey": "meta.stats.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "StatsPanel"},
|
{"id": "meta_stats", "nameKey": "meta.stats", "descriptionKey": "meta.stats.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "StatsPanel"},
|
||||||
{"id": "meta_portrait", "nameKey": "meta.portrait", "descriptionKey": "meta.portrait.desc", "category": "Meta", "rarity": "Epic", "tags": ["Meta"], "metaUnlock": "PortraitPanel"},
|
{"id": "meta_portrait", "nameKey": "meta.portrait", "descriptionKey": "meta.portrait.desc", "category": "Meta", "rarity": "Epic", "tags": ["Meta"], "metaUnlock": "PortraitPanel"},
|
||||||
{"id": "meta_chat", "nameKey": "meta.chat", "descriptionKey": "meta.chat.desc", "category": "Meta", "rarity": "Epic", "tags": ["Meta"], "metaUnlock": "ChatPanel"},
|
|
||||||
{"id": "meta_layout", "nameKey": "meta.layout", "descriptionKey": "meta.layout.desc", "category": "Meta", "rarity": "Legendary", "tags": ["Meta"], "metaUnlock": "FullLayout"},
|
{"id": "meta_layout", "nameKey": "meta.layout", "descriptionKey": "meta.layout.desc", "category": "Meta", "rarity": "Legendary", "tags": ["Meta"], "metaUnlock": "FullLayout"},
|
||||||
{"id": "meta_shortcuts", "nameKey": "meta.shortcuts", "descriptionKey": "meta.shortcuts.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "KeyboardShortcuts"},
|
{"id": "meta_shortcuts", "nameKey": "meta.shortcuts", "descriptionKey": "meta.shortcuts.desc", "category": "Meta", "rarity": "Rare", "tags": ["Meta"], "metaUnlock": "KeyboardShortcuts"},
|
||||||
{"id": "meta_animation", "nameKey": "meta.animation", "descriptionKey": "meta.animation.desc", "category": "Meta", "rarity": "Uncommon", "tags": ["Meta"], "metaUnlock": "BoxAnimation"},
|
{"id": "meta_animation", "nameKey": "meta.animation", "descriptionKey": "meta.animation.desc", "category": "Meta", "rarity": "Uncommon", "tags": ["Meta"], "metaUnlock": "BoxAnimation"},
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
"action.appearance": "Change appearance",
|
"action.appearance": "Change appearance",
|
||||||
"action.save": "Save",
|
"action.save": "Save",
|
||||||
"action.quit": "Return to menu",
|
"action.quit": "Return to menu",
|
||||||
|
"action.new": "NEW",
|
||||||
|
|
||||||
"prompt.name": "What is your name, brave box-opener?",
|
"prompt.name": "What is your name, brave box-opener?",
|
||||||
"prompt.choose_action": "What would you like to do?",
|
"prompt.choose_action": "What would you like to do?",
|
||||||
|
|
@ -115,7 +116,6 @@
|
||||||
"meta.resources": "Characteristics Panel",
|
"meta.resources": "Characteristics Panel",
|
||||||
"meta.stats": "Stats Panel",
|
"meta.stats": "Stats Panel",
|
||||||
"meta.portrait": "Portrait Panel",
|
"meta.portrait": "Portrait Panel",
|
||||||
"meta.chat": "Chat Panel",
|
|
||||||
"meta.layout": "Full Layout Mode",
|
"meta.layout": "Full Layout Mode",
|
||||||
"meta.shortcuts": "Keyboard Shortcuts",
|
"meta.shortcuts": "Keyboard Shortcuts",
|
||||||
"meta.animation": "Box Opening Animation",
|
"meta.animation": "Box Opening Animation",
|
||||||
|
|
@ -300,8 +300,6 @@
|
||||||
"lore.name_9": "Fragment: The Manual",
|
"lore.name_9": "Fragment: The Manual",
|
||||||
"lore.name_10": "Fragment: Schrödinger",
|
"lore.name_10": "Fragment: Schrödinger",
|
||||||
|
|
||||||
"log.title": "Event Log",
|
|
||||||
|
|
||||||
"cookie.1": "A box within a box is still a box.",
|
"cookie.1": "A box within a box is still a box.",
|
||||||
"cookie.2": "ERROR: This cookie contains no fortune. Please try again.",
|
"cookie.2": "ERROR: This cookie contains no fortune. Please try again.",
|
||||||
"cookie.3": "You will open many boxes. This prediction has a 100% accuracy rate.",
|
"cookie.3": "You will open many boxes. This prediction has a 100% accuracy rate.",
|
||||||
|
|
@ -431,6 +429,7 @@
|
||||||
"adventure.none_available": "No adventures available yet. Keep opening boxes!",
|
"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.coming_soon": "Adventure '{0}' is coming soon! The boxes are still being assembled.",
|
||||||
"adventure.done": "Done",
|
"adventure.done": "Done",
|
||||||
|
"adventure.unavailable": "Unavailable",
|
||||||
"adventure.unlocked": "🎉 New adventure unlocked! Discover '{0}' in the adventure menu!",
|
"adventure.unlocked": "🎉 New adventure unlocked! Discover '{0}' in the adventure menu!",
|
||||||
"adventure.name.Space": "Starbound",
|
"adventure.name.Space": "Starbound",
|
||||||
"adventure.name.Medieval": "Castle Cardboard",
|
"adventure.name.Medieval": "Castle Cardboard",
|
||||||
|
|
@ -442,6 +441,27 @@
|
||||||
"adventure.name.Microscopic": "Cell Division",
|
"adventure.name.Microscopic": "Cell Division",
|
||||||
"adventure.name.DarkFantasy": "Ashen Wastes",
|
"adventure.name.DarkFantasy": "Ashen Wastes",
|
||||||
"adventure.name.Destiny": "Gallery of Echoes",
|
"adventure.name.Destiny": "Gallery of Echoes",
|
||||||
|
|
||||||
|
"character.captain_nova": "Captain Nova",
|
||||||
|
"character.aria": "ARIA",
|
||||||
|
"character.zxthorp": "Zx'thorp",
|
||||||
|
"character.sir_boxalot": "Sir Boxalot",
|
||||||
|
"character.malkith": "Malkith",
|
||||||
|
"character.blackbeard_the_unboxable": "Blackbeard the Unboxable",
|
||||||
|
"character.linu": "Linu",
|
||||||
|
"character.sandrea": "Sandrea",
|
||||||
|
"character.samuel": "Samuel",
|
||||||
|
"character.mordecai_the_grim": "Mordecai the Grim",
|
||||||
|
"character.pierrick": "Pierrick",
|
||||||
|
"character.grug": "Grug",
|
||||||
|
"character.duncan": "Duncan",
|
||||||
|
"character.chenda": "Chenda",
|
||||||
|
"character.farah": "Farah",
|
||||||
|
"character.dr._quantum": "Dr. Quantum",
|
||||||
|
"character.the_box_entity": "The Box Entity",
|
||||||
|
"character.dr._cellina": "Dr. Cellina",
|
||||||
|
"character.mitochondria_mike": "Mitochondria Mike",
|
||||||
|
"character.the_box": "The Box",
|
||||||
"ui.inventory": "Inventory",
|
"ui.inventory": "Inventory",
|
||||||
"stats.boxes_opened": "Boxes Opened",
|
"stats.boxes_opened": "Boxes Opened",
|
||||||
"stats.title": "Stats",
|
"stats.title": "Stats",
|
||||||
|
|
@ -481,7 +501,6 @@
|
||||||
"meta.resources.desc": "Displays your character's characteristics (health, mana, etc.).",
|
"meta.resources.desc": "Displays your character's characteristics (health, mana, etc.).",
|
||||||
"meta.stats.desc": "Shows your progression stats and character attributes.",
|
"meta.stats.desc": "Shows your progression stats and character attributes.",
|
||||||
"meta.portrait.desc": "Displays your character's visual appearance.",
|
"meta.portrait.desc": "Displays your character's visual appearance.",
|
||||||
"meta.chat.desc": "Shows an event log of recent actions.",
|
|
||||||
"meta.layout.desc": "Arranges all panels into a full dashboard layout.",
|
"meta.layout.desc": "Arranges all panels into a full dashboard layout.",
|
||||||
"meta.shortcuts.desc": "Enables keyboard shortcuts for quick actions.",
|
"meta.shortcuts.desc": "Enables keyboard shortcuts for quick actions.",
|
||||||
"meta.animation.desc": "Adds opening animations when unboxing.",
|
"meta.animation.desc": "Adds opening animations when unboxing.",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
"action.appearance": "Changer d'apparence",
|
"action.appearance": "Changer d'apparence",
|
||||||
"action.save": "Sauvegarder",
|
"action.save": "Sauvegarder",
|
||||||
"action.quit": "Retourner au menu",
|
"action.quit": "Retourner au menu",
|
||||||
|
"action.new": "NOUVEAU",
|
||||||
|
|
||||||
"prompt.name": "Quel est ton nom, brave ouvreur de boîtes ?",
|
"prompt.name": "Quel est ton nom, brave ouvreur de boîtes ?",
|
||||||
"prompt.choose_action": "Que veux-tu faire ?",
|
"prompt.choose_action": "Que veux-tu faire ?",
|
||||||
|
|
@ -115,7 +116,6 @@
|
||||||
"meta.resources": "Panneau de caractéristiques",
|
"meta.resources": "Panneau de caractéristiques",
|
||||||
"meta.stats": "Panneau de statistiques",
|
"meta.stats": "Panneau de statistiques",
|
||||||
"meta.portrait": "Panneau portrait",
|
"meta.portrait": "Panneau portrait",
|
||||||
"meta.chat": "Panneau de discussion",
|
|
||||||
"meta.layout": "Mise en page complète",
|
"meta.layout": "Mise en page complète",
|
||||||
"meta.shortcuts": "Raccourcis clavier",
|
"meta.shortcuts": "Raccourcis clavier",
|
||||||
"meta.animation": "Animation d'ouverture de boîte",
|
"meta.animation": "Animation d'ouverture de boîte",
|
||||||
|
|
@ -300,8 +300,6 @@
|
||||||
"lore.name_9": "Fragment : Le Manuel",
|
"lore.name_9": "Fragment : Le Manuel",
|
||||||
"lore.name_10": "Fragment : Schrödinger",
|
"lore.name_10": "Fragment : Schrödinger",
|
||||||
|
|
||||||
"log.title": "Journal d'événements",
|
|
||||||
|
|
||||||
"cookie.1": "Une boîte dans une boîte reste une boîte.",
|
"cookie.1": "Une boîte dans une boîte reste une boîte.",
|
||||||
"cookie.2": "ERREUR : Ce cookie ne contient aucune fortune. Réessayez.",
|
"cookie.2": "ERREUR : Ce cookie ne contient aucune fortune. Réessayez.",
|
||||||
"cookie.3": "Vous ouvrirez beaucoup de boîtes. Cette prédiction a un taux de précision de 100%.",
|
"cookie.3": "Vous ouvrirez beaucoup de boîtes. Cette prédiction a un taux de précision de 100%.",
|
||||||
|
|
@ -431,6 +429,7 @@
|
||||||
"adventure.none_available": "Aucune aventure disponible. Continue à ouvrir des boîtes !",
|
"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.coming_soon": "L'aventure '{0}' arrive bientôt ! Les boîtes sont encore en cours d'assemblage.",
|
||||||
"adventure.done": "Terminée",
|
"adventure.done": "Terminée",
|
||||||
|
"adventure.unavailable": "Indisponible",
|
||||||
"adventure.unlocked": "🎉 Nouvelle aventure débloquée ! Découvre '{0}' dans « Partir à l'aventure » !",
|
"adventure.unlocked": "🎉 Nouvelle aventure débloquée ! Découvre '{0}' dans « Partir à l'aventure » !",
|
||||||
"adventure.name.Space": "Odyssée stellaire",
|
"adventure.name.Space": "Odyssée stellaire",
|
||||||
"adventure.name.Medieval": "Château Carton",
|
"adventure.name.Medieval": "Château Carton",
|
||||||
|
|
@ -442,6 +441,28 @@
|
||||||
"adventure.name.Microscopic": "Division cellulaire",
|
"adventure.name.Microscopic": "Division cellulaire",
|
||||||
"adventure.name.DarkFantasy": "Terres Cendrées",
|
"adventure.name.DarkFantasy": "Terres Cendrées",
|
||||||
"adventure.name.Destiny": "Galerie des Échos",
|
"adventure.name.Destiny": "Galerie des Échos",
|
||||||
|
|
||||||
|
"character.captain_nova": "Capitaine Nova",
|
||||||
|
"character.aria": "ARIA",
|
||||||
|
"character.zxthorp": "Zx'thorp",
|
||||||
|
"character.sir_boxalot": "Sire Boîtalot",
|
||||||
|
"character.malkith": "Malkith",
|
||||||
|
"character.blackbeard_the_unboxable": "Barbe-Noire l'Inouvrable",
|
||||||
|
"character.linu": "Linu",
|
||||||
|
"character.sandrea": "Sandrea",
|
||||||
|
"character.samuel": "Samuel",
|
||||||
|
"character.mordecai_the_grim": "Mordecai le Sinistre",
|
||||||
|
"character.pierrick": "Pierrick",
|
||||||
|
"character.grug": "Grug",
|
||||||
|
"character.duncan": "Duncan",
|
||||||
|
"character.chenda": "Chenda",
|
||||||
|
"character.farah": "Farah",
|
||||||
|
"character.dr._quantum": "Dr. Quantum",
|
||||||
|
"character.the_box_entity": "L'Entité Boîte",
|
||||||
|
"character.dr._cellina": "Dr. Cellina",
|
||||||
|
"character.mitochondria_mike": "Mike Mitochondrie",
|
||||||
|
"character.the_box": "La Boîte",
|
||||||
|
|
||||||
"ui.inventory": "Inventaire",
|
"ui.inventory": "Inventaire",
|
||||||
"stats.boxes_opened": "Boîtes ouvertes",
|
"stats.boxes_opened": "Boîtes ouvertes",
|
||||||
"stats.title": "Stats",
|
"stats.title": "Stats",
|
||||||
|
|
@ -481,7 +502,6 @@
|
||||||
"meta.resources.desc": "Affiche les caractéristiques de ton personnage (santé, mana, etc.).",
|
"meta.resources.desc": "Affiche les caractéristiques de ton personnage (santé, mana, etc.).",
|
||||||
"meta.stats.desc": "Affiche ta progression et les attributs de ton personnage.",
|
"meta.stats.desc": "Affiche ta progression et les attributs de ton personnage.",
|
||||||
"meta.portrait.desc": "Affiche l'apparence visuelle de ton personnage.",
|
"meta.portrait.desc": "Affiche l'apparence visuelle de ton personnage.",
|
||||||
"meta.chat.desc": "Affiche un journal des événements récents.",
|
|
||||||
"meta.layout.desc": "Organise tous les panneaux en tableau de bord complet.",
|
"meta.layout.desc": "Organise tous les panneaux en tableau de bord complet.",
|
||||||
"meta.shortcuts.desc": "Active les raccourcis clavier pour des actions rapides.",
|
"meta.shortcuts.desc": "Active les raccourcis clavier pour des actions rapides.",
|
||||||
"meta.animation.desc": "Ajoute des animations d'ouverture de boîtes.",
|
"meta.animation.desc": "Ajoute des animations d'ouverture de boîtes.",
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,16 @@ public sealed class AdventureEngine
|
||||||
|
|
||||||
private void HandleDialogue(Loreline.Interpreter.Dialogue dialogue)
|
private void HandleDialogue(Loreline.Interpreter.Dialogue dialogue)
|
||||||
{
|
{
|
||||||
_renderer.ShowAdventureDialogue(dialogue.Character, dialogue.Text);
|
// Translate character name if a localization key exists
|
||||||
|
string? displayName = dialogue.Character;
|
||||||
|
if (displayName is not null)
|
||||||
|
{
|
||||||
|
string key = $"character.{displayName.ToLowerInvariant().Replace(" ", "_").Replace("'", "")}";
|
||||||
|
string localized = _loc.Get(key);
|
||||||
|
if (!localized.StartsWith("[MISSING:"))
|
||||||
|
displayName = localized;
|
||||||
|
}
|
||||||
|
_renderer.ShowAdventureDialogue(displayName, dialogue.Text);
|
||||||
_renderer.WaitForKeyPress();
|
_renderer.WaitForKeyPress();
|
||||||
dialogue.Callback();
|
dialogue.Callback();
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +220,8 @@ public sealed class AdventureEngine
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
options.Add($"(unavailable) {text}");
|
string prefix = hint ?? _loc.Get("adventure.unavailable");
|
||||||
|
options.Add($"({prefix}) {text}");
|
||||||
hints.Add(hint);
|
hints.Add(hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@ public enum UIFeature
|
||||||
/// <summary>Phase 6: ASCII art portrait reflecting equipped cosmetics.</summary>
|
/// <summary>Phase 6: ASCII art portrait reflecting equipped cosmetics.</summary>
|
||||||
PortraitPanel,
|
PortraitPanel,
|
||||||
|
|
||||||
/// <summary>Phase 6: Chat panel for NPC dialogues and narrative events.</summary>
|
|
||||||
ChatPanel,
|
|
||||||
|
|
||||||
/// <summary>Phase 8: Complete multi-panel layout with all UI elements organized.</summary>
|
/// <summary>Phase 8: Complete multi-panel layout with all UI elements organized.</summary>
|
||||||
FullLayout,
|
FullLayout,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,6 @@ public sealed class GameState
|
||||||
public HashSet<TextColor> AvailableTextColors { get; set; } = [];
|
public HashSet<TextColor> AvailableTextColors { get; set; } = [];
|
||||||
public List<CraftingJob> ActiveCraftingJobs { get; set; } = [];
|
public List<CraftingJob> ActiveCraftingJobs { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In-memory event log shown in the ChatPanel (not persisted to save).
|
|
||||||
/// </summary>
|
|
||||||
[System.Text.Json.Serialization.JsonIgnore]
|
|
||||||
public List<string> EventLog { get; set; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current value of a resource, or 0 if the resource is not tracked.
|
/// Returns the current value of a resource, or 0 if the resource is not tracked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,12 @@ public static class Program
|
||||||
actions.Add((_loc.Get("action.inventory"), "inventory"));
|
actions.Add((_loc.Get("action.inventory"), "inventory"));
|
||||||
|
|
||||||
if (_state.UnlockedAdventures.Count > 0)
|
if (_state.UnlockedAdventures.Count > 0)
|
||||||
actions.Add((_loc.Get("action.adventure"), "adventure"));
|
{
|
||||||
|
string adventureLabel = _loc.Get("action.adventure");
|
||||||
|
if (_state.CompletedAdventures.Count == 0)
|
||||||
|
adventureLabel = $"({_loc.Get("action.new")}) {adventureLabel}";
|
||||||
|
actions.Add((adventureLabel, "adventure"));
|
||||||
|
}
|
||||||
|
|
||||||
if (_state.UnlockedCosmetics.Count > 0)
|
if (_state.UnlockedCosmetics.Count > 0)
|
||||||
actions.Add((_loc.Get("action.appearance"), "appearance"));
|
actions.Add((_loc.Get("action.appearance"), "appearance"));
|
||||||
|
|
@ -503,7 +508,6 @@ public static class Program
|
||||||
RefreshRenderer();
|
RefreshRenderer();
|
||||||
var featureLabel = _loc.Get(GetUIFeatureLocKey(uiEvt.Feature));
|
var featureLabel = _loc.Get(GetUIFeatureLocKey(uiEvt.Feature));
|
||||||
_renderer.ShowUIFeatureUnlocked(featureLabel);
|
_renderer.ShowUIFeatureUnlocked(featureLabel);
|
||||||
AddEventLog($"* {featureLabel}");
|
|
||||||
_renderer.WaitForKeyPress(_loc.Get("prompt.press_key"));
|
_renderer.WaitForKeyPress(_loc.Get("prompt.press_key"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -524,7 +528,6 @@ public static class Program
|
||||||
case ResourceChangedEvent resEvt:
|
case ResourceChangedEvent resEvt:
|
||||||
var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}");
|
var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}");
|
||||||
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
||||||
AddEventLog($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageEvent msgEvt:
|
case MessageEvent msgEvt:
|
||||||
|
|
@ -542,7 +545,6 @@ public static class Program
|
||||||
case AdventureUnlockedEvent advUnlockedEvt:
|
case AdventureUnlockedEvent advUnlockedEvt:
|
||||||
var advName = GetAdventureName(advUnlockedEvt.Theme);
|
var advName = GetAdventureName(advUnlockedEvt.Theme);
|
||||||
_renderer.ShowMessage(_loc.Get("adventure.unlocked", advName));
|
_renderer.ShowMessage(_loc.Get("adventure.unlocked", advName));
|
||||||
AddEventLog($">> {advName}");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AdventureStartedEvent advEvt:
|
case AdventureStartedEvent advEvt:
|
||||||
|
|
@ -582,10 +584,6 @@ public static class Program
|
||||||
{
|
{
|
||||||
_renderer.ShowLootReveal(allLoot);
|
_renderer.ShowLootReveal(allLoot);
|
||||||
|
|
||||||
// Proposal 6A: Feed loot to the event log
|
|
||||||
foreach (var (name, rarity, _) in allLoot)
|
|
||||||
AddEventLog($"+ {name} [{_loc.Get($"rarity.{rarity.ToLower()}")}]");
|
|
||||||
|
|
||||||
// Resource summary removed — characteristics are shown in the dedicated panel
|
// Resource summary removed — characteristics are shown in the dedicated panel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,7 +751,6 @@ public static class Program
|
||||||
: _loc.Get("inventory.item_used", itemName);
|
: _loc.Get("inventory.item_used", itemName);
|
||||||
_renderer.ShowMessage(usedMsg);
|
_renderer.ShowMessage(usedMsg);
|
||||||
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
||||||
AddEventLog($"{itemName} {UnicodeSupport.Arrow} {resName} {resEvt.OldValue}{UnicodeSupport.Arrow}{resEvt.NewValue}");
|
|
||||||
break;
|
break;
|
||||||
case MessageEvent msgEvt:
|
case MessageEvent msgEvt:
|
||||||
_renderer.ShowMessage(_loc.Get(msgEvt.MessageKey, msgEvt.Args ?? []));
|
_renderer.ShowMessage(_loc.Get(msgEvt.MessageKey, msgEvt.Args ?? []));
|
||||||
|
|
@ -1000,7 +997,6 @@ public static class Program
|
||||||
UIFeature.ResourcePanel => "meta.resources",
|
UIFeature.ResourcePanel => "meta.resources",
|
||||||
UIFeature.StatsPanel => "meta.stats",
|
UIFeature.StatsPanel => "meta.stats",
|
||||||
UIFeature.PortraitPanel => "meta.portrait",
|
UIFeature.PortraitPanel => "meta.portrait",
|
||||||
UIFeature.ChatPanel => "meta.chat",
|
|
||||||
UIFeature.FullLayout => "meta.layout",
|
UIFeature.FullLayout => "meta.layout",
|
||||||
UIFeature.KeyboardShortcuts => "meta.shortcuts",
|
UIFeature.KeyboardShortcuts => "meta.shortcuts",
|
||||||
UIFeature.BoxAnimation => "meta.animation",
|
UIFeature.BoxAnimation => "meta.animation",
|
||||||
|
|
@ -1018,18 +1014,6 @@ public static class Program
|
||||||
return name.StartsWith("[MISSING:") ? theme.ToString() : name;
|
return name.StartsWith("[MISSING:") ? theme.ToString() : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int MaxEventLogEntries = 20;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a message to the in-memory event log displayed in the ChatPanel.
|
|
||||||
/// </summary>
|
|
||||||
private static void AddEventLog(string message)
|
|
||||||
{
|
|
||||||
_state.EventLog.Add(message);
|
|
||||||
if (_state.EventLog.Count > MaxEventLogEntries)
|
|
||||||
_state.EventLog.RemoveAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetLocalizedName(string definitionId)
|
private static string GetLocalizedName(string definitionId)
|
||||||
{
|
{
|
||||||
var itemDef = _registry.GetItem(definitionId);
|
var itemDef = _registry.GetItem(definitionId);
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
using OpenTheBox.Localization;
|
|
||||||
using Spectre.Console;
|
|
||||||
using Spectre.Console.Rendering;
|
|
||||||
|
|
||||||
namespace OpenTheBox.Rendering.Panels;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Renders a compact event log panel showing recent game events.
|
|
||||||
/// Replaces the original dialogue-only chat panel with a general-purpose log.
|
|
||||||
/// </summary>
|
|
||||||
public static class ChatPanel
|
|
||||||
{
|
|
||||||
private const int MaxVisibleMessages = 8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a renderable event log from a list of recent log messages.
|
|
||||||
/// </summary>
|
|
||||||
public static IRenderable Render(List<string> logMessages, LocalizationManager? loc = null)
|
|
||||||
{
|
|
||||||
var rows = new List<IRenderable>();
|
|
||||||
|
|
||||||
// Show only the most recent messages
|
|
||||||
var visible = logMessages.Count > MaxVisibleMessages
|
|
||||||
? logMessages.Skip(logMessages.Count - MaxVisibleMessages).ToList()
|
|
||||||
: logMessages;
|
|
||||||
|
|
||||||
if (visible.Count == 0)
|
|
||||||
{
|
|
||||||
var emptyText = loc?.Get("craft.panel.empty") ?? "...";
|
|
||||||
rows.Add(new Markup($"[dim]{Markup.Escape(emptyText)}[/]"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var msg in visible)
|
|
||||||
{
|
|
||||||
rows.Add(new Markup($"[dim]{Markup.Escape(msg)}[/]"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var title = loc?.Get("log.title") ?? "Log";
|
|
||||||
return new Panel(new Rows(rows))
|
|
||||||
.Header($"[bold aqua]{Markup.Escape(title)}[/]")
|
|
||||||
.Border(BoxBorder.Rounded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -31,7 +31,6 @@ public sealed class RenderContext
|
||||||
public bool HasResourcePanel => Has(UIFeature.ResourcePanel);
|
public bool HasResourcePanel => Has(UIFeature.ResourcePanel);
|
||||||
public bool HasStatsPanel => Has(UIFeature.StatsPanel);
|
public bool HasStatsPanel => Has(UIFeature.StatsPanel);
|
||||||
public bool HasPortraitPanel => Has(UIFeature.PortraitPanel);
|
public bool HasPortraitPanel => Has(UIFeature.PortraitPanel);
|
||||||
public bool HasChatPanel => Has(UIFeature.ChatPanel);
|
|
||||||
public bool HasFullLayout => Has(UIFeature.FullLayout);
|
public bool HasFullLayout => Has(UIFeature.FullLayout);
|
||||||
public bool HasKeyboardShortcuts => Has(UIFeature.KeyboardShortcuts);
|
public bool HasKeyboardShortcuts => Has(UIFeature.KeyboardShortcuts);
|
||||||
public bool HasBoxAnimation => Has(UIFeature.BoxAnimation);
|
public bool HasBoxAnimation => Has(UIFeature.BoxAnimation);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ public static class RendererFactory
|
||||||
context.HasResourcePanel ||
|
context.HasResourcePanel ||
|
||||||
context.HasStatsPanel ||
|
context.HasStatsPanel ||
|
||||||
context.HasPortraitPanel ||
|
context.HasPortraitPanel ||
|
||||||
context.HasChatPanel ||
|
|
||||||
context.HasFullLayout ||
|
context.HasFullLayout ||
|
||||||
context.HasKeyboardShortcuts ||
|
context.HasKeyboardShortcuts ||
|
||||||
context.HasBoxAnimation ||
|
context.HasBoxAnimation ||
|
||||||
|
|
|
||||||
|
|
@ -496,9 +496,6 @@ public sealed class SpectreRenderer : IRenderer
|
||||||
if (context.HasCraftingPanel)
|
if (context.HasCraftingPanel)
|
||||||
rightItems.Add(CraftingPanel.Render(state, _registry, _loc));
|
rightItems.Add(CraftingPanel.Render(state, _registry, _loc));
|
||||||
|
|
||||||
if (context.HasChatPanel)
|
|
||||||
rightItems.Add(ChatPanel.Render(state.EventLog, _loc));
|
|
||||||
|
|
||||||
if (context.HasCompletionTracker)
|
if (context.HasCompletionTracker)
|
||||||
rightItems.Add(new Markup($"[bold cyan]{Markup.Escape(_loc.Get("ui.completion", context.CompletionPercent))}[/]"));
|
rightItems.Add(new Markup($"[bold cyan]{Markup.Escape(_loc.Get("ui.completion", context.CompletionPercent))}[/]"));
|
||||||
|
|
||||||
|
|
@ -547,9 +544,6 @@ public sealed class SpectreRenderer : IRenderer
|
||||||
if (context.HasCraftingPanel)
|
if (context.HasCraftingPanel)
|
||||||
AnsiConsole.Write(CraftingPanel.Render(state, _registry, _loc));
|
AnsiConsole.Write(CraftingPanel.Render(state, _registry, _loc));
|
||||||
|
|
||||||
if (context.HasChatPanel)
|
|
||||||
AnsiConsole.Write(ChatPanel.Render(state.EventLog, _loc));
|
|
||||||
|
|
||||||
if (context.HasCompletionTracker)
|
if (context.HasCompletionTracker)
|
||||||
AnsiConsole.Write(new Rule($"[bold cyan]{Markup.Escape(_loc.Get("ui.completion", context.CompletionPercent))}[/]").RuleStyle("cyan"));
|
AnsiConsole.Write(new Rule($"[bold cyan]{Markup.Escape(_loc.Get("ui.completion", context.CompletionPercent))}[/]").RuleStyle("cyan"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ public class MetaEngine
|
||||||
"meta_stats", // StatsPanel
|
"meta_stats", // StatsPanel
|
||||||
"meta_resources", // ResourcePanel
|
"meta_resources", // ResourcePanel
|
||||||
"meta_portrait", // PortraitPanel
|
"meta_portrait", // PortraitPanel
|
||||||
"meta_chat", // ChatPanel
|
|
||||||
"meta_extended_colors", // ExtendedColors
|
"meta_extended_colors", // ExtendedColors
|
||||||
"meta_animation", // BoxAnimation
|
"meta_animation", // BoxAnimation
|
||||||
"meta_crafting", // CraftingPanel
|
"meta_crafting", // CraftingPanel
|
||||||
|
|
|
||||||
|
|
@ -348,60 +348,6 @@ public class InventoryPanelTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ChatPanelTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Render_Empty_DoesNotThrow()
|
|
||||||
{
|
|
||||||
var result = RenderHelper.RenderToString(ChatPanel.Render([]));
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Render_SingleMessage_DoesNotThrow()
|
|
||||||
{
|
|
||||||
var messages = new List<string> { "The door creaks open." };
|
|
||||||
var result = RenderHelper.RenderToString(ChatPanel.Render(messages));
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Render_MultipleMessages_DoesNotThrow()
|
|
||||||
{
|
|
||||||
var messages = new List<string>
|
|
||||||
{
|
|
||||||
"+ Small Health Potion [Common]",
|
|
||||||
"★ Text Colors",
|
|
||||||
"Health: 0 → 10",
|
|
||||||
"🗺 Starbound"
|
|
||||||
};
|
|
||||||
var result = RenderHelper.RenderToString(ChatPanel.Render(messages));
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Render_OverflowMessages_ShowsOnlyRecent()
|
|
||||||
{
|
|
||||||
var messages = Enumerable.Range(0, 15)
|
|
||||||
.Select(i => $"Event {i}")
|
|
||||||
.ToList();
|
|
||||||
var result = RenderHelper.RenderToString(ChatPanel.Render(messages));
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Render_SpecialCharacters_DoesNotThrow()
|
|
||||||
{
|
|
||||||
var messages = new List<string>
|
|
||||||
{
|
|
||||||
"+ [Boss] item <rare>",
|
|
||||||
"A [mysterious] voice echoes."
|
|
||||||
};
|
|
||||||
var result = RenderHelper.RenderToString(ChatPanel.Render(messages));
|
|
||||||
Assert.NotEmpty(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── RenderContext + RendererFactory Tests ────────────────────────────────
|
// ── RenderContext + RendererFactory Tests ────────────────────────────────
|
||||||
|
|
||||||
public class RenderContextTests
|
public class RenderContextTests
|
||||||
|
|
@ -457,7 +403,6 @@ public class RenderContextTests
|
||||||
Assert.False(ctx.HasResourcePanel);
|
Assert.False(ctx.HasResourcePanel);
|
||||||
Assert.False(ctx.HasStatsPanel);
|
Assert.False(ctx.HasStatsPanel);
|
||||||
Assert.False(ctx.HasPortraitPanel);
|
Assert.False(ctx.HasPortraitPanel);
|
||||||
Assert.False(ctx.HasChatPanel);
|
|
||||||
Assert.False(ctx.HasFullLayout);
|
Assert.False(ctx.HasFullLayout);
|
||||||
Assert.False(ctx.HasKeyboardShortcuts);
|
Assert.False(ctx.HasKeyboardShortcuts);
|
||||||
Assert.False(ctx.HasBoxAnimation);
|
Assert.False(ctx.HasBoxAnimation);
|
||||||
|
|
@ -488,7 +433,6 @@ public class RendererFactoryTests
|
||||||
[InlineData(UIFeature.ResourcePanel)]
|
[InlineData(UIFeature.ResourcePanel)]
|
||||||
[InlineData(UIFeature.StatsPanel)]
|
[InlineData(UIFeature.StatsPanel)]
|
||||||
[InlineData(UIFeature.PortraitPanel)]
|
[InlineData(UIFeature.PortraitPanel)]
|
||||||
[InlineData(UIFeature.ChatPanel)]
|
|
||||||
[InlineData(UIFeature.FullLayout)]
|
[InlineData(UIFeature.FullLayout)]
|
||||||
[InlineData(UIFeature.KeyboardShortcuts)]
|
[InlineData(UIFeature.KeyboardShortcuts)]
|
||||||
[InlineData(UIFeature.BoxAnimation)]
|
[InlineData(UIFeature.BoxAnimation)]
|
||||||
|
|
@ -749,7 +693,6 @@ public class SpectreRendererOutputTests : IDisposable
|
||||||
ctx.Unlock(UIFeature.StatsPanel);
|
ctx.Unlock(UIFeature.StatsPanel);
|
||||||
ctx.Unlock(UIFeature.ResourcePanel);
|
ctx.Unlock(UIFeature.ResourcePanel);
|
||||||
ctx.Unlock(UIFeature.InventoryPanel);
|
ctx.Unlock(UIFeature.InventoryPanel);
|
||||||
ctx.Unlock(UIFeature.ChatPanel);
|
|
||||||
ctx.Unlock(UIFeature.CompletionTracker);
|
ctx.Unlock(UIFeature.CompletionTracker);
|
||||||
ctx.CompletionPercent = 42;
|
ctx.CompletionPercent = 42;
|
||||||
var r = new SpectreRenderer(ctx, _loc);
|
var r = new SpectreRenderer(ctx, _loc);
|
||||||
|
|
|
||||||
|
|
@ -420,7 +420,6 @@ public class ContentValidationTests
|
||||||
[UIFeature.ResourcePanel] = "meta.resources",
|
[UIFeature.ResourcePanel] = "meta.resources",
|
||||||
[UIFeature.StatsPanel] = "meta.stats",
|
[UIFeature.StatsPanel] = "meta.stats",
|
||||||
[UIFeature.PortraitPanel] = "meta.portrait",
|
[UIFeature.PortraitPanel] = "meta.portrait",
|
||||||
[UIFeature.ChatPanel] = "meta.chat",
|
|
||||||
[UIFeature.FullLayout] = "meta.layout",
|
[UIFeature.FullLayout] = "meta.layout",
|
||||||
[UIFeature.KeyboardShortcuts] = "meta.shortcuts",
|
[UIFeature.KeyboardShortcuts] = "meta.shortcuts",
|
||||||
[UIFeature.BoxAnimation] = "meta.animation",
|
[UIFeature.BoxAnimation] = "meta.animation",
|
||||||
|
|
@ -1889,6 +1888,29 @@ public class ContentValidationTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workstation summary: what each bench can craft
|
||||||
|
report.AppendLine("## Workstations");
|
||||||
|
report.AppendLine(new string('─', 80));
|
||||||
|
var recipesByStation = registry.Recipes.Values
|
||||||
|
.GroupBy(r => r.Workstation)
|
||||||
|
.OrderBy(g => g.Key.ToString());
|
||||||
|
foreach (var stationGroup in recipesByStation)
|
||||||
|
{
|
||||||
|
report.AppendLine($" 🔨 {stationGroup.Key}");
|
||||||
|
foreach (var recipe in stationGroup.OrderBy(r => r.Id))
|
||||||
|
{
|
||||||
|
var ingredientNames = recipe.Ingredients
|
||||||
|
.Select(i => registry.Items.TryGetValue(i.ItemDefinitionId, out var iDef)
|
||||||
|
? $"{loc.Get(iDef.NameKey)} x{i.Quantity}"
|
||||||
|
: $"{i.ItemDefinitionId} x{i.Quantity}");
|
||||||
|
var resultName = registry.Items.TryGetValue(recipe.Result.ItemDefinitionId, out var rDef)
|
||||||
|
? loc.Get(rDef.NameKey)
|
||||||
|
: recipe.Result.ItemDefinitionId;
|
||||||
|
report.AppendLine($" {recipe.Id}: {string.Join(" + ", ingredientNames)} → {resultName} x{recipe.Result.Quantity}");
|
||||||
|
}
|
||||||
|
report.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
// Orphan check: items with no usage at all
|
// Orphan check: items with no usage at all
|
||||||
report.AppendLine("## Orphan Items (no usage context)");
|
report.AppendLine("## Orphan Items (no usage context)");
|
||||||
report.AppendLine(new string('─', 80));
|
report.AppendLine(new string('─', 80));
|
||||||
|
|
@ -1903,9 +1925,9 @@ public class ContentValidationTests
|
||||||
|
|
||||||
string reportText = report.ToString();
|
string reportText = report.ToString();
|
||||||
|
|
||||||
// Write snapshot to repo root (tests/snapshots/) so it can be committed
|
// Write snapshot to tests/snapshots/ relative to the test project file
|
||||||
var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
|
var testProjectDir = Path.GetDirectoryName(GetThisFilePath())!;
|
||||||
var snapshotDir = Path.Combine(repoRoot, "tests", "snapshots");
|
var snapshotDir = Path.Combine(testProjectDir, "..", "snapshots");
|
||||||
Directory.CreateDirectory(snapshotDir);
|
Directory.CreateDirectory(snapshotDir);
|
||||||
var snapshotPath = Path.Combine(snapshotDir, "item_utility_report.txt");
|
var snapshotPath = Path.Combine(snapshotDir, "item_utility_report.txt");
|
||||||
|
|
||||||
|
|
@ -1950,4 +1972,6 @@ public class ContentValidationTests
|
||||||
if (item.Tags.Contains("Cookie")) count++;
|
if (item.Tags.Contains("Cookie")) count++;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetThisFilePath([System.Runtime.CompilerServices.CallerFilePath] string path = "") => path;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Item Utility Report
|
# Item Utility Report
|
||||||
# Total items: 146
|
# Total items: 145
|
||||||
# Total boxes: 31
|
# Total boxes: 31
|
||||||
# Total recipes: 18
|
# Total recipes: 18
|
||||||
|
|
||||||
|
|
@ -481,7 +481,7 @@
|
||||||
|
|
||||||
[NO USE] material_wood_nail (Common) — Bois
|
[NO USE] material_wood_nail (Common) — Bois
|
||||||
|
|
||||||
## Meta (34 items)
|
## Meta (33 items)
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
[**] meta_colors (Rare) — Couleurs de texte
|
[**] meta_colors (Rare) — Couleurs de texte
|
||||||
Loot: box_meta_basics
|
Loot: box_meta_basics
|
||||||
|
|
@ -511,10 +511,6 @@
|
||||||
Loot: box_meta_basics
|
Loot: box_meta_basics
|
||||||
Unlock: PortraitPanel
|
Unlock: PortraitPanel
|
||||||
|
|
||||||
[**] meta_chat (Epic) — Panneau de discussion
|
|
||||||
Loot: box_meta_deep
|
|
||||||
Unlock: ChatPanel
|
|
||||||
|
|
||||||
[**] meta_layout (Legendary) — Mise en page complète
|
[**] meta_layout (Legendary) — Mise en page complète
|
||||||
Loot: box_meta_interface
|
Loot: box_meta_interface
|
||||||
Unlock: FullLayout
|
Unlock: FullLayout
|
||||||
|
|
@ -610,6 +606,50 @@
|
||||||
[*] meta_stat_wisdom (Rare) — Sagesse
|
[*] meta_stat_wisdom (Rare) — Sagesse
|
||||||
Loot: box_meta_mastery
|
Loot: box_meta_mastery
|
||||||
|
|
||||||
|
## Workstations
|
||||||
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
|
🔨 DrawingTable
|
||||||
|
chart_star_navigation: Coordonnées mystérieuses x1 + Carte stellaire x1 → Clé d'accès au sas x1
|
||||||
|
|
||||||
|
🔨 EngineerDesk
|
||||||
|
engineer_rocket_boots: Short x1 + Titane x1 + Éclat de nébuleuse x2 → Bottes à réaction x1
|
||||||
|
|
||||||
|
🔨 EngravingBench
|
||||||
|
engrave_royal_seal: Blason de chevalier x1 + Parchemin ancien x1 → Sceau royal x1
|
||||||
|
|
||||||
|
🔨 Forge
|
||||||
|
craft_box_cool: Acier x2 + Néon x1 → box_cool x1
|
||||||
|
forge_armored_plate: Acier x3 + Fibre de carbone x2 → Armure x1
|
||||||
|
forge_carbonfiber_sheet: Fibre de carbone x4 → Fibre de carbone x1
|
||||||
|
|
||||||
|
🔨 Foundry
|
||||||
|
refine_wood: Bois x2 → Bois x1
|
||||||
|
|
||||||
|
🔨 Furnace
|
||||||
|
smelt_bronze_ingot: Bronze x3 → Bronze x1
|
||||||
|
smelt_iron_ingot: Fer x3 → Fer x1
|
||||||
|
smelt_steel_ingot: Acier x4 → Acier x1
|
||||||
|
smelt_titanium_ingot: Titane x5 → Titane x1
|
||||||
|
|
||||||
|
🔨 GeneticModStation
|
||||||
|
splice_glowing_dna: Échantillon de bactérie sentiente x1 + Prion amical (probablement) x1 → Brin d'ADN luminescent x1
|
||||||
|
|
||||||
|
🔨 MatterSynthesizer
|
||||||
|
fuse_cosmic_crystal: Éclat de nébuleuse x2 → Cristal de quasar x1
|
||||||
|
|
||||||
|
🔨 Printer3D
|
||||||
|
craft_box_epic: Titane x2 + Fibre de carbone x1 + Or x1 → box_epic x1
|
||||||
|
|
||||||
|
🔨 StasisChamber
|
||||||
|
preserve_amber: Dent de dinosaure x2 → Pierre d'ambre x1
|
||||||
|
|
||||||
|
🔨 TransformationPentacle
|
||||||
|
enchant_dark_grimoire: Anneau maudit x2 → Grimoire du nécromancien x1
|
||||||
|
|
||||||
|
🔨 Workbench
|
||||||
|
craft_box_ok_tier: Bois x2 + Bronze x1 → box_ok_tier x1
|
||||||
|
craft_pilot_glasses: Lunettes de soleil x1 + Bronze x1 + Argent x1 → Lunettes d'aviateur x1
|
||||||
|
|
||||||
## Orphan Items (no usage context)
|
## Orphan Items (no usage context)
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
material_wood_nail (Material, Common)
|
material_wood_nail (Material, Common)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue