Globalizes the attachment modifiers, implements volt gun and cleans up automaton lua. Co-Authored-By: WinterSolstice8 <60417494+WinterSolstice8@users.noreply.github.com>
612 lines
30 KiB
Lua
612 lines
30 KiB
Lua
-----------------------------------
|
|
-- Automaton Global
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
xi.automaton = xi.automaton or {}
|
|
|
|
xi.automaton.abilities =
|
|
{
|
|
CHIMERA_RIPPER = 1940,
|
|
STRING_CLIPPER = 1941,
|
|
ARCUBALLISTA = 1942,
|
|
SLAPSTICK = 1943,
|
|
SHIELD_BASH = 1944,
|
|
PROVOKE = 1945,
|
|
SHOCK_ABSORBER = 1946,
|
|
FLASHBULB = 1947,
|
|
MANA_CONVERTER = 1948,
|
|
RANGED_ATTACK = 1949,
|
|
ERASER = 2021,
|
|
REACTIVE_SHIELD = 2031,
|
|
CANNIBAL_BLADE = 2065,
|
|
DAZE = 2066,
|
|
KNOCKOUT = 2067,
|
|
ECONOMIZER = 2068,
|
|
REPLICATOR = 2132,
|
|
BONE_CRUSHER = 2299,
|
|
ARMOR_PIERCER = 2300,
|
|
MAGIC_MORTAR = 2301,
|
|
STRING_SHREDDER = 2743,
|
|
ARMOR_SHATTERER = 2744,
|
|
HEAT_CAPACITOR = 2745,
|
|
BARRAGE_TURBINE = 2746,
|
|
DISRUPTOR = 2747,
|
|
REGULATOR = 3485,
|
|
}
|
|
|
|
-- [FRAME][HEAD] = Model ID
|
|
local automatonModels =
|
|
{
|
|
[xi.automaton.frame.HARLEQUIN] =
|
|
{
|
|
[xi.automaton.head.HARLEQUIN ] = 0x07B9,
|
|
[xi.automaton.head.VALOREDGE ] = 0x07BA,
|
|
[xi.automaton.head.SHARPSHOT ] = 0x07BC,
|
|
[xi.automaton.head.STORMWAKER ] = 0x07BB,
|
|
[xi.automaton.head.SOULSOOTHER ] = 0x07D3,
|
|
[xi.automaton.head.SPIRITREAVER] = 0x07D7,
|
|
},
|
|
[xi.automaton.frame.VALOREDGE] =
|
|
{
|
|
[xi.automaton.head.HARLEQUIN ] = 0x07BE,
|
|
[xi.automaton.head.VALOREDGE ] = 0x07BF,
|
|
[xi.automaton.head.SHARPSHOT ] = 0x07C1,
|
|
[xi.automaton.head.STORMWAKER ] = 0x07C0,
|
|
[xi.automaton.head.SOULSOOTHER ] = 0x07D4,
|
|
[xi.automaton.head.SPIRITREAVER] = 0x07D8,
|
|
},
|
|
[xi.automaton.frame.SHARPSHOT] =
|
|
{
|
|
[xi.automaton.head.HARLEQUIN ] = 0x07C3,
|
|
[xi.automaton.head.VALOREDGE ] = 0x07C4,
|
|
[xi.automaton.head.SHARPSHOT ] = 0x07C6,
|
|
[xi.automaton.head.STORMWAKER ] = 0x07C5,
|
|
[xi.automaton.head.SOULSOOTHER ] = 0x07D5,
|
|
[xi.automaton.head.SPIRITREAVER] = 0x07D9,
|
|
},
|
|
[xi.automaton.frame.STORMWAKER] =
|
|
{
|
|
[xi.automaton.head.HARLEQUIN ] = 0x07C8,
|
|
[xi.automaton.head.VALOREDGE ] = 0x07C9,
|
|
[xi.automaton.head.SHARPSHOT ] = 0x07CB,
|
|
[xi.automaton.head.STORMWAKER ] = 0x07CA,
|
|
[xi.automaton.head.SOULSOOTHER ] = 0x07D6,
|
|
[xi.automaton.head.SPIRITREAVER] = 0x07DA,
|
|
},
|
|
}
|
|
|
|
local maneuverList =
|
|
{
|
|
['dark_maneuver' ] = { xi.effect.DARK_MANEUVER, xi.element.DARK, nil },
|
|
['earth_maneuver' ] = { xi.effect.EARTH_MANEUVER, xi.element.EARTH, xi.mod.VIT },
|
|
['fire_maneuver' ] = { xi.effect.FIRE_MANEUVER, xi.element.FIRE, xi.mod.STR },
|
|
['ice_maneuver' ] = { xi.effect.ICE_MANEUVER, xi.element.ICE, xi.mod.INT },
|
|
['light_maneuver' ] = { xi.effect.LIGHT_MANEUVER, xi.element.LIGHT, xi.mod.CHR },
|
|
['thunder_maneuver'] = { xi.effect.THUNDER_MANEUVER, xi.element.THUNDER, xi.mod.DEX },
|
|
['water_maneuver' ] = { xi.effect.WATER_MANEUVER, xi.element.WATER, xi.mod.MND },
|
|
['wind_maneuver' ] = { xi.effect.WIND_MANEUVER, xi.element.WIND, xi.mod.AGI },
|
|
}
|
|
|
|
-- Table of attachment modifiers.
|
|
-- Modifiers are keyed by number of maneuvers active. 0 / 1 / 2 / 3
|
|
-- Regen & Refresh values are calculated separately in their own function and left nil on purpose.
|
|
xi.automaton.attachmentModifiers =
|
|
{
|
|
-- Attachment Modifier Maneuver Values Optic Fiber Bonus
|
|
['accelerator' ] = { { xi.mod.EVA, { 5, 10, 15, 20 }, true }, },
|
|
['accelerator_ii' ] = { { xi.mod.EVA, { 10, 15, 20, 25 }, true }, },
|
|
['accelerator_iii' ] = { { xi.mod.EVA, { 20, 30, 40, 50 }, true }, },
|
|
['accelerator_iv' ] = { { xi.mod.EVA, { 30, 45, 60, 80 }, true }, },
|
|
['analyzer' ] = { { xi.mod.AUTO_ANALYZER, { 1, 2, 4, 6 }, false }, },
|
|
['amplifier' ] = { { xi.mod.MAGIC_BURST_BONUS_UNCAPPED, { 10, 20, 35, 50 }, true },
|
|
{ xi.mod.ELEMENTAL_CELERITY, { 25, 25, 25, 25 }, true }, },
|
|
['amplifier_ii' ] = { { xi.mod.MAGIC_BURST_BONUS_UNCAPPED, { 20, 30, 50, 70 }, true },
|
|
{ xi.mod.ELEMENTAL_CELERITY, { 25, 25, 25, 25 }, true }, },
|
|
['arcanic_cell' ] = { { xi.mod.OCCULT_ACUMEN, { 10, 20, 35, 50 }, true }, },
|
|
['arcanic_cell_ii' ] = { { xi.mod.OCCULT_ACUMEN, { 20, 40, 70, 100 }, true }, },
|
|
['arcanoclutch' ] = { { xi.mod.MAGIC_DAMAGE, { 20, 40, 60, 80 }, true }, },
|
|
['arcanoclutch_ii' ] = { { xi.mod.MAGIC_DAMAGE, { 40, 60, 80, 120 }, true }, },
|
|
['armor_plate' ] = { { xi.mod.DMGPHYS, { -500, -700, -1000, -1500 }, true }, },
|
|
['armor_plate_ii' ] = { { xi.mod.DMGPHYS, { -1000, -1500, -2000, -2500 }, true }, },
|
|
['armor_plate_iii' ] = { { xi.mod.DMGPHYS, { -1500, -2000, -2500, -3000 }, true }, },
|
|
['armor_plate_iv' ] = { { xi.mod.DMGPHYS, { -2000, -2500, -3000, -4000 }, true }, },
|
|
['auto-repair_kit' ] = { { xi.mod.REGEN, { nil, nil, nil, nil }, true }, },
|
|
['auto-repair_kit_ii' ] = { { xi.mod.REGEN, { nil, nil, nil, nil }, true }, },
|
|
['auto-repair_kit_iii'] = { { xi.mod.REGEN, { nil, nil, nil, nil }, true }, },
|
|
['auto-repair_kit_iv' ] = { { xi.mod.REGEN, { nil, nil, nil, nil }, true }, },
|
|
['barrier_module' ] = { { xi.mod.SHIELDBLOCKRATE, { 0, 5, 10, 15 }, true },
|
|
{ xi.mod.AUTO_SHIELD_BASH_DELAY, { 0, 5, 10, 15 }, false }, },
|
|
['barrier_module_ii' ] = { { xi.mod.SHIELDBLOCKRATE, { 0, 10, 20, 30 }, true },
|
|
{ xi.mod.AUTO_SHIELD_BASH_DELAY, { 0, 5, 10, 15 }, false }, },
|
|
['coiler' ] = { { xi.mod.DOUBLE_ATTACK, { 3, 10, 20, 30 }, true }, },
|
|
['coiler_ii' ] = { { xi.mod.DOUBLE_ATTACK, { 10, 15, 25, 35 }, true }, },
|
|
['damage_gauge' ] = { { xi.mod.AUTO_HEALING_THRESHOLD, { 50, 60, 70, 80 }, false },
|
|
{ xi.mod.AUTO_HEALING_DELAY, { 3, 3, 3, 3 }, false }, },
|
|
['damage_gauge_ii' ] = { { xi.mod.AUTO_HEALING_THRESHOLD, { 60, 70, 80, 90 }, false },
|
|
{ xi.mod.AUTO_HEALING_DELAY, { 3, 3, 3, 3 }, false }, },
|
|
['drum_magazine' ] = { { xi.mod.AUTO_RANGED_DELAY, { 3, 6, 9, 15 }, true }, },
|
|
['dynamo' ] = { { xi.mod.CRITHITRATE, { 3, 5, 7, 9 }, true }, },
|
|
['dynamo_ii' ] = { { xi.mod.CRITHITRATE, { 5, 10, 15, 20 }, true }, },
|
|
['dynamo_iii' ] = { { xi.mod.CRITHITRATE, { 10, 15, 25, 35 }, true }, },
|
|
['flame_holder' ] = { { xi.mod.WEAPONSKILL_DAMAGE_BASE, { 125, 200, 275, 350 }, true }, },
|
|
['equalizer' ] = { { xi.mod.AUTO_EQUALIZER, { 10, 25, 50, 75 }, true }, },
|
|
['galvanizer' ] = { { xi.mod.COUNTER, { 10, 20, 35, 50 }, true }, },
|
|
['hammermill' ] = { { xi.mod.SHIELD_BASH, { 15, 25, 50, 100 }, true },
|
|
{ xi.mod.AUTO_SHIELD_BASH_SLOW, { 0, 12, 19, 25 }, true }, },
|
|
['heatsink' ] = { { xi.mod.BURDEN_DECAY, { 2, 4, 5, 6 }, false }, },
|
|
['ice_maker' ] = { { xi.mod.AUTO_MAB_COEFFICIENT, { 0, 50, 75, 100 }, true }, },
|
|
['inhibitor' ] = { { xi.mod.STORETP, { 5, 15, 25, 40 }, true }, },
|
|
['inhibitor_ii' ] = { { xi.mod.STORETP, { 10, 25, 40, 65 }, true }, },
|
|
['loudspeaker' ] = { { xi.mod.MATT, { 5, 10, 15, 20 }, true }, },
|
|
['loudspeaker_ii' ] = { { xi.mod.MATT, { 10, 15, 20, 25 }, true }, },
|
|
['loudspeaker_iii' ] = { { xi.mod.MATT, { 20, 30, 40 , 50 }, true }, },
|
|
['loudspeaker_iv' ] = { { xi.mod.MATT, { 30, 40, 50, 60 }, true }, },
|
|
['loudspeaker_v' ] = { { xi.mod.MATT, { 40, 50, 60, 70 }, true }, },
|
|
['magniplug' ] = { { xi.mod.MAIN_DMG_RATING, { 5, 15, 30, 45 }, true },
|
|
{ xi.mod.RANGED_DMG_RATING, { 5, 15, 30, 45 }, true }, },
|
|
['magniplug_ii' ] = { { xi.mod.MAIN_DMG_RATING, { 10, 20, 35, 50 }, true },
|
|
{ xi.mod.RANGED_DMG_RATING, { 10, 20, 35, 50 }, true }, },
|
|
['mana_booster' ] = { { xi.mod.FASTCAST, { 20, 30, 45, 60 }, false }, },
|
|
['mana_channeler' ] = { { xi.mod.MATT, { 10, 15, 25, 35 }, true },
|
|
{ xi.mod.AUTO_MAGIC_COOLDOWN, { 3, 6, 9, 12 }, true }, },
|
|
['mana_channeler_ii' ] = { { xi.mod.MATT, { 20, 30, 40, 50 }, true },
|
|
{ xi.mod.AUTO_MAGIC_COOLDOWN, { 6, 12, 18, 24 }, true }, },
|
|
['mana_conserver' ] = { { xi.mod.CONSERVE_MP, { 15, 30, 45, 60 }, true }, },
|
|
['mana_jammer' ] = { { xi.mod.MDEF, { 10, 20, 30, 40 }, true }, },
|
|
['mana_jammer_ii' ] = { { xi.mod.MDEF, { 20, 30, 40, 50 }, true }, },
|
|
['mana_jammer_iii' ] = { { xi.mod.MDEF, { 30, 40, 50, 60 }, true }, },
|
|
['mana_jammer_iv' ] = { { xi.mod.MDEF, { 40, 50, 60, 70 }, true }, },
|
|
['mana_tank' ] = { { xi.mod.REFRESH, { nil, nil, nil, nil }, true }, },
|
|
['mana_tank_ii' ] = { { xi.mod.REFRESH, { nil, nil, nil, nil }, true }, },
|
|
['mana_tank_iii' ] = { { xi.mod.REFRESH, { nil, nil, nil, nil }, true }, },
|
|
['mana_tank_iv' ] = { { xi.mod.REFRESH, { nil, nil, nil, nil }, true }, },
|
|
['optic_fiber' ] = { { xi.mod.AUTO_PERFORMANCE_BOOST, { 10, 20, 25, 30 }, false }, },
|
|
['optic_fiber_ii' ] = { { xi.mod.AUTO_PERFORMANCE_BOOST, { 15, 30, 37, 45 }, false }, },
|
|
['percolator' ] = { { xi.mod.COMBAT_SKILLUP_RATE, { 5, 10, 15, 20 }, true }, },
|
|
['power_cooler' ] = { { xi.mod.MP_COST_REDUCTION, { 10, 20, 35, 50 }, true }, },
|
|
['repeater' ] = { { xi.mod.DOUBLE_SHOT_RATE, { 10, 15, 35, 65 }, true }, },
|
|
['resister' ] = { { xi.mod.STATUSRES, { 5, 10, 20, 30 }, true }, },
|
|
['resister_ii' ] = { { xi.mod.STATUSRES, { 10, 20, 40, 60 }, true }, },
|
|
['scanner' ] = { { xi.mod.AUTO_SCAN_RESISTS, { 0, 1, 1, 1 }, false }, },
|
|
['schurzen' ] = { { xi.mod.AUTO_SCHURZEN, { 0, 1, 1, 1 }, false }, },
|
|
['scope' ] = { { xi.mod.RACC, { 10, 20, 30, 40 }, true }, },
|
|
['scope_ii' ] = { { xi.mod.RACC, { 20, 30, 40, 50 }, true }, },
|
|
['scope_iii' ] = { { xi.mod.RACC, { 30, 40, 55, 70 }, true }, },
|
|
['scope_iv' ] = { { xi.mod.RACC, { 40, 50, 65, 80 }, true }, },
|
|
['speedloader' ] = { { xi.mod.SKILLCHAINBONUS, { 20, 30, 40, 60 }, true }, },
|
|
['speedloader_ii' ] = { { xi.mod.SKILLCHAINBONUS, { 35, 45, 60, 80 }, true }, },
|
|
['smoke_screen' ] = { { xi.mod.EVA, { 20, 40, 80, 160 }, true },
|
|
{ xi.mod.ACC, { -20, -40, -80, -160 }, true },
|
|
{ xi.mod.RACC, { -20, -40, -80, -160 }, true }, },
|
|
['stabilizer' ] = { { xi.mod.ACC, { 5, 10, 15, 20 }, true }, },
|
|
['stabilizer_ii' ] = { { xi.mod.ACC, { 10, 15, 20, 25 }, true }, },
|
|
['stabilizer_iii' ] = { { xi.mod.ACC, { 20, 30, 40, 50 }, true }, },
|
|
['stabilizer_iv' ] = { { xi.mod.ACC, { 30, 40, 55, 70 }, true }, },
|
|
['stabilizer_v' ] = { { xi.mod.ACC, { 40, 50, 65, 80 }, true }, },
|
|
['stealth_screen' ] = { { xi.mod.ENMITY, { -10, -20, -30, -40 }, true }, },
|
|
['stealth_screen_ii' ] = { { xi.mod.ENMITY, { -15, -25, -35, -45 }, true }, },
|
|
['steam_jacket' ] = { { xi.mod.AUTO_STEAM_JACKET_REDUCTION, { 30, 45, 60, 80 }, true }, },
|
|
['strobe' ] = { { xi.mod.ENMITY, { 10, 25, 40, 60 }, true }, },
|
|
['strobe_ii' ] = { { xi.mod.ENMITY, { 20, 40, 65, 100 }, true }, },
|
|
['tactical_processor' ] = { { xi.mod.AUTO_DECISION_DELAY, { 50, 70, 85, 115 }, false }, },
|
|
['tension_spring' ] = { { xi.mod.ATTP, { 3, 6, 9, 12 }, true },
|
|
{ xi.mod.RATTP, { 3, 6, 9, 12 }, true }, },
|
|
['tension_spring_ii' ] = { { xi.mod.ATTP, { 6, 9, 12, 15 }, true },
|
|
{ xi.mod.RATTP, { 6, 9, 12, 15 }, true }, },
|
|
['tension_spring_iii' ] = { { xi.mod.ATTP, { 12, 15, 18, 21 }, true },
|
|
{ xi.mod.RATTP, { 12, 15, 18, 21 }, true }, },
|
|
['tension_spring_iv' ] = { { xi.mod.ATTP, { 15, 18, 21, 24 }, true },
|
|
{ xi.mod.RATTP, { 15, 18, 21, 24 }, true }, },
|
|
['tension_spring_v' ] = { { xi.mod.ATTP, { 18, 21, 24, 27 }, true },
|
|
{ xi.mod.RATTP, { 18, 21, 24, 27 }, true }, },
|
|
['tranquilizer' ] = { { xi.mod.MACC, { 10, 30, 40, 50 }, true }, },
|
|
['tranquilizer_ii' ] = { { xi.mod.MACC, { 20, 40, 55, 70 }, true }, },
|
|
['tranquilizer_iii' ] = { { xi.mod.MACC, { 30, 50, 70, 80 }, true }, },
|
|
['tranquilizer_iv' ] = { { xi.mod.MACC, { 40, 60, 80, 110 }, true }, },
|
|
['truesights' ] = { { xi.mod.AUTO_RANGED_DAMAGEP, { 5, 15, 30, 45 }, true }, },
|
|
['turbo_charger' ] = { { xi.mod.HASTE_MAGIC, { 500, 1500, 2000, 2500 }, true }, },
|
|
['turbo_charger_ii' ] = { { xi.mod.HASTE_MAGIC, { 700, 1700, 2800, 4375 }, true }, },
|
|
['vivi-valve' ] = { { xi.mod.CURE_POTENCY, { 5, 15, 30, 45 }, true }, },
|
|
['vivi-valve_ii' ] = { { xi.mod.CURE_POTENCY, { 10, 20, 35, 50 }, true }, },
|
|
['volt_gun' ] = { { xi.mod.VOLT_GUN_POTENCY, { 0, 20, 40, 100 }, false }, },
|
|
}
|
|
|
|
-----------------------------------
|
|
-- Attuner
|
|
-----------------------------------
|
|
---@param actor CBaseEntity
|
|
---@param target CBaseEntity
|
|
---@return number
|
|
xi.automaton.handleAttuner = function(actor, target)
|
|
if not actor:isAutomaton() then
|
|
return 0
|
|
end
|
|
|
|
if not actor:hasAttachmentSet(xi.item.ATTUNER_ATTACHMENT) then
|
|
return 0
|
|
end
|
|
|
|
if actor:getMainLvl() >= target:getMainLvl() then
|
|
return 0
|
|
end
|
|
|
|
local master = actor:getMaster()
|
|
if not master then
|
|
return 0
|
|
end
|
|
|
|
-- We have reason to believe BG wiki is wrong about the attuner values
|
|
-- so we are using these for the moment; JP wiki and dev posts imply that it's not as simple as level + 1 and higher gets massive def ignore.
|
|
local numFireManeuvers = math.min(master:countEffect(xi.effect.FIRE_MANEUVER), 3)
|
|
|
|
if numFireManeuvers > 0 and master:hasStatusEffect(xi.effect.OVERDRIVE) then
|
|
numFireManeuvers = 3
|
|
end
|
|
|
|
local attunerEffect =
|
|
{
|
|
[0] = 0.05,
|
|
[1] = 0.10,
|
|
[2] = 0.15,
|
|
[3] = 0.20
|
|
}
|
|
|
|
return attunerEffect[numFireManeuvers] or 0
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Auto Repair Kit
|
|
-----------------------------------
|
|
-- Repair Kit HP boost is calculated per frame using the attachment's hpBoost and the frame divisor.
|
|
-- Example: Repair Kit IV on Harlequin is 4 / 20 = 0.2, or a 20% HP boost.
|
|
xi.automaton.repairKit =
|
|
{
|
|
frameDivisors =
|
|
{
|
|
[xi.automaton.frame.HARLEQUIN ] = 20,
|
|
[xi.automaton.frame.VALOREDGE ] = 24,
|
|
[xi.automaton.frame.SHARPSHOT ] = 18,
|
|
[xi.automaton.frame.STORMWAKER] = 16,
|
|
},
|
|
|
|
data =
|
|
{
|
|
['auto-repair_kit' ] = { id = 193, hpBoost = 1, regenBase = { 0, 1, 2, 3 }, regenMultiplier = { 0, 0.125, 0.225, 0.375 } },
|
|
['auto-repair_kit_ii' ] = { id = 196, hpBoost = 2, regenBase = { 0, 3, 6, 9 }, regenMultiplier = { 0, 0.600, 1.200, 1.800 } },
|
|
['auto-repair_kit_iii'] = { id = 202, hpBoost = 3, regenBase = { 0, 9, 12, 15 }, regenMultiplier = { 0, 1.800, 2.400, 3.000 } },
|
|
['auto-repair_kit_iv' ] = { id = 205, hpBoost = 4, regenBase = { 0, 15, 18, 21 }, regenMultiplier = { 0, 3.000, 3.600, 4.200 } },
|
|
},
|
|
}
|
|
|
|
local function getRegenModValue(pet, attachment, numManeuvers)
|
|
local repairKitData = xi.automaton.repairKit.data[attachment:getName()]
|
|
if not repairKitData then
|
|
return 0
|
|
end
|
|
|
|
return repairKitData.regenBase[numManeuvers + 1] + pet:getMaxHP() * repairKitData.regenMultiplier[numManeuvers + 1] / 100
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Coiler
|
|
-----------------------------------
|
|
-- Returns the number of extra hits granted by the DOUBLE_ATTACK modifier based on the base number of hits.
|
|
xi.automaton.getExtraHits = function(automaton, numHits)
|
|
local doubleAttackRate = utils.clamp(automaton:getMod(xi.mod.DOUBLE_ATTACK), 0, 100)
|
|
local extraHits = 0
|
|
if doubleAttackRate > 0 then
|
|
for _ = 1, numHits do
|
|
if math.random(1, 100) <= doubleAttackRate then
|
|
extraHits = extraHits + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
return extraHits
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Equalizer
|
|
-----------------------------------
|
|
---@param actor CBaseEntity
|
|
---@param damage integer
|
|
---@return integer
|
|
xi.automaton.handleEqualizer = function(actor, damage)
|
|
local equalizerModifier = actor:getMod(xi.mod.AUTO_EQUALIZER)
|
|
local maxHP = actor:getMaxHP()
|
|
|
|
-- No Equalizer Equipped, return unmodified damage.
|
|
if equalizerModifier == 0 then
|
|
return damage
|
|
end
|
|
|
|
-- No Damage to reduce, return unmodified damage.
|
|
if damage <= 0 then
|
|
return damage
|
|
end
|
|
|
|
-- Equalizer damage reduction becomes more effective the higher the damage is in relation to the automatons max HP.
|
|
local reductionRate = (damage / maxHP) * (equalizerModifier / 100)
|
|
|
|
reductionRate = math.floor(reductionRate * 100) / 100
|
|
|
|
-- Damage reduction is capped at 90%.
|
|
reductionRate = math.min(reductionRate, 0.90)
|
|
|
|
return math.floor(damage * (1 - reductionRate))
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Flame Holder
|
|
-----------------------------------
|
|
-- Applies the FTP multiplier for an Automaton Weapon Skill, factoring in the WEAPONSKILL_DAMAGE_BASE modifier from Flame Holder.
|
|
xi.automaton.applyFlameHolder = function(automaton, ftp)
|
|
local flameHolderFTP = automaton:getMod(xi.mod.WEAPONSKILL_DAMAGE_BASE) / 100
|
|
if flameHolderFTP > 0 then
|
|
ftp[1] = ftp[1] * flameHolderFTP
|
|
ftp[2] = ftp[2] * flameHolderFTP
|
|
ftp[3] = ftp[3] * flameHolderFTP
|
|
end
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Mana Tank
|
|
-----------------------------------
|
|
-- Mana Tank MP boost is calculated per frame using the attachment's mpBoost and the frame divisor.
|
|
-- Example: Mana Tank IV on Harlequin is 4 / 20 = 0.2, or a 20% MP boost.
|
|
xi.automaton.manaTank =
|
|
{
|
|
frameDivisors =
|
|
{
|
|
[xi.automaton.frame.HARLEQUIN ] = 20,
|
|
[xi.automaton.frame.STORMWAKER] = 24,
|
|
},
|
|
|
|
data =
|
|
{
|
|
['mana_tank' ] = { id = 225, mpBoost = 1, refreshBase = { 0, 1, 2, 3 }, refreshMultiplier = { 0, 0.2, 0.4, 0.6 } },
|
|
['mana_tank_ii' ] = { id = 228, mpBoost = 2, refreshBase = { 0, 2, 3, 4 }, refreshMultiplier = { 0, 0.4, 0.6, 0.8 } },
|
|
['mana_tank_iii'] = { id = 233, mpBoost = 3, refreshBase = { 0, 3, 4, 5 }, refreshMultiplier = { 0, 0.6, 0.8, 1.0 } },
|
|
['mana_tank_iv' ] = { id = 235, mpBoost = 4, refreshBase = { 0, 4, 5, 6 }, refreshMultiplier = { 0, 0.8, 1.0, 1.2 } },
|
|
},
|
|
}
|
|
|
|
local function getRefreshModValue(pet, attachment, numManeuvers)
|
|
local manaTankData = xi.automaton.manaTank.data[attachment:getName()]
|
|
if not manaTankData then
|
|
return 0
|
|
end
|
|
|
|
return manaTankData.refreshBase[numManeuvers + 1] + pet:getMaxMP() * manaTankData.refreshMultiplier[numManeuvers + 1] / 100
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Optic Fiber
|
|
-----------------------------------
|
|
local function isOpticFiber(attachmentName)
|
|
if string.find(attachmentName, 'optic_fiber') ~= nil then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Due to load order, we can't expect to determine Optic Fiber enhancements on change.
|
|
-- For maneuvers, calculate this based on the number of light maneuvers that are active.
|
|
local function calculatePerformanceBoost(pet)
|
|
local master = pet:getMaster()
|
|
local performanceBoost = 0
|
|
|
|
local numLightManeuvers = master and master:countEffect(xi.effect.LIGHT_MANEUVER) or 0
|
|
for _, attachmentName in pairs(pet:getAttachments()) do
|
|
if
|
|
isOpticFiber(attachmentName) and
|
|
xi.automaton.attachmentModifiers[attachmentName]
|
|
then
|
|
performanceBoost = performanceBoost + xi.automaton.attachmentModifiers[attachmentName][1][2][numLightManeuvers + 1]
|
|
end
|
|
end
|
|
|
|
return performanceBoost
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Truesights
|
|
-----------------------------------
|
|
-- Return the base damage of an Automaton Ranged Attack, factoring in the AUTO_RANGED_DAMAGEP modifier.
|
|
xi.automaton.getRangedBaseDamage = function(automaton)
|
|
return automaton:getRangedDmg() * (1 + automaton:getMod(xi.mod.AUTO_RANGED_DAMAGEP) / 100)
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Volt Gun
|
|
-----------------------------------
|
|
-- Calculates the potency of Volt Gun AE. TODO: Additional dINT testing.
|
|
xi.automaton.calculateVoltGunPotency = function(automaton, target)
|
|
local voltGunModifier = automaton:getMod(xi.mod.VOLT_GUN_POTENCY)
|
|
local skillLevel = math.max(automaton:getSkillLevel(xi.skill.AUTOMATON_MELEE), automaton:getSkillLevel(xi.skill.AUTOMATON_RANGED), automaton:getSkillLevel(xi.skill.AUTOMATON_MAGIC))
|
|
local basePotency = math.floor(skillLevel / 16)
|
|
local dINT = automaton:getStat(xi.mod.INT) - target:getStat(xi.mod.INT)
|
|
local power = math.min(math.max(math.floor(dINT / 2), -10), 30)
|
|
|
|
return math.max(math.floor((basePotency + power) * (1 + voltGunModifier / 100)), 1)
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Animator Check
|
|
-----------------------------------
|
|
local function hasAnimatorEquipped(player)
|
|
if
|
|
player:getWeaponSubSkillType(xi.slot.RANGED) == 10 or
|
|
player:getWeaponSubSkillType(xi.slot.RANGED) == 11
|
|
then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Global functions to handle attachment modifiers.
|
|
-- NOTE: Core is 0-indexed for maneuvers, yet the table above is 1-indexed.
|
|
-- Maneuvers are updated in core before the appropriate function is called in Lua. This is why some of the functions below have offsets applied.
|
|
-----------------------------------
|
|
xi.automaton.onAttachmentEquip = function(pet, attachment)
|
|
xi.automaton.updateAttachmentModifier(pet, attachment, 0)
|
|
end
|
|
|
|
xi.automaton.onAttachmentUnequip = function(pet, attachment)
|
|
local attachmentName = attachment:getName()
|
|
local modTable = xi.automaton.attachmentModifiers[attachmentName]
|
|
|
|
for k, modList in ipairs(modTable) do
|
|
local previousMod = pet:getLocalVar(attachmentName .. k)
|
|
|
|
if modList[2][4] and modList[2][4] < 0 then
|
|
previousMod = previousMod * -1
|
|
end
|
|
|
|
if previousMod ~= 0 then
|
|
pet:delMod(modList[1], previousMod)
|
|
end
|
|
end
|
|
|
|
pet:clearLocalVarsWithPrefix(attachmentName)
|
|
end
|
|
|
|
xi.automaton.updateAttachmentModifier = function(pet, attachment, maneuvers)
|
|
local attachmentName = attachment:getName()
|
|
local modTable = xi.automaton.attachmentModifiers[attachmentName]
|
|
|
|
for attachmentModPos, modList in ipairs(modTable) do
|
|
local previousMod = pet:getLocalVar(attachmentName .. attachmentModPos)
|
|
local modValue = 0
|
|
|
|
-- Local Vars are uint. In all cases, a negative modifier carries for all number of maneuvers.
|
|
-- If the last entry is negative, restore this as a negative value.
|
|
if modList[2][4] and modList[2][4] < 0 then
|
|
previousMod = previousMod * -1
|
|
end
|
|
|
|
if modList[1] == xi.mod.REGEN then
|
|
modValue = getRegenModValue(pet, attachment, maneuvers)
|
|
elseif modList[1] == xi.mod.REFRESH then
|
|
modValue = getRefreshModValue(pet, attachment, maneuvers)
|
|
else
|
|
modValue = modList[2][maneuvers + 1]
|
|
end
|
|
|
|
if modList[3] then
|
|
modValue = math.floor(modValue * (1 + calculatePerformanceBoost(pet) / 100))
|
|
end
|
|
|
|
if modValue ~= previousMod then
|
|
if previousMod ~= 0 then
|
|
-- If the automaton was reset to a blank state (LEVEL_RESTRICTION) and the local variables were not cleared, this will under/overflow.
|
|
pet:delMod(modList[1], previousMod)
|
|
end
|
|
|
|
pet:addMod(modList[1], modValue)
|
|
pet:setLocalVar(attachmentName .. attachmentModPos, math.abs(modValue))
|
|
|
|
local master = pet:getMaster()
|
|
if master and isOpticFiber(attachmentName) then
|
|
master:updateAttachments()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Burden Calculation
|
|
-----------------------------------
|
|
local function getAddBurdenValue(player, maneuverInfo)
|
|
local compareMod = maneuverInfo[3]
|
|
|
|
if not compareMod then
|
|
return player:getMP() < player:getPet():getMP() and 15 or 10
|
|
else
|
|
return player:getStat(compareMod) < player:getPet():getStat(compareMod) and 20 or 15
|
|
end
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Global functions to handle maneuvers
|
|
-----------------------------------
|
|
xi.automaton.onManeuverGain = function(pet, attachment, maneuvers)
|
|
xi.automaton.updateAttachmentModifier(pet, attachment, maneuvers)
|
|
end
|
|
|
|
xi.automaton.onManeuverLose = function(pet, attachment, maneuvers)
|
|
xi.automaton.updateAttachmentModifier(pet, attachment, maneuvers)
|
|
end
|
|
|
|
xi.automaton.onManeuverCheck = function(player, target, ability)
|
|
if
|
|
not player:hasStatusEffect(xi.effect.OVERLOAD) and
|
|
player:getPet() and
|
|
hasAnimatorEquipped(player)
|
|
then
|
|
return 0, 0
|
|
else
|
|
return 71, 0
|
|
end
|
|
end
|
|
|
|
xi.automaton.onUseManeuver = function(player, target, ability, action)
|
|
local maneuverInfo = maneuverList[ability:getName()]
|
|
local element = maneuverInfo[2] - 1
|
|
local burdenValue = getAddBurdenValue(player, maneuverInfo)
|
|
local overload = target:addBurden(element, burdenValue)
|
|
|
|
if
|
|
overload ~= 0 and
|
|
(player:getMod(xi.mod.PREVENT_OVERLOAD) > 0 or player:getPet():getMod(xi.mod.PREVENT_OVERLOAD) > 0) and
|
|
player:delStatusEffectSilent(xi.effect.WATER_MANEUVER)
|
|
then
|
|
overload = 0
|
|
end
|
|
|
|
action:messageID(player:getID(), xi.msg.basic.AUTO_OVERLOAD_CHANCE)
|
|
|
|
if overload ~= 0 then
|
|
target:removeAllManeuvers()
|
|
target:addStatusEffect(xi.effect.OVERLOAD, { duration = overload, origin = player })
|
|
action:messageID(player:getID(), xi.msg.basic.AUTO_OVERLOADED)
|
|
else
|
|
local puppetmasterLevel = target:getMainJob() == xi.job.PUP and target:getMainLvl() or target:getSubLvl()
|
|
local maneuverBonus = 1 + (puppetmasterLevel / 15) + target:getMod(xi.mod.MANEUVER_BONUS)
|
|
|
|
if target:getActiveManeuverCount() == 3 then
|
|
target:removeOldestManeuver()
|
|
end
|
|
|
|
local maneuverDuration = player:getPet():getLocalVar('MANEUVER_DURATION')
|
|
|
|
target:addStatusEffect(maneuverInfo[1], { power = maneuverBonus, duration = utils.clamp(maneuverDuration, 60, 300), origin = player })
|
|
end
|
|
|
|
return target:getOverloadChance(element)
|
|
end
|
|
|
|
-----------------------------------
|
|
---Retrieve model ID of the automaton for cutscene purposes
|
|
-----------------------------------
|
|
---@param player CBaseEntity
|
|
---@return integer
|
|
xi.automaton.getModelId = function(player)
|
|
local frame = player:getAutomatonFrame()
|
|
local head = player:getAutomatonHead()
|
|
local defaultModelId = automatonModels[xi.automaton.frame.HARLEQUIN][xi.automaton.head.HARLEQUIN]
|
|
|
|
if not frame or not head then
|
|
return defaultModelId
|
|
end
|
|
|
|
local frameTable = automatonModels[frame]
|
|
if not frameTable then
|
|
return defaultModelId
|
|
end
|
|
|
|
return frameTable[head] or defaultModelId
|
|
end
|