だいぶ更新間隔が空いてしまいましたが、例によって「ダウンロード」ページにうpしてあります。今回の変更点は以下のとおり。
- 複合型フィールドの対応
- 他内部的な構造の見直し
DQ5ではモンスターのHP、攻撃力などがなぜか分かれて保存されています(上2ビット、下8ビットなど)。これを直感的に参照・変更できるようにするというのが今回の変更の一番の大きいところです。この実装に絡んで内部構造の見直しも行ったため、実装にずいぶんと時間がかかってしまいました(ただ単にやる気が起きなくて投入時間自体も少なかったのですが…)。エラーチェックがまるでザルなので今後はその辺りのケアもする必要もあるのですがとりあえず現状あまりやる気が起きないので後で対応することにします。
プログラムの変更とは別にDQ5の解析も少し進めました。BRKの引数のバリエーションが多く、引数の数まではそこそこ正確に把握できるようになったものの、依然としてその多くは用途が不明のままです。戦闘部分は大枠は解析が進んだので仲間モンスター加入部分の処理がぼんやりとつかめてきました。あの有名な「ひとしこのみ」の実装も場所が特定出来ました。バグ技というよりはデバッグ用に仕込まれたものというのがわかります。
- SR: 仲間モンスター加入判定(加入c=off)?
| 1041F6 | LDA $208000 | A=$100000 | |
|---|---|---|---|
| 1041FA | LSR | A>>1 | |
| 1041FB | BCS #$2A | if(c==on) goto $104227 | |
| 1041FD | BRK #$0B #$40 #$21 | goto $000F80 #$0B #$40 #$21 | |
| 104201 | BEQ #$3C | if(z==on) goto $10423F | |
| 104203 | LDA $1010 | A=$1010 | |
| 104206 | BEQ #$37 | if(z==on) goto $10423F | |
| 104208 | BRK #$88 #$A6 | goto $000F80 #$88 #$A6 | 待機モンスター数取得 |
| 10420B | LDA $46 | A=DP($46) | |
| 10420D | CMP #$32 | A>=#$32? | |
| 10420F | BCS #$2E | if(c==on) goto $10423F | |
| 104211 | LDA #$00 | A=#$00 | |
| 104213 | BRK #$8A #$DB | goto $000F80 #$8A #$DB | |
| 104216 | LDA $47 | A=DP($47) | |
| 104218 | BRK #$8A #$27 | goto $000F80 #$8A #$27 | |
| 10421B | LDA $100F | A=$100F | |
| 10421E | SEC | c=on | |
| 10421F | SBC $46 | A-=(DP($46)+!c) | |
| 104221 | BCC #$04 | if(c==off) goto $104227 | |
| 104223 | CMP #$08 | A>=#$08? | |
| 104225 | BCS #$18 | if(c==on) goto $10423F | |
| 104227 | LDA $1117 | A=$1117 | |
| 10422A | CMP #$FF | A==#$FF? | |
| 10422C | BEQ #$11 | if(z==on) goto $10423F | |
| 10422E | LDA #$00 | A=#$00 | |
| 104230 | BRK #$8A #$78 | goto $000F80 #$8A #$78 | |
| 104233 | LDX $46 | X=DP($46) | |
| 104235 | CPX $1117 | X==$1117? | |
| 104238 | BEQ #$07 | if(z==on) goto $104241 | |
| 10423A | INC | A++ | |
| 10423B | CMP #$10 | A>=#$10? | |
| 10423D | BCC #$F1 | if(c==off) goto $104230 | |
| 10423F | SEC | c=on | |
| 104240 | RTS | return | |
| 104241 | BRK #$8A #$76 | goto $000F80 #$8A #$76 | |
| 104244 | LDX $46 | X=DP($46) | |
| 104246 | STX $74 | DP($74)=X | |
| 104248 | BRK #$88 #$77 | goto $000F80 #$88 #$77 | |
| 10424B | LDX $46 | X=DP($46) | |
| 10424D | STX $71 | DP($71)=X | |
| 10424F | LDA $7E29EA,X | A=$7E29EA+X | |
| 104253 | AND #$0F | A&=#$0F | |
| 104255 | STA $72 | DP($72)=A | |
| 104257 | TAX | X=A | |
| 104258 | BEQ #$44 | if(z==on) goto $10429E | |
| 10425A | LDA $278153,X | A=$138153+X | |
| 10425E | CMP #$02 | A>=#$02? | |
| 104260 | BCC #$1F | if(c==off) goto $104281 | |
| 104262 | BNE #$DB | if(z==off) goto $10423F | |
| 104264 | JSL $268D0A | SR: $130D0A | 所持アイテムチェック(ひとしこのみ判定) |
| 104268 | BCS #$4F | if(c==on) goto $1042B9 | |
| 10426A | LDA $74 | A=DP($74) | |
| 10426C | ASL | A<<1 | |
| 10426D | TAX | X=A | |
| 10426E | REP #$20 | m=off(A/M:16b) | |
| 104270 | LDA $278182,X | A=$138182+X | |
| 104274 | JSL $00953E | SR: $00153E | 乱数発生?(0~A-1) |
| 104278 | CMP #$0000 | A==or>=#$0000? | |
| 10427B | SEP #$20 | m=on(A/M:8b) | |
| 10427D | BNE #$C0 | if(z==off) goto $10423F | |
| 10427F | BEQ #$38 | if(z==on) goto $1042B9 | |
| 104281 | JSL $268D0A | SR: $130D0A | 所持アイテムチェック(ひとしこのみ判定) |
| 104285 | BCS #$32 | if(c==on) goto $1042B9 | |
| 104287 | LDA $74 | A=DP($74) | |
| 104289 | ASL | A<<1 | |
| 10428A | TAX | X=A | |
| 10428B | REP #$20 | m=off(A/M:16b) | |
| 10428D | LDA $278172,X | A=$138172+X | |
| 104291 | JSL $00953E | SR: $00153E | 乱数発生?(0~A-1) |
| 104295 | CMP #$0000 | A==or>=#$0000? | |
| 104298 | SEP #$20 | m=on(A/M:8b) | |
| 10429A | BNE #$A3 | if(z==off) goto $10423F | |
| 10429C | BEQ #$1B | if(z==on) goto $1042B9 | |
| 10429E | JSL $268D0A | SR: $130D0A | 所持アイテムチェック(ひとしこのみ判定) |
| 1042A2 | BCS #$15 | if(c==on) goto $1042B9 | |
| 1042A4 | LDA $74 | A=DP($74) | |
| 1042A6 | ASL | A<<1 | |
| 1042A7 | TAX | X=A | |
| 1042A8 | REP #$20 | m=off(A/M:16b) | |
| 1042AA | LDA $278162,X | A=$138162+X | |
| 1042AE | JSL $00953E | SR: $00153E | 乱数発生?(0~A-1) |
| 1042B2 | CMP #$0000 | A==or>=#$0000? | |
| 1042B5 | SEP #$20 | m=on(A/M:8b) | |
| 1042B7 | BNE #$86 | if(z==off) goto $10423F | |
| 1042B9 | CLC | c=off | |
| 1042BA | RTS | return |
ざっくりとしか見ていませんが、待機モンスターが50体を超えていたら加入判定はスキップされます。同じ処理が3回繰り返されているのは1体目、2体目、3体目の判定のためのようです。乱数による判定の前に所持アイテムチェックを行い、合致していれば必ず仲間になるようになります。この部分が例の「ひとしこのみ」に相当するわけです。
- SR: $130D14 所持アイテムチェック(ひとしこのみ判定)
| 130D14 | LDA $7E2056,X | A=$7E2056+X | |
|---|---|---|---|
| 130D18 | CMP $268D33,X | A==$130D33+X? | |
| 130D1C | BNE #$0D | if(z==off) goto $130D2B | |
| 130D1E | INX | X++ | |
| 130D1F | CPX #$07 | X>=#$07? | |
| 130D21 | BCC #$F1 | if(c==off) goto $130D14 | |
| 130D23 | REP #$30 | m=off(A/M:16b) x=off(X/Y:16b) | |
| 130D25 | PLY | Pull Y | |
| 130D26 | PLX | Pull X | |
| 130D27 | PLA | Pull A | |
| 130D28 | PLP | Pull P Flag | |
| 130D29 | SEC | c=on | |
| 130D2A | RTL | return | |
| 130D2B | REP #$30 | m=off(A/M:16b) x=off(X/Y:16b) | |
| 130D2D | PLY | Pull Y | |
| 130D2E | PLX | Pull X | |
| 130D2F | PLA | Pull A | |
| 130D30 | PLP | Pull P Flag | |
| 130D31 | CLC | c=off | |
| 130D32 | RTL | return |
ちゃんとRAMの解析はしていませんが、どうやら7E2056が先頭のキャラクターのアイテムアドレスの開始位置なのでしょう。$130D33から6バイトは
| 00 | ひのきのぼう |
|---|---|
| 0A | とがったホネ |
| 91 | しあわせのぼうし |
| 02 | こんぼう |
| 3C | のこぎりがたな |
| 64 | みかわしのふく |
となっているので、この順番にアイテムを持っていると確実に仲間になる、ということのようです。しかし、正直残念グラフィックのDQ5はDQ3や6に比べると「変えてやろう」というモチベーションが段違いで沸かないのでどこまで続くかは未定です。モンスター画像やマップのデコードなんかもひと通りやりたい気はあるのですが、DQ5のグラフィックは圧縮されているとのことでデコードするプログラムを書くのも面倒だなあということでモチベーションは絶賛低下中です。ただ、ここしばらくツールの改良ばっかりやっていたわけですが、とりあえずそこそこ必要な機能は実装できたということでまた解析作業に戻れる感じです。そもそもDQ6やDQ5の解析をやっていたのは「AIの挙動を調べて可能であればDQ3にインポートしたい」「一応SFCのDQの解析はひと通りやりたい」というところから始まっていたのですが、最近改めてAI付きのDQをやって実感したのは「ボス戦はともかく、通常戦闘はAI任せだとボタン連打の白痴プレイになるわなあ」ということです。実装する側としては挑むべき壁としてはなかなか魅力的なネタではあるのですが、やっぱり便利なものがあると使ってしまうのは人間の性なので「実装したとして果たしておもろいんかね」というのが疑問として残ります。FC版のDQ4は自分で操作が一切できなかった+学習機能があったので自分の期待した通りの行動を取ってくれると嬉しかったりしたのですが、「命令させろ」があるのが当たり前の現在となってはAIは単なる自分のコマンド入力の手間を省くためのものに成り下がってしまっているという印象しか持てないので、モチベーションが湧きません。しばらくはゆるゆると作業をしようかと思います。


コメント