一通りStepManiaもしくはITGの譜面が作れるようになって、ちょっとギミックを使って演出を強化したりプレイヤーの邪魔をしたりしたいな、という方向けにいくつか記事を書いていこうと思います。特別編です。
今回は、luaをStepMania5で動かすときの、ハイスピの取得法について記載します。
半年も空いてしまいましたが、前回はこんなluaを書いていました。
このluaはまあ動くといえば動くんですが、多くの問題が残っています。
・XMod以外取得できない
・そのXModも8xまでしか取得できない上、0.05x刻み
・ApplyGameCommand系が、*にどんな数字を入れても一瞬で変化してしまう
・曲が終わった後もオプションが変更されたまま
他にもグローバル変数だから本体やテーマ側とかぶってバグることがあるとか見た目が悪いとか色々あるんですが、とりあえずプレイする上で致命的なものとして、ぱっと思いつくだけでもこれだけあります。
というわけでこの記事を含む2記事で、これらの問題を解決していこうと思います。今回はハイスピの取得です。
まずは天下り式に筆者が今使っている方法を紹介します。
とりあえず、上のluaでハイスピを取得してる部分を削除します。
それで、ハイスピ取得用のレイヤーを追加します。
そしてここにおまじないを入れます。
InitCommand内のbase_BPMはその曲の基本となるBPM(XModで合わせるBPM)に変えておいてください。
は? 長すぎますが……
更にmodsを格納しているレイヤーの、OnCommandをgotTSMessageCommandに書き換えます。
これで万事OKです。hispeedP1、hispeedP2がそれぞれP1、P2のハイスピとして使えるようになりました。
ただ使うならこれだけです。使い方の具体例&コピペ元としてはHagoromoSimfiles(3月)のふしぎなくすりがありますのでそちらをご参照ください。
めちゃくちゃ長いですけど、一回手打ちするとluaのフォーマットに慣れたり、なんとなくどういう動作をさせてるかわかったりするので温かみのある手打ちをしてみるのもおすすめです。
全部説明すると魔界になるので簡単な原理だけ紹介しておきます。
以前まで使用していたハイスピの取得法は「このオプション使ってる?」って聞いて「使ってるよ」って返ってきたらその値をハイスピとして記録する方式でした。
一方で今回使用している取得法は「使ってるオプション全部よこせ!」って言って一覧をもらい、その中からハイスピに相当する部分を探し出して記録する方式です。
この手法のいいところは、XMod、CMod、MModのそれぞれに対して「これ使ってる?」って聞きまくる必要がないことです。もっと言えば、0.05刻みじゃないXModや、5刻みじゃないCMod/MModにも対応できるので良いことづくめです。
というわけでまずは「使ってるオプション全部よこせ」を実装します。
使ってるオプション自体は
GAMESTATE:GetPlayerState(pn):GetPlayerOptionsString('ModsLevel_Song')
で取得することができます。ここでpnは1Pを取得するときは0、2Pを取得するときは1を指定します。 上のluaでは、これをpo1もしくはpo2としています。
次に、po1の中身を見てみると、だいたいこんな感じになっています(順番とかは適当です)。
3.5x, 50% mini, Overhead, metal, ...
ここから、ハイスピに相当するところだけを抜き出してきて保存すればよいです。とりあえず、XMod→CMod→MModの順に探していきます。
・XModを探す
XModの記述は以下のいずれかです。
3.5x(小数点を含む場合)
3x(小数点を含まない場合)
めんどくさいですね。とりあえず〇.〇xを探して、それがなかった場合に〇xを探す仕組みにしてあります。それがこの部分です。
if string.match(po1, '(%d+%.%d+)x') == nil then
hispeedP1 = tonumber(string.match(po1, '(%d+)x'));
%d+は任意の桁数の数字を表す正規表現です。つまりそういうことです。
・CMod/MModを探す
CMod/MModの記述は以下の通りです。
C600
これも%d+を使ってそれぞれ探しています。
if string.match(po1, 'm(%d+)') == nil then
hispeedP1 = tonumber(string.match(po1, 'C(%d+)'));
ところでCとMをXに変換するときにはBPMで割ってやらなければなりません。なのでbase_BPMなんていう変数を用意しておく必要があったんですね。
ところでここまでやっても何のハイスピも見つからない場合はあります。小数点以下のCModとか、1xを指定した場合です。 前者はまあそんなの使う方が悪いと思うんで無視するとして、後者の場合はそもそも使ってるオプションに表示されないので取得できません。
なので、ハイスピが見つからなかった場合 (hispeedPxがnilの場合) に、1を指定する行を最後に入れてあります。
で、このハイスピを探す全体の処理をSCREENMAN:GetTopScreen()がうまく動く=ScreenGamePlayに移行するまで待つようになっているんですが……よく考えたらこの処理いらない気がします。
ただopenITGの場合はこれを待たないとオプション一覧を取得できないのでその時の名残で残してあります。ついでにSCREEN系の変数も取得できるので便利なんですよ。いやほんとに。
ともあれこれでハイスピが取得できました。次はこれを使って正しくApplyGameCommandすることを目指します。