2025/4/21 全ジョブロジック大幅変更 コピペする前に必ずバックアップしてください!

GearSwapで使えるファンクションと優先度について

Mote-libsのhandle_actions(precastやmidcast)や各種ファンクションは、優先順位別に名前を変えて利用することができますが、少し複雑なので整理しておきたいと思います。

優先順位を変えて処理を組むと、ジョブロジックをシンプルにできるのでメンテナンス性が向上します。

例えば、睡眠時に自動で起床する処理など、全ジョブで共通する処理を共通ファイルに記述し、個別のジョブロジックで個別の定義をすることもできます。

どのファンクションを使えば正解なのかよくわからん状態をスッキリさせます。

目次

やりたいこと

buff_changが分かりやすいと思いますので、これを例にして説明します。

buff_changeの説明

Mote-Include.luabuff_changeは次のように定義されています。

function buff_change(buff, gain)
    -- Init a new eventArgs
    local eventArgs = {handled = false}

    if state.Buff[buff] ~= nil then
        state.Buff[buff] = gain
    end

    -- Allow a global function to be called on buff change.
    if user_buff_change then
        user_buff_change(buff, gain, eventArgs)
    end

    -- Allow jobs to handle buff change events.
    if not eventArgs.handled then
        if job_buff_change then
            job_buff_change(buff, gain, eventArgs)
        end
    end
end

このうち、次のものが利用可能で先に書かれているものが優先的に処理されます。

  • user_buff_change
  • job_buff_change

user_buff_changeが実行された後に、job_buff_changeが実行されます。

ただし、job_buff_changenot eventArgs.handledがフラグになっており、このフラグがfalseの場合に限り処理が実行されるようになっています。eventArgs.handledの初期値はfalseですので、意図的にeventArgs.handledをセットしない限り実行されるようになっています。

活用例

この仕組みを利用することで、ジョブロジックをシンプルにすることができます。

私の共通ファイルでは、次の定義をしています。

function user_buff_change(buff, gain)
    if state.Buff['睡眠'] then
        equip({main=sets.Weapons.Slip})
        equip({range=sets.Weapons.Slip})
    elseif buff == "ファランクス" and not gain then
        windower.add_to_chat(167,'■■■■■ファランクス切れ■■■■■')
    end
end

この処理が何をしているのかというと、次の通りです。

  • 睡眠の自動起床
  • ファランクス切れの通知

睡眠はプライムウェポンを利用することで自動的に起床できますので、該当ジョブの装備定義にsets.Weapons.Slipでプライムウェポンを書いておけば、自動的に起床可能です。sets.Weapons.Slipの定義がない場合はエラーにならずそのままスルーしてくれます。

buff_changeが必要なジョブは次のように定義します。これは魔道剣士のコードを抜粋したものです。

function job_buff_change(buff, gain)
    if buff == 'バットゥタ' and gain then
        send_command('gs c set OffenseMode Parry')

    elseif buff == 'バットゥタ' and not gain then
        send_command('gs c set OffenseMode Normal')

    elseif buff == 'エンボルド' and gain then
        equip(sets.buff['エンボルド'])
        disable('back')

    elseif buff == 'エンボルド' and not gain then
        enable('back')
        Idle()
    end
end

このようにjob_buff_changeを定義している場合、GearSwapはuser_buff_changeとjob_buff_changeを順番に実行してくれます。

job_buff_changeを定義しないジョブの場合は、user_buff_changeだけが実行されます。

その他ファンクション

Mote-Include.luaから抜き出して整理すると次のようになります。

これらはフラグ(cancel,handled)を組み合わせて処理の実行有無を判断できるしくみになっているのですが、フラグの制御はややこしく私は使いこなせていないため、説明は割愛します。

ファンクション名組み合わせ優先順位
pretarget
precast
midcast
aftercast
pet_midcast
pet_aftercast
filter_
user_
job_
default_
user_post_
job_post_
cleanup_
customize_idle_setuser_customize_idle_set
customize_idle_set
customize_melee_setcustomize_melee_set
user_customize_melee_set
status_changeuser_status_change
job_status_change
buff_changeuser_buff_change
job_buff_change

※customize_melee_setだけ順番が逆になっているのは、単純にバグだと思います。

この仕組みを利用することで、先に処理されるファンクションを共通ファイルに記述し、後に処理されるファンクションが必要なのであれば、個別のジョブファイルに書くことでシンプルに構成することが可能になります。

実行順番の確認

precastやmidcastの着替え、例えば属性帯の着替えなどを上書きするためにはどのファンクションで上書きすれば良いのかを確認するために、次のコードをgs degugmodeを使って検証してみました。

Mote-libsで該当するファンクションは次のものです。

function handle_actions(spell, action)
    -- Init an eventArgs that allows cancelling.
    local eventArgs = {handled = false, cancel = false}
    
    mote_vars.set_breadcrumbs:clear()

    -- Get the spell mapping, since we'll be passing it to various functions and checks.
    local spellMap = get_spell_map(spell)

    -- General filter checks to see whether this function should be run.
    -- If eventArgs.cancel is set, cancels this function, not the spell.
    if _G['filter_'..action] then
        _G['filter_'..action](spell, spellMap, eventArgs)
    end

    -- If filter didn't cancel it, process user and default actions.
    if not eventArgs.cancel then
        -- Global user handling of this action
        if _G['user_'..action] then
            _G['user_'..action](spell, action, spellMap, eventArgs)
            
            if eventArgs.cancel then
                cancel_spell()
            end
        end
        
        -- Job-specific handling of this action
        if not eventArgs.cancel and not eventArgs.handled and _G['job_'..action] then
            _G['job_'..action](spell, action, spellMap, eventArgs)
            
            if eventArgs.cancel then
                cancel_spell()
            end
        end
    
        -- Default handling of this action
        if not eventArgs.cancel and not eventArgs.handled and _G['default_'..action] then
            _G['default_'..action](spell, spellMap)
            display_breadcrumbs(spell, spellMap, action)
        end
        
        -- Global post-handling of this action
        if not eventArgs.cancel and _G['user_post_'..action] then
            _G['user_post_'..action](spell, action, spellMap, eventArgs)
        end

        -- Job-specific post-handling of this action
        if not eventArgs.cancel and _G['job_post_'..action] then
            _G['job_post_'..action](spell, action, spellMap, eventArgs)
        end
    end

    -- Cleanup once this action is done
    if _G['cleanup_'..action] then
        _G['cleanup_'..action](spell, spellMap, eventArgs)
    end
end

フラグの制御なしで実行すると、次の順番で処理されることになります。

  1. filter_precast
  2. user_precast
  3. job_precast
  4. default_precast
  5. user_post_precast
  6. job_post_precast
  7. cleanup_precast

次のコードをgs degugmodeを使って検証してみました。ファンクションが実行されるとファンクション名を出力するシンプルなものです。

function filter_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'filter_precast')
end
function user_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'user_precast')
end
function job_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'job_precast')
end
function default_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'default_precast')
end
function user_post_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'user_post_precast')
end
function job_post_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'job_post_precast')
end
function cleanup_precast(spell, action, spellMap, eventArgs)
    windower.add_to_chat(167,'cleanup_precast')
end

実行されていないファンクションもありましたが、この結果を見る限り次のことが分かります。

job_precastuser_post_precastの間、つまりdefault_precastで着替えが行われていたので、sets.precast.RAに定義している内容を上書きできるのはuser_post_precastjob_post_precastになります。

このことから、装備定義している内容をプログラムから上書きするには、user_post_precastjob_post_precastで行うのが正解だということが分かりました。

default_precastより前に実行させるべきファンクションの用途がいまいち分かりませんが、おそらくフラグを利用して着替えをキャンセルさせるなどになるのでしょうか。現実的な方法ではないと思うので、とりあえず無視しておきます。

コメント

コメントする

目次