子ウィンドウ
概要
カードやボタンから開く子ウィンドウを決めるドキュメントです。ウィンドウの中に並ぶカードと枠の
見た目は 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 節)。説明と中身の並びをタブで分けるので、 どちらを出すかという選択そのものがありません。
- オブジェクト自身のカードは、説明のタブにだけ出します。 スロットのタブは中段を並びで使い切ります。 容量で出し分けません——4 枠を超えて入るスロットでだけカードが消えることになり、同じ窓の中で カードが在ったり無かったりする理由が、映しているものではなく枠の数になります。
説明と中身を別の窓へ分けると、中身を持つ対象にアクションを出せません(コンテナのカードを押すと 中身しか見えない)。中身が 1 枠しかない対象——怪我の治療具——では、何を治療しているのかも分かりません。
- 中身の並びはレーン(
CardLane)そのものなので、横スクロール・ドラッグ&ドロップ・並べ替えは レーン間とまったく同じ仕組みで動きます。 - 空けておく枠の数は、そのスロットの枠の数(
cell_count、 SlotSystem.md 3 節)で決めます。 3 枠なら 3 枠、1 枠なら 1 枠 (治療具。4 枠空けると「4 つ当てられる」と誤って伝わります)。中身のかさの合計の上限(capacity) とは別物です。4 枠で頭打ちにするのは、それ以上並べると画面からはみ出すためで、入り切らない分は 横スクロールで送ります。枠の数まで縮めたレーンは中央へ寄せます。 - 枠の数が決まっているスロットは、埋まるまで常にその数だけ枠を見せます。 1 枠の治療具に 2 枠目が 出ると「もう 1 つ当てられる」と誤って伝わります。枠の数が決まっていないスロットは、末尾に 1 枠だけ 添えて、落とせる場所であることを示します。
- 頭打ちに掛かるときは、次の枠の頭を少しだけ覗かせます。 ちょうど 4 枠ぶんで切ると、そこで 終わっているのか右へ送れるのかが分かりません。覗かせる幅はカードの間隔より広く取り、隙間ではなく カードの縁だと分かるようにします。
- 寸法はタブによらず固定で、最も広いタブ・最も高いタブに合わせます。切り替えのたびに枠が伸び縮み すると、どのタブが開いているかより枠の動きのほうが目に付きます。各タブの中身は中段の中央へ寄せます。
- 横幅は、スロットのタブがあるなら等倍のカードが並ぶ幅(枠の数、領域の幅が上限)です。カードを
縮めないのは、レーンの中のカードが縮まないため——片方だけ縮めると大きさが揃いません(1.1 節)。
- 説明のタブの幅より狭くはしません。 幅は最下段の操作のボタンの幅でもあるので、枠が 1 つしか無いスロット(治療具)に合わせて詰めると、映しているものとは関係のない都合でボタンが 窮屈になります。レーン自体は枠の数まで縮めて中央へ寄せるので、「1 つしか入らない」ことは 変わらず枠が言います。
- カードを受け入れるスロットでは、並びの末尾に受け皿の空枠を出します
(
CardView.md12 節)。 - 位置はハンドレーンを覆わない領域(フィールドエリアからハンドレーンを除いた範囲)の中央です。 開いている間も手持ちとカードをやり取りするため、手持ちは見えていて操作できる必要があります。 同じ理由で、背景を暗くする覆いもこの領域の中だけに敷きます(画面全体を覆うと手持ちが押せなくなります)。
- 開いている間、覆われる設置物・アイテムのレーンは落とし先から外します。 隠れているレーンに 落とし先の枠やふちの光が出ると、ウィンドウ越しに落とせるように見えます。落とし先になるのは、 ウィンドウの中の並びと手持ちだけです。
- 同時に開けるのは 1 つだけです。別のスロットを開くと入れ替わります(手持ちの端が指す先が 1 つに 定まらなくなるため)。
開いている間、手持ちの上端(▲)はそのウィンドウのスロットを指します。 ウィンドウの中のカードの 下端(▼)は手持ちを指します。カードをやり取りする相手が画面に出ているなら、端を押す操作もその相手を 指すのが自然だからです。ウィンドウが受け取れないスロット(怪我)のときは、▲ は元どおりフィールドを 指します。
1.1 カードは写さず、元の枠から借りる
1 つのものを映す札は、画面に 1 枚しかありません。 ウィンドウが出すオブジェクトのカードは
見た目の写しではなく、元の枠から飛んできた札そのものです。開くと元の枠からウィンドウの位置へ
飛び、閉じると元の枠へ飛んで帰ります。速さはカードの移動アニメーション(
CardInteraction.md 6 節)と同じで、運んでいる間その 1 枚はどこの枠にも
居ません(同 6.2 節。着くまでウィンドウ側にも出しません)。
借りるための仕組みはありません。 ウィンドウの枠も並びの 1 つとして差し替えに乗るので、開けば
その札の居場所が元の枠からウィンドウの枠へ変わり、あとはレーン間の移動とまったく同じ規則
(CardInteraction.md 6 節)が運びます。閉じれば居場所が戻り、同じ規則が
帰らせます。どこに出ているかを答えるのは常に 1 箇所(ShownCards)で、運びの途中かどうかは
画面の側だけが知っています。
- 借りた枠には、帰ってくる場所を示す薄い印が残ります(同 6.2 節)。掴んで運び出した枠と同じ 見え方で、そこに在るのは札ではないので押しても掴めません。
- 束から借りるのは先頭の 1 枚だけです。 ボタンの操作が効くのもその 1 個なので、束を持ち出すと 「操作が効かない残り」まで画面から消えることになります。石を 2 個持って開けば、1 個はウィンドウに、 1 個は手持ちに残ります。残った札は掴めるので、そのままウィンドウの石へ重ねて打ち割れます。
- 借りた札の枠も落とし先です。 ウィンドウの中のカードへ手持ちから重ねればドラッグ型の操作が
実行され(成立しなければ理由を言うだけで何も起こしません。
CardInteraction.md2.1 節)、中身を持つカード(かご・製作中オブジェクト) ならその中へ入ります。枠 1 つのレーン (CardLane)なので、レーンとまったく同じ仕組みで動きます。ただし枠そのものへ「移す」ことは できません——そこはワールドの場所ではなく、その 1 枚が今そこに在るというだけだからです。 - その一点だけが、場所と置き場を分けます。 ワールドのスロットを映す枠(設置物・アイテム・手持ち・ ポートレイト・ウィンドウの中身)は移動の行き先になり、借りた札の枠と探索の発見物の枠 (5.1 節)は「その札が今そこに在る」だけの置き場です。仕組みは分けません——どちらも同じ 差し替えに乗り、同じ規則で飛びます。
- カードは縮めません。 レーンから来た札そのものなので、大きさが変わると別の札に見えます。 狭い画面で削るのは説明文の側です。
- 帰り先は、開いたときの枠ではなく閉じる時点の枠です。 開いている間に別のレーンへ移ることも あります。世界から出ていれば帰り先は無く、その場で消えます(重ねて打ち割った石は帰りません)。
映しているものが現在地から見えなくなったとき、ウィンドウは閉じます。 映すものも、ボタンが効く 相手も無くなっているためです。「世界から消えたか」では足りません——道を渡って別の土地へ移れば、 その道は世界に在り続けるのに、移った先からは触れません。
- 見えるかどうかは、現在地とその外側の場所(
ScreenLayout.md7.1.1 節)の 中に在るかで決まります。 筏の中に居ても外側の海区に在るものは見えているので、「現在地の子か」 では判定になりません。設置物レーンがどちらを映しているかにも依りません——映す先を切り替える たびにウィンドウが閉じては読めません。 - 札を借りない窓(場所そのもの・キャラクタ・装備・怪我)も同じ基準で閉じます。 窓が映している 1 個が何であるかは、借りた札の有無とは別に分かるためです。
- ポートレイトも同じです。 キャラクタ自身の札も枠 1 つのレーンに置いてあるので、日時から開く キャラクタのウィンドウ(4 節)へは、ポートレイトがそのまま飛んでいきます。
- 画面を作り直して同じウィンドウを開き直すとき(リサイズ)は飛ばしません。既にそこに在ったものが 動いて見えてはいけないためです。
覆いはどのウィンドウも領域の中だけに敷きます(4 節)。借りた札へ手持ちから物を重ねられる以上、 読み取り専用のウィンドウはもう無いためです。
借りた札はタブによらず借りたままで、説明のタブでだけ描かれます(1.2 節)。スロットのタブへ移っても 枠へ返しません——タブを行き来するたびに札が飛ぶほうが、どこにも描かれない一瞬より落ち着きません。 画面に 2 枚出ないという決まりは、0 枚でも破れていません。
1.2 タブは「説明」と可視のスロット。最初に開くタブは覚える
タブの並びは「説明」が先頭で、その後ろに可視のスロットが続きます。 どのスロットを見せるかと
その並びは、ワールド側が名前を並べて宣言します(visible_slots、
GameElementDefinition.md 7.11 節)。説明と中身の並びを排他に
すると、オブジェクトの一面しか見せられません——かごを開けば中身しか読めず、装備を開けば本人の説明が
読めない、という状態になります。
- タブの数はその型が宣言したスロットの数で決まります。 世界のデータが際限なく増やせる並び (レシピ一覧の棚、9.1 節)ではないので、タブで分けて構いません。
- タブのラベルはスロットの名前で、持ち主は込めません(「マルコの装備」ではなく「装備」)。持ち主の
名前は上段の見出しに既に出ているので(1 節)、タブにまで繰り返す場所がありません。名前は対応表から
引くので(
slot_texts、Localization.md)、画面側は「装備」「怪我」と いった文字列を持ちません。 - 画面に定位置を持つスロットは名乗りません。 手持ちはレーンに出ているので、タブにも出すと同じ札が 画面に 2 枚出ます(1.1 節)。筏の積荷も、乗り込めばレーンに出るので名乗りません。
- 空のスロットのタブも出します。 出さないと「見えないのか、空なのか」が区別できません
(容量のバーを空でも 0 で出すのと同じ理由、
CardView.md8 節)。
最初に開くタブは、プログラムの指定 > 型ごとの記憶 > 説明の順で決めます。
- YAML は初期タブを持ちません。 どのタブを見たいかは著者ではなくプレイヤーが決めることで、 同じ型でも見たいものはその人の遊び方によります。動物の怪我のスロットを「なんとなく」開いていた のは、著者が決めようのないものを著者に決めさせていたからです。
- 記憶は型ごとです。個体ごとに覚えても、拾い直した物も新しく編んだかごも別の個体なので次に 効きません。かごを一度開いて中身を見れば、以後どのかごも中身のタブから開きます。
- 一度も開いていない型は説明から始まります。かごの用がほぼ中身だけだとしても、初めて手にした 物がまず何であるかを名乗るのは導線として正しく、代わりに払うのは 1 回のタップだけです。
- プログラムの指定は、開いた文脈がそのスロットを見に来たと分かっている場合だけです。装備・怪我の
ボタン(2 節)と、レシピ一覧から作り始めた直後の製作中オブジェクト(材料を入れる以外にすることが
無い、
RecipeSystem.md4 節)がこれにあたります。 - 記憶の置き場所はセーブデータではなく設定(
Settings)です。プレイヤーの好みであって、この島の 出来事ではないためです。
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(ドラッグ)と対になります。
- 並ぶのは、そのカードが映しているオブジェクト自身が持つアクションです。中身入りの甕は甕とは
別の型(
LiquidContainerSystem.md1 節)なので、そのカードには 「飲む」が出ます。 - 日時(状況エリアのフリップカード)を押すと、キャラクタ自身のウィンドウが開きます。 並ぶのは
休息のアクション(
Characters.md休息節)で、時間を進める専用のダイアログでは ありません——休息はキャラクタが宣言しているアクションなので、カードのウィンドウとまったく同じ 経路を通ります。中身のスロットは渡しません(3 節)。 ポートレイトを押すと、日時を押したときと同じキャラクタ自身の子ウィンドウが開きます(どのタブから 始まるかは記憶が決めます、1.2 節)。 - 「閉じる」は操作と同じ行に置かず、その下にもう 1 行取ります。 閉じるは世界を変えない別のもの
なのに同じ行に居ると、操作の数が閉じるの大きさを決め、閉じるが操作の幅を 1 つぶん削ります。数で
割る限りこの綱引きは消えないので、行を分けて切り離します。アクションを持たないオブジェクトでは
操作の行を空けず、最下段が「閉じる」だけになります(説明文だけのウィンドウの見え方は変わりません)。
- 閉じるは常に最下段の同じ場所に在ります。 スマホでは「押しやすい場所に必ず在る」ことが閉じる 操作の値打ちそのものです。数が増えたら折り返すという手は採りません——3 つなら 1 行目の末尾、 4 つなら 2 行目の頭、と位置が動いてしまい、その値打ちを真っ先に壊すからです。右上の × も 採りません(片手で最も遠い場所です)。
- 操作の行に並べられるのは 6 つまでです。 最も狭いウィンドウの内側は 696u で、24u の間隔を挟んで
等分すると 6 つで 96u——アイコンボタンの最小タップ領域(88u、
ScreenLayout.md2 節)を割らない最後の数です。7 つでは 79u になり、 押せる幅そのものが足りません。 - 名前は、等分した箱に収まる長さまでです。 ラベルは縮みも折り返しもしないので、箱より長い名前は そのまま箱の外へ出ます。並べる数がそのまま名前の予算で、上限まで並べた箱(96u)に入るのは 全角 3 文字です。
- 上限を超えたときに移す先はありません。 縦の並び(1 行 1 アクション)は入りません——1 行が
88u + 間隔 24u を取るので、タブを持つウィンドウでは 3 行で横型の画面の高さ(1080u)が尽き、
4 つ目は画面の外に出ます。折り返しは上と同じ理由で採らず、別のウィンドウへ送ることもしません
(メニュー型の操作の入口がここだけでなくなります)。上限に届いたら見直すのは世界の側で、
操作の分け方を変えます。数と名前の長さは、同梱の宣言すべてに対して
tests/world-codex/actionRow.test.tsが検査します。
- アクションのボタンは、長押しの間だけ説明文とかかる時間を吹き出しで出します(ドラッグ中の
吹き出しと同じ部品)。ボタンには名前しか載らないので、実行する前に「何が起きるか・どれだけ時間を
取られるか」を確かめられるようにするためです。長押しで見せたときは、指を離しても実行しません。
かかる時間の行は
durationを持つアクションにだけ出します(0 分と出しても意味が無いため)。 - 今の状況で実行できないアクションは、押せない見た目にします(
conditionsを満たしていない、 GameElementDefinition.md 14 節)。押している間は、その要件が 宣言している理由(同 14.6 節のreason)を同じ吹き出しに出します——こちらは長押しを待たず押した瞬間に 出します。実行できないボタンには待たせる理由が無いためです。理由の宣言が無い要件で落ちている場合は 「今はできない。」とだけ出します。 - 落ちた条件をステータスエリアで指し示すことはしません。 実行できない理由は明るさ・道具の有無など、 画面に出ていないものでもありうるので、指せる場合と指せない場合が混在します。
- ボタンではウィンドウを閉じません。閉じるのは、映しているものが世界から消えたときです。 食べれば・打ち割れば・中断すれば、映すものもボタンが効く相手も無くなるので閉じます。何も消えない 操作(休息・自動補充・工程が1つ進んだだけの作業)では開いたままで、何が起きたかをそのまま 見せます——素材が減り、進捗が伸び、押せなくなったボタンが押せなくなる。
- ボタンの可否は、中身が変わるたびに引き直します。 ボタンは作った時点の可否で固まるので、素材を
入れれば「作業する」が押せるようになり、抜けば押せなくなる、という追従には並びの差し替え
(
CardTable)と同じ経路で渡し直す必要があります。ウィンドウごと開き直しはしません——閉じて 開き直すと、入っていく素材が飛ぶ先のレーンごと消えてしまいます。 - 説明文は
localeのdescriptionです(Localization.md)。まだ書かれて いないオブジェクトには、代わりの 1 行を薄い文字で出します。 - 覆いは領域の中だけに敷きます。 画面全体を覆うと、開いている間も操作できるはずの手持ちが覆いに 入力を吸われます。読み取り専用のウィンドウはもうありません——借りた札(1.1 節)へ手持ちから 物を重ねられるためです。全画面を覆うのは、カードを扱わないウィンドウ(プロパティ・地図・ ステータス詳細)だけになります。
道のカードから「移動する」を実行すると現在地が変わります。このときはレーンの中身だけでなく現在地
カードもレーンの背景の絵も入れ替わるため、カードの差し替え(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 枚出ないためです。
- 借りている間、レーンには枠も空けません。 見つかったばかりの物はまだどの枠にも居たことが
ないので、帰ってくる場所を空けておく理由がありません(借りた枠に薄い印が残る 1.1 節との違いは
ここだけで、
CardInteraction.md6.2 節が引く区別と同じものです)。 - 既に持っていたものに合流する分は、見つかった個体だけを借ります。 石を持っているところへ石を 見つけたら、枠に出るのは見つかった分で、元から在った分はレーンに残ります。
- 現在地の札から飛んできて枠に着きます。着くまでは枠に出しません(同 6.2 節)。
カードはレーンと同じ寸法(205×320u)で描き、縮めません——レーンから来てレーンへ帰る札
そのものなので、大きさが変わると別の札に見えます。4 枠ぶんの幅が無い画面と、4 件を超えた分は
横スクロールで送ります。枠からはみ出したカードはレーンと違って背景板では隠せないため、マスクで
切り抜きます。送り具合はスクロールバー(ScreenLayout.md 7.4 節)で示します。
5.2 探索にかかる実時間
「探索する」を押すと、そのアクションの duration が進めるゲーム内時間の分だけ実時間をかけて
進みます(ゲーム内 15 分=現実 0.5 秒。探索は 1 tick(15 分)なので 0.5 秒)。
- 押した瞬間: 発見物の枠が空へ戻り、「探索する」は押せなくなります。
- その間: フィールドエリアの中央に時間経過のドーナツグラフ
(
CardInteraction.md7 節)が出て、状況エリアの時計が 1 tick(15 分)刻みで進みます。 - 経過し切った時点: グラフが消え、見つかったカードが枠へ入り、探索率・各レーンが更新されます。
ワールド自体は押した瞬間に変わっていて、実時間をかけるのは見せ方だけです。連続で探索したときに 「前回の結果が残ったまま次の結果に入れ替わる」ことがないよう、いったん空へ戻してから埋めます。
6. プロパティのタブ (Properties)
プロパティをカテゴリごとにバーで並べるタブです。ステータスエリアには一部しか出さないため
(StatusArea.md 1 節)、残りを含めて見るための面です。
- 出すのはタグの付いたプロパティだけです(プロパティのタグ、 GameElementDefinition.md 6.7 節)。見せるかどうかは型ごとの 真偽値ではなく、プロパティ 1 つずつが「人に見せる値か」を名乗ることで決まります——今タグを持つのは キャラクタだけなので、他の型ではタブそのものが出ません。見せたくなった型は、そのプロパティに タグを足せば出ます。
- カテゴリは左の縦タブで、並び順は
property_tagsの宣言順です。そのタグのプロパティを 1 つも持たないカテゴリは出しません。 - 1 行は「名前+バー」で、ステータスエリアと同じ部品(
StatusBar)です。rangeを持たず割合を 定義できないプロパティは、バーの代わりに値そのもの——段を持つもの(腕前)は今いる段の名前と段内進捗 ——を出します(StatusArea.md9 節)。増減の記号 (StatusArea.md5 節)は出しません——開いている間はワールドが変わらないためで、 右の欄は空いたままになります。 - 左にはアイコンと表示名を並べます(
StatusArea.md3 節)。ステータスエリアは アイコンだけなので、どの絵がどのプロパティかをユーザが覚える場がここになります。名前の欄は 「穀物・イモの栄養」が収まる幅(260u)を取ります——横幅に余裕があり、バーを詰めずに置けます。 - 行はどこをタップしてもステータス詳細(8 節)が開きます(
StatusArea.md3 節)。 ステータスエリアに出ないプロパティも、その詳細で固定表示にすればステータスエリアへ出せます。 ウィンドウを開いたまま切り替えられるので、印(📌)はその場で付け外しされます。 - プロパティの数で窓の寸法は変わりません。 決め打ちの行数(5 行)ぶんの高さを要求し、収まらない 分は縦にスクロールして送ります。カテゴリを切り替えるたびに枠が伸び縮みすると、どのカテゴリを 見ているかより枠の動きのほうが目に付きます。
7. 地図ウィンドウ (Map Window)
既知の土地のカードを、ユーザ自身がドラッグで並べて自分だけの地図を作る全画面の子ウィンドウです。
地図ボタンから開きます。実装は src/game/ui/MapWindow.ts。
- 出すのは既知の土地(現在地と、発見済みの道の行き先)と発見済みの道だけです。発見の状態は
フラグではなく「path オブジェクトが
fixturesスロットに出ているか」 (ExplorationSystem.md 3 節)から導出します。 - 土地のカードはレーンのカードの 0.5 倍で描き、ドラッグで好きな位置へ置けます。現在地の
カードだけは太い黒枠で囲んで強調します。まだ置いていないカードは下端の待機列に並びます。
置いた位置はセーブデータに残ります
(SaveDataManagement.md の
mapCardPositions。キーは地形生成の サイト index で、座標は画面に対する 0〜1 の正規化値。画面の向きが変わっても画面内に収まる)。 - 発見済みの道は、カードの中心同士を結ぶ太めの点線の弧(2 次ベジェ)で描きます。膨らむ向きは 辺ごとに固定で、ドラッグに追従して描き直します。
- 背景は古い海図風の下地です。羊皮紙の薄茶に、ごく薄い線で円形に近い島の輪郭だけを描きます。 これは実際の地形ではなく、カードを置くときの目安になる個性のない形です——実際の配置を写して しまうと、自分で地図を描き上げる遊びが成り立たないためです。
- ピンチ(スマホ)・ホイール(PC)で等倍〜3 倍に拡大できます(小さめのカードはスマホでは 読みにくいため)。拡大中は背景のドラッグで見る範囲を動かせます。ズームとパンは描画の変換に 閉じていて、保存されるカード位置(正規化座標)には影響しません。
- 全画面を覆う読み取り専用のウィンドウで(カードの配置は表示上のもので、ワールドは変わりません)、 「閉じる」だけで閉じます。
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 今いる段は、バーに刻んだ目盛りと名札で示す
バーには段の境目をすべて刻み、今いる段だけを囲みと明るい面で示して、その上に名札を置きます。 名札は下向きのしっぽで今いる段を指します。
- 全部の段を刻むのは、次の段まであとどれだけかに加えて、このステータスが全部で何段あって、 自分は何番目かまで 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 節)ので、道の枠は歩き始めてから歩き終えるまでの間だけ現れます。それでよいのは、 効果自体が同じ間しか効かないからです——歩いていない人の速さを道が下げることはないので、一覧に無いことが そのまま事実です。
- 原因は、その効果のゲート(同 8.2 節)が見ている段です。「痛みが
unbearableの段にある間だけ 意識を −45」という宣言から、痛み → 意識という 1 本を導きます。UI はどのステータスが何に効くかを 1 つも知りません。 - 輸送(
transfer)は両端が互いの原因です。受け取る側が増えるのは出す側に在庫があるからで、 出す側が減るのは受け取る側へ持っていかれるからで、1 本の輸送はどちらの端から見ても相手のせいで 動いて見えます。 - 条件(
conditions)で縛られた効果は、条件を評価して今その条件が成立しているかを札に反映します (8.2 節の薄い札)。 - 中身から受ける寄与(
weight・load、ContainerSystem.md 1〜2 節) だけは持続効果ではありませんが、同じ 1 件として並べます。 読むたびに導出される可逆な押し上げなので、 記号はmodifyと同じ ▲ です。中身 1 つずつではなく 1 件にまとめます——担いでいる物の数だけ札が 増えても、「何がこの値を押し上げているか」の答えは「中身」の 1 つだからです。空身のときは押し上げて いないので、条件の成立していない効果と同じく薄く出ます。
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 と同じ文の形で書けます。