# Lowmoor 変更まとめ（1.5.2 → 1.6.0 → 1.6.1）

2026-08-22 作成。Steam が 1.6.0 を配信したのを受けて、旧版（`app.asar.en.bak` = 1.5.2）と
新版を突き合わせて調べた結果と、日本語化側で必要になった対応の記録。

---

## 1. 規模

| | 1.5.2 | 1.6.0 |
|---|---|---|
| `game/index.html` | 2.03 MB / 26,469 行 | **3.97 MB / 43,311 行** |
| トップレベル宣言 | 1,207 | **2,000**（+813、削除20） |
| セーブのスキーマ | v13 | **v17** |

**ゲーム本体がほぼ倍になっています。** 作者の言葉を借りれば
「An update too large for this window: the full patch notes are the v1.6.0 post on Steam」
（このウィンドウに収まらない規模の更新。完全なパッチノートは Steam の v1.6.0 投稿にある）。

ソース中のコメントには `v1.9.0` から `v2.10.0` までの内部版番号が散在しており、
Steam の「1.6.0」は作者の内部リリースを長期間ぶんまとめたものと読めます。

---

## 2. 作者のパッチノート（ゲーム内 PATCH_NOTES より、2026-08-21 付）

> **The Steam bundle: the fight on the die, a signature move for every trade, pets, feats and desks.**

- 戦闘がダイス判定になった。命中判定・ダメージダイス・ステータスブロックを持つ獣。装備は**4つの名前つき枠**に。
- 詠唱者は INT / WIS / CHA から戦う。**全職業に固有の必殺技**があり、その横の「＋」から強化を開く。
- **ペットが3種**（櫃・骨犬・蛭）。連れ歩けるのは1匹で、報酬もその1匹だけ。
- **功績は13個**になり、papers の枠は3。
- **雇い人が次の年代記にランクを持ち越す。** 需品係の庭が上限を上げる。
- 種族と職業の贈り物を作り直し。商店・名誉録・ギルドハウスがいずれも拡張。
- **設定にプレイスタイル切替と3種のデスク**が追加。

---

## 3. データ表の増減（実測）

| 表 | 1.5.2 | 1.6.0 | |
|---|---|---|---|
| モンスター | 179 | 179 | 変わらず（ただし**20種が入れ替わり**） |
| ボス | 31 | 31 | 変わらず |
| 実績 | 100 | 100 | 変わらず（Steam実績と1対1のため固定） |
| 功績（Feats） | 11 | **13** | +2 |
| ギルドハウス増築 | 8 | **13** | +5 |
| 名誉録の行 | 14 | **20** | +6 |
| 雇い人 / 祝祭日 / 呪文 / 技 | 21 / 10 / 32 / 19 | 同左 | 変わらず |

**入れ替わったモンスター20種**（1.5.2 には無かった名前）:

```
warren ratling / copy-thrall / lens-warden / loosed armature / roll-thrall
steam mephit / foundation-slough / blank warden / ground-drake / sum-taker
shale-lizard / moor-skulker / werebear / kennel-brute / harpy
minotaur skeleton / gnoll / sprite / quasit / hippogriff
```

---

## 4. 新しく入った仕組み（ソースから確認できたもの）

| 名前 | 中身 |
|---|---|
| **TECHNIQUES** | 戦技。職業ごとの必殺技とその強化。 |
| **BATTLE_WORDS** | 「戦の言葉」。呪文とは別枠の能力で、`SPELLS` には入らず `gainSpell` も通らないが、**MPの井戸は共有**する。その取り合いが調整の要になっている。 |
| **TREE_\***（約20個の宣言） | 才能ツリー。列は Power / Duration / Mana（内部名 weight / reach / thrift）。格子には字面と段位だけを出し、名前と数値はツールチップに置く設計。 |
| **PREP_\*** | 一つの年代記ぶんだけ買える「準備」。4種が `G.preparations` に入り、この版から保存キーになった。 |
| **rung / forge** | 装備の段位（強化）。 |
| **DREAM_\* / LIGHT_\* / PACT_\* / SEAM_\* / NOVA_\*** | 上記に付随する各種の仕掛け。 |

**新しいメニュー項目 3つ**: `Armoury`（武具庫）／ `Pets`（ペット）／ `Memorial Wall`（慰霊の壁）

**新しい保存キー 5つ**: `lowmoor.desk.v1` `lowmoor.armfilt.v1` `lowmoor.logkinds.v1` `lowmoor.forge.v1` `lowmoor.setup.v1`

---

## 5. 消えた仕組み

`LEANINGS`（常置注文の傾向）、`hwOf` / `noteHighWater`（歴代最高装備の記録）、
`OUTFIT_MAX`、`WAIN_CARRY_STAGES`、`WORKS_REGRADE`、`examinerWord`、`DISPATCH_MAX`、
`ROSTER_ASKS`、`assignHireling2`、`improveItem`、`gradeCost`、`hpRegen`、`coverSvg` ほか計20個。

装備まわりが「4つの名前つき枠」に作り直されたことに伴う整理と読めます。

---

## 6. セーブについて

**スキーマが v13 → v17 に上がりました。** 1.5.2 で進めていた年代記は 1.6.0 では
読めない可能性が高いです。これはゲーム本体の仕様変更で、日本語化とは無関係です。

加えて、日本語版で作った年代記は内部値（種族・職業・装備部位・獣の名前）が日本語に
なるため、英語版に戻すと参照が外れます。版を移る前に `Export Save` でコードを
控えておくのが確実です。

---

## 7. 日本語化側の対応

### 翻訳

| | |
|---|---|
| 新規に訳した文 | **2,012 件 / 54,661 文字**（PLaMo、約25分） |
| 翻訳メモリ | 5,894 → **8,685 件** |
| 新規モンスター20種 | **手訳**（ノール、ハーピー、ヒッポグリフ、ワーベア等） |
| 画面上の日本語比率 | **86%** |
| 残る未訳 | 59 件（JWT・CSSセレクタ・テンプレート断片など、訳してはいけないもの） |

### 更新の過程で見つけて直した不具合 5件

**① 抽出の絞り込みがスクリプトに入っていなかった（こちらの不備）**
`out-fragment-safe` `out2-plain-go` など4種の絞り込みを、その場かぎりのコマンドで
作っていた。そのため更新時に前版のファイルが使われ、1.6.0 で増えたツールチップが
まるごと抽出対象から漏れた。日本語比率が 92% → 68% に落ちて気づいた。
真の未訳は 903 件ではなく 2,012 件だった。4つの抽出スクリプトすべてに組み込み済み。

**② `（CON）` を「残った伏せ字」と誤検出**
伏せ字の検出が「括弧＋大文字1〜3字」を無条件に拾うため、能力値の略号に反応し、
正しく訳せている文を **50件中47件** 突き返していた。1.6.0 で説明文にこの書き方が
増えて表面化。実際に立てた符号（A, B, C…）の範囲だけを見るよう修正。

**③ キーの書き換えが暴走して起動時クラッシュ**
「クォートなしのオブジェクトキーも一緒に訳す」処理の対象が 27 → 237 件に増え、
その中に `CHART_QUARTERS[q.id]` のように**変数で引くキー**が混ざった。変数側は
data から来るので書き換えようがなく、署名した瞬間に落ちた。
参照経路を確かめた11語だけの許可リストに変更（表示の日本語を少し諦める側に倒した）。

**④ CSS が翻訳された**
`"background:var(--desk); margin:0;"` が「背景: var(--desk); マージン: 0;」に。
**lint が検出**（前回入れた検査が働いた）。`prop:value` 形も門で止めるよう拡張。

**⑤ 書き換え規則 4件が空振り**
作者が `log()` に第三引数 `"guild"` を足す、選択待ちボタンに「重要」印を追加する等の
変更を入れていた。すべて追随。その過程で、訳文中の `;` で正規表現が切れて構文エラーに
なる問題も踏んだので、末尾を飲み込まない形に直した。

### 検証結果

| 項目 | 結果 |
|---|---|
| 未捕捉例外 / console.error | 0 件 |
| ゲージ 23 本 | clip不正 0 / 幅ゼロ 0 |
| 複数形 s の残り | 0 |
| 伏せ字の残り | 0 |
| 文字化け・テンプレート式の露出 | 0 |
| LINT / 構文チェック | clean / OK |
| セーブコードの往復 | OK（v17） |

---

## 7.5 追い込み（2026-08-22 追記）

画面を全メニュー巡回して、英語のまま出ている文字列を数える道具（`tools/sweep-en.mjs`）を
作り、残りを潰しました。**291 件 → 17 件**。残る 17 件は「英語のままが正しい」もの
だけです（`30k gp` などの通貨単位、デスクの意匠名 Windows 98 / Mac OS 8 / GNOME 1、
ライセンスの URL）。**画面上の日本語比率は 86% → 92%。**

見つかった原因は、いずれも訳文ではなく**抽出側の穴**でした。

**① テンプレートリテラルの中が丸ごと未抽出**（最大の穴）

```js
`<div class="armstore"><button data-arm-equip="${escAttr(r.id)}">Equip</button></div>`
```

抽出器は「`${` があるテンプレートは触らない」で避けていたため、この `Equip` は
一度も抽出されていませんでした。画面には **1,032 箇所**出ていました。
`tools/extract4.mjs` を新設。`${}` の入れ子を数えてテンプレートを正しく切り出し、
穴を1文字の番兵に置き換えた「仮想HTML」を組み立てて、テキストノードと
`title` / `data-help` 等の属性値だけを取り出します。192 箇所を回収。

**② `${...}` の中の文字列リテラルも未抽出**

```js
`<span class="m">${esc(has ? petGrowthSay(p) : "not yet taken")}</span>`
```

走査がバッククォートからバッククォートまでを一つの文字列として読んでいたので、
穴の中の `"not yet taken"` が見えていませんでした。入れ子を数える走査
（`tools/jsscan.mjs`）で穴の中だけを別に拾うようにし、**310 件**を回収。

**③ 語彙表の差し替えで鍵が 20 個消えていた**（1.6.0 で入った退行）

`lexicon.mjs` は語順のために語彙表を関数ごと日本語で書き下ろす作りですが、
その中身が 1.5.x 時代のままでした。1.6.0 で原文の表が 8 鍵 → 28 鍵に増えたため、
差し替えると 20 鍵が消え、`mechLine` のフォールバックに落ちて

```
+1 keeneye.   +0.03 procCeil.   +2 footing.   +0.75 debuff.   …
```

が贈り物・才能・雇い人のツールチップに並んでいました。4つの表を 1.6.0 の
全鍵ぶん書き直し（世界の語り口と平明な言い方の2声）、さらに
**「差し替えで原文の鍵が失われたらビルドを止める」検査**を入れました。
黙って通っていたのが本当の問題だったので、同じ見落としは次からビルドが拒否します。

**④ 絞り込みの正規表現が「A␣」で始まる文言を全部捨てていた**

SVG パスを除く判定が先頭1文字だけを見ており、`A` が円弧コマンドなので
`A Good Boy` `A Larger Satchel` `A Hundred Lids` といった実績名・強化名が
まとめて捨てられていました。全体がパスの字種の場合だけ落とすよう修正し、
**176 件**の未訳を発掘。PLaMo で訳したうえで、多義語（chest / leg / hand /
stand / yard / quire）の訳語をそろえて 135 件を手で直しました。

**⑤ 綴りで守ると表示側まで巻き添えになる**

`el.style.display = "none"` を守るための門が、`$("chartwrit").textContent = ... : "none"`
まで止めていました。**「位置からして表示だと分かる箇所」は別の門にかける**方式に変更。
対象は、静的HTMLのテキストノードと属性、テンプレートの地の文、
`textContent` / `title` / `placeholder` への代入、表の `eff:` / `line:` の返り値。
ここでは構造用の門（structural-key・identifier-property・browser-api-value）を通さず、
壊れ方の門（タグ不一致・プレースホルダ欠落・CSS値）だけを見ます。

**⑥ 抽出器が4系統になり、同じ場所を二重に置き換えていた**

範囲の途中で終わった置換のせいで構文が壊れました
（`…します。` の後ろに `ne line and never…` が残る形）。重なりを検出して
片方を見送るようにし、見送った件数を出すようにしました。

**あわせて直したこと**

- 回帰テストの既定が `build/index-auto.html` を見ており、それが前バージョンの
  置き土産だったため、直したはずの不具合がテストにだけ出続けていました。
  毎ビルド作り直される `index-test.html` を見るように変更し、古い成果物は削除。
- `mkapp.mjs` を追加。検証用の派生ファイルをビルドごとに作り直します。

### この回の検証

| 項目 | 結果 |
|---|---|
| 未捕捉例外 / console.error | 0 件 |
| ゲージ 23 本 | clip不正 0 / 幅ゼロ 0 |
| 文字化け・`${}` の露出・実体参照の混入 | 0 |
| 画面上の日本語比率 | **92%**（前回 86%） |
| 画面に残る英語 | 17 件（すべて意図的） |
| LINT / 構文チェック | clean / OK |
| セーブコードの往復 | OK（v17） |

---

## 8. 反映

```powershell
cd C:\Users\Administrator\Documents\Lowmoor\release
.\install-ja.ps1
```

次回以降の更新手順は `更新の手順.md` を参照。

---

## 9. 1.6.1 対応（2026-08-22）

Steam が 1.6.1 を配信。**システム側の小規模更新で、文面はほとんど変わっていません**
（新規に訳す必要があったのは 102 件・5,774 文字だけ）。そこでこの回は、
残っていた英語の掃き出しと誤訳の修正に充てました。**画面上の日本語比率 86% → 93%。**

### 直した不具合（道具側）

**① 退避（app.asar.en.bak）が更新前のまま残り、原文を失った**

`install-ja.ps1` が「退避が既にあれば何もしない」だったため、1.6.0 が配信されて
app.asar が新しい英語版に入れ替わったあとに日本語版を入れると、退避は 1.5.2 のまま、
1.6.0 の英語原文は上書きされて消えました。1.6.1 を訳そうとして初めて気づき、
Steam の整合性確認で取り直すことになりました。

- `install-ja.ps1`: 退避は「英語版で、かつ今の退避より新しい版」のときだけ取り直す。
  古い退避は `app.asar.en.<版>.bak` として残す。日本語版が入っているときは触らない。
- `update.mjs`: 読もうとしている原文が手元の `app/` より古ければ、展開せずに止める。

**② `extract4.mjs`（テンプレート内の地の文）が更新手順に入っていなかった**

前の版の `out4-tpl.json`（＝古いオフセット）がそのまま使われるところでした。
抽出物が原文より古ければビルドを止める検査も追加。

**③ 巡回ツールが「日本語混じりの文」を訳済みと見なして飛ばしていた**

`看破` と `足場` の説明文が英語のまま残っていたのに検出できなかったのはこれ。
日本語が1文字でもあれば済み、としていたため、途中まで訳された文が丸ごと見えていませんでした。
英語の連なりが実質的に残っているかで判定するよう変更。

**④ 語彙表の差し替えが CRLF で隣の宣言を飲み込んだ**

`MECH_PROCS` の終端を `;
` で当てていたが、原文の改行は CRLF なので `;` の次は `
`。
走査が止まらず、直前に差し替えた `WORLD_PROCS` の末尾まで飲み込み、`return` だけが
宙に浮いて「Illegal return statement」になりました。`;
?
` に修正し、
**「差し替え範囲に別の宣言が入っていたら止める」検査**を追加。

**⑤ `.dataset.help =` を「触ってはいけない dataset」として保護していた**

値がそのままツールチップ本文になる場所なので、名誉録のリセット説明などが英語で残っていました。
`help` / `helptitle` / `helpstats` は保護対象から外し、表示位置として扱うよう変更。

**⑥ 死んだ規則の掃除**

`wintitle` の書き換え規則が、テンプレートの地の文も訳すようになった結果
`Lowmoor` が「ロウムーア」に変わって当たらなくなっていました。訳語に依存しない形に修正。
冒険者票の見出しを力ずくで置換していた 1.5.x 時代の規則 9 件は、通常の経路で
訳されるようになったので削除（当たらない規則を残すと、毎回の警告が雑音になる）。

### 直した誤訳

**能力値・呪文表**（利用者からの指摘）

- `Fires` → 「炎」を **「発動率」**、`Upon` → 「の上に」を **「発動条件」**
- 学派の略称 `Road`（未訳）→ **道行き**、`Stroke` → **一撃**、`Fury` → **猛り**
- **看破・足場の説明文が未訳**だったのを翻訳
- 装備の要約行 `dmg` → **打撃**、`Def` → **防御**

**装備の枠名**

`Armour` / `Accessory` が英語のまま、`Main hand` / `Off hand` は
「メインハンド」「手近なところで」と不揃いでした。この4語は `G.equip` の **キー**で
そのままセーブに入るため、内部の値は触らず、**表の升目に出すときだけ**
鎧／利き手／盾手／装身具に読み替える形にしました（既存のセーブはそのまま読めます）。

**戦闘ログを関数ごと書き直し**

```
旧: あなたが切り開く追い詰められたゼラチナス・キューブのために9、それはあなたに5. It stands at 33 of 42.
新: はぐれのレヴナントに斬りつけ、7。返す一撃を受けて 3。残り 104 / 111。
```

英語は「動詞 目的語」、日本語は「目的語 動詞」。断片ごとに訳しても語順は直らないので、
9 つの分岐すべてを日本語の語順で書き下ろしました（世界の語り口／簡明な言葉の二声とも）。

**用語のブレ 73 か所**

原文がゲーム内の意味で使っていて、かつ訳文に誤読が出ているものだけを直しました
（"lace by the yard" の「ヤード」や "in your chest" の「胸」は正しいので触っていません）。

| 原語 | 誤訳 | 修正 |
|---|---|---|
| Roll | 振る | 名誉録 |
| rank cap | 階級帽 | 階級上限 |
| hand | 手 | 雇い人 |
| stand | 舞台装置・立札 | 段 |
| the yard | ヤード | 需品係の庭 |
| log | 丸太 | 記録 |
| mimic | 模倣者・擬態生物 | ミミック |
| chest | 胸板 | 櫃 |
| Barracks | バラックス | 兵舎 |
| sending | 送電 | 送り |

ほか、成績表の「獣たちを舐め」→「討ち取った獣」、
「床の障害物除去完了」→「踏破した階層」、保存表示「保存済みちょうど今」→「たった今保存」など。

### この回の検証

| 項目 | 結果 |
|---|---|
| 未捕捉例外 / console.error | 0 件 |
| ゲージ 23 本 | clip不正 0 / 幅ゼロ 0 |
| 文字化け・`${}` の露出・実体参照の混入 | 0 |
| 画面上の日本語比率 | **93%**（前回 86%） |
| 画面に残る英語 | 18 件（通貨単位 gp・デスクの意匠名・ライセンス表記。すべて意図的） |
| LINT / 構文チェック | clean / OK |
| セーブコードの往復 | OK（v17） |
| 翻訳メモリ / 手訳 | 8,856 件 / 1,004 件 |

Steam スレッドへの追記文は `steam-post-1.6.1.md` にあります。

---

## 10. 音が出なくなった不具合（2026-08-24 修正）

**1.6.1 対応版で、ゲームの音がすべて止まっていました。日本語化が原因です。**

### 何が起きていたか

Web Audio と canvas の**列挙値**（ブラウザが綴りを決めている文字列）が訳されていました。

| 場所 | 原文 | 訳されて | 起きたこと |
|---|---|---|---|
| `tone.type = …` ×4 | `"lowpass"` | 「ローパス」 | 代入時に例外。音の組み立てが途中で止まる |
| `audioCtx.state === …` ×3 | `"running"` | 「流れる」 | **永久に偽**。`audioLive()` が常に false になり、`beep()` も `schedule()` も何もせず戻る |
| `function beep(…, type=…)` | `"square"` | 「平方」 | 発振器の既定波形が不正になり例外 |
| `ctx.textBaseline = …` ×3 | `"middle"` | 「中間」 | ブラウザが黙って無視。図表の文字が上下にずれる |
| `ctx.font = …` ×4 | `"px Tahoma, sans-serif"` | 「px Tahoma、サンセリフ体」 | 字体指定が不正になり、図表が既定フォントに落ちる |

とくに 2 番目が効いていました。**例外も lint も出ず、画面も普通に出て、音だけが消えます。**

### なぜ門をすり抜けたか

「ブラウザ API に渡る文字列は訳さない」門は前からありましたが、拾い方が足りませんでした。

- `apiValues` は `{type:"sine"}` のような**オブジェクト直値**と `.style.X =` は見ていたが、
  **`.type = "lowpass"` という代入**は見ていなかった。
- `runtimeCmp` は比較の左辺が `location` / `navigator` / `document` … のときだけ拾う作りで、
  **`audioCtx.state`** は対象外だった。
- `function beep(…, type="square")` の**既定引数**は、どの形にも当てはまらなかった。
- `ctx.font` は `"bold " + n + "px Tahoma, sans-serif"` と**連結で組み立てる**ので、
  リテラル単体（`"px Tahoma, sans-serif"`）を見ても API 値だと分からなかった。

### 直したこと

**門（patch.mjs）**

- **API 代入の範囲**という考え方を追加。`type` / `oversample` / `font` / `textBaseline` /
  `lineCap` など綴りの決まったプロパティへの代入は、**右辺の式ぜんぶ**を触らない範囲にする。
  連結で組み立てる `font` も、これで守れる。
- **状態プロパティとの比較**（`.state` / `.readyState` / `.visibilityState` / `.protocol` …）は、
  左辺が何であれ保護する。
- **値そのものの禁止表**を追加（`sine` `square` `lowpass` `running` `middle` `alphabetic` …）。
  既定引数や変数経由で渡る分は、これで押さえる。小文字ちょうどのものだけなので、
  画面に出る `Center` のような語は巻き込まない。

**検査（二重にした）**

- `patch.mjs` の LINT に 4 パターン追加。**訳されていればビルドが止まる。**
- `tools/apienum.mjs` — ビルド全体を走査して、API の列挙値に日本語が入っていないか見る。
- `tools/audiotest.mjs` — **実際に音が鳴るところまで確かめる。**
  Web Audio のノード生成と `start()` を数え、`beep()` が途中で戻っていないか、
  列挙値の代入で例外が出ていないかを見る。壊れた版で失敗し、直した版で通ることを確認済み。

  ```
  壊れた版: audioLive=false / ノード 0 / start 0   → 失敗
  直した版: audioLive=true  / ノード 4 / start 2   → OK
  ```

- `update.mjs` の手順にこの二つを組み込み。以後の更新では必ず通る。

### 反省

これは「例外も lint も出ないのに壊れている」型の三度目です
（一度目はゲージが真っ白、二度目は窓が小窓になる、今回は音が消える）。
毎回、**その壊れ方を検出する仕掛けを足す**ことで塞いできましたが、
今回のように「比較が永久に偽になる」形は、静的検査だけでは次の綴りで再発します。
そのため今回は、**結果を実際に測る試験**（音が鳴りに行っているか）まで用意しました。
