2023年5月18日木曜日

mote-include:ヘイストやインスニをしても、強化魔法スキル装備に着替えない件

結構いまさら感ある内容ですが、mote-includeの話を書いてみます。

mote-include を使った強化魔法での自動着替えで注意が必要になるというお話です。


ヘイストとかインスニとか、バフの効果時間の延長を狙って強化魔法の効果時間延長装備に着替えたいわけですが、 強化魔法用に sets.midcast['強化魔法'] を定義してもデフォルトでは着替えてくれません。これはバグではなくて仕様です。

でも、ヘイストIIの場合はちゃんと着替えてくれますし、ストンスキンとかアクアベールでも着替えてくれます。ちょっと謎な仕様ではあります。

結論から申し上げますと、Mote-Mappings.lua で以下のように定義されていて、これが悪さ?をしています。

no_skill_spells_list = S{'Haste', 'Refresh', 'Regen', 'Protect', 'Protectra', 'Shell', 'Shellra','Raise', 'Reraise', 'Sneak', 'Invisible', 'Deodorize'}

この no_skill_spells_list に含まれている魔法は、midcast のときに着替えない仕様となっています。より正確には、sets.midcast['強化魔法'] の対象外となるようにプログラムされています。

そのことは、Mote-Include.lua にもちゃんと書かれておりました。

-- List of spells and spell maps that don't benefit from greater skill (though
-- they may benefit from spell-specific augments, such as improved regen or refresh).
-- Spells that fall under this category will be skipped when searching for
-- spell.skill sets.
classes.NoSkillSpells = no_skill_spells_list

強化魔法スキルの恩恵を受けないから、着替えないと書かれております。でも、スキルの恩恵はなくても強化魔法の効果時間延長のためには着替えて欲しいわけで、なかなか微妙なところではあります。

応急処置としては、ユーザーlua ファイルの get_sets() の中で classes.NoSkillSpells を上書きしてしまうのが一番楽ちんだと思います。

-- Initialization function for this job file.
function get_sets()
    mote_include_version = 2
    -- Load and initialize the include file.
    include('Mote-Include.lua')
    classes.NoSkillSpells = S{'Protect', 'Protectra', 'Shell', 'Shellra', 'Raise', 'Reraise' }
    set_language('japanese')
end


あるいは、個別に

sets.midcast.Haste = sets.midcast['強化魔法']

とかでも良いですが、classes.NoSkillSpells を上書きしちゃう方が楽かな。


2022年6月7日火曜日

menu IDって何のID?

 NPCと会話するとincoming 0x034でmenu idが送られてきますが、このmenu idって何のテーブルのIDなのか良くわからなかったので、WEB検索して少し調べてみました。

https://www.reddit.com/r/FFXIPrivateServers/comments/tcxycb/xievents_ffxis_event_vm_fully_reversed/

https://github.com/atom0s/XiEvents

英語ですが上記のリンクが参考になりました。そして、結論から言うと、Zone Events DATファイルの中のイベントスクリプトのID(EvectExecNum)でした。かなりややこしいデータ構造なので、備忘録代わりに簡単にさらっと書いてみます。なお、変数名は上記リンクのものをそのまま使用しています。

まず、例えばルルデの庭のDATファイル群は以下のようになっていて

zone id: 243
エリア名: Ru'Lude Gardens
Zone Entities:  ROM/27/52.DAT
Zone Events:   ROM/21/52.DAT
Zone Dialogs/Strings:  ROM/25/52.DAT(English)

一番下のZone Dialogs/StringsのDATファイルの中にはNPCがしゃべる実際のテキストデータが格納されています。たとえば、シグネットをかけてやろう、などの文とかです。なお、このテキストデータのIDは、incoming 0x02AのMessage IDとして使われています。エリチェンする度にハッピーパワーのログが表示されたりしますが、あれは0x02Aで送られてきており、そのとき指定されるのがこのテキストデータのIDとなります。(ただし、Message IDの最上位ビットをクリアする必要があります。それとバージョンアップの度にMessage IDはコロコロと値が変わるので扱いづらいです)

windower の lib/dialog.lua の function dialog.get_entry(dat, id) を使うと、datファイルとMessage IDを引数として渡すことで、対応するエリアのDATファイルからIDで指定されたテキストデータを取得することができます。

なお、引数である dat には function dialog.open_dat_by_zone_id(zone_id, language) の返り値をそのまま代入します。(処理が終わったらクローズ処理が必要です)

ここまでの話はそんなに難しくないのでよいのですが、ここから難しくなります。


Zone Events DATファイルの話をします。

まず、上記のリンクに書かれていることによると、NPCとおしゃべりをするとクライアントプログラムは裏でEventVM(仮想マシン)を動かしているとのことです。例えば、シグネットをかけてやろう、においては、文字を表示したり、ユーザーの選択を待ったり、NPCに口パクをさせたり、ユーザーにエフェクトをかけたりなど、いろいろな処理が行われますが、そういった処理が、どの順番でどういうタイミングで実行されるのかをスクリプトのようにくみ上げ、それをバイトコード列としてあらかじめDATファイルに記録しておき、実行時にそれを動的に解釈して処理を実施しています。また、カットシーンのNPCの動きなども仮想マシンで処理されていおり、汎用性の高いスクリプトのようです。

どういう順番でどういう処理をするのかという指示データはバイトコード列(EventData)としてZone Events DATファイルに格納されていて、そのバイトコード列のIDが menu id です。つまり、イベントスクリプトのIDです。なお、これは、かなりざっくりした説明で、実際にはもっと細かくデータ構造がくみ上げられています。

バイトコード列はマシン語のようにオペコードとオペランドの列になっていて、たとえばオペコード0x1Dはオペランドで指定した文字列を表示する命令になっています。

例えば、0x1D 0x04 0x80というバイト列の場合、0x04が表示する文字列の番号になっていて、これはImidData[0x04]で指定されているテキストデータを表示するという意味になります。ImidDataもZone Events DATファイルに記録されているデータで、Message IDの配列となっているようです。なのでオペランドは間接参照のようですね。ですが、これは0x80の場合のみで、0x80以外の場合はワークメモリ(Work_Zone)を参照したりします。

上記のリンクのすべてを読みこなせているわけではないので、これ以上の細かい部分の説明はできませんが大枠ではこんなところです。

で、一番肝心なことですが、menu idの意味が分かったとしてそれが何の役にたつのかという質問の答えは、現状はたいして役には立たないかも^^)vという回答になりそうです。


2020年9月19日土曜日

構文図(precast, midcast, engaged, idle)[Mote-Include]

Mote-Includeのsets.precastなどの構文図のようなものを作ってみました。




・sets.precast

















・sets.midcast











・sets.engaged






・sets.idle

作って気が付きましたが、sets.engagedとsets.idleとでは、user_customize_*_set()とcustomize_*_set()の呼び出し順が逆なんですね。