Release 1.3.21

- Overhauled buff/debuff and curse handling
- Added support for Punishment, Generosity and Block Chance Reduction
- Minor corrections to unique stat wording
This commit is contained in:
Openarl
2017-03-20 21:06:47 +10:00
parent 946f042501
commit c9a5e7fc9a
16 changed files with 390 additions and 88 deletions

View File

@@ -3,7 +3,7 @@
-- Module: Calcs
-- Performs all the offense and defense calculations.
-- Here be dragons!
-- This file is 3300 lines long, over half of which is in one function...
-- This file is 3400 lines long, over half of which is in one function...
--
local pairs = pairs
@@ -474,6 +474,29 @@ local function buildNodeModList(env, nodeList, finishJewels)
return modList
end
-- Merge an instance of a buff, taking the highest value of each modifier
local function mergeBuff(src, destTable, destKey)
if not destTable[destKey] then
destTable[destKey] = { }
end
local dest = destTable[destKey]
for _, mod in ipairs(src) do
local param = modLib.formatModParams(mod)
for index, destMod in ipairs(dest) do
if param == modLib.formatModParams(destMod) then
if type(destMod.value) == "number" and mod.value > destMod.value then
dest[index] = mod
end
param = nil
break
end
end
if param then
t_insert(dest, mod)
end
end
end
-- Calculate min/max damage of a hit for the given damage type
local function calcHitDamage(env, source, cfg, breakdown, damageType, ...)
local modDB = env.modDB
@@ -1002,7 +1025,7 @@ local function initEnv(build, mode, override)
end
-- Finalise environment and perform the calculations
-- This function is 1900 lines long. Enjoy!
-- This function is 2100 lines long. Enjoy!
local function performCalcs(env)
local modDB = env.modDB
local enemyDB = env.enemyDB
@@ -1049,9 +1072,35 @@ local function performCalcs(env)
-- Merge flask modifiers
if env.mode_combat then
local effectInc = modDB:Sum("INC", nil, "FlaskEffect")
local flaskBuffs = { }
for item in pairs(env.flasks) do
modDB.conditions["UsingFlask"] = true
modDB:ScaleAddList(item.modList, 1 + (effectInc + item.flaskData.effectInc) / 100)
-- Avert thine eyes, lest they be forever scarred
-- I have no idea how to determine which buff is applied by a given flask,
-- so utility flasks are grouped by base, unique flasks are grouped by name, and magic flasks by their modifiers
local effectMod = 1 + (effectInc + item.flaskData.effectInc) / 100
if item.buffModList[1] then
local srcList = common.New("ModList")
srcList:ScaleAddList(item.buffModList, effectMod)
mergeBuff(srcList, flaskBuffs, item.baseName)
end
if item.modList[1] then
local srcList = common.New("ModList")
srcList:ScaleAddList(item.modList, effectMod)
local key
if item.rarity == "UNIQUE" then
key = item.title
else
key = ""
for _, mod in ipairs(item.modList) do
key = key .. modLib.formatModParams(mod) .. "&"
end
end
mergeBuff(srcList, flaskBuffs, key)
end
end
for _, buffModList in pairs(flaskBuffs) do
modDB:AddList(buffModList)
end
end
@@ -1124,27 +1173,6 @@ local function performCalcs(env)
if env.mode_effective then
condList["Effective"] = true
end
-- Check for extra curses
for _, value in ipairs(modDB:Sum("LIST", nil, "ExtraCurse")) do
local modList = common.New("ModList")
mergeGemMods(modList, {
level = value.level,
quality = 0,
data = data.gems[value.name],
})
modDB.multipliers["CurseOnEnemy"] = (modDB.multipliers["CurseOnEnemy"] or 0) + 1
local curseModList = { }
for _, mod in ipairs(modList) do
for _, tag in ipairs(mod.tagList) do
if tag.type == "GlobalEffect" and tag.effectType == "Curse" then
t_insert(curseModList, mod)
break
end
end
end
enemyDB:ScaleAddList(curseModList, (1 + enemyDB:Sum("INC", nil, "CurseEffect") / 100) * enemyDB:Sum("MORE", nil, "CurseEffect"))
end
-- Check for extra modifiers to apply to aura skills
local extraAuraModList = { }
@@ -1155,7 +1183,10 @@ local function performCalcs(env)
end
end
-- Merge auxillary skill modifiers and calculate skill life and mana reservations
-- Combine buffs/debuffs and calculate skill life and mana reservations
local buffs = { }
local debuffs = { }
local curses = { }
env.reserved_LifeBase = 0
env.reserved_LifePercent = 0
env.reserved_ManaBase = 0
@@ -1168,37 +1199,58 @@ local function performCalcs(env)
local skillModList = activeSkill.skillModList
local skillCfg = activeSkill.skillCfg
-- Merge auxillary modifiers
-- Combine buffs/debuffs
if env.mode_buffs then
if activeSkill.buffModList and (not activeSkill.skillFlags.totem or activeSkill.skillData.allowTotemBuff) and (not activeSkill.skillData.offering or modDB:Sum("FLAG", nil, "OfferingsAffectPlayer")) then
if activeSkill.buffModList and
not activeSkill.skillFlags.curse and
(not activeSkill.skillFlags.totem or activeSkill.skillData.allowTotemBuff) and
(not activeSkill.skillData.offering or modDB:Sum("FLAG", nil, "OfferingsAffectPlayer")) then
activeSkill.buffSkill = true
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "BuffEffect")
if activeSkill.activeGem.data.golem and modDB:Sum("FLAG", skillCfg, "LiegeOfThePrimordial") and (activeSkill.activeGem.data.fire or activeSkill.activeGem.data.cold or activeSkill.activeGem.data.lightning) then
inc = inc + 100
end
modDB:ScaleAddList(activeSkill.buffModList, 1 + inc / 100)
local more = modDB:Sum("MORE", skillCfg, "BuffEffect")
srcList:ScaleAddList(activeSkill.buffModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, activeSkill.activeGem.name)
end
if activeSkill.auraModList then
if activeSkill.auraModList and not activeSkill.skillData.auraCannotAffectSelf then
activeSkill.buffSkill = true
local inc = modDB:Sum("INC", skillCfg, "AuraEffect") + skillModList:Sum("INC", skillCfg, "AuraEffect") + modDB:Sum("INC", skillCfg, "BuffEffect")
local more = modDB:Sum("MORE", skillCfg, "AuraEffect") * skillModList:Sum("MORE", skillCfg, "AuraEffect")
modDB:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
modDB:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "AuraEffect", "BuffEffect") + skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect")
local more = modDB:Sum("MORE", skillCfg, "AuraEffect", "BuffEffect") * skillModList:Sum("MORE", skillCfg, "AuraEffect", "BuffEffect")
srcList:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, activeSkill.activeGem.name)
condList["HaveAuraActive"] = true
end
end
if env.mode_effective then
if activeSkill.debuffModList then
activeSkill.debuffSkill = true
enemyDB:ScaleAddList(activeSkill.debuffModList, activeSkill.skillData.stackCount or 1)
local srcList = common.New("ModList")
srcList:ScaleAddList(activeSkill.debuffModList, activeSkill.skillData.stackCount or 1)
mergeBuff(srcList, debuffs, activeSkill.activeGem.name)
end
if activeSkill.curseModList then
activeSkill.debuffSkill = true
condList["EnemyCursed"] = true
modDB.multipliers["CurseOnEnemy"] = (modDB.multipliers["CurseOnEnemy"] or 0) + 1
if activeSkill.curseModList or (activeSkill.skillFlags.curse and activeSkill.buffModList) then
local curse = {
name = activeSkill.activeGem.name,
priority = activeSkill.skillTypes[SkillType.Aura] and 3 or 1,
}
local inc = modDB:Sum("INC", skillCfg, "CurseEffect") + enemyDB:Sum("INC", nil, "CurseEffect") + skillModList:Sum("INC", skillCfg, "CurseEffect")
local more = modDB:Sum("MORE", skillCfg, "CurseEffect") * enemyDB:Sum("MORE", nil, "CurseEffect") * skillModList:Sum("MORE", skillCfg, "CurseEffect")
enemyDB:ScaleAddList(activeSkill.curseModList, (1 + inc / 100) * more)
if activeSkill.curseModList then
curse.modList = common.New("ModList")
curse.modList:ScaleAddList(activeSkill.curseModList, (1 + inc / 100) * more)
end
if activeSkill.buffModList then
-- Curse applies a buff; scale by curse effect, then buff effect
local temp = common.New("ModList")
temp:ScaleAddList(activeSkill.buffModList, (1 + inc / 100) * more)
curse.buffModList = common.New("ModList")
local buffInc = modDB:Sum("INC", skillCfg, "BuffEffect")
local buffMore = modDB:Sum("MORE", skillCfg, "BuffEffect")
curse.buffModList:ScaleAddList(temp, (1 + buffInc / 100) * buffMore)
end
t_insert(curses, curse)
end
end
@@ -1231,6 +1283,76 @@ local function performCalcs(env)
end
end
-- Check for extra curses
for _, value in ipairs(modDB:Sum("LIST", nil, "ExtraCurse")) do
local curse = {
name = value.name,
priority = 2,
modList = common.New("ModList")
}
local gemModList = common.New("ModList")
mergeGemMods(gemModList, {
level = value.level,
quality = 0,
data = data.gems[value.name],
})
local curseModList = { }
for _, mod in ipairs(gemModList) do
for _, tag in ipairs(mod.tagList) do
if tag.type == "GlobalEffect" and tag.effectType == "Curse" then
t_insert(curseModList, mod)
break
end
end
end
curse.modList:ScaleAddList(curseModList, (1 + enemyDB:Sum("INC", nil, "CurseEffect") / 100) * enemyDB:Sum("MORE", nil, "CurseEffect"))
t_insert(curses, curse)
end
-- Assign curses to slots
local curseSlots = { }
env.curseSlots = curseSlots
output.EnemyCurseLimit = modDB:Sum("BASE", nil, "EnemyCurseLimit")
for _, curse in ipairs(curses) do
local slot
for i = 1, output.EnemyCurseLimit do
if not curseSlots[i] then
slot = i
break
elseif curseSlots[i].name == curse.name then
if curseSlots[i].priority < curse.priority then
slot = i
else
slot = nil
end
break
elseif curseSlots[i].priority < curse.priority then
slot = i
end
end
if slot then
curseSlots[slot] = curse
end
end
-- Merge buff/debuff modifiers
for _, modList in pairs(buffs) do
modDB:AddList(modList)
end
for _, modList in pairs(debuffs) do
enemyDB:AddList(modList)
end
modDB.multipliers["CurseOnEnemy"] = #curseSlots
for _, slot in ipairs(curseSlots) do
condList["EnemyCursed"] = true
if slot.modList then
enemyDB:AddList(slot.modList)
end
if slot.buffModList then
modDB:AddList(slot.buffModList)
end
end
-- Process misc modifiers
for _, value in ipairs(modDB:Sum("LIST", nil, "Misc")) do
if value.type == "Condition" then
@@ -2538,7 +2660,7 @@ local function performCalcs(env)
skillFlags.bleed = false
skillFlags.poison = false
skillFlags.ignite = false
skillFlags.igniteCanStack = modDB:Sum("FLAG", nil, "IgniteCanStack")
skillFlags.igniteCanStack = modDB:Sum("FLAG", skillCfg, "IgniteCanStack")
skillFlags.shock = false
skillFlags.freeze = false
for _, pass in ipairs(passList) do
@@ -3263,8 +3385,8 @@ function calcs.buildOutput(build, mode)
end
end
end
for _, value in ipairs(env.modDB:Sum("LIST", nil, "ExtraCurse")) do
t_insert(curseList, value.name)
for _, slot in ipairs(env.curseSlots) do
t_insert(curseList, slot.name)
end
output.BuffList = table.concat(buffList, ", ")
output.CombatList = table.concat(combatList, ", ")

View File

@@ -620,6 +620,16 @@ function itemLib.buildItemModList(item)
return
end
local baseList = { }
if item.base.weapon then
item.weaponData = { }
elseif item.base.armour then
item.armourData = { }
elseif item.base.flask then
item.flaskData = { }
item.buffModList = { }
elseif item.type == "Jewel" then
item.jewelData = { }
end
item.baseModList = baseList
item.rangeLineList = { }
item.modSource = "Item:"..(item.id or -1)..":"..item.name
@@ -638,7 +648,11 @@ function itemLib.buildItemModList(item)
if type(mod.value) == "table" and mod.value.mod then
mod.value.mod.source = mod.source
end
t_insert(baseList, mod)
if modLine.buff then
t_insert(item.buffModList, mod)
else
t_insert(baseList, mod)
end
end
end
end
@@ -646,15 +660,6 @@ function itemLib.buildItemModList(item)
-- Hack to remove the energy shield
t_insert(baseList, { name = "Misc", type = "LIST", value = { type = "ArmourData", key = "EnergyShield" }, flags = 0, keywordFlags = 0, tagList = { } })
end
if item.base.weapon then
item.weaponData = { }
elseif item.base.armour then
item.armourData = { }
elseif item.base.flask then
item.flaskData = { }
elseif item.type == "Jewel" then
item.jewelData = { }
end
if item.base.weapon or item.type == "Ring" then
item.slotModList = { }
for i = 1, 2 do

View File

@@ -546,7 +546,11 @@ local specialModList = {
mod("FireDamage", "INC", num, { type = "Condition", var = "HaveFireGolem"}),
mod("ChaosDamage", "INC", num, { type = "Condition", var = "HaveChaosGolem"})
} end,
["100%% increased effect of buffs granted by your elemental golems"] = { flag("LiegeOfThePrimordial") },
["(%d+)%% increased effect of buffs granted by your elemental golems"] = function(num) return {
mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.FireSkill }),
mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.ColdSkill }),
mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.LightningSkill }),
} end,
["every 10 seconds, gain (%d+)%% increased elemental damage for 4 seconds"] = function(num) return { mod("ElementalDamage", "INC", num, { type = "Condition", var = "PendulumOfDestruction" }) } end,
["every 10 seconds, gain (%d+)%% increased area of effect of area skills for 4 seconds"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "Condition", var = "PendulumOfDestruction" }) } end,
["enemies you curse take (%d+)%% increased damage"] = function(num) return { mod("Misc", "LIST", { type = "EnemyModifier", mod = mod("DamageTaken", "INC", num) }, { type = "Condition", var = "EnemyCursed" }) } end,
@@ -1081,7 +1085,6 @@ local function parseMod(line, order)
end
modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
modName = { damageType.."Min", damageType.."Max" }
modFlag = modFlag or { flags = bor(ModFlag.Attack, ModFlag.Spell) }
end
-- Combine flags and tags

View File

@@ -113,6 +113,10 @@ function modLib.formatValue(value)
return "{"..ret.."}"
end
function modLib.formatMod(mod)
return string.format("%s = %s|%s|%s|%s|%s", modLib.formatValue(mod.value), mod.name, mod.type, modLib.formatFlags(mod.flags, ModFlag), modLib.formatFlags(mod.keywordFlags, KeywordFlag), modLib.formatTags(mod.tagList))
function modLib.formatModParams(mod)
return string.format("%s|%s|%s|%s|%s", mod.name, mod.type, modLib.formatFlags(mod.flags, ModFlag), modLib.formatFlags(mod.keywordFlags, KeywordFlag), modLib.formatTags(mod.tagList))
end
function modLib.formatMod(mod)
return modLib.formatValue(mod.value) .. " = " .. modLib.formatModParams(mod)
end