基礎知識
戦闘関係
味方キャラがレベルアップすると、HP・力・速・体・知が、キャラに設定された値だけ上昇する(上限は、HPが999、力・速・体・知が99)。
ただし、上昇値にブレ幅がある場合が多い。
例えば、おぼろ丸であれば、
である。
上昇値に幅がある場合は、実際の上昇値が乱数で決められているはずだが、プログラム上ではどのように計算されているのだろうか。
まず、そもそも上のステータス上昇値がどこに記録されているのかから説明する。
味方キャラデータには、レベルアップ時のHP・力・速・体・知上昇値を計算するための値が収納されている。
該当するのは、味方キャラデータ21~24と27である。各1バイトのデータが格納されている。
ゲーム内でレベルアップしないキャラにも設定されている。
| No. | 内容 |
|---|---|
| 21 | レベルアップ時 力上昇値 |
| 22 | レベルアップ時 速上昇値 |
| 23 | レベルアップ時 体上昇値 |
| 24 | レベルアップ時 知上昇値 |
| 27 | レベルアップ時 HP上昇値 |
計算方法は力・速・体・知とHPとで異なる。
特にHP上昇値については少々ややこしい。
味方キャラデータ27の値を使って計算する。
上2ビットに計算方法パターン、下6ビットに計算用の数値が入っている。
便宜上、下の表では味方キャラデータ27の下6ビットの値を、「計算値」とする。
x, y は計算に使うための変数。
x が基礎値、y が加算値のようなもので、基礎値に加算値を加えて上昇値とするのだが、加算値には乱数でブレを生じさせる仕組みである。
| 上2ビット | 基礎値x | 加算値y |
|---|---|---|
| %00 | 計算値 | 計算値/2 |
| %01 | 計算値 | 計算値 |
| %10 | 計算値/2 | 計算値 |
| %11 | 0 | 計算値 |
HP上昇の下限値は、
下限値 = x
HP上昇の上限値は、
上限値 = x + trunc(y * $FF/$100)
このように計算される。
上限値の$FF/$100の部分だが、ゲームでは乱数で決まる部分で、最大が$FF/$100である。
yの値が0でなければ、「y * $FF/$100」の計算値は「y - 1」に等しい。
よって、
上限値 = x + max(y - 1, 0)
これでも構わない。
例:アキラの場合
味方キャラデータ27 = $94
下6ビット = $14
上2ビット = $80(上2ビット10)
下限値 = 下6ビット/2
= $14/2
= $0A(10進数10)
上限値 = 下6ビット/2 + max(下6ビット-1, 0)
= $14/2 + max($14-1, 0)
= $0A + $13
= $1D(10進数29)
HP上昇値は、10~29ということになる。
例:サモの場合
味方キャラデータ27 = $FC
下6ビット = $3C
上2ビット = $C0(上2ビット11)
下限値 = 0
上限値 = 0 + max(下6ビット-1, 0)
= $3C - $1
= $3B(10進数59)
HP上昇値は、0~59ということになる。
計算方法は4種とも共通。
味方キャラデータ21~24の値を使って計算する。
上4ビットに基礎値、下4ビットに加算値が入っている。
よって、HP上昇の下限値は、
下限値 = 基礎値 = (データ21~24) & $F0 / $10
HP上昇の上限値は、
上限値 = 基礎値 + (データ21~24)4 & $0F * $FF/$100
= 基礎値 + max((データ21~24) & $0F - 1, 0)
このように計算される。
例:高原 日勝の場合
味方キャラデータ21~24に$52, $42, $11, $01と値が入っているので、
| 能力 | 下限値 | 上限値 |
|---|---|---|
| 力 | $52 & $F0 / $10 = $5 | max($5 + $52 & $0F -1, 0) = $6 |
| 速 | $42 & $F0 / $10 = $4 | max($4 + $42 & $0F -1, 0) = $5 |
| 体 | $11 & $F0 / $10 = $1 | max($1 + $11 & $0F -1, 0) = $1 |
| 知 | $01 & $F0 / $10 = $0 | max($0 + $01 & $0F -1, 0) = $0 |
力上昇値は5~6、速上昇値は4~5、体上昇値は1、知上昇値は0となる。
以上が、味方キャラデータからステータス上昇値を計算する方法になる。
上でも説明したが、実際の上昇値は乱数によりブレが生じる。
HP・力・速・体・知全て、基礎値 + 加算値*$FF/$100という計算になるのだが、実際には乱数$00~$FF/$100を加算値に掛け算するので、加算値は0~上限まで均等な確率でいずれかになる。
例えばアキラのHPは上で計算した通り、HP上昇値が10~29の19通りから選ばれるが、どの値もほぼ均等の1/19で選ばれることになる。
では、実際の処理を見てみよう。
前ページの経験値計算の処理の後に、レベルアップする場合はレベルアップ時のモーションなどが入るが、以下のステータス上昇値の計算は、そのモーション直後に行われるようだ。
レベルアップ時、ステータス上昇値の計算は、HP・力・速・体・知の順に行われる。
計算の際には、上で紹介した、味方キャラデータ21~24と27、そして計算した値をキャラの新たなステータスとするため、シナリオ別キャラデータのアドレスが使われることになる。
$C1/2459 LDA $D5001B,x ;Aに[$D5001B,x](味方キャラデータ27・HP上昇値)をロード $C1/245D JSR $1A09 [$C1:1A09] ;[$C1:1A09]へジャンプ ; $C1/1A09 STA $10 [$00:0310] ;A(味方キャラデータ27)を[$00:0310]に書き込み $C1/1A0B AND #$3F ;A(味方キャラデータ27)と$3Fで論理積(下6ビット取り出し) $C1/1A0D STA $20 [$00:0320] ;Aを[$00:0320]に書き込み $C1/1A0F LDA $10 [$00:0310] ;Aに[$00:0310](味方キャラデータ27)をロード $C1/1A11 AND #$C0 ;[$00:0310](味方キャラデータ27)と$C0で論理積(上2ビット取り出し) $C1/1A13 BEQ $12 [$1A27] ;ゼロフラグが立っているとき[$1A27]分岐 ※上2ビットが00 $C1/1A15 CMP #$40 ;Aと$40($0100 0000)を減算比較 $C1/1A17 BEQ $17 [$1A30] ;ゼロフラグが立っているとき[$1A30]分岐 ※上2ビットが01 $C1/1A19 CMP #$80 ;Aと$80($1000 0000)を減算比較 $C1/1A1B BEQ $1B [$1A38] ;ゼロフラグが立っているとき[$1A38]分岐 ※上2ビットが10 $C1/1A1D LDA $20 [$00:0320] ;Aに[$00:0320](下6ビット)をロード ※上2ビットが11 $C1/1A1F STZ $21 [$00:0321] ;[$00:0321] = 0 $C1/1A21 STA $22 [$00:0322] ;[$00:0322] = 下6ビット $C1/1A23 STA $23 [$00:0323] ;[$00:0323] = 下6ビット $C1/1A25 BRA $1A [$1A41] ;フラグにかかわりなく常に分岐[$1A41] $C1/1A27 LDA $20 [$00:0320] ;※上2ビットが00のジャンプ先 Aに[$00:0320](下6ビット)をロード $C1/1A29 STA $21 [$00:0321] ;[$00:0321] = 下6ビット $C1/1A2B LSR A ;論理右シフト /2 $C1/1A2C STA $22 [$00:0322] ;[$00:0322] = 下6ビット/2 $C1/1A2E BRA $0F [$1A3F] ;フラグにかかわりなく常に分岐[$1A3F] $C1/1A30 LDA $20 [$00:0320] ;※上2ビットが01のジャンプ先 Aに[$00:0320](下6ビット)をロード $C1/1A32 STA $21 [$00:0321] ;[$00:0321] = 下6ビット $C1/1A34 STA $22 [$00:0322] ;[$00:0322] = 下6ビット $C1/1A36 BRA $07 [$1A3F] ;フラグにかかわりなく常に分岐[$1A3F] $C1/1A38 LDA $20 [$00:0320] ;※上2ビットが10のジャンプ先 Aに[$00:0320](下6ビット)をロード $C1/1A3A STA $22 [$00:0322] ;[$00:0322] =下6ビット $C1/1A3C LSR A ;論理右シフト /2 $C1/1A3D STA $21 [$00:0321] ;[$00:0321] = 下6ビット/2 $C1/1A3F STZ $23 [$00:0323] ;[$00:0323] = 0 $C1/1A41 JSR $6150 [$C1:6150] ;[$C1:6150]へ
$C1/2459でまず、[$D5001B,x](味方キャラデータ27・HP上昇値)をロードしている。Xの値はここまでに、該当キャラのアドレスを呼び出すために計算されているがここでは省く。
その後は、先に述べた通り、味方キャラデータ27の上2ビットによる分岐を行っている。
以下、味方キャラデータ27の下6ビットの値は単に「下6ビット」と省略する。
長くなるので細かい説明は省くが、共通して[$00:0320]に下6ビットが入り、分岐により、アドレス[$00:0321]~[$00:0323]に以下のように値が入っていく。
| 上2ビット | [$00:0321] | [$00:0322] | [$00:0323] |
|---|---|---|---|
| 00 | 下6ビット | 下6ビット/2 | 0 |
| 01 | 下6ビット | 下6ビット | 0 |
| 10 | 下6ビット/2 | 下6ビット | 0 |
| 11 | 0 | 下6ビット | 下6ビット |
後の処理でわかるのだが、実は[$00:0323]の値は、HP上昇値計算には関係がない。
最後に[$C1:6150]にジャンプしているが、これは戦闘用乱数の計算を行うサブルーチンである。
$C1/6150 PHX ;戦闘乱数計算 $C1/6151 PHB ;↓ $C1/6152 LDA #$00 ;↓ $C1/6154 PHA ;↓ $C1/6155 PLB ;↓ $C1/6156 LDA $38 [$00:0338] ;↓ $C1/6158 ORA $39 [$00:0339] ;↓ $C1/615A ORA $3A [$00:033A] ;↓ $C1/615C ORA $3B [$00:033B] ;↓ $C1/615E BNE $24 [$6184] ;↓ $C1/6184 LDX $38 [$00:0338] ;↓ $C1/6186 STX $4204 [$00:4204] ;↓ $C1/6189 LDA #$0D ;↓ $C1/618B STA $4206 [$00:4206] ;↓ $C1/618E JSR $46C8 [$C1:46C8] ;↓ ; $C1/46C8 NOP ;↓ $C1/46C9 RTS ;↓ ; $C1/6191 LDA $39 [$00:0339] ;↓ $C1/6193 STA $38 [$00:0338] ;↓ $C1/6195 LDA $3A [$00:033A] ;↓ $C1/6197 STA $39 [$00:0339] ;↓ $C1/6199 LDA $3B [$00:033B] ;↓ $C1/619B STA $3A [$00:033A] ;↓ $C1/619D LDA $4214 [$00:4214] ;↓ $C1/61A0 STA $3B [$00:033B] ;↓ $C1/61A2 PLB ;↓ $C1/61A3 PLX ;↓ $C1/61A4 RTS ;乱数(1)がAと[$00:033B]に入る ; $C1/1A44 STA $4202 [$00:4202] ;符号付8bit x 8bit 被乗数 = 乱数(1) $C1/1A47 LDA $23 [$00:0323] ;Aに[$00:0323]をロード $C1/1A49 STA $4203 [$00:4203] ;符号付8bit x 8bit 乗数 = [$00:0323] $C1/1A4C JSR $6150 [$C1:6150] ;[$C1:6150]へ
戦闘用乱数計算サブルーチンの説明は省略する。
上に乱数(1)と書いたが、この後にもう一度乱数計算があるので、区別するため(1)を付けた。
計算された乱数(1)は、$C1/1A44で[$00:4202]に書き込まれているが、これは符号付8bit x 8bitの乗算を行うため。
符号付8bit x 8bitは、[$00:4202]に被乗数、[$00:4203]に乗数を入れると、[$00:4216]に乗算結果の下1バイト、[$00:4217]に乗算結果の上1バイトの合計2バイトが出力される。
上では、乱数(1)×[$00:0323]を行おうとしていることがわかる。
この後もう一度、戦闘用乱数計算の[$C1:6150]へジャンプする。
$C1/6150 PHX ;戦闘乱数計算 $C1/6151 PHB ;↓ $C1/6152 LDA #$00 ;↓ $C1/6154 PHA ;↓ $C1/6155 PLB ;↓ $C1/6156 LDA $38 [$00:0338] ;↓ $C1/6158 ORA $39 [$00:0339] ;↓ $C1/615A ORA $3A [$00:033A] ;↓ $C1/615C ORA $3B [$00:033B] ;↓ $C1/615E BNE $24 [$6184] ;↓ $C1/6184 LDX $38 [$00:0338] ;↓ $C1/6186 STX $4204 [$00:4204] ;↓ $C1/6189 LDA #$0D ;↓ $C1/618B STA $4206 [$00:4206] ;↓ $C1/618E JSR $46C8 [$C1:46C8] ;↓ ; $C1/46C8 NOP ;↓ $C1/46C9 RTS ;↓ ; $C1/6191 LDA $39 [$00:0339] ;↓ $C1/6193 STA $38 [$00:0338] ;↓ $C1/6195 LDA $3A [$00:033A] ;↓ $C1/6197 STA $39 [$00:0339] ;↓ $C1/6199 LDA $3B [$00:033B] ;↓ $C1/619B STA $3A [$00:033A] ;↓ $C1/619D LDA $4214 [$00:4214] ;符号付16bit / 8bitの商(下位バイト) $C1/61A0 STA $3B [$00:033B] ;↓※$4216-$4217には余りが入る $C1/61A2 PLB ;↓ $C1/61A3 PLX ;↓ $C1/61A4 RTS ;乱数(2)がAと[$00:033B]に入る ; $C1/1A4F LDA $4217 [$00:4217] ;Aに符号付16bit / 8bitの商(上位バイト)をロード = 00 $C1/1A52 STA $24 [$00:0324] ;A = $00を[$00:0324]に書き込み
これが続きの処理で、乱数(2)を算出している。
乱数(2)の算出前に、乱数(1)×[$00:0323]の計算をするために値を[$00:4202][$00:4203]にセットしたのだが、上を見ると何かおかしい。
戦闘乱数の計算では、途中で符号付16bit / 8bitの割り算を行う。
符号付16bit / 8bitでは、[$00:4204]~[$00:4205]に被除数、[$00:4206]に除数をセットすると、[$00:4214]~[$00:4215]に商(2バイト)、[$00:4216]~[$00:4217]に余り(2バイト)が入る。
ところが、[$00:4216]~[$00:4217]は、符号付8bit x 8bitの乗算結果の2バイトが入るアドレスでもある。
乱数計算の符号付16bit / 8bitの方が後に行われたから、符号付8bit x 8bitの結果は上書きされてなくなってしまった。
結果、乱数(2)計算後の、[$00:4217]の中には、戦闘乱数算出中の符号付16bit / 8bitの結果の余り(上1バイト)が入っている。
符号付16bit / 8bitを計算したら、余りは除数(8bit)より小さくなるはずであるから、余り(上1バイト)に入る値は必ず$00なのである。
つまり、$C1/1A4FでAに読み込まれる[$00:4217]の値は、必ず$00である。
この後の処理を見てもわかるが、最初に行おうとした「乱数(1)×[$00:0323]」の結果は、この後全く登場しない(答えが上書きされて消えたので当然なのだが)。
いったい何の意味があったのかわからない処理になってしまったが、意図したものかどうなのかもよくわからない。
とにかく、続きを見てみよう。
$C1/1A54 LDA $3B [$00:033B] ;Aに[$00:033B](乱数(2))をロード $C1/1A56 STA $4202 [$00:4202] ;符号付8bit x 8bit 被乗数 $C1/1A59 LDA $22 [$00:0322] ;Aに[$00:0322]をロード $C1/1A5B STA $4203 [$00:4203] ;符号付8bit x 8bit 乗数 $C1/1A5E LDA $24 [$00:0324] ;Aに[$00:0324]をロード $C1/1A60 CLC ;キャリーフラグクリア $C1/1A61 NOP ;計算待機 乱数(2)×[$00:0322] $C1/1A62 NOP ;計算待機 $C1/1A63 ADC $4217 [$00:4217] ;Aに乗算結果上1バイト(乱数(2)×[$00:0322]/$100)を加算 $C1/1A66 ADC $21 [$00:0321] ;Aに[$00:0321]を加算 $C1/1A68 STA $24 [$00:0324] ;Aを[$00:0324]に書き込み $C1/1A6A RTS ;サブルーチン戻り
$C1/1A54から、再び符号付8bit x 8bitの計算が行われる。
[$00:4202]に乱数(2)、[$00:4203]に[$00:0322]が入るので、乱数(2)×[$00:0322]の計算である。
計算待ちの間に、Aに[$00:0324]をロードしているが、先ほど述べた通り、この時点の[$00:0324]には必ず$00が入っている。よってAに$00が書き込まれる。
$C1/1A63では、Aに[$00:4217]が加算されている。[$00:4217]は乗算結果の上1バイトであり、乗算結果を÷$100した値(小数点を2つ左にずらした値)に等しい。
よって、
A = $00 + 乱数(2)×[$00:0322]/$100
である。
$C1/1A66で、更に[$00:0321]も加算しているので、
A = 乱数(2)×[$00:0322]/$100 + [$00:0321]
となった。
この値が[$00:0324]に書き込まれたので、
[$00:0324] = 乱数(2)×[$00:0322]/$100 + [$00:0321]
である。
この値が結局、レベルアップによるHP上昇値になる。
$C1/2460 STA $10 [$00:0310] ;Aを[$00:0310]に書き込み $C1/2462 STZ $11 [$00:0311] ;[$00:0311] = $00 $C1/2464 REP #$21 ;Aレジスタは16bit幅, キャリーフラグクリア $C1/2466 LDA $000A,y ;Aに[$000A,y+1][$000A,y]をロード(シナリオ別キャラデータの最大HP、2バイト分) $C1/2469 ADC $10 [$00:0310] ;A + [$00:0311][$00:0310](HP上昇値) $C1/246B CMP #$03E7 ;Aと$03E7(10進数999)を減算比較 $C1/246E BCC $03 [$2473] ;キャリーフラグが立っていないとき[$2473]分岐 $C1/2470 LDA #$03E7 ;(キャリーフラグON)Aに$03E7をロード $C1/2473 STA $000A,y ;Aを[$000A,y+1][$000A,y]に書き込み $C1/2476 STA $0008,y ;Aを[$0008,y+1][$0008,y](シナリオ別キャラデータの現在HP、2バイト分)に書き込み
[$00:0324]の計算結果は、[$00:0310]にも書き込まれる。
[$00:0311]に$00が書き込まれた後、16bitモードに変更。
$C1/2466で、[$000A,y]をロードしているが、16bitモードだから、一緒にひとつ後のアドレス[$000A,y+1]を上1バイトにロードしている。
このふたつのアドレスは、該当キャラのシナリオ別キャラデータの最大HP(2バイト分)になる。HPは最大値が999であるため、記録に2バイト分の領域が必要である。
$C1/2469で、最大HPに[$00:0310]を加算しているが、ここも16bitモードだから、[$00:0311][$00:0310]の2バイト分を加算している。
ただし[$00:0311]には$00を入れたばかりなので、実際に入るのは下1バイトに[$00:0310]の値である。これで、レベルアップ前の最大HPに、レベルアップ分のHP上昇値を加算したことになる。
$C1/246Bで、新しい最大HPと$03E7(10進数999)を減算比較しているが、レベルアップ後の最大HPが999を越えていないかどうかを判定していることになる。
ここでキャリーフラグが立つのは、減算の結果が0か正の数の時、つまり999以上の時である。
その場合は$C1/2470に進み、Aに$03E7をロードする。つまりAの値に$03E7(10進数999)を上書きして、最大HPを上限の999に変更している。
キャリーフラグが立たない場合は999未満なので、そのままの値が、$C1/2473で[$000A,y+1][$000A,y](シナリオ別キャラデータの最大HP)に書き込まれる。
また、同じ値が、[$0008,y+1][$0008,y]にも書き込まれているが、これはシナリオ別キャラデータにある現在HPのアドレスになる。
本作は戦闘終了時にHPが全回復するが、その処理をここで行ったことになる。
以上で、レベルアップ時のHP上昇値の計算・処理が終了した。
乱数計算を2回行っているのに1回目が結局使われずに終わったのだが、何かのミスか、開発中に何かの事情でこうなったのかは今となってはわからない。
このページの最後に考察を付けてあるので、気になったら読んでいただきたい。
HPの計算後は、力・速・体・知の処理に移る。
こちらはHPの処理よりシンプルである。
また、サブルーチンは共通であり、力・速・体・知がループ処理される。
$C1/248B LDA $D50015,x ;Aに[$D50015,x](レベルアップ時 該当ステータス上昇値)をロード $C1/248F AND #$F0 ;Aと$F0で論理積(上4ビット取り出し) $C1/2491 LSR A ;論理右シフト /2 $C1/2492 LSR A ;論理右シフト /2 $C1/2493 LSR A ;論理右シフト /2 $C1/2494 LSR A ;論理右シフト /2(上4ビットが下4ビットまで移動) $C1/2495 STA $20 [$00:0320] ;Aを[$00:0320]に書き込み $C1/2497 LDA $D50015,x ;Aに[$D5:03D2](レベルアップ時 該当ステータス上昇値)をロード $C1/249B AND #$0F ;Aと$0Fで論理積(下4ビット取り出し) $C1/249D STA $21 [$00:0321] ;Aを[$00:0321]に書き込み $C1/249F JSR $2554 [$C1:2554] ;[$C1:2554]へジャンプ ; $C1/2554 JSR $6150 [$C1:6150] ;[$C1:6150]へジャンプ ; $C1/6150 PHX ;戦闘乱数計算 $C1/6151 PHB ;↓ $C1/6152 LDA #$00 ;↓ $C1/6154 PHA ;↓ $C1/6155 PLB ;↓ $C1/6156 LDA $38 [$00:0338] ;↓ $C1/6158 ORA $39 [$00:0339] ;↓ $C1/615A ORA $3A [$00:033A] ;↓ $C1/615C ORA $3B [$00:033B] ;↓ $C1/615E BNE $24 [$6184] ;↓ $C1/6184 LDX $38 [$00:0338] ;↓ $C1/6186 STX $4204 [$00:4204] ;↓ $C1/6189 LDA #$0D ;↓ $C1/618B STA $4206 [$00:4206] ;↓ $C1/618E JSR $46C8 [$C1:46C8] ;↓ ; $C1/46C8 NOP ;↓ $C1/46C9 RTS ;↓ ; $C1/6191 LDA $39 [$00:0339] ;↓ $C1/6193 STA $38 [$00:0338] ;↓ $C1/6195 LDA $3A [$00:033A] ;↓ $C1/6197 STA $39 [$00:0339] ;↓ $C1/6199 LDA $3B [$00:033B] ;↓ $C1/619B STA $3A [$00:033A] ;↓ $C1/619D LDA $4214 [$00:4214] ;↓ $C1/61A0 STA $3B [$00:033B] ;↓ $C1/61A2 PLB ;↓ $C1/61A3 PLX ;↓ $C1/61A4 RTS ;乱数がAと[$00:033B]に入る ; $C1/2557 STA $211B [$00:211B] ;符号付16bit x 8bit 被乗数の下位バイト(乱数) $C1/255A LDA #$00 ;Aに$00をロード $C1/255C STA $211B [$00:211B] ;符号付16bit x 8bit 被乗数の上位バイト($00) $C1/255F LDA $21 [$00:0321] ;Aに[$00:0321](下4ビット)をロード $C1/2561 STA $211C [$00:211C] ;符号付16bit x 8bit 乗数 $C1/2564 STA $211C [$00:211C] ;符号付16bit x 8bit 乗数 $C1/2567 LDA $2135 [$00:2135] ;Aに乗算結果(24bit)の中位バイトをロード(乱数*下4ビット/$100) $C1/256A CLC ;キャリーフラグクリア $C1/256B ADC $20 [$00:0320] ;A + [$00:0320](上4ビット) $C1/256D RTS ;サブルーチン戻り ; $C1/24A2 ADC $0011,y[$00:0E91] ;A + [$0011,y](シナリオ別キャラデータ 該当ステータス) $C1/24A5 CMP #$63 ;Aと$63(10進数99)を減算比較) $C1/24A7 BCC $02 [$24AB] ;キャリーフラグが立っていないとき[$24AB]分岐 $C1/24A9 LDA #$63 ;(キャリーフラグON)Aに$63をロード $C1/24AB STA $0011,y[$00:0E91] ;Aを[$0011,y](シナリオ別キャラデータ 該当ステータス)に書き込み $C1/24AE LDA $0012,y[$00:0E92] ;Aを[$0011,y](シナリオ別キャラデータ 該当ステータス(装備上昇分))に書き込み
順に見ていく。
$C1/248B LDA $D50015,x ;Aに[$D50015,x](レベルアップ時 該当ステータス上昇値)をロード $C1/248F AND #$F0 ;Aと$F0で論理積(上4ビット取り出し) $C1/2491 LSR A ;論理右シフト /2 $C1/2492 LSR A ;論理右シフト /2 $C1/2493 LSR A ;論理右シフト /2 $C1/2494 LSR A ;論理右シフト /2(上4ビットが下4ビットまで移動) $C1/2495 STA $20 [$00:0320] ;Aを[$00:0320]に書き込み $C1/2497 LDA $D50015,x ;Aに[$D50015,x](レベルアップ時 該当ステータス上昇値)をロード $C1/249B AND #$0F ;Aと$0Fで論理積(下4ビット取り出し) $C1/249D STA $21 [$00:0321] ;Aを[$00:0321]に書き込み $C1/249F JSR $2554 [$C1:2554] ;[$C1:2554]へジャンプ ; $C1/2554 JSR $6150 [$C1:6150] ;[$C1:6150]へジャンプ
$C1/248Bで、判定するステータス上昇値を味方キャラデータからロードする。
この値と$F0とで論理積をとっている。つまり上4ビットの値を取り出しているが、この時点では16進数の2桁目に値がある。
$C1/2491~$C1/2494で論理右シフトを4回繰り返している。つまり÷$10であり、16進数の2桁目から1桁目に値が移動した。
この値を[$00:0320]に書き込んでいるので、[$00:0320] = 該当ステータス上昇値の上4ビットの値である。
$C1/2497からは、判定するステータス上昇値と$0Fで論理積を取っているので、今度は下4ビットの取り出しである。
この値は$C1/249Dで、[$00:0321]に書き込まれている。
ここまで行った後、戦闘乱数計算のサブルーチンにジャンプしている。
戦闘乱数計算のサブルーチンの説明は省略する。
$C1/61A4 RTS ;乱数がAと[$00:033B]に入る ; $C1/2557 STA $211B [$00:211B] ;符号付16bit x 8bit 被乗数の下位バイト(乱数) $C1/255A LDA #$00 ;Aに$00をロード $C1/255C STA $211B [$00:211B] ;符号付16bit x 8bit 被乗数の上位バイト($00) $C1/255F LDA $21 [$00:0321] ;Aに[$00:0321](下4ビット)をロード $C1/2561 STA $211C [$00:211C] ;符号付16bit x 8bit 乗数 $C1/2564 STA $211C [$00:211C] ;符号付16bit x 8bit 乗数 $C1/2567 LDA $2135 [$00:2135] ;Aに乗算結果(24bit)の中位バイトをロード(乱数*下4ビット/$100) $C1/256A CLC ;キャリーフラグクリア $C1/256B ADC $20 [$00:0320] ;A + [$00:0320](上4ビット) $C1/256D RTS ;サブルーチン戻り
戦闘乱数計算後、符号付16bit x 8bitの計算を行っている。
[$00:211B]に2回連続で被乗数の下位バイト・上位バイトを書き込み、[$00:211C]に乗数をセットすると、[$00:2134][$00:2135][$00:2136]に、24ビット分の乗算の結果が入る(順に、乗算結果の下位1バイト・中位1バイト・上位1バイト)。
よって、$00??(??は乱数)×[$00:0321](下4ビット)の掛け算を行っており、$C1/2567では乗算結果の中位バイト[$00:2135]をAにロードしている。
要するに、乗算結果の3・4桁目にあたり、乗算結果/$100と同値である。
$C1/256Bでは、乗算結果/$100と[$00:0320](上4ビット)を加算している。
これがレベルアップによる該当ステータスの上昇値になる。
$C1/24A2 ADC $0011,y ;A + [$0011,y](シナリオ別キャラデータ 該当ステータス) $C1/24A5 CMP #$63 ;Aと$63(10進数99)を減算比較 $C1/24A7 BCC $02 [$24AB] ;キャリーフラグが立っていないとき[$24AB]分岐 $C1/24A9 LDA #$63 ;(キャリーフラグON)Aに$63をロード $C1/24AB STA $0011,y ;Aを[$0011,y](シナリオ別キャラデータ 該当ステータス)に書き込み $C1/24AE LDA $0012,y ;Aに[$0012,y](シナリオ別キャラデータ 該当ステータス(装備上昇分))をロード $C1/24B1 BMI $0A [$24BD] ;ネガティブフラグが立っているとき[$24BD]分岐 $C1/24B3 CLC ;キャリーフラグクリア $C1/24B4 ADC $0011,y[$00:0E94] ;A + [$0011,y](シナリオ別キャラデータ 該当ステータス) $C1/24B7 BCC $0C [$24C5] ;キャリーフラグが立っていないとき[$24C5]分岐 $C1/24B9 LDA #$FF ;($C1/24B7・キャリーフラグON処理)Aに$FFをロード $C1/24BB BRA $08 [$24C5] ;フラグにかかわりなく常に分岐[$24C5] $C1/24BD CLC ;($C1/24B1・ネガティブフラグON処理)キャリーフラグクリア $C1/24BE ADC $0011,y[$00:0E51] ;↓A + [$0011,y](シナリオ別キャラデータ 該当ステータス) $C1/24C1 BCS $02 [$24C5] ;↓キャリーフラグが立っているとき[$24C5]分岐 $C1/24C3 LDA #$00 ;↓Aに$00をロード $C1/24C5 STA $0010,y[$00:0E93] ;Aを[$0010,y](シナリオ別キャラデータ 該当ステータス(ステータス+装備上昇分))に書き込み
$C1/24A2で、算出したステータスの上昇値に、[$0011,y](シナリオ別キャラデータ 該当ステータス)を加算している。
元のステータスに、レベルアップ分のステータスを加算したことになる。
$C1/24A5では、元のステータス + レベルアップ分のステータスを、$63(10進数99)と減算比較している。
ステータスの最大値は99だから、99を越えていないかどうかの判定である。
キャリーフラグが立つ場合は99以上なので、$C1/24A9で、Aに$63を書き込む。
キャリーフラグが立たない場合は99未満だから、そのままの値が[$0011,y]に書き込まれ、新たな「シナリオ別キャラデータ・該当ステータス」になる。
$C1/24AEで、[$0012,y]をロードしているが、これは「該当ステータス(装備上昇分)」のアドレスである。
要するに該当ステータスの、装備補正分の値である。装備補正なので、正の値の時と負の値の時がある。
$C1/24B1でネガティブフラグを確認しているが、ネガティブフラグは、16進数を符号付き8ビット整数として見た時(上1ビットが正負の符号、下7ビットが数値)、上1ビットに1が立っていて、負の数を意味している時に立つフラグである。
※符号付き8ビット整数の説明は、2進法・16進法 > 正負表現の項目も参照のこと。
つまり、ネガティブフラグが立つなら、装備補正が負の値の時である。
少しややこしいので、ここではまず、ネガティブフラグが立たない、つまり装備補正が正の値の処理を見てみよう。
その場合は、$C1/24B3に進む。
キャリーフラグクリア後に、レベルアップで上昇した、新たなステータスの値と、装備上昇分を加算する。
キャラの素のステータス+装備補正を計算したことになるが、$C1/24B7でキャリーフラグが立っているかどうかを判定している。
加算でキャリーフラグが立つのは$100(10進数256)以上の値になった時である。
キャリーフラグが立つ場合は$C1/24B9に進むが、Aに$FFを書き込んでいるので、加算結果が$100以上の時は$FFに上書きする、という処理である。その後は[$24C5]にジャンプする。
キャリーフラグが立たない場合、加算の結果が$100(10進数256)未満だから、そのまま[$24C5]にジャンプ。
$C1/24C5で、加算の結果が$100(10進数256)未満の時は加算結果を、$100以上なら$FFが[$0010,y]に書き込んでいる。
[$0010,y]は、「シナリオ別キャラデータ・該当ステータス(ステータス+装備上昇分)」である。
これで、レベルアップにより、「ステータス」「ステータス+装備上昇分」の値が更新されたことになる。
では、少し巻き戻って、$C1/24B1でネガティブフラグが立っている場合の処理を見てみよう。
ネガティブフラグが立つ、つまり装備補正が負の値の時は、$C1/24BDにジャンプする。
キャリーフラグクリア後にA + [$0011,y]を計算しているので、こちらでも結局「新たなステータス+装備補正」である。
次の処理が異なり、計算結果でキャリーフラグが立っているとき[$24C5]分岐、キャリーフラグが立たないときは$C1/24C3に進む。
ここでは「負の装備補正 + キャラのステータス」を行っているが、キャリーフラグはどういう場合に立つだろうか。
例えば、装備補正で該当ステータスが-5されている場合、-5は10進数表記になるが、符号付き8ビット整数だと$FBである。
キャラのステータスが$20(10進数32)だったとしたら、ここでの加算は、
$FB + $20 = $11B
キャリーフラグが立つが、答えの下2桁の「$1B」は10進数27である。
実は32-5の計算結果と全く同一なのである。
以上から、「負の装備補正 + キャラのステータス」の計算の結果、キャリーフラグが立ち、下2桁をそのまま「ステータス + 装備上昇分」として良いことがわかる。
逆にキャリーフラグが立たないのは、上の計算結果が負の時である。
キャラのステータスが$02(10進数2)だったとしたら、ここでの加算は、
$FB + $02 = $FD
繰り上がりが生じないため、キャリーフラグが立たない。
また、$FDは符号付き8ビット整数だと-2である。
本作はステータスを装備補正して負の値になる場合は0とするのだが、$C1/24C3がまさにその処理で、キャリーフラグが立たないならAに$00をロードしている。
以上で算出されたAを[$0010,y]に書き込み「新たなステータス+装備補正」としている。
これでレベルアップによる「ステータス」「ステータス+装備上昇分」の処理終了となる。
ここまででステータス1種類分の処理になるので、実際には力・速・体・知の4回分処理をして、全てのステータスの計算処理終了になる。
以上から、レベルアップ時のステータス上昇値は、味方キャラデータ21~24(レベルアップ時の力・速・体・知ステータス上昇値)を使い、以下のように計算していることがわかった。
ステータス上昇値 = 味方キャラデータ21~24の上4ビット(基礎値) + 味方キャラデータ21~24の下4ビット(変動値)×$00~$FFの乱数/$100
= 基礎値 + 変動値×(0~255の乱数/256)
上昇値は乱数により、「変動値-1(ただし変動値が0なら0のまま)」の幅から当確率で選ばれることになる。
高原の「力」なら、
基礎値(5) + 変動値(2)×(0~255の乱数/256)
になるが、乱数最大値が255だから、小数点以下切り捨ての計算を考えると、「変動値(2)×(0~255の乱数/256)」の部分の最大値が1となる。
よって力上昇値は5か6のどちらかになる。
「知」だと基礎値も変動値も0であり、変動値×(0~255の乱数/256)は0のままである。
ここまででレベルアップ時のステータス上昇値の説明は終わったのだが、最後に、HP上昇値のサブルーチンにおいて、乱数計算を2回行ったのに、1回目がなかったことにされてしまった件について、ちょっとだけ考察をしておく。
1回目の乱数計算前、味方キャラデータ27の上2ビットの値により分岐が起きて、メモリアドレスに以下のように値が入った。
| 上2ビット | [$00:0321] | [$00:0322] | [$00:0323] |
|---|---|---|---|
| 00 | 下6ビット | 下6ビット/2 | 0 |
| 01 | 下6ビット | 下6ビット | 0 |
| 10 | 下6ビット/2 | 下6ビット | 0 |
| 11 | 0 | 下6ビット | 下6ビット |
そして、1回目の乱数計算後、乱数(1)×[$00:0323]の乗算のセットだけは行われている。
この直後に乱数(2)の計算が行われ、
$C1/1A4F LDA $4217 [$00:4217] ;Aに符号付16bit / 8bitの商(上位バイト)をロード = $00 $C1/1A52 STA $24 [$00:0324] ;A = $00を[$00:0324]に書き込み
という処理に続くのだが、もし乱数(2)の計算が行われていなければ、[$00:4217]に、乱数(1)×[$00:0323]の乗算結果の上1バイトが入っていたはずだ。
ところが乱数(2)の計算を挟んだために、[$00:4217]には乱数計算の途中の符号付16bit / 8bitの商(上位バイト)が入ってしまったのである。
乱数(2)計算の前に、乱数(1)×[$00:0323]の乗算結果が入った状態の[$00:4217]の値をどこかのメモリに書き込んでおけば、上書きされても問題はなかったのだが、そういった処理も見られない。
もし、実際には、[$00:0324]に乱数(1)×[$00:0323]の乗算結果の上1バイト = 乱数(1)×[$00:0323]/$100を入れるつもりだったとしたら、この後はどうなるだろうか?
詳細は省略するが、
レベルアップによるHP上昇値 = 乱数(1)×[$00:0323]/$100 + 乱数(2)×[$00:0322]/$100 + [$00:0321]
このように計算されることになる。
[$00:0323]には、味方キャラデータ27の上2ビットの値が%11の時だけ、下6ビットの値が入り、それ以外は00が入っている。
よって、影響があるのは、味方キャラデータ27の上2ビットの値が%11の時だけになる。
先に、例としてサモのHP上昇値について計算してみたが、サモは味方キャラデータ27が$FCで、下6ビット$3C、上2ビット$C0(上2ビット11)である。
レベルアップによるHP上昇値 = 乱数(1)×[$00:0323]/$100 + 乱数(2)×[$00:0322]/$100 + [$00:0321]
だったら、どう変わるだろうか。
乱数が最大値の時、HP上昇値の上限値になるから、
レベルアップによるHP上昇値 = [$00:0323]*$FF/$100 + [$00:0322]*$FF/$100 + [$00:0321]
= 下6ビット*$FF/$100 + 下6ビット*$FF/$100 + 0
= 下6ビット*2 - 2
= $3C*2 - 2
= $76(10進数118)
レベルが1上がるだけで、HPが最大+118される可能性がある、ということだ。
下限は0のままだから、HP上昇値の幅が0~118と、とんでもないブレ幅になる。
最大HPが999の本作において、最大値はさすがに大きすぎる。
平均値の59でもかなり大きめである。
実際には「乱数(1)×[$00:0323]/$100」部分が無効化されて、サモのHP上昇値は0~59と算出される。
意図的に変更したのか、何かしらのミスだったのか、よくわからないが、さすがにHP上昇値が0~118はピーキーにも程があるので、無効化されて正解だったのだろう。