anoyetta の開発記録

FINAL FANTASY XIV 向けのプラグイン「補助輪」の開発をしています。セットアップ、便利な使い方、その他デバイスなどの情報を扱っています。

スペスペたいむからフィールドマーカを制御する

f:id:anoyetta:20210527121015j:plain

みんな大好きフィールドマーカの自動設置です。
絶などでは敵のフォーメーションに合わせてこちらのフォーメーションも動的に変わる。またその判断までの猶予時間が短い。予めフィールドマーカを置きたいが前述の通り、フォーメーションが動的に定まるため決め打ちで置いておくわけにもいかない。次のフェーズになったら別のフォーメーション、マーカ配置が必要になったりもしますからね。

ならば、PaisleyPark を外部から操作してプリセットを動的に切り替えてしまおうということです。

目次

絶アレキ「時間停止」を例に要件の確認

絶アレキサンダー討滅戦の時間停止を例にします。

f:id:anoyetta:20210527121019j:plain
時間停止ではこのようなフォーメーションを取る必要があります。

ざっくりとした殺され方

  • 6人にデバフが付与されます
    • 接近強制ペア → ペア同士が近くにいないと死刑
    • 接近禁止ペア → ペア同士が離れていないと死刑
    • 加重罰 → 耐性ダウン。他の攻撃を喰らったら死刑 → 後述のチェイサー、ジャスティスの攻撃に当たらなければ生存する
  • チェイサーは最も近い2名に向けて前方扇形物理攻撃を2回それぞれに放つ。1人1発まで耐えられる
  • ジャスティスは最も近い2名に向けて前方扇形魔法攻撃を2回それぞれに放つ。1人1発まで耐えられる
  • チェイサーとジャスティスの位置は毎回ランダムで入れ替わる

単純化した各PCの生存戦術

タンク、ヒーラーの行動

  • 接近強制 → 1の北に行け、そして極力ペア対象に近づけ
  • 接近禁止 → Aに行け
  • 加重罰 → Aに行け
  • 無罪(デバフなし) → 2の北に行け

DPSの行動

  • 接近強制 → 1の南に行け、そして極力ペア対象に近づけ
  • 接近禁止 → Bに行け
  • 加重罰 → Aに行け
  • 無罪(デバフなし) → 2の南に行け

思考を極小にするための対策

  • それぞれの行き先を瞬時に判断できるようにフィールドマーカを置く
  • 左右が入れ替わるので敵の位置を検知してフィールドマーカ2パターンを切り替える

「マーカなくても余裕です」というくだらん話はさておき単純で理解しやすいギミックですので例として採用します。
ちなみに「俺はなくても余裕だし」意訳すると「俺TUEEE」という話には興味はなく、戦闘前の準備で戦闘中の思考量を減らせられるならばなるべく減らしておいたほうが得だということですね。

PaisleyPark の準備

まずは PaisleyPark(以後、PPという) の準備をしましょう。
PP を外部から呼び出せるように設定します。デフォルトの状態だと外部からの呼び出し機能は止まっています。

f:id:anoyetta:20210527121023j:plain

手順

  1. Port に 1337 と入力する
  2. Stop の隣のチェックチェックボックスをチェックする
  3. Start を押す

これだけです。一応解説すると…

PP には REST API という外部 I/F が搭載されています *1。これは PP が簡易的なWebサーバとして機能しそのWebサーバに外部からアクセスすることによって PP を操作するという仕組みです。Webサーバへのアクセスとしてどのポート番号を使うのか?ということを決める必要があります。それが 1337 というわけです。ポート番号は2Byteの範囲内ですので1-65535の範囲内ならば何番でもよいです。1337 としているのは、PP の本家の解説で 1337 を使っているのでそれに倣っているだけです。
なお、同じPCの中で異なるアプリケーションが同じポート番号を使用することはできません。もしも 1337 を指定してエラーになるようでしたら *2、1337 は別のアプリケーションに使われているということなので別のポートを使いましょう。1つずらして 1338 にしたらいいと思います。

ここまでの PP の設定は sheeva さんが記事に書いているので詳しくはそちらを読みましょう。
blog.sheeva.me

敵のフォーメーションの収集

敵のフォーメーションに合わせてフィールドマーカを置き換えるには敵の位置を補足するトリガを作る必要があります。今回はチェイサーとジャスティスの位置が対象となるのですが、チェイサー、ジャスティスは左右入れ替わるだけですのでどちらかに注目するだけでよいです。よって欲しい情報は下記の通り。

  • ジャスティスが東に出現したときの座標
  • ジャスティスが西に出現したときの座標

「出現」と書きましたがデータ的にはジャスティスは戦闘開始時点から存在しています。したがって時間停止フォーメーションで出現したときに "Added new combatant" のログは発生しません。時間停止でジャスティスが出現したときにメモリを覗けばいいわけですが戦闘中にそんなことをする時間はありません。

dump を仕込む

そこで任意のタイミングで周りの戦闘員(Combatant)の座標をログに出力するという仕組みをスペスペたいむに追加してあります。<dump> というタグです。では、タイムラインに <dump> を仕込みましょう。

<a time="@(352 - origin)" sync="アレキサンダー・プライム:我はアレキサンダー……機械仕掛けの神なり……。 理想郷に到る道を導くため……我が審判を受けよ……。" />
<a time="@(356 - origin)" text="時間停止(詠唱)" sync="アレキサンダー・プライムは「時間停止」の構え。" />
<a time="@(365 - origin)" text="時間停止" sync="アレキサンダー・プライムの「時間停止」" />
<a time="@(366 - origin)" name="時間停止の座標">
  <dump target="Position" />
</a>
<a time="@(368 - origin)" text="神判(罰着弾)" sync="アレキサンダー・プライムの「誓約の神判」" />
<a time="@(370 - origin)" text="ソード+火炎放射1" sync="クルーズチェイサーの「アルファソード」" />
<a time="@(371 - origin)" text="ソード+火炎放射2" />
<a time="@(372 - origin)" text="ソード+火炎放射3" />
<a time="@(386 - origin)" text="神罰の熱線" sync="アレキサンダー・プライムの「神罰の熱線」" notice-o="-3" notice="熱線!">

このように仕込みます。
時間停止の詠唱が完了した1秒後ならば間違いなく欲しい位置に出現しているでしょう。そのタイミングで座標をログに出させるわけです。

拾った座標を解析する

拾ったログはこちら。

"00:0000:Hojoring:[TL] Dump position name="Taro Yamada" X="23.49" Y="23.49" Z="0.00" hp="93616" max_hp="93616"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Jiro Suzuki" X="23.52" Y="23.53" Z="0.00" hp="103384" max_hp="103384"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Saburo Tanaka" X="23.49" Y="23.50" Z="0.00" hp="133335" max_hp="148856"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Shiro Yamamoto" X="23.49" Y="23.57" Z="0.00" hp="103208" max_hp="103208"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Goro Noguchi" X="23.51" Y="23.53" Z="0.00" hp="104134" max_hp="104134"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Rokusaburo Michy" X="23.57" Y="23.47" Z="0.00" hp="93594" max_hp="93594"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Shichiro Seven" X="23.50" Y="23.52" Z="0.00" hp="100326" max_hp="148636"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="Hachiro Tacosuke" X="23.50" Y="23.51" Z="0.00" hp="93594" max_hp="93594"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.58" Y="23.66" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="イフリート・エギ" X="23.53" Y="23.45" Z="0.00" hp="89020" max_hp="89020"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="ブルートジャスティス" X="23.69" Y="23.67" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="アレキサンダー・プライム" X="23.28" Y="23.44" Z="0.00" hp="14800000" max_hp="14800000"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.58" Y="23.66" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.26" Y="23.45" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.58" Y="23.66" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.24" Y="23.40" Z="0.00" hp="14800000" max_hp="14800000"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="アレキサンダー" X="23.70" Y="23.28" Z="0.00" hp="148000" max_hp="148000"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="ブルートジャスティス" X="23.83" Y="23.50" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="ブルートジャスティス" X="23.55" Y="23.45" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="クルーズチェイサー" X="23.38" Y="23.50" Z="0.00" hp="8329440" max_hp="8329440"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="アレキサンダー・プライム" X="23.50" Y="23.26" Z="0.00" hp="12214440" max_hp="12214440"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="ブルートジャスティス" X="23.62" Y="23.50" Z="0.00" hp="8329440" max_hp="8329440"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="アレキサンダー・プライム" X="23.45" Y="23.67" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="リキッドレイジ" X="23.80" Y="23.50" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="アレキサンダー・プライム" X="23.45" Y="23.67" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="リキッドレイジ" X="23.50" Y="23.20" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="リキッドレイジ" X="23.20" Y="23.50" Z="0.00" hp="44" max_hp="44"","The Epic Of Alexander (Ultimate)"
"00:0000:Hojoring:[TL] Dump position name="脱出地点" X="23.50" Y="23.16" Z="0.00" hp="1065353216" max_hp="1065353216"","The Epic Of Alexander (Ultimate)"

かなりの行数が出力されます。プレイヤーには見えませんがこれだけのダミーデータが存在しているわけです。ここから自分の欲しいジャスティスの座標を特定します。

座標値だけを見ていても判別するのは難しいため私はこのようなスプレッドシートを作って座標をグラフにプロットしてチェックしています。 f:id:anoyetta:20200201162459p:plain
上記のログですとジャスティス東パターンでした。X=23.62 Y=23.50 が正解ということが分かります。ちなみに西のパターンは左右対称ですから計算で求めています。わざわざ東西両方のパターンを経験する必要はありません。

プリセットを用意する

次は PP にフィールドマーカを覚えさせてプリセットを作ります。今回は2セットのプリセットを用意します。

  • ジャスティス東パターン用 東から順番にA, 1, 2, B とマーカを配置したセット
  • ジャスティス西パターン用 西から順番にA, 1, 2, B とマーカを配置したセット

これは PP の普通の操作で作ります。絶アレキのフィールド内で実際に手動でマーカを配置してその状態をそれぞれプリセットとして保存します。気をつける点としては後からスペスペたいむから呼び出すときに日本語名だと手間が増えるためプリセット名は英語にしておくことです。

f:id:anoyetta:20210527121033j:plain
私はこのような名前で保存してあります。英語にしておくのがコツですが命名は任意です。自分で分かりやすい名前をつけておきましょう。

スペスペたいむから呼び出す

最終フェーズです。スペスペたいむから PP のプリセットを呼び出すトリガを作ります。

<t name="時間停止ジャスティスE" exec="http://localhost:1337/preset/ULT_ALEX_JUSTICE_E/load">
  <p-sync interval="120">
    <combatant name="ブルートジャスティス" X="23.62" Y="23.50" />
  </p-sync>
</t>

<t name="時間停止ジャスティスW" exec="http://localhost:1337/preset/ULT_ALEX_JUSTICE_W/load">
  <p-sync interval="120">
    <combatant name="ブルートジャスティス" X="23.38" Y="23.50" />
  </p-sync>
</t>

敵の座標に対してマッチングしますので <p-sync> を使います。マッチング対象の名前と先ほど取得した座標を指定します。
ヒットしたときの処理として PP のプリセットを呼び出すための URI *3 をセットします。

これで完成です。以後、時間停止に到達するとマーカが再配置されます。分かりやすいので時間停止を例にしましたがもっと複雑なフォーメーション系ギミックですとかなりの時短効果が見込めます。マーカの自動配置は見た目にも派手ですので思い通りに動作するとかなりの満足感があります。

まとめ

  • スペスペたいむに dump コマンドを仕込んで敵の座標を収集するよ
  • 拾った座標を解析して数値的に敵の配置を認識するよ
  • マーカが自動で配置されると気持ちいいよ

私はこの作業が結構好きです。根っからのITエンジニアなんだと思います。データをこねくり回して机上で戦うのが楽しいですね。孫子の兵法じゃないですが戦う前に勝敗が決まるほどに作り込むことにカタルシスを感じます。FFXIV ではそこまで自動化することは出来ないのであくまでも補助程度ですが。

あとはワールドファースト競争のさなかにプレイヤーが戦いながらこれを作るのは厳しいと思います。それより戦ったほうが早いか、バックでこういったものを用意してくれるスタッフを配置するかでしょうか。私もすでにギミックの解法がわかっているからこのくらいのコストをかけても対策を作り込むのであって、解法を試行錯誤している段階ではここまでの作業をやらないでしょうね。無駄になりますから。そして解法がわかった段階ではすでに対応出来る状態になっているわけで補助の追加の必要性は低くなっているでしょう。その後のギミックへのトライ数を稼ぐために前を固めるという意味で定まった解法をシステム化するというのは有効かもしれません。でもそれも戦闘員ではなくスタッフがやったほうがいいでしょう。戦闘員は最新の課題に取り組んでいる状態ですからね。

私としても面白い機能だと思いますので皆さんも楽しんでみてください。それでは!

おまけ:ぼくのかいたさいきょうのたいむらいん(未完)

あのさんのたいむらいんおきば

github.com

*1:実は私が機能を追加した。

*2:Razer Synapse が 1337 ポートを使っている場合がある。

*3:URLと同じ。API に対してのアドレスは URI と呼称する

©2019 anoyetta