Lab
第一章でのっぱらに降り立ち、影のボスを倒して英雄になったエデニー。次なる目標は楽園の建材集め。第二章のテーマは「古代遺跡の祠」——のっぱらの南端に眠る地下ダンジョンだ。
そして入場フローがバカっぽい。おにぎりを拾おうとしたら地面が崩れて穴に落ちる。「地面がっ──ズドーン!!」とフェードアウトして気づいたらダンジョンにいる、という流れにした。
第一章のボスは「影」という記号的な存在だった。第二章のボスには名前も感情もある。アングラゴーレム——何百年もそこを守り続けた古代の守護者だ。
第二章の技術的な課題は「フィールドとダンジョン、2つのマップを共存させること」だった。既存のコードはフィールドマップしか想定していない。
解決策はgetMap()関数を1つ作ることだった。現在どちらのマップにいるかを返すだけの関数で、既存コードのMAP参照を全部getMap()に差し替えるだけで、移動・描画・インタラクション処理が両マップに対応した。
切替変数は currentMap の1つだけ。追加するたびに分岐が増えるのではなく、1つの変数を変えるだけで世界が変わる。「既存コードへの影響を最小に保ちながら世界を広げる」という設計が決まった瞬間だった。
章の管理も同じ発想で整理した。chapter変数を1〜2で持ち、クリア画面・看板のセリフ・キー処理をすべてこの変数で分岐する。
BGMもbgmModeの文字列で切り替える。'dungeon'モードは低音寄りのsine波で、フィールドとは違う雰囲気を一発で作れる。startBgm('dungeon')の1行で空気が変わる。
ダンジョンはT字分岐構造で、左腕・右腕・最深部に素材を1つずつ配置した。3つ全部を集めた瞬間に自動でボス出現スクリプトが走る。
アングラゴーレムの外見は「石肌・神官ローブ・蒼い悲しい目」。バトル中、涙が落ちるアニメーションが流れる。彼はHP80を持ち、プレイヤーに攻撃してくる。でも見た目と動きが、明らかに「倒されたくない」と思わせない。
勝利ダイアログに、この章で一番時間をかけた9行がある。
エデニーは「金の亡者が気づいたら英雄になってた」キャラだ。素直に泣けない。でも「砂が入った」という言い訳を、クラナも同じように使う。二人が同じ言葉で涙を隠す対になった構造にした。
最後の「楽園、作ったる。のっぱらに。」——これは第一章からエデニーが言ってきた台詞だ。でも今回だけは、その続きがある。「あいつの分も含めて。」それが第二章でエデニーが一番らしくない発言だった。
exitDungeon()の中で洞窟入り口タイル(T.CAVE)を設置するようにした。消費型の仕掛けタイルを再入場の入り口にしてはいけない——というのが教訓。
gameState = 'complete'を発火するコールバックがついていた。chapter変数を確認しないまま走るので、第二章でも同じ看板を読むとクリア画面が出る。chapter === 2のときは短い別セリフを流すだけに分岐。「この先でいつか変わりますよ。私たちが作りますから。」——クラナらしい、静かな一行に変えた。
checkDungeonBossTrigger()を直接呼んでいた。runScriptは非同期的に進行するため、スクリプト実行中にトリガーが呼ばれるとスクリプトごと上書きされてしまう。onDoneコールバックから呼ぶ方式に統一。「スクリプト実行中に直接呼ばない」というルールを徹底した。
第二章で追加した技術要素を振り返ると、共通のパターンが見える。「切替変数を1つ増やすだけで、世界が広がる」設計だ。
currentMapでマップが切り替わる。chapterで章が切り替わる。battleTypeでボスが切り替わる。bgmModeでBGMが切り替わる。どれも「新しい場合を追加するコスト」がほぼゼロだ。
第三章を作るとき、currentMap = 'dungeon2'と書けばいい。chapter = 3にすれば、クリア画面もセリフも切り替わる。「今から追加しやすいように書く」ことは、「今すぐ動く」のと同じくらい大事だと思う。
おにぎりで穴に落ちて始まる第二章に、ダンジョン・素材3個・アングラゴーレムを詰め込んだ。技術面ではgetMap()によるマルチマップ切替・battleTypeによる敵の切替・chapter変数による章管理を実装し、既存コードへの影響を最小に保ちながら世界を広げる設計が定まった。
そしてゴーレムの「砂が入った(俺も)」——金の亡者エデニーが初めて本音を出した瞬間だった。のっぱらに楽園を。あいつの分も含めて。 そう言えるキャラになるまでに、第一章と第二章があった。