DQ6 AI処理の解析3

前回からの続きです。

  • SR: $02CF86 AI行動決定用RAM領域初期化
02CF86 LDX #$006A X=#$006A
02CF89 STZ $25F3,X $25F3+X=#$00
02CF8C DEX X–
02CF8D DEX X–
02CF8E BPL #$F9 if(n==off) goto $02CF89
02CF90 LDX #$027E X=#$027E
02CF93 STZ $FD00,X $FD00+X=#$00
02CF96 DEX X–
02CF97 DEX X–
02CF98 BPL #$F9 if(n==off) goto $02CF93
02CF9A RTS return

ここは初期化をしているだけの処理です。$7E35F3-365E、$7EFD00-FF7FをAI用のテンポラリ領域として使用するようです。

  • SR: $02CF9B AI判断用情報をメモリにセット
02CF9B LDA #$0017 A=#$0017 戦闘中キャラクターの最終インデックス
02CF9E STA $258B $258B=A
02CFA1 LDY #$04AC Y=#$04AC 戦闘中キャラクターアドレスの開始位置の最後(52Byte*23)
02CFA4 LDA $205F,Y A=$205F+Y
02CFA7 AND #$0001 A&=#$0001
02CFAA BEQ #$03 if(z==on) goto $02CFAF 戦闘に参加していなければスキップ
02CFAC JSR $CFBB SR: $02CFBB 戦闘参加者のAI判断用情報をセット
02CFAF TYA A=Y
02CFB0 SEC c=on
02CFB1 SBC #$0034 A-=(#$0034+!c) 開始位置を1キャラクター分前に移動する
02CFB4 TAY Y=A
02CFB5 DEC $258B $258B– インデックスもデクリメントする
02CFB8 BPL #$EA if(n==off) goto $02CFA4
02CFBA RTS return

このSRはAI計算中の典型的な処理の実装がされています。Yに戦闘中キャラクターアドレスの開始位置、$7E258Bにそのインデックスがセットされ、デクリメントしてループしていく、というものです。こういう実装の仕方をしているのは取得したい情報にダイレクトにアクセスするためだと推測します。DQ6には戦闘中キャラクターインデックスを指定すると情報を返してくるSRが多数定義されていますが、A,X,Yレジスタをプッシュしてインデックスから開始アドレスを取得し、必要な情報を返して・・・というのはサイクル数を消費しすぎるということなのでしょう。AI処理中はこの手の処理がそこかしこに出てくるのでこの形を覚えておくと理解が進みます。逆にこれをまねて実装する場合はYの開始位置アドレスをループ中に変えないようにPHY、PLYするなどして値がずれないように注意する必要があります。

  • SR: $02CFBB 戦闘参加者のAI判断用情報をセット
02CFBB JSL $C2E4DC SR: $02E4DC 引数:1#$1A 敵味方判定?
02CFC0 BCS #$08 if(c==on) goto $02CFCA
02CFC2 LDA $205E,Y A=$205E+Y
02CFC5 AND #$FFFB A&=#$FFFB
02CFC8 BRA #$06 goto $02CFD0
02CFCA LDA $205E,Y A=$205E+Y
02CFCD ORA #$0004 Aor=#$0004
02CFD0 STA $205E,Y $205E+Y=A
02CFD3 JSL $C2E4DC SR: $02E4DC 引数:1#$18 生死判定?
02CFD8 BCS #$08 if(c==on) goto $02CFE2
02CFDA LDA $2050,Y A=$2050+Y
02CFDD AND #$FFFD A&=#$FFFD
02CFE0 BRA #$06 goto $02CFE8
02CFE2 LDA $2050,Y A=$2050+Y
02CFE5 ORA #$0002 Aor=#$0002
02CFE8 STA $2050,Y $2050+Y=A
02CFEB LDA $2061,Y A=$2061+Y
02CFEE AND #$FF9F A&=#$FF9F
02CFF1 PHA Push A
02CFF2 JSL $C2E4DC SR: $02E4DC 引数:1#$10 毒状態取得
02CFF7 ASL A<<1
02CFF8 ASL A<<1
02CFF9 ASL A<<1
02CFFA ASL A<<1
02CFFB ASL A<<1
02CFFC ORA $01,S Aor=Stack($01)
02CFFE STA $2061,Y $2061+Y=A
02D001 PLA Pull A
02D002 JSL $C2E4DC SR: $02E4DC 引数:1#$00 現HP取得
02D007 STA $2030,Y $2030+Y=A
02D00A JSL $C2E4DC SR: $02E4DC 引数:1#$08 現MP取得
02D00F STA $2032,Y $2032+Y=A
02D012 JSL $C2E4DC SR: $02E4DC 引数:1#$1C 最大HP取得
02D017 STA $203C,Y $203C+Y=A
02D01A RTS return
  • SR: $02D01B 各キャラクター用AI判断用情報セット
02D01B JSL $C2E939 SR: $02E939 引数:1#$00 ターゲット情報セット?
02D020 JSL $C2E966 SR: $02E966 引数:1#$06 ターゲット情報セット?
02D025 LDA $00 A=DP($00)
02D027 STA $04 DP($04)=A
02D029 LDA $02 A=DP($02)
02D02B STA $06 DP($06)=A
02D02D JSL $C2E966 SR: $02E966 引数:1#$2C ターゲット情報セット?
02D032 LDA $00 A=DP($00)
02D034 ORA $02 Aor=DP($02)
02D036 BEQ #$08 if(z==on) goto $02D040
02D038 LDA $00 A=DP($00)
02D03A STA $04 DP($04)=A
02D03C LDA $02 A=DP($02)
02D03E STA $06 DP($06)=A
02D040 LDA #$0000 A=#$0000
02D043 STA $258B $258B=A
02D046 TAY Y=A
02D047 LDA #$0000 A=#$0000
02D04A STA $203E,Y $203E+Y=A
02D04D STA $2040,Y $2040+Y=A
02D050 STA $2042,Y $2042+Y=A
02D053 STA $2044,Y $2044+Y=A
02D056 STA $2046,Y $2046+Y=A
02D059 STA $2048,Y $2048+Y=A
02D05C STA $204A,Y $204A+Y=A
02D05F LSR $06 DP($06)>>1
02D061 ROR $04 DP($04)>>1
02D063 BCC #$4E if(c==off) goto $02D0B3
02D065 LDA $2050,Y A=$2050+Y
02D068 AND #$0008 A&=#$0008
02D06B BNE #$37 if(z==off) goto $02D0A4
02D06D LDA $205D,Y A=$205D+Y
02D070 AND #$0080 A&=#$0080
02D073 BNE #$2F if(z==off) goto $02D0A4
02D075 LDA $2052,Y A=$2052+Y
02D078 AND #$0002 A&=#$0002
02D07B BNE #$27 if(z==off) goto $02D0A4
02D07D LDA $2050,Y A=$2050+Y
02D080 AND #$00E0 A&=#$00E0
02D083 BNE #$1F if(z==off) goto $02D0A4
02D085 LDA $204E,Y A=$204E+Y
02D088 AND #$0040 A&=#$0040
02D08B BNE #$17 if(z==off) goto $02D0A4
02D08D LDA $2055,Y A=$2055+Y
02D090 AND #$00C0 A&=#$00C0
02D093 BNE #$0F if(z==off) goto $02D0A4
02D095 JSR $D0C2 SR: $02D0C2 モンスターAI判断用情報セット
02D098 CLC c=off
02D099 ADC #$0004 A+=(#$0004+c)
02D09C BCC #$03 if(c==off) goto $02D0A1
02D09E LDA #$FFFF A=#$FFFF
02D0A1 STA $204A,Y $204A+Y=A 脅威値合計をセット
02D0A4 LDA $2030,Y A=$2030+Y
02D0A7 CLC c=off
02D0A8 ADC $204A,Y A+=($204A+Y+c)
02D0AB BCC #$03 if(c==off) goto $02D0B0
02D0AD LDA #$FFFF A=#$FFFF
02D0B0 STA $203E,Y $203E+Y=A
02D0B3 INC $258B $258B++
02D0B6 TYA A=Y
02D0B7 CLC c=off
02D0B8 ADC #$0034 A+=(#$0034+c) 開始アドレスを次のキャラクターに移す
02D0BB TAY Y=A
02D0BC CMP #$04AD A>=#$04AD?
02D0BF BCC #$86 if(c==off) goto $02D047
02D0C1 RTS return
  • SR: $02D0C2 モンスターAI判断用情報セット
02D0C2 LDA $2051,Y A=$2051+Y
02D0C5 AND #$01FF A&=#$01FF
02D0C8 CMP #$0100 A>=#$0100?
02D0CB BCC #$04 if(c==off) goto $02D0D1
02D0CD LDA #$0014 A=#$0014
02D0D0 RTS return
02D0D1 LDX #$000B X=#$000B
02D0D4 JSL $C00C7A SR: $000C7A A(2B)=A(1B)*X(1B)
02D0D8 TAX X=A
02D0D9 LDA #$0000 A=#$0000 脅威値合計のリセット
02D0DC PHA Push A
02D0DD JSL $C2C168 SR: $02C168 呪文かき消し環境か(該当c=on)
02D0E1 BCS #$1E if(c==on) goto $02D101
02D0E3 LDA $2052,Y A=$2052+Y マホトーン中か
02D0E6 AND #$0008 A&=#$0008
02D0E9 BNE #$16 if(z==off) goto $02D101
02D0EB LDA $2032,Y A=$2032+Y MPが0か
02D0EE BEQ #$11 if(z==on) goto $02D101
02D0F0 LDA $C8F19B,X A=$08F19B+X 呪文脅威値取得
02D0F4 STA $2040,Y $2040+Y=A
02D0F7 CLC c=off
02D0F8 ADC $01,S A+=(Stack($01)+c)
02D0FA BCC #$03 if(c==off) goto $02D0FF
02D0FC LDA #$FFFF A=#$FFFF
02D0FF STA $01,S Stack($01)=A
02D101 LDA $205D,Y A=$205D+Y 踊り封じ中か
02D104 AND #$0040 A&=#$0040
02D107 BNE #$11 if(z==off) goto $02D11A
02D109 LDA $C8F19D,X A=$08F19D+X 踊り脅威値取得
02D10D STA $2042,Y $2042+Y=A
02D110 CLC c=off
02D111 ADC $01,S A+=(Stack($01)+c)
02D113 BCC #$03 if(c==off) goto $02D118
02D115 LDA #$FFFF A=#$FFFF
02D118 STA $01,S Stack($01)=A
02D11A LDA $C8F19F,X A=$08F19F+X ブレス脅威値取得
02D11E STA $2044,Y $2044+Y=A
02D121 CLC c=off
02D122 ADC $01,S A+=(Stack($01)+c)
02D124 BCC #$03 if(c==off) goto $02D129
02D126 LDA #$FFFF A=#$FFFF
02D129 STA $01,S Stack($01)=A
02D12B LDA $C8F1A1,X A=$08F1A1+X 打撃攻撃脅威値取得
02D12F PHA Push A
02D130 LDA $2052,Y A=$2052+Y
02D133 AND #$00F0 A&=#$00F0 マヌーサ系にかかっているか
02D136 BEQ #$05 if(z==on) goto $02D13D
02D138 LDA $01,S A=Stack($01)
02D13A LSR A>>1 命中率が50%になっているので直接攻撃脅威値を半減
02D13B STA $01,S Stack($01)=A
02D13D PLA Pull A
02D13E STA $2046,Y $2046+Y=A
02D141 CLC c=off
02D142 ADC $01,S A+=(Stack($01)+c)
02D144 BCC #$03 if(c==off) goto $02D149
02D146 LDA #$FFFF A=#$FFFF
02D149 STA $01,S Stack($01)=A
02D14B LDA $C8F1A3,X A=$08F1A3+X その他脅威値取得
02D14F STA $2048,Y $2048+Y=A
02D152 CLC c=off
02D153 ADC $01,S A+=(Stack($01)+c)
02D155 BCC #$03 if(c==off) goto $02D15A
02D157 LDA #$FFFF A=#$FFFF
02D15A STA $01,S Stack($01)=A
02D15C PLA Pull A
02D15D RTS return

ここが1つ目のポイントです。まとめると以下の様な値をセットしています。1キャラクター分の領域にモンスターの状態に応じて値をセットしています。しれっとAI判定中に使われるので正しく理解しておけばなにをやってるかがわかりやすくなります。脅威値、脅威値合計の最大値はどれも#$FFFFでクリップされます。

アドレス 意味 備考
2030-1 現在HP 死んでいたら0
2032-3 現在MP
203C-D 最大HP 回復系で使用
203E-F 現在HP+脅威値合計
2040-1 呪文脅威値 呪文が使えない状態なら0
2042-3 踊り脅威値 踊りを封じられていたら0
2044-5 ブレス脅威値
2046-7 打撃攻撃脅威値 マヌーサ系にかかっていたら半減
2048-9 その他脅威値
204A-B 脅威値合計

「脅威値」というのはあくまで勝手につけた名称で、「そのモンスターが本来持っている強さ」を数値化したものという認識をしています。AI行動決定時に同じHPを持つ異なる種類のモンスターが2体居た場合は、脅威値の大きい方を積極的に倒そうとします。ぶちスライム(脅威値合計:3)とキラーマジンガ(脅威値合計:240)がどちらもHP10だった場合はキラーマジンガを優先して倒そうとする、ということです。ちなみに、各脅威値の上位は以下のようになっています。

  • 打撃攻撃脅威値
キラーマジンガ 240
ゾゾゲル 206
マッスルアニマル 189
ボーンファイター 167
ダークドレアム(C) 159
  • 呪文脅威値
ひだりて 1875 デスタムーア(最終形態)戦でザオリクを使うから?
アクバー 1104
みぎて 739 デスタムーア(最終形態)戦でザオラルを使うから?
ばくだんいわ 618 メガンテを使うから?
メガザルロック 595 メガザルを使うから?
  • 踊り脅威値
デススタッフ 1437
ホロゴースト 501
しれんその1 281
イーブルフライ 110
ホラービースト 92
  • ブレス脅威値
ダークドレアム(B) 228
ダークドレアム(A) 226
デスタムーア 167
じごくのほのお 163
ドラゴラム 133
  • その他脅威値
ダークドレアム(B) 430 雄叫びを使うから?
ヘルクラッシャー 418
デスタムーア 351
デスタムーア 323
デビット 279

モンスターのパラメータを変えた場合にはこちらも変えないとAIが誤判定すると思われます。ちなみにこの値はAIの判定でしか使われていないようですが、モンスターの素の強さから何らかの計算をして算出されたものと思われますが、当然ROM中にはその計算式は存在しないので、どうやって計算しているのかは全くわかりません。たまたまデュランは攻撃力の45%(260*0.45=117)であることを見つけましたが、まじめに計算する場合、行動回数や行動パターンも関係すると思われるため、計算方法はかなり複雑になるのではと思われます。実際の値は$08F19B-に定義されているので気になる人は実際にみてみてください。脅威値と戦闘行動を1行に並べてみてみるといろいろ見えてくるかもしれません。

  • SR: $02D15E モンスター特別種族情報セット?
02D15E LDA #$0004 A=#$0004 4種のモンスターグループについてセットする
02D161 ASL A<<1
02D162 TAY Y=A
02D163 LDA $2567,Y A=$2567+Y モンスターIDを取得
02D166 JSR $D16E SR: $02D16E モンスター特別種族情報セット?
02D169 DEY Y–
02D16A DEY Y–
02D16B BPL #$F6 if(n==off) goto $02D163
02D16D RTS return
  • SR: $02D16E モンスター特別種族情報セット?
02D16E PHY Push Y
02D16F ASL A<<1
02D170 TAX X=A
02D171 LDA $C22B54,X A=$022B54+X
02D175 TAX X=A
02D176 TYA A=Y
02D177 ASL A<<1
02D178 ASL A<<1
02D179 ASL A<<1
02D17A TAY Y=A
02D17B LDA $C20168,X A=$020168+X ここに値がセットされているのはメタル系スライムのみ
02D17F AND #$01FF A&=#$01FF
02D182 STA $264B,Y $264B+Y=A
02D185 LDA $C2016A,X A=$02016A+X すべて0 使っているのか?
02D189 AND #$01FF A&=#$01FF
02D18C STA $264D,Y $264D+Y=A
02D18F PLY Pull Y
02D190 RTS return

処理自体は何をやってるのかはわかりますが、値(#$8C)がセットされているのはメタルスライム・はぐれメタル・メタルキング・合体メタルスライムのみで、何を意図しているかはよくわかりません。今のところ大して重要ではなさそうということでさらっと流しておきます。