タイトルでラノベバリに出オチしておりますが、
初めてまともに書いたプログラムが
「ロマサガ2の集気法乱数調整アイテムドロップ補助ツール」でして
しかもWindowsのバッチで書いておりました。
あのツールは、今は亡きWindowMEのハードディスクとともに失われてしまっていたので、
せっかくの機会なので2x年越しに、当時を振り返って書き起こしてみることにしました。
ロマサガ2の乱数調整について前提共有
1990年代のコンシューマーゲームの乱数は、
しばしば乱数テーブル方式で実装されておりました。
おおむね256個サイズの配列に0~255の数値が格納されており、
乱数Index変数が指し示すテーブル値を乱数として取得する
その後、乱数Indexは何らかの方法で更新します。
何らかの更新方法は、
- 乱数Indexを固定値でいくつか進める方式 (俗に、乱数を消費すると言う)
- ゲーム内のタイマー(フレームカウンタ)を参照
などがありました。
ロマサガ2は前者で実装されております。
乱数の位置を知ることができたり、乱数の更新方法を制御できれば、
攻撃を全部クリティカルにしたり、レアドロップを確定入手できたりと思いのままで、
このような手法は乱数調整と呼ばれています。
ロマサガ2では、乱数の更新方法が比較的かんたんで、「攻撃したら1Indexを進める」のような感じで、固定で進めることができます。
これを使って、乱数位置を調整して、
敵を倒したとき、ちょうどレアドロップを入手できる乱数位置におぜん立てして、アイテムを入手する・・・
ということを補助するのが本ツールの目的です。
夏休み2日かけて全然ドロップしなかったレアアイテム:オートクレールに対して、これ以上コストかけてらんないと思って、乱数調整の補助ツールを作りました
ツールの出力
rs2.bat 800 20 40 340
最大HP = 800
魔 力 = 20
体術Lv = 40
回復量 = 340
0 R 1 2★ 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40★ 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 R 71 72 73 74 75 76 77 78 79 D
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119 120 121 122 123 124 125★ 126 127
128 129 130 131 132 133 134 135 136 137 138 D 139 140 141 142 143
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158★ 159
160 161 162 163 164 165 166 167 168 R 169 170 171 172 173 174 175
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190★ 191
192 193★ 194 195 196 197 198 199 200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215 216 217 D 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 D
バッチに、
引数で最大HP, 魔力, 体術Lv, 集気法を使って回復した値
を指定することで、
乱数テーブルの現在地候補を「★」マークで表示します。
ついでにアイテムドロップ、レアドロップ位置を、それぞれ「D」「R」マークで表示しています。
実装
改めて、書いてみるとかなり癖の強いプログラムが出来上がりました。
(バッチで書くもんじゃないと少し後悔しました)
@echo off
setlocal enabledelayedexpansion
set randomTable[0]=0
set randomTable[1]=229
set randomTable[2]=59
set randomTable[3]=208
・・・(中略)・・・
set randomTable[251]=209
set randomTable[252]=51
set randomTable[253]=246
set randomTable[254]=96
set hp=%1
set mag=%2
set lv=%3
set heal=%4
echo 最大HP = %hp%
echo 魔 力 = %mag%
echo 体術Lv = %lv%
echo 回復量 = %heal%
for /L %%i in (0, 1, 15) do (
set lineMessage=
for /L %%j in (0, 1, 15) do (
set "mark= "
set /A table_index=%%i*16+%%j
if !table_index! neq 255 (
call set randVal=%%randomTable[!table_index!]%%
set /A tmp=%lv% + %mag% + !randVal! %% %mag% + 1
set /A shukihoValue=%hp% / 8 + 3 * !tmp!
REM 乱数テーブルindex候補を★で記載
REM 64で割り切れたらレアドロップ、32なら通常ドロップ
set /A tmp=%heal%
set /A div64=!randVal! %% 64
set /A div32=!randVal! %% 32
if !shukihoValue! equ !tmp! (
set mark=★
) else if !div64! equ 0 (
set mark= R
) else if !div32! equ 0 (
set mark= D
)
set /A table_index=%%i*16+%%j
set "formatted= !table_index!"
set "formatted=!formatted:~-3!"
set "lineMessage=!lineMessage!!formatted!!mark! "
)
)
echo !lineMessage!
)
endlocal
一個ずつ、実装を見ていきます。
@echo off
コマンドがいちいち出力されるのをOFFにします。
setlocal enabledelayedexpansion
遅延環境変数を有効にします。
これをやっとかないとfor分がえらく大変になります。
set randomTable[0]=0
set randomTable[1]=229
set randomTable[2]=59
set randomTable[3]=208
・・・(中略)・・・
set randomTable[251]=209
set randomTable[252]=51
set randomTable[253]=246
set randomTable[254]=96
バッチって配列ないらしいので、
愚直に1行ずつ定義しました・・・
set hp=%1
set mag=%2
set lv=%3
set heal=%4
echo 最大HP = %hp%
echo 魔 力 = %mag%
echo 体術Lv = %lv%
echo 回復量 = %heal%
入力引数のsetと表示をしています。
for /L %%i in (0, 1, 15) do (
set lineMessage=
for /L %%j in (0, 1, 15) do (
set "mark= "
set /A table_index=%%i*16+%%j
set 変数名= ...で空の環境変数を定義します
set "mark= " ... 半角スペース*2を設定したいのでダブルクオーテーションで囲ってます。
set /A ... で数値演算できるようになります。
if !table_index! neq 255 (
ロマサガ2の乱数テーブルは0~254なので、255の時に実行しないようif文でガードしています。
call set randVal=%%randomTable[!table_index!]%%
配列のような書き方をしたとき、
callで呼ぶと変数が展開されることを利用して、randamTableの指定Indexの値=乱数値を参照しています。
set /A tmp=%lv% + %mag% + !randVal! %% %mag% + 1
set /A shukihoValue=%hp% / 8 + 3 * !tmp!
この辺は集気法の回復量の計算をしています。
REM 乱数テーブルindex候補を★で記載
REM 64で割り切れたらレアドロップ、32なら通常ドロップ
set /A tmp=%heal%
set /A div64=!randVal! %% 64
set /A div32=!randVal! %% 32
if !shukihoValue! equ !tmp! (
set mark=★
) else if !div64! equ 0 (
set mark= R
) else if !div32! equ 0 (
set mark= D
)
if文の中で数値演算のやり方がわからんかったので、div64のような変数を定義しております。。。
set /A table_index=%%i*16+%%j
set "formatted= !table_index!"
set "formatted=!formatted:~-3!"
頭に最初空白3つ付けた後、「:~-3」という書き方をすると先頭から3文字引いてくれます。
ここでは数値の桁数が少なければ、先頭を空白で埋めるフォーマット整形処理をやっています。
set "lineMessage=!lineMessage!!formatted!!mark! "
)
)
echo !lineMessage!
)
lineMessageに行の出力テキストを積んで、for文の最後で1行分出力しています。
endlocal
つかった環境変数をリセットします。
感想
このプログラムは、私のプログラムやっている理由の原点(効率よくゲームを攻略したい)であるので、
今回の機会に、記憶の中からサルページできてよかったです。