子ウィンドウ

概要

カードやボタンから開く子ウィンドウを決めるドキュメントです。ウィンドウの中に並ぶカードと枠の 見た目は CardView.md、カードの出し入れの操作は CardInteraction.md、画面のどこに置くかの前提(フィールドエリア・ ハンドレーン)は ScreenLayout.md が扱います。

オブジェクト・コンテナ・キャラクタのスロットを 1 つの部品(ObjectWindow)で扱い、1 つの窓に要るものは 対象ごとに 1 つのまとまりで受け取ります(1 節)。装備・怪我にも固有の 処理は無く(3 節)、カードを押したとき何を出すかもワールド側の宣言(visible_slots)から導きます (4 節)。探索とプロパティは、この子ウィンドウのタブです(5・6 節)——専用のウィンドウを持つのは 地図・ステータス詳細・レシピだけです(7〜9 節)。

ウィンドウに出るオブジェクトのカードは写しではなく、元の枠から借りてきた札そのものです (1.1 節)。同じものを映す札が画面に 2 枚出ることはありません。

実装は src/game/ui/ObjectWindow.ts・MapWindow.ts・StatusDetailWindow.ts・RecipeWindow.ts で、 実装済みです。タブの中身は 1 つの契約(ObjectWindowPane.ts)に載る面が丸ごと持ち、窓は面を 捨てて作るだけで中身を知りません。面は DescriptionPane.ts・SlotPane.ts・ExplorationPane.ts・ PropertiesPane.ts です。

1. 子ウィンドウの共通構造 (Object Window)

カード・スロットのボタン・日時を押すと開く子ウィンドウです。オブジェクト・コンテナ・キャラクタの スロット・キャラクタ自身(4 節)を1 つの部品(ObjectWindow)で扱います。

1 つの窓に要るものは、対象ごとに 1 つのまとまりで受け取ります(PlayScreenView.ObjectWindowView)。 札・説明・アクション・可視のスロット・プロパティ・探索のタブの中身が 1 組で、開く側がばらばらのメンバー から組み立てることはありません——組み立てが呼び出し側に在ると、窓に何かを足すたびに組み立ての手順も 増えます。

画面が覚えているものだけは、開く側が重ねます。 発見物の枠(直前の探索で何が出たか)と固定表示の印は ワールドではなく画面の記憶なので、まとまりの側は探索率と補足の 1 行までしか答えません (1.1 節・5.1 節)。

タブ 1 つに要るものも同じ形で受け取ります(PlayScreenView.SlotView)。見出し・記憶の鍵・枠の数・ 落とせるかどうか・敷く絵・製作の材料が 1 組で、レーンもタブもこの 1 つを見ます。並ぶ札だけは含みません ——画面に出る札は貸し出している 1 枚を引いた後のもので(1.1 節)、ワールドがそう持っている並びを そのまま描くことはないためです。

組み方はどれも同じ 4 段です。

段 中身
上 見出し。常にオブジェクトの名前
タブ 「説明」と、スロット 1 つにつき 1 つ(1.2 節)。スロットが無ければ行ごと空けない
中 選んでいるタブの中身
下 操作のボタン。アクションが無ければ「閉じる」だけ

オブジェクトの一面だけを見せる窓にはしません(1.2 節)。説明と中身の並びをタブで分けるので、 どちらを出すかという選択そのものがありません。

説明と中身を別の窓へ分けると、中身を持つ対象にアクションを出せません(コンテナのカードを押すと 中身しか見えない)。中身が 1 枠しかない対象——怪我の治療具——では、何を治療しているのかも分かりません。

開いている間、手持ちの上端(▲)はそのウィンドウのスロットを指します。 ウィンドウの中のカードの 下端(▼)は手持ちを指します。カードをやり取りする相手が画面に出ているなら、端を押す操作もその相手を 指すのが自然だからです。ウィンドウが受け取れないスロット(怪我)のときは、▲ は元どおりフィールドを 指します。

1.1 カードは写さず、元の枠から借りる

1 つのものを映す札は、画面に 1 枚しかありません。 ウィンドウが出すオブジェクトのカードは 見た目の写しではなく、元の枠から飛んできた札そのものです。開くと元の枠からウィンドウの位置へ 飛び、閉じると元の枠へ飛んで帰ります。速さはカードの移動アニメーション( CardInteraction.md 6 節)と同じで、運んでいる間その 1 枚はどこの枠にも 居ません(同 6.2 節。着くまでウィンドウ側にも出しません)。

借りるための仕組みはありません。 ウィンドウの枠も並びの 1 つとして差し替えに乗るので、開けば その札の居場所が元の枠からウィンドウの枠へ変わり、あとはレーン間の移動とまったく同じ規則 (CardInteraction.md 6 節)が運びます。閉じれば居場所が戻り、同じ規則が 帰らせます。どこに出ているかを答えるのは常に 1 箇所(ShownCards)で、運びの途中かどうかは 画面の側だけが知っています。

映しているものが現在地から見えなくなったとき、ウィンドウは閉じます。 映すものも、ボタンが効く 相手も無くなっているためです。「世界から消えたか」では足りません——道を渡って別の土地へ移れば、 その道は世界に在り続けるのに、移った先からは触れません。

覆いはどのウィンドウも領域の中だけに敷きます(4 節)。借りた札へ手持ちから物を重ねられる以上、 読み取り専用のウィンドウはもう無いためです。

借りた札はタブによらず借りたままで、説明のタブでだけ描かれます(1.2 節)。スロットのタブへ移っても 枠へ返しません——タブを行き来するたびに札が飛ぶほうが、どこにも描かれない一瞬より落ち着きません。 画面に 2 枚出ないという決まりは、0 枚でも破れていません。

1.2 タブは「説明」と可視のスロット。最初に開くタブは覚える

タブの並びは「説明」が先頭で、その後ろに可視のスロットが続きます。 どのスロットを見せるかと その並びは、ワールド側が名前を並べて宣言します(visible_slots、 GameElementDefinition.md 7.11 節)。説明と中身の並びを排他に すると、オブジェクトの一面しか見せられません——かごを開けば中身しか読めず、装備を開けば本人の説明が 読めない、という状態になります。

最初に開くタブは、プログラムの指定 > 型ごとの記憶 > 説明の順で決めます。

2. 装備と怪我

どちらも中身はスロットで、装備にも怪我にも固有の処理はありません(characters/ のスロット定義 だけです)。同じウィンドウを使い、画面は場所ごとの決まりを持ちません。

怪我を出し入れできないのは、画面の制限ではなくワールド側の宣言の帰結です。捻挫が bound_to_owner(GameElementDefinition.md 7.9 節)なので 取り出せず、取り出せない以上プレイヤーが怪我を手にすることもないので入れられません。画面はカードごとに 「そこへ移せるか」をエンジンへ訊き(WorldObject.rejectionForMoveTo)、答えが「移せない」なら 落とし先の枠を出しません。設置物を持ち歩けないのも同じ道筋です——枠の型が合わないのであって、 設置物のレーンが読み取り専用だからではありません。持ち歩ける設置物を足せば、画面を直さずに外せる ようになります。

3. 中身を見せるカード

カードを押したとき中身も出すかは、ワールド側が宣言します(visible_slots、 GameElementDefinition.md 7.11 節)。編み籠(containers.yaml)も 怪我の治療具も、そのスロットを名乗っているからタブが出ます。液体の容器は名乗らないので開かず、水を 単独で取り出せません。

重ねた物の行き先は別の決まりです(同 7.8 節)。筏は積荷のスロットを名乗らないので外から中は 見えませんが、カードへ重ねれば積めます。

UI 側では決められません。 タグの有無も、スロット名の一覧も、物の側が別の目的で名乗ったもの なので、「押したとき中身も出すか」を言い当てる手がかりになりません (DesignNotes.md)。

装備・怪我の子ウィンドウは、この宣言に依りません。 開くスロットをボタンが渡し、そのタブから 開きます(1.2 節のプログラムの指定)。キャラクタは複数のスロットを持ちますが、そのどれも 「キャラクタと言えばこれ」ではないためです。

装備と違うのは「場所がインスタンスごとに違う」点だけです。籠が 2 つあれば中身も 2 つあるため、場所は 型ではなくそのインスタンスで見分けます。それ以外——出し入れ・並べ替え・末尾の受け皿・手持ちの端が 指す先の切り替え——はすべて装備と同じ経路を通ります。

入れ物を自分自身の中へ入れることはできません(自分の中に入っている入れ物の中へも入れられません)。

4. アクションの行

最下段に実行できるアクションをボタンとして横に並べます。タブを切り替えても並びは変わりません ——アクションはオブジェクトのもので、今どの面を見ているかとは関係が無いためです。 カード 1 枚で完結する操作(ActionSystem.md 1 節のメニュー型の操作)の入口は ここだけで、カードを重ねる combination(ドラッグ)と対になります。

道のカードから「移動する」を実行すると現在地が変わります。このときはレーンの中身だけでなく現在地 カードもレーンの背景の絵も入れ替わるため、カードの差し替え(CardTable)ではなくフィールドエリアを 作り直します(CardInteraction.md 9 節)。

5. 探索のタブ (Exploration)

現在地の子ウィンドウのタブで、上から発見物の枠・探索率の横長プログレスバー(高さ 72u、中央に 百分率)を置きます。探索できる場所(explore アクションを持つ場所)だけが持ちます。

タブの見出しは、その型が explore を言い換えていればその語です(対応表の object_texts.<型>.interactions.explore、Localization.md)。海区の 見出しは「見張り」で、言い換えていない型は画面の既定語(ui_texts.exploration =「探索」)です。 最下段のボタンと同じ語から引くので、同じ操作を、窓の中で 2 通りに呼びません。

「探索する」はタブの中に置きません。 探索は現在地が宣言しているアクションなので、最下段の操作の行に 他のアクションと並びます(4 節)——宣言から来たボタンと画面の都合で足したボタンを分けないためです。 所要時間の吹き出しも、実行できない理由の口も、そこにいれば無料で付いてきます。

押した結果はこのタブに出るので、探索するとこのタブへ自動で移ります。 説明のタブを見ながら押しても、 見つかったものがそのまま目に入ります。探索は必ず 1 個以上見つける( ExplorationSystem.md 2 節)ので、「見つかったときだけ移る」という 条件は要りません。

窓の横幅は発見物の 4 枠ぶん(カード 4 枚+ギャップ+左右パディング)を下回りません。枠は縮めず、 入りきらない画面では横スクロールで送ります。

バーの下に説明の行は置きません。 窓が高くなるとその下のハンドレーンが隠れます。探索で何が 見つかるかは型で違いますが——土地なら道とアイテム、海区なら航路 (Voyage.md 3 節)——それは explore の説明文(対応表の object_texts.<型>.interactions.explore.description、Localization.md)が 言うので、タブで言い直しません。

土地でも海区でも、100% の後もボタンは押せます。 100% で変わるのは「隠された道(海区なら航路)が もう見つからない」ことだけで、拾えるものは出続けます。

5.1 発見物の枠

直前の探索で見つかったもの(spawn されたアイテムと、発見された道の両方)を、カードとして並べます。 枠は常に 4 個表示し、見つかっていない分は破線の空枠のままにします。

「見つかった」は、レーンの外からレーンへ来たことです。 世界に生まれたもの(spawn)も、未発見の 枠から公開された道(ExplorationSystem.md 1.2 節)も、この 1 つの 規則で入ります。探索の前後でレーンの並びを見比べるのではありません——海区の見張りが拾うものは 手元へ直に入る(Voyage.md 3 節)ので、設置物レーンにもアイテムレーンにも 現れず、並びの差では航路しか数えられません。

ここに並ぶのは写しではなく、見つかったもの自身の札です(1.1 節と同じ扱い)。見つかったものは まずこの枠へ入り、ウィンドウを閉じるか次の探索を始めた時点で本来の場所——それが今いるレーン (アイテム・設置物・手持ち)——へ飛んでいきます。そのあいだレーンには並びません——同じものを映す札が 画面に 2 枚出ないためです。

カードはレーンと同じ寸法(205×320u)で描き、縮めません——レーンから来てレーンへ帰る札 そのものなので、大きさが変わると別の札に見えます。4 枠ぶんの幅が無い画面と、4 件を超えた分は 横スクロールで送ります。枠からはみ出したカードはレーンと違って背景板では隠せないため、マスクで 切り抜きます。送り具合はスクロールバー(ScreenLayout.md 7.4 節)で示します。

5.2 探索にかかる実時間

「探索する」を押すと、そのアクションの duration が進めるゲーム内時間の分だけ実時間をかけて 進みます(ゲーム内 15 分=現実 0.5 秒。探索は 1 tick(15 分)なので 0.5 秒)。

ワールド自体は押した瞬間に変わっていて、実時間をかけるのは見せ方だけです。連続で探索したときに 「前回の結果が残ったまま次の結果に入れ替わる」ことがないよう、いったん空へ戻してから埋めます。

6. プロパティのタブ (Properties)

プロパティをカテゴリごとにバーで並べるタブです。ステータスエリアには一部しか出さないため (StatusArea.md 1 節)、残りを含めて見るための面です。

7. 地図ウィンドウ (Map Window)

既知の土地のカードを、ユーザ自身がドラッグで並べて自分だけの地図を作る全画面の子ウィンドウです。 地図ボタンから開きます。実装は src/game/ui/MapWindow.ts。

8. ステータス詳細ウィンドウ (Status Detail Window)

ステータス 1 つについて、それが何を表し、今どの段にいて、他のステータスと何をやり取りしているかを 見せる子ウィンドウです。実装は src/game/ui/StatusDetailWindow.ts。

行はどこをタップしても開きます(ステータスエリアでもプロパティウィンドウでも同じ、 StatusArea.md 3 節)。

中身は上から次のとおりです。

段 中身
見出し 絵と表示名。右端に固定表示のボタン
意味 対応表の description。まだ書かれていないプロパティには代わりの 1 行を薄い文字で出す
バー ステータスエリアと同じ部品(ProgressBar)を横いっぱいに伸ばしたもの。段の目盛りと、今いる段の囲み・名札を重ねる(8.1 節)
影響の一覧 「与えている影響」と「受けている影響」。1 件を札 1 枚で並べる
閉じる 中身を出し入れしない読み取り専用のウィンドウなので、覆いは画面全体(プロパティウィンドウと同じ)

高さは横型(1920×1080u)に収まることを条件に決めます。 縦型は余裕がありますが、横型は画面の 高さがそのまま上限になるので、寸法を足すときはこちらで見ます。

数値は出しません。 目盛りに値を添えず、値そのものも書きません。ここで決めるのは 「何をすればどれが動くか」で、いくつ動くかを突き合わせる画面ではないためです。

固定表示(StatusArea.md 3 節)の付け外しは、見出しの行の右端のボタンです。 行のタップは詳細を開くことだけに使うので、付け外しの入口は画面にこの 1 つだけになります。付いている かどうかはボタンの塗りで示し(付いていれば反転。選ばれているボタンと同じ流儀)、付いていない間は 📌 を薄くします——絵文字はグレースケールにできないので、濃さで代えています(最終的に画像へ 差し替えます)。値を持つのはこのウィンドウではないので、押した結果は固定表示の持ち主から返ってきます。

8.1 今いる段は、バーに刻んだ目盛りと名札で示す

バーには段の境目をすべて刻み、今いる段だけを囲みと明るい面で示して、その上に名札を置きます。 名札は下向きのしっぽで今いる段を指します。

区間はプロパティの宣言(stages、GameElementDefinition.md 6.4 節) から導きます。下端はその段が値の並びの上で始まる位置で、下端を名乗らない受け皿はそれより下の全部を拾う ので range の下限へ丸めます。上端は、値の並びの上でその段のすぐ上に来る段の下端です(無ければ range の上限。受け皿の上に来るのは、下端を名乗った段のうち最も低いもの)。完全一致(eq)で決まる段 (シンボル型、同 6.4 節)は値の並びの上に幅を持たないので、区間そのものがありません。

range を持たないプロパティ(腕前、StatusArea.md 9 節)のバーは、今いる段の 中での進みだけを映します。 目盛りも囲みも range の中での位置なので、段 1 つぶんしか映さないこの バーには引けません。代わりに名札はバーの左端を指します——そこがその段の下端そのものだからです。 右端には次の段の名前を重ねて、満ちる先がどこかを示します(行と同じ、StatusArea.md 9 節)。

このバーも、進みを言えない段では出しません(名札だけを出します)。次の段が無い最上段、下端を 名乗らない受け皿(GameElementDefinition.md 6.4 節)、値の並びの 上に位置を持たないシンボル型がこれにあたります。0 のバーを出すと値が動いても止まって見えるためです (StatusArea.md 9 節)。指す先が無いので、名札のしっぽも出ません。段も割合も持たないプロパティでは、 バーの場所ごと空けます——値そのものを出さない画面なので、代わりに数字を置くこともしません。

8.2 影響の一覧

相手の絵に、影響方法の記号を添えた枠を並べます。形が効き方、向きが増減です。

記号 意味
▲ ▼ modify(可逆。条件を満たす間だけ実効値を押し上げる/押し下げる、GameElementDefinition.md 8.3 節)
+ − add・transfer(不可逆。tick 毎に積み上がる/削られる、同 8.4 節)

色は良し悪しで、増減の記号と同じ規則(StatusArea.md 6 節)です。良し悪しを 見るのは動く側のステータスなので、与えている影響なら相手にとって、受けている影響なら自分にとっての 良し悪しになります。荷重のように増えると悪いステータスでは、+が赤になります。

枠はすべて同じ寸法で、格子に並べます。 名前を出さず絵だけにするのは、ステータスエリアの行と 同じ理由です(StatusArea.md 3 節)——並ぶのは毎回同じ顔ぶれなので、読み直すのでは なく位置と絵で見分けます。絵を持たない相手にだけ、代わりに名前を小さく出します。記号の欄は記号が 無い間も空けておきます(増減の記号と同じ、StatusArea.md 5 節)。

同じに描かれる枠は 1 つに畳み、件数を右上の丸数字で出します。 中身の子も同じ怪我も、いくつあっても 同じ絵と同じ記号になるので、並べたところで数えるしかありません。丸数字はカードのスタック数 (CardView.md 6 節)と同じ姿・同じ規則で、2 件以上のときだけ出します。

畳むのは、その枠が同じに描かれるときだけです。相手が同じ型でも段によって絵が変わることがあり (art_by_stage)、条件が成立している枠と薄い枠も別々に並びます——まとめると、効いていないものまで 効いているように読めます。

一覧の高さは、件数が少なくても 2 行ぶん空けます。 影響の枠から相手の詳細へ渡り歩けるので、 渡るたびにバーや「閉じる」の位置が動くと、同じ画面を見ているつもりのプレイヤーが毎回目で追い直す ことになります。

枠をタップすると、その相手のステータスの詳細に入れ替わります。 相手がオブジェクト(怪我・治療具)の 枠は詳細を持たないので、押しても何も起きません。

開ける相手は、どこかに並んでいるプロパティに限りません。 ステータスエリアもプロパティのタブも タグで絞った一部を並べる面ですが(1 節・6 節)、影響の枠が指す先はキャラクタのプロパティなら何でも ——居心地のように、タグを 1 つも持たずどこにも並ばないものも含みます (Characters.md ホームシック節)。タグが決めるのはどこへ並べるかだけで、 そこに並んでいないことは「読めない」を意味しません。 押せる枠として描いておきながら開かないと、 プレイヤーには効かない指として残ります。

条件が成立していない影響は薄くし、記号を出しません。 影響のほとんどは段や条件で縛られていて (「痛みが耐えがたい段にある間だけ意識を押し下げる」)、今その値を動かしていないものに増減の向きを 出すと、動いていないのに動いているように読めます。薄い札は「今は効いていないが、条件が揃えばここへ 効く」を表します。

8.3 相手はステータスとは限らない

影響元・影響先がオブジェクトの場合は、そのオブジェクトの絵と名前を出します。 痛みを押し上げるのは 怪我、押し下げるのは治療具で、どちらもステータスではありません。ステータスに限ると痛みの一覧は常に 空になります。

自分が自分を動かす影響も出します(満腹度が tick 毎に減る、体脂肪の段ごとの基礎代謝、 持ち物の重さが荷重を押し上げる)。これを外すと 水分・満腹度・覚醒度の一覧はどれも空になり、「放っておいても減る」という最も基本的な事実だけが 画面から抜け落ちます。自分自身の影響は「受けている影響」にだけ出します——同じ 1 本を両側へ書くと、 自分の段が自分を削っていることが 2 回並ぶためです。

相手も記号も同じになる影響は 1 件にまとめます。 段ごとに宣言された基礎代謝は段の数だけ宣言を持ちますが、 プレイヤーにとっては「体脂肪が自分を削っている」の 1 件で、今効いているのはそのうち 1 つだけです。 1 つでも効いていれば、まとまりとして効いているものとして出します。

これは同じ相手の宣言が複数あるときの話で、別々の相手が同じに描かれるとき(8.2 節の畳み込み)とは 別です。あちらは相手の数を丸数字で出しますが、こちらは相手が 1 つなので数字は付きません。

8.4 影響は宣言から導く

一覧は、このステータスの持ち主自身・その祖先・その子孫が宣言している持続効果 (GameElementDefinition.md 8 節)と、今その持ち主と同じ関係に 加わっている相手(同 11.5 節。関係が入れ子なら、外側の関係の相手も含みます)が宣言している持続効果、 そして今経過している操作が宣言している持続効果(同 11.7 節)から導きます (WorldObject.readInfluences)。 対象に何を書けるかは宣言が置かれた場所が決めます(一覧は同 14.1 節の表)。

集める範囲が木だけで済まないのは、宣言元が相手へ届く経路が木だけではないからです。 木を辿る対象 (self・parent・child・ancestor)なら、自分へ届く効果も自分が届かせる効果も、宣言元は必ず 自分・祖先・子孫のどれかに居ます。操作の関係の役(agent・instrument・patient)を対象に書いた 効果は木を辿りません——道が「今歩いている人の速さを下げる」を modify: {agent: ...} で持つ形では、 宣言元(道)は歩いている人から見て木の上に居ません——が、役を指せるのは参加者からだけなので、 宣言元も相手も同じ 1 つの操作に加わっています。横に並んだ物どうしは、木でも操作でも結ばれていない 限り互いに届きません。

操作が宣言した持続効果は、物の型をいくら辿っても見つかりません。 宣言しているのは物ではなく操作で、 効いている間その宣言を持っているのは実行中のセッションだけなので、そこから引きます (WorldSession.collectRunningInteractionInfluences)。休んでいる最中に体力の枠へ「戻している」が 並ぶのはこの経路で、抜けていると、値が増えていく理由だけが画面のどこにも無いことになります。

役を対象にした影響と、操作が宣言した影響が並ぶのは、その操作が続いている間だけです。関係は始まって 終わる(同 11.5 節)ので、道の枠は歩き始めてから歩き終えるまでの間だけ現れます。それでよいのは、 効果自体が同じ間しか効かないからです——歩いていない人の速さを道が下げることはないので、一覧に無いことが そのまま事実です。

9. レシピウィンドウ (Recipe Window)

レシピボタンから開くモーダルで、何を作るかを完成品のカードで選ばせます。上から見出し・棚(棚の 見出しと、その下に折り返して並ぶカード)・「閉じる」ボタンを置きます。

横幅は完成品のカード 4 枚ぶん(カード 4 枚+ギャップ+左右パディング)です。入りきらない画面では その範囲まで絞り、1 列に並ぶ枚数のほうを減らします。高さは中身をそのまま積んだぶんで、画面に収まらない 分だけ縦にスクロールして送ります。

解放条件を満たしていないレシピも並べます(SkillSystem.md 4 節)。 作れないものが見えないと、必要なレシピに辿り着けないまま詰みます。押せるのは解放済みのものだけで、 なぜ作れないかは 9.3 節の形で伝えます。

9.1 棚はタブではなく見出し

カテゴリ(棚)はタブに分けず、1 ページに見出しとして積みます。 タブは横幅を等分するので、棚が 増えるほど 1 つあたりが痩せて文字が収まらなくなります——棚の数は世界の側のデータで決まるため、 画面の都合では抑えられません。縦に積めばページが伸びるだけで、送る仕組みは既にあります。

空の棚は出しません。 見出しだけの行は「ここには何も無い」を伝えず、場所を取るだけです。

9.2 棚は recipe_categories が決め、1 つの完成品は 1 つの棚にだけ載る

どのタグを棚にするか、どの順に並べるかは recipe_categories が持ちます(世界データのルートキー、 GameElementDefinition.md 3.1 節)。値は object_def のタグ (同 4.1 節)の名前の列で、棚の見出しはそのタグの文言(tag_texts、 Localization.md)です。分類のために別の語彙を持ち込まず、既にあるタグを 並べ替えて指すだけにします。

recipe_categories: [tool, container, fixture, item]

完成品が載る棚は 1 つだけです。 物は用途のぶんだけタグを持つので(石斧は item・tool・ cutting_tool・chopping_tool・weapon)、持っているタグ全部の棚に載せると同じ札が何度も並びます。 この列で最初に一致したタグの棚を採るため、並びは粗い分類ほど後ろに置きます。

どの棚にも当たらない完成品は「その他」へ落とします。 落ちること自体はエラーにしません—— アセットパックが足した完成品が画面から消えるより、末尾に出るほうが安全だからです。同梱データに その他へ落ちる完成品が無いことは、自動テスト(tests/world-codex/purposeTags.test.ts)が見張ります。

9.3 未解放の理由は、札には鍵の印だけを出し、押している間に吹き出しで言う【確定】

出どころ: #656(未解放レシピの理由は、札に添えず、押している間の吹き出しで出す)

理由を札の名前へ書き込みません。 札に出すのは鍵の印(CardView.md 9 節のカードの 印)だけです。それが押せない札だと一目で分かればよく、なぜ押せないかは押したときに読めれば足ります。

なぜ作れないかは、押している間だけ吹き出しで出します。 押せないボタン(本書 4 節)や、重ねても 成立しない組み合わせ(CardInteraction.md 2.1 節)と同じ吹き出しで、プレイヤーから 見て同じ「できない」を 3 通りの言い方で持たないためです。押せない札を待たせる理由は無いので、 長押しを待たずに押した瞬間から出します。

9.4 名前の板の予算と、吹き出しに出す文言の引き先

9.3 節から導いたもので、覆すのに人間の判断は要りません。

名前の板に収まるのは全角 10 文字までで(CardView.md 1 節)、完成品の名前だけでその 予算を使い切ります。理由を足せば名前の側がはみ出して読めなくなるため、板に載せられる長さの理由は 原理的にありません。

文言は解放条件が宣言している reason(GameElementDefinition.md 14.6 節)で、宣言が無いときだけ決まり文句(ui_texts.recipe_locked)で断ります。吹き出しは幅を 持つので、理由は他の reason と同じ文の形で書けます。