Skip to content

Latest commit

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Gambit

A rule-based automation addon for Final Fantasy XI (Windower). Define conditional "gambits" that automatically execute actions when a set of conditions are met — similar to the AI programming systems found in some other RPGs. Designed for multi-boxing, support automation, and intelligent behavior chains.


Table of Contents


Installation

  1. Place the Gambit folder in your Windower addons/ directory.
  2. Load the addon: //lua load Gambit
  3. To auto-load, add lua load Gambit to your Windower/scripts/init.txt.

Commands

//gambit start Enable gambits
//gambit stop Pause all gambits
//gb start Shorthand versions work too
//gb stop
//gb aura List active aura suppressions
//gb aura reset Clear all aura suppressions
//gb txt <speaker> <text...>

Gambits begin processing as soon as start is issued. Use stop to pause without unloading.

//gb txt injects (Speaker) text as if it arrived in party chat — test chat-driven gambits without a second character (e.g. //gb txt Bob keep rolling chaos).


Configuration Files

Gambits are defined in Lua files inside the stacks/ folder. The addon searches for a config file in this priority order (first match wins):

PriorityFilename
1CharacterName_JOB.lua (e.g. Bob_COR.lua)
2CharacterName-JOB.lua
3CharacterName_Corsair.lua (full job name)
4CharacterName-Corsair.lua
5CharacterName.lua
6COR.lua (job-only template)
7Corsair.lua
8default.lua

Start by copying stacks/test_gambits.lua and renaming it to match your character and job.

Config File Structure

-- stacks/MyChar_WHM.luaregistered_gambits=flatten{
-- Put your gambits here
}

The only required export is the registered_gambits table. No includes are needed: every condition and helper is injected by the engine as a bare global name (is_engaged, tp_above, chat_match_buff, handle_cures, gambit, when...). The full registry also remains reachable as Gambits.* for back-compat.


Writing Gambits

Gambit Types (Triggers)

Every gambit is built around a trigger type that determines when it fires.

gambit{}

Every gambit is a condition list plus an action. Fires when all listed conditions pass (AND logic).

gambit{
condition1,
condition2,
condition3,
action=use_command("Savage Blade", "t"),
}

Conditions are evaluated left to right; evaluation stops at the first failure. action is required — a gambit without one fails at load, not mid-fight. (Gambits.multi_condition_trigger.cond({...}, action) is the underlying implementation and still works.)


Legacy trigger types

Earlier trigger modules (hp_below_trigger, hpp_below_trigger, tp_trigger, ja_recast_ready, ma_recast_ready, chat_trigger) predate the realization that everything is a condition list + action. They still work via Gambits.*, but each is expressed more flexibly as a gambit{}:

Legacy triggergambit{} equivalent
hp_below_trigger(500, act)gambit{ hp_below(500), action = act }
hpp_below_trigger(70, act)gambit{ hpp_below(70), action = act }
tp_trigger(1000, act)gambit{ tp_above(1000), action = act } (or when "tp >= 1000")
ja_recast_ready("X", act)gambit{ ja_recast_ready("X"), action = act }
ma_recast_ready("X", act)gambit{ ma_recast_ready("X"), action = act }
chat_trigger("msg", act)gambit{ chat_match("msg"), action = act }

timed_input (repeating timer commands) has no condition equivalent and remains available: Gambits.timed_input.cond(command, start_delay, repeat_delay).


Conditions

Conditions are bare global names — no imports. The name is the condition module's registry key with any trailing _cond dropped (tp_above_condtp_above, chat_match_condchat_match).

gambit{
is_engaged(),
tp_above(1000),
action=use_command("Savage Blade", "t"),
}

See the Conditions Reference for all available conditions.


Actions

For literal commands use use_command; to execute the current decision (see below) use do_action. Both are available globally (no require needed).

use_command(command, target)
Target valueIn-game target
"me" or "self"<me>
"t" or "target"<t>
nilDefault target for the spell/ability
use_command("Cure IV", "me") -- /ma "Cure IV" <me>use_command("Savage Blade", "t") -- /ws "Savage Blade" <t>use_command("Berserk", nil) -- /ja "Berserk"do_action() -- perform the decided action on the decided targetqueue_commands(B"buff_casts") -- run a match-time-decided command list, one per GCD

The Decision System

The decision is how Multi-Condition Triggers pass data forward. Selector conditions fill decision = {action, kind, target} — the action name, what kind of action it is ("spell", "ja", "ws", "item"), and who it is aimed at. Later conditions read the decision, and do_action() executes it.

Example: Cast the appropriate buff when asked in chat

gambit{
-- Matches any known buff name in chat and sets the decision to the-- best available spell for it, targeted at the speaker. Pass nil to-- listen to any speaker, or pass "Bob" to only listen to Bob.chat_match_buff(nil),
-- Checks that the decided action can be performed right now (recast-- ready, enough MP, correct job level, not silenced, in range, etc.)-- With no argument, can_use always checks the decision.can_use(),
-- Perform the decided action on the decided target (the speaker)action=do_action(),
}

Dynamic arguments: every condition argument may be a literal or a function(params) -> value, evaluated at check time. Three helpers cover the common cases:

HelperReadsExample
G"name"a global set with set_global_conditiondecide_spell(G"geo_spell")
D"field"a field of the current decisionbuff_active(D"action", 3)
B"key"chat-command captures (chat_command<name> slots)set_global_condition("geo_spell", B"buff")

Decision-setting conditions:

ConditionWhat it decides
chat_match_buffBest available spell for the buff mentioned; target = speaker
chat_match_healBest available heal spell mentioned; target = speaker
decide_spellSet the action to a specific spell
decide_targetSet the target ("me", "t", a name, or G"var")
decide_itemSet the action to an item (e.g. decide_item("Remedy", "me"))
decide_leaders_targetTarget = leader's current target
decide_geo_targetGeo-style target: leader's target for offense, leader for buffs
decide_abilitySet the action to a job ability (target left for whoever filled it)
decide_runeSet the action to the rune JA for an element
decide_highest_tierSet the action to the highest castable tier of a buff
decide_unclaimed_targetTarget = nearest unclaimed mob from a name list
decision_target_is / decision_target_is_notGate on who the decided target is

Combining Conditions

A gambit's condition list is an implicit AND. For OR and NOT, wrap conditions in combinators — each returns a normal condition, so they nest freely (and/or/not are Lua keywords, hence the names; all_/any_/and_/or_ are aliases of all/any):

-- "the leader is fighting": engaged, OR staging a never-engaged bosslocalleader_fighting=any{
leader_is_engaged(G"leader"),
leader_target_is(G"leader", S{"Aminon"}),
}
gambit{
buff_not_active("Copy Image (3)", 1),
any{
is_engaged(),
all{
check_global_equals("pull_mode", true, false),
not_(leader_is_engaged(G"leader")),
},
},
action=use_command("Utsusemi:Ni", "me"),
}

Rules: use combinators for gates only (keep decide_* selectors in the flat list); all{}/any{} short-circuit left to right, so put cheap conditions first. Edge-triggered conditions compose correctly: any{} commits state only for the child that passed, not_() never commits the wrapped condition.


Scalar Checks: when

when "expr" builds a condition from a comparison over scalar game words. The string is compiled once at stack load and evaluated every tick; an unknown word or syntax error fails at load with the known-word list, so a typo can never reach a fight.

Vocabulary: tp, hp, hpp, mp, mpp, pet_hpp, target_hpp — scalars only, by design. Boolean world-state (engagement, pet existence...) stays in named conditions, composed in the gambit list or with all{}/any{}/not_. and/or/not inside a string are expression syntax over scalars.

gambit{ is_engaged(), when"mp <= 500 and tp >= 1000", action=... },
gambit{ when"tp >= 500 and hpp <= 60", action=... },
any{
when"hpp <= 30",
all{ is_engaged(), when"mpp <= 10" },
}

Exact equivalents of the named threshold conditions (mind the boundaries — the named library is inconsistent): tp_above(1000) = when "tp >= 1000" and the *_below vitals are inclusive (hpp_below(60) = when "hpp <= 60"), while pet/target hpp are strict (pet_hpp_below(50) = when "pet_hpp < 50"). pet_hpp/target_hpp read as false when the pet or target is absent, matching the named conditions.


Aura Suppression

Some mobs project auras: a debuff re-applied every second or two that no cure can remove (Erase completes with "no effect"). Without protection the cleanse gambits would burn every GCD against it. The engine detects and suppresses these automatically:

  • Party members: when your status cure completes and removes NOTHING it could address, every surviving addressable debuff on that target is convicted. Casts that remove something never convict, so multi-effect Erase works down real debuffs first and the aura costs exactly ONE wasted cast. Yagrush AoE cures convict several members from a single splash; two convicted members make it party-wide.
  • Self: your own buff packet shows expiry timestamps -- an effect whose expiry keeps advancing without ever dropping is convicted with zero casts.
  • A convicted (member, debuff) reads as absent to every cleanse gambit and the downtime sweep. Explicit chat heal requests bypass suppression.
  • Doom is exempt: Cursna's doom removal is chance-based, so doom surviving a cast is normal attrition — it never convicts and Cursna keeps being cast for as long as doom is up.
  • Lifecycle: suppression holds while the debuff stays present (15s grace for people moving out of range), re-earns itself every 90s with one probe, clears on zone change, and announces once per detection ("Aura detected: ..." -- set the global aura_announce to false to silence).
  • //gb aura lists active suppressions; //gb aura reset clears them.

Reacting to Actions

Gambits can react to what players and monsters DO -- spells landing, job abilities and weaponskills firing, monsters readying TP moves and starting casts. Action packets are normalized onto a short-lived queue with the same consumption rules as chat: a matched packet is consumed even when the response was blocked -- no retries -- and unheard packets are dropped after 5 seconds. Reactions only get the action slot on ticks that chat and state gambits left free, so on a busy tick (GCD, mid-cast) a packet can be heard a few seconds after the event.

MatcherFires when
on_cast(what, opts)a party member's spell LANDED
on_ability(what, opts)a party member's job ability went off ("voke" resolves via ja_requests)
on_weaponskill(what, opts)a party member's weaponskill landed
on_mob_ability(what, opts)a monster's TP move LANDED
on_readies(what, opts)a monster BEGAN READYING a TP move -- the stun/defense hook
on_mob_starts_casting(what, opts)a monster STARTED CASTING a spell -- the interrupt hook (interrupted casts never match)

The what argument, all matchers: nil matches anything of that kind; a set or list (S{"Shock", "Thunder IV"}) matches exactly those names, nothing else; a string tries the family tables first ("dia" = every Dia tier, "fire" = every single-target Fire tier) and falls back to the literal name. When an exact spell is what you mean, use the set form.

opts.by narrows the actor: player matchers default to any party member (trusts included); pass "me", a name, or a set. Mob matchers accept a mob name or set.

Every matcher fills the decision target (by id) plus B"actor" / B"what" scratch, so a decide_spell / decide_ability + can_use + do_action completes the response. LANDED matchers target whoever the packet says was hit; the START matchers (on_readies, on_mob_starts_casting) target the acting mob instead, so a stun response needs no decide_target:

-- when Joe lands any Dia tier on a mob, Light Shot that mobgambit{
on_cast("dia", { by="Joe" }),
decide_ability("Light Shot"),
can_use(),
action=do_action(),
},
-- when Kam'lanaut starts casting Dispelga, Stun himgambit{
on_mob_starts_casting("Dispelga", { by="Kam'lanaut" }),
decide_ability("Stun"),
can_use(),
action=do_action(),
},

Data-Driven Stacks

Repeated gambit families (presets, toggles, WS-by-target rules...) live as data tables at the top of a stack, expanded into gambits at one position in the list. Two engine helpers make this work:

  • flatten{...} — builds registered_gambits from a list whose entries are either single gambits or lists of gambits; lists expand in place, so priority stays positional.
  • expand(table, factory) — one gambit per data row. Positional rows {a, b, c} call factory(a, b, c); keyed rows {ja = "Pflug"} and plain strings pass through whole as factory(row).
localtoggles= {
{ "joe do hastes", "hastes", true },
{ "joe stop hastes", "hastes", false },
}
localfunctiontoggle(phrase, global, value)
returngambit{
chat_match(phrase, nil),
action=set_global_condition(global, value),
}
endregistered_gambits=flatten{
some_gambit,
expand(toggles, toggle), -- all toggles expand here, in table orderanother_gambit,
}

Adding a row to the table is the whole edit — no new gambit to write. When a family's priorities interleave with other gambits (WHM's cleanses), call the factory inline at each position instead of using one expand(). See stacks/GEO.lua (sets/profiles), stacks/WAR.lua (WS rules), and stacks/WHM.lua (both styles) for worked examples.


Conditions Reference

Vitals

All of these are also expressible as when-strings (see Scalar Checks).

ConditionDescription
hp_below(amount)Player HP <= amount
hpp_below(percent)Player HP% <= percent
mp_below(amount)Player MP <= amount
mpp_below(percent)Player MP% <= percent
tp_above(amount)Player TP >= amount
tp_below(amount)Player TP <= amount
target_hpp_below(percent)Current target HP% < percent
pet_hpp_below(percent)Pet HP% < percent
pet_hpp_above(percent)Pet HP% > percent

Buffs & Debuffs

ConditionDescription
buff_active(buff_name, count)Player has buff (count = stacks, default 1)
buff_not_active(buff_name, count)Player does NOT have buff
buff_active_on_party_member(name, buff_name, count)Party member has buff
buff_not_active_on_party_member(name, buff_name, count)Party member lacks buff
is_buff_missing_on_party_member(names_set, buff_name)Any listed member lacks the buff; decides that member as target
debuff_active_on_player(debuff_set, player_name)Named player has one of the debuffs; decides the cure
debuff_active_on_party_member(debuff_set)Any party member has one of the debuffs; decides member + cure

Abilities & Spells

ConditionDescription
ja_recast_ready(ability_name)Job ability is off cooldown (charge-aware for Quick Draw)
ma_recast_ready(spell_name)Spell is off cooldown
can_use(action, cast_while_moving)Can the action be performed now: level, recast, MP, status effects, movement, range, and zone restrictions (item kind checks inventory). With no argument it checks the current decision.
can_use_ability(ability_name)Job ability usable now (knows it, recast/charges ready). With no argument it checks the current decision.

Engagement & Movement

ConditionDescription
is_engaged()Player is in combat (and the target is claimed)
is_not_moving()Player is not moving
is_in_range(spell_name)Target is in range for spell
is_target_valid()The decided target is in range of the decided spell
leader_is_engaged(leader)Leader is in combat (pass a name or G"leader")
leader_is_not_engaged(leader)Leader is NOT in combat

Targeting

ConditionDescription
target_is(name_or_set, invert)Target name matches (pass a set S{...} for multiple names; set invert=true to negate)
leader_target_is(leader, name_or_set)Leader's target matches
target_name_changed(watched_name, uuid)Named player's target changed since this gambit last fired (edge trigger; unique uuid per gambit, shared uuid = shared edge)
leader_target_changed(leader, uuid)Leader's target changed (by index) since this gambit last fired
leader_target_name_changed(leader, uuid)Leader's target changed (by name) since this gambit last fired

Pets

ConditionDescription
has_pet()Player has an active pet
does_not_have_pet()Player has no pet
pet_in_range_to_target(leader, range, use_leader_target)Pet is within range of the (leader's) target
pet_not_in_range_to_target(leader, range, use_leader_target)Pet is NOT within range

Chat

ConditionDescription
chat_match(text, speaker)Exact chat match. Pass nil for speaker to match anyone.
chat_command(pattern, speaker)Whole-command pattern: literal words + <name> captures into B"name" (chat_command("set entrust <buff>")). Typed captures canonicalize and validate the word: <buff> (gd.buffs + picker), <spell> (the "cast" offense grammar), <ja> (gd.ja_requests keywords); <name:type> gives a typed capture its own key ("swap <first:buff> <second:buff>"). Any other <name> captures the raw word. Parsed and validated at load.
chat_match_buff(speaker)Chat contains a known buff name; decides the best spell. Bare requests are distance-aware: the self-AoE tier when the requester stands within 10 (cast on self), the single-target tier when farther. "buff " / "buff t" explicit targets always use the single-target tier.
chat_match_buffs(speaker)ADDRESSED multi-buff request ("joe shell protect str"): every word must resolve as a buff keyword (joins allowed), two-buff minimum, each aimed at the speaker with the same distance rule; uncastable words get one combined /p line. Pair with chat_strip_my_name(true) and action = queue_commands(B"buff_casts").
chat_match_heal(speaker)Chat contains a known heal spell
chat_match_ja(speaker)Chat contains a ja_requests keyword ("light shot", "voke"); enemy JAs target the speaker's current target, player JAs target the speaker. Only characters that have the ability respond.
chat_strip_my_name(required)Address router: strips this character's name when the message starts with it ("joe haste" → "haste" on Joe only), so a request can be routed to one character. Place FIRST, before chat matchers. Pass-through by default (bare requests still work for everyone); chat_strip_my_name(true) passes only when addressed.
chat_match_offense(speaker)"cast <key>" → offensive spell at the speaker's current target (recast/MP-aware tier pick). Keys: family word = highest tier (cast fire, cast firaga, cast blizzara, cast dia); force a tier with fire3/fireiii/f3, fg1fg3 (+fg4 = Firaja), br1br3 (GEO -ra), dia2 etc. The "cast" gate keeps bare words ("dia", "sleep") on their status-cure meaning.

Weapons

ConditionDescription
main_weapon_type(type)Main hand weapon type matches ("Sword", "Great Axe", ...)
main_weapon_name(name)Main hand weapon name matches exactly

Global State

Useful for coordinating behavior across multiple gambits.

-- In an action, set a global variable:set_global_condition("mode", "healing")()
-- In a condition, check it:check_global_equals("mode", "healing") -- true when equalcheck_global_equals("mode", "healing", true) -- true when NOT equal (inverted)

One-Per-Fight

Ensures a gambit only fires once per combat encounter. Requires a unique UUID string to track state.

once_per_fight("550e8400-e29b-41d4-a716-446655440000")

Generate any unique string — you can use an online UUID generator or just make up a unique identifier.


Special Systems

Smart Healing (handle_cures)

An intelligent healing system for WHM/support characters. Rather than casting a fixed spell, it surveys the party, picks the optimal target and cure tier, and handles AoE vs. single-target decisions automatically.

registered_gambits=flatten{
handle_cures(),
}

Behavior:

  • AoE cure (Curaga) when multiple party members are critically low
  • Critical members (<= 55%) always outrank non-critical ones; ties break to lowest absolute HP
  • Alliance members are cured single-target only, and only when critical
  • Falls back to Full Cure when caster MP is below 80
  • Requires WHM main job or subjob for Curaga access

Set the global cure_alliance to false for party-only healing (unset defaults to alliance critical-only):

set_global_condition("cure_alliance", false)()

Bard Song Automation (handle_bard_songs)

A chat-driven song system for Bard. Party members issue commands in party chat to queue songs, which the BRD character then casts automatically. Handles Pianissimo for off-self targets, dummy song insertion, and automatic re-cast timing.

Setup

registered_gambits=flatten{
-- Pass the name of a global variable that holds the leader's name (or nil for anyone).-- Only that player's chat commands will be processed.handle_bard_songs("leaderName"),
}

The leader_var argument is the name of a global variable (set via set_global_condition) that contains the leader's character name or a set of names. Pass nil to respond to any speaker.

Global Variables

These globals control behavior and should be set in your config or via a gambit before using bard songs:

VariableTypeDescription
useJAbooleanWhether ja in a sing command actually uses Nightingale/Troubadour/Marcato
use1HrbooleanWhether 1hr in a sing command actually uses Clarion Call + Soul Voice
hasHonorMarchbooleanSet to true if the BRD has access to Honor March (instrument-gated)
hasAriabooleanSet to true if the BRD has access to Aria of Passion
do_dummy_songsbooleanWhether to insert dummy songs between real songs to push old buffs off
dummy_song_onestringSpell name for the 2nd-slot dummy song
dummy_song_twostringSpell name for the 3rd-slot dummy song
dummy_song_threestringSpell name for the 4th-slot dummy song

Chat Commands

All commands are spoken in party chat.


Sing songs once:

sing [songs...] [target] [ja] [1hr]

Queues up to 5 songs for a single cast cycle.


Keep singing (auto-repeat):

keep singing [songs...] [target] [ja] [1hr]

Queues songs and automatically re-casts them before they expire. The system calculates recast timing from song duration + gear bonus. Max 4 songs (5 with 1hr).


Stop all songs:

clear songs

Clears both the active cast queue and any repeating song cycles.


Check what's queued:

which songs

Prints each active repeating song cycle to party chat, including what songs are in it, the target, and time until next cast.


Parameters for sing / keep singing

ParameterDescription
Song keyword (e.g. march, minuet)The buff type to sing. Automatically selects the highest available tier. Case-insensitive.
Character name (e.g. Bob)Cast songs on that player instead of self (uses Pianissimo automatically).
jaPrepend Nightingale, Troubadour, and Marcato before singing (requires useJA = true).
1hrUse Clarion Call and Soul Voice before singing (requires use1Hr = true).

Multiple songs and modifiers can be combined in any order:

keep singing march minuet ja Bob

→ Use Nightingale/Troubadour/Marcato, then Pianissimo + highest March tier on Bob, then Pianissimo + highest Minuet tier on Bob. Auto-repeats.

sing ballad madrigal 1hr

→ Clarion Call + Soul Voice, then Ballad and Madrigal on self. One time only.

Dummy Songs

When a BRD needs to overwrite old songs (e.g. to land 2× March), dummy songs fill the intermediate slots to push the old buff off the target's song list. Set do_dummy_songs = true and configure the dummy spell names:

set_global_condition("do_dummy_songs", true)()
set_global_condition("dummy_song_one", "Army Paeon")()
set_global_condition("dummy_song_two", "Army Paeon II")()
set_global_condition("dummy_song_three", "Army Paeon III")()

Corsair Roll Automation (handle_roll)

A chat-driven roll system for Corsair: party members request rolls in party chat, and the COR executes the full sequence — Crooked Cards, Phantom Roll, Double-Up decisions, and Snake Eye — automatically.

Setup

registered_gambits=flatten{
-- Same convention as handle_bard_songs: pass the NAME of a global-- variable holding the leader's name (or a set of names) to only obey-- that player, or nil to obey anyone. Own commands are always accepted.handle_roll(nil),
}

Chat Commands

All commands are spoken in party chat.

roll <roll> [<roll2>] [cc] one-shot: roll one or two rolls
keep rolling <roll> [<roll2>] [cc] auto-repeat: re-roll whenever a
tracked roll buff drops
clear rolls stop repeating and flush the queue
  • Roll keywords resolve through gd.rolls (gambits/gambit_defines.lua), which knows every roll plus aliases: job shorthand (drk = Chaos, whm = Healer's, rng = Hunter's) and effect words (att = Chaos, acc = Hunter's, exp = Corsair's, cure = Healer's). Each entry carries the roll's lucky/unlucky numbers.
  • cc queues Crooked Cards before the first roll (skipped gracefully if on cooldown).
  • A request is ignored while the asked-for buffs are already up or a roll sequence is mid-flight.
keep rolling chaos hunters cc

→ Crooked Cards, Chaos Roll (+ Double-Ups), then Hunter's Roll (+ Double-Ups), re-rolling each whenever its buff drops.

Double-Up Strategy

Decided per result from the roll's lucky/unlucky numbers:

ResultAction
lucky or 11stop (11 is the max; doubling risks bust)
10, Snake Eye readySnake Eye → Double-Up (guaranteed 11)
below 6Double-Up (cannot bust from < 6)
unlucky, Snake Eye readySnake Eye to escape → Double-Up
anything elsestop; a bust just advances to the next roll

Double-Up and Snake Eye are gated on the "Double-Up Chance" window: if the window expires with follow-ups still queued, the slot finalizes at the total it landed on — at most one wasted attempt, never a retry loop.

Rolling pauses (and resumes when clear) under silence, sleep, terror, petrify, stun, charm, mute, Omerta, amnesia, and impairment, while mounted, and while moving. Two textboxes show the pending action queue and the repeat status per roll.


Examples

Warrior: Use abilities on cooldown

registered_gambits=flatten{
gambit{ is_engaged(), ja_recast_ready("Berserk"), action=use_command("Berserk", "me") },
gambit{ is_engaged(), ja_recast_ready("Warcry"), action=use_command("Warcry", "me") },
gambit{ is_engaged(), ja_recast_ready("Aggressor"), action=use_command("Aggressor", "me") },
}

White Mage: Self-cure when HP is low

registered_gambits=flatten{
gambit{
hpp_below(50),
action=use_command("Cure IV", "me"),
},
}

Corsair: Chat-driven rolls

registered_gambits=flatten{
handle_roll(nil),
}

Then in party chat: keep rolling chaos hunters cc — or test without a party from the COR's own console: //gb txt Bob roll corsair. See Corsair Roll Automation for the full command set and Double-Up strategy.


Any job: Cast buff when asked in party chat

registered_gambits=flatten{
gambit{
chat_match_buff(nil),
can_use(),
action=do_action(),
},
}

Melee DPS: Weaponskill with conditions

registered_gambits=flatten{
-- Only use Garland of Bliss to build Aftermath: Lv.3 (don't waste if already up)gambit{
is_engaged(),
tp_above(3000),
buff_not_active("Aftermath: Lv.3", 1),
action=use_command("Garland of Bliss", "t"),
},
}

Corsair: Dance on specific enemies, once per fight

registered_gambits=flatten{
gambit{
is_engaged(),
tp_above(100),
is_in_range("Box Step"),
target_is(S{ "BossA", "BossB", "BossC" }, false),
ja_recast_ready("Box Step"),
once_per_fight("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
action=use_command("Box Step", "t"),
},
}

Tips

  • Order matters: Conditions are checked left to right. Put cheap/fast checks (like is_engaged) before expensive ones (like range checks).
  • GCD: After an action fires, the cooldown before gambits run again depends on what was used. The addon intercepts the outgoing packet to set the delay: spell casts use ~3.1 seconds, job abilities and weaponskills use ~1.2 seconds.
  • Multiple gambits: The addon processes the registered_gambits list in order. The first gambit whose conditions all pass will execute, then the GCD kicks in.
  • UUIDs for once_per_fight: Each unique once_per_fight condition needs its own UUID so different gambits can independently track their per-fight state.
  • Testing: Use the commented-out examples in stacks/test_gambits.lua as a reference when building new gambits.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages