TOP > プログラミング関係解説&調査 > 位置変化・向き変え

位置変化・向き変え

当ページの内容は、ある程度のプログラミングの知識を必要とする。
まず、プログラミング関係解説&調査をひととおり読んでいただきたい。

技の中には、攻撃を当てた相手を吹き飛ばしたり、向きを変える(回転)追加効果がついているものがある。
ただし、吹き飛ばしや向き変えは確実に成功するのではない。
また、最終編のオディオモールとピュアオディオのみ、吹き飛ばしと向き変えを無効化する。
確率計算はどのように行われているのか。

結論だけ先に述べると、世界の合言葉は森部様に確率について記されている。

 ・向き変え・吹き飛ばし発動確率:
それぞれ3/4

オディオモール・ピュアオディオは戦えば分かることだけど、
吹き飛ばしや向き変え技は無効。

吹き飛ばしと向き変えだが、実際には以下のようにいくつかパターンがある。

  1. 後方吹き飛ばし
  2. 90度右回転
  3. 180度回転
  4. 使用者後退
  5. 使用者90度右回転
  6. 使用者180度回転

1.~3.は攻撃を当てた相手に対しての効果だが、4.~6.は技使用者に対しての効果である。
1.~4.は味方技の追加効果にもあるが、5.と6.は敵の技のみ(「水神のヒゲむち」「ライブイレイザー」「エッシャー空間」など)。
使用者に対しての追加効果は確実に発動するが、1.~3.の成功率は3/4である。

また、攻撃相手が技のタメ中だった場合、吹き飛ばしと向き変えをされたらタメが解除されるから、成功時はタメ時間に関する処理も行われているはずだ。

では、実際の処理について説明する。
今回は、味方キャラが敵を攻撃して回転または吹き飛ばしをする場合の処理について紹介する。
吹き飛ばしは、座標に関する処理も行うので少々ややこしい。

メモリアドレス

向き変え・吹き飛ばし関連のアドレスは以下の通り。

技データ11

技データ11には命中タイプと向き変え・吹き飛ばしに関する値が入っている。
上2ビットは命中タイプ。

数値
2進数
内容
%00狙撃
%01直進
%10貫通(目標まで)
%11貫通(目標より先まで)

下6ビットは位置変化の値、以下合計値が入る。

数値
2進数
数値
16進数
内容
%000001$01後方吹き飛ばし
%000010$0290度右回転
%000100$04180度回転
%001000$08使用者後退
%010000$10使用者90度右回転
%100000$20使用者180度右回転

攻撃相手に対しての効果は下3ビットの値で判定できることがわかる。

向き・座標・タメ時間

吹き飛ばしや向き変えで変わることになる、座標や向きの値は、以下のアドレスに入っている。
また、敵の場合は敵のサイズが吹き飛ばしで重要なので合わせて記す。
タメ時間は実行中の技にタメ時間がある場合$00以上の値が入り、時間経過で減少、$00になると技が出るが、向き変えや吹き飛ばしに成功するとキャンセルされる。

味方キャラ
味方X座標Y座標向き技関係状態タメ時間
1$00:1C18$00:1C19$00:1C1D$00:1C05$00:1C06
2$00:1C58$00:1C59$00:1C5D$00:1C45$00:1C46
3$00:1C98$00:1C99$00:1C9D$00:1C85$00:1C86
4$00:1CD8$00:1CD9$00:1CDD$00:1CC5$00:1CC6

X座標は左から$00$06。計算値が$00:1CX0 + $1Aに入る。
Y座標は上から$00$06。計算値が$00:1CX0 + $1Bに入る。
向きは、右下向き$00, 左下向き$01, 左上向き$03, 右上向き$04
技関係状態は、$00:通常, $40:タメ時間中, $F0:技を出している最中。

敵キャラ

敵の横幅・縦幅は、敵種類のアドレスに入る。

敵種類横幅縦幅
1$7E:1A04$7E:1A05
2$7E:1A44$7E:1A45
3$7E:1A84$7E:1A85
4$7E:1AC4$7E:1AC5

向きや座標など、敵個体ごとのデータは、n=0~E(敵番号)として以下のアドレスに入る。

アドレス内容
$00:1Bn4向き(右下$00/左下$40/右上$80/左上$C0
$00:1BnAX座標(左から$00$06)※敵の左上マス基準
$00:1BnBY座標(上から$00$06)※敵の左上マス基準
$00:1Bn5技関係状態($00:通常 $40:タメ時間中 $F0:技を出している最中)
$00:1Bn6タメ時間

向き表記が敵と味方では異なる。
敵の場合は味方の表記方法+下4ビットを0で埋める形式。
座標は敵と味方で共通だが、敵はサイズが1×1ではない場合、敵の左上マス位置が座標にあたる。

サブルーチン

後方吹き飛ばし・90度右回転・180度回転が成功するかどうか、判定は分けて行われるのではなく一度に判定される。
攻撃相手がタメ時間のある技のタメ中の場合、吹き飛ばしや回転を成功させるとキャンセルできるが、これについては別途処理が入るので、分けて説明していく。

今回は味方キャラが敵を攻撃して向き変え・吹き飛ばしさせる場合を紹介する。
$7E:9010$7E:9028には、使用中の技データ00~34が入るので、技データ11は$7E:901Bに入るが、「どう回し回転げり」の技データ11は$02、90度右回転の効果があることが記録されている。

以下がサブルーチンで、ここまでで攻撃相手に技が当たることが判定された上で続く。

$C1/CBE3 JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
$C1/6150 PHX                     ;戦闘乱数計算開始
;(戦闘乱数計算処理省略)
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る
;
$C1/CBE6 STA $11    [$00:0311]   ;A(乱数1)を[$00:0311]に書き込み
$C1/CBE8 JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
$C1/6150 PHX                     ;戦闘乱数計算開始
;(戦闘乱数計算処理省略)
$C1/61A4 RTS                     ;サブルーチン戻り
;
$C1/CBEB ORA $11    [$00:0311]   ;A(乱数2)と[$00:0311](乱数1 $EA)で論理和
$C1/CBED AND $7E901B[$7E:901B]   ;Aと[$7E:901B](技データ11)で論理積
$C1/CBF1 AND #$07                ;Aと$07(%0000 0111)で論理積
$C1/CBF3 STA $11    [$00:0311]   ;Aを[$00:0311]に書き込み

$C1/CBE3~が判定処理である。
まず、戦闘乱数を2回生成する。
ここでは乱数1と乱数2としておく。
$C1/CBEB~の処理を見ると、

((乱数1 | 乱数2) & 技データ11) & $07
(※A | Bは論理和、A & Bは論理積)

を計算して、[$00:0311]に書き込んでいる。
この計算値が実質、吹き飛ばし、90度右回転、180度回転の成功判定値である。
どういうことか順に説明する。

乱数1・乱数2は$00(%0000 0000)~$FF(%1111 1111)の8ビットの値である。
論理和は、2進数で見た時、それぞれの位のどちらかに1が立っていれば1、どちらも0なら0とする演算になる。
例えば乱数1が$EA(%1110 1010)、乱数2が$A9(%1010 1001)なら、

%1110 1010
%1010 1001

上から4ビットと6ビットが0、他はどちらも1か、どちらかが1であるから、
$EA | $A9 = %1110 1110 = $EB(%1110 1011)
である。

続いて、$C1/CBEDで、技データ11と論理積を取り、次の$C1/CBF1$07(%0000 0111)と論理積を取っている。
最後の$07(%0000 0111)との論理積を取っていることからして、技データ11の下3ビットの判定であることがわかる。

技データ11の下3ビットは下のように、それぞれの位が後方吹き飛ばし・90度右回転・180度回転に対応している。

内容
%0000 0001後方吹き飛ばし
%0000 001090度右回転
%0000 0100180度回転

例えば、高原の「どう回し回転げり」の技データ11は90度右回転の効果だけがあるから$02(%0000 0010)が入っている。
乱数1・乱数2の論理和の下から2ビット目に1が立っていれば、技データ11と論理積を取っても下から2ビット目に1が残る。
だが、乱数1・乱数2の論理和の下から2ビット目が0だと、技データ11と論理積を取ったら下から2ビット目が0になってしまう。
下から2ビット目に1が残れば、この後、「どう回し回転げり」の回転が成功と判定されることになる。

例として、先程と同じく乱数1が$EA(%1110 1010)、乱数2が$A9(%1010 1001)だと、乱数1 | 乱数2 = $EB(%1110 1011)である。
「どう回し回転げり」の技データ11 = $02を入れて計算すると、

((乱数1 | 乱数2) & 技データ11) & $07
= ($EB & $02) & $07
= $00

$02(%0000 0010)となって、「どう回し回転げり」の回転は成功である。
もしこれがとらわれの男の「いかく射撃」だったら、技データ11 = $06(%0000 0110)、90度回転と180度回転の効果がある。

((乱数1 | 乱数2) & 技データ11) & $07
= ($EB & $06) & $07
= $02

$02(%0000 0010)が答えで、90度右回転に対応した桁・下2ビット目は1が立っているが、180度回転に対応した桁・下3ビット目は0であるから、「90度右回転は成功」し、「180度回転は失敗」となるのである。
後方吹き飛ばしの判定は一番下の1ビットの値で決まるので、もし「アロハリテ」「パンチャマキック」などの後方吹き飛ばしでの乱数判定で、上の計算結果だったとしたら、「乱数1 | 乱数2(=$EB)」の下3ビットが「%011」だから、吹き飛ばしは成功判定になる。

整理してみると、後方吹き飛ばし・90度右回転・180度回転の判定は、1ビットの乱数(01か)を2回生成して論理和を取り、1なら成功、0なら失敗、と判定しているのと同じである。
成功は、「1,1」, 「1,0」, 「0,1」の3パターン、失敗は「0,0」の1パターンのみ。
ということは、成功確率は3/4、失敗確率は1/4ということになる。
実際には8ビットの乱数2種の下3桁(3ビット分)の領域で判定をしていることになる。

$C1/CBF5 LDX $60    [$00:0360]   ;Xに[$00:0360]をロード
$C1/CBF7 LDA #$00                ;Aに$00をロード
$C1/CBF9 STA $7E9202,x           ;Aを[$7E:ADx2]に書き込み
$C1/CBFD STA $7E9203,x           ;Aを[$7E:ADx3]に書き込み
$C1/CC01 LDA $7E9003,x           ;Aに[$7E:ABx3](ヒット数)をロード
$C1/CC05 BNE $01    [$CC08]      ;ゼロフラグが立っていないとき[$CC08]分岐
;ゼロフラグON
$C1/CC07 RTS                     ;サブルーチン戻り
;ゼロフラグOFF
$C1/CC08 AND #$7F                ;Aと$7Fで論理積
$C1/CC0A STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/CC0C JSR $03F1  [$C1:03F1]   ;[$C1:03F1]へジャンプ

実質成功・失敗判定の計算後、[$7E:ABx3](敵番号xへのヒット数)が0ではないか、つまり命中しているかの判定を行っている。
ヒット数0の時は$C1/CC07でサブルーチンが戻るから、ここまでの計算値が何であっても位置変化は起こらない。
そうでない場合は更に判定が続く。

$C1/03F1 LDY $0002,x             ;Yに[$00:1Bx2]をロード
$C1/03F4 LDA $002F,y             ;Aに[$00:1AxF](敵データ15 タイプ)をロード
$C1/03F7 BIT #$04                ;Aと$04(タイプ:OBJECT)で論理積(ステータスフラグ変更のみ)
$C1/03F9 RTS                     ;サブルーチン戻り
;
$C1/CC0F BEQ $02    [$CC13]      ;ゼロフラグが立っているとき[$CC13]分岐
;ゼロフラグOFF
$C1/CC11 STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
;ゼロフラグON
$C1/CC13 LDA $0000,x[$00:1B20]   ;Aに[$00:1Bx0](敵ID)をロード
$C1/CC16 CMP #$D0                ;Aと$D0(敵IDオディオモール)を減算比較(ステータスレジスタ変更のみ)
$C1/CC18 BEQ $04    [$CC1E]      ;ゼロフラグが立っているとき[$CC1E]分岐
$C1/CC1A CMP #$D1                ;Aと$D1(敵IDピュアオディオ)を減算比較(ステータスレジスタ変更のみ)
$C1/CC1C BNE $02    [$CC20]      ;ゼロフラグが立っていないとき[$CC20]分岐
;$C1/CC18 ゼロフラグON
$C1/CC1E STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
;ゼロフラグOFF
$C1/CC20 LDY $10    [$00:0310]   ;Yに[$00:0310]をロード
$C1/CC22 PHY                     ;Yをスタックへプッシュ
$C1/CC23 STX $AC    [$00:03AC]   ;Xを[$00:03AC]に書き込み
$C1/CC25 PHX                     ;Xをスタックにプッシュ
$C1/CC26 JSR $1429  [$C1:1429]   ;[$C1:1429]へジャンプ

ここが、攻撃相手の判定にあたる。
$C1/03F1$C1/CC0Fでは、敵データ15に入っている敵タイプがOBJECTだと、$C1/CC0Fでゼロフラグが立たず、他のタイプならゼロフラグが立つ。
敵タイプがOBJECTというのは岩やしょく台など、倒す必要がない障害物のこと。
敵タイプがOBJECTだと、((乱数1 | 乱数2) & 技データ11) & $07の計算値を入れた[$00:0311]に、$C1/CC1E$00を上書きしてしまう。
つまり、敵タイプがOBJECTだと、この処理で向き変えや吹き飛ばしを無効にしてしまう。

敵タイプがOBJECTではない場合、$C1/CC13~の処理に入る。
ここでは[$00:1Bx0](敵ID)をロードして、$D0または$D1ではないかを判定していく。
減算比較してゼロフラグ判定なので、$D0または$D1に一致したら、$C1/CC1Eに進み、敵タイプがOBJECTと同様、[$00:0311]$00を上書きしている。
では、敵IDが$D0または$D1は何かというと、$D0はオディオモール、$D1はピュアオディオである。
ここで、攻撃相手がオディオモールかピュアオディオなら、向き変えや吹き飛ばしは無効化、という処理が入っているのである。
敵IDを指定しているのはこの2体のみ。

以上から、

  • 敵タイプがOBJECT
  • オディオモール
  • ピュアオディオ

の3パターンは、向き変えや吹き飛ばしが無効化されることがわかった。

この先は実際に向き変えや吹き飛ばしを行い、敵の向きや位置を計算する処理である。
[$00:0311]に入っている((乱数1 | 乱数2) & 技データ11) & $07から判定・計算を行う。

$C1/1429 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/142B BNE $01    [$142E]      ;ゼロフラグが立っていないとき[$142E]分岐
;ゼロフラグON
$C1/142D RTS                     ;サブルーチン戻り
;ゼロフラグOFF
$C1/142E LDA $0005,x[$00:1B25]   ;Aに[$00:1Bx5](技関連状態)をロード
$C1/1431 CMP #$40                ;Aと$40を減算比較(ステータスレジスタ変更のみ)
$C1/1433 BNE $03    [$1438]      ;ゼロフラグが立っていないとき[$1438]分岐
;ゼロフラグON(技関連状態が$40・タメ時間中)
$C1/1435 STZ $0005,x[$00:1B25]   ;[$00:1Bx5]に$00を書き込み
;ゼロフラグOFF(技関連状態が$40以外・タメ時間ではない)
$C1/1438 RTS                     ;サブルーチン戻り

上は、攻撃相手がタメ時間のある技を使っているかどうかの判定である。
最初の$C1/142Bのゼロフラグ分岐は、[$00:0311]に入った計算値が0ならタメ時間判定自体をスキップする、という意味である。
[$00:0311]に何らかの値が入ってれば、$C1/142E[$00:1Bx5]xは敵番号)をロードする。
このアドレスには、攻撃した敵の技関係状態が入っており、$40はタメ時間中を意味する。
タメ時間中なら$C1/1435に進み、[$00:1Bx5]$00を上書きするが、技関係状態が$00は通常状態、つまりタメ時間中ではなくなるので、この時点でタメていた技がキャンセルされることになる。

ただしこの時点では、残りタメ時間が入る[$00:1Bx6]には値を入れない。
実際には、[$00:1Bx6]から、向き変え・吹き飛ばし技を当てたことによる時間経過$10を引いた後に、再びタメ時間(初期値)をセットし、敵味方が何か行動したタイミングで[$00:1Bx6]0になる、という少々面倒な手順で、残りタメ時間を変えている。この処理の詳細な説明は、今回は省く。

どのパターンでもサブルーチンを抜けて次の処理へ移るが、次は行動異常関係の処理($C1/CC29$C1/CC3F)で、以前にも紹介したことがあるので、ここでは飛ばす。

$C1/CC42 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/CC44 BIT #$02                ;Aと$02で論理積(ステータスフラグ変更のみ)
$C1/CC46 BEQ $09    [$CC51]      ;ゼロフラグが立っているとき[$CC51]分岐
;ゼロフラグOFF 90度回転処理へ
$C1/CC48 JSR $CED4  [$C1:CED4]   ;[$C1:CED4]へジャンプ
;ゼロフラグON
$C1/CC51 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/CC53 BIT #$04                ;Aと$04で論理積(ステータスフラグ変更のみ)
$C1/CC55 BEQ $09    [$CC60]      ;ゼロフラグが立っているとき[$CC60]分岐
;ゼロフラグOFF 180度回転処理へ
$C1/CC57 JSR $CF0A  [$C1:CF0A]   ;[$C1:CF0A]へジャンプ
;ゼロフラグON
$C1/CC60 JSR $D025  [$C1:D025]   ;[$C1:D025]へジャンプ
;
$C1/D025 LDA $000A,x             ;Aに[$00:1BxA](現在のマス目のX座標)をロード
$C1/D028 STA $0008,x             ;Aを[$00:1Bx8](敵のX座標・計算値)に書き込み
$C1/D02B LDA $000B,x             ;Aに[$00:1BxB](現在のマス目のY座標)をロード
$C1/D02E STA $0009,x             ;Aを[$00:1Bx9](敵のY座標・計算値)に書き込み
$C1/D031 RTS                     ;サブルーチン戻り
;
$C1/CC63 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/CC65 BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C1/CC67 BEQ $09    [$CC72]      ;ゼロフラグが立っているとき[$CC72]分岐(向き変え・吹き飛ばしなし)
;ゼロフラグOFF 吹き飛ばし処理へ
$C1/CC69 LDY $10    [$00:0310]   ;Yに[$00:0310]をロード
$C1/CC6B PHY                     ;Yをスタックへプッシュ
$C1/CC6C JSR $D052  [$C1:D052]   ;[$C1:D052]へジャンプ

ここで、[$00:0311]の下3ビットの値から処理を分岐させる。
$02と論理積をとってゼロフラグが立たない場合は下から2ビット目に1が立っているから、$C1/CC48のジャンプ先[$C1:CED4]以降で90度回転の処理を行う。
$04と論理積をとってゼロフラグが立たない場合は下から3ビット目に1が立っているから、$C1/CC57のジャンプ先[$C1:CF0A]以降で180度回転の処理を行う。

それ以外は$C1/CC60$C1/D025とジャンプし、攻撃した敵の座標[$00:1BxA][$00:1BxB]をそれぞれ[$00:1Bx8][$00:1Bx9]にコピーしてから、今度は[$00:0311]$01で論理積をとり、ゼロフラグが立たない場合は下から1ビット目に1が立っているから、吹き飛ばし処理先の[$C1:D052]へ進むことになる。

飛び先が分岐したので、それぞれの処理を分けて見ていこう。

90度回転

ジャンプ先[$C1:CED4]以降の処理は以下のとおり。

;90度回転処理
$C1/CED4 LDA $0004,x[$00:1B24]   ;Aに[$00:1Bx4](敵の向き)をロード
$C1/CED7 BIT #$C0                ;Aと$C0で論理積(ステータスフラグ変更のみ)
$C1/CED9 BEQ $0C    [$CEE7]      ;ゼロフラグが立っているとき[$CEE7]分岐
$C1/CEDB CMP #$40                ;Aと$40を減算比較(ステータスレジスタ変更のみ)
$C1/CEDD BEQ $0C    [$CEEB]      ;ゼロフラグが立っているとき[$CEEB]分岐
$C1/CEDF CMP #$80                ;Aと$80を減算比較(ステータスレジスタ変更のみ)
$C1/CEE1 BEQ $0C    [$CEEF]      ;ゼロフラグが立っているとき[$CEEF]分岐
;向きが$C0(右上)
$C1/CEE3 LDA #$80                ;Aに$80をロード
$C1/CEE5 BRA $0A    [$CEF1]      ;フラグにかかわりなく常に分岐[$CEF1]
;向きが$00(右下)
$C1/CEE7 LDA #$40                ;Aに$40をロード
$C1/CEE9 BRA $06    [$CEF1]      ;フラグにかかわりなく常に分岐[$CEF1]
;向きが$40(左下)
$C1/CEEB LDA #$C0                ;Aに$C0をロード
$C1/CEED BRA $02    [$CEF1]      ;フラグにかかわりなく常に分岐[$CEF1]
;向きが$80(左上)
$C1/CEEF LDA #$00                ;Aに$00をロード
;分岐合流
$C1/CEF1 STA $0004,x[$00:1B24]   ;Aを[$00:1Bx4](敵の向き)に書き込み
$C1/CEF4 RTS                     ;サブルーチン戻り

まず、$C1/CED4で敵の向きが記録されている[$00:1Bx4]をロードする。
向きに対応した値は以下の通り。90度回転後の値も追加してある。

向き数値90度回転後
右下$00$40
左下$40$C0
右上$80$00
左上$C0$80

上の処理を見ての通り、向き4パターンで分岐し、Aに新たな向き(90度回転後)をロードして、$C1/CEF1[$00:1Bx4]に上書き、という方法で向き変えを行っている。

180度回転

ジャンプ先[$C1:CF0A]の処理は以下のとおり。

$C1/CF0A LDA $0004,x             ;Aに[$00:1Bx4](敵の向き)をロード
$C1/CF0D EOR #$C0                ;Aと$C0で排他的論理和XOR
$C1/CF0F STA $0004,x             ;Aを[$00:1Bx4](敵の向き)に書き込み
$C1/CF12 RTS                     ;サブルーチン戻り

90度回転と比べると非常にシンプルである。
排他的論理和は、2進数で各桁を見た時に、片方の桁だけ1なら1、それ以外は0とする論理演算にあたる。
$C0(%1100 0000)と排他的論理和を取るということは実質、上2ビットの値を逆にすることとと変わらない。
つまり、

向き数値2進数$C0と排他的論理和
右下$00%0000 0000%1100 0000 = $C0
左下$40%0100 0000%1000 0000 = $80
右上$80%1000 0000%0100 0000 = $40
左上$C0%1100 0000%0000 0000 = $00

このようにちょうど、180度逆の方向が計算できることになる。
この値が新たな敵の向き[$00:1Bx4]としてセットされる。

吹き飛ばし

マス目の座標のコピー部分も含めて紹介していく。
吹き飛ばし後のマス位置のサブルーチンは、敵のサイズやフィールド隅の場合など、考慮しなければならない点が多く、少々複雑。

$C1/D025 LDA $000A,x             ;Aに[$00:1BxA](敵のX座標)をロード
$C1/D028 STA $0008,x             ;Aを[$00:1Bx8](敵のX座標・計算値)に書き込み
$C1/D02B LDA $000B,x             ;Aに[$00:1BxB](敵のY座標)をロード
$C1/D02E STA $0009,x             ;Aを[$00:1Bx9](敵のY座標・計算値)に書き込み
$C1/D031 RTS                     ;サブルーチン戻り
;
$C1/CC63 LDA $11    [$00:0311]   ;Aに[$00:0311]をロード
$C1/CC65 BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C1/CC67 BEQ $09    [$CC72]      ;ゼロフラグが立っているとき[$CC72]分岐(吹き飛ばしなし)
;ゼロフラグOFF 吹き飛ばし処理へ
$C1/CC69 LDY $10    [$00:0310]   ;Yに[$00:0310]をロード
$C1/CC6B PHY                     ;Yをスタックへプッシュ
$C1/CC6C JSR $D052  [$C1:D052]   ;[$C1:D052]へジャンプ
;
$C1/D052 JSR $CF2F  [$C1:CF2F]   ;[$C1:CF2F]へジャンプ
;
$C1/CF2F LDY $78    [$00:0378]   ;Yに[$00:0378]をロード
$C1/CF31 CPY #$1C00              ;Yと$1C00を減算比較(ステータスレジスタ変更のみ)
$C1/CF34 BCS $1A    [$CF50]      ;キャリーフラグが立っているとき[$CF50]分岐
$C1/CF50 LDA $0018,y             ;Aに[$00:1C18+40n](技使用者のX座標)をロード
$C1/CF53 STA $14    [$00:0314]   ;Aを[$00:0314]に書き込み
$C1/CF55 LDA $0019,y             ;Aに[$00:1C19+40n](技使用者のY座標)をロード
$C1/CF58 STA $15    [$00:0315]   ;Aを[$00:0315]に書き込み
$C1/CF5A RTS                     ;サブルーチン戻り
;
$C1/D055 LDY $0002,x[$00:1B22]   ;Yに[$00:1Bx2]をロード
$C1/D058 LDA $14    [$00:0314]   ;Aに[$00:0314](技使用者のX座標)をロード
$C1/D05A CMP $000A,x             ;Aと[$00:1BxA](敵のX座標)を減算比較(ステータスレジスタ変更のみ)
$C1/D05D BCC $10    [$D06F]      ;キャリーフラグが立っていないとき[$D06F]分岐

$C1/CC65の分岐で、[$00:0311]が0と判定される場合は吹き飛ばし処理なし。$C1/CC69以降が吹き飛ばし用の専用処理になる。
$C1/D025$C1/D02Eでは敵の座標を計算用アドレスにコピーしたが、$C1/CF50からは吹き飛ばし技を使ったキャラのX座標を[$00:0314]、Y座標を[$00:0315]にコピーする。
$C1/D055からが最初の分岐であり、『[$00:0314](技使用者のX座標) - [$00:1BxA](敵のX座標)』を計算してキャリーフラグ分岐を行う。
マス目と座標の対応は以下のようになっている。

(0,0)(1,0)(2,0)(3,0)(4,0)(5,0)(6,0)
(0,1)(1,1)(2,1)(3,1)(4,1)(5,1)(6,1)
(0,2)(1,2)(2,2)(3,2)(4,2)(5,2)(6,2)
(0,3)(1,3)(2,3)(3,3)(4,3)(5,3)(6,3)
(0,4)(1,4)(2,4)(3,4)(4,4)(5,4)(6,4)
(0,5)(1,5)(2,5)(3,5)(4,5)(5,5)(6,5)
(0,6)(1,6)(2,6)(3,6)(4,6)(5,6)(6,6)

X座標は戦闘フィールドの横方向にあたる。
技使用者X座標-敵X座標』が正か0(キャリーフラグON)は、技使用者X座標の方が大きいか同じ座標になる。
技使用者X座標-敵X座標』が負(キャリーフラグOFF)は、技使用者X座標の方が小さい。

;キャリーフラグON(技使用者のX座標≧敵のX座標)
$C1/D05F LDA $000A,x[$00:1B1A]   A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;Aに[$00:1BxA](敵のX座標)をロード
$C1/D062 CLC                     A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;キャリーフラグクリア
$C1/D063 ADC $0004,y[$00:1A04]   A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;A + [$00:1An4](敵種類nの横の幅)
$C1/D066 DEC A                   A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;Aをデクリメント -1
$C1/D067 CMP $14    [$00:0314]   A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;Aと[$00:0314](技使用者のX座標)を減算比較(ステータスレジスタ変更のみ)
$C1/D069 BCC $08    [$D073]      A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;キャリーフラグが立っていないとき[$D073]分岐
$C1/D06B LDA #$00                A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;Aに$00をロード
$C1/D06D BRA $06    [$D075]      A:0003 X:1B10 Y:1A00 P:eNvMxdIzc;フラグにかかわりなく常に分岐[$D075]★
;
;キャリーフラグOFF(技使用者のX座標<敵のX座標)
$C1/D06F LDA #$01                A:0003 X:1B20 Y:1A00 P:eNvMxdIzc;Aに$01をロード
$C1/D071 BRA $02    [$D075]      A:0001 X:1B20 Y:1A00 P:envMxdIzc;フラグにかかわりなく常に分岐[$D075]★
;
;$C1/D069 キャリーフラグOFF(減算結果が負)
$C1/D073 LDA #$FF                A:0002 X:1B10 Y:1C00 P:envMxdIzC;Aに$FFをロード

分岐した先の処理だが、まず$C1/D05F~の使用者のX座標≧敵のX座標の場合を見てみる。
まず、敵のX座標にその敵の横幅を加算している。
例えば、下図において、敵■のX座標が3で、味方○のX座標が5、敵の横幅2の場合、敵味方が横位置列に並ぶと以下のようになっているはず。

□□□■■○□

横幅を足してから$C1/D066-1しているから(3+2-1=4)、敵の一番右側のマスのX座標を求めていることがわかる。
$C1/D067では、A(敵の一番右側のマスのX座標)と、[$00:0314](技使用者のX座標)を減算比較する。
上のような並びなら、4-5で減算結果が負だから、キャリーフラグが立たない。
その場合は$C1/D073A$FFをロードしてから$C1/D075にジャンプする。

キャリーフラグが立つ場合は、敵の一番右側のマスのX座標≧技使用者のX座標という意味である。

□□□■■□□
□□□□○□□

上のように敵の一番右側のマスのX座標と技使用者のX座標が同一か、

□□□■■■□
□□□□○□□

または敵の一番右側のマスのX座標>技使用者のX座標のどちらかになる。
つまり、敵の真上か真下に居る場合になり、この時は$C1/D06BA$00をロードしてから$C1/D075にジャンプする。

これで使用者のX座標≧敵のX座標の場合の処理は終了。

技使用者のX座標<敵のX座標の飛び先、$C1/D06Fでは、A$01をロードしてから$C1/D075に進む。
幅による分岐はない。
というのは、技使用者のX座標<敵のX座標だと、敵幅を見るまでもなく、必ず味方キャラが敵の左のマスにいることになる。

□○□■■□□

以上から、

  • 技使用者が敵の右側にいる時はA$FFをロード
  • 技使用者が敵の左側にいる時はA$01をロード
  • 技使用者が敵の真上か真下にいる時はA$00をロード

という処理をしてから、次の$C1/D075に進むことがわかった。
なお、$C1/D075ではAの値を[$00:0314]に書き込んでいるから、これまで技使用者のX座標が入っていたアドレスが上書きされたことになる。

;処理合流(★飛び先)
$C1/D075 STA $14    [$00:0314]   ;A($00/$FF/$01)を[$00:0314]に書き込み
$C1/D077 LDA $15    [$00:0315]   ;Aに[$00:0315](技使用者のY座標)をロード
$C1/D079 CMP $000B,x             ;Aと[$00:1BxB](敵のY座標)を減算比較(ステータスレジスタ変更のみ)
$C1/D07C BCC $10    [$D08E]      ;キャリーフラグが立っていないとき[$D08E]分岐
;キャリーフラグON(技使用者のY座標≧敵のY座標)
$C1/D07E LDA $000B,x             ;Aに[$00:1BxB](敵のY座標)をロード
$C1/D081 CLC                     ;キャリーフラグクリア
$C1/D082 ADC $0005,y[$00:1A05]   ;A + [$00:1An5](敵種類nの縦の幅)
$C1/D085 DEC A                   ;Aをデクリメント -1
$C1/D086 CMP $15    [$00:0315]   ;Aと[$00:0315](技使用者のY座標)を減算比較(ステータスレジスタ変更のみ)
$C1/D088 BCC $08    [$D092]      ;キャリーフラグが立っていないとき[$D092]分岐
;$C1/D088でキャリーフラグON
$C1/D08A LDA #$00                ;Aに$00をロード
$C1/D08C BRA $06    [$D094]      ;フラグにかかわりなく常に分岐[$D094]★
;$C1/D07CでキャリーフラグOFF(技使用者のY座標<敵のY座標)
$C1/D08E LDA #$01                ;Aに$01をロード
$C1/D090 BRA $02    [$D094]      ;フラグにかかわりなく常に分岐[$D094]★
;$C1/D088でキャリーフラグOFF
$C1/D092 LDA #$FF                ;Aに$FFをロード
;★合流
$C1/D094 STA $15    [$00:0315]   ;Aを[$00:0315]に書き込み
$C1/D096 ORA $14    [$00:0314]   ;Aと[$00:0314]で論理和
$C1/D098 BEQ $5A    [$D0F4]      ;ゼロフラグが立っているとき[$D0F4]分岐

上処理はY座標に関しての分岐で、分岐の条件はX座標と似ている。
技使用者のY座標-敵のY座標』による分岐と、敵の縦幅での分岐を行っている。

  • 技使用者が敵の下側にいる時はA$FFをロード
  • 技使用者が敵の上側にいる時はA$01をロード
  • 技使用者が敵の真右か真左にいる時はA$00をロード

このAの値は$C1/D094[$00:0315]に書き込まれる。
これまで技使用者のY座標が入っていたアドレスが上書きされたことになる。

$C1/D096では[$00:0314][$00:0315]の論理和を取り、ここでゼロフラグが立つと[$C1/D0F4]にジャンプして、処理を抜ける。
ここでの論理和が0になることはありえない。攻撃相手の真上か真下かつ、真右か真左というマスはないからである。
少しわかりにくくなってきたので、下図を見ていただきたい。
[$00:0314],[$00:0315]の順で値を記すと、敵位置に対し味方のいるマスで下のように値が入ることになる。

この通り、[$00:0314][$00:0315]どちらも$00になるケースはないので、処理終了となり[$C1/D0F4]にジャンプして、処理を抜けるのである。
また、これらの値から吹き飛ぶ方向が決まることもだいたい察しがつくだろう。
上の値を座標に加算すれば飛ぶマスの座標になるからである。$FFの加算は符号付き8ビット整数での加算になり(負の数の加算)、10進数なら-1を意味していると考えれば良い。
では、続きを見ていく。

$C1/D09A LDA $000A,x             ;Aに[$00:1BxA](敵のX座標)をロード
$C1/D09D CLC                     ;キャリーフラグクリア
$C1/D09E ADC $14    [$00:0314]   ;A + [$00:0314]($00/$FF/$01)
$C1/D0A0 CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/D0A2 BCS $17    [$D0BB]      ;キャリーフラグが立っているとき[$D0BB]分岐
$C1/D0A4 STA $0008,x             ;Aを[$00:1Bx8](敵のX座標・計算値)に書き込み
;
$C1/D0A7 LDA $000B,x             ;Aに[$00:1BxB](敵のY座標)をロード
$C1/D0AA CLC                     ;キャリーフラグクリア
$C1/D0AB ADC $15    [$00:0315]   ;A + [$00:0315]($00/$FF/$01)
$C1/D0AD CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/D0AF BCS $26    [$D0D7]      ;キャリーフラグが立っているとき[$D0D7]分岐
$C1/D0B1 STA $0009,x             ;Aを[$00:1Bx9](敵のY座標・計算値)に書き込み
$C1/D0B4 JSR $41F2  [$C1:41F2]   ;[$C1:41F2]へジャンプ

途中に分岐があるが、まず分岐先には飛ばない上の処理を見てみよう。
$C1/D09A$C1/D0A4は、[$00:1BxA](敵のX座標)に、ここまでで計算した[$00:0314]($00/$FF/$01)を加算する。つまり飛び先のX座標の値の計算である。
右に飛ぶのなら$01を加算、左に飛ぶのなら$FFを加算(-1の加算と同じ)、左右方向へは飛ばない場合は$00の加算である。
$C1/D0A0で、計算結果から$07を減算比較し、キャリーフラグ判定をしている。
これは、計算したX座標が戦闘フィールドからはみ出していないかの判定である。
左フィールド隅(X座標0)で左に飛ばすと座標が$FFになるし、右フィールド隅(X座標6)で右に飛ばすと座標が$07となって戦闘フィールド外になってしまう。
この場合、$07と減算比較すると、戦闘フィールド内($00$06)なら減算結果が必ず負となりキャリーフラグは立たないが、戦闘フィールド外($FF$07)なら減算結果が正か0でキャリーフラグが立ち、$C1/D0BBにジャンプすることになる。
キャリーフラグが立たない時はX座標計算結果を[$00:1Bx8](敵のX座標・計算値)に書き込む。

$C1/D0A7$C1/D0B1は同じ処理をY座標で行う。
[$00:1BxB](敵のY座標)に[$00:0315]($00/$FF/$01)を加算して吹き飛ばされたマスのY座標を計算し、フィールド外の場合は$C1/D0D7にジャンプする。
フィールド内に収まっているなら、Y座標計算結果を[$00:1Bx9](敵のY座標・計算値)に書き込む。
ここまで済むと[$C1:41F2]へジャンプする。
ただし、この時点で、[$00:1Bx8][$00:1Bx9]が飛び先の座標と決まったのではない。
ここまでの判定は、フィールド外にはみ出さないかどうかだけである。
敵を吹き飛ばす場合はサイズを考慮しなければならない。ここまでの座標計算は、大型の敵だと、敵の占めるマスの左上位置を基準とした座標であるから、左上マスが戦闘フィールド内に収まっていても、それ以外の部分が戦闘フィールド内かどうかは判定を行う必要がる。
他に、飛び先の座標に、既に他のキャラ(敵または味方)がいるかどうかの判定も行わなければならない。
その処理が[$C1:41F2]以降になる。

その前に、フィールド外にはみ出した場合の処理、$C1/D0BB~と$C1/D0D7~の処理を見てみよう。

;X座標飛び先がフィールド外の場合
$C1/D0BB LDA $000A,x             ;Aに[$00:1BxA](敵のX座標)をロード
$C1/D0BE STA $0008,x             ;Aを[$00:1Bx8](敵のX座標・計算値)に書き込み
$C1/D0C1 LDA $15    [$00:0315]   ;Aに[$00:0315]($00/$FF/$01)をロード
$C1/D0C3 BEQ $12    [$D0D7]      ;ゼロフラグが立っているとき[$D0D7]分岐
$C1/D0C5 CLC                     ;キャリーフラグクリア
$C1/D0C6 ADC $000B,x             ;A + [$00:1BxB](敵のY座標)
$C1/D0C9 CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/D0CB BCS $0A    [$D0D7]      ;キャリーフラグが立っているとき[$D0D7]分岐
$C1/D0CD STA $0009,x             ;Aを[$00:1Bx9](敵のY座標・計算値)に書き込み
$C1/D0D0 JSR $41F2  [$C1:41F2]   ;[$C1:41F2]へジャンプ
;
;Y座標飛び先がフィールド外の場合
$C1/D0D7 LDA $000B,x             ;Aに[$00:1BxB](敵のY座標)をロード
$C1/D0DA STA $0009,x             ;Aを[$00:1Bx9](敵のY座標・計算値)に書き込み
$C1/D0DD LDA $14    [$00:0314]   ;Aに[$00:0314]($00/$FF/$01)をロード
$C1/D0DF BEQ $13    [$D0F4]      ;ゼロフラグが立っているとき[$D0F4]分岐
$C1/D0E1 CLC                     ;キャリーフラグクリア
$C1/D0E2 ADC $000A,x             ;A + [$00:1BxA](敵のX座標)
$C1/D0E5 CMP #$07                ;Aと$07を減算比較(ステータスレジスタ変更のみ)
$C1/D0E7 BCS $0B    [$D0F4]      ;キャリーフラグが立っているとき[$D0F4]分岐
$C1/D0E9 STA $0008,x             ;Aを[$00:1Bx8](敵のX座標・計算値)に書き込み
$C1/D0EC JSR $41F2  [$C1:41F2]   ;[$C1:41F2]へジャンプ
;
$C1/D0F4 RTS                     ;サブルーチン戻り

X座標、Y座標ともに、フィールド外にはみ出すのなら加算せず、そのままの値を[$00:1Bx8](敵のX座標・計算値)または[$00:1Bx9](敵のY座標・計算値)に書き込む、という処理をしている。フィールド隅だからそれ以上移動できないという処理である。
この仕組みにより、たとえばフィールドの上端にいる敵キャラを右上方向へ飛ばした時、上方向へはフィールド外だから飛ばないが、右方向がフィールド外でない時、右方向にだけ1マス飛ぶ、というような現象が起こるのである。

その後だが、X座標の判定後にY座標の処理($C1/D0C1$C1/D0CD)、Y座標の判定後にX座標の処理($C1/D0DD$C1/D0E9)をするが、$C1/D0DD$C1/D0E9X方向への移動がない場合([$00:0314]$00)、Y方向にもX方向にも飛ばないということになる。
つまり吹き飛ばしは成功だが吹き飛ばす先のマスがないので、結局誰も移動しないから、飛び先の座標に誰かが既にいるかどうかの判定も飛ばして構わないということになり、$C1/D0F4でサブルーチンを抜けるのである。

吹き飛ばし成功でも吹き飛ばせなかった場合を除き、[$C1:41F2]にジャンプして処理が続く。

$C1/41F2 LDA #$10                ;Aに$10をロード
$C1/41F4 STA $13    [$00:0313]   ;Aを[$00:0313]に書き込み
$C1/41F6 LDA $0001,x[$00:1B21]   ;Aに[$00:1Bx1](敵番号x戦闘状態)をロード
$C1/41F9 CMP #$20                ;Aと$20を減算比較(ステータスレジスタ変更のみ)
;(戦闘状態$20:敵キャラをプレイヤーが操作)
$C1/41FB BNE $02    [$41FF]      ;ゼロフラグが立っていないとき[$41FF]分岐
$C1/41FF TXA                     ;Xレジスタの値をAレジスタに転送
$C1/4200 LSR A                   ;Aを論理右シフト (/2)
$C1/4201 LSR A                   ;Aを論理右シフト (/2)
$C1/4202 LSR A                   ;Aを論理右シフト (/2)
$C1/4203 LSR A                   ;Aを論理右シフト (/2)
$C1/4204 INC A                   ;Aをインクリメント +1
$C1/4205 ORA $13    [$00:0313]   ;Aと[$00:0313]で論理和
$C1/4207 STA $13    [$00:0313]   ;Aを[$00:0313]に書き込み
$C1/4209 JSR $4260  [$C1:4260]   ;[$C1:4260]へジャンプ
;
$C1/4260 LDY $0002,x             ;Yに[$00:1Bx2]をロード
$C1/4263 LDA $0004,y             ;Aに[$00:1Ax4](敵種類xの横の幅)をロード
$C1/4266 STA $0A    [$00:030A]   ;Aを[$00:030A]に書き込み
$C1/4268 LDA $0005,y             ;Aに[$00:1A05](敵種類xの縦の幅)をロード
$C1/426B STA $0B    [$00:030B]   ;Aを[$00:030B]に書き込み
$C1/426D STA $09    [$00:0309]   ;Aを[$00:0309]に書き込み
$C1/426F LDA #$08                ;Aに$08をロード
$C1/4271 SEC                     ;キャリーフラグON
$C1/4272 SBC $0A    [$00:030A]   ;A - [$00:030A](敵種類xの横の幅)
$C1/4274 STA $12    [$00:0312]   ;Aを[$00:0312]に書き込み
$C1/4276 RTS                     ;サブルーチン戻り

$C1/41F2$C1/4276は、吹き飛ぶ相手の吹き飛ばし先座標$00:04C8$00:04FEを求めるための計算である。
敵味方の状態が入っている座標のアドレスは$00:04C8$00:04FEで、下には$00:04????の値部分のみ記している。

C8C9CACBCCCDCE
D0D1D2D3D4D5D6
D8D9DADBDCDDDE
E0E1E2E3E4E5E6
E8E9EAEBECEDEE
F0F1F2F3F4F5F6
F8F9FAFBFCFDFE

座標に入る値だが、敵の場合は上1桁が1、下1桁が敵番号+1である。
$C1/41F6$C1/4207は、敵番号[$00:1Bx1]から、座標に入る値を算出して[$00:0313]に書き込む処理である。
敵の場合$11$1Eのいずれかの値が[$00:0313]に書き込まれる。

$C1/4260からは、[$00:030A]に横幅、[$00:030B][$00:0309]に縦幅、[$00:0312]に「$08-横幅」が入る。
これらの値から、攻撃相手の吹き飛ぶ先の$00:04C8$00:04FEのアドレスなどが計算される。

$C1/420C LDA $0008,x             ;Aに[$00:1Bx8](敵のX座標・計算値)をロード
$C1/420F CLC                     ;キャリーフラグクリア
$C1/4210 ADC $0A    [$00:030A]   ;A + [$00:030A](敵種類xの横の幅)
$C1/4212 CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C1/4214 BCS $47    [$425D]      ;キャリーフラグが立っているとき[$425D]分岐
;キャリーフラグOFF(横方向フィールド内)
$C1/4216 LDA $0009,x             ;Aに[$00:1Bx9](敵のY座標・計算値)をロード
$C1/4219 CLC                     ;キャリーフラグクリア
$C1/421A ADC $0B    [$00:030B]   ;A + [$00:030B](敵種類xの縦の幅)
$C1/421C CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C1/421E BCS $3D    [$425D]      ;キャリーフラグが立っているとき[$425D]分岐
;キャリーフラグOFF(縦方向フィールド内)
$C1/4220 LDA $0009,x             ;Aに[$00:1Bx9](敵のY座標・計算値)をロード
$C1/4223 ASL A                   ;Aを算術左シフト *2
$C1/4224 ASL A                   ;Aを算術左シフト *2
$C1/4225 ASL A                   ;Aを算術左シフト *2
$C1/4226 ADC $0008,x             ;A + [$00:1Bx8](敵のX座標・計算値)
$C1/4229 STA $10    [$00:0310]   ;Aを[$00:0310]に書き込み
$C1/422B STZ $11    [$00:0311]   ;[$00:0311]に$00を書き込み
$C1/422D LDY $10    [$00:0310]   ;Yに[$00:0310]をロード

敵が飛ばされる先の仮のX座標・Y座標は[$00:1Bx8][$00:1Bx9]にコピーされているが、それぞれに横幅または縦幅を加算している。
例えばX座標5、Y座標2が飛ばされる位置とする。以下の★のマスである。

□□□□□□□
□□□□□□□
□□□□□★□
□□□□□□□
□□□□□□□
□□□□□□□
□□□□□□□

この★の位置は、敵の左上マスである。
敵の横幅が2までなら、フィールドマスの範囲に収まる。X座標5+横幅2=7である。

□□□□□□□
□□□□□□□
□□□□□★■
□□□□□■■
□□□□□□□
□□□□□□□
□□□□□□□

だが、敵の横幅が3だったら、フィールド外に敵の一部がはみ出す。X座標5+横幅3=8である。

□□□□□□□
□□□□□□□
□□□□□★■■
□□□□□■■■
□□□□□□□
□□□□□□□
□□□□□□□

つまりX座標+横幅8だったら横方向へは吹き飛ばしができない。
同じことはY座標でもいえる。
$C1/4212X座標+横幅の値を$08と減算比較していたり、$C1/421CY座標+縦幅の値を$08と減算比較してキャリーフラグ判定を行うことで、敵のサイズから、各方向への吹き飛ばし後にフィールド内に収まるかどうかでの分岐になる。
どちらかもキャリーフラグが立たない場合は、縦方向・横方向ともに敵のサイズが戦闘フィールド内に収まっていることになるが、どちらかでキャリーフラグが立つ場合は飛ぶ先のマスがフィールド外なので、吹き飛ばし判定は成功だが吹き飛ぶことはない。
吹き飛ばない場合の飛び先$C1/425Dは、

$C1/425D ORA #$FF                ;Aと$FFで論理和
$C1/425F RTS                     ;サブルーチン戻り

X座標+横幅またはY座標+縦幅$FFで論理和を取ってから、サブルーチンから抜ける。

一方、吹き飛ばし先において敵のサイズがすべて戦闘フィールドに収まるのなら、$C1/4220に処理が進み、

[$00:0310] = 敵のY座標*8 + 敵のX座標 [$00:0311] = $00

を書き込んで次の処理へ進む。
[$00:0310]に入る値は、下3ビットが敵のX座標、その更に上3ビットが敵のY座標という値である。
座標の値は0~6なので、2進数では3桁(3ビット)に収まるから、

上2ビットX座標Y座標
00000000

の形式で8ビット(1バイト)に座標の値を収めている。
敵味方の状態が入っている座標のアドレスは$00:04C8$00:04FEになるが、$0004C8[$00:0310]の座標の値を加算すると、ちょうど該当のマスのアドレスが計算できるようになっている。

$C1/422Dで、[$00:0310]Yに読み込まれ、$C1/422Fからループ処理が入る。
$04C8 + Yで敵の左上位置マスのアドレスが読み込め、+1していくと1マスずつ右方向のアドレスが読み出せることになる。

;敵の横幅確認ループ開始
$C1/422F LDA $0A    [$00:030A]   ;Aに[$00:030A](敵種類xの横の幅)をロード
$C1/4231 STA $08    [$00:0308]   ;A(敵種類xの横の幅)を[$00:0308]に書き込み
;1マスずつ確認ループ開始
$C1/4233 LDA $04C8,y[$00:04D5]   ;Aに[$04C8,y](戦闘フィールドマスの状態)をロード
$C1/4236 INY                     ;Yをインクリメント +1
$C1/4237 AND #$1F                ;Aと$1Fで論理積
$C1/4239 CMP $13    [$00:0313]   ;Aと[$00:0313]を減算比較(ステータスレジスタ変更のみ)
$C1/423B BEQ $04    [$4241]      ;ゼロフラグが立っているとき[$4241]分岐
;ゼロフラグOFF
$C1/423D BIT #$0F                ;Aと$0Fで論理積(ステータスフラグ変更のみ)
$C1/423F BNE $1C    [$425D]      ;ゼロフラグが立っていないとき[$425D]分岐
;ゼロフラグON
$C1/4241 DEC $08    [$00:0308]   ;[$00:0308](敵種類xの横の幅)をデクリメント -1
$C1/4243 BNE $EE    [$4233]      ;ゼロフラグが立っていないとき[$4233]分岐
;1マスずつ確認ループここまで
$C1/4245 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/4247 TYA                     ;Yレジスタの値をAレジスタに転送
$C1/4248 ADC $12    [$00:0312]   ;A + [$00:0312]($08-横幅)
$C1/424A AND #$00FF              ;Aと$00FFで論理積
$C1/424D TAY                     ;Aの値をYレジスタに転送
$C1/424E SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/4250 DEC $09    [$00:0309]   ;[$00:0309](敵の縦幅)をデクリメント -1
$C1/4252 BNE $DB    [$422F]      ;ゼロフラグが立っていないとき[$422F]分岐
;敵の横幅確認ループここまで
$C1/4254 JSR $4277  [$C1:4277]   ;[$C1:4277]へジャンプ

例えば敵が横2マス・縦3マスの敵であったら、

12
34
56

上で1の位置が敵座標[$00:1Bx8][$00:1Bx9]にあたるが、上のループ処理では、まず1の位置に該当する座標のアドレス$00:04C8$00:04FEを、$C1/4233でロードする。
ここに自身以外の敵または味方キャラがいるかどうかの判定が、1マスずつ確認ループ$C1/4233$C1/4243である。

$00:04C8$00:04FEは、

内容
$01$04味方キャラ1~味方キャラ4
$11$1F敵番号0~E
$80/$A0/$C0/$E0水/毒/火炎/電撃フィールド

このように値が入り、キャラとダメージフィールドの値は加算される。
$C1/4237~で、$1Fと論理積を取り(フィールドの値を削除する)、[$00:0313](飛ばされた敵自身の値$11$1F)と減算比較してゼロフラグ判定を行っている。
つまり、吹き飛んだ先に既に自身がいるかどうかを判定しているが、大型の敵の場合は1マス飛ばされたとしても、下のように、

12□
34□
56□

↓右1マス吹き飛ばし

□12
□34
□56

敵の右上マス「2」の位置に、敵の左上マス「1」が移動する、ということがあり得るので、$C1/423Bにてゼロフラグが立つ場合もある。その場合は$C1/423D$C1/423Fは飛ばされて$C1/4241に進む。
一方、吹き飛んだ先が自身の一部がいるマスでない場合は$C1/423D$C1/423Fの処理が入るが、ここでは$0Fとの論理積を取ってゼロフラグ判定をしている。つまり、$00:04C8$00:04FEの下1桁の値だけ取ってゼロフラグ判定をするが、下1桁は、敵味方いずれかがいれば0以外の値、誰もいないマスなら0が入るから、$C1/423Fでゼロフラグが立たないのは該当マスに他の敵か味方キャラがいる場合になり、$C1/425Dにジャンプしてサブルーチンを抜ける。
ゼロフラグが立つ場合は、吹き飛ばし先がカラのマス、またはダメージフィールドだけがあるマスのみである。

以上の判定で「吹き飛ばし先のマスに吹き飛ばしが可能」となり、[$00:0308]をデクリメント(-1)してから、ループの最後の$C1/4243に到達する。
これで1マス分の判定終了である。

12
34
56

の、1の位置の判定が終わったので、敵の幅[$00:0308]0になるまで横方向にマス目の判定を続ける。
横1列分の判定が終わったら(上の2の位置)、$C1/4245に抜けるが、ここで一段下の3のマスのアドレスを計算して[$00:0309](敵の縦幅)をデクリメント(-1)し、[$00:0309](敵の縦幅)が0になるまで$C1/422Fに戻るループを繰り返すことで、1から6までの6マスについて判定をしていく。
全てのマスの判定で「吹き飛ばし先のマスに自身の一部がいるか、誰もいないマス」であった場合はループを終了して、$C1/4254に到達する。
一方、1マスでも「吹き飛ばし先のマスに自身の一部がいるか、誰もいないマス」ではなかったのなら、吹き飛ばし判定は成功だが吹き飛ばされないということになり、$C1/425Dにジャンプしてサブルーチンを抜ける。

ここまでは「吹き飛ばし先のマスに自身の一部がいるか、誰もいないマス」ということが確定しただけなので、次に、座標の値の書き換えをしなければならない。
次のジャンプ先$C1:4277以降の処理は以下のようになる。

$C1/4277 LDA $000B,x             ;Aに[$00:1BxB](敵のY座標)をロード
$C1/427A ASL A                   ;Aを算術左シフト *2
$C1/427B ASL A                   ;Aを算術左シフト *2
$C1/427C ASL A                   ;Aを算術左シフト *2
$C1/427D ADC $000A,x             ;A + [$00:1BxA](敵のX座標)
$C1/4280 STA $14    [$00:0314]   ;A(敵のY座標・X座標)を[$00:0314]に書き込み
$C1/4282 STZ $15    [$00:0315]   ;[$00:0315]に$00を書き込み
$C1/4284 LDY $14    [$00:0314]   ;Yに[$00:0314]をロード
$C1/4286 LDA $0B    [$00:030B]   ;Aに[$00:030B](敵種類xの縦の幅)をロード
$C1/4288 STA $09    [$00:0309]   ;A(敵種類xの縦の幅)を[$00:0309]に書き込み
;敵の横幅確認ループ開始
$C1/428A LDA $0A    [$00:030A]   ;Aに[$00:030A](敵種類xの横の幅)をロード
$C1/428C STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
;1マスずつ確認ループ
$C1/428E LDA $04C8,y             ;Aに[$04C8,y](戦闘フィールドマスの状態)をロード
$C1/4291 AND #$E0                ;Aと$E0で論理積
$C1/4293 STA $04C8,y             ;Aを[$04C8,y](戦闘フィールドマスの状態)に書き込み
$C1/4296 INY                     ;Yをインクリメント +1
$C1/4297 DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/4299 BNE $F3    [$428E]      ;ゼロフラグが立っていないとき[$428E]分岐
;1マスずつ確認ループここまで
$C1/429B REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/429D TYA                     ;Yレジスタの値をAレジスタに転送
$C1/429E ADC $12    [$00:0312]   ;A + [$00:0312]
$C1/42A0 AND #$00FF              ;Aと$00FFで論理積
$C1/42A3 TAY                     ;Aの値をYレジスタに転送
$C1/42A4 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/42A6 DEC $09    [$00:0309]   ;[$00:0309]をデクリメント -1
$C1/42A8 BNE $E0    [$428A]      ;ゼロフラグが立っていないとき[$428A]分岐
;敵の横幅確認ループ処理ここまで
$C1/42AA RTS                     ;サブルーチン戻り

ここまでで、

  • 敵が飛ばされる前の元の座標:(X,Y) = [$00:1BxA][$00:1BxB]
  • 敵が飛ばされる先の仮の座標:(X,Y) = [$00:1Bx8][$00:1Bx9]

と、値が入った。
上の処理は、敵が飛ばされる前の元の座標[$00:1BxA][$00:1BxB]から、$00:04C8$00:04FE内の、敵が飛ぶ前の座標をすべて計算して、敵がいない状態に上書きするループ処理になる。
敵がいない状態といっても、$00で上書きするのではない。$00:04C8$00:04FEにはダメージフィールドの状況も入っているため、敵の値だけ削除する必要がある。
$00:04C8$00:04FEの座標計算自体は先の処理とほぼ同じなので、詳細は端折る。
重要なのは1マスずつの確認ループ処理$C1/428E$C1/4299で、[$04C8,y](該当の$00:04C8$00:04FE)をロードし、$E0と論理積を取ってから、[$04C8,y]に上書きしている。
例えば敵番号0だったら、ダメージフィールド上に立っていなければ、[$04C8,y]$10が入るし、水/毒/火炎/電撃フィールド上なら、$10 + $80/$A0/$C0/$E0が入っていることになるが、ダメージフィールド上ではないなら、

$10 & $E0 = $00

で、$00を上書きし、敵番号0は該当マスからいなくなる。
火炎フィールド上なら[$04C8,y]$D0が入っているが、

$D0 & $E0 = $C0

と、火炎フィールドの値$C0だけ残る。
$E0と論理積を取る処理により、元からいた敵の値だけを削除できるのである。
敵が元からいたマスすべてに対しループ処理を終えると$C1/42AAでサブルーチンを抜けて、次の処理に移る。

$C1/4257 JSR $42AB  [$C1:42AB]   ;[$C1:42AB]へジャンプ
;
$C1/42AB LDA $0B    [$00:030B]   ;Aに[$00:030B](敵種類xの縦の幅)をロード
$C1/42AD STA $09    [$00:0309]   ;A(敵種類xの縦の幅)を[$00:0309]に書き込み
$C1/42AF LDY $10    [$00:0310]   ;Yに[$00:0310](敵のY座標・X座標)をロード
;敵の横幅確認ループ開始
$C1/42B1 LDA $0A    [$00:030A]   ;Aに[$00:030A](敵種類xの横の幅)をロード
$C1/42B3 STA $08    [$00:0308]   ;Aを[$00:0308]に書き込み
;1マスずつ確認ループ
$C1/42B5 LDA $04C8,y             ;Aに[$04C8,y](戦闘フィールドマスの状態)をロード
$C1/42B8 AND #$E0                ;Aと$E0で論理積
$C1/42BA ORA $13    [$00:0313]   ;Aと[$00:0313](飛ばされた敵自身の値$11~$1F)で論理和
$C1/42BC STA $04C8,y[$00:04D5]   ;Aを[$04C8,y](戦闘フィールドマスの状態)に書き込み
$C1/42BF INY                     ;Yをインクリメント +1
$C1/42C0 DEC $08    [$00:0308]   ;[$00:0308]をデクリメント -1
$C1/42C2 BNE $F1    [$42B5]      ;ゼロフラグが立っていないとき[$42B5]分岐
;1マスずつ確認ループここまで
$C1/42C4 REP #$21                ;Aを16bit幅に変更、キャリーフラグクリア
$C1/42C6 TYA                     ;Yレジスタの値をAレジスタに転送
$C1/42C7 ADC $12    [$00:0312]   ;A + [$00:0312]
$C1/42C9 AND #$00FF              ;Aと$00FFで論理積
$C1/42CC TAY                     ;Aの値をYレジスタに転送
$C1/42CD SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/42CF DEC $09    [$00:0309]   ;[$00:0309]をデクリメント -1
$C1/42D1 BNE $DE    [$42B1]      ;ゼロフラグが立っていないとき[$42B1]分岐
;敵の横幅確認ループ処理ここまで
$C1/42D3 RTS                     ;サブルーチン戻り

そして最後に、敵が飛ばされる先の仮の座標:(X,Y) = [$00:1Bx8][$00:1Bx9]から、$00:04C8$00:04FE内の、敵が飛ばされる先の座標をすべて計算して、敵がいる状態に上書きする([$00:0313]の値を加算する)ループ処理を行う。
1マスずつ確認ループ$C1/42B5$C1/42C2の処理は、まず$C1/42B8で戦闘フィールドマスの状態と$E0の論理積を取ることにより、一度ダメージフィールドの値だけ取り出す。
飛ばされる先なので、何もないマスか、ダメージフィールドだけがある状態か、既に飛ばされる敵の一部分があるマスかのいずれかになるのだが、$E0との論理積で、もし既に飛ばされる敵の一部分があるマスであっても一度削除している。
そこにあらためて、[$00:0313](飛ばされた敵自身の値$11$1F)と論理和を取った値を書き込み、1マス分の処理終了である。
全ての処理が終わり$C1/42D3でサブルーチンが戻った段階で、$00:04C8$00:04FE内は、敵が吹き飛ばしにあった後の座標の状態となった。

なお、敵が飛ばされる先の仮の座標、つまり座標計算値が入っている[$00:1Bx8][$00:1Bx9]は、現在の座標[$00:1BxA][$00:1BxB]を下の処理で上書きする。
これで$00:04C8$00:04FE内の値と、敵番号0~E用の座標アドレスのどちらも新たな値に更新されたことになる。

$C1/437F LDA $0008,x             ;Aに[$00:1Bx8](敵のX座標・計算値)をロード
$C1/4382 STA $000A,x             ;Aを[$00:1BxA](敵のX座標)に書き込み
$C1/4385 JSR $456B               ;[$C1:456B]へジャンプ
(中略)
$C1/438B LDA $0009,x             ;Aに[$00:1Bx9](敵のY座標・計算値)をロード
$C1/438E STA $000B,x             ;Aを[$00:1BxB](敵のY座標)に書き込み
$C1/4391 JSR $4577               ;[$C1:4577]へジャンプ

以上で、味方から敵に対する吹き飛ばし判定と、吹き飛ばし成功時の座標関連処理が終了となる。
敵から味方に対しても処理自体は似たようなものである。

また、吹き飛ばしでは、吹き飛ばし判定に成功したとしても、飛ばす先の座標すべてが、フィールド外ではない、かつ、誰もいない、ダメージフィールドのみ、既に自身の一部がある(これは敵のみということになる)のいずれかを満たした時でないと吹き飛ばない、という処理を行っていることがわかった。
このため、斜めに設置された岩の間を1×1マスキャラがすり抜けて吹き飛ばされる現象などが起こる。

□3□
□12
敵□□

上のような配置で、2と3に味方キャラがいる状態で、敵が右上の1の味方キャラに吹き飛ばしを仕掛けて成功した時、吹き飛ばし先は1の右上、つまり一番右上のマスと判断される。
その位置は誰も居ないので、吹き飛ばしが成功すると下図のように飛ばされる。

□31
□↗2
敵□□

実際のゲームの画面上では2と3の間に隙間がないように見えるので、妙な現象のように見えてしまうが、吹き飛ばされる先のマスの状態しか判定しないから、一見すり抜けのように見える現象が起きてしまうのである。
おそらくこの仕様は、ある程度意図したものだろう。
味方キャラの場合、「山猿拳」や「天馬後すい脚」(自身後退)をうまく使って斜め移動をし、狭い場所から脱出する方法もある。



このページをシェアする

上へ