Fixes mods on the tree that referred to 'curses' not correctly applying to hexs and marks Fix Projectile weakness mods on spectres Fix +1 curse gems from Awakened Blasphemy
686 lines
25 KiB
Lua
686 lines
25 KiB
Lua
-- Path of Building
|
|
--
|
|
-- Module: Calc Active Skill
|
|
-- Active skill setup.
|
|
--
|
|
local calcs = ...
|
|
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local t_insert = table.insert
|
|
local t_remove = table.remove
|
|
local m_floor = math.floor
|
|
local m_min = math.min
|
|
local m_max = math.max
|
|
local bor = bit.bor
|
|
local band = bit.band
|
|
local bnot = bit.bnot
|
|
|
|
-- Merge level modifier with given mod list
|
|
local mergeLevelCache = { }
|
|
local function mergeLevelMod(modList, mod, value)
|
|
if not value then
|
|
modList:AddMod(mod)
|
|
return
|
|
end
|
|
if not mergeLevelCache[mod] then
|
|
mergeLevelCache[mod] = { }
|
|
end
|
|
if mergeLevelCache[mod][value] then
|
|
modList:AddMod(mergeLevelCache[mod][value])
|
|
elseif value then
|
|
local newMod = copyTable(mod, true)
|
|
if type(newMod.value) == "table" then
|
|
newMod.value = copyTable(newMod.value, true)
|
|
if newMod.value.mod then
|
|
newMod.value.mod = copyTable(newMod.value.mod, true)
|
|
newMod.value.mod.value = value
|
|
else
|
|
newMod.value.value = value
|
|
end
|
|
else
|
|
newMod.value = value
|
|
end
|
|
mergeLevelCache[mod][value] = newMod
|
|
modList:AddMod(newMod)
|
|
else
|
|
modList:AddMod(mod)
|
|
end
|
|
end
|
|
|
|
-- Merge skill modifiers with given mod list
|
|
function calcs.mergeSkillInstanceMods(env, modList, skillEffect, extraStats)
|
|
calcLib.validateGemLevel(skillEffect)
|
|
local grantedEffect = skillEffect.grantedEffect
|
|
local stats = calcLib.buildSkillInstanceStats(skillEffect, grantedEffect)
|
|
if extraStats and extraStats[1] then
|
|
for _, stat in pairs(extraStats) do
|
|
stats[stat.key] = (stats[stat.key] or 0) + stat.value
|
|
end
|
|
end
|
|
for stat, statValue in pairs(stats) do
|
|
local map = grantedEffect.statMap[stat]
|
|
if map then
|
|
for _, mod in ipairs(map) do
|
|
mergeLevelMod(modList, mod, map.value or statValue * (map.mult or 1) / (map.div or 1))
|
|
end
|
|
end
|
|
end
|
|
modList:AddList(grantedEffect.baseMods)
|
|
end
|
|
|
|
-- Create an active skill using the given active gem and list of support gems
|
|
-- It will determine the base flag set, and check which of the support gems can support this skill
|
|
function calcs.createActiveSkill(activeEffect, supportList, actor, socketGroup, summonSkill)
|
|
local activeSkill = {
|
|
activeEffect = activeEffect,
|
|
supportList = supportList,
|
|
actor = actor,
|
|
summonSkill = summonSkill,
|
|
socketGroup = socketGroup,
|
|
skillData = { },
|
|
buffList = { },
|
|
}
|
|
|
|
local activeGrantedEffect = activeEffect.grantedEffect
|
|
|
|
-- Initialise skill types
|
|
activeSkill.skillTypes = copyTable(activeGrantedEffect.skillTypes)
|
|
if activeGrantedEffect.minionSkillTypes then
|
|
activeSkill.minionSkillTypes = copyTable(activeGrantedEffect.minionSkillTypes)
|
|
end
|
|
|
|
-- Initialise skill flag set ('attack', 'projectile', etc)
|
|
local skillFlags = copyTable(activeGrantedEffect.baseFlags)
|
|
activeSkill.skillFlags = skillFlags
|
|
skillFlags.hit = skillFlags.hit or activeSkill.skillTypes[SkillType.Attack] or activeSkill.skillTypes[SkillType.Hit] or activeSkill.skillTypes[SkillType.Projectile]
|
|
|
|
-- Process support skills
|
|
activeSkill.effectList = { activeEffect }
|
|
for _, supportEffect in ipairs(supportList) do
|
|
-- Pass 1: Add skill types from compatible supports
|
|
if calcLib.canGrantedEffectSupportActiveSkill(supportEffect.grantedEffect, activeSkill) then
|
|
for _, skillType in pairs(supportEffect.grantedEffect.addSkillTypes) do
|
|
activeSkill.skillTypes[skillType] = true
|
|
end
|
|
end
|
|
end
|
|
for _, supportEffect in ipairs(supportList) do
|
|
-- Pass 2: Add all compatible supports
|
|
if calcLib.canGrantedEffectSupportActiveSkill(supportEffect.grantedEffect, activeSkill) then
|
|
t_insert(activeSkill.effectList, supportEffect)
|
|
if supportEffect.isSupporting and activeEffect.srcInstance then
|
|
supportEffect.isSupporting[activeEffect.srcInstance] = true
|
|
end
|
|
if supportEffect.grantedEffect.addFlags and not summonSkill then
|
|
-- Support skill adds flags to supported skills (eg. Remote Mine adds 'mine')
|
|
for k in pairs(supportEffect.grantedEffect.addFlags) do
|
|
skillFlags[k] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return activeSkill
|
|
end
|
|
|
|
-- Get weapon flags and info for given weapon
|
|
local function getWeaponFlags(env, weaponData, weaponTypes)
|
|
local info = env.data.weaponTypeInfo[weaponData.type]
|
|
if not info then
|
|
return
|
|
end
|
|
if weaponTypes and not weaponTypes[weaponData.type] and
|
|
(not weaponData.countsAsAll1H or not (weaponTypes["Claw"] or weaponTypes["Dagger"] or weaponTypes["One Handed Axe"] or weaponTypes["One Handed Mace"] or weaponTypes["One Handed Sword"])) then
|
|
return nil, info
|
|
end
|
|
local flags = ModFlag[info.flag]
|
|
if weaponData.countsAsAll1H then
|
|
flags = bor(ModFlag.Axe, ModFlag.Claw, ModFlag.Dagger, ModFlag.Mace, ModFlag.Sword)
|
|
end
|
|
if weaponData.type ~= "None" then
|
|
flags = bor(flags, ModFlag.Weapon)
|
|
if info.oneHand then
|
|
flags = bor(flags, ModFlag.Weapon1H)
|
|
else
|
|
flags = bor(flags, ModFlag.Weapon2H)
|
|
end
|
|
if info.melee then
|
|
flags = bor(flags, ModFlag.WeaponMelee)
|
|
else
|
|
flags = bor(flags, ModFlag.WeaponRanged)
|
|
end
|
|
end
|
|
return flags, info
|
|
end
|
|
|
|
-- Build list of modifiers for given active skill
|
|
function calcs.buildActiveSkillModList(env, activeSkill)
|
|
local skillTypes = activeSkill.skillTypes
|
|
local skillFlags = activeSkill.skillFlags
|
|
local activeEffect = activeSkill.activeEffect
|
|
local activeGrantedEffect = activeEffect.grantedEffect
|
|
local effectiveRange = 0
|
|
|
|
-- Set mode flags
|
|
if env.mode_buffs then
|
|
skillFlags.buffs = true
|
|
end
|
|
if env.mode_combat then
|
|
skillFlags.combat = true
|
|
end
|
|
if env.mode_effective then
|
|
skillFlags.effective = true
|
|
end
|
|
|
|
-- Handle multipart skills
|
|
local activeGemParts = activeGrantedEffect.parts
|
|
if activeGemParts then
|
|
if env.mode == "CALCS" and activeSkill == env.player.mainSkill then
|
|
activeEffect.srcInstance.skillPartCalcs = m_min(#activeGemParts, activeEffect.srcInstance.skillPartCalcs or 1)
|
|
activeSkill.skillPart = activeEffect.srcInstance.skillPartCalcs
|
|
else
|
|
activeEffect.srcInstance.skillPart = m_min(#activeGemParts, activeEffect.srcInstance.skillPart or 1)
|
|
activeSkill.skillPart = activeEffect.srcInstance.skillPart
|
|
end
|
|
local part = activeGemParts[activeSkill.skillPart]
|
|
for k, v in pairs(part) do
|
|
if v == true then
|
|
skillFlags[k] = true
|
|
elseif v == false then
|
|
skillFlags[k] = nil
|
|
end
|
|
end
|
|
activeSkill.skillPartName = part.name
|
|
skillFlags.multiPart = #activeGemParts > 1
|
|
end
|
|
|
|
if (skillTypes[SkillType.Shield] or skillFlags.shieldAttack) and not activeSkill.summonSkill and (not activeSkill.actor.itemList["Weapon 2"] or activeSkill.actor.itemList["Weapon 2"].type ~= "Shield") then
|
|
-- Skill requires a shield to be equipped
|
|
skillFlags.disable = true
|
|
activeSkill.disableReason = "This skill requires a Shield"
|
|
end
|
|
|
|
if skillFlags.shieldAttack then
|
|
-- Special handling for Spectral Shield Throw
|
|
skillFlags.weapon2Attack = true
|
|
activeSkill.weapon2Flags = 0
|
|
elseif skillFlags.attack then
|
|
-- Set weapon flags
|
|
local weaponTypes = activeGrantedEffect.weaponTypes
|
|
local weapon1Flags, weapon1Info = getWeaponFlags(env, activeSkill.actor.weaponData1, weaponTypes)
|
|
if not weapon1Flags and activeSkill.summonSkill then
|
|
-- Minion skills seem to ignore weapon types
|
|
weapon1Flags, weapon1Info = ModFlag[env.data.weaponTypeInfo["None"].flag], env.data.weaponTypeInfo["None"]
|
|
end
|
|
if weapon1Flags then
|
|
activeSkill.weapon1Flags = weapon1Flags
|
|
skillFlags.weapon1Attack = true
|
|
if weapon1Info.melee and skillFlags.melee then
|
|
skillFlags.projectile = nil
|
|
elseif not weapon1Info.melee and skillFlags.projectile then
|
|
skillFlags.melee = nil
|
|
end
|
|
elseif skillTypes[SkillType.DualWield] or skillTypes[SkillType.MainHandOnly] or skillFlags.forceMainHand or (env.build.targetVersion ~= "2_6" and weapon1Info) then
|
|
-- Skill requires a compatible main hand weapon
|
|
skillFlags.disable = true
|
|
activeSkill.disableReason = "Main Hand weapon is not usable with this skill"
|
|
end
|
|
if not skillTypes[SkillType.MainHandOnly] and not skillFlags.forceMainHand then
|
|
local weapon2Flags, weapon2Info = getWeaponFlags(env, activeSkill.actor.weaponData2, weaponTypes)
|
|
if weapon2Flags then
|
|
activeSkill.weapon2Flags = weapon2Flags
|
|
skillFlags.weapon2Attack = true
|
|
elseif skillTypes[SkillType.DualWield] or (env.build.targetVersion ~= "2_6" and weapon2Info) then
|
|
-- Skill requires a compatible off hand weapon
|
|
skillFlags.disable = true
|
|
activeSkill.disableReason = activeSkill.disableReason or "Off Hand weapon is not usable with this skill"
|
|
elseif not skillFlags.weapon1Attack then
|
|
-- Neither weapon is compatible
|
|
skillFlags.disable = true
|
|
activeSkill.disableReason = "No usable weapon equipped"
|
|
end
|
|
end
|
|
skillFlags.bothWeaponAttack = skillFlags.weapon1Attack and skillFlags.weapon2Attack
|
|
end
|
|
|
|
-- Build skill mod flag set
|
|
local skillModFlags = 0
|
|
if skillFlags.hit then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Hit)
|
|
end
|
|
if skillFlags.attack then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Attack)
|
|
else
|
|
skillModFlags = bor(skillModFlags, ModFlag.Cast)
|
|
if skillFlags.spell then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Spell)
|
|
end
|
|
end
|
|
if skillFlags.melee then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Melee)
|
|
elseif skillFlags.projectile then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Projectile)
|
|
skillFlags.chaining = true
|
|
end
|
|
if skillFlags.area then
|
|
skillModFlags = bor(skillModFlags, ModFlag.Area)
|
|
end
|
|
|
|
-- Build skill keyword flag set
|
|
local skillKeywordFlags = 0
|
|
if skillFlags.hit then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Hit)
|
|
end
|
|
if skillTypes[SkillType.Aura] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Aura)
|
|
end
|
|
if skillTypes[SkillType.Hex] or skillTypes[SkillType.Mark] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Curse)
|
|
end
|
|
if skillTypes[SkillType.Warcry] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Warcry)
|
|
end
|
|
if skillTypes[SkillType.MovementSkill] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Movement)
|
|
end
|
|
if skillTypes[SkillType.Vaal] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Vaal)
|
|
end
|
|
if skillTypes[SkillType.LightningSkill] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Lightning)
|
|
end
|
|
if skillTypes[SkillType.ColdSkill] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Cold)
|
|
end
|
|
if skillTypes[SkillType.FireSkill] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Fire)
|
|
end
|
|
if skillTypes[SkillType.ChaosSkill] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Chaos)
|
|
end
|
|
if skillFlags.weapon1Attack and band(activeSkill.weapon1Flags, ModFlag.Bow) ~= 0 then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Bow)
|
|
end
|
|
if skillFlags.totem then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Totem)
|
|
elseif skillFlags.trap then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Trap)
|
|
elseif skillFlags.mine then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Mine)
|
|
elseif skillFlags.brand then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Brand)
|
|
else
|
|
skillFlags.selfCast = true
|
|
end
|
|
if skillTypes[SkillType.Attack] then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Attack)
|
|
end
|
|
if skillTypes[SkillType.Spell] and not skillFlags.cast then
|
|
skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Spell)
|
|
end
|
|
|
|
-- Get skill totem ID for totem skills
|
|
-- This is used to calculate totem life
|
|
if skillFlags.totem then
|
|
activeSkill.skillTotemId = activeGrantedEffect.skillTotemId
|
|
if not activeSkill.skillTotemId then
|
|
if activeGrantedEffect.color == 2 then
|
|
activeSkill.skillTotemId = 2
|
|
elseif activeGrantedEffect.color == 3 then
|
|
activeSkill.skillTotemId = 3
|
|
else
|
|
activeSkill.skillTotemId = 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Calculate Distance for meleeDistance or projectileDistance (for melee proximity, e.g. Impact)
|
|
if skillFlags.melee then
|
|
effectiveRange = env.configInput.meleeDistance
|
|
else
|
|
effectiveRange = env.configInput.projectileDistance
|
|
end
|
|
|
|
-- Build config structure for modifier searches
|
|
activeSkill.skillCfg = {
|
|
flags = bor(skillModFlags, activeSkill.weapon1Flags or activeSkill.weapon2Flags or 0),
|
|
keywordFlags = skillKeywordFlags,
|
|
skillName = activeGrantedEffect.name:gsub("^Vaal ",""):gsub("Summon Skeletons","Summon Skeleton"), -- This allows modifiers that target specific skills to also apply to their Vaal counterpart
|
|
summonSkillName = activeSkill.summonSkill and activeSkill.summonSkill.activeEffect.grantedEffect.name,
|
|
skillGem = activeEffect.gemData,
|
|
skillGrantedEffect = activeGrantedEffect,
|
|
skillPart = activeSkill.skillPart,
|
|
skillTypes = activeSkill.skillTypes,
|
|
skillCond = { },
|
|
skillDist = env.mode_effective and effectiveRange,
|
|
slotName = activeSkill.slotName,
|
|
}
|
|
if skillFlags.weapon1Attack then
|
|
activeSkill.weapon1Cfg = copyTable(activeSkill.skillCfg, true)
|
|
activeSkill.weapon1Cfg.skillCond = setmetatable({ ["MainHandAttack"] = true }, { __index = activeSkill.skillCfg.skillCond })
|
|
activeSkill.weapon1Cfg.flags = bor(skillModFlags, activeSkill.weapon1Flags)
|
|
end
|
|
if skillFlags.weapon2Attack then
|
|
activeSkill.weapon2Cfg = copyTable(activeSkill.skillCfg, true)
|
|
activeSkill.weapon2Cfg.skillCond = setmetatable({ ["OffHandAttack"] = true }, { __index = activeSkill.skillCfg.skillCond })
|
|
activeSkill.weapon2Cfg.flags = bor(skillModFlags, activeSkill.weapon2Flags)
|
|
end
|
|
|
|
-- Initialise skill modifier list
|
|
local skillModList = new("ModList", activeSkill.actor.modDB)
|
|
activeSkill.skillModList = skillModList
|
|
activeSkill.baseSkillModList = skillModList
|
|
|
|
if skillModList:Flag(activeSkill.skillCfg, "DisableSkill") then
|
|
skillFlags.disable = true
|
|
activeSkill.disableReason = "Skills of this type are disabled"
|
|
end
|
|
|
|
if skillFlags.disable then
|
|
wipeTable(skillFlags)
|
|
skillFlags.disable = true
|
|
calcLib.validateGemLevel(activeEffect)
|
|
activeEffect.grantedEffectLevel = activeGrantedEffect.levels[activeEffect.level]
|
|
return
|
|
end
|
|
|
|
-- Add support gem modifiers to skill mod list
|
|
for _, skillEffect in pairs(activeSkill.effectList) do
|
|
if skillEffect.grantedEffect.support then
|
|
calcs.mergeSkillInstanceMods(env, skillModList, skillEffect)
|
|
local level = skillEffect.grantedEffect.levels[skillEffect.level]
|
|
if level.manaMultiplier then
|
|
skillModList:NewMod("SupportManaMultiplier", "MORE", level.manaMultiplier, skillEffect.grantedEffect.modSource)
|
|
end
|
|
if level.manaCostOverride then
|
|
activeSkill.skillData.manaCostOverride = level.manaCostOverride
|
|
end
|
|
if level.cooldown then
|
|
activeSkill.skillData.cooldown = level.cooldown
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Apply gem/quality modifiers from support gems
|
|
for _, value in ipairs(skillModList:List(activeSkill.skillCfg, "SupportedGemProperty")) do
|
|
if value.keyword == "active_skill" then
|
|
activeEffect[value.key] = activeEffect[value.key] + value.value
|
|
end
|
|
end
|
|
|
|
-- Add active gem modifiers
|
|
activeEffect.actorLevel = activeSkill.actor.minionData and activeSkill.actor.level
|
|
calcs.mergeSkillInstanceMods(env, skillModList, activeEffect, skillModList:List(activeSkill.skillCfg, "ExtraSkillStat"))
|
|
activeEffect.grantedEffectLevel = activeGrantedEffect.levels[activeEffect.level]
|
|
|
|
-- Add extra modifiers from granted effect level
|
|
local level = activeEffect.grantedEffectLevel
|
|
activeSkill.skillData.CritChance = level.critChance
|
|
activeSkill.skillData.duration = level.duration
|
|
if level.damageMultiplier then
|
|
skillModList:NewMod("Damage", "MORE", level.damageMultiplier, activeEffect.grantedEffect.modSource, ModFlag.Attack)
|
|
end
|
|
if level.attackSpeedMultiplier then
|
|
skillModList:NewMod("Speed", "MORE", level.attackSpeedMultiplier, activeEffect.grantedEffect.modSource, ModFlag.Attack)
|
|
end
|
|
if level.cooldown then
|
|
activeSkill.skillData.cooldown = level.cooldown
|
|
end
|
|
|
|
-- Add extra modifiers from other sources
|
|
activeSkill.extraSkillModList = { }
|
|
for _, value in ipairs(skillModList:List(activeSkill.skillCfg, "ExtraSkillMod")) do
|
|
skillModList:AddMod(value.mod)
|
|
t_insert(activeSkill.extraSkillModList, value.mod)
|
|
end
|
|
|
|
-- Find totem level
|
|
if skillFlags.totem then
|
|
activeSkill.skillData.totemLevel = activeEffect.grantedEffectLevel.levelRequirement
|
|
end
|
|
|
|
-- Add active mine multiplier
|
|
if skillFlags.mine then
|
|
activeSkill.activeMineCount = (env.mode == "CALCS" and activeEffect.srcInstance.skillMineCountCalcs) or (env.mode ~= "CALCS" and activeEffect.srcInstance.skillMineCount)
|
|
if activeSkill.activeMineCount and activeSkill.activeMineCount > 0 then
|
|
skillModList:NewMod("Multiplier:ActiveMineCount", "BASE", activeSkill.activeMineCount, "Base")
|
|
end
|
|
end
|
|
|
|
-- Extract skill data
|
|
for _, value in ipairs(env.modDB:List(activeSkill.skillCfg, "SkillData")) do
|
|
activeSkill.skillData[value.key] = value.value
|
|
end
|
|
for _, value in ipairs(skillModList:List(activeSkill.skillCfg, "SkillData")) do
|
|
activeSkill.skillData[value.key] = value.value
|
|
end
|
|
|
|
-- Create minion
|
|
local minionList, isSpectre
|
|
if activeGrantedEffect.minionList then
|
|
if activeGrantedEffect.minionList[1] then
|
|
minionList = copyTable(activeGrantedEffect.minionList)
|
|
else
|
|
minionList = copyTable(env.build.spectreList)
|
|
isSpectre = true
|
|
end
|
|
else
|
|
minionList = { }
|
|
end
|
|
for _, skillEffect in ipairs(activeSkill.effectList) do
|
|
if skillEffect.grantedEffect.support and skillEffect.grantedEffect.addMinionList then
|
|
for _, minionType in ipairs(skillEffect.grantedEffect.addMinionList) do
|
|
t_insert(minionList, minionType)
|
|
end
|
|
end
|
|
end
|
|
activeSkill.minionList = minionList
|
|
if minionList[1] and not activeSkill.actor.minionData then
|
|
local minionType
|
|
if env.mode == "CALCS" and activeSkill == env.player.mainSkill then
|
|
local index = isValueInArray(minionList, activeEffect.srcInstance.skillMinionCalcs) or 1
|
|
minionType = minionList[index]
|
|
activeEffect.srcInstance.skillMinionCalcs = minionType
|
|
else
|
|
local index = isValueInArray(minionList, activeEffect.srcInstance.skillMinion) or 1
|
|
minionType = minionList[index]
|
|
activeEffect.srcInstance.skillMinion = minionType
|
|
end
|
|
if minionType then
|
|
local minion = { }
|
|
activeSkill.minion = minion
|
|
skillFlags.haveMinion = true
|
|
minion.parent = env.player
|
|
minion.enemy = env.enemy
|
|
minion.type = minionType
|
|
minion.minionData = env.data.minions[minionType]
|
|
minion.level = activeSkill.skillData.minionLevelIsEnemyLevel and env.enemyLevel or activeSkill.skillData.minionLevel or activeEffect.grantedEffectLevel.levelRequirement
|
|
-- fix minion level between 1 and 100
|
|
minion.level = m_min(m_max(minion.level,1),100)
|
|
minion.itemList = { }
|
|
minion.uses = activeGrantedEffect.minionUses
|
|
minion.lifeTable = isSpectre and env.data.monsterLifeTable or env.data.monsterAllyLifeTable
|
|
local attackTime = minion.minionData.attackTime * (1 - (minion.minionData.damageFixup or 0))
|
|
local damage = env.data.monsterDamageTable[minion.level] * minion.minionData.damage * attackTime
|
|
if activeGrantedEffect.minionHasItemSet then
|
|
if env.mode == "CALCS" and activeSkill == env.player.mainSkill then
|
|
if not env.build.itemsTab.itemSets[activeEffect.srcInstance.skillMinionItemSetCalcs] then
|
|
activeEffect.srcInstance.skillMinionItemSetCalcs = env.build.itemsTab.itemSetOrderList[1]
|
|
end
|
|
minion.itemSet = env.build.itemsTab.itemSets[activeEffect.srcInstance.skillMinionItemSetCalcs]
|
|
else
|
|
if not env.build.itemsTab.itemSets[activeEffect.srcInstance.skillMinionItemSet] then
|
|
activeEffect.srcInstance.skillMinionItemSet = env.build.itemsTab.itemSetOrderList[1]
|
|
end
|
|
minion.itemSet = env.build.itemsTab.itemSets[activeEffect.srcInstance.skillMinionItemSet]
|
|
end
|
|
end
|
|
if activeSkill.skillData.minionUseBowAndQuiver and env.player.weaponData1.type == "Bow" then
|
|
minion.weaponData1 = env.player.weaponData1
|
|
elseif env.theIronMass and minionType == "RaisedSkeleton" then
|
|
minion.weaponData1 = env.player.weaponData1
|
|
else
|
|
minion.weaponData1 = {
|
|
type = minion.minionData.weaponType1 or "None",
|
|
AttackRate = 1 / attackTime,
|
|
CritChance = 5,
|
|
PhysicalMin = round(damage * (1 - minion.minionData.damageSpread)),
|
|
PhysicalMax = round(damage * (1 + minion.minionData.damageSpread)),
|
|
range = minion.minionData.attackRange,
|
|
}
|
|
end
|
|
minion.weaponData2 = { }
|
|
if minion.uses then
|
|
if minion.uses["Weapon 1"] then
|
|
if minion.itemSet then
|
|
local item = env.build.itemsTab.items[minion.itemSet[minion.itemSet.useSecondWeaponSet and "Weapon 1 Swap" or "Weapon 1"].selItemId]
|
|
if item then
|
|
minion.weaponData1 = item.weaponData[1]
|
|
end
|
|
else
|
|
minion.weaponData1 = env.player.weaponData1
|
|
end
|
|
end
|
|
if minion.uses["Weapon 2"] then
|
|
if minion.itemSet then
|
|
local item = env.build.itemsTab.items[minion.itemSet[minion.itemSet.useSecondWeaponSet and "Weapon 2 Swap" or "Weapon 2"].selItemId]
|
|
if item and item.weaponData then
|
|
minion.weaponData2 = item.weaponData[2]
|
|
end
|
|
else
|
|
minion.weaponData2 = env.player.weaponData2
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Separate global effect modifiers (mods that can affect defensive stats or other skills)
|
|
local i = 1
|
|
while skillModList[i] do
|
|
local effectType, effectName, effectTag
|
|
for _, tag in ipairs(skillModList[i]) do
|
|
if tag.type == "GlobalEffect" then
|
|
effectType = tag.effectType
|
|
effectName = tag.effectName or activeGrantedEffect.name
|
|
effectTag = tag
|
|
break
|
|
end
|
|
end
|
|
if effectTag and effectTag.modCond and not skillModList:GetCondition(effectTag.modCond, activeSkill.skillCfg) then
|
|
t_remove(skillModList, i)
|
|
elseif effectType then
|
|
local buff
|
|
for _, skillBuff in ipairs(activeSkill.buffList) do
|
|
if skillBuff.type == effectType and skillBuff.name == effectName then
|
|
buff = skillBuff
|
|
break
|
|
end
|
|
end
|
|
if not buff then
|
|
buff = {
|
|
type = effectType,
|
|
name = effectName,
|
|
allowTotemBuff = effectTag.allowTotemBuff,
|
|
cond = effectTag.effectCond,
|
|
enemyCond = effectTag.effectEnemyCond,
|
|
stackVar = effectTag.effectStackVar,
|
|
stackLimit = effectTag.effectStackLimit,
|
|
stackLimitVar = effectTag.effectStackLimitVar,
|
|
modList = { },
|
|
}
|
|
if skillModList[i].source == activeGrantedEffect.modSource then
|
|
-- Inherit buff configuration from the active skill
|
|
buff.activeSkillBuff = true
|
|
buff.applyNotPlayer = activeSkill.skillData.buffNotPlayer
|
|
buff.applyMinions = activeSkill.skillData.buffMinions
|
|
buff.applyAllies = activeSkill.skillData.buffAllies
|
|
buff.allowTotemBuff = activeSkill.skillData.allowTotemBuff
|
|
end
|
|
t_insert(activeSkill.buffList, buff)
|
|
end
|
|
local match = false
|
|
for d = 1, #buff.modList do
|
|
local destMod = buff.modList[d]
|
|
if modLib.compareModParams(skillModList[i], destMod) and (destMod.type == "BASE" or destMod.type == "INC") then
|
|
destMod = copyTable(destMod)
|
|
destMod.value = destMod.value + skillModList[i].value
|
|
buff.modList[d] = destMod
|
|
match = true
|
|
break
|
|
end
|
|
end
|
|
if not match then
|
|
t_insert(buff.modList, skillModList[i])
|
|
end
|
|
t_remove(skillModList, i)
|
|
else
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
if activeSkill.buffList[1] then
|
|
-- Add to auxiliary skill list
|
|
t_insert(env.auxSkillList, activeSkill)
|
|
end
|
|
end
|
|
|
|
-- Initialise the active skill's minion skills
|
|
function calcs.createMinionSkills(env, activeSkill)
|
|
local activeEffect = activeSkill.activeEffect
|
|
local minion = activeSkill.minion
|
|
local minionData = minion.minionData
|
|
|
|
minion.activeSkillList = { }
|
|
local skillIdList = { }
|
|
for _, skillId in ipairs(minionData.skillList) do
|
|
if env.data.skills[skillId] then
|
|
t_insert(skillIdList, skillId)
|
|
end
|
|
end
|
|
for _, skill in ipairs(activeSkill.skillModList:List(activeSkill.skillCfg, "ExtraMinionSkill")) do
|
|
if not skill.minionList or isValueInArray(skill.minionList, minion.type) then
|
|
t_insert(skillIdList, skill.skillId)
|
|
end
|
|
end
|
|
if #skillIdList == 0 then
|
|
-- Not ideal, but let's avoid horrible crashes if a spectre has no skills for some reason
|
|
t_insert(skillIdList, "Melee")
|
|
end
|
|
for _, skillId in ipairs(skillIdList) do
|
|
local activeEffect = {
|
|
grantedEffect = env.data.skills[skillId],
|
|
level = 1,
|
|
quality = 0,
|
|
}
|
|
if #activeEffect.grantedEffect.levels > 1 then
|
|
for level, levelData in ipairs(activeEffect.grantedEffect.levels) do
|
|
if levelData[1] > minion.level then
|
|
break
|
|
else
|
|
activeEffect.level = level
|
|
end
|
|
end
|
|
end
|
|
local minionSkill = calcs.createActiveSkill(activeEffect, activeSkill.supportList, minion, nil, activeSkill)
|
|
calcs.buildActiveSkillModList(env, minionSkill)
|
|
minionSkill.skillFlags.minion = true
|
|
minionSkill.skillFlags.minionSkill = true
|
|
minionSkill.skillFlags.haveMinion = true
|
|
minionSkill.skillFlags.spectre = activeSkill.skillFlags.spectre
|
|
minionSkill.skillData.damageEffectiveness = 1 + (activeSkill.skillData.minionDamageEffectiveness or 0) / 100
|
|
t_insert(minion.activeSkillList, minionSkill)
|
|
end
|
|
local skillIndex
|
|
if env.mode == "CALCS" then
|
|
skillIndex = m_max(m_min(activeEffect.srcInstance.skillMinionSkillCalcs or 1, #minion.activeSkillList), 1)
|
|
activeEffect.srcInstance.skillMinionSkillCalcs = skillIndex
|
|
else
|
|
skillIndex = m_max(m_min(activeEffect.srcInstance.skillMinionSkill or 1, #minion.activeSkillList), 1)
|
|
if env.mode == "MAIN" then
|
|
activeEffect.srcInstance.skillMinionSkill = skillIndex
|
|
end
|
|
end
|
|
minion.mainSkill = minion.activeSkillList[skillIndex]
|
|
end |