TOP > プログラミング関係解説&調査 > 状態異常

状態異常

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

本作の状態異常は8種類存在している(石化・酔い・眠り・マヒ・毒・腕かため・足かため・首かため)。
首かためは隠し状態異常でゲーム画面には表示されないが、キャラの行動の制御に使われていることがほとんどである。
状態異常の発生方法は2種類ある。

  1. 技(アイテム使用時含む)の追加効果として状態異常が発生する。
  2. 武器の追加効果として状態異常が発生する。

1.は味方が敵、または敵が味方を攻撃した時どちらでも発生するが、2.は味方キャラしか武器を装備できないため、味方が敵を攻撃した時のみ発生する。
また、キャラ自身の能力または味方の装備品で、特定の状態異常を完全無効化可能であり、その場合は該当の状態異常になることはない。

状態異常発生時には、その状態異常の継続時間もセットされる。ここでは「状態異常継続時間」とする。
全ての敵味方キャラには、各状態異常の状態異常継続時間を記録するアドレスが存在している(自然回復しない石化にもある)。
状態異常継続時間の減少方法は、タメ時間ありの技のタメ時間などと同じで、1ターンで16、味方の移動・パス・Yボタン足踏みで4、向き変えで2減っていく仕組みである。

状態異常が解除される条件は、状態異常によって多少変わるが以下のとおり。
敵側の石化を解除する方法はない(撃破と同じであるため)。

  1. 石化以外、状態異常継続時間が0になる。
  2. 状態異常を解除できる技(回復アイテム含む)で回復する。
  3. 眠り状態のみ、攻撃されると解除される。

1.はいわゆる、時間経過での解除にあたる。
他の解除方法で解除された場合、状態異常継続時間に0が上書きされるようになっている。

ここでは、状態異常の発生について具体的に説明していく。
状態異常が発生する条件は、既に世界の合言葉は森部様がとても詳しく調べていらっしゃるため、ここでは、実際にどういう処理がなされているのかを紹介する。

世界の合言葉は森部様によれば、状態異常の発生条件は以下の通りである。

 ・状態異常:
(自能力値+(0~(自能力値-1)))/2-敵能力値が正のとき状態異常発生(下の異常時間が17以上であることも条件)
 ・状態異常時間:
上の引き算結果*異常時間係数/8+自能力値/敵LV

 ・武器固有状態異常発生条件:
技が武器影響ありで、技自体に状態異常がない時?
 ・武器固有状態異常発生率:
((0~15)>敵LV)のとき状態異常(異常時間は自LV*8)

計算方法は少々ややこしい。
結論は既に上にある通りだが、筆者の調査の結果わかった、もう少し詳しい計算式は以下のようになる。
技による状態異常の発生条件は世界の合言葉は森部様と一致したのだが、武器で発生する状態異常については、世界の合言葉は森部様による説明とは少し異なる部分があり、どちらが正しいのかはわからない。
「自能力値」「敵能力値」「自LV」「敵LV」は技を使用した時点での値であり、ステータスアップ・ダウンの効果が反映される。

技による状態異常の発生条件:

敵が該当の状態異常を無効化せず、かつ、該当の状態異常に既にかかっていない時に、以下のように判定する。
多段ヒット技の場合でも、判定は各状態異常につき1回。
※計算途中の割り算はすべて小数点以下切り捨て。trunc()で表記すると見辛いので以下では記していない。

状態異常の判定値 = (自能力値 + 乱数(0~255)*自能力値/256)/2 - 敵能力値
が正の値の時、続けて状態異常継続時間を計算する。

状態異常継続時間 = min(状態異常の判定値*状態異常時間係数/8 + 自能力値/敵LV, 255)
状態異常継続時間が17以上の時、該当の状態異常が発生になる。

※「(自能力値 + 乱数(0~255)*自能力値/256)/2」は、自能力値の0.5倍~0.99倍(厳密には255/256倍)のことになる。

武器による状態異常の発生条件:

該当する武器は「ムラサメ」(眠り)、「ヨシユキ」(マヒ)のみ。
敵が該当の状態異常を無効化せず、使用技に状態異常の効果がない、かつ、武器の攻撃力依存の時に、

乱数(0~255) mod 256 - 敵LV(現在値)
= 乱数(0~15) - 敵LV(現在値)
0以上なら、状態異常が発生する。
※「mod」は剰余(割り算の余り)

状態異常継続時間 = 自LV(現在値)*8 mod 256
自LV(現在値)*8の計算結果の繰り上がりを考慮していないため、レベル32以上で状態異常発生時間のオーバーフローが起きてしまうようである。仕様なのかミスなのかは不明。

既に該当の状態異常が発生している場合でも、状態異常が起きるかどうか判定し、該当の状態異常発生時間を上書きする(加算はされない)。

このページではまず、技による状態異常の発生処理を説明する。
実際におぼろ丸の「忍法夢幻蝶」(毒と眠りの追加効果あり)を、毒と眠りの耐性がない敵1体に当てた場合の処理方法を見ていくことにする。
ちなみに「忍法夢幻蝶」は8ヒットするが、だからといって状態異常判定を8回行うことはない。
範囲攻撃なので2体以上に当たれば各攻撃相手に状態異常判定を行うが、1体につき1回分の判定である。
この処理はどの状態異常つきの技でも同じ。

状態異常関連のデータ

実際のサブルーチンを見る前に、計算に必要なデータを紹介していく。
まず、使用技の状態異常関係のデータが必要である。

技データ08・発生させる状態異常

1バイトのデータの中に8種類の状態異常の値が収納されている。
該当の状態異常に1が立つ。

2進法の位2726252423222120
状態異常石化酔い眠りマヒ腕かため足かため首かため

「忍法夢幻蝶」だと毒と眠りの状態異常があるので、2進数だと%0010 1000になる。16進数だと$28が入っている。

なお、敵味方の現在の状態異常の記録方法も上の表記と同じで、1バイトに8種類の状態異常を格納している。

技データ18・状態異常関連値

1バイトの中に、以下のように3種類のデータが入っている。

  • 上1~2ビット:状態異常自身依存ステータス
  • 上3~4ビット:状態異常敵依存ステータス
  • 下4ビット:状態異常時間係数

技の状態異常は、使用者自身の依存ステータスと攻撃される側(以下、敵とする)自身の依存ステータスの値が必要になる。
ステータスは力・速・体・知のいずれか。

数値内容
%00
%01
%10
%11

以上のように、上1~2ビットに使用者自身の依存ステータス、上3~4ビットに敵自身の依存ステータスが入る。

下4ビットは状態異常時間係数で、$0$Fが入る。

「忍法夢幻蝶」だと、使用者自身の依存ステータスも敵自身の依存ステータスも「知」で、状態異常時間係数は$A%1010)なので、技データ18の値は、%1111 1010 = $FAである。

戦闘中の味方キャラデータ

味方キャラのデータは、レベル(現在値)、使用者自身の依存ステータス(現在値)が必要になる。
戦闘中の味方キャラ4人分のデータは、順に$00:1C00~、$00:1C40~、$00:1C80~、$00:1CC0~に入るが、各アドレスに以下数値を加えると該当ステータスのアドレスになる。

アドレス加算値ステータス
+$34力(現在値)
+$35速(現在値)
+$36体(現在値)
+$37知(現在値)
+$38レベル(現在値)

実際にはレベル(現在値)は必要ないが、次ページの武器による状態異常関係で必要になるので、ついでで載せておく。

戦闘中の敵キャラデータ

敵1体目(敵番号1)のステータス及び状態異常関係のアドレスは以下のように入る。
敵番号2以降はアドレスを+10していく。

アドレス
$7E:AA03力(現在値)
$7E:AA04速(現在値)
$7E:AA05体(現在値)
$7E:AA06知(現在値)
$7E:AA07状態異常
$7E:AA08首かため 状態異常継続時間
$7E:AA09足かため 状態異常継続時間
$7E:AA0A腕かため 状態異常継続時間
$7E:AA0B毒 状態異常継続時間
$7E:AA0Cマヒ 状態異常継続時間
$7E:AA0D眠り 状態異常継続時間
$7E:AA0E酔い 状態異常継続時間
$7E:AB00レベル(現在値)

サブルーチン

以上を踏まえた上で、サブルーチンを見ていく。

$C1/785C LDA $D50000,x[$D5:2CEF] ;Aに[$D5:2CEF](技データ09)をロード
$C1/7860 STA $0000,y[$7E:9019]   ;Aを[$7E:9019]に書き込み
;(中略)
$C1/785C LDA $D50000,x[$D5:2CF8] ;Aに[$D5:2CF8](技データ18)をロード
$C1/7860 STA $0000,y[$7E:9022]   ;Aを[$7E:9022]に書き込み
;(中略)
$C1/D754 STZ $13    [$00:0313]   ;[$00:0313]に$00を書き込み

状態異常発生処理前の諸々の処理が上の通り。
技データはループ処理で全ての値が呼び出されて[$7E:9010][$7E:9028]に読み込まれるのだが、技データ09と技データ18は、上のようにそれぞれ[$7E:9019][$7E:9022]に書き込まれる。
また、$C1/D754で、[$00:0313]$00が書き込まれているが、[$00:0313]には後々で発生した状態異常を書き込んでいくことになる。
なお、$C1/D754の後あたりから、技で発生する行動異常の処理が入り、状態異常の処理はその後から開始になる。

以降が状態異常発生処理になる。

$C1/DC66 LDA $7E9019[$7E:9019]   ;[$7E:9019](技データ09)をロード
$C1/DC6A STA $12    [$00:0312]   ;A(技データ09)を[$00:0312]に書き込み
$C1/DC6C LDA $7E9022[$7E:9022]   ;[$7E:9022](技データ18)をロード
$C1/DC70 STA $14    [$00:0314]   ;A(技データ18)を[$00:0314]に書き込み
$C1/DC72 LDX $78    [$00:0378]   ;Xに[$00:0378]をロード
$C1/DC74 CPX #$1C00              ;Xと$1C00を減算比較(ステータスレジスタ変更のみ)
$C1/DC77 BCC $21    [$DC9A]      ;キャリーフラグが立っていないとき[$DC9A]分岐
$C1/DC79 AND #$C0                ;A(技データ18)と$C0で論理積
$C1/DC7B BEQ $0D    [$DC8A]      ;ゼロフラグが立っているとき[$DC8A]分岐(使用者依存ステータスが力の時)
$C1/DC7D CMP #$40                ;Aと$40を減算比較(ステータスレジスタ変更のみ)
$C1/DC7F BEQ $0E    [$DC8F]      ;ゼロフラグが立っているとき[$DC8F]分岐(使用者依存ステータスが速の時)
$C1/DC81 CMP #$80                ;Aと$80を減算比較(ステータスレジスタ変更のみ)
$C1/DC83 BEQ $0F    [$DC94]      ;ゼロフラグが立っているとき[$DC94]分岐(使用者依存ステータスが体の時)
$C1/DC85 LDA $0037,x             ;Aに[$0037,x](味方キャラ・知(現在値))をロード
$C1/DC88 BRA $0D    [$DC97]      ;フラグにかかわりなく常に[$DC97]分岐
;
;依存ステータス分岐先
$C1/DC8A LDA $0034,x             ;Aに[$0034,x]をロード(使用者依存ステータスが力の時)
$C1/DC8D BRA $08    [$DC97]      ;フラグにかかわりなく常に[$DC97]分岐
$C1/DC8F LDA $0035,x             ;Aに[$0035,x]をロード(使用者依存ステータスが速の時)
$C1/DC92 BRA $03    [$DC97]      ;フラグにかかわりなく常に[$DC97]分岐
$C1/DC94 LDA $0036,x             ;Aに[$0036,x]をロード(使用者依存ステータスが体の時)
;
$C1/DC97 STA $15    [$00:0315]   ;使用者依存ステータス(知(現在値))を[$00:0315]に書き込み
$C1/DC99 RTS                     ;サブルーチン戻り

$C1/DC66$C1/DC70で、先に[$7E:9019][$7E:9022]に読み込まれた技データ09と技データ18が、それぞれ[$00:0312][$00:0314]に書き込まれている。
$C1/DC79は、技データ18と$C0で論理積を取っている。
$C0 = %1100 0000だから、技データ18の上2ビット分の値の取り出しである。
技データ18の上2ビット分は、状態異常の使用者自身依存ステータスなので、依存ステータスにより以下のような値になる。

技データ18と$C0の論理積内容
%0000 0000
%0100 0000
%1000 0000
%1100 0000

よって、$C1/DC7Bでゼロフラグが立つのは、依存ステータスが「力」の時であり、[$DC8A]に分岐。
$C1/DC7Dでは$40 = %0100 0000と減算比較しているので、ゼロフラグが立つのは依存ステータスが「速」の時であり、[$DC8F]に分岐。
$C1/DC81では$80 = %1000 0000と減算比較しているので、ゼロフラグが立つのは依存ステータスが「体」の時であり、[$DC94]に分岐。
残るのは依存ステータスが「知」の時なので、$C1/DC85で使用者の「知(現在値)」である[$00:0037,x]をロードしている。
今回は「忍法夢幻蝶」の処理を見ていくので、依存ステータスは「知」になり、サブルーチンは$C1/DC88まで読み込むのだが、依存ステータスによる分岐[$DC8A][$DC8F][$DC94]も上に参考までに記しておいた。要するに、該当する依存ステータス値をAにロードする処理である。
そして、最終的に$C1/DC97で、使用者の依存ステータスの現在値が[$00:0315]に書き込まれる。

$C1/DB38 LDA $12    [$00:0312]   ;Aに[$00:0312](技データ09)をロード
$C1/DB3A BEQ $F5    [$DB31]      ;ゼロフラグが立っているとき[$DB31]分岐
$C1/DB3C LDA $14    [$00:0314]   ;Aに[$00:0314](技データ18)をロード
$C1/DB3E LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/DB40 AND #$30                ;A(技データ18)と$30で論理積
$C1/DB42 BEQ $0E    [$DB52]      ;ゼロフラグが立っているとき[$DB52]分岐(敵依存ステータスが力の時)
$C1/DB44 CMP #$10                ;Aと$10を減算比較(ステータスレジスタ変更のみ)
$C1/DB46 BEQ $10    [$DB58]      ;ゼロフラグが立っているとき[$DC8F]分岐(敵依存ステータスが速の時)
$C1/DB48 CMP #$20                ;Aと$20を減算比較(ステータスレジスタ変更のみ)
$C1/DB4A BEQ $12    [$DB5E]      ;ゼロフラグが立っているとき[$DC8F]分岐(敵依存ステータスが体の時)
$C1/DB4C LDA $7E8F06,x           ;Aに[$7E8F06,x](敵番号x・知(現在値))をロード
$C1/DB50 BRA $12    [$DB64]      ;フラグにかかわりなく常に[$DB64]分岐
$C1/DB64 STA $16    [$00:0316]   ;(敵番号x・知(現在値))を[$00:0316]に書き込み

$C1/DB38で技データ09をロードし、ゼロフラグ判定をしているが、技データ09には技で発生する状態異常の値が格納されている。
技データ09が$00ということは、該当技では状態異常は発生しないため、[$DB31]に飛ぶ。つまり状態異常の処理はしない。
使用者の依存ステータスを計算後に、状態異常が発生するかどうかを判定するという、妙な順序なのだが、とにかくここで状態異常発生処理を行うかどうかが判定されている。

$C1/DB3C以降は、敵依存ステータスの判定と該当ステータスの現在値のロードを行っている。つまり先の味方側の処理を敵側でも行うことになる。
$C1/DB40で、技データ18と$30で論理積を取っているが、$30 = %0011 0000なので、技データ18の上3~4ビットの取り出し、つまり状態異常敵依存ステータスを取り出している。
$C1/DB42でゼロフラグが立つのは、依存ステータスが「力」の時であり、[$DB52]に分岐。
$C1/DC7Dでは$10 = %0001 0000と減算比較しているので、ゼロフラグが立つのは依存ステータスが「速」の時であり、[$DC8F]に分岐。
$C1/DC81では$20 = %0010 0000と減算比較しているので、ゼロフラグが立つのは依存ステータスが「体」の時であり、[$DC8F]に分岐。
残るのは依存ステータスが「知」の時なので、$C1/DC85で敵番号xの「知(現在値)」である[$7E8F06,x]をロードしている。敵番号1なら[$7E:AA06]である。
この先は説明を簡単にするため、攻撃した敵は敵番号1としておく。
今回は「忍法夢幻蝶」の処理なので、敵依存ステータスは「知」になる。つまり敵番号1の「知(現在値)」がロードされた。
最終的に$C1/DC97で、敵依存ステータスの現在値が[$00:0316]に書き込まれる。

ここまでで、

  • [$00:0312]:技データ09
  • [$00:0313]$00(初期値)
  • [$00:0314]:技データ18
  • [$00:0315]:使用者の依存ステータスの現在値(おぼろ丸の「知」現在値)
  • [$00:0316]:敵の依存ステータスの現在値(敵番号1の「知」現在値)

が入ったということになる。

$C1/DB66 LDA $15    [$00:0315]   ;Aに[$00:0315]をロード
$C1/DB68 STA $4204  [$00:4204]   ;符号付16bit / 8bit 被除数(下1バイト)
$C1/DB6B LDA #$00                ;Aに$00をロード
$C1/DB6D STA $4205  [$00:4205]   ;符号付16bit / 8bit 被除数(上1バイト)
$C1/DB70 LDA $7E9000,x[$7E:AB00] ;Aに[$7E:AB00]	をロード
$C1/DB74 STA $4206  [$00:4206]   ;符号付16bit / 8bit 除数
$C1/DB77 JSR $46C8  [$C1:46C8]   ;[$C1:46C8]へジャンプ
;
$C1/46C8 NOP                     ;計算待機
$C1/46C9 RTS                     ;計算待機
;
$C1/DB7A LDA $4214  [$00:4214]   ;符号付16bit / 8bit 商(下1バイト)
$C1/DB7D STA $20    [$00:0320]   ;Aを[$00:0320]に書き込み

次に符号付16bit / 8bit(割り算)の計算を行っている。
[$00:4204][$00:4205]に被除数の下1バイト・上1バイト、[$00:4206]に除数を入れると、割り算の商(下1バイト)が[$00:4214]に入る。
被除数(下1バイト)に[$00:0315](使用者の依存ステータスの現在値)、上1バイトに$00、除数に[$7E:AB00]をセットしているが、[$7E:AB00]は先の表で紹介した通りに、敵番号1のレベル(現在値)である。
つまり、この計算は、

trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」)

になる。
割り算の商なので余りを考慮しないため、trunc(小数点以下切り捨て)である。
デバフにより、レベル(現在値)が0になる可能性もあるため、いわゆるゼロ除算(÷0)になる可能性もあるのだが、この符号付16bit / 8bitの方法だと、ゼロ除算で商と余りに$FFFFを返す仕組みになっているので、妙なエラーが出る心配はない。
何にせよ、上の計算結果は[$00:0320]に書き込みされる。

  • [$00:0312]:技データ09
  • [$00:0313]$00(初期値)
  • [$00:0314]:技データ18
  • [$00:0315]:使用者の依存ステータスの現在値(おぼろ丸の「知」現在値)
  • [$00:0316]:敵の依存ステータスの現在値(敵番号1の「知」現在値)
  • [$00:0320]trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」)
$C1/DB7F LDA #$08                ;Aに$08をロード
$C1/DB81 STA $08    [$00:0308]   ;A($08)を[$00:0308]に書き込み
$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐

ここから、状態異常8種類について、「技に該当の状態異常がセットされているか」をループ処理して判定していく。

まず、[$00:0308]$08を書き込みしているが、これは8種類ある状態異常判定のためのループ回数をセットしている。ループするたびに[$00:0308]の値を-1して、0になるまでループを繰り返す仕組みである。
続いて、[$00:0312](技データ09)を論理右シフトしている。
技データ09に入っているのは、技で発生する状態異常の値であり、2進数の上の桁から順に、石化・酔い・眠り・マヒ・毒・腕かため・足かため・首かために対応している。
「忍法夢幻蝶」は$28、2進数だと%0010 1000である。
論理右シフトは÷2の計算であり、2進数においては8桁の値をすべて右へ1つずつズラすことでもあるが、一番右の数値はキャリー分の値となり、1だった時には論理右シフト時にキャリーフラグが立つ。
「忍法夢幻蝶」の%0010 1000を論理右シフトすると、%0001 0100となり、一番右の値、つまり「首かため」に対応した値は0だったため、キャリーフラグは立たない。

次の$C1/DB85が、キャリーフラグによる分岐になっており、キャリーフラグが立ったら該当の状態異常の発生処理を行うサブルーチンへ、キャリーフラグが立たない場合は状態異常の発生処理をスキップし[$DBDC]へ、となっている。
つまり、論理右シフトで状態異常の値(2進数8桁)を回しながら、該当の状態異常に1が立っているかどうかキャリーフラグで判断していくループ処理である。
「忍法夢幻蝶」では「首かため」が発生しないので、$C1/DBDCにジャンプし、キャリーフラグをクリアしている。

次の処理$C1/DBDDがポイントで、[$00:0313]の値を、キャリーフラグを含めた9bitで右ローテートする。
[$00:0313]は、ここより前に、初期値として$00を入れた。つまり%0000 0000が入っている。
直前でキャリーフラグをクリアしたから、キャリーフラグを含めた9bitというのは、

%0 0000 0000

と、頭にキャリー分の0を加えた9桁である。
右ローテートは文字通りに、このまま右に1桁ずつ数値を移動させ、一番右の値はキャリー分として一番左に入る。つまり円のようにぐるぐる回す。
といっても現状ではすべて0が入っているから、回してもこのままである。

このままでは何の意味もない処理のように見えるが、実は状態異常が発生すると、キャリーフラグがONのまま、同じ処理をすることになる。
その処理はおいおい説明する。

$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312]を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐
;
$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312]を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐

「忍法夢幻蝶」での処理を続けてみていく。
状態異常の値が入った[$00:0312]は1回論理右シフトしているから、最初の$C1/DB83の論理右シフトでは、%0001 0100%0000 1010で、一番下の位に「足かため」の状態異常の有無の値が来る。
「忍法夢幻蝶」は「足かため」の効果もないので、先程と同じ処理が続く。
$C1/DBDD[$00:0313]の右ローテートも、「%0 0000 0000」のままである。

次のループの処理は、最初の$C1/DB83の論理右シフトで、[$00:0312]%0000 1010%0000 0101、「腕かため」も効果なしなので、やはり処理は変わらない。

$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DB87 LDA $7E8F08,x[$7E:AA0B] ;Aに[$7E:AA0B](敵1 毒回復までの残り時間)をロード
$C1/DB8B BNE $4F    [$DBDC]      ;ゼロフラグが立っていないとき[$DBDC]分岐
$C1/DB8D JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ
;
$C1/6150 PHX                     ;戦闘乱数計算
$C1/6151 PHB                     ;↓
;(省略)
$C1/61A3 PLX                     ;↓
$C1/61A4 RTS                     ;乱数がAと[$00:033B]に入る

[$00:0312]が論理右シフトで%0000 0101%1000 0010、「毒」状態の時はキャリーフラグが立つ。
ここで「忍法夢幻蝶」の「毒」状態についての計算が開始になる。
まず、[$7E:AA0B](敵1 毒 状態異常継続時間)がロードされ、ゼロフラグが立っていないのならこの先の処理はスキップされる。
つまり、既に毒状態で、状態異常継続時間が0以上の時、新たに毒状態にする処理は行われない。
ここから、

何かしらの状態異常にある敵をもう一度同じ状態異常にして、状態異常継続時間を伸ばし続けることはできない。

ということがわかる。
例えば原始編のキングマンモー相手に完封したい場合、マヒ状態の時にマヒを続けて入れるだけでは状態異常継続時間が伸びないので、かならずどこかのタイミングでマヒが切れる。
眠り状態とマヒ状態を交互に入れることで、どちらかの状態異常の継続時間が切れたらどちらかの状態異常が働いている状態にし、キングマンモーを行動させないようにすることが可能になる。

話が少し逸れたが、ここでは、敵は「忍法夢幻蝶」をくらった時点で何の状態異常にもなっていない前提として先を進める。
その場合は戦闘乱数の生成処理に進む。
戦闘乱数生成サブルーチンは既に紹介済みなので、該当部分の$C1/6150$C1/61A4の説明は省略する。
この時点で$00$FFの乱数がA[$00:033B]に入る。

$C1/DB90 STA $4202  [$00:4202]   ;符号付8bit x 8bit 被乗数
$C1/DB93 LDA $15    [$00:0315]   ;Aに[$00:0315]をロード
$C1/DB95 STA $4203  [$00:4203]   ;符号付8bit x 8bit 乗数
$C1/DB98 NOP                     ;乗算計算待機
$C1/DB99 NOP                     ;乗算計算待機
$C1/DB9A NOP                     ;乗算計算待機
$C1/DB9B CLC                     ;キャリーフラグクリア
$C1/DB9C ADC $4217  [$00:4217]   ;A + 符号付8bit x 8bit結果の上位1バイト
$C1/DB9F ROR A                   ;Aにキャリーフラグを含めた9bitで右ローテート
$C1/DBA0 SEC                     ;キャリーフラグON
$C1/DBA1 SBC $16    [$00:0316]   ;A-[$00:0316](敵1 知(現在値))
$C1/DBA3 BEQ $37    [$DBDC]      ;ゼロフラグが立っているとき[$DBDC]分岐
$C1/DBA5 BCC $35    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBA7 STA $17    [$00:0317]   ;Aを[$00:0317]に書き込み

ここから符号付8bit x 8bitの計算を行う。
[$00:4202]に被乗数、[$00:4203]に乗数を入れると、計算結果の上1バイトが[$00:4217]、下1バイトが[$00:4216]に入る。
生成した乱数は、符号付8bit x 8bitの計算の被乗数としてセットされている。
更に、[$00:0315]が乗数としてセットされているので、この乗算は、

乱数($00~$FF)×使用者依存ステータス(現在値)

である。
$C1/DB9Cで、Aに[$00:4217]を加算している。
Aには[$00:0315]が入ったままであり、[$00:4217]には乗算結果の上1バイトが入っている。つまり乗算結果4桁の上2桁部分だから、乗算結果を$100で割った値である。
つまり、$C1/DB9Cの計算結果は、

使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100
trunc表記は見づらくなるので省く)

乱数×使用者依存ステータス(現在値)/$100」の部分だが、乱数は$00$FFだから、使用者依存ステータス(現在値)に$00$FFを掛けて$100で割る場合、使用者依存ステータス(現在値)の0~255/256倍の値である。
これに、更に「使用者依存ステータス(現在値)」を足しているので、結局、

使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100

というのは、「使用者依存ステータス(現在値)」の1倍~511/256倍の値の算出を意味する。

例:
おぼろ丸の知が99の時だったら、16進数で$63だから、

使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100
= $63 + ($00~$FFの乱数)*$63/$100
= $63 + 0 ~ $63 + $FF*$63/$100
= $63 ~ $C5

10進数だと99197で、元の知の値の1倍~約1.99倍が乱数で計算されたことになる。

ステータスと乱数次第では、計算結果が$100を越える、つまりキャリーフラグが立つことになる。
次の$C1/DB9Fは、上の結果をキャリーフラグを含めた9bitで右ローテートする。つまり、キャリーフラグ含めて÷2するので、

(使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100)/2

次の$C1/DBA1で、キャリーフラグを立ててから[$00:0316](敵依存ステータス(現在値))を引いている。

(使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100)/2 - 敵依存ステータス(現在値)

ここでゼロフラグが立つか、キャリーフラグが立たない場合は、処理中止で[$DBDC]分岐になり、それ以外だと計算結果を[$00:0317]に書き込む。
つまり、上の計算結果が0か負になった場合は、状態異常が発生しないことになる。正の値だった時のみ、計算結果が[$00:0317]に入って、処理が続く。
大雑把に言えば、使用者依存ステータスの0.5倍~0.99倍(厳密には255/256倍)と敵依存ステータスを比較して、前者の方が大きいのなら状態異常発生、ということになる。

例:
おぼろ丸の知が99の時だったら、先程の計算の続きで、$63$C5を2で割って、
$31$62
10進数だと4998
敵の知の値がこの値未満なら、状態異常発生条件を満たす。
また、敵の知の値が49未満なら、乱数が何であっても必ず状態異常発生条件を満たすことになり、敵の知の値が98以上だと、乱数が何であっても状態異常が発生しないことになる。

[$00:0317]の最大値は、使用者依存ステータスが$FF、敵依存ステータスが$00、乱数が$FFの時の「$FE」になる。

  • [$00:0312]:技データ09
  • [$00:0313]$00(初期値)
  • [$00:0314]:技データ18
  • [$00:0315]:使用者の依存ステータスの現在値(おぼろ丸の「知」現在値)
  • [$00:0316]:敵の依存ステータスの現在値(敵番号1の「知」現在値)
  • [$00:0317](使用者依存ステータス(現在値) + 乱数×使用者依存ステータス(現在値)/$100)/2 - 敵依存ステータス(現在値)
  • [$00:0320]trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」)

さて、以上で状態異常発生条件のひとつが満たされただけで、ここから先に更に状態異常時間に関する計算が続く。

$C1/DBA9 STA $4202  [$00:4202]   ;符号付8bit x 8bit 被乗数
$C1/DBAC LDA $14    [$00:0314]   ;[$00:0314](技データ18)をロード
$C1/DBAE AND #$0F                ;Aと$0Fで論理積(下4ビット:状態異常時間係数)
$C1/DBB0 STA $4203  [$00:4203]   ;符号付8bit x 8bit 乗数
$C1/DBB3 NOP                     ;乗算計算待機
$C1/DBB4 NOP                     ;乗算計算待機
$C1/DBB5 NOP                     ;乗算計算待機
$C1/DBB6 NOP                     ;乗算計算待機
$C1/DBB7 LDA $4217  [$00:4217]   ;Aに符号付8bit x 8bit答えの上バイトをロード
$C1/DBBA AND #$F8                ;Aと$F8(%1111 1000)で論理積
$C1/DBBC BNE $0F    [$DBCD]      ;ゼロフラグが立っていないとき[$DBCD]分岐
$C1/DBBE REP #$20                ;Mフラグをクリア Aレジスタは16bit幅
$C1/DBC0 LDA $4216  [$00:4216]   ;Aに符号付8bit x 8bit答えの下バイトをロード
$C1/DBC3 LSR A                   ;論理右シフト /2
$C1/DBC4 LSR A                   ;論理右シフト /2
$C1/DBC5 LSR A                   ;論理右シフト /2
$C1/DBC6 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C1/DBC8 CLC                     ;キャリーフラグクリア
$C1/DBC9 ADC $20    [$00:0320]   ;A+[$00:0320](trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」))
$C1/DBCB BCC $02    [$DBCF]      ;キャリーフラグが立っていないとき[$DBCF]分岐
$C1/DBCD LDA #$FF                ;Aに$FFをロード
$C1/DBCF CMP #$11                ;Aと$11(17)を減算比較
$C1/DBD1 BCC $09    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBD3 STA $7E8F08,x[$7E:AA0B] ;Aを[$7E:AA0B]に書き込み

$C1/DBA9は先程までの計算結果[$00:0317]を被乗数、$C1/DBB0は技データ18と$0Fの論理積で下4ビットの「状態異常時間係数」を取り出し乗数としてセットしている。
つまり[$00:0317]×状態異常時間係数を計算し、$C1/DBB7でこの答えの上1バイトが入る[$00:4217]Aにロードしている。
$C1/DBB7の時点でのAは、

[$00:0317]*状態異常時間係数/$100

である。
$C1/DBBAではA$F8%1111 1000)で論理積を取り、ゼロフラグが立つかどうかを判断している。
$F8と論理積を取ってゼロフラグが立つのは(%0000 0???)の時、つまりA$07以下の時。
ゼロフラグが立たないのはA$08以上になる。
Aに入っているのは、[$00:0317]×状態異常時間係数の上1バイトだから、ゼロフラグが立たないのは「掛け算の答えが$0800以上」と言い換えることができる。
この後の$C1/DBC0~の処理を見るとわかるが、この時点で状態異常時間の判定をするまでもなく大きな値になっているので、ゼロフラグが立たない時は$C1/DBCDにジャンプしてA$FFをロードしている。

では、ゼロフラグが立つ場合の$C1/DBC0以降の処理を見ていく。
Aを16bitモードに変更後、A[$00:4216]をロードしている。つまり、先の掛け算の答えを16ビットでAにロードした。この時の答えは$07FF以下のはずである。
$C1/DBC3$C1/DBC5で論理右シフトを3回繰り返しているので、÷8である。
ここでAを8bitモードに変更している。掛け算の答えが$07FF以下だったから、÷8の結果、Aは$FF以下になっているから、8bitモードに変更しても値は変わらない。
$C1/DBC9で、A[$00:0320]を加算している。[$00:0320]trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」)が入っているから、

trunc([$00:0317]*状態異常時間係数/$8) + trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」)

という計算になる。これが「状態異常継続時間」になる。
キャリーフラグが立つかどうかで分岐になるが、キャリーフラグが立つ場合は、加算で繰り上がりが起き、計算結果は$100以上だったことになる。
この時は$C1/DBCDに進みA$FFをロードしている。
計算結果が$FF以下の時は、$C1/DBCFに進み、計算結果と$11(10進数17)と減算比較している。
この結果でキャリーフラグが立っていない、つまり減算の結果が負の数だったら、状態異常は発生せず[$DBDC]に飛ぶ。
減算の結果が0以上なら、キャリーフラグが立たず、状態異常が発生する。
つまり、

状態異常継続時間 = [$00:0317]*状態異常時間係数/$8 + trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」

が、17以上の時に状態異常が発生する。
ただし最大値は$FFだから、厳密には、

状態異常継続時間 = min(trunc([$00:0317]*状態異常時間係数/$8) + trunc(「使用者依存ステータス(現在値)」/「敵番号1レベル(現在値)」), $FF)

である。
$C1/DBD3で、状態異常継続時間が[$7E:AA0B]に格納される。

ここまでをまとめると、

状態異常の判定値[$00:0317] = (使用者依存ステータス(現在値) + 乱数($00$FF)×使用者依存ステータス(現在値)/$100)/2 - 敵依存ステータス(現在値)
が正の値の時、続けて状態異常継続時間を計算する。

状態異常継続時間 = min(trunc([$00:0317]*状態異常時間係数/$8) + trunc(使用者依存ステータス(現在値) / 敵番号1レベル(現在値)), $FF)
$11(10進数17)以上の時、該当の状態異常が発生になる。

ということがわかった。
これは世界の合言葉は森部様の以下の計算と同一になる。

 ・状態異常:
(自能力値+(0~(自能力値-1)))/2-敵能力値が正のとき状態異常発生(下の異常時間が17以上であることも条件)
 ・状態異常時間:
上の引き算結果*異常時間係数/8+自能力値/敵LV

$C1/DBD7 SEC                     C;キャリーフラグON
$C1/DBD8 ROR $13    [$00:0313]   C;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDA BRA $03    [$DBDF]      c;フラグにかかわりなく常に分岐[$DBDF]
$C1/DBDF INX                     c;X+1
$C1/DBE0 DEC $08    [$00:0308]   c;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      c;ゼロフラグが立っていないとき[$DB83]分岐

状態異常が発生することが決定した後は、$C1/DBD7でキャリーフラグをONにする。
$C1/DBD8で、先程までは何の意味もなかった「[$00:0313]をキャリーフラグを含めた9bitで右ローテート」を行うのだが、今回はキャリーフラグが入っているから、

%1 0000 0000%0 1000 0000

ここで初めて[$00:0313]%1000 0000という値が入った。
これが「毒の状態異常が発生した」処理になる。
$C1/DBDFでようやく、状態異常判定ループに戻ってきたことになり、X+1[$00:0308]-1の処理をして、毒の次の状態異常であるマヒの判定に移る。

$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1

[$00:0312]の論理シフトは%1000 0010%0100 0001なので、キャリーフラグは立たない。マヒの判定となる。
$C1/DBDDの右ローテートは、さきほど毒の状態異常の処理が入ったから、
%0 1000 0000%0 0100 0000
である。
[$00:0313]%0100 0000が入り、次の処理へ移る。

$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐
$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DB87 LDA $7E8F08,x[$7E:AA0D] ;Aに[$7E:AA0D](敵1 眠り 状態異常継続時間)をロード
$C1/DB8B BNE $4F    [$DBDC]      ;ゼロフラグが立っていないとき[$DBDC]分岐
$C1/DB8D JSR $6150  [$C1:6150]   ;[$C1:6150]へジャンプ

[$00:0312]の論理シフトは%0100 0001%1010 0000でキャリーフラグが立つ。眠りの判定である。
よって眠り状態にできるかどうかの判定が行われる。
A[$7E:AA0D](敵1 眠り 状態異常継続時間)をロードし、0の時は戦闘乱数生成へ移る。
この後の処理は、毒状態の時と同じである。変わるのは乱数の値だけである。
乱数が変わるので、ステータス次第で「毒状態にはできたが眠り状態にはできない」や、その逆もあり得るし、もし毒状態にも眠り状態にもできたとしても、状態異常継続時間に異なる値が入ることもある。

ここでは処理の紹介は省略し、眠り状態にできたものとする。

$C1/DBD7 SEC                     ;キャリーフラグON
$C1/DBD8 ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDA BRA $03    [$DBDF]      ;フラグにかかわりなく常に分岐[$DBDF]
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐

眠り状態にできた場合は、毒状態と同じく、[$00:0313]に対してキャリーフラグ分の値を入れて右ローテートする。

%1 0100 0000%0 1010 0000

[$00:0313] = %1010 0000となる。

$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート 
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐

$C1/DB83 LSR $12    [$00:0312]   ;[$00:0312](技データ09)を論理右シフト(/2)
$C1/DB85 BCC $55    [$DBDC]      ;キャリーフラグが立っていないとき[$DBDC]分岐
$C1/DBDC CLC                     ;キャリーフラグクリア
$C1/DBDD ROR $13    [$00:0313]   ;[$00:0313]をキャリーフラグを含めた9bitで右ローテート
$C1/DBDF INX                     ;X+1
$C1/DBE0 DEC $08    [$00:0308]   ;[$00:0308]-1
$C1/DBE2 BNE $9F    [$DB83]      ;ゼロフラグが立っていないとき[$DB83]分岐

残るは酔い状態と石化状態の処理で、「忍法夢幻蝶」ではどちらの状態異常も入らないから、

酔い状態の[$00:0313]
%0 1010 0000→%0 0101 0000

石化状態の[$00:0313]
%0 0101 0000→%0 0010 1000

となって、最終的に[$00:0313]には%0010 1000が入った。
つまり毒状態と眠り状態に対応した位に1が立って、他は0のままである。
石化処理を終えた時点で、ループ回数を入れてある[$00:0308]0になるため、$C1/DBE2でゼロフラグが立ち、状態異常処理ループを抜ける。

$C1/DBE4 LDX $10    [$00:0310]   ;Xに[$00:0310]をロード
$C1/DBE6 LDY $0002,x[$00:1B02]   ;Yに[$00:1B02]をロード
$C1/DBE9 LDA $002A,y[$00:1A2A]   ;Aに[$00:1A2A]をロード
$C1/DBEC EOR #$FF                ;Aと$FFで排他的論理和
$C1/DBEE AND $13    [$00:0313]   ;Aと[$00:0313]で論理積
$C1/DBF0 STA $7E900C,x[$7E:AB0C] ;Aを[$7E:AB0C]に書き込み
$C1/DBF4 RTS                     ;サブルーチン戻り

$C1/DBE9で、[$00:1A2A](敵種類1の敵データ10)をAにロードしている。
敵データ10に入っているのは、無効状態異常の値であり、8ビット中、石化・酔い・眠り・マヒ・毒・腕かため・足かため・首かための順に、無効化できる状態異常に1、無効化できない状態異常に0が入っている。
ここでは敵がすべての状態異常を無効化できない、つまり敵データ10 = $00としておく。

$C1/DBEC$FF%1111 1111)と排他的論理和を取っている。
排他的論理和は「どちらかが1なら1、それ以外は0」にする処理であるから、$FFとの排他的論理和は実質、敵データ10の8ビットの値をすべて逆(0なら11なら0)にする処理と同じである。
このため、無効化できる状態異常に0、無効化できない状態異常に1が入る。
敵データ10 = $00だったら、$FFと排他的論理和をとると$FFになる。
この値と[$00:0313]、つまり状態異常処理ループにより、状態異常にできるビットに1が入っている数値と論理積を取る。
「状態異常にできる値に1が立っている8ビットの数値」と「状態異常にした値に1が立っている8ビットの数値」で論理積を取ったことで、最終的に状態異常にできる状態異常が決定となった。

ということは、仮に「忍法夢幻蝶」を当てた敵が全状態異常を無効化できるとしても、まずは「状態異常にできるかどうか」をステータスから判定し、その後に敵の無効状態異常の値を見て、すべての状態異常にできなかった、という判定を行うことになる。
順序としては何か妙な気もするが、色々な事情があるのであろう。
とにかく、これで決まった状態異常の値は、[$7E:AB0C]に書き込まれる。
この段階ではまだ、敵番号1の状態異常のアドレスには書き込んでいない。処理がまだ続くことになる。

$C1/BD6C LDA $7E9003,x[$7E:AB03] ;Aに[$7E:AB03]をロード
$C1/BD70 BEQ $10    [$BD82]      ;ゼロフラグが立っているとき[$BD82]分岐
$C1/BD72 LDA $7E900C,x[$7E:AB0C] ;Aに[$7E:AB0C]をロード
$C1/BD76 BEQ $0A    [$BD82]      ;ゼロフラグが立っているとき[$BD82]分岐
$C1/BD78 ORA $7E8F07,x[$7E:AA07] ;Aと[$7E:AA07](敵番号1の状態異常)で論理和
$C1/BD7C STA $7E8F07,x[$7E:AA07] ;Aを[$7E:AA07](敵番号1の状態異常)に書き込み
$C1/BD80 INC $0F    [$00:030F]   ;[$00:030F]をインクリメント
$C1/BD82 RTS                     ;サブルーチン戻り

$C1/BD6Cで、[$7E:AB03]をロードしているが、これは「忍法夢幻蝶」の敵番号1へのヒット数である。
多段ヒット技の「忍法夢幻蝶」が敵番号1に1ヒット以上していなければそもそも状態異常にできないので、ここでゼロフラグが立っていないかどうかを判定している。
(多段ヒットする範囲攻撃に2体以上の敵を巻き込んだ場合、どの敵に命中するかは1ヒット毎に乱数で判定している)

続いて状態異常の値[$7E:AB0C]がロードされ、ゼロフラグが立っていない場合は、敵番号1の状態異常[$7E:AA07]の値と論理和を取る。
つまり、既に発生している状態異常の値と、新たに発生した状態異常の値が加算される。
それを新たな状態異常の値として、[$7E:AA07]に書き込み、状態異常発生処理は終了である。

まとめると以下のようになる。

技による状態異常の発生条件:

技に設定された状態異常を、当てる相手が無効化せず、かつ、既にその状態異常になっていない時に、状態異常を発生させるか以下のように判定する。

  1. 状態異常の判定値[$00:0317] = (使用者依存ステータス(現在値) + 乱数($00~$FF)×使用者依存ステータス(現在値)/$100)/2 - 敵依存ステータス(現在値)

  2. が正の値の時、続けて状態異常継続時間を計算する。
  3. 状態異常継続時間 = min(trunc([$00:0317]*状態異常時間係数/$8) + trunc(使用者依存ステータス(現在値) / 敵番号1レベル(現在値)), $FF)
    $11(10進数17)以上の時、該当の状態異常が発生になる。

次ページでは、武器の追加効果として状態異常が発生する場合について説明する。



このページをシェアする

上へ