DQ3 ゲストキャラクター概念の追加6(移動中編3)

1年以上放置していたこのトピックですが、ようやくスマートな解決方法がわかったのでそちらを最終的な解決策として投下してこのトピックは終わらせます。そもそも根本の原因は「特定のキャラクターを抜いたPC*1リストを選択した結果値がパーティ内の実インデックスではなくリスト中のインデックスそのままで返ってくる」ということが問題でした(https://retrogamehackers.net/dq3-guest-004/参照のこと)。このズレを補うために下流まで延々と場当たり的な対処をしていく羽目になりました。これをパーティ内の実インデックスを返すようにすれば下流は一切変える必要はありませんし、余計な対応表を持っておく必要もありません。実は今回お試しでAIを実装する作業の途中で「勇者を抜いて他のPCに設定された作戦情報を表示」「ウィンドウに表示された項目とそのIDとの関係」を調べる必要に迫られたため、時間もじっくりあるということで腰を据えて調べたところ理解が進みました。

通常ウィンドウ表示処理($SR: $032251など)はリターンしてくるとAレジスタにプレイヤーの選択結果がセットされますが、実はウィンドウの設定値で「選択肢リスト中のインデックス」「各選択肢に個別で設定されたインデックス」を返すかなどをコントロールできます。具体的にはウィンドウ構造体($030000-)の2バイト目上位2ビットがそれにあたります(0~3まで)。各設定値はSRアドレスリスト$033BB7-のSR群のインデックスに相当します。0が選択肢リスト中のインデックス、1が各選択肢に個別で設定されたインデックスを返すようになっています(2については不明、3は該当するウィンドウは存在しない)。「どうぐ」コマンドを選んだ時のPCリストのウィンドウ(#$21)には0が設定されているため、「選択肢リスト中のインデックス」が返ってくるわけです。これを1に変えると同時に、このウィンドウの描画部分を変えてやれば基本的には他は何もいじらなくていいということになります。ちなみに現在リリースしているDQ3 K.Mixの実装はこの方法がわからなかったために下流の個別のアイテム実行処理に至るまで「選択肢リスト中のインデックス→パーティ内の実インデックス(必要に応じて選択肢リスト中のインデックスに再変換)」という処理を正直自分でも把握しきれないくらい挟むというとんでもなくマヌケな実装になっています。とはいえver1.0.0リリース直後にいくつかバグ報告を受け、その修正を行って以降この問題関連の不具合の指摘を受けていないので(ゲストが加入するのがごく一部というのもあり)一応は収束したと判断し、再修正したときのバグ発生の危険を考えて今回の実装を反映させることはしません。

ウィンドウに表示するメニュー文字列($03A74A-)は#$00をターミネータとして1バイト文字列でメニューに表示する文字列を表現していますが、どれも先頭に1バイトのIDを持つようになっています。これが「各選択肢に個別で設定されたインデックス」として使用されるものです。ウィンドウ描画SRの各所で使用されているメニュー文字列描画SRの中を見るとわかります。

  • SR: $032B21 メニュー文字列を選択肢として設定
032B21 SEC c=on False
032B22 SBC #$A749 A-=(#$A749+!c)
032B25 TAX X=A False
032B26 LDA $C3A749,X A=$03A749+X
032B2A AND #$00FF A&=#$00FF
032B2D JSL $C33D96 SR: $033D96 選択肢IDセット?

このSRはメニュー文字列の開始アドレスの下位2バイトをAレジスタに引数として取ることを前提としていますが、その先頭1バイトを選択肢IDとして扱っています。ウィンドウの設定によってはこの値が選択結果として返されることになります。これを踏まえて対象のSRを見てみます。

  • SR: $0363EF ウィンドウ_描画SR_0021
036429 JSL $C347A8 SR: $0347A8 PC名取得+選択肢としてセット
  • SR: $0347A8 PC名取得+選択肢としてセット
0347A8 STY $20 DP($20)=Y
0347AA JSL $C42F5E SR: $042F5E 引数:1#$01 引数:2#$06 引数:3#$FF 選択したPCの登録インデックスを得る
0347B1 JSL $C33D96 SR: $033D96 選択肢IDセット?
0347B5 JSL $C1BA53 SR: $01BA53 パーティの先頭キャラの文字列デコード?
0347B9 RTL return

オリジナルでは選択肢IDとしては対象のPCの「登録インデックス」がセットされるようです。「登録インデックス」とは登録所で登録した順番のようなものなので、これをそのまま使うわけには行きません。このSRをコールする直前にDP($06)にはこのPCのパーティ内インデックスがセットされているので代わりにこれをセットしてやればいいということになります。

  • SR: $0363EF ウィンドウ_描画SR_0021
03641D NOP
03641E JSL $C5F07C SR: $05F07C ゲストとして扱うPCか(ゲストc=off)
036422 BCC #$FA if(c==off) goto $03641E
036424 NOP
036425 NOP
036426 NOP
036427 NOP
036428 NOP
036429 JSL $CFE855 SR: $0FE855 PC名取得+パーティ内実インデックスを選択肢IDとしてセット
  • SR: $05F07C ゲストとして扱うPCか(ゲストc=off)(新SR)
05F07C PHX Push X
05F07D LDX $06 X=DP($06)
05F07F JSL $C5E1F8 SR: $05E1F8 PCリストに追加するか(追加するc=on)
05F083 PLX Pull X
05F084 BCC #$0D if(c==off) goto $05F093
05F086 JSL $C3554B SR: $03554B ステータスに応じて文字色を変える?
05F08A LDA #$0000 A=#$0000
05F08D JSL $C33D5C SR: $033D5C 1文字指定して選択肢として扱う?
05F091 SEC c=on
05F092 RTL return
05F093 INC $06 DP($06)++
05F095 CLC c=off
05F096 RTL return
  • SR: $05E1F8 PCリストに追加するか(追加するc=on)(新SR) 省略
  • SR: $0FE855 PC名取得+パーティ内実インデックスを選択肢IDとしてセット(新SR)
0FE855 STY $20 DP($20)=Y
0FE857 LDA $06 A=DP($06)
0FE859 JSL $C33D96 SR: $033D96 選択肢IDセット?
0FE85D JSL $C42F5E SR: $042F5E 引数:1#$01 引数:2#$06 引数:3#$FF 選択したPCの登録インデックスを得る
0FE864 JSL $C1BA53 SR: $01BA53 固定文字列開始アドレス取得(オフセットをYにセット)
0FE868 RTL return

これで全部カバーしているとは思いませんが、一応肝心な部分はカバーしていると思います。とりあえず戦闘中の作戦変更部分の実装はこの方式で実装してあります。AI機能が日の目を見る場合にはこの実装が生きる事になります。

*1:プレイヤーキャラクター