FF14棒読みちゃん:スクリプト設定アラーム

このページでは、FF14棒読みちゃんにおける、スクリプトによるアラームの作成方法を説明します。

スクリプトモードでは、HTML/JavaScript/CSS という、Webページを作成する技術を利用して、
自由度の高いアラーム制御を簡単に行うことができます。
デバッグコンソールも利用可能なので、アラーム開発もスムーズにできます。

※ここではHTML等の各言語に関する説明は行いませんので、ご了承ください。
スクリプト設定アラームの利用時の注意点
JavaScriptでコードが制御できるため、セキュリティ面にご注意ください。
特に野良で配布(あるのかな…)されているようなものは、内容を確認してから利用するようにしてください。

アラームマネージャ経由で配布されているものは、作者がチェックしているので安全です。
(まぁ俺が安全だって宣言するのにどんだけ価値があるかわからんですが、少なくとも悪意のあるコードは設定しません。自分のアプリ蹴落とすようなことは意味ないしねぇ)

セキュリティ対策として、ページ移動禁止、隠しフレームや音声再生の禁止、マイク・カメラ禁止などを行っていますが、
それでも完全ではありません。予めご了承ください。

新規作成時

ルートファイルアラームを稼働させるメインファイルです。作成方法は後述。
ウィンドウ棒読みやサウンド再生のみの場合は、チェックをはずすと画面表示がなくなります。
背景背景色及び透過度を設定します。透過度は「0で透過しない」形になります。
開始条件FF14のログに、ここで定義したメッセージが出てきたら、アラームが起動します。
「極***の攻略を開始します。」などのメッセージを指定してください。
空の場合は常時起動となります。
終了条件同じく、ここで定義したメッセージが出てきたら、アラームが終了します。
空の場合は停止方法がなくなります。

アラームファイルの作成
ファイル構成
script_alarms
  ├ do_not_edit_base.js
  ├ sample
  │   ├ sample01.html ★ルートファイル
  │   ├ sample01.js
  │   ├ sample01.css
  │      ...
  ├ [任意のアラームフォルダ]
  │   ├ 任意のファイル名.html ★ルートファイル
普通に web ページを作るイメージです。
HTMLからはじまって、そこからJSやCSSを読み込みます。
読み込み元となる HTML を「ルートファイル」と呼びます。

新しくアラームを作る場合は、専用フォルダを1つ作成し、その中に必要なリソースを用意してください。
複数のウィンドウを制御することも可能です。

作成方法(考え方)
こちらもやはり、web ページを作る考え方とだいたい同じです。比率は違いますが。

・初期表示しておきたいHTML(とCSS)をはじめに書いておく(これはあまりない)
・イベント(ログとか)がくるので、それに応じてコンテンツを書き換える。

後者の書き換えで色々とインタラクティブなコンテンツを作ることで、リッチなアラームを作ることができます。
とはいえ作るの大変なので「このログが出てきたら読み上げする」だけを並べるだけでも、必要なものは作れると思います。

ただし、ユーザ操作は出来ません。alertすると動きますが閉じられないのでご注意。

デバッグ方法
開発の前に、デバッグ方法を確認しましょう。
メイン画面の「補助ツール起動」から、「スクリプトアラーム詳細設定」を開きます。


色々な条件がありますが、まずは「アラーム試験」タブにある「デバッグ機能を有効にする」にチェックをいれて、ツールを再起動しましょう。


この状態でアラームが起動すると、edge の開発者コンソールが表示されます。
ff14textreader.vhn というドメインの配下に作成したコードがあります。
あとはブレークポイント入れたり変数の参照したり、開発者コンソールの普通の使い方でデバッグができます。


注意点として、未定義の関数・メソッドや、変数の参照によるエラーが発生しません。
なんでかわかんねぇ…。しれっと処理が中断されます。
特に typo するとハマります。大文字小文字の差で参照できないとかもあります。ご注意ください。

とりあえずガワを用意しよう
手っ取り早いのでサンプルコピーからはじめましょう。

・sample01 とかのフォルダをまるまるコピーする。
・フォルダ名と、とりあえずルートファイルである sample01/sample01-a.html だけ、ちゃんとした名前にかえましょう。

とりあえずこれで何かができるので、この時点でアラーム定義の「新規作成時」の作業をしてください。
上から読んでる場合は「後述」って書いてあったのがここの話になります。

ルートファイルのHTMLを見てみよう
<!DOCTYPE html>
<html lang="ja">
<head>
	<!-- UTF8で保存してください -->

	<!-- ここから編集不可 -->
	<meta charset="UTF-8">
	<base href="http://ff14textreader.vhn/">
	<script src="/do_not_edit_base.js" type="text/javascript"></script>
	<link href="/do_not_edit_base.css" rel="stylesheet">
	<!-- ここまで -->
	
	<script>
		//自分のウィンドウIDを定義
		//このHTMLは親ウィンドウなので、空文字列。
		//どのアラームでも親ウィンドウはこの値。子ウィンドウを利用する場合は、
		//ウィンドウ生成時に指定する。
		var ownWindowId = "";
	</script>

	<!-- 例:処理用JSの読み込み 
			パスは script_alarms フォルダをルートとした絶対パス表記で記述する。-->
	<!-- サンプル:ログ読み上げ -->
	<script src="/sample01/sample01-a1.js" type="text/javascript"></script>
	<!-- サンプル:リキャスト管理 -->
	<script src="/sample01/sample01-a2.js" type="text/javascript"></script>
	<!-- サンプル:別ウィンドウ連携 -->
	<script src="/sample01/sample01-a3.js" type="text/javascript"></script>

	<!-- 例:ローカル以外のファイルを読み込む
			普通に読めます。このサンプルでは jquery を多用しています -->
	<script src="https://code.jquery.com/jquery-3.6.0.min.js" 
		integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
		crossorigin="anonymous"></script>

	<!-- 例;このアラーム専用のCSSの読み込み 
			同じようにパス指定する。-->
	<link href="/sample01/sample01-a.css" rel="stylesheet">

</head>
<body>
	<!-- タイトル -->
	<h1>リキャスト管理</h1>

	<!-- リキャスト表示領域 -->
	<div id="recastArea"></div>

	<!-- リキャスト用HTML デザイン例その1 -->
	<div id="recastLineTemplate" style="display: none">
		<div class="recastLine01-a" id="{ID}">
			<div class="titleLine">
				<span class="name">旅神のメヌエット</span>
				<span class="time">23.4 / 20.3</span>
			</div>
			<div class="barLine">
				<div class="barInner" style="width: 45%"></div>
			</div>
		</div>
	</div>


</body>
</html>
・編集不可のところはそのまま残しておく。
・ウィンドウIDも、とりあえず1ウィンドウで始めると思うので、そのまま残しておく。
・JSとCSSの読み込みを定義する。
※パスの書き方は注意が必要。
・HTML作る。
サンプルで入っているのはリキャスト管理のHTMLなので、
表示領域と、アクション1個分のHTMLのテンプレが書いてあります。
もちろんJSでHTML書いて入れてもOK。

ここは本当にふつーにHTML書く気分でかくだけ。

CSSを見てみよう(自分で)
ここでは引用しません。
書くことないくらい普通にCSS定義してるだけ!

JSを見てみよう
いくつか使っていますが、sample01-a1.js を見てみます。
表示の都合上、ちょっとコメントとか省略してます。
//サンプル:ログからのテキスト読み上げ
//UTF8で保存してください。

/**
 * 初期化
 */
document.addEventListener("DOMContentLoaded", function() {
	//イベント受信関数の登録
	document.addEventListener("alarm.log", onReceiveLogForReadText);
});

/**
 * イベント:ログ受信>テキスト読み上げ
 * 
 * @param {object} e イベントデータ
 */
function onReceiveLogForReadText(e) {

	//ログはタイミングにより複数同時に来るので、
	//ループしてきたものをすべて処理
	for (var i in e.detail) {
		//とりあえず1個参照して…
		var log = e.detail[i];

		//logType1 が18(LS3)であれば
		if (18 == log.logType1) {
			//読み上げを行う。
			AP_ReadBouyomi(log.message, true);
		}
	}
}

・イベント受信して。
・何か表示変えたりする。
・標準で用意してる特殊な関数も使えるよ。
です。

まずはとにかくイベントドリブンです。
「条件によって色々と画面が変わるHTML(とJS)」を作るわけですが、
その「条件」がイベントです。
それに加えて、このスクリプトアラーム特有の機能が用意されています。
テキストの読み上げとかです。

イベントの拾い方

document.addEventListener("DOMContentLoaded", function() {
	//イベント受信関数の登録
	document.addEventListener("alarm.log", onReceiveLogForReadText);
});
//イベント受けるほう
function onReceiveLogForReadText(e) { ... }
・document にイベントが飛んでくるので、そこのイベントを拾うようにコードを書きます。
・どこで定義してもよいですが、まぁDOMContentLoadedかなと。

イベントの種類は複数あります。
また、イベントごとに e.detail にデータが入っています。
詳しくは console.log して確認してください。
概要は以下となります。

イベント名データ概要
log
[
{ logType0: 0, logType1: 16, name: "Test Tarou", message: "テスト", time: 63783029828920 }
]
								
・ログの情報が格納されます。
・一度に複数のログ情報がくる場合があります。
・logType については、このページにある「ログ種類の指定方法」をご確認ください。
・time については、標準で用意している ticksToDate 関数に与えると Date オブジェクトが得られます。
player
{ job: "PLD", name: "Test Jiro" } 
・プレイヤーの情報が格納されます。
・タイミングによっては Unknown とか出てくる場合もあります。
・name が空なら無視しましょう。
pop
unpop
pop はキャラの出現検知、unpop は削除検知です。
いずれも以下のフォーマットです。
[
{
	name: "Test Saburou",
	id: 10293940,
	hpCurrent: 20293,
	castingId: 0,
	job: "AST",
	statusList: [ ... ]
}
]
・キャラの各種情報が格納されます。
・項目いっぱいあるので記載は省略します。大体キー名からわかるはず!
update ステータス(バフとか)の変動、アクション詠唱を検知します。
ちょっと複雑。
{
  update: [
    { type: 'Add', status: 'Status', statusId: 12345, castingId: 0, actorId: 298372123 }
    ...
  ],
  actor: {
    298372123: {
      name: "Test Shirou",
      id: 10232832,
      hpCurrent: 29123,
      ...
    }
  }
}
とりあえず update と actor の2種類のデータが入っています。
update は、ステータスの追加/削除、または詠唱中アクションの変更を表します。
status が 'Status' であれば前者、'CastingID' であれば後者です。
type は Add/Remove/Change(追加とか削除とか)、
statusId/castingId に変更後の値が入ります。

actorId はキャラIDです。この値をキーとして actor にあるデータを参照すると、
そのキャラの各情報が取得できます。


特有の機能ってなに?
・任意の設定の読み込み/保存
・全ユーザ情報のリクエスト(通常のイベントでは全部は来ないので)
・テキスト読み上げ
・子ウィンドウ制御(window.open とかは出来ません)
  。代わりにこの機能でサブ胃画面的なもの作れます。
・日付変換、ランダム文字列などのユーティリティ
...
などが用意されています。全部グローバル定義。
こちらの詳細については、script_alarms フォルダ直下に置いてある、
do_not_edit_base.js に定義があるので、参照ください。

結局これを使って何ができるの?
入力:FF14で起こったできごと(基本はログトリガ)
出力:HTML上で表現できるものの大半

です。いや出力は結構制限してるけど。その先はアイデア次第。

・特定のチャネルにログが来たら読み上げる(サンプルで作ってあるよ)
・シナジー系アクション発動したら目立たせる(これもあるよ)
・難しいギミックが来たら、あんチョコ画像作っておいて表示させる
・GP全快したら声でおしらせ。

色々できると思います。コーディング大変だけど、興味ある方はいろいろやってみてください(’’

ガチ攻略用にはやっぱり音声がおすすめ。
このツール作るのに「攻略における情報量の強化」があったんだけど、
聴覚を使ってないのもったいなくね?の思いがあった。だから「FF14棒読みちゃん」。
嗅覚とか触覚とかは無理。大技来たら臭くなるとか面白いかもしれんけどさ(’’

ログについて
アラーム構築の補助のために、スクリプトイベントに関連するログが記録できます。
スクリプトアラーム詳細設定の、その他項において、保存有無や保存フォルダが設定できます。

ログは大きく2種類あります。読みやすい版と詳細版。

読みやすい版の構成
通常ログは、指定したログフォルダ内に「20220424_script.txt」といったファイル名で保存されます。
内容はTSVになっており、1-4列目がその行が表すデータの種類など、5列目以降がデータ本体になっています。
全選択してEXCELとかスプレッドシートにコピペすると見やすいです。

各列の情報はイベントの種類によって変わります。
以下の通りとなります。
イベント種類1列目2列目3列目4列目5列目6列目7列目
ログ時間イベント名("log")logType0logType1発言者名メッセージ内容(空)
プレイヤー情報時間イベント名("player")(空)(空)プレイヤー名ジョブ(空)
キャラ出現情報時間イベント名("pop")(空)(空)キャラ名キャラID(空)
キャラ削除情報時間イベント名("unpop")(空)(空)キャラ名キャラID(空)
ステータス更新時間イベント名("update")CastingID(詠唱)
またはStatus(ステータス変更)
Add/Remove/Changeキャストまたは
ステータスID
キャラ名キャラID

詳細版の構成
通常ログは、指定したログフォルダ内に「20220424_script_detail.txt」といったファイル名で保存されます。
内容はTSVになっていますが、実際にJS側で受け取るデータがすべて記録されています。
データ量が多いので、何か調査したい場合は、通常ログであたりをつけてから詳細ログを見るようにするとやりやすいです。

各列の情報は以下で共通になります。
イベント種類1列目2列目3列目
共通時間イベント名JSONデータ