Inner Ability
On this page
Provides read access to the character's Inner Ability lines for the currently active preset, a manual circulate() re-roll, and the Auto Inner Ability engine that circulates until your goals are met.
Line Object Structure
core.inner_ability.get_current() returns an array of line objects with the following properties:
| Property | Type | Description |
|---|---|---|
skill_id | number | Ability ID (see the ability reference) |
pos | number | Slot index (1-3) |
grade | number | Rarity grade (see table below) |
name | string | Ability name in your client's language, e.g. "Boss Monster Damage Increase" |
The name is the ability's name only — it never carries the rolled value. The magnitude the game shows in the tooltip is resolved separately and is not exposed here.
Inner Ability Grades
| Grade | Name |
|---|---|
0 | Rare |
1 | Epic |
2 | Unique |
3 | Legendary |
Functions
core.inner_ability.get_current() -> table
Returns an array of the active preset's Inner Ability lines (up to 3). Returns an empty table if unavailable.
local lines = core.inner_ability.get_current()
for _, line in ipairs(lines) do
core.log(string.format("Slot %d (Grade %d): %s", line.pos, line.grade, line.name))
endcore.inner_ability.get_active_preset() -> number
Returns the index of the currently active Inner Ability preset (0-based), or -1 if none. This is the preset get_current() reads and the default target of circulate().
core.inner_ability.get_honor_exp() -> number
Returns the character's current Honor EXP — the currency spent circulating Inner Ability (capped at 9,999,999).
core.inner_ability.circulate(preset_index?: number, lock_positions?: table) -> boolean
Re-rolls (circulates) the Inner Ability lines, spending Honor EXP. preset_index (0-based) defaults to the active preset. lock_positions is an optional array of slot numbers (1-3) to keep — the rest are re-rolled. Omit to re-roll all lines. Locking is only allowed once the preset is Unique grade or higher, and at least one slot must be left to re-roll.
-- Re-roll every line of the active preset
core.inner_ability.circulate()
-- Keep slot 1, re-roll the rest
core.inner_ability.circulate(core.inner_ability.get_active_preset(), { 1 })Auto Inner Ability
The functions above act once. Auto Inner Ability keeps circulating for you and stops on its own the moment the lines satisfy your goals, the Honor EXP floor is reached, or the circulate cap runs out. It is the same engine the web menu drives — right-click your character portrait on the Player screen and choose Auto Inner Ability — so a script and the menu can never disagree about a run.
Goal Object Structure
Each entry of opts.goals describes one line you want. Every goal must be met by a different line, so two goals never both count the same lucky roll.
| Property | Type | Description |
|---|---|---|
skill_ids | table | Ability IDs this goal accepts, e.g. { 70000035 }. See the ability reference |
min_level | number | Minimum line level |
min_grade | number | Minimum grade for that line (see the grade table above) |
ability | string | Legacy. Case-insensitive substring of the ability name, used only when skill_ids is absent |
Prefer skill_ids: IDs are the same in every region, while names are localized, so an ID-based goal keeps working on a non-English client. Some abilities share a display name — "Increases Speed" is both 70000005 and 70000011 — so a goal that means "either" lists both.
min_level is the line's level as the client reports it. Whether that equals the magnitude the tooltip shows (i.e. whether level 20 means "+20%") is not verified and is unlikely to hold for every ability — several lines bake a fixed value into their text, and the dual-stat lines produce two numbers from one level. Treat it as a floor on the level, not a promise about the percentage.
core.inner_ability.start(opts?: table) -> boolean, string?
Starts the Auto Inner Ability engine, returning false plus an error message if the options are invalid. Every option persists until changed, so omitting one keeps whatever the menu holds.
| Option | Type | Description |
|---|---|---|
preset | number | Preset index to circulate (0-31); omit or -1 for the active preset |
min_grade | number | Grade every line must reach (0-3). Default 2 (Unique) |
goals | table | Array of goal objects (see above). Empty means the grade gate alone decides |
auto_lock | boolean | Keep goal-matching lines and re-roll only the rest. Only lines that also clear min_grade are kept, and only on a Unique+ preset |
min_honor | number | Stop once Honor EXP reaches this. 0 spends it all |
max_circulates | number | Safety cap on circulates for the run (1-999), or 0 for no cap |
roll_delay | number | Milliseconds to wait between circulates (0-5000). Default 350. Not shown in the menu — scripts only |
Every goal row is validated before it is stored. A row that names no ability at all (neither skill_ids nor a non-empty ability), or whose min_level / min_grade / skill_ids are the wrong type, is rejected with false and a message rather than being carried into the run.
core.inner_ability.start({
min_grade = 3, -- every line Legendary
goals = {
-- Boss Monster Damage Increase, Legendary, level 20+
{ skill_ids = { 70000035 }, min_level = 20, min_grade = 3 },
},
min_honor = 50000,
max_circulates = 100,
})core.inner_ability.stop() -> boolean
Stops a running Auto Inner Ability run. Safe to call when nothing is running.
core.inner_ability.is_running() -> boolean
Returns whether an Auto Inner Ability run is currently active.
core.inner_ability.get_status() -> table
Returns the live state of the run: running, state, preset, circulates_used, honor_spent, and a result table mirroring what the web menu shows.
| Field | Type | Description |
|---|---|---|
running | boolean | Whether the engine is enabled |
state | string | idle, waiting_result, checking_result or cooldown |
preset | number | Preset this run is circulating (-1 once the run has ended) |
circulates_used | number | Circulates spent so far by the live run |
honor_spent | number | Honor EXP spent so far by the live run |
result | table | { status, circulatesUsed, honorSpent, honor, preset, nextCost, startLines, currentLines } |
result.status is one of circulating, rolling, done, no_honor, max_reached, preset_not_found, timeout or character_changed. honor is the character's Honor EXP balance at the time of the update. nextCost is the Honor EXP the run has reserved for its next circulate (see Circulate cost). startLines and currentLines are arrays of { pos, skillId, level, grade, name }, letting a script report what the run changed.
The top-level circulates_used / honor_spent / preset describe the run in progress and are cleared the moment it ends. Read the totals of a finished run from result.circulatesUsed / result.honorSpent, which keep the values that stopped it.
A run spends real Honor EXP on every circulate. Always set max_circulates (or min_honor) to something you are willing to lose — a goal that cannot roll will otherwise keep circulating until the cap trips. With max_circulates = 0 there is no cap at all, and min_honor becomes the only thing that ends an unrollable run.
roll_delay is the only pacing knob the run exposes, and the only wait in the engine the server can see. Lowering it makes a run finish sooner; if circulates start coming back refused (a timeout status), raise it again.
Circulate cost
A circulate is priced by the preset's current rank and by how many lines are locked. Before each circulate the engine reserves the larger of this table's price for the current rank and lock count and the cost the previous circulate actually took, and refuses to spend when that would break min_honor. Taking the larger of the two matters: the table can be wrong about a given server, while an observed cost goes stale the moment the preset ranks up or a lock is added.
| Rank | No locks | 1 locked | 2 locked |
|---|---|---|---|
| Rare | 100 | 400 | 900 |
| Epic | 200 | 1,100 | 2,600 |
| Unique | 1,500 | 3,000 | 5,500 |
| Legendary | 8,000 | 11,000 | 16,000 |
Examples
Print the active Inner Ability setup
local lines = core.inner_ability.get_current()
if #lines == 0 then
core.log("No Inner Ability lines")
return
end
for _, line in ipairs(lines) do
core.log(string.format("[%d] %s (skill %d, grade %d)",
line.pos, line.name, line.skill_id, line.grade))
endCirculate for a Legendary Boss Damage line, then report
core.inner_ability.start({
min_grade = 3,
goals = { { skill_ids = { 70000035 }, min_level = 30, min_grade = 3 } },
max_circulates = 200,
})
function on_tick()
if core.inner_ability.is_running() then return end
-- Read the finished run's totals from `result` — the top-level counters
-- are cleared as soon as the run ends.
local result = core.inner_ability.get_status().result
if result and result.status == "done" then
core.log(string.format("Done in %d circulates (%d Honor EXP)",
result.circulatesUsed, result.honorSpent))
end
endInner Ability Reference
Every ability a line can roll, from the client's own data (v270). Use these IDs in a goal's skill_ids.
Where two IDs share a name they are listed together — they are indistinguishable in-game, so a goal that wants that ability should list both.
| Ability | ID(s) | Effect |
|---|---|---|
| Additional Damage on Targets with Abnormal Status | 70000039 | +#y% damage when attacking targets inflicted with Abnormal Status. |
| All Stats Increase | 70000015, 70000044 | All Stats: +#strFX |
| Attack Increase | 70000012 | Attack +#padX |
| Attack Increase By Level | 70000025, 70000040 | Attack +1 for every #lv2pad levels |
| Attack Skill Target Increase | 70000047 | Number of enemies hit by multi-target skill +#targetPlus |
| Attack Speed Increase | 70000016 | Attack Speed +1 Level |
| Avoidability Boost | 70000034 | Avoidability +#er% |
| Boss Monster Damage Increase | 70000035 | Boss Damage: +#bdR% |
| Buff Duration Increase | 70000048 | Buff skill duration: +#bufftimeR% |
| Chance for no cooldown. | 70000045 | #nocoolProp% chance to skip cooldowns |
| Critical Rate Increase | 70000014 | Critical Rate: +#cr% |
| DEF to Damage Conversion | 70000041 | Final Damage: +#pdd2dam% of DEF |
| Defense Set Percentage Increase | 70000029, 70000030 | DEF: +#pddR% |
| DEX and INT Increase | 70000054 | DEX: +#dexFX, INT: +#intFX |
| DEX and LUK Increase | 70000055 | DEX: +#dexFX, LUK: +#lukFX |
| DEX and STR Increase | 70000057 | DEX: +#dexFX, STR: +#strFX |
| DEX Increase | 70000001 | DEX: +#dexFX |
| DEX to STR Conversion | 70000022 | #dex2str% of AP assigned to DEX added to STR |
| Increases Critical Rate | 70000043 | Critical Rate: +#cr% |
| Increases DEF | 70000006, 70000007 | Defense: +#pddX |
| Increases DEX | 70000018 | DEX: +#dexFX |
| Increases INT | 70000019 | INT: +#intFX |
| Increases Jump | 70000004 | Jump: +#psdJump |
| Increases LUK | 70000020 | LUK: +#lukFX |
| Increases Meso Drop | 70000033 | Mesos Obtained: +#mesoR% |
| Increases Speed | 70000005, 70000011 | Speed +#psdSpeed |
| Increases STR | 70000017 | STR: +#strFX |
| Instant-Kill on Normal Monsters in Azwan Liberation Supply Mode | 70000038 | #minionDeathProp% chance to instant-kill when attacking normal monsters in Azwan Liberation Supply Mode |
| INT and DEX Increase | 70000060 | INT: +#intFX, DEX: +#dexFX |
| INT and LUK Increase | 70000056 | INT: +#intFX, LUK: +#lukFX |
| INT and STR Increase | 70000058 | INT: +#intFX, STR: +#strFX |
| INT Increase | 70000002 | INT: +#intFX |
| INT to LUK Conversion | 70000023 | #int2luk% of AP assigned to INT added to LUK |
| Item Drop Rate Increase | 70000049 | Item Drop Rate +#dropR% |
| JUMP Increase | 70000010 | Jump +#psdJump |
| LUK and DEX Increase | 70000061 | LUK: +#lukFX, DEX: +#dexFX |
| LUK and INT Increase | 70000062 | LUK: +#lukFX, INT: +#intFX |
| LUK and STR Increase | 70000059 | LUK: +#lukFX, STR: +#strFX |
| LUK Increase | 70000003 | LUK: +#lukFX |
| LUK to DEX Conversion | 70000024 | #luk2dex% of AP assigned to LUK added to DEX |
| Magic Attack Increase | 70000013 | Magic Attack +#madX |
| Magic Attack Increase By Level | 70000026, 70000042 | Magic Attack +1 for every #lv2mad levels |
| Max HP Increase | 70000008 | Max HP: +#mhpX |
| Max HP Set Percentage Increase | 70000027, 70000031 | Max HP: +#mhpR% |
| Max MP Increase | 70000009 | Max MP: +#mmpX |
| Max MP Set Percentage Increase | 70000028, 70000032 | Max MP: +#mmpR% |
| Meso Drop Increase | 70000050 | Mesos Obtained: +#mesoR% |
| Normal Monster Damage Increase | 70000036 | +#nbdR% damage to normal monsters |
| Passive Skill Level +1 | 70000046 | Passive Skills: +#passivePlus Level (excludes Active Hybrids, 5th job skills, 6th job skills) |
| STR and DEX Increase | 70000051 | STR: +#strFX, DEX: +#dexFX |
| STR and INT Increase | 70000052 | STR: +#strFX, INT: +#intFX |
| STR and LUK Increase | 70000053 | STR: +#strFX, LUK: +#lukFX |
| STR Increase | 70000000 | STR: +#strFX |
| STR to DEX Conversion | 70000021 | #str2dex% of AP assigned to STR added to DEX |
| Tower Damage Increase | 70000037 | +#tdR% damage to towers |
The #placeholder in each effect is filled in by the game from the line's level; that per-level table is not readable from the client's data files, which is why it is shown raw.