カードと枠の表示
概要
カード 1 枚の見た目——枠・絵・状態バー・印・桟の文字——と、カードを収める枠(セル)の描き方を決める
ドキュメントです。カードが並ぶレーンとエリアの配置は ScreenLayout.md、
カードを動かす操作は CardInteraction.md が扱います。
カードは紙 → 絵 → 枠の 3 層(1 節)で、何を出すかはすべてワールド側の宣言から導きます——
状態バーはプロパティの gauge 宣言と capacity・fill から(8 節)、枠の色は種別の
タグから(2 節)。UI 側の対応表は持ちません。UI 専用の宣言はプロパティの gauge
(GameElementDefinition.md 6.8 節)と、
status タグ(StatusArea.md)だけです。
実装は src/game/ui/Card.ts(カード)と src/game/ui/CardLane.ts・laneCells.ts(枠)、色は
src/game/looks/theme.ts、絵の解決は objectArt.ts・backgroundArt.ts で、実装済みです。
1. カードの枠
枠は境界線ではなく意匠です。実在のトレーディングカードは「枠が無い」か「しっかり太い枠が入る」かの どちらかで、その中間——境界を示すだけの中途半端な太さ——にはなりません。
構造は上から順に、紙 → 絵 → 枠です。枠が絵の上に乗るので、絵は縁でフェードさせず角丸で切り落とします (5 節)。
| 部分 | 幅 | 中身 |
|---|---|---|
| 左右と上の桟 | 8u | 無地 |
| タイトルの板 | 22u | カード名(中央揃え・1 行・16u) |
| 下の桟 | 可変(8u 〜) | 状態バー、道の矢印、1 行の文字(16 節) |
下の桟は中身で高さが変えます。 耐久度と内容量を両方持つ入れ物は 2 本ぶん、何も無いカードは左右と
同じ細さです。固定にすると、書くものが無いカードで帯が空いて間延びします。可変なので枠は 1 枚絵に
できず、図形として描きます(意匠が入る段になったら 9patch へ移します。src/ui/nineSlice.ts
——Phaser の NineSlice は WebGL が無いと消えるので、自前のものを使います)。
中身の下は左右と同じ 8u を空けます。 上下へ同じ余白を取ると、中身を持つカードだけ下の枠が細くなり、 持たないカードと並んだときに枠が痩せて見えます。中身の上は枠ではなく絵との間隔なので、揃える相手が 違います。
状態バー・道の矢印・1 行の文字(16 節)は桟の中へ入れます。 絵の上に浮かせると、絵の濃淡に 埋もれるうえ、枠の意匠から浮きます。矢印は桟の中央に置きます(左下に寄せると、道であることが記号として 読めません)。積む順は「矢印 → 文字 → バー」で、文字はバーより上です——バーの本数はカードごとに 変わるので、後ろに置くと文字の出る位置が定まりません。
2. 枠の色は種別で変える
枠の色は場所・設置物・アイテム・怪我・動物・キャラクタで変えます。道が設置物の中に埋没する問題がこれで 解けます——同じレーンに並ぶ道と設置物は、行き先の絵を出すぶん見分けが付きにくく、桟の矢印 (1 節)だけでは手がかりが細すぎます。色は矢印よりずっと速く読めます。
種別は物の型が名乗るタグで決まります。 キャラクタも土地もタグを持つ物なので(character /
location)、札の作り方を対象ごとに分けません——印もバーも個体の識別子も、同じ 1 つの経路
(cardLooks.contentOf)から付きます。道だけは例外で、行き先の土地の名前と絵を借ります。
現在地と道は同じ色(琥珀)です。 どちらも土地の名前と絵を出す場所の札で、色が言うのはそこまでです。
歩いて行けるかどうかは桟の矢印が言い、現在地はそもそも設置物レーンの左端にピン留めされている(
ScreenLayout.md)ので、位置だけで一意に決まります。色をもう 1 つ使っても、
位置と矢印が既に答えている問いに二度目の答えを足すだけです。
動物の色に意味は持たせません。 血の赤は怪我、毛皮の茶はアイテム、緑は設置物、青はキャラクタが既に
取っているので、動物には残っている色相のうち他から最も遠いもの(紫)を当てます。ここで要るのは色の意味
ではなく、同じアイテムレーンに並んだときに一目で違うことです(動物はアイテムを兼ねるため、種別を決める
順序では動物を先に見ます、HuntingSystem.md 1.1 節)。
2.1 アイテムは用途で分ける
ゲームに出る物の大半はアイテムなので、アイテムが 1 色のままではレーンが同じ色で埋まります。そこで 食事(黄)・入れ物(青緑)・道具(灰)を分け、残りをアイテムの茶のままにします。
- 素材には色を当てません。 素材は物の性質ではなくレシピの中での役割だからです——尖った石は 道具でありながら石斧の素材でもあり、色は 1 枚に 1 つしか塗れません。そして素材はアイテムの多数派なので、 塗っても同じ密度の別の色になるだけです。残り全部が素材で、既定の茶がそのまま素材の色になります。
- 水は独立させません。 液体の容器は入れ物と同じ色で、水が入っているかは中身のバーが液体自身の色 (水なら青)で言います(8.2 節)。枠は「何のための物か」、バーは「今どうなっているか」を言う分担です。 空の甕は水ではなく入れ物です。
- 兼ねる物は、生存の時計に近いほうを先に見ます(食事 → 入れ物 → 道具)。中に水を抱えた青いヤシの実は 入れ物ではなく食事です。
- 食事の黄は、道の琥珀と色相を離します。彩度で開けるアイテムの茶と違い、琥珀とは彩度も明度も近いので、 離せるのは色相だけです。地面へ置いた食料は設置物レーンで道と並びます。
どれに当たるかはワールド側のタグが決める
UI 側に「この識別子は道具」という対応表は持ちません。持つと、後から足された物——アセットパックや新しい YAML——が 必ず既定の茶で出ます。付けるかどうかは、その物自身の宣言で決まります。
| タグ | 付ける物 |
|---|---|
food |
そのカード自身を口に入れると体の値が動く物。 自分の宣言する操作が、食べる本人の satiety・hydration を増やすこと。道具や工程を挟むかは問いません(青いヤシの実は刃物で穴を開けて飲むので食事です) |
container |
物を入れて運べる入れ物(item_container trait が付けます) |
liquid_container |
液体を受けるスロットを持つ容器(liquid_container trait が付けます) |
tool |
他の物の操作が使う側として指名し、使っても無くならない物。能力のタグ(cutting_tool・weapon)と重ねて付けます |
- 加工すれば食べられる物は食事ではありません。 熟したヤシの実は皮をはいでも自分は体に何も入れず、 産物(果肉)が食べ物であることは産物のカードが言います。
- 指名されても移ってしまう物は道具ではありません。 注ぎ込む水は
with: {tag: liquid}で指名されますが、 操作の中で容器へ移るので素材の側です。切っても減らない尖った石とはここで分かれます。
タグの付け忘れはエラーにならず、素材と同じ枠で静かに出るだけなので、食事のタグは自動テストが
現物と突き合わせます(tests/world-codex/purposeTags.test.ts。上の条件をそのまま検査します)。
タイトルの板は枠より暗くします。 枠と同色のべたにすると単調で、半透明にすると背後の絵に負けて 名前が読めません。強調したいのは絵であって、名前は枠の一部です。この規約から外れるのは金だけで、 理由は 2.2 節に書きます。
2.2 アーティファクトは金の枠で、板が光を返す
artifact タグを持つ物(ContentSkeleton.md 6 節)は、種別を覆って
金の枠になります。 金の聖杯はアイテムのままですが、アイテムの茶で出るとレーンの中で素材に埋没します
——アーティファクトはゲームのゴールそのもの(GameEndings.md 6 節)なので、
持っていることが一目で分かる必要があります。
種別と並ぶ選択肢ではなく、種別の上に掛かります。 青写真(10 節)と同じ掛かり方で、実装でも同じ型
(CardFrameKind)の値です。両方に当たる物では青写真が勝ちます——出来上がるまでは「まだその物に
なっていない」ほうが、何になるのかより先に言うべきことだからです。
金は単色では表せません。 金が金に見えるのは面が一様に光るからではなく、斜めに反射の光が走る からで、1 色で塗ると他の枠と同じ「ただ黄色い板」にしかなりません。そこでタイトルの板を「1 色」から 「左から順に、斜めの筋として塗る帯の並び」へ広げます。普通の札は帯 1 本=今までどおりの平らな板で、 金だけが多数の帯を持ちます。板の描き方は帯の並びだけで決まり、描く側は何の枠かを知りません。
- 板を明るい側へ振ります。 板を暗くする 2 節の規約は「強調したいのは絵で、名前は枠の一部」を理由に していますが、アーティファクトはその前提が逆で、札そのものが目立つことが目的です。
- 翳りは桟より暗いところまで落とします。 金属に見えるかは板の明るさそのものではなく振れ幅で
決まるので、板全体を桟より明るくすると淡い黄色の帯にしかなりません。検査するのは「反射が桟より
明るいこと」だけです(
tests/game/cardFrameColors.test.ts)。 - 翳りも彩度の高い琥珀に留めます。 縁の青銅へ寄せると、金ではなく茶に見えます。
- 名前の文字は暗い青銅にします。 板が明るくなると、他の枠で使っている紙の白は板の上で読めません。 文字を暗くするのは、光の筋を文字の外へ通す・文字に縁を付けるのと違い、筋の引き方と文字の位置が 互いを縛らずに済むからです。
- 帯は翳り→反射→翳りを板の左端から右端までに 2 度繰り返します。板の幅に対して 1 本が数ピクセルに なる本数(25 本)にして、段ではなく面の照りに見せます。
- 筋の角度は札の倍率で変わりません。 板の高さに対する横のずれで決めるので、棚や子ウィンドウで 板の幅が変わっても同じ角度で走ります。板の角と角を結ぶ引き方はこれができません。
- 筋は板の左右の端で止めます。 板の角が丸いのは上端だけなので、板をまず 1 本目の色で角丸に塗って から筋を重ね、丸みに掛かる左右の端だけは下地のまま残します。帯の並びは両端が同じ色なので、 残した端は板の左端と同じ色で埋まります。
3. 警戒している動物は輪郭を明滅させる
警戒(wariness)が安全域を外れている動物のカードは、紙の縁を赤く明滅させます
(HuntingSystem.md 3.1 節)。放っておくと物を壊されたり持ち去られたりする
——つまり手を止めるべき——ことを、ステータスエリアではなくその札の上で言うためです。
- 域の深さでは分けません。 輪郭が言うのは「この札を放っておくな」の 1 つだけで、どれだけ気を立てて いるかはカードを開けば読めます。域ごとに色を変えると、バーの黄(危険域)・赤(致命的域)の規約と 混ざります。
- 枠の中ではなく紙の縁に出します。 枠の中へ描き足すと絵の濃淡に埋もれ、レーンを流し見していると 気付けません。太さは枠の縁の線より太くして、明滅していることが形からも読めるようにします。
- しきい値を持つのはワールド側の
stagesだけです。UI は「その名前のプロパティが安全域を外れたら 明滅させる」とだけ知っていて、何がどれだけ危ないのかは知りません (StatusArea.md2 節と同じ分担)。
4. カード名は 1 行に収まる長さで書く
板の高さが 22u で固定なので、名前は 1 行です。文字は 16u(板の高さの 7 割。これ以上大きくすると 字が板の縁に触ります)で、板の使える幅 172u に収まるのは全角 10 文字までです。今ある名前は 最長が「皮を剥いだヤシの実」の 9 文字で、すべて収まります。
名前を大きく出すより、絵を大きく見せるほうを採っています。肖像も同じ板に名前を出します——絵が 窓いっぱいに入るので、名前を下へ逃がして顔を空ける必要がありません。
英字は全角のおよそ半分の幅です(16u での実測で 1 文字 8.0u)。目安として「全角 10 文字=英字 20 文字」で
考えて構いませんが、字ごとの差は大きく(i l は全角の 0.19 字ぶん、W は 0.85 字ぶん)保証にはなりません。
検査は文字数ではなく幅で行います(tests/world-codex/bundledLocale.test.ts)。Node には文字の描画幅を測る
手段が無いので、全角 1.0・半角 0.5 の近似で数えます。この近似は実測より常に大きめに出るため、
通れば実物は必ず収まります。
製作中オブジェクトの名前は完成品と同じにします(variation_names の recipe)。自動で語を足すと、
元から限界に近い名前がはみ出すためです。作りかけであることは青い覆いが示す(10 節)ので、名前で
言い直す必要はありません。
5. カードの絵
カードの絵は src/assets/objects/<型の絵の名前>.png に置きます。ファイル名がそのまま絵の名前で、
コード側への登録は要りません(src/art/objectArt.ts)。
- 絵の名前は既定で object_def の識別子です。 型が
art(GameElementDefinition.md4.3 節)を宣言したときだけ、それ以外の名前を名乗ります——行き先しか違わない航路のように、 同じ絵しか持ちようがない型の群れで 1 枚を共有するためのものです。 - 種別のプレフィックス・サフィックスは付けません。 識別子は Codex 全体で一意なので不要ですし、 アイテムから設置物へ移したときにファイル名を変えずに済みます。
- 絵は枠と同じ寸法(205:320)で作り、枠の上にそのまま重ねます。背景は透過にします。
- 土地そのもののカード(設置物レーンの左端、押すと現在地の子ウィンドウが開くカード)も同じ規約で 絵を持ちます。ただしこれは景色なので、絵をカード全面に敷いて透過させません——空や海が紙へ 置き換わると景色にならないためで、設置物のカードの背景(7 節)と同じ理由です。
- 絵がまだ無い object_def は、
cardLooksが種別ごとに割り当てた絵文字で代用します。 絵は少しずつ増える前提なので、両者が混ざった状態を正常とします。
乗算で重なる絵は <絵の名前>_multiply.png に置きます。同じ名前の絵と、その下の地の上へ、乗算で
載ります。痣のような肌の変色——肌の上に在る物ではなく、肌そのものの色が変わったもの——を描くための
層で、通常の重ねだと絵の具に見えてしまいます。
接尾辞が合成方法そのものを名乗ります。 「怪我の絵は乗算」のように種別へ結び付けると、絵のファイルを 見ただけではどう載るのかが分かりません。名前が読み手(絵を描く人)への制約をそのまま伝えます。
- 描けるのは暗くすることだけです。白い骨や腫れの照り返しは描けないので、そこは印(9 節)や 状態バー(8 節)に任せます。
- 「変わらない」は透明で表します。白ではありません。 乗算が効かない環境では白がそのまま白く出ます
(
DesignNotes.md)。色と濃さだけで描いておけば、落ちても少し平たく 見えるだけで済みます。 - 通常の絵を持たず、この層だけを持つ絵もあります。その場合は絵文字で代用しません——絵はもう在ります。
面が破れている傷は、通常の絵で肌を敷いてから乗算を重ねます。 乗算は地を暗くすることしかできない ので、切り傷を乗算だけで描くと、毛皮の暗い所へ掛かったときに傷が見えなくなり、毛の上に線を引いた だけに見えます。切り傷の辺りでは毛が分かれて肌が覗くはずなので、乗算の層と同じアルファの肌を 通常の絵として先に敷きます。これで傷は地の明暗によらず同じに見えます。皮膚の下の変色(痣)は面を 破っていないので、乗算の層だけを持ちます。
一覧はビルド時に作ります(import.meta.glob)。実行時に総当たりで読みに行くと、絵をまだ用意していない
object_def のぶんだけ 404 が出るためです。ファイル名の綴りが object_def と食い違っていると絵は黙って
使われないままになるので、実在の名前かどうかは自動テスト(tests/art/objectArt.test.ts)で検査します。
5.1 段で差し替わる絵は <絵の名前>_<接尾辞>.png
同じ個体のまま姿だけが変わる状態は、型を差し替えず絵だけを差し替えます。 気を失った動物、火のついた
炉がこれです。段(GameElementDefinition.md 6.4節の stages の art)が接尾辞を宣言し、
art_by_stage が指すプロパティの実効値が今その段にあれば、<絵の名前>_<接尾辞>.png を通常の絵の代わりに
使います(objectArt.ts の artNameFor)。
_multiplyと同じ既定動作です。 宣言した接尾辞のファイルがまだ無ければ、型自身の絵(無印)の ままになります。絵を描く前に文法だけ先に書けます。- 複数の段が同じ接尾辞を指せます。 段の数だけ絵を用意する必要はありません(
GameElementDefinition.md6.4節)。 - 接尾辞を足した名前が、別の型の絵と一致してもかまいません。 1枚を2つの型で共有できます——気を
失った動物は死体の絵(
<動物>_carcass.png)をそのまま借ります。倒れた個体と死にたての死体は同じ姿 なので、絵を分ける理由がありません。 - 型そのものを表すカード(インスタンスの無い型カード)は、個体の状態を持たないため常に無印の絵です。
6. スタック数の丸
同種のアイテムは 1 枚のカード(スタック)にまとまります。2 個以上をまとめているカードは、右上に 丸で囲んだ数字で個数を出します(レーンでも手持ちでも同じです)。
名前にかぶってでも読める大きさを採ります(丸 42u・数字 24u)。枚数はカードを開かずに読む値で、 小さい画面ではカード名より先に要ります。名前を避ける大きさまで縮めると、実機で読めなくなります。
丸が札からはみ出す量は、カード間ギャップの半分(SIZE.gap / 2)で固定です。ここは枠の強調
(11 節)が使うのと同じ溝で、増やすと隣のカードの側へ食い込みます。丸を大きくした分は内側——
名前の板の上——へ伸びるので、板の右端が全角 1.6 文字ぶん隠れます。
3 桁の数(石 100)は丸から左右へわずかにはみ出します。丸と数字を同じ比率で決めているので、
大きさを変えてもこの見え方は変わりません。収めるなら桁数で丸を横長にする必要がありますが、
3 桁になるのは石を大量に持ったときだけなので、形を分ける理由には足りていません。
7. 背景の絵
レーンの全面に敷く絵と、カードの地に敷く絵をまとめて背景と呼びます。置き場所は
src/assets/backgrounds/ 直下のみで、名前は
<持ち主の object_def の識別子>_<スロット名>_<敷く場所>.png です(src/art/backgroundArt.ts)。
敷く場所は lane(レーンの全面)か card(カードの地)で、それ以外はすべてワールド側の言葉です。
| ファイル名 | 何を描くか |
|---|---|
sandy_beach_fixtures_lane.png |
その土地の眺め |
sandy_beach_items_lane.png |
その土地の地面 |
sandy_beach_fixtures_card.png |
その土地の眺め(カードの縦長) |
medic_injuries_card.png |
怪我を負った本人の肌 |
hand_lane.png |
プレイヤーの手(持ち主によらない) |
どのスロットの絵かを、名前が言います。 「設置物レーンには土地の眺め」「怪我のカードには本人の肌」
といった対応を画面側が持つと、絵を 1 枚足すたびにコードを触ることになり、絵の一覧を見ても何に使われる
のか分かりません。名前がスロットを名乗るので、画面側の規則は、「そのカードが今在るスロットの絵を敷く」
の 1 つだけです(cardLooks はカードの親とスロット名をそのまま渡します)。
その帰結として、傷の絵は傷だけを描けばよくなります——誰の身体に付いているかは地が言うので、絵の 枚数は「怪我の数 × 身体の数」ではなく足し算で収まります。そのぶん、絵に部位は出せません(捻挫と打撲は 絵では見分けが付かず、重さは状態バー、骨折のような特別な状態は印が言います)。
- 持ち主によらない絵は
<スロット名>_<敷く場所>.pngに置き、持ち主ごとの絵が無いときの受け皿に なります。手はプレイヤー自身のものですが、キャラクタごとに描き分けていないのでこちらです。 - 用途でディレクトリを分けません。 土地を 1 つ足すと 3 枚が対で必要になるため、1 か所に 集めた方が「何を描き足せばよいか」が一覧で分かります。
- 持ち主・スロット・敷く場所の順に置きます。 ファイル名一覧で同じ持ち主の絵が隣り合うためです。
- 絵がまだ無いスロットは、レーンならレーンの背景色で塗り、カードなら紙がそのまま地になります。 絵は少しずつ増える前提なので、絵のあるスロットと無いスロットが混ざった状態を正常とします。
一覧はビルド時に作り、その持ち主が本当にそのスロットを持っているかを自動テスト
(tests/art/backgroundArt.test.ts)で検査します。理由はカードの絵と同じです。
レーンの背景は次の規約に従います。
- 絵はレーン高(352u)へ合わせて縦横同率で拡大縮小します。原寸は問いませんが、レーン高より
低い絵は拡大されるので、高さは 2 倍の 704px を目安にします(
ScreenLayout.md1 節「絵は u の 2 倍で描く」)。 - 横方向は繰り返して並べるので、左右の端が繋がるように描きます。
- 背景はカードと同じだけ横へ送ります。カードが地面の上に置かれているように見せるためです。
カードの地は、枠と絵の間に敷きます。設置物は持ち歩けるアイテムと違ってその土地から切り離せない もので、カード自体が土地の一部として見えるべきだからです。道のカードもここに含みます。
-
敷くかどうかはオブジェクトの種類ではなく、今どのスロットに在るかで決まります。 地が表すのは 「何の上に在るか」であって、そのオブジェクトが何であるかではないためです。同じヤシの実でも、 地面(
items)に在る間は地が付かず、fixturesに在れば土地の眺めが付きます。 発見物の枠や子ウィンドウに出るカードは、レーンから借りてきた札そのもの (Windows.md1.1 節)なので、地も一緒に付いてきます。 例外は製作中オブジェクトのカード(10 節)だけで、こちらは景色より覆いを優先します。 -
寸法・角の丸めはカードの絵と同じ規約に従いますが、背景は透過させません。空や海が紙へ 置き換わると景色にならないためです。代わりに縁だけを内側へ向けて薄くし、カードの枠へ馴染ませます。
絵は ComfyUI で生成し、油絵風にぼかしてから横方向をシームレスに繋いでいます。プロンプト・
ワークフロー・後処理の設定は tools/comfyui/ に置いてあり、
レシピ 1 つで作り直せます。
8. カードの状態バー
そのカードが映しているものの状態を、枠の下の桟(1 節)の中にバーで出します。どれも「その値を
持つカードにだけ出る」ので、値を持たないカードの見た目は変わりません。実装は src/game/ui/Card.ts で、
バーの部品(ProgressBar)はステータスエリアと共通です。
バーはすべて 1 つの形(ゲージ)に揃えます。 何を映すバーであっても、持っているのは「割合」と
「両端がどう見えるか」だけで、そこから 1 つの関数(gaugeColorFor)が塗ります。カード側は
どのバーが何かを知らず、渡された順に積むだけです。
| 何を映すか | 両端 | |
|---|---|---|
| プロパティのゲージ | gauge を宣言したプロパティの range に対する割合(8.1 節) |
宣言が決める |
| 材料のバー | 今の工程が要求する素材と道具が、どれだけ揃っているか(10.1 節) | 満ちる側が good |
| 中身のバー | 中身の量(fill)が、その容器の上限をどれだけ満たしているか(8.2 節) |
色を別に持つ |
| 容量のバー | 入れ物に入っている物の volume の合計が、そのスロットの capacity をどれだけ満たしているか |
空いている側が good |
耐久度・炉の残り薪・残っている傷・意識・工程の進捗は、いずれも 1 行目のプロパティのゲージです。 UI 側にプロパティ名の対応表はありません。下に挙げるものだけがビルトインで、入れ物と中身の関係から 出る値——単一のプロパティではないので宣言に載せられない——を映します。
- どれも同じ寸法(高さ 12u)で、上から積みます。 どれが主要情報かはカードごとに違う——道具に とっての耐久度と入れ物にとっての容量はどちらもそのカードの主役——ので、太さで格を付けません。 順は「材料 → プロパティの宣言順 → 中身 → 容量」です。
- 何本出るかはワールド側の宣言の数で決まります。 UI 側は上限を持たず、
gaugeを 2 つ宣言した物は 2 本出ます。積んだぶんだけ桟が厚くなります。 - 枠線は札の縁と同じ色で引きます。 バーが桟に嵌まって見え、平らな塗りより目盛りとして読めます ——効くのは容量のバーのように薄い色を持つものです(濃い緑は地から浮くので枠が無くても読めます)。 色は種別で変わる(2 節)ので、差し替えのたびにカードが渡します。
- 左右は窓(1 節)に揃えます。 桟とその上の絵は縦に隣り合うので、端が揃っていないと桟だけが はみ出して見えます。窓の縁の線もバーの枠線も線の外周が窓の縁に重なるので、両者の幅は 見た目のうえで一致します。
- 意識のバーは1本で足ります。 痛み・衝撃・失血はいずれも意識へ合流する
(
VitalsSystem.md2 節)ので、あと何手で倒れるかはこの1本が答えます。 衝撃や失血のバーを並べると、2本が同じ問いに別々の答えを返します。 - トラックの枠線は描きません。 数 px の太さでは枠線が大半を占め、塗りが読めなくなるためです。
- 中身のバーは、
fillを持つカードに出ます。空の容器では 0 のバーになります——どれだけ入るかは 容器自身の情報なので、容量のバーと同じ理由で 0 でも出します。色は中身がcolorとして宣言した値で、 中身の無い容器は灰色です。 - 容量のバーは、物を溜める入れ物と名乗った型(
storage、 GameElementDefinition.md 7.12 節)のカードに出ます。上限 (capacity)を持つスロットが 1 つも無ければ出しません——いくらでも入るものに満たされ具合は無いため です。複数あるときは最も詰まっているものを映します。中身が空でも 0 のバーは出します。あとどれだけ 入るかは入れ物自身の情報で、バーが消えると「まだ入るのか、そもそも限りが無いのか」が見分けられなく なるためです。 - 液体の容器は容量のバーを出さず、中身のバーが受け持ちます。 容量のバーが映すのはスロットの
詰まり具合で、液体の容器はスロットを持たないためです
(
LiquidContainerSystem.md1 節)。液体の容器は入れ物とも 名乗らないので、この住み分けは宣言どおりに決まります。 - どのバーも、変わった分を帯として残して遅れて追いつかせます(
StatusArea.md4 節と同じ動き・同じ時間で、1 回の行動の途中では動かさないところまで同じ)。バーは作り直さず 値だけを差し替えるので、飲んだ量・溜まった量がそのまま帯として読めます(中身が消えた瞬間だけは、 追いつく相手ごと無くなるのでバーも消えます)。同じものを映しているかは鍵で見分けます—— プロパティのゲージはプロパティ名、ビルトインは@で始まる名前です。 - スタックにまとまったカードは代表の状態を出します。名前も絵も操作も代表のものなので、状態だけを 別の個体から採ることはしません。
8.1 ゲージは、宣言した両端だけで塗る
どのプロパティがバーになるかも、どちらの端が良いかも、UI 側の対応表ではなくプロパティ自身の
gauge 宣言(GameElementDefinition.md 6.8 節)が決めます。
durability: {gauge: {min: bad, max: good}} # 尽きると使えない: 満タン側が良い
severity: {gauge: {min: good, max: bad}} # 残っている傷: 減るほど良い
finished_steps: {gauge: {min: neutral, max: neutral}} # 工程の進捗: 良し悪しを言わない
- 色は両端の見せ方と割合だけで決まります(
gaugeColorFor)。goodの端は緑、badの端は赤で、 中間は琥珀を通します——両端を直接混ぜると中間が濁った茶になり、致命的域のステータスバーと 見分けが付かなくなるためです。両端が同じなら値によらず 1 色で、工程の進捗が 0 のときに 赤で急かすことがありません。 - ステータスバーと違って域(
alert)からは引きません。 ゲージが映すのは「どちらの端へどれだけ 寄っているか」で、その向きは宣言が言い切っているためです。域はカードの外——ステータスエリアの 並び順・画面の枠の明滅(StatusArea.md)——が引き続き使います。 - 増えた分の帯の向き(
worsensUpward)も同じ宣言から決まります。 傷は増えるほど赤い帯が伸び、 耐久度は減るほど赤い帯が残ります。同じ向きは他の宣言も述べます(worsens・stagesのalert) が、どれも同じ 1 つの事実なので、食い違うとロード時エラーです (GameElementDefinition.md6.8 節)。 - 後から足された物——アセットパックの道具に独自の耐久度を持たせる、別の消耗品に残量を持たせる——も、
そのプロパティへ
gaugeを書くだけで同じバーに出ます。
8.2 中身のバーの色は、液体自身が color として宣言する
中身のバーだけは、良し悪しではなく中身そのものの色を映すので、両端からではなく液体自身が
color(0xRRGGBB)として宣言した色で塗ります(水は青、茶は茶緑、油は黄色、
liquid_containers.yaml)。何色に見えるかはその液体の性質なので、容器側にも UI 側にも持たせません。
UI 側に「液体 → 色」の対応表を持ちません。 表を持つと、後から足された液体——アセットパックや新しい
YAML——が必ず「色の分からない液体」になり、YAML を書くだけでは画面に出せなくなるためです。UI が
知っているのは「液体は color という名前で自分の色を宣言する」ということだけで、液体の種類は
一切知りません。色を宣言していない液体は灰色で出ます(中身があること自体は見えます)。宣言し忘れは
自動テスト(tests/world-codex/liquidContainersYaml.test.ts)が捕まえます。
この色にタグは要りません。 中身のバーはカードに 1 本しか無く、1 つの物が 2 つの color を持つ
こともありえない(プロパティ名は物ごとに一意)ので、タグにして数を数えられるようにしても得るものが
ありません。同じ理由で、このバーが読むもう一方の値——中身の volume と容器の capacity——も
名前で引いています。
空の容器にも割合 0 のバーを出します。 空であることも中身の情報で、バーが消えてしまうと 「空なのか、そもそも液体を入れられないものなのか」が見分けられなくなるためです。
9. カードの印
その札について、絵を描き替えずに言いたいことを、絵文字ひとつで絵の左下へ小さく重ねます。映して
いるものの状態(血が流れている🩸・手当てが当たっている🩹)も、その札が今どう扱えるか(未解放の
レシピの🔒、Windows.md 9.3 節)も、同じ印で出します。
- 掛け合わせで増える状態は、絵を差し替えず印で出します。 手当ての有無で絵を差し替えると、怪我の
部位 × 治療具の数だけ絵が要り、すぐ組み合わせ爆発を起こします
(
InjurySystem.md3節)。印なら、状態の数だけ絵文字を足せば済みます。 - 置き場所は枠の内側の窓の左下です。桟の中は状態バーと道の矢印のもので、印はそこへ積まずに絵へ 重ねます——状態バーと違って高さを取り合う相手ではなく、絵の上なら札の面積の中で最も目に入るからです。
- 1枚に出るのは1つだけで、急がせるほうが勝ちます。 血が流れているなら、手当てを当ててあっても 🩸 を出します——伝えるべきは「当ててある」ではなく「まだ止まっていない」のほうです。
9.0 出血の印は、負っている本人まで上がる
傷のカードは開かないと見えません。 レーンに並ぶのは動物やキャラクタ本人の1枚で、負っている傷は
その中の injuries スロットにあります。傷のカードにだけ印を出すと、流し見しているあいだに失血が
進み、開いて初めて気付くことになります。
そこで、血が流れている物を中に抱えている物にも同じ印を出します。動物のカードにも、キャラクタの ポートレイトにも、同じ 🩸 が同じ意味で出ます。
- UI は怪我がどのスロットに入るかを知りません。 見るのは「直下の子に、血が流れている物がいるか」
だけです(
WorldObject.children)。スロット名を知らないので、将来どこへ傷が刺さっても同じに効きます。 - 上がるのは出血だけです。 手当て済み(🩹)は「もう手を打った」を言うもので、急がせる必要が ありません。本人のカードまで上げると、印が常時点いた状態になって「今すぐ見ろ」の力が失われます。
- 何段も辿りません。傷は負っている本人の直下にあり、それ以上遠くの出血は、その物の問題ではない ——かごに入れた獣が血を流していれば、それはかごではなく獣のカードが言うことです。
9.1 動かなくなったことは、大きく出てから上部へ残る文字で言う
印で足りるのは「その物に何かが起きている」までで、動かなくなったことは絵の左下の絵文字1つでは 伝わりません。 気を失った動物なら、レーンを流し見して気付ける強さが要ります。
そこで状態の名前を、絵の上へ文字で重ねます(CardContent.overlay)。1つのオブジェクトに1つの絵
は崩しません——状態ごとに絵を差し替えると、動物の種類 × 状態の数だけ絵が要りますが、文字なら状態の
数だけ言葉を足せば済み、どの動物にも同じものが効きます。
現れ方は次のように移ります。
- 立った瞬間は大きく、カードの中央へ出して気付かせます
- 0.9秒後に縮んで、窓の上部へ収まって残ります。以後はそこが「今こうなっている」の定位置です
出っぱなしで大きいと絵を潰し、初めから小さいと気付けません。文言が変わらない差し替えでは掛け直しません ——時間経過の再生で何度も差し替わるので、そのたびに大きく出ては読めなくなります。
- 色は赤にします。 「放っておくな」ではなく「今こうなっている」を言うものですが、一目で異常だと 分かる強さが要るためです。絵の濃淡の上でも字形が切れないよう、白いふちを付けます。
- 絵文字ではなく文字にしたのは、色を選べないからです。 カラー絵文字は自前の色を持つため色の指定が
効かず、乗算(
tint)では暗く沈むだけです。 - 出すかどうかを決めるのはワールド側の段の名前です。UI は「意識が
unconsciousの段に居たら覆いを 出す」とだけ知っていて、何がどれだけ意識を奪ったかは知りません (VitalsSystem.md6 節)。文言はLocalization.mdstage_texts節から引きます。 - 入り切らない文言は幅に合わせて縮めます。 言語によって長さが大きく違う(「気絶」と
unconscious) ためで、長い語ほど大きく出る効果が薄れます。短い言葉を選ぶことが、そのまま伝わる強さになります。
10. 製作中オブジェクトのカード
まだ出来上がっていないもの(RecipeSystem.md の製作中オブジェクト)は、
絵に青をかぶせ、枠も青にしたカードで出します(COLOR.cardInProgress と、枠の blueprint)。
青写真です——設計図はあるがまだ物になっていない、という状態そのものを指す図像なので、覆いの青と
枠の青が同じことを言います。
- 枠の青は種別を覆います。 作りかけの籠は入れ物の青緑ではなく青写真の青を持ちます。まだその物に なっていないので、何になる予定かより先に「まだ物ではない」が読めるべきだからです。 キャラクタの青(2 節)より彩度を上げて、並んだときに混ざらない差を取ります。
- どのレーンに並んでいても同じ見た目にします。 作りかけかどうかは物の型が決めることで、地面に
据わっているか手に持っているかとは関係がないためです。判定は
wipタグ(RecipeSystem.md5 節)だけで、レーンは見ません。製作中オブジェクトは用途のタグ(2.1 節)を持たない(同 5 節)ので、 種別の見た目と競合しません。 - そのため製作中の設置物には土地の背景(7 節)を敷きません。青の覆いが読めるだけの無地の地が 要るので、「今その土地に在るもの」を表す景色より覆いの方を優先します。
- 覆いは絵だけを覆い、枠には掛けません。名前と状態のバーは枠の側にあるので、何が出来つつあるのかと、 それが今どういう状態なのかは覆いの下へ沈まずに読めます。
10.1 作りかけのカードが出す 2 本のバー
上に材料の充足率、下に工程の進捗を出します(8 節の桟の中)。
- 材料の充足率は、今の工程が要求する分だけを数えます。 満ちた時点が「作業する」を押せる時点と
一致します(どちらも同じ
currentStepSupplyRatioを見ます)。残りの工程まで数えると、今すぐ作業できるのに 満たないバーが出てしまいます。道具(consume: false)も数に入れます——揃っていなければ工程が 進まないのは素材と同じだからです。要求数を超えて入っている分は数えません。 - 材料のバーだけは、カードだけで「今作業できるか」を答えます。 同じことは材料の枠の
2/6も 言っていますが、そちらは材料の枠(13 節)を開かないと見えません。満ちる側をgoodにして、 緑=作業できる、で読めるようにします。 - 工程の進捗は、
gaugeを宣言したプロパティfinished_steps(inProgressObjects.ts)が出します。 工程を 1 つ終えるたびに 1 増える純粋な回数で、工程が 2 つ以上のレシピにだけ生成されます。工程が 1 つのレシピでは、最初の作業でそのまま完成してカードが入れ替わるので、そもそもこのプロパティ自体を 生成しません——「gaugeを宣言したプロパティが無ければバーも出ない」という 1 つの規約だけで 済むので、UI 側は工程数を知りません。常に空のバーは「まだ進んでいない」と「進みようがない」を 区別できず、カードの情報量を減らします。- 完成そのものは、工程が宣言した仕事の量で動く別のプロパティ
progressがrange.maxへ届くこと (on_max)が起こします(advanceCraftingが工程まるごとを一度に進める)。finished_stepsは 表示専用の回数で、工程の長さが不揃いでも「終えた工程の数 ÷ 全工程数」がそのまま割合になります。
- 完成そのものは、工程が宣言した仕事の量で動く別のプロパティ
- 進捗のバーは良し悪しではなく「ここまで終えた」量なので、両端とも
neutralを宣言します (8.1 節)。値によらず 1 色になり、着手前の 0 を赤で急かしません。 - 絵は完成品のものを映します。 製作中の型はレシピから自動生成されるので
その型あての絵は用意できず、完成品の絵だけが「何が出来つつあるのか」を出せる唯一の出所です
(名前も同じ理由で
variation_namesのrecipeを使って完成品から組み立てます)。作りかけで あることは覆いだけが示します。 - 実行時の
setTintは使えません(Canvas レンダラで無言で効かない、DesignNotes.mdPhaser の WebGL 専用機能節)。型ごとに染めた絵を焼く 手もありますが、製作中オブジェクトはレシピから自動生成されるので レシピの数だけ絵が要ります。重ねる覆いなら 1 枚も要りません。
11. 枠(セル)は一級の単位
レーンが並べるのは枠(LaneCell)で、カードはその中身です。 位置=添字なので、枠ごとの装飾を
位置で指せます。渡し方も枠の配列 1 本で、カードの並びと枠数の 2 本立ては持ちません。
cells: readonly {
card?: CardContent; // 居なければ空き枠
accepts?: CardContent; // その枠が受け入れる物(名乗る枠なら、今出している1つ)
borderColor?: number; // 枠の縁を染める色
overlay?: string; // 「2/3」など、カードに重ねる表示
}[];
枠は背景 → カード → 重ねる物の 3 層で描きます。空き枠の破線と縁の色は 1 層目、重ねる文字は 3 層目です。
どちらもカードが入った枠で隠れてはいけない(縁を塗りにしないのはそのため)のですが、隠れない ようにする手立てがあります。縁はカードの外側を回ることで、文字はカードより手前へ置く ことで隠れません。縁が前者を選ぶのは、手前へ上げるとカードが持つスタック数の丸(6 節)を 横切ってしまうためです。数字はそのカードが何個かを表すもので、枠がどれかより先に読めるべきなので、 縁の方が下がります。
縁の太さはカードの矩形のすぐ外側の余白——カード間ギャップの半分であり、レーンの左右の余白とも 同じ——をちょうど埋める幅です。隣のカードにも掛からず、レーンの端でも切れません。
受け入れる物を名乗る空き枠は、紙の代わりにその物のカードを薄く敷きます(accepts)。
何を入れる枠なのかは名前と絵が一番早く伝えるためです。破線は薄めずに重ねます——薄めると「まだ
空いている」ことが読み取れなくなります。当てはまる型が複数あるなら、1 秒ごとに順に送ります
——どれか 1 つを選んで出すと、その型でなければ入らないように見えてしまいます。速さの理由は
PlayScene の EMPTY_CELL_CYCLE_MS が持ちます。
名乗るのは、同じ並びの中で枠によって受け入れの宣言が違うときだけです(typesShownInEmptyCells)。
どの枠も同じものを受けるなら、そこへ何が入るかはその場所そのものが既に言っているので、枠が言っても
二度目の答えになります——手持ちの空き枠に持ち物の絵を順に送っても、何も絞られません。宣言が枠ごとに
違う並びの例が炉の火床で、火の中の枠と石の上の枠が別のものを受けます
(FireSystem.md 1.1 節)。当てはまる型がまだ 1 つも無い枠は、敷くものが
無いので紙のままです。
これらは枠の情報であって、カードの情報ではありません。 Card は設置物・アイテム・手持ち・装備・
怪我・レシピ一覧のすべてが通る共通のクラスなので、1 つの画面の都合で CardContent を広げると、使わない
フィールドを全レーンが運ぶことになります。Card は枠のことを何も知りません。
枠の並びは slotCells(view/slotCells.ts)が作ります。縁の色と重ねる文字を持つのは材料の枠
(13 節)だけで、それ以外のレーンはスロットの宣言をそのまま形にした枠(plainCells)——カードの
入った枠を前から並べ、そのあとに末尾の受け皿(12 節)ぶんの空き枠を足したもの——が並びます。
12. 末尾の受け皿(枠が増えるレーン)
落とせば枠が増えるスロットでカードを受け入れるレーンは、並びの末尾に破線の空枠を 1 つ出します。 アイテムレーン・設置物レーンと、受け入れるスロットの子ウィンドウ(装備)が対象です。どのレーンかで 例外は作りません——出すかどうかを決めるのはスロットの宣言(枠が増えるか・受け入れるか)だけです。
枠数を宣言していないスロットは、カードを落とすたびに枠が 1 つ増えます(cell_count なし、
SlotSystem.md 3 節)。末尾の空枠はその増える先で、ここへ落とすと並びの
末尾へ入ります。1 つだけなのは、一度に増える枠が 1 つだからです。中身が空のときは 1 枚も描かれない
ので、そこがドラッグ&ドロップを受け付ける場所だと分かるのもこの枠の役目です。落とし先の枠も、隙間を
示す細い縦帯ではなくこの空枠そのものを塗って示します。
枠数を宣言したスロット(手持ち・入れ物の中身)は落としても枠が増えず、空き枠が常に見えているので、 この空枠は出しません。受け入れないスロット(怪我)にも出しません——出せば「落とせる」と誤って伝える ことになるためです。
一度に見える数(4枠)を超える枠を持つスロットも、枠数のぶんだけ並べます(編み籠の中身は10枠)。 見える数はウィンドウの幅の話でしかなく、入り切らない枠は横スクロールで送れるので、「あと何種類 入るか」は覗いて分かります。頭打ちに掛かるときは次の枠の頭を覗かせて、まだ続くことを示します。
13. 製作中オブジェクトの材料の枠
製作中オブジェクトの材料スロット(RecipeSystem.md)だけは、枠の並びを
残りの要求から組み立てます(materialCells)。
- 枠は残りの工程が要求する型ごとに 1 つで、その型がまだ 1 つも入っていなければ空き枠になります。 要求の合計数ぶん空き枠を並べません——3 本要る枠が 3 つ並ぶと、置ける場所が 3 か所あるように見えます。
- 空き枠には、その枠が要求する型のカードを薄く敷きます(
accepts、11 節)。 何を探してくればよいかが、枠を見るだけで分かります。 - あと何枚要るかは、枠に重ねた「今の数/要求数」が出します。 他に書ける場所がありません。
1 つしか要らない枠には出しません——枠そのものが既に言っていることの繰り返しになるためです。
「今の数」は、その要求へ当てられた数です(
GameElementDefinition.md13.1 節)。枠ごとに数え直すと、尖った石 1 つがcutting_toolとsharp_stoneの両方で数えられ、 どちらの枠も満ちて見えるのに作業は始まらない、という形になります。 - 入っている物の縁の色と数は、その物が当てられた要求の枠に出します(
materialOf)。型からは 引きません——尖った石はcutting_toolにもsharp_stoneにも当てはまるので、型から引くと、石斧が 刃物を受け持って石がsharp_stoneへ当たっているのに、石の枠に刃物の印が出ます。 物はまず入れた枠の要求へ当たるので(GameElementDefinition.md13.1 節)、置いたとおりで揃うなら 印も置いた枠のとおりです。束の中身が 2 つの要求に分かれて当たったときは(置いたとおりでは揃わず 振り替えたとき、別々の工程で当たったとき)、多く当たっているほうを、同じ数なら今の工程の要求を採ります。 子ウィンドウへ貸し出して待ち印だけが残った枠も、待っている個体で引くので印を保ちます。 どの要求にも当たっていない物は、印を持たない枠に入ります。 - 今の工程で要る枠と、後の工程で要る枠を、縁の色で分けます(暖色が今、寒色が後)。どちらから 埋めればよいかだけが読めればよいので、色相の違いに留めます。
- 出番の終わった型の枠は並びから消えます。 こぼしたあとの空枠が残っていると、まだ何か入れられる
ように見えてしまうためです(
spillUnneeded)。 - このレーンへ落としたカードは、落とした位置によらず型の合う枠へ入ります(
slotPositionAt)。 並びがスロットの枠をそのまま映していないので、レーン上の位置はスロットの中の位置になりません。 同じ理由で、このレーンの中での並び替えもしません。
14. 出来事は、起きた札の上に文字で浮かぶ
世界の形が変わらない出来事は、それが起きたオブジェクトの札の上に短い文字が浮かんで消えます
(signalLabel、GameElementDefinition.md 9.8 節の signal)。
武器を重ねて外した回はカードも値も動かないため、これが無いと操作そのものが効かなかったように見えます。
- 場所はその札の上です。 輪郭の明滅(3 節)と同じ理由で、レーンに並ぶ 1 枚に起きたことは、 ステータスエリアではなくその札の上で言います。文字は札の中ほどから立ち上がって薄れます。
- どの札に出すかもワールドが決めます。 効果が指した対象(同 9.8 節)がそのまま出す先で、UI は 「宣言した型の札」といった読み替えをしません——殴って外した出来事は殴られた側の札に出ます。
- UI は出来事の種類を知りません。 出す語はワールドが告げた識別子から locale が引くので
(
Localization.mdsignal_texts節)、告げる出来事が増えても UI は 変わりません。動物ごとの分岐名も、当たり外れの区別も UI 側には持ちません。 - 語は全角 4 文字までにします。 カードの幅(205u)に対して文字は 52u——名前(16u)よりずっと 大きく取るのは、一瞬しか出ないものを読みに行かずに済ませるためです。
- 押さえ込む値は持ちません。 出来事が起きた回にだけ出るので、続けて起きれば続けて出ます。
- いつ出るかもワールドが決めます。 経過を実時間で見せている最中に告げられた出来事は、その tick の
控えと一緒に出ます(
recording.ts)。だから、時間が過ぎる前に告げる出来事(announce、GameElementDefinition.md11.6 節)は、その時間を見せ始める ところで出ます——押した覚えの無い数時間が過ぎるときに、理由が先に出るのはこのためです。
15. 加熱されている札は、絵を覆って残り時間を出す
火にかかっているものは、絵の上に熾色の覆いをかけ、その上に「変わるまでの残り時間」と進み具合の
バーを出します(CardContent.cooking、FireSystem.md 7 節)。焼き上がる
肉も、熱を溜めて焼け石になる石(同 9.1 節)も、同じ 1 つの覆いで出ます。
絵は隠れてかまいません。 火にかけた物は炉の中で暗く沈んで見えないもので、そこで読むべきは姿では なく数字だからです。覆いは、その数字を絵の濃淡から浮かせる下地も兼ねます。
なぜ桟のバー(8 節)ではないのか
桟のバーは常に出ます。 加熱は料理の間だけの一時の状態で、しかもほとんどの料理は一度火にかけたら 最後まで通します。常時出す枠は、腐敗のようにいつ見ても意味のある値のために空けておきます。
加熱で読みたいのは割合ではなく絶対値です。 焼き上がりを過ぎると焦げる(同 7.2 節)ので、要るのは 「あと何分か」で、バーだけでは答えられません。数字を出す場所は桟には無く、絵の上にしかありません。
どの値を見るかは、名前ではなく宣言が決める
画面はプロパティの名前を 1 つも名指ししません。 進みを受ける名前は 1 つではなく(料理は
cooking_progress、石は heat_soak)、名前で選ぶと、火の中で変わる物を足すたびに画面を直すことに
なります。覆いに回すかどうかは、その値の宣言だけで決まります。
gauge(8 節)を宣言していない値。 桟のバーで常時見せると宣言した値は、そちらが言います ——焼け石に残っている熱も、生の甕に残っている水気(SurvivalItems.md4 節)もバーを持ちます。同じ値を 2 か所で言いません。生の甕は 1 枚でこの分かれ目をそのまま見せます ——焼成の進み(cooking_progress)が覆いに、水気(moisture)がバーに出ます。- 進み切った先を著者が書いている値(
on_max、GameElementDefinition.md6.3 節)。rangeを書いただけの値は 上限で丸められるだけで、着いても何も起きません——炉の火力(heat)がそれで、上限へ育つ途中に 「あと何分」を出しても、指す出来事がありません。
2 つ以上が同時に進んでいたら、先に変わるほうを出します。 数字が答えているのは「次に何が起きるのは いつか」なので、変わってしまう前に気付ける側を出します。
出すかどうかは、場所ではなく「今進んでいるか」
UI は「炉の中にあるか」を見ません。 加熱を進めているのは炉の火力の段が宣言した寄与
(FireSystem.md 7 節)で、
その寄与が今効いているかどうかがそのまま答えになります。火から出せば寄与が外れ、火が消えても外れ、
どちらの場合も覆いは消えます。炉のスロット名も火力の段も、画面側は知りません。
残り時間は、その寄与のまま進んだときに max へ届く(on_max が起きる)までの tick 数から
出します(PropertyValue.ticksUntilMax)。火力が変われば残り時間も変わりますが、毎 tick
引き直すので勝手に追随します。
数字は「あと何 tick か」ではなく「その tick が回るまであと何分か」です(World.minutesUntilTick)。
加熱が進むのは tick が回る瞬間だけなので、焼き上がる時刻は必ず tick の格子の上にあり、そこから
今の時刻を引いたものが残り時間になります。
このため、tick の間で時間が進むと、加熱がひとつも進んでいなくても残り時間は減ります。5 分の行動を 挟めば 120 分は 115 分になります——焼き上がる時刻は動いていないのだから、これが正しい減り方です。
同じ tick に入れた物どうしは残り時間が揃います。 00 分に入れた肉と 14 分に入れた肉は、どちらも同じ 瞬間に焼き上がるので、同じ数字を出します。数字が指しているのは「この物がどれだけ焼けたか」ではなく 「いつ変わるか」です。
中で焼けているものは、抱えている札まで上がる
火にかけた物は炉を押して開くまで見えません。 そこにだけ出していると、開かないあいだに焦げます。 そこで、中で一番早く変わるものの残り時間を炉の札にも出します——出血の印が負っている本人まで 上がるのと同じ理由(9.0 節)で、辿るのは直下の子までです。
バーは良し悪しを言わない 1 色
生 → 焼きは完成へ、焼き → 焦げは劣化へ進みますが、その進み先が完成なのか焦げなのかを画面は
知り得ません(on_max が生む型が良いものかは判定できない)。片方だけ赤くする規則は書けないので、
工程の進捗(10.1 節)と同じく両端 neutral の 1 色にします。焦げが近いことは、色ではなく数字が言います。
16. 筏の札は、今の積載での推定日数を桟へ出す
積み下ろしの間、今の積載で本土まで何日かかるかを、幅のまま筏の札の桟へ 1 行の文字で出します
(GameEndings.md 9.3 節)。足りるかどうかは言いません。
出す先が筏の札なのは、積み下ろしをしているのが筏だからです。積荷を並べるのは筏のスロットへの操作
なので、そのあいだ筏は現在地の札としてピン留めされています(ScreenLayout.md
7.1.1 節)。岸へ繋いである間も設置物レーンに札が出るので、出航前も航海中も同じ 1 枚が答えます。
ステータスエリア(StatusArea.md)はキャラクタ自身のプロパティを並べる場所なので、
別のオブジェクトの値も、割合でない数も入りません。
バーではなく文字にします。 読みたいのは割合ではなく「何日か」という絶対値で、しかも幅を持つ ——1 本のバーでは下限と上限を同時に言えません。数字を出す場所は絵の上(15 節)と桟しかなく、絵を 覆ってよいのは火にかかっている物のように姿を見る必要が無い場合だけなので、桟へ入れます。
数は 2 つの掛け算です(GameEndings.md 12.6 節)。
- 海図が言う残りの海区数(
zones_to_mainland_min/zones_to_mainland_max、Voyage.md3.7 節)。真値は読みません。土台になっている真値は最短の 経路の海区数なので、遠回りの航路(同 3 節)を選べば実際はそのぶん延びます——桟の数字が言うのは 「最短で行けば何日か」で、延びるぶんは海図の粗さではなく針路の選択です。 - 1 海区を渡るのにかかる時間。素の横断時間から、筏の速さの段が引きます
(
Voyage.md3.2 節)。読むのはこれから渡る海区 1 つですが、素の横断時間は どの海区も同じなので、その 1 区間が残り全部を代表します。
日数の幅は、そのまま海区数の幅です(GameEndings.md 12.6 節)。
積載の側に不確かさを持ち込まないため、見積もりに入れる速さは次のように決めます。
- 風は入れません。
wind_remainingが尽きるたびに引き直されるものを入れると、積み下ろしをしていないのに数字が動き、 幅が海図の粗さを表さなくなります。海流と季節風を読めるようになる段階は未実装です (GameEndings.md9.1 節)。 - 組み込んだ部品(帆)は入れます。 部品は海に出れば必ず効く筏の一部で、陸で効かないのは
浜に繋いだ筏に嘘を言わせないためだけの条件です(
Voyage.md3.2 節)。 - 海が配るもの(海流)も入れます。 条件の付かない、その海区の側の事実だからです。
どの海区で測るかは、これから渡る海区です。 海に浮かんでいれば今いる海区、海岸に繋いであれば
出航の卓(set_sail の pick)が立てうる海区で、画面は「出航」という名前を知りません
——自分を型で名指した場所へ移す抽選が出航で、行き先が海区かどうかは行き先が横断時間を持つかで決まります。
重みが 0 の候補=面していない海区は挙がらないので、出航地点ごとの差がそのまま日数の差として読めます
(GameEndings.md 5 節後段)。抽選の候補が 2 つ以上あるときは、両方を
含む幅にします。
刻みは半日で、どちらの端も切り上げます。 分まで出すと海図の粗さ(±5 海区)より細かい数字になり、 半端な半日ぶんの水と食料も要るためです。両端が揃うのは海図の幅が消えたとき(渡り終えた海区)だけで、 そのときだけ 1 つの数になります。
実装は src/game/view/voyageForecast.ts(見積もり)と src/game/looks/timeTexts.ts(文言)、
桟への配置は src/game/ui/Card.ts の CardContent.railText で、実装済みです。プロパティの名前以外は
すべて宣言から引きます——段の境目も、段が引く分数も、海図の幅も、YAML に書かれた値がそのまま出ます。