前回からの続きです。
- 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)がセットされているのはメタルスライム・はぐれメタル・メタルキング・合体メタルスライムのみで、何を意図しているかはよくわかりません。今のところ大して重要ではなさそうということでさらっと流しておきます。
コメント