miririi6のブログ

RTAのこととかその他のこととか適当に

ポケモンFRLG TID決定時の乱数初期化処理を見てみた

当方初心者です。乱数ガチ勢向けの新情報とかはありません。

 

名前決めて暗転した直後の処理

                      --------sub start--------
8000564  10 B5          PUSH {R4,LR}
8000566  06 48          LDR R0, [PC, #$06]
8000568  04 88          LDRH R4, [R0, #$00] //$4000104(TM1CNT_L)タイトル画面と主人公の名前決める画面で動いてる 他の画面では止まったまま
800056A  20 1C          ADD R0, R4, #$00
800056C  43 F0          BLL #$0043
800056E  9E FF          BLH #$079E //初期乱数を代入するサブルーチンへジャンプ
8000570  04 49          LDR R1, [PC, #$04]
8000572  00 20          MOV R0, #$00
8000574  08 80          STRH R0, [R1, #$00]
8000576  04 48          LDR R0, [PC, #$04]
8000578  04 80          STRH R4, [R0, #$00]
800057A  10 BC          POP {R4}
800057C  01 BC          POP {R0}
800057E  00 47          BX R0

                      ----------------

                      --------sub start--------
80444AC  00 04          LSL R0, R0, #$10
80444AE  00 0C          LSR R0, R0, #$10//上2バイトを0にする
80444B0  01 49          LDR R1, [PC, #$01]
80444B2  08 60          STR R0, [R1, #$00]//乱数アドレスへ代入
80444B4  70 47          BX LR
                      ----------------

$4000104(TM1CNT_L)にはハードウェアのタイマーの値入ってるっぽい。ブレークポイントは効かなかった。

参考 tutorial.9 - GBA開発Wiki


多分乱数ガチ勢が初期シード集めとかしてるのはここの挙動が読めないからだと思う。

AIに色々聞いたりエミュで軽く試したりしてみたが、一致させる詳しい条件は分からなかった。

 

疑問点

  • タイトル画面終了時に一旦止まったTM1CNT_Lは、名前決定画面で再起動されるTM1CNT_Lには影響無さそう?
  • 名前決定画面に入ってから決定までのフレーム数が一致して、かつボタン入力とかの状態が何らかの条件を満たせば一致する?

 

以上、難しそうなのでこれ以上の調査は多分しません。終わり。

SFC版DQ1,2 ブレス・呪文のダメージ計算についての調査+α

よく参照されてる記事がありますが、詳細な処理内容、装備補正がどこで入るのか等が分からないので調査してみました。

よく参照されてる記事↓
https://bamboo.ninja-web.net/dq1,2/damage-disp/DQ12randave_jsplot2.html

アセンブリはよく分からないので、AIに聞きながら「なんとなくこうっぽい?」で進めてます。正確性は保証できません。

 

ダメージ乱数補正処理について

気付いたこと

256をダメージ幅で割り切れない場合は低ダメージが出やすいようです。

例えば振れ幅が9の火の息(12~20)の場合、乱数が255の時に255/9=28余り3。
そのため、12~15は29/256、16~20は28/256の確率で出ることになります。

 

また、割り算と切り捨てが入るので大きい値よりも小さい値の方が出やすくなります。


乱数2回で72を引く場合、2連続で72を引く必要がある。
乱数2回で65を引く場合、65,66の組み合わせでも切り捨てで65となる。

激しい炎のダメージ幅は8で割り切れるにも関わらず72が出る確率が65より低いのは、この仕様によるものだと思います。

 

処理内容

サブルーチン(ブレス・呪文ダメージ)
2026/03/20追記:敵・味方のベホイミもこのサブルーチンで計算されている

0BA5AD  DA             PHX
0BA5AE  A9 00          LDA #$00
0BA5B0  EB             XBA
0BA5B1  AD 67 00       LDA $0067
0BA5B4  29 03          AND #$03 //ここで下位2bitをマスクしてループ回数決定
0BA5B6  AA             TAX
0BA5B7  BF 09 A6 0B    LDA $0BA609,X
0BA5BB  85 08          STA $08
0BA5BD  64 09          STZ $09
0BA5BF  48             PHA
0BA5C0  AD 30 10       LDA $1030 //ダメージ最大値を読み込み。(はげほなら0x48)
0BA5C3  38             SEC
0BA5C4  ED 2F 10       SBC $102F //ダメージ最大値から最小値を引く。(48-41=7)
0BA5C7  A2 00 00       LDX #$0000
0BA5CA  86 0A          STX $0A

//ループ開始
0BA5CC  48             PHA //サブルーチン内で計算する値をスタックへ
0BA5CD  22 7E A5 0B    JSL $0BA57E //サブルーチン(振れ幅内の乱数を返す)へ
0BA5D1  18             CLC
0BA5D2  65 0A          ADC $0A //合計値計算
0BA5D4  85 0A          STA $0A //合計値の一時保存
0BA5D6  A9 00          LDA #$00
0BA5D8  65 0B          ADC $0B
0BA5DA  85 0B          STA $0B
0BA5DC  68             PLA //スタックからダメージ幅(8)を取り出す
0BA5DD  E8             INX //ループ回数カウント+1
0BA5DE  E4 08          CPX $08 //ループ回数確認
0BA5E0  90 EA          BCC $0BA5CC //回数に達していなければ0BA5CCに戻る

//ループ終了
0BA5E2  68             PLA
0BA5E3  85 08          STA $08
0BA5E5  C2 20          REP #$20
0BA5E7  A5 0A          LDA $0A //合計値をロード
0BA5E9  A6 08          LDX $08 //ループ回数をロード
0BA5EB  E0 01 00       CPX #$0001
0BA5EE  F0 11          BEQ $0BA601
0BA5F0  4A             LSR
0BA5F1  A6 08          LDX $08
0BA5F3  E0 02 00       CPX #$0002
0BA5F6  F0 09          BEQ $0BA601
0BA5F8  4A             LSR
0BA5F9  A6 08          LDX $08
0BA5FB  E0 04 00       CPX #$0004
0BA5FE  F0 01          BEQ $0BA601
0BA600  4A             LSR //ここまで、ループ回数に応じて割り算
0BA601  E2 20          SEP #$20
0BA603  18             CLC
0BA604  6D 2F 10       ADC $102F //計算した乱数に、ダメージ最低値を足す
0BA607  FA             PLX
0BA608  6B             RTL

 

サブルーチン(振れ幅内の乱数を返す)
0BA57E  DA             PHX
0BA57F  1A             INC //受け取った値+1(はげほなら8)
0BA580  48             PHA //スタックに8を一時保存
0BA581  22 50 A5 0B    JSL $0BA550 //サブルーチン(0~255の乱数生成)
0BA585  A6 E2          LDX $E2 //生成した乱数取り出し
0BA587  8E 04 42       STX $4204 //乱数をハードウェアにセット
0BA58A  68             PLA //スタックから8を取り出し
0BA58B  8D 06 42       STA $4206 //8をハードウェアにセット
0BA58E  EA             NOP //ここからハードウェアの割り算処理待ち
0BA58F  EA             NOP
0BA590  EA             NOP
0BA591  EA             NOP
0BA592  EA             NOP
0BA593  EA             NOP
0BA594  EA             NOP
0BA595  EA             NOP //ここまで待ち
0BA596  AD 16 42       LDA $4216 //ハードウェアから割り算の余りを取得
0BA599  FA             PLX
0BA59A  6B             RTL //戻る

サブルーチン(0~255の乱数生成)
0BA550  C2 20          REP #$20
0BA552  A5 E2          LDA $E2
0BA554  48             PHA
0BA555  A5 E0          LDA $E0
0BA557  06 E0          ASL $E0
0BA559  26 E2          ROL $E2
0BA55B  18             CLC
0BA55C  65 E0          ADC $E0
0BA55E  85 E0          STA $E0
0BA560  68             PLA
0BA561  65 E2          ADC $E2
0BA563  85 E2          STA $E2
0BA565  A5 E0          LDA $E0
0BA567  18             CLC
0BA568  69 49 35       ADC #$3549
0BA56B  65 67          ADC $67
0BA56D  85 E0          STA $E0
0BA56F  A5 E2          LDA $E2
0BA571  69 00 00       ADC #$0000
0BA574  85 E2          STA $E2
0BA576  E2 20          SEP #$20
0BA578  A9 00          LDA #$00
0BA57A  EB             XBA
0BA57B  A5 E2          LDA $E2
0BA57D  6B             RTL

 

計算順について

結論

乱数補正の後に3/4倍処理が入るっぽいです。
各ダメージの確率を知りたい場合、冒頭の記事の数値を参照し、補正して一緒になる数値は合算した確率でいいと思います。


激しい炎の68と69は共に、3/4倍で切り捨てて51になる。
⇒68と69の確率を合算して49.59270358%の確率で51ダメージとなる。

 

根拠

以下は実行時のTrace Log。Aレジスタの中身がダメージの値(16進数)になっている。

0B906B  LDA $00 [$000000] = $0044      A:000E X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdiZC V:251 H:326
0B906D  AND #$00FF                     A:0044 X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdizC V:251 H:334
0B9070  PHA                            A:0044 X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdizC V:252 H:0  
024B  MOV A,$F4 [CPUIO0] = $00         A:00 X:50 Y:00 S:FD P:nvpbhiZc V:252 H:3  
0B9071  ASL                            A:0044 X:0000 Y:0000 S:02E1 D:0000 DB:00 P:nvmxdizC V:252 H:7  
0B9072  ADC $01,S [$0002E2] = $0044    A:0088 X:0000 Y:0000 S:02E1 D:0000 DB:00 P:nvmxdizc V:252 H:11 
024D  BEQ $024A                        A:00 X:50 Y:00 S:FD P:nvpbhiZc V:252 H:18 
0B9074  STA $01,S [$0002E2] = $0044    A:00CC X:0000 Y:0000 S:02E1 D:0000 DB:00 P:nvmxdizc V:252 H:20 
0B9076  PLA                            A:00CC X:0000 Y:0000 S:02E1 D:0000 DB:00 P:nvmxdizc V:252 H:30 
0B9077  LSR                            A:00CC X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdizc V:252 H:39 
024A  RET                              A:00 X:50 Y:00 S:FD P:nvpbhiZc V:252 H:40 
0B9078  LSR                            A:0066 X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdizc V:252 H:42 
0B9079  SEP #$20                       A:0033 X:0000 Y:0000 S:02E3 D:0000 DB:00 P:nvmxdizc V:252 H:46 

 

・LDAでAレジスタにダメージ値(0x44 = 68)を読み込み

・ASLでAレジスタの値を2倍(0x88)

・ADCでAレジスタの値を加算(0xCC ここまでで3倍になる)

・LSR1/2倍を2回行って1/4倍(0x33 = 51)

実際にこの後ダメージは51だった。

 

この後もう一度試したら、読み込まれるダメージ値が変わっていました。
計算終了時点でAレジスタが0x30、実際のダメージが48。

このことから、既に乱数補正済みの数値を読み込んで3/4倍処理が走っていることが分かります。

 

処理内容(サブルーチン全体)

0B9069  C2 20          REP #$20 //16bitモードに変更
0B906B  A5 00          LDA $00 //Aレジスタにダメージ値読み込み
0B906D  29 FF 00       AND #$00FF //下位8bitを使うためのマスク
0B9070  48             PHA //スタックに保存
0B9071  0A             ASL //Aレジスタ2倍
0B9072  63 01          ADC $01,S //Aレジスタ+スタック⇒元の値の3倍
0B9074  83 01          STA $01,S //スタックをきれいにする処理らしい①
0B9076  68             PLA //スタックをきれいにする処理らしい②
0B9077  4A             LSR //÷2
0B9078  4A             LSR //÷2 →元の値の3/4倍
0B9079  E2 20          SEP #$20 //8bitモードに戻す
0B907B  85 00          STA $00 //結果をメモリに保存
0B907D  80 A9          BRA $0B9028

 

追記

勇者の通常攻撃ダメージの計算ルーチン

                     --------sub start--------
0BA05A  64 03          STZ $03
0BA05C  DA             PHX
0BA05D  AE 60 0F       LDX $0F60
0BA060  BD E2 0F       LDA $0FE2,X
0BA063  FA             PLX
0BA064  C9 FF          CMP #$FF
0BA066  F0 01          BEQ $0BA069
0BA068  4A             LSR //守備力/2
0BA069  85 01          STA $01
0BA06B  A5 00          LDA $00
0BA06D  38             SEC
0BA06E  E5 01          SBC $01 //攻撃力-守備力/2
0BA070  90 43          BCC $0BA0B5
0BA072  4A             LSR //(攻撃力-守備力/2)/2 基礎値計算完了
0BA073  C9 02          CMP #$02
0BA075  90 3E          BCC $0BA0B5 //2回ならジャンプ
0BA077  85 03          STA $03
0BA079  C9 09          CMP #$09
0BA07B  90 3F          BCC $0BA0BC //9以下ならジャンプ
0BA07D  8D 02 42       STA $4202
0BA080  A9 07          LDA #$07
0BA082  8D 03 42       STA $4203 //基礎値*7
0BA085  EA             NOP
0BA086  EA             NOP
0BA087  EA             NOP
0BA088  C2 20          REP #$20
0BA08A  AD 16 42       LDA $4216
0BA08D  4A             LSR
0BA08E  4A             LSR
0BA08F  4A             LSR //基礎値*7/8 最低ダメージ計算完了
0BA090  85 06          STA $06 //最低ダメージメモ
0BA092  E2 20          SEP #$20
0BA094  A5 03          LDA $03 //基礎値読み込み
0BA096  4A             LSR
0BA097  4A             LSR //基礎値/4
0BA098  1A             INC //+1
0BA099  48             PHA
0BA09A  22 50 A5 0B    JSL $0BA550 //乱数生成サブルーチンへ
0BA09E  8D 02 42       STA $4202 //乱数0-255をセット
0BA0A1  68             PLA
0BA0A2  8D 03 42       STA $4203 //ダメージ振れ幅をセット
0BA0A5  EA             NOP
0BA0A6  EA             NOP
0BA0A7  EA             NOP
0BA0A8  EA             NOP
0BA0A9  AC 16 42       LDY $4216 //セットした値の積を取得
0BA0AC  84 08          STY $08 //乗算結果を格納
0BA0AE  A5 09          LDA $09 //乗算結果の上位バイトをAレジスタに読み込み(÷256)
0BA0B0  18             CLC
0BA0B1  65 06          ADC $06 //Aレジスタに最低ダメージを加算(最終ダメージ)
0BA0B3  80 12          BRA $0BA0C7
0BA0B5  AD 67 00       LDA $0067
0BA0B8  29 01          AND #$01
0BA0BA  80 0B          BRA $0BA0C7
0BA0BC  A9 02          LDA #$02
0BA0BE  22 7E A5 0B    JSL $0BA57E
0BA0C2  18             CLC
0BA0C3  65 03          ADC $03
0BA0C5  3A             DEC
0BA0C6  3A             DEC
0BA0C7  C9 00          CMP #$00
0BA0C9  D0 07          BNE $0BA0D2
0BA0CB  9C 91 0C       STZ $0C91
0BA0CE  9C 92 0C       STZ $0C92
0BA0D1  60             RTS
                     ----------------

 

敵の通常攻撃のダメージを計算するルーチン
ロジックは同じっぽい

                     --------sub start--------

0B8AF4  A6 AE          LDX $AE
0B8AF6  B9 DB 0F       LDA $0FDB,Y
0B8AF9  85 00          STA $00
0B8AFB  64 03          STZ $03
0B8AFD  BD 3D 0C       LDA $0C3D,X
0B8B00  4A             LSR //守備力/2
0B8B01  85 01          STA $01
0B8B03  A5 00          LDA $00
0B8B05  38             SEC
0B8B06  E5 01          SBC $01 //攻撃力-守備力/2
0B8B08  90 44          BCC $0B8B4E
0B8B0A  4A             LSR //(攻撃力-守備力/2)/2 基礎値計算完了
0B8B0B  85 03          STA $03 //基礎値をメモ
0B8B0D  C9 02          CMP #$02
0B8B0F  90 3D          BCC $0B8B4E //2以下の場合ジャンプ
0B8B11  C9 09          CMP #$09
0B8B13  90 41          BCC $0B8B56 //9以下の場合ジャンプ
0B8B15  8D 02 42       STA $4202
0B8B18  A9 07          LDA #$07
0B8B1A  8D 03 42       STA $4203 //基礎値*7をハードウェアで計算
0B8B1D  EA             NOP
0B8B1E  EA             NOP
0B8B1F  EA             NOP
0B8B20  C2 20          REP #$20
0B8B22  AD 16 42       LDA $4216
0B8B25  4A             LSR
0B8B26  4A             LSR
0B8B27  4A             LSR //基礎値*7/8 最低ダメージ計算完了
0B8B28  85 06          STA $06 //最低ダメージをメモ
0B8B2A  E2 20          SEP #$20
0B8B2C  A5 03          LDA $03 //基礎値読み込み
0B8B2E  4A             LSR
0B8B2F  4A             LSR //基礎値/4
0B8B30  1A             INC //+1
0B8B31  48             PHA
0B8B32  22 50 A5 0B    JSL $0BA550 //乱数生成
0B8B36  8D 02 42       STA $4202
0B8B39  68             PLA
0B8B3A  8D 03 42       STA $4203
0B8B3D  EA             NOP
0B8B3E  EA             NOP
0B8B3F  EA             NOP
0B8B40  EA             NOP
0B8B41  AC 16 42       LDY $4216
0B8B44  84 08          STY $08
0B8B46  A5 09          LDA $09 //乗算結果の上位バイトを取得
0B8B48  18             CLC
0B8B49  65 06          ADC $06 //最低ダメージを加算(最終ダメージ)
0B8B4B  4C 63 8B       JMP $8B63
0B8B4E  22 50 A5 0B    JSL $0BA550
0B8B52  29 01          AND #$01
0B8B54  80 0D          BRA $0B8B63
0B8B56  A5 03          LDA $03
0B8B58  A9 02          LDA #$02
0B8B5A  22 7E A5 0B    JSL $0BA57E
0B8B5E  18             CLC
0B8B5F  65 03          ADC $03
0B8B61  3A             DEC
0B8B62  3A             DEC
0B8B63  C9 00          CMP #$00
0B8B65  D0 08          BNE $0B8B6F
0B8B67  9C 91 0C       STZ $0C91
0B8B6A  9C 92 0C       STZ $0C92
0B8B6D  38             SEC
0B8B6E  60             RTS
                     ----------------

 

ホイミ回復量計算
25-28の確率がそれぞれ43/256、29,30の確率がそれぞれ42/256になると思われる

                     --------sub start--------
0BA59B  DA             PHX
0BA59C  AD 30 10       LDA $1030 //30
0BA59F  38             SEC
0BA5A0  ED 2F 10       SBC $102F //30-25
0BA5A3  22 7E A5 0B    JSL $0BA57E //振れ幅内の乱数を返すサブルーチン
0BA5A7  18             CLC
0BA5A8  6D 2F 10       ADC $102F //計算結果+最低値25
0BA5AB  FA             PLX
0BA5AC  6B             RTL
                     ----------------

 

薬草の計算処理
振れ幅16なので、一様分布

                     --------sub start--------
0BAE25  64 1B          STZ $1B
0BAE27  A9 0F          LDA #$0F //15(振れ幅取得)
0BAE29  22 7E A5 0B    JSL $0BA57E //0-15の乱数取得
0BAE2D  18             CLC
0BAE2E  69 14          ADC #$14 //振れ幅+20(回復量の計算完了)
0BAE30  A6 1A          LDX $1A
0BAE32  18             CLC
0BAE33  7D 09 0C       ADC $0C09,X
0BAE36  90 02          BCC $0BAE3A
0BAE38  A9 FF          LDA #$FF
0BAE3A  DD 2D 0C       CMP $0C2D,X
0BAE3D  90 03          BCC $0BAE42
0BAE3F  BD 2D 0C       LDA $0C2D,X
0BAE42  9D 09 0C       STA $0C09,X
0BAE45  A5 70          LDA $70
0BAE47  30 07          BMI $0BAE50
0BAE49  A9 12          LDA #$12
0BAE4B  22 63 83 09    JSL $098363
0BAE4F  6B             RTL
                     ----------------

 

通常と会心の分岐部分

                     --------sub start--------

0B9FA6  BD 35 0C       LDA $0C35,X
0B9FA9  85 00          STA $00
0B9FAB  22 50 A5 0B    JSL $0BA550
0B9FAF  C9 08          CMP #$08
0B9FB1  B0 15          BCS $0B9FC8 //Aレジスタの値が8以上の場合、通常ダメージへ
0B9FB3  20 D9 A0       JSR $A0D9 //会心ダメージへ
0B9FB6  BD BB 0F       LDA $0FBB,X
0B9FB9  D0 03          BNE $0B9FBE
0B9FBB  FE BB 0F       INC $0FBB,X
0B9FBE  AD 91 0C       LDA $0C91
0B9FC1  8D 49 10       STA $1049
0B9FC4  A0 02 00       LDY #$0002
0B9FC7  6B             RTL
                     ----------------

 

会心ダメージ計算
下位3乱数が24/256、上位8乱数が23/256の確率で出る

                     --------sub start--------
0BA0D9  A9 0A          LDA #$0A
0BA0DB  22 7E A5 0B    JSL $0BA57E //0~10の乱数を返す
0BA0DF  1A             INC //乱数+1
0BA0E0  18             CLC
0BA0E1  69 36          ADC #$36 //乱数+1+54 → 55~65になる
0BA0E3  8D 02 42       STA $4202
0BA0E6  A5 00          LDA $00
0BA0E8  8D 03 42       STA $4203
0BA0EB  EA             NOP
0BA0EC  EA             NOP
0BA0ED  EA             NOP
0BA0EE  C2 20          REP #$20
0BA0F0  AD 16 42       LDA $4216 //55~65×攻撃力
0BA0F3  4A             LSR
0BA0F4  4A             LSR
0BA0F5  4A             LSR
0BA0F6  4A             LSR
0BA0F7  4A             LSR
0BA0F8  4A             LSR //÷64
0BA0F9  C9 FE 00       CMP #$00FE
0BA0FC  90 03          BCC $0BA101
0BA0FE  A9 FE 00       LDA #$00FE
0BA101  E2 20          SEP #$20
0BA103  C9 00          CMP #$00
0BA105  F0 C0          BEQ $0BA0C7
0BA107  8D 91 0C       STA $0C91
0BA10A  9C 92 0C       STZ $0C92
0BA10D  60             RTS
                     ----------------

 

行動順計算

                     --------sub start--------
0B9E1E  A2 00 00       LDX #$0000
0B9E21  9B             TXY
0B9E22  AD A0 0F       LDA $0FA0
0B9E25  F0 03          BEQ $0B9E2A
0B9E27  4C 62 9E       JMP $9E62
0B9E2A  A9 7F          LDA #$7F
0B9E2C  22 7E A5 0B    JSL $0BA57E //サブルーチンへ 0-127の乱数を返す
0B9E30  8D 02 42       STA $4202
0B9E33  B9 D4 0F       LDA $0FD4,Y //敵の素早さ
0B9E36  8D 03 42       STA $4203
0B9E39  EA             NOP
0B9E3A  EA             NOP
0B9E3B  EA             NOP
0B9E3C  DA             PHX
0B9E3D  AE 16 42       LDX $4216 //Xレジスタに乱数と振れ幅の乗算結果取得
0B9E40  86 0D          STX $0D
0B9E42  FA             PLX
0B9E43  B9 D4 0F       LDA $0FD4,Y
0B9E46  38             SEC
0B9E47  E5 0E          SBC $0E //最大値-振れ幅の減少分
0B9E49  9D 80 0B       STA $0B80,X
0B9E4C  E8             INX
0B9E4D  A9 00          LDA #$00
0B9E4F  9D 80 0B       STA $0B80,X
0B9E52  99 4B 0F       STA $0F4B,Y
0B9E55  E8             INX
0B9E56  C8             INY
0B9E57  98             TYA
0B9E58  CD DC 0E       CMP $0EDC
0B9E5B  90 CD          BCC $0B9E2A
0B9E5D  AD E6 0C       LDA $0CE6
0B9E60  F0 33          BEQ $0B9E95
0B9E62  A0 00 00       LDY #$0000
0B9E65  A9 57          LDA #$57
0B9E67  22 7E A5 0B    JSL $0BA57E //サブルーチンへ 0-87の乱数を返す
0B9E6B  8D 02 42       STA $4202
0B9E6E  B9 21 0C       LDA $0C21,Y //勇者の素早さ
0B9E71  8D 03 42       STA $4203
0B9E74  EA             NOP
0B9E75  EA             NOP
0B9E76  EA             NOP
0B9E77  DA             PHX
0B9E78  AE 16 42       LDX $4216
0B9E7B  86 06          STX $06
0B9E7D  FA             PLX
0B9E7E  B9 21 0C       LDA $0C21,Y
0B9E81  38             SEC
0B9E82  E5 07          SBC $07
0B9E84  9D 80 0B       STA $0B80,X
0B9E87  E8             INX
0B9E88  A9 01          LDA #$01
0B9E8A  9D 80 0B       STA $0B80,X
0B9E8D  E8             INX
0B9E8E  C8             INY
0B9E8F  98             TYA
0B9E90  CD E6 0C       CMP $0CE6
0B9E93  90 D0          BCC $0B9E65
0B9E95  AD DC 0E       LDA $0EDC
0B9E98  48             PHA
0B9E99  AD A0 0F       LDA $0FA0
0B9E9C  F0 03          BEQ $0B9EA1
0B9E9E  9C DC 0E       STZ $0EDC
0B9EA1  8A             TXA
0B9EA2  4A             LSR
0B9EA3  20 AB 9E       JSR $9EAB
0B9EA6  68             PLA
0B9EA7  8D DC 0E       STA $0EDC
0B9EAA  6B             RTL
                     ----------------

 

勇者睡眠時の行動処理

                     --------sub start--------
0BA97A  E2 10          SEP #$10
0BA97C  64 00          STZ $00
0BA97E  A0 00          LDY #$00
0BA980  B9 BE 0E       LDA $0EBE,Y //ラリホーフラグ読み込み 最初は4
0BA983  F0 0E          BEQ $0BA993 //0ならジャンプ
0BA985  A9 02          LDA #$02
0BA987  99 BB 0F       STA $0FBB,Y //勇者の行動に02(防御)をセット
0BA98A  C8             INY
0BA98B  CC E6 0C       CPY $0CE6
0BA98E  90 F0          BCC $0BA980 //多分、人数分ループ処理
0BA990  38             SEC
0BA991  80 01          BRA $0BA994
0BA993  18             CLC
0BA994  C2 10          REP #$10
0BA996  6B             RTL
                     ----------------

勇者起床判定

                     --------sub start--------
00D514  F0 E3          BEQ $00D4F9
00D516  B9 BE 0E       LDA $0EBE,Y
00D519  F0 1F          BEQ $00D53A //眠りフラグ0ならジャンプ
00D51B  C9 01          CMP #$01
00D51D  F0 04          BEQ $00D523 //1ならジャンプ
00D51F  3A             DEC //フラグを更新(4→3→2→1と減らす)
00D520  99 BE 0E       STA $0EBE,Y
00D523  AA             TAX
00D524  22 50 A5 0B    JSL $0BA550 //乱数取得
00D528  DD 75 D2       CMP $D275,X //乱数と以下の数値を比較
//D276(3T目以降) : C0、D277(2T目) : 80、D278(1T目) : 40
00D52B  90 04          BCC $00D531//乱数が規定値未満なら、起床処理へ
00D52D  A0 08 00       LDY #$0008 //表示用眠っているフラグ?
00D530  60             RTS
                     ----------------

 

勇者起床処理

                     --------sub start--------
00D531  A9 00          LDA #$00
00D533  99 BE 0E       STA $0EBE,Y
00D536  A0 0A 00       LDY #$000A //表示用起きたフラグ?
00D539  60             RTS
                     ----------------

 

以下、DQ2での検証

 

敵出現時のHP決定処理

                     --------sub start--------

0B9D7C  C9 0A          CMP #$0A
0B9D7E  90 30          BCC $0B9DB0//最大HP10未満の分岐
0B9D80  DA             PHX
0B9D81  5A             PHY
0B9D82  48             PHA
0B9D83  A9 14          LDA #$14 //Aレジスタに20を入れる
0B9D85  22 7E A5 0B    JSL $0BA57E //振れ幅内(0~20)の乱数を返す
0B9D89  18             CLC
0B9D8A  69 50          ADC #$50 //0~20の乱数+80
0B9D8C  8D 02 42       STA $4202 //80~100の値をセット
0B9D8F  68             PLA
0B9D90  8D 03 42       STA $4203 //元のHPをセット
0B9D93  EA             NOP
0B9D94  EA             NOP
0B9D95  EA             NOP
0B9D96  EA             NOP
0B9D97  AE 16 42       LDX $4216 //80~100の値×元のHP
0B9D9A  8E 04 42       STX $4204
0B9D9D  A9 64          LDA #$64
0B9D9F  8D 06 42       STA $4206
0B9DA2  EA             NOP
0B9DA3  EA             NOP
0B9DA4  EA             NOP
0B9DA5  EA             NOP
0B9DA6  EA             NOP
0B9DA7  EA             NOP
0B9DA8  EA             NOP
0B9DA9  EA             NOP
0B9DAA  AD 14 42       LDA $4214 //÷100
0B9DAD  7A             PLY
0B9DAE  FA             PLX
0B9DAF  60             RTS
                     ----------------

RTA in Japan Winter 2025 感想

RTA in Japan Winter 2025に走者・解説として参加しました。

走者としてはオンライン含めて5回目、解説としては初の参加でした。

 

カービィ

走りの反省点なのですが、ダイナ1面のミックス時、パラソルで同時吸いするまでのかっこいい動きで「パチパチあるだろうなー どのくらい反応あるかなー」とか考えてたらミックスに集中できずミスりました。集中すべきところで意識を別のところに持っていったらダメですね。

ちなみに、銀河に願いをの途中で「ペゴッ」って感じの音が頻繁に鳴っています。(長い間一位で走ってたので配信上にも乗ってる。)これはDSの不具合でタッチが誤動作しているために鳴っています。
元々は誤動作しない正常なDSを使う予定だったのですが、当日使う予定だった正常なDSの取り込みがうまく動かなくなったので急遽不具合のあるDSを使うことになりました。音はちょっと気になるけど無視してください。
DS2台目なかったら普通に詰んでました。持ってきててよかった~。

 

解説のわかざさんには4人並走の解説という難しいポジションで色々と頑張っていただきました。ありがとうございました。

最後のノヴァ爆破が想像以上に反響大きかったですね。

ゼルダ

知り合いのオクトパスカルさんがゼルダを走ることになり、このゲームを走っている日本人が他に僕しかいないということで解説に起用いただきました。2人とも、ゼルダ勢というよりはゲームウォッチ勢(?)です。

本番では想定以上にタイムが早かったこともあり、最低限のゲーム内容説明とゲーム概要説明で終わってしまいました。もう少しプレイングも詳しく話したかったのですが、走者解説2人ともゲームウォッチ好きな人間なので話したい周辺情報が多くてね……。

空いた時間にオクトパスカルさんが持ってきた実機のゼルダやマンホール(ニューワイド版)を遊ばせてもらいました。どちらも海外限定発売で、本体だけで数万円、箱付きならもっと高額になる貴重なものです。
ギャラリーに収録されているゲームでも実機ならではのプレイ感覚の違いが味わえて面白かったです。

 

ちなみに今のゼルダ世界1位は0xwasさんです。RiJの時にオクトパスカルさんがGBAのROMを貸して、その2日後にはWR更新してました。

www.speedrun.com

 

終わり

ゲームキューブのディスク読み込み不良の対処法について

近年(2021年頃から?)、ゲームキューブのディスク読み込み不良が多くの本体が発生しています。

経年劣化によって多くの本体で同時多発的に起きている問題と思われ、私も借り物含め4台ほどの初期型GC全てでこの問題に悩まされました。

 

一時的な対処法(非推奨)

この問題に簡単に対処する方法としては、ディスクドライブの可変抵抗を低くする方法が知られています。

 

ゲームキューブ 読み込み不良」検索上位の記事↓

ディスクを読まないゲームキューブを自分で修理してみた - 積みゲーマー死ねない

【修理徹底解説】ゲームキューブのディスクが読み込まないので、分解してレーザー出力調整してみた | 英二六ブログ

 

しかし、これらの方法で読み込めるようにしても、数か月~1年程度使っていると再びディスクを読み込まなくなってしまいます。

 

根本的な対処法

以下の動画で紹介されています。

youtu.be

詳しいことは動画本編を見てもらえばいいのですが、少しだけ説明。

  • 読み込み不良の原因は、コンデンサーの劣化によるレーザーの出力不足。
  • 可変抵抗による修理は、レーザーの出力を上げて延命していただけ。
  • コンデンサーを取り換えることで、抵抗値を従来の値に戻しても問題無く読み込めるようになる。

交換手順は私は説明できないので、動画や他の方のブログ記事などを参考にしてください。自己責任でお願いします。

 

なお、本記事投稿時点でこれらの動画はGoogle検索の上位に出てきません。(Youtubeでは検索上位に出ているが。)

ということで、本記事投稿のメインの目的は上記の動画への動線を増やすことだったりします。

 

その他の代替策

GBIやSwissの専用機としてGCを使っている場合、ディスクドライブを復活させる以外にも複数の起動方法があります。いずれもゲームディスクからSave Exploitするより早く、便利に起動できるようになります。

2025年現在、入手しやすいものは以下の2つです。(GC LoaderやCubeODEは入手困難という認識)

picoboot

特徴

  • はんだ付けが必要。
  • 起動時に好きなソフトを直接起動できる。
  • 費用は1500-2000円程度。(セットでSDGeckoまたはSD2SP2もついてくる。)

biosの画面すら飛ばして即好きなソフトを起動できるため、非常に快適な環境になります。はんだ付けが得意な人ならコンデンサ交換の代わりにこちらだけ導入すればいいかもしれません。

導入記事:【GCハード改造】Picoboot導入記事 - 聖域跡地(仮)

FlippyDrive

特徴

  • はんだ付け不要。
  • 通常起動後、SDカードのファイルを直接指定して起動できる。
  • 費用は約10000円。
  • かなり長納期。

ゲームキューブ本来のbios起動後にディスクドライブをエミュレーションしているため、picobootと比べると自由度は低いようです。価格も高めですが、はんだ付け無しで起動する貴重な手段です。

picoboot買ってはんだ付けをココナラとかで外注した方が早くて安くて快適かも。

公式:FlippyDrive | Crowd Supply

余談

調べてみたら割と初期からコンデンサ交換してた人もいるみたいです。

GC 光学ドライブ基板のコンデンサ交換

しかし読み込み不良修理の検索に全く引っかからず誰も気づかなかったという……。

GBギャラリー4 自己ベスト一覧

ある程度真面目にやったやつだけ載せます。
1000点超えた段階で満足してやめたのもありますが。
まるごと保存・復元は本ゲーム起動中1度も使っていません。

2025/11/08追記:現在VCのデータを吸い出してGBIでプレイしています。GBIで新しく出した記録を右側にカッコで併記します。

FIRE

いま - やさしい:1034
いま - むずかしい:928(1982)
いま - すごくむずかしい:357(984)
むかし - やさしい:669(1026)
むかし - むずかしい:691(2701)

ギャラリー1の3DSVCでいま版すごくむずかしい9999点達成済み。

DONKEY KONG 3

いま - 1P:1030
むかし - 1P:1000

1000点でやめたシリーズ。
特に難しい要素は無い。

DONKEY KONG JR.

いま - やさしい:1047
いま - むずかしい:774(1200)
むかし - やさしい:266(855)
むかし - むずかしい:277(534)

むかし版むずい。

MARIO'S CEMENT FACORY

いま - やさしい:1000
いま - むずかしい:1002
むかし - やさしい:1000
むかし - むずかしい:1000

1000点でやめたシリーズ。
むかし版はスペランカーみたいに落ちたら4ぬのでちょっと難しいが、安定取ればわりといける。

RAIN SHOWER

いま - やさしい:1397
いま - むずかしい:1148
むかし - やさしい:1584
むかし - むずかしい:640

むかし版GAME Bではカラスが勝手に紐を動かしてくるのでストレスがすごい。
GAME Aの1584はかなり上振れた。

BOXING

いま - 1P:15
むかし - 1P:16

15点(☆5)でやめたシリーズ。
特に難しい要素は無い。

CHEF

いま - やさしい:1370
いま - むずかしい:1568
いま - すごくむずかしい:279
むかし - やさしい:805
むかし - むずかしい:995

いま版はヨッシーが大きくなる時やタマゴを産む時など、動きが止まって食べてくれない場合がある。これがものすごいストレス。
こういう目に見えづらい余計な要素をゲムヲ系ゲームに持ち込むな。
むかし版GAME Bでギリギリ1000点行けてないの悔しい。

FIRE ATTACK

いま - やさしい:1009
いま - むずかしい:1907
むかし - やさしい:650(792)
むかし - むずかしい:493

むかし版はまあまあ難しいが、出てきた順に対応すればいいだけなのでFIREよりはやりやすい印象。でもそんなに頑張ってないので低スコア。

OCTPUS

いま - やさしい:1026
いま - むずかしい:1003
むかし - やさしい:843(2048)
むかし - むずかしい:772(1710)

むかし版けっこう頑張って1000点目指したけど、どっかでやらかす。こまめに戻るのと宝取り続けるのどっちがいいのかね?

DONKEY KONG

いま - やさしい:1004
いま - むずかしい:1000
むかし - やさしい:940
むかし - むずかしい:571

むかし版の2段目が苦手。

MARIO BROS.

いま - やさしい:1651
いま - むずかしい:1158
いま - すごくむずかしい:1644
むかし - やさしい:575(4408)
むかし - むずかしい:598(1170)

ギャラリー3のSwitchOnlineでむかし版GAME B 9999点達成済み。

MANHOLE(以下、いま版無し)

むかし - やさしい:5376(9999)
むかし - むずかしい:2981(9999)

一番遊んだゲーム。
時々Aボタンが2回反応してしまい、それが原因でミスることがある。ソフト側の問題か僕が使ってるWiiUの問題か不明。(追記:WiiU側でした。GBIでは発生していません。)
ギャラリー1の3DSVCでむかし版GAME B 9999点達成済み。

PARACHUTE

やさしい:1148(2846)

むずかしい:(1109)

むずかしい最高速度で木に引っかかったのが落ちてくるとき、ありえん速さで落ちるから常に警戒しないといけない

BOMB SWEEPER

むずかしい:3160

パターン全暗記したらどのくらいいけるのか気になる。
進めるほど残り時間が短くなっていくので、どこかで操作が間に合わなくなりそう。

CLIMBER

2237

操作をいくらでも早くできるアクションゲーム。
スピード感があって気持ちいい。

ZELDA

9999

周を重ねると敵の体力増えてちょっとずつ大変になる。
でもそんなに難しくない。

DQ1RTA 竜王乙ってメッセージ速度1で不思議実持ってて吟味する場合について考えてみた

タイトル長いけど普通によくあるシチュエーション

 

今回比較するパターン

パターン①:セーブ後即リセットしてメッセージ速度を変えてから吟味開始

パターン②:セーブ後吟味して、成功ならセーブ&リセットしてメッセージ速度を変える(失敗ならリセしてパターン①と合流)

 

前提

メッセージ速度は絶対8にする(タイム差知らないけど、単純化のため)

吟味成功後はセーブせずに竜王城に向かう

 

計測

セーブから会話終了まで 2.1秒

不思議実使用からセーブまで 4.9秒

吟味失敗ロス 1回あたり18.2秒

 

計算結果

パターン②を使う場合のパターン①との差分

・1回目成功→7秒ロス

・1回目失敗→2回目の挑戦時点で16.1秒のアドバンテージ

 

結論

4吟味でも5吟味でもパターン②の方が期待値早そう

HPバーの計算式

ごあいさつ

こちらは Pokemon Past Generation Advent Calendar 2024 3日目の記事です。

adventar.org

「ネタあるけど枠埋まってるからいいや」と思ってたら空いたので入れさせていただきました。

 

結論

  • 少なくとも3-5世代においては、HPバーの長さは48ピクセル*1
  • 少なくとも3世代においては、"現在HP/最大HP*48(小数点以下切捨て、0以上1未満は1)"がHPバーの長さになると思われる。*2
  • バーの長さを判別するためには、なるべく高解像度でキャプチャして上からこのように画像をのせてやるのがおすすめ。(私の環境ではどうしてもちょっと滲む。)

この時のHPは"8緑"(長さで言うと32ピクセル

 

結論から話せとこの間小学校で習ったので結論から言いました。計算式は適当に仮説立てて計算したら一発で当たったので、特に紆余曲折とか無いです。

HPが49以上の敵に関しては1ピクセルの中に複数のHPが入っていることがあります。その場合は正確に特定することはできません。

 

HPバーと向き合うことにした背景

敵のHPバーには数値が表示されないのですが、RTAでは正確な数値を知ることで戦略を変えたいという場面がちょくちょくあります。
(例)「PP節約のため、確実に倒せるなら弱い技で代用したい」「低威力の命中安定技で倒せるか知りたい」「効果抜群・いまひとつのテキストを出したくないので、等倍技で倒せるか知りたい」「敵の回復ラインに入らないよう調整したい」等

 

しかし、ネットで調べてもHPバーの仕様に関する記事は見つかりませんでした。
ということで、以前は仕方なくエミュで一個一個調べては比較画像を作り、目印を覚えて判別していました。

体当たりを打つべきか、マドショを打つべきか判別する画像。Lの右端より左なら体当たり。

これでは比較画像作りも面倒だし、覚えるのも大変です。

ちなみに日本版はまだマシで、英語版では文字が左右にキュッとまとまってるので目印がなくなってさらに判別し辛いです。無理ゲーです。

かいりきを打つべきか、マドショを打つべきか判別する画像*3

ということで、

  • できれば計算式を特定して計算だけで分かるようにしたい。
  • 英語版でも現実的に見分ける手段が欲しい。

と思い考えた結果が上記の結論です。

これ乱数とか対戦とかやってる人にとって役に立つんですかね?ナイトヘッドでHPの個体値判別するとかならできるかなあ。

 

おわりに

個人的に、ポケモンwiki等で手に入らなくて困っている情報はまだあります。
例えば、ダメージ計算式の持ち物補正・ランク補正・バッジ補正がどの順番でかかるのかが分かりません。順番が変わると端数処理の関係でダメージが変わってくるので割と重要です。確定だと思ってたら実は乱数15/16でしたーなんてこともあり得るので。

こういったことも気が向いたときに調べて共有できたらいいなと思います。*4
いつか自分で解析できるようになりたいなあ。

 

終わり。

 

明日はサイファー氏によるポケスタ2の乱数についての記事です。

e52301147.hatenablog.com

 

*1:サファイアファイアレッドハートゴールド、ブラックで確認済み。

*2:何パターンか計算し、英語版サファイアで確認したら全てその通りになった。

*3:この場面ではかいりきのPPが切れていることが多く、判別できてもあんま役に立たない。

*4:前に調べたんだけど結果に矛盾が生じててよく分かんなくなってやめた気がする。