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:プレイヤーキャラクター
コメント