パワポケ・パワプロ

【パワポケ12】千脇の情報をくれる人の出現条件を調べてみた【小ネタ】

皆さん、こんにちは。ぽけ太です。

今回は、パワポケ12で千脇の情報をくれる人について調査した内容のまとめです。

興味のある方はぜひ最後までご覧ください。

※本記事は、DSソフトの非公式デバッガー(NO$GBA)を利用した内部解析の情報を含んでいます。本来は入手不可能な情報も一部含まれていますので、抵抗のある方はブラウザバックをお願いします。

Q.なぜ調査しようと思ったのか?

A.パワポケ12の乱数調整有り育成の運要素を消し去りたかったため。

千脇の情報をくれる人のイベントは表サクセス序盤の必須イベントですが、ソフト起動時から消費されるフレーム数をもとに発生有無が決まる特殊なイベントとなっています。

パワポケ12のイベントは線形合同法により生成される乱数(初期シードと定数が既知)によって決定されるため、実機であっても乱数調整を駆使すればある程度イベントの発生タイミングを左右することができますが、このイベントは消費フレーム数が関わっていること以外は発生条件が分かっておらず、乱数調整を使用した育成チャートを組む際の運要素となっていました。

イベントは”情報ポイント”という特定の場所へのうろつきで蓄積するパラメータに依存しており一定の数値を超えると100%発生するものとなっていますが、乱数調整を使用して育成する以上は最低限のコマンド数かつ再現性を持ってイベントをこなしたいです。

なので今回しっかりと調査を行い育成時の運要素を消しさろうと試みました。

結論:発生条件は分かったが実機で再現性を確保するのは難しそう

逆アセンブルを読んで調査した結果、消費フレーム数を25で割った余りが情報ポイント-1以下のときにイベントが発生するとわかりました。

イベント発生フラグが成立するのは情報ポイントが6以上であることを踏まえると、25フレーム周期の初手6フレームを狙うことができれば任意でイベントを発生させることができるということになります。

乱数調整が可能なゲームの中にはポケモンシリーズのように1フレーム単位での調整が必要なものも有りますが、それらを狙えるプレイヤーも毎回成功するわけではありません。

ポケモンのようにリセットペナルティがないゲームであれば、今回のイベントは「実機での調整が可能である」と結論づけていましたが、少なくとも私は6フレームを安定して狙える気がしません。

エミュレータで検証した際にAボタンの入力から判定まで5フレームほどラグがあったことも踏まえると実機での再現性は確保できないでしょう。

ちなみにX(旧Twitter)でこの話を先行した際は、メトロノームを使う方法がほのめかされていたので興味のある方は再現してみてください。

最後に

今回は小ネタとして、パワポケ12で千脇の情報をくれる人の出現条件を紹介しました。

出現条件の詳細を明らかにして実機での調整を可能にすることが目的でしたが、結果としてはこの願望を実現することが難しいことが分かりました。

パワポケの内部解析は実機での乱数調整を目標として行っていますが、今回のような小ネタも増やしていきたいなと考えています。

おまけ

今回のイベント発生条件は、アセンブリ言語をPythonに起こすことで見つけ出しました(高水準言語としてPythonを選んだのは私がそれしかできないからです)。

実際に使用したコードを乗せるので興味のある方はご自身の環境で試してみてください。

ちなみに私は25フレーム周期の表現についてアセンブリ言語の時点では見抜くことができませんでした。比較的単純な条件なだけに見抜けなかったことが悔しいです。

何かしら専門用語を当てはめることができる計算パターンなのかもしれませんが、数学に詳しくないのでわかりません。わかる方はコメントいただけると嬉しいです。

#アセンブラ関連の関数を定義

#Cレジスタのフラグ判定
def check_C_resister_32bit(num):
    if num > 0xFFFFFFFF or num < 0x0: #キャリーorボローの場合、0x1を返す
        return 0x1
    else: #キャリーorボローがない場合、0x0を返す
        return 0x0

#オーバーフローとアンダーフロー対応
def check_num_flow(num):
    if num > 0xFFFFFFFF: #オーバーフロー対応(下位32bitを取得)
        num = num & 0xFFFFFFFF
    elif num < 0x0: #アンダーフロー対応(補数?取得)
        num = 0x100000000 - (num * -1)
    return num     

#ニーモニックcmp
def mnemonic_cmp(num1, num2, C):
    judge = num1 - num2
    C = check_C_resister_32bit(judge)
    return judge, C

#ニーモニックsub
def mnemonic_sub(num1, num2, C, s=False):
    num = num1 - num2
    if s == True:
        C = check_C_resister_32bit(num)
    num = check_num_flow(num)
    return num, C

#ニーモニックrsb
def mnemonic_rsb(num1, num2, C, s=False):
    num = num2 - num1
    if s == True:
        C = check_C_resister_32bit(num)
    num = check_num_flow(num)
    return num, C

#ニーモニックadd
def mnemonic_add(num1, num2, C, s=False):
    num = num1 + num2
    if s == True:
        C = check_C_resister_32bit(num)
    num = check_num_flow(num)
    return num, C

#ニーモニックadc
def mnemonic_adc(num1, num2, C, s=False):
    num = num1 + num2 + C
    if s == True:
        C = check_C_resister_32bit(num)
    num = check_num_flow(num)
    return num, C

#判定(範囲指定)

for frame_num in range(0, 10000+1):

    #定数・変数の初期状態
    info_point = 0x6 #情報ポイント
    r1 = 0x19
    r2 = 0x1C
    r3 = (frame_num >> 0x4)
    pass_flag = False #19F以下の特殊ケースに対してのフラグ(実機で気にする必要はない)
    r15 = 0x02038090
    C = 0x0 #Cレジスタのフラグ
    
    #消費フレーム数による判定1(0x190000 <= 消費フレーム数)
    judge, C = mnemonic_cmp(r1, (r3 >> 0xC), C)
    if judge <= 0x0:
        r2, C = mnemonic_sub(r2, 0x10, C)
        r3 = r3 >> 0x10
    
    #消費フレーム数による判定2(0x1900 <= 消費フレーム数  <= 0x190000)
    judge, C = mnemonic_cmp(r1, (r3 >> 0x4), C)
    if judge <= 0x0:
        r2, C = mnemonic_sub(r2, 0x8, C)
        r3 = r3 >> 0x8
    
    #消費フレーム数による判定3(0x19 <= 消費フレーム数  <= 0x1900 or 0x19 <= 判定1,2後のr3)
    judge, C = mnemonic_cmp(r1, r3, C)
    if judge <= 0x0:
        r2, C = mnemonic_sub(r2, 0x4, C)
        r3 = r3 >> 0x4
    
    #消費フレーム数による判定4
    judge, C = mnemonic_cmp(frame_num, r1, C)
    if judge < 0x0:
        pass_falg = True
    
    #ループ前段階
    r0 = check_num_flow(frame_num << r2)
    r1, C = mnemonic_rsb(r1, 0x0, C)
    r0, C = mnemonic_add(r0, r0, C, s=True)
    r2, C = mnemonic_add(r2, (r2 << 0x1), C)
    r15, C = mnemonic_add(r15, (r2 << 0x2), C)
    r15 = r15 + 0x8
    
    #ループ
    if not pass_flag == True:
        loop = int((0x02038214 - r15 + 0x4) / 0xC)
        for i in range(loop):
            r3, C = mnemonic_adc(r1, (r3 << 0x1), C, s=True)
            if C == 0x0:
                r3, C = mnemonic_sub(r3, r1, C)
            r0, C = mnemonic_adc(r0, r0, C, s=True)
    
    #最終判定
    if pass_flag == True:
        final_judge = 0x0
    else:
        final_judge = r3
    if info_point > final_judge:
        print(f'消費フレーム数:{frame_num}F {final_judge} 情報屋出現!!')
    else:
        print(f'消費フレーム数:{frame_num}F {final_judge} 出現しない')

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です