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
|
||||
|
||||
Tous les items ont été traités. Ce fichier est conservé comme historique.
|
||||
|
||||
## 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.
|
||||
Tous les items ont été traités.
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ Examiner la boîte
|
|||
#opt-ignore // "Ignore it and continue"
|
||||
L'ignorer et continuer
|
||||
|
||||
#opt-scan // "Run a deep scan first"
|
||||
Lancer un scan approfondi d'abord
|
||||
#opt-scan // "Run a deep scan first|||Fuel reserves insufficient for deep scan"
|
||||
Lancer un scan approfondi d'abord|||Réserves de carburant insuffisantes pour un scan approfondi
|
||||
|
||||
#deepscan-init // "Deep scan initiated. Fuel reserves reduced."
|
||||
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"
|
||||
L'ouvrir immédiatement
|
||||
|
||||
#opt-scan-first // "Scan it first"
|
||||
La scanner d'abord
|
||||
#opt-scan-first // "Scan it first|||Not enough fuel for a scan"
|
||||
La scanner d'abord|||Pas assez de carburant pour un scan
|
||||
|
||||
#opt-poke // "Poke it with a stick"
|
||||
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?"
|
||||
L'extraterrestre ÉTAIT la boîte ?
|
||||
|
||||
#alien-intro // I'm Zx'thorp."
|
||||
#alien-intro // "I'm Zx'thorp."
|
||||
Je suis Zx'thorp.
|
||||
|
||||
#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"
|
||||
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"
|
||||
É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"
|
||||
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!"
|
||||
Vous osez ? Personne ne surpasse Zx'thorp à l'ouverture !
|
||||
|
||||
#alien-contest // "...fine. Best of three. You open first."
|
||||
...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."
|
||||
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"
|
||||
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."
|
||||
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.\""
|
||||
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"
|
||||
Ouvrir les trois en même temps
|
||||
#captain-nebula // "There's a whole nebula of boxes?"
|
||||
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."
|
||||
Les trois, ARIA. Vitesse maximale de boîte.
|
||||
|
||||
#ai-enthusiasm // "That's not a real measurement, but I admire your enthusiasm."
|
||||
Ce n'est pas une vraie mesure, mais j'admire votre enthousiasme.
|
||||
#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. 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."
|
||||
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."
|
||||
Évidemment.
|
||||
|
||||
#opt-happy // "Leave them orbiting, they seem happy"
|
||||
Les laisser en orbite, elles ont l'air heureuses
|
||||
#ai-proud // "A bigger, better box. It's vibrating at a frequency I've never seen. I think it's... proud of itself."
|
||||
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."
|
||||
Laissons-les orbiter en paix.
|
||||
|
|
@ -307,9 +436,18 @@ Laissons-les orbiter en paix.
|
|||
#ai-philosophical // "A surprisingly philosophical choice, Commander."
|
||||
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.
|
||||
|
||||
#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."
|
||||
Aventure terminée. Mise à jour du journal de bord.
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ beat Intro
|
|||
-> InvestigateBox
|
||||
Ignore it and continue #opt-ignore
|
||||
-> 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
|
||||
-> DeepScan
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ beat InvestigateBox
|
|||
choice
|
||||
Open it immediately #opt-open-now
|
||||
-> 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
|
||||
-> ScanThenOpen
|
||||
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
|
||||
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
|
||||
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
|
||||
discovered += 1
|
||||
-> OpenSpaceBox
|
||||
|
||||
beat PokeBox
|
||||
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
|
||||
ai: Commander, I don't think boxes in zero gravity have an \"up\". #ai-gravity
|
||||
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
|
||||
captain: Maybe the box defines its own reality. #captain-reality
|
||||
ai: That's... philosophically concerning. #ai-philosophy
|
||||
-> OpenSpaceBox
|
||||
|
||||
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
|
||||
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
|
||||
-> OpenSpaceBox
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ beat LeaveForGood
|
|||
ai: I hope you're happy, Commander. #ai-happy
|
||||
captain: I am. No weird space boxes for me today. #captain-happy
|
||||
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
|
||||
-> Ending
|
||||
|
||||
|
|
@ -158,19 +158,71 @@ beat AlienEncounter
|
|||
alien: I've been collecting boxes across the galaxy for centuries. #alien-collecting
|
||||
alien: Most species open them. Very few appreciate them. #alien-appreciate
|
||||
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
|
||||
Ask about the galaxy's rarest box #opt-rare-box
|
||||
alien: The Singularity Box. It contains everything and nothing. Also a coupon. #alien-singularity
|
||||
-> Ending
|
||||
-> RareBoxStory
|
||||
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
|
||||
captain: Deal. Wait-- #captain-deal
|
||||
-> Ending
|
||||
-> AlienTrade
|
||||
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: ...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
|
||||
|
||||
beat SpaceExploration
|
||||
|
|
@ -182,20 +234,52 @@ beat SpaceExploration
|
|||
|
||||
choice
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
ai: Commander, the items are forming... a bigger box. #ai-biggerbox
|
||||
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
|
||||
Leave them orbiting, they seem happy #opt-happy
|
||||
|
||||
beat HappyBoxes
|
||||
captain: Let them orbit in peace. #captain-orbit
|
||||
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
|
||||
|
||||
beat Ending
|
||||
|
|
|
|||
|
|
@ -238,7 +238,6 @@
|
|||
"rollCount": 1,
|
||||
"entries": [
|
||||
{"itemDefinitionId": "meta_completion", "weight": 3},
|
||||
{"itemDefinitionId": "meta_chat", "weight": 3},
|
||||
{"itemDefinitionId": "meta_crafting", "weight": 3},
|
||||
{"itemDefinitionId": "meta_extended_colors", "weight": 3},
|
||||
{"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_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_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_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"},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"action.appearance": "Change appearance",
|
||||
"action.save": "Save",
|
||||
"action.quit": "Return to menu",
|
||||
"action.new": "NEW",
|
||||
|
||||
"prompt.name": "What is your name, brave box-opener?",
|
||||
"prompt.choose_action": "What would you like to do?",
|
||||
|
|
@ -115,7 +116,6 @@
|
|||
"meta.resources": "Characteristics Panel",
|
||||
"meta.stats": "Stats Panel",
|
||||
"meta.portrait": "Portrait Panel",
|
||||
"meta.chat": "Chat Panel",
|
||||
"meta.layout": "Full Layout Mode",
|
||||
"meta.shortcuts": "Keyboard Shortcuts",
|
||||
"meta.animation": "Box Opening Animation",
|
||||
|
|
@ -300,8 +300,6 @@
|
|||
"lore.name_9": "Fragment: The Manual",
|
||||
"lore.name_10": "Fragment: Schrödinger",
|
||||
|
||||
"log.title": "Event Log",
|
||||
|
||||
"cookie.1": "A box within a box is still a box.",
|
||||
"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.",
|
||||
|
|
@ -431,6 +429,7 @@
|
|||
"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.unavailable": "Unavailable",
|
||||
"adventure.unlocked": "🎉 New adventure unlocked! Discover '{0}' in the adventure menu!",
|
||||
"adventure.name.Space": "Starbound",
|
||||
"adventure.name.Medieval": "Castle Cardboard",
|
||||
|
|
@ -442,6 +441,27 @@
|
|||
"adventure.name.Microscopic": "Cell Division",
|
||||
"adventure.name.DarkFantasy": "Ashen Wastes",
|
||||
"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",
|
||||
"stats.boxes_opened": "Boxes Opened",
|
||||
"stats.title": "Stats",
|
||||
|
|
@ -481,7 +501,6 @@
|
|||
"meta.resources.desc": "Displays your character's characteristics (health, mana, etc.).",
|
||||
"meta.stats.desc": "Shows your progression stats and character attributes.",
|
||||
"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.shortcuts.desc": "Enables keyboard shortcuts for quick actions.",
|
||||
"meta.animation.desc": "Adds opening animations when unboxing.",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"action.appearance": "Changer d'apparence",
|
||||
"action.save": "Sauvegarder",
|
||||
"action.quit": "Retourner au menu",
|
||||
"action.new": "NOUVEAU",
|
||||
|
||||
"prompt.name": "Quel est ton nom, brave ouvreur de boîtes ?",
|
||||
"prompt.choose_action": "Que veux-tu faire ?",
|
||||
|
|
@ -115,7 +116,6 @@
|
|||
"meta.resources": "Panneau de caractéristiques",
|
||||
"meta.stats": "Panneau de statistiques",
|
||||
"meta.portrait": "Panneau portrait",
|
||||
"meta.chat": "Panneau de discussion",
|
||||
"meta.layout": "Mise en page complète",
|
||||
"meta.shortcuts": "Raccourcis clavier",
|
||||
"meta.animation": "Animation d'ouverture de boîte",
|
||||
|
|
@ -300,8 +300,6 @@
|
|||
"lore.name_9": "Fragment : Le Manuel",
|
||||
"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.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%.",
|
||||
|
|
@ -431,6 +429,7 @@
|
|||
"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.unavailable": "Indisponible",
|
||||
"adventure.unlocked": "🎉 Nouvelle aventure débloquée ! Découvre '{0}' dans « Partir à l'aventure » !",
|
||||
"adventure.name.Space": "Odyssée stellaire",
|
||||
"adventure.name.Medieval": "Château Carton",
|
||||
|
|
@ -442,6 +441,28 @@
|
|||
"adventure.name.Microscopic": "Division cellulaire",
|
||||
"adventure.name.DarkFantasy": "Terres Cendrées",
|
||||
"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",
|
||||
"stats.boxes_opened": "Boîtes ouvertes",
|
||||
"stats.title": "Stats",
|
||||
|
|
@ -481,7 +502,6 @@
|
|||
"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.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.shortcuts.desc": "Active les raccourcis clavier pour des actions rapides.",
|
||||
"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)
|
||||
{
|
||||
_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();
|
||||
dialogue.Callback();
|
||||
}
|
||||
|
|
@ -211,7 +220,8 @@ public sealed class AdventureEngine
|
|||
}
|
||||
else
|
||||
{
|
||||
options.Add($"(unavailable) {text}");
|
||||
string prefix = hint ?? _loc.Get("adventure.unavailable");
|
||||
options.Add($"({prefix}) {text}");
|
||||
hints.Add(hint);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ public enum UIFeature
|
|||
/// <summary>Phase 6: ASCII art portrait reflecting equipped cosmetics.</summary>
|
||||
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>
|
||||
FullLayout,
|
||||
|
||||
|
|
|
|||
|
|
@ -41,12 +41,6 @@ public sealed class GameState
|
|||
public HashSet<TextColor> AvailableTextColors { 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>
|
||||
/// Returns the current value of a resource, or 0 if the resource is not tracked.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -391,7 +391,12 @@ public static class Program
|
|||
actions.Add((_loc.Get("action.inventory"), "inventory"));
|
||||
|
||||
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)
|
||||
actions.Add((_loc.Get("action.appearance"), "appearance"));
|
||||
|
|
@ -503,7 +508,6 @@ public static class Program
|
|||
RefreshRenderer();
|
||||
var featureLabel = _loc.Get(GetUIFeatureLocKey(uiEvt.Feature));
|
||||
_renderer.ShowUIFeatureUnlocked(featureLabel);
|
||||
AddEventLog($"* {featureLabel}");
|
||||
_renderer.WaitForKeyPress(_loc.Get("prompt.press_key"));
|
||||
break;
|
||||
|
||||
|
|
@ -524,7 +528,6 @@ public static class Program
|
|||
case ResourceChangedEvent resEvt:
|
||||
var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}");
|
||||
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
||||
AddEventLog($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
||||
break;
|
||||
|
||||
case MessageEvent msgEvt:
|
||||
|
|
@ -542,7 +545,6 @@ public static class Program
|
|||
case AdventureUnlockedEvent advUnlockedEvt:
|
||||
var advName = GetAdventureName(advUnlockedEvt.Theme);
|
||||
_renderer.ShowMessage(_loc.Get("adventure.unlocked", advName));
|
||||
AddEventLog($">> {advName}");
|
||||
break;
|
||||
|
||||
case AdventureStartedEvent advEvt:
|
||||
|
|
@ -582,10 +584,6 @@ public static class Program
|
|||
{
|
||||
_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
|
||||
}
|
||||
|
||||
|
|
@ -753,7 +751,6 @@ public static class Program
|
|||
: _loc.Get("inventory.item_used", itemName);
|
||||
_renderer.ShowMessage(usedMsg);
|
||||
_renderer.ShowMessage($"{resName}: {resEvt.OldValue} {UnicodeSupport.Arrow} {resEvt.NewValue}");
|
||||
AddEventLog($"{itemName} {UnicodeSupport.Arrow} {resName} {resEvt.OldValue}{UnicodeSupport.Arrow}{resEvt.NewValue}");
|
||||
break;
|
||||
case MessageEvent msgEvt:
|
||||
_renderer.ShowMessage(_loc.Get(msgEvt.MessageKey, msgEvt.Args ?? []));
|
||||
|
|
@ -1000,7 +997,6 @@ public static class Program
|
|||
UIFeature.ResourcePanel => "meta.resources",
|
||||
UIFeature.StatsPanel => "meta.stats",
|
||||
UIFeature.PortraitPanel => "meta.portrait",
|
||||
UIFeature.ChatPanel => "meta.chat",
|
||||
UIFeature.FullLayout => "meta.layout",
|
||||
UIFeature.KeyboardShortcuts => "meta.shortcuts",
|
||||
UIFeature.BoxAnimation => "meta.animation",
|
||||
|
|
@ -1018,18 +1014,6 @@ public static class Program
|
|||
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)
|
||||
{
|
||||
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 HasStatsPanel => Has(UIFeature.StatsPanel);
|
||||
public bool HasPortraitPanel => Has(UIFeature.PortraitPanel);
|
||||
public bool HasChatPanel => Has(UIFeature.ChatPanel);
|
||||
public bool HasFullLayout => Has(UIFeature.FullLayout);
|
||||
public bool HasKeyboardShortcuts => Has(UIFeature.KeyboardShortcuts);
|
||||
public bool HasBoxAnimation => Has(UIFeature.BoxAnimation);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ public static class RendererFactory
|
|||
context.HasResourcePanel ||
|
||||
context.HasStatsPanel ||
|
||||
context.HasPortraitPanel ||
|
||||
context.HasChatPanel ||
|
||||
context.HasFullLayout ||
|
||||
context.HasKeyboardShortcuts ||
|
||||
context.HasBoxAnimation ||
|
||||
|
|
|
|||
|
|
@ -496,9 +496,6 @@ public sealed class SpectreRenderer : IRenderer
|
|||
if (context.HasCraftingPanel)
|
||||
rightItems.Add(CraftingPanel.Render(state, _registry, _loc));
|
||||
|
||||
if (context.HasChatPanel)
|
||||
rightItems.Add(ChatPanel.Render(state.EventLog, _loc));
|
||||
|
||||
if (context.HasCompletionTracker)
|
||||
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)
|
||||
AnsiConsole.Write(CraftingPanel.Render(state, _registry, _loc));
|
||||
|
||||
if (context.HasChatPanel)
|
||||
AnsiConsole.Write(ChatPanel.Render(state.EventLog, _loc));
|
||||
|
||||
if (context.HasCompletionTracker)
|
||||
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_resources", // ResourcePanel
|
||||
"meta_portrait", // PortraitPanel
|
||||
"meta_chat", // ChatPanel
|
||||
"meta_extended_colors", // ExtendedColors
|
||||
"meta_animation", // BoxAnimation
|
||||
"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 ────────────────────────────────
|
||||
|
||||
public class RenderContextTests
|
||||
|
|
@ -457,7 +403,6 @@ public class RenderContextTests
|
|||
Assert.False(ctx.HasResourcePanel);
|
||||
Assert.False(ctx.HasStatsPanel);
|
||||
Assert.False(ctx.HasPortraitPanel);
|
||||
Assert.False(ctx.HasChatPanel);
|
||||
Assert.False(ctx.HasFullLayout);
|
||||
Assert.False(ctx.HasKeyboardShortcuts);
|
||||
Assert.False(ctx.HasBoxAnimation);
|
||||
|
|
@ -488,7 +433,6 @@ public class RendererFactoryTests
|
|||
[InlineData(UIFeature.ResourcePanel)]
|
||||
[InlineData(UIFeature.StatsPanel)]
|
||||
[InlineData(UIFeature.PortraitPanel)]
|
||||
[InlineData(UIFeature.ChatPanel)]
|
||||
[InlineData(UIFeature.FullLayout)]
|
||||
[InlineData(UIFeature.KeyboardShortcuts)]
|
||||
[InlineData(UIFeature.BoxAnimation)]
|
||||
|
|
@ -749,7 +693,6 @@ public class SpectreRendererOutputTests : IDisposable
|
|||
ctx.Unlock(UIFeature.StatsPanel);
|
||||
ctx.Unlock(UIFeature.ResourcePanel);
|
||||
ctx.Unlock(UIFeature.InventoryPanel);
|
||||
ctx.Unlock(UIFeature.ChatPanel);
|
||||
ctx.Unlock(UIFeature.CompletionTracker);
|
||||
ctx.CompletionPercent = 42;
|
||||
var r = new SpectreRenderer(ctx, _loc);
|
||||
|
|
|
|||
|
|
@ -420,7 +420,6 @@ public class ContentValidationTests
|
|||
[UIFeature.ResourcePanel] = "meta.resources",
|
||||
[UIFeature.StatsPanel] = "meta.stats",
|
||||
[UIFeature.PortraitPanel] = "meta.portrait",
|
||||
[UIFeature.ChatPanel] = "meta.chat",
|
||||
[UIFeature.FullLayout] = "meta.layout",
|
||||
[UIFeature.KeyboardShortcuts] = "meta.shortcuts",
|
||||
[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
|
||||
report.AppendLine("## Orphan Items (no usage context)");
|
||||
report.AppendLine(new string('─', 80));
|
||||
|
|
@ -1903,9 +1925,9 @@ public class ContentValidationTests
|
|||
|
||||
string reportText = report.ToString();
|
||||
|
||||
// Write snapshot to repo root (tests/snapshots/) so it can be committed
|
||||
var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
|
||||
var snapshotDir = Path.Combine(repoRoot, "tests", "snapshots");
|
||||
// Write snapshot to tests/snapshots/ relative to the test project file
|
||||
var testProjectDir = Path.GetDirectoryName(GetThisFilePath())!;
|
||||
var snapshotDir = Path.Combine(testProjectDir, "..", "snapshots");
|
||||
Directory.CreateDirectory(snapshotDir);
|
||||
var snapshotPath = Path.Combine(snapshotDir, "item_utility_report.txt");
|
||||
|
||||
|
|
@ -1950,4 +1972,6 @@ public class ContentValidationTests
|
|||
if (item.Tags.Contains("Cookie")) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
private static string GetThisFilePath([System.Runtime.CompilerServices.CallerFilePath] string path = "") => path;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Item Utility Report
|
||||
# Total items: 146
|
||||
# Total items: 145
|
||||
# Total boxes: 31
|
||||
# Total recipes: 18
|
||||
|
||||
|
|
@ -481,7 +481,7 @@
|
|||
|
||||
[NO USE] material_wood_nail (Common) — Bois
|
||||
|
||||
## Meta (34 items)
|
||||
## Meta (33 items)
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
[**] meta_colors (Rare) — Couleurs de texte
|
||||
Loot: box_meta_basics
|
||||
|
|
@ -511,10 +511,6 @@
|
|||
Loot: box_meta_basics
|
||||
Unlock: PortraitPanel
|
||||
|
||||
[**] meta_chat (Epic) — Panneau de discussion
|
||||
Loot: box_meta_deep
|
||||
Unlock: ChatPanel
|
||||
|
||||
[**] meta_layout (Legendary) — Mise en page complète
|
||||
Loot: box_meta_interface
|
||||
Unlock: FullLayout
|
||||
|
|
@ -610,6 +606,50 @@
|
|||
[*] meta_stat_wisdom (Rare) — Sagesse
|
||||
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)
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
material_wood_nail (Material, Common)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue