Release 1.4.11

- Fixed stack overflow in copyTable()
- Fixed interaction between weapon swap and skilsl granted by items
- Consolidated list controls using a new base class
This commit is contained in:
Openarl
2017-05-16 19:05:02 +10:00
parent 8a1c666b2d
commit eb91bcbf66
28 changed files with 1282 additions and 1840 deletions

View File

@@ -554,7 +554,7 @@ function buildMode:OnFrame(inputEvents)
self.calcsTab:Draw(tabViewPort, inputEvents)
end
self.unsaved = self.modFlag or self.notesTab.modFlag or self.configTab.modFlag or self.spec.modFlag or self.skillsTab.modFlag or self.itemsTab.modFlag or self.calcsTab.modFlag
self.unsaved = self.modFlag or self.notesTab.modFlag or self.configTab.modFlag or self.treeTab.modFlag or self.spec.modFlag or self.skillsTab.modFlag or self.itemsTab.modFlag or self.calcsTab.modFlag
SetDrawLayer(5)
@@ -651,8 +651,8 @@ function buildMode:OpenSpectreLibrary()
return data.minions[a].name < data.minions[b].name
end
end)
controls.list = common.New("MinionList", nil, -100, 40, 190, 250, destList, true)
controls.source = common.New("MinionList", nil, 100, 40, 190, 250, sourceList, false, controls.list)
controls.list = common.New("MinionList", nil, -100, 40, 190, 250, destList)
controls.source = common.New("MinionList", nil, 100, 40, 190, 250, sourceList, controls.list)
controls.save = common.New("ButtonControl", nil, -45, 300, 80, 20, "Save", function()
self.spectreList = destList
self.modFlag = true

View File

@@ -7,7 +7,6 @@ local launch, main = ...
local pairs = pairs
local ipairs = ipairs
local t_insert = table.insert
local listMode = common.New("ControlHost")
@@ -17,25 +16,27 @@ function listMode:Init(selBuildName)
return main.screenW / 2
end
self.list = { }
self.controls.new = common.New("ButtonControl", {"TOP",self.anchor,"TOP"}, -210, 0, 60, 20, "New", function()
main:SetMode("BUILD", false, "Unnamed build")
end)
self.controls.open = common.New("ButtonControl", {"LEFT",self.controls.new,"RIGHT"}, 8, 0, 60, 20, "Open", function()
self:LoadSel()
self.controls.buildList:LoadBuild(self.controls.buildList.selValue)
end)
self.controls.open.enabled = function() return self.sel ~= nil end
self.controls.open.enabled = function() return self.controls.buildList.selValue ~= nil end
self.controls.copy = common.New("ButtonControl", {"LEFT",self.controls.open,"RIGHT"}, 8, 0, 60, 20, "Copy", function()
self:CopySel()
self.controls.buildList:RenameBuild(self.controls.buildList.selValue, true)
end)
self.controls.copy.enabled = function() return self.sel ~= nil end
self.controls.copy.enabled = function() return self.controls.buildList.selValue ~= nil end
self.controls.rename = common.New("ButtonControl", {"LEFT",self.controls.copy,"RIGHT"}, 8, 0, 60, 20, "Rename", function()
self:RenameSel()
self.controls.buildList:RenameBuild(self.controls.buildList.selValue)
end)
self.controls.rename.enabled = function() return self.sel ~= nil end
self.controls.rename.enabled = function() return self.controls.buildList.selValue ~= nil end
self.controls.delete = common.New("ButtonControl", {"LEFT",self.controls.rename,"RIGHT"}, 8, 0, 60, 20, "Delete", function()
self:DeleteSel()
self.controls.buildList:DeleteBuild(self.controls.buildList.selValue)
end)
self.controls.delete.enabled = function() return self.sel ~= nil end
self.controls.delete.enabled = function() return self.controls.buildList.selValue ~= nil end
self.controls.sort = common.New("DropDownControl", {"LEFT",self.controls.delete,"RIGHT"}, 8, 0, 140, 20, {{val="NAME",label="Sort by Name"},{val="CLASS",label="Sort by Class"},{val="EDITED",label="Sort by Last Edited"}}, function(sel, val)
main.buildSortMode = val.val
self:SortList()
@@ -48,7 +49,7 @@ function listMode:Init(selBuildName)
end
self:BuildList()
self:SelByFileName(selBuildName and selBuildName..".xml")
self.controls.buildList:SelByFileName(selBuildName and selBuildName..".xml")
self:SelectControl(self.controls.buildList)
end
@@ -56,19 +57,6 @@ function listMode:Shutdown()
end
function listMode:OnFrame(inputEvents)
for id, event in ipairs(inputEvents) do
if event.type == "KeyDown" then
if self.edit then
if event.key == "RETURN" then
self:EditFinish()
inputEvents[id] = nil
elseif event.key == "ESCAPE" then
self:EditCancel()
inputEvents[id] = nil
end
end
end
end
self:ProcessControlsInput(inputEvents, main.viewPort)
main:DrawBackground(main.viewPort)
@@ -107,7 +95,7 @@ function listMode:BuildList()
end
function listMode:SortList()
local oldSelFileName = self.sel and self.list[self.sel] and self.list[self.sel].fileName
local oldSelFileName = self.controls.buildList.selValue and self.controls.buildList.selValue.fileName
table.sort(self.list, function(a, b)
if main.buildSortMode == "EDITED" then
return a.modified > b.modified
@@ -125,131 +113,8 @@ function listMode:SortList()
return a.fileName:upper() < b.fileName:upper()
end)
if oldSelFileName then
self:SelByFileName(oldSelFileName)
self.controls.buildList:SelByFileName(oldSelFileName)
end
self.controls.buildList:ScrollSelIntoView()
end
function listMode:SelByFileName(selFileName)
self.sel = nil
for index, build in ipairs(self.list) do
if build.fileName == selFileName then
self.sel = index
self.controls.buildList:ScrollSelIntoView()
break
end
end
end
function listMode:EditInit(prompt, finFunc)
self.edit = self.sel
self.editFinFunc = finFunc
self.controls.buildList:ScrollSelIntoView()
self.controls.buildList.controls.nameEdit.prompt = prompt
self.controls.buildList.controls.nameEdit:SetText(self.list[self.sel].buildName or "")
end
function listMode:EditFinish()
if not self.edit then
return
end
local msg = self.editFinFunc(self.controls.buildList.controls.nameEdit.buf)
if msg then
main:OpenMessagePopup("Message", msg)
return
end
self.edit = nil
self:SelectControl(self.controls.buildList)
end
function listMode:EditCancel()
self.sel = nil
self.edit = nil
self:BuildList()
self:SelectControl(self.controls.buildList)
end
function listMode:LoadSel()
if self.edit or not self.sel or not self.list[self.sel] then
return
end
main:SetMode("BUILD", main.buildPath..self.list[self.sel].fileName, self.list[self.sel].buildName)
end
function listMode:CopySel()
if self.edit or not self.sel or not self.list[self.sel] then
return
end
local srcName = self.list[self.sel].fileName
table.insert(self.list, self.sel + 1, copyTable(self.list[self.sel]))
self.sel = self.sel + 1
self.list[self.sel].fileName = srcName:gsub("%.xml$","") .. " (copy)"
self:EditInit("Enter new name", function(buf)
if #buf < 1 then
return "No name entered"
end
local inFile, msg = io.open(main.buildPath..srcName, "r")
if not inFile then
return "Couldn't copy '"..srcName.."': "..msg
end
local dstName = buf .. ".xml"
local outFile, msg = io.open(main.buildPath..dstName, "r")
if outFile then
outFile:close()
return "'"..dstName.."' already exists"
end
outFile, msg = io.open(main.buildPath..dstName, "w")
if not outFile then
return "Couldn't create '"..dstName.."': "..msg
end
outFile:write(inFile:read("*a"))
inFile:close()
outFile:close()
self.list[self.edit].fileName = dstName
self.list[self.edit].buildName = buf
self:BuildList()
end)
end
function listMode:RenameSel()
if self.edit or not self.sel or not self.list[self.sel] then
return
end
local oldName = self.list[self.sel].fileName
self:EditInit("Enter new name", function(buf)
if #buf < 1 then
return "No name entered"
end
local newName = buf .. ".xml"
if newName == oldName then
return
end
if newName:lower() ~= oldName:lower() then
local newFile = io.open(main.buildPath..newName, "r")
if newFile then
newFile:close()
return "'"..newName.."' already exists"
end
end
local res, msg = os.rename(main.buildPath..oldName, main.buildPath..newName)
if not res then
return "Couldn't rename '"..oldName.."' to '"..newName.."': "..msg
end
self.list[self.edit].fileName = newName
self.list[self.edit].buildName = buf
self:SortList()
end)
end
function listMode:DeleteSel()
if self.edit or not self.sel or not self.list[self.sel] then
return
end
main:OpenConfirmPopup("Confirm Delete", "Are you sure you want to delete build:\n"..self.list[self.sel].buildName.."\nThis cannot be undone.", "Delete", function()
os.remove(main.buildPath..self.list[self.sel].fileName)
self:BuildList()
self.sel = nil
end)
end
return listMode

View File

@@ -69,44 +69,46 @@ 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(activeGem, supportList, summonSkill)
local activeSkill = { }
activeSkill.activeGem = activeGem
activeSkill.gemList = { activeSkill.activeGem }
activeSkill.supportList = supportList
activeSkill.summonSkill = summonSkill
local activeSkill = {
activeGem = activeGem,
supportList = supportList,
summonSkill = summonSkill,
skillData = { },
}
-- Initialise skill types
activeSkill.skillTypes = copyTable(activeGem.data.skillTypes)
if activeGem.data.minionSkillTypes then
activeSkill.minionSkillTypes = copyTable(activeGem.data.minionSkillTypes)
end
activeSkill.skillData = { }
-- Initialise skill flag set ('attack', 'projectile', etc)
local skillFlags = copyTable(activeGem.data.baseFlags)
activeSkill.skillFlags = skillFlags
skillFlags.hit = activeSkill.skillTypes[SkillType.Attack] or activeSkill.skillTypes[SkillType.Hit] or activeSkill.skillTypes[SkillType.Projectile]
for _, gem in ipairs(supportList) do
if calcLib.gemCanSupport(gem, activeSkill) then
if gem.data.addFlags then
-- Support gem adds flags to supported skills (eg. Remote Mine adds 'mine')
for k in pairs(gem.data.addFlags) do
skillFlags[k] = true
end
end
for _, skillType in pairs(gem.data.addSkillTypes) do
-- Process support skills
activeSkill.gemList = { activeGem }
for _, supportGem in ipairs(supportList) do
-- Pass 1: Add skill types from compatible supports
if calcLib.gemCanSupport(supportGem, activeSkill) then
for _, skillType in pairs(supportGem.data.addSkillTypes) do
activeSkill.skillTypes[skillType] = true
end
end
end
-- Process support gems
for _, gem in ipairs(supportList) do
if calcLib.gemCanSupport(gem, activeSkill) then
t_insert(activeSkill.gemList, gem)
if gem.isSupporting then
gem.isSupporting[activeGem.name] = true
for _, supportGem in ipairs(supportList) do
-- Pass 2: Add all compatible supports
if calcLib.gemCanSupport(supportGem, activeSkill) then
t_insert(activeSkill.gemList, supportGem)
if supportGem.isSupporting then
supportGem.isSupporting[activeGem.name] = true
end
if supportGem.data.addFlags and not summonSkill then
-- Support skill adds flags to supported skills (eg. Remote Mine adds 'mine')
for k in pairs(supportGem.data.addFlags) do
skillFlags[k] = true
end
end
end
end

View File

@@ -231,19 +231,30 @@ function calcs.initEnv(build, mode, override)
-- Build and merge item modifiers, and create list of radius jewels
env.radiusJewelList = wipeTable(env.radiusJewelList)
env.player.itemList = { }
env.itemGrantedSkills = { }
env.flasks = { }
env.modDB.conditions["UsingAllCorruptedItems"] = true
for slotName, slot in pairs(build.itemsTab.slots) do
local item
if slot.weaponSet and slot.weaponSet ~= (build.itemsTab.useSecondWeaponSet and 2 or 1) then
item = nil
elseif slotName == override.repSlotName then
if slotName == override.repSlotName then
item = override.repItem
elseif slot.nodeId and override.spec then
item = build.itemsTab.list[env.spec.jewels[slot.nodeId]]
else
item = build.itemsTab.list[slot.selItemId]
end
if item then
-- Find skills granted by this item
for _, skill in ipairs(item.grantedSkills) do
local grantedSkill = copyTable(skill)
grantedSkill.sourceItem = item
grantedSkill.slotName = slotName
t_insert(env.itemGrantedSkills, grantedSkill)
end
end
if slot.weaponSet and slot.weaponSet ~= (build.itemsTab.useSecondWeaponSet and 2 or 1) then
item = nil
end
if slot.weaponSet == 2 and build.itemsTab.useSecondWeaponSet then
slotName = slotName:gsub(" Swap","")
end
@@ -337,64 +348,53 @@ function calcs.initEnv(build, mode, override)
if env.mode == "MAIN" then
-- Process extra skills granted by items
local markList = { }
for _, mod in ipairs(modDB.mods["ExtraSkill"] or { }) do
if mod.value.name ~= "Unknown" then
-- Extract the name of the slot containing the item this skill was granted by
local slotName
for _, tag in ipairs(mod.tagList) do
if tag.type == "SocketedIn" then
slotName = tag.slotName
local markList = wipeTable(tempTable1)
for _, grantedSkill in ipairs(env.itemGrantedSkills) do
-- Check if a matching group already exists
local group
for index, socketGroup in pairs(build.skillsTab.socketGroupList) do
if socketGroup.source == grantedSkill.source and socketGroup.slot == grantedSkill.slotName then
if socketGroup.gemList[1] and socketGroup.gemList[1].nameSpec == grantedSkill.name then
group = socketGroup
markList[socketGroup] = true
break
end
end
end
if not group then
-- Create a new group for this skill
group = { label = "", enabled = true, gemList = { }, source = grantedSkill.source, slot = grantedSkill.slotName }
t_insert(build.skillsTab.socketGroupList, group)
markList[group] = true
end
-- Check if a matching group already exists
local group
for index, socketGroup in pairs(build.skillsTab.socketGroupList) do
if socketGroup.source == mod.source and socketGroup.slot == slotName then
if socketGroup.gemList[1] and socketGroup.gemList[1].nameSpec == mod.value.name then
group = socketGroup
markList[socketGroup] = true
break
end
end
end
if not group then
-- Create a new group for this skill
group = { label = "", enabled = true, gemList = { }, source = mod.source, slot = slotName }
t_insert(build.skillsTab.socketGroupList, group)
markList[group] = true
end
-- Update the group
group.sourceItem = build.itemsTab.list[tonumber(mod.source:match("Item:(%d+):"))]
local activeGem = group.gemList[1] or {
nameSpec = mod.value.name,
quality = 0,
enabled = true,
fromItem = true,
}
activeGem.level = mod.value.level
wipeTable(group.gemList)
t_insert(group.gemList, activeGem)
if mod.value.noSupports then
group.noSupports = true
else
for _, socketGroup in pairs(build.skillsTab.socketGroupList) do
-- Look for other groups that are socketed in the item
if socketGroup.slot == slotName and not socketGroup.source then
-- Add all support gems to the skill's group
for _, gem in ipairs(socketGroup.gemList) do
if gem.data and gem.data.support then
t_insert(group.gemList, gem)
end
-- Update the group
group.sourceItem = grantedSkill.sourceItem
local activeGem = group.gemList[1] or {
nameSpec = grantedSkill.name,
quality = 0,
enabled = true,
fromItem = true,
}
activeGem.level = grantedSkill.level
wipeTable(group.gemList)
t_insert(group.gemList, activeGem)
if grantedSkill.noSupports then
group.noSupports = true
else
for _, socketGroup in pairs(build.skillsTab.socketGroupList) do
-- Look for other groups that are socketed in the item
if socketGroup.slot == grantedSkill.slotName and not socketGroup.source then
-- Add all support gems to the skill's group
for _, gem in ipairs(socketGroup.gemList) do
if gem.data and gem.data.support then
t_insert(group.gemList, gem)
end
end
end
end
build.skillsTab:ProcessSocketGroup(group)
end
build.skillsTab:ProcessSocketGroup(group)
end
-- Remove any socket groups that no longer have a matching item
@@ -434,12 +434,12 @@ function calcs.initEnv(build, mode, override)
-- Build list of active skills
env.activeSkillList = { }
local groupCfg = wipeTable(tempTable1)
for index, socketGroup in pairs(build.skillsTab.socketGroupList) do
local socketGroupSkillList = { }
local slot = socketGroup.slot and build.itemsTab.slots[socketGroup.slot]
socketGroup.slotEnabled = not slot or not slot.weaponSet or slot.weaponSet == (build.itemsTab.useSecondWeaponSet and 2 or 1)
if index == env.mainSocketGroup or (socketGroup.enabled and socketGroup.slotEnabled) then
local groupCfg = wipeTable(tempTable1)
groupCfg.slotName = socketGroup.slot
local propertyModList = env.modDB:Sum("LIST", groupCfg, "GemProperty")
@@ -454,14 +454,17 @@ function calcs.initEnv(build, mode, override)
name = gemData.name,
data = gemData,
level = value.level,
quality = 0,
enabled = true,
quality = 0,
enabled = true,
})
end
end
end
for _, gem in ipairs(socketGroup.gemList) do
-- Add support gems from this group
if env.mode == "MAIN" then
gem.displayGem = nil
end
if gem.enabled and gem.data and gem.data.support then
local supportGem = copyTable(gem, true)
supportGem.srcGem = gem

View File

@@ -145,16 +145,25 @@ function isMouseInRegion(region)
end
-- Make a copy of a table and all subtables
function copyTable(tbl, noRecurse)
local out = {}
for k, v in pairs(tbl) do
if not noRecurse and type(v) == "table" then
out[k] = copyTable(v)
else
out[k] = v
do
local subTableMap = { }
function copyTable(tbl, noRecurse, isSubTable)
local out = {}
if not noRecurse then
subTableMap[tbl] = out
end
for k, v in pairs(tbl) do
if not noRecurse and type(v) == "table" then
out[k] = subTableMap[v] or copyTable(v, false, true)
else
out[k] = v
end
end
if not noRecurse and not isSubTable then
wipeTable(subTableMap)
end
return out
end
return out
end
-- Wipe all keys from the table and return it, or return a new table if no table provided

View File

@@ -620,7 +620,7 @@ function itemLib.buildItemModList(item)
if not item.base then
return
end
local baseList = { }
local baseList = common.New("ModList")
if item.base.weapon then
item.weaponData = { }
elseif item.base.armour then
@@ -652,14 +652,25 @@ function itemLib.buildItemModList(item)
if modLine.buff then
t_insert(item.buffModList, mod)
else
t_insert(baseList, mod)
baseList:AddMod(mod)
end
end
end
end
item.grantedSkills = { }
for _, skill in ipairs(baseList:Sum("LIST", nil, "ExtraSkill")) do
if skill.name ~= "Unknown" then
t_insert(item.grantedSkills, {
name = skill.name,
level = skill.level,
noSupports = skill.noSupports,
source = item.modSource,
})
end
end
if item.name == "Tabula Rasa, Simple Robe" or item.name == "Skin of the Loyal, Simple Robe" or item.name == "Skin of the Lords, Simple Robe" then
-- Hack to remove the energy shield
t_insert(baseList, { name = "ArmourData", type = "LIST", value = { key = "EnergyShield" }, flags = 0, keywordFlags = 0, tagList = { } })
baseList:NewMod("ArmourData", "LIST", { key = "EnergyShield" })
end
if item.base.weapon or item.type == "Ring" then
item.slotModList = { }

View File

@@ -38,6 +38,7 @@ local classList = {
"ScrollBarControl",
"SliderControl",
"TextListControl",
"ListControl",
-- Misc
"PopupDialog",
-- Mode: Build list
@@ -327,7 +328,7 @@ function main:OpenUpdatePopup()
end
end
local controls = { }
controls.changeLog = common.New("TextListControl", nil, 0, 20, 780, 190, nil, changeList)
controls.changeLog = common.New("TextListControl", nil, 0, 20, 780, 192, nil, changeList)
controls.update = common.New("ButtonControl", nil, -45, 220, 80, 20, "Update", function()
self:ClosePopup()
local ret = self:CallMode("CanExit", "UPDATE")
@@ -446,7 +447,7 @@ function main:RenderCircle(x, y, width, height, oX, oY, radius)
end
end
for ly = minY, maxY do
DrawImage(nil, x + minX[ly], y + ly, maxX[ly] - minX[ly], 1)
DrawImage(nil, x + minX[ly], y + ly, maxX[ly] - minX[ly] + 1, 1)
end
end

View File

@@ -712,17 +712,17 @@ local specialModList = {
["socketed gems gain (%d+)%% of physical damage as extra lightning damage"] = function(num) return { mod("PhysicalDamageGainAsLightning", "BASE", num, { type = "SocketedIn" }) } end,
["socketed red gems get (%d+)%% physical damage as extra fire damage"] = function(num) return { mod("PhysicalDamageGainAsFire", "BASE", num, { type = "SocketedIn", keyword = "strength" }) } end,
-- Extra skill/support
["grants level (%d+) (.+)"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["casts level (%d+) (.+) when equipped"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["cast level (%d+) (.+) when you deal a critical strike"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["cast level (%d+) (.+) when hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["cast level (%d+) (.+) when you kill an enemy"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["%d+%% chance to attack with level (%d+) (.+) on melee hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["%d+%% chance to cast level (%d+) (.+) on hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["%d+%% chance to cast level (%d+) (.+) on kill"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["attack with level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
["curse enemies with (%D+) on %a+"] = function(_, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill] or "Unknown", level = 1, noSupports = true }, { type = "SocketedIn" }) } end,
["curse enemies with level (%d+) (.+) on %a+"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill] or "Unknown", level = num, noSupports = true }, { type = "SocketedIn" }) } end,
["grants level (%d+) (.+)"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["casts level (%d+) (.+) when equipped"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["cast level (%d+) (.+) when you deal a critical strike"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["cast level (%d+) (.+) when hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["cast level (%d+) (.+) when you kill an enemy"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["%d+%% chance to attack with level (%d+) (.+) on melee hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["%d+%% chance to cast level (%d+) (.+) on hit"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["%d+%% chance to cast level (%d+) (.+) on kill"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["attack with level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }) } end,
["curse enemies with (%D+) on %a+"] = function(_, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill] or "Unknown", level = 1, noSupports = true }) } end,
["curse enemies with level (%d+) (.+) on %a+"] = function(num, _, skill) return { mod("ExtraSkill", "LIST", { name = gemNameLookup[skill] or "Unknown", level = num, noSupports = true }) } end,
["socketed [%a+]* ?gems a?r?e? ?supported by level (%d+) (.+)"] = function(num, _, support) return { mod("ExtraSupport", "LIST", { name = gemNameLookup[support] or gemNameLookup[support:gsub("^increased ","")] or "Unknown", level = num }, { type = "SocketedIn" }) } end,
-- Conversion
["increases and reductions to minion damage also affects? you"] = { flag("MinionDamageAppliesToPlayer") },