Module:Attacks: Difference between revisions
From Melvor Idle
(Initial creation) |
(Support Lifesteal) |
||
(9 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
local p = {} | local p = {} | ||
local | local GameData = require('Module:GameData') | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
p.effectDefinition = { | p.effectDefinition = { | ||
["Burn"] = { | |||
{ | |||
["effectType"] = 'Burn' | |||
}, | |||
-- Alternative definition specifically for familiars like Dragon | |||
{ | |||
["type"] = 'Modifier', | |||
["subtype"] = 'Familiar', | |||
["modifiers"] = { | |||
["include"] = { 'increasedChanceToApplyBurn' } | |||
} | |||
} | |||
}, | |||
["Poison"] = { | |||
["effectType"] = 'Poison' | |||
}, | |||
["Deadly Poison"] = { | |||
["effectType"] = 'DeadlyPoison' | |||
}, | |||
["Slow"] = { | |||
["type"] = 'Modifier', | |||
["modifiers"] = { | |||
["include"] = { 'increasedAttackIntervalPercent' }, | |||
["exclude"] = { 'increasedFrostburn' } | |||
} | |||
}, | |||
["Bleed"] = { | |||
["type"] = 'DOT', | |||
["subtype"] = 'Bleed' | |||
}, | |||
["Frostburn"] = { | |||
["effectType"] = 'Frostburn' | |||
}, | |||
["Mark of Death"] = { | |||
["type"] = 'Stacking', | |||
["modifiers"] = { | |||
["include"] = { 'decreasedDamageReductionPercent' } | |||
} | |||
}, | |||
["Affliction"] = { | |||
{ ["effectType"] = 'Affliction' }, | |||
{ | |||
["type"] = 'Modifier', | |||
["modifiers"] = { | |||
["include"] = { 'increasedAfflictionChance' } | |||
} | |||
} | |||
}, | |||
["Sleep"] = { | |||
{ | |||
["type"] = 'Sleep' | |||
}, | |||
-- Alternative definition specifically for familiars like Siren | |||
{ | |||
["type"] = 'Modifier', | |||
["subtype"] = 'Familiar', | |||
["modifiers"] = { | |||
["include"] = { 'increasedGlobalSleepChance' } | |||
} | |||
} | |||
}, | |||
["Slow"] = { | |||
["effectType"] = 'Slow' | |||
}, | |||
["Freeze"] = { | |||
["type"] = 'Stun', | |||
["flavour"] = 'Freeze' | |||
}, | |||
["Stun"] = { | |||
["type"] = 'Stun', | |||
["flavour"] = 'Stun' | |||
}, | |||
["Regen"] = { | |||
["type"] = 'DOT', | |||
["subtype"] = 'Regen' | |||
}, | |||
["Lifesteal"] = { | |||
["attFunc"] = function(attack) | |||
return type(attack.lifesteal) == 'number' and attack.lifesteal > 0 | |||
end | |||
} | |||
-- TODO Implement curses | |||
} | } | ||
function p.getAttackByID(ID) | function p.getAttackByID(ID) | ||
return | return GameData.getEntityByID('attacks', ID) | ||
end | end | ||
function p.getAttack(name) | function p.getAttack(name) | ||
name = string.gsub(name, "%%27", "'") | |||
name = string.gsub(name, "'", "'") | |||
name = string.gsub(name, "'", "'") | |||
return GameData.getEntityByName('attacks', name) | |||
end | end | ||
function p.getAttacks(checkFunc) | function p.getAttacks(checkFunc) | ||
return GameData.getEntities('attacks', checkFunc) | |||
end | end | ||
function p.getAttackEffects(attack) | function p.getAttackEffects(attack) | ||
local attackEffects = {} | |||
for effectName, effectDefn in pairs(p.effectDefinition) do | |||
if p.attackHasEffect(attack, effectDefn) then | |||
table.insert(attackEffects, effectName) | |||
end | |||
end | |||
return attackEffects | |||
end | |||
-- Convert effect definition into list of definitions to be checked | |||
function p.getEffectDefnList(effectDefn) | |||
-- Some effects (e.g. Burn) have multiple definitions, so handle these correctly | |||
if type(effectDefn[1]) == 'table' then | |||
-- Definition is actually multiple definitions | |||
return effectDefn | |||
else | |||
-- Definition is singular, wrap it within a table so we can iterate | |||
return { effectDefn } | |||
end | |||
end | end | ||
-- Determines if attack applies the effect defined in effectDefinition | -- Determines if attack applies the effect defined in effectDefinition | ||
function p.attackHasEffect(attack, | function p.attackHasEffect(attack, effectDefnRaw) | ||
if type(attack) == 'table' and type(effectDefnRaw) == 'table' then | |||
local effectDefnList = p.getEffectDefnList(effectDefnRaw) | |||
for i, effectDefn in pairs(effectDefnList) do | |||
if effectDefn.attFunc ~= nil then | |||
-- Attack level check, for effects like lifesteal | |||
if effectDefn.attFunc(attack) then | |||
return true | |||
end | |||
else | |||
-- Process pre-hit effects | |||
for i, effect in ipairs(attack.prehitEffects) do | |||
if p.effectMatchesDefn(effect, effectDefn) then | |||
return true | |||
end | |||
end | |||
-- Process on hit effects | |||
for i, effect in ipairs(attack.onhitEffects) do | |||
if p.effectMatchesDefn(effect, effectDefn) then | |||
return true | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return false | |||
end | end | ||
function p.effectMatchesDefn(effect, effectDefn) | function p.effectMatchesDefn(effect, effectDefn) | ||
if effectDefn.type ~= effect.type then | |||
-- Effect's type doesn't match that of the effect definition | |||
return false | |||
elseif (effectDefn.effectType ~= nil and (effect.effectType == nil or effect.effectType ~= effectDefn.effectType)) then | |||
return false | |||
elseif (effectDefn.subtype ~= nil and (effect.subtype == nil or effect.subtype ~= effectDefn.subtype)) | |||
or (effectDefn.flavour ~= nil and (effect.flavour == nil or effect.flavour ~= effectDefn.flavour)) then | |||
-- Effect's subtype or flavour doesn't match that of the effect definition | |||
return false | |||
elseif type(effectDefn.modifiers) == 'table' and (effectDefn.modifiers.include ~= nil or effectDefn.modifiers.exclude ~= nil) then | |||
-- Definition contains modifiers which need to be checked | |||
local modsIncl, modsExcl = (effectDefn.modifiers.include or {}), (effectDefn.modifiers.exclude or {}) | |||
local modsInclFound = {} | |||
if Shared.tableCount(modsIncl) > 0 and (type(effect.modifiers) ~= 'table' or Shared.tableCount(effect.modifiers) < Shared.tableCount(modsIncl)) then | |||
-- Definition has 1+ included modifiers but effect has fewer modifiers than the definition | |||
return false | |||
end | |||
for modName, modVal in pairs(effect.modifiers) do | |||
if Shared.contains(modsExcl, modName, false) then | |||
-- Effect contains a modifier on the exclusion list | |||
return false | |||
elseif Shared.contains(modsIncl, modName, false) then | |||
-- Flag included modifier as found | |||
modsInclFound[modName] = true | |||
end | |||
end | |||
if Shared.tableCount(modsInclFound) < Shared.tableCount(modsIncl) then | |||
-- Effect doesn't have all of the included modifiers | |||
return false | |||
end | |||
end | |||
return true | |||
end | end | ||
return p | return p |
Latest revision as of 00:24, 6 March 2024
Documentation for this module may be created at Module:Attacks/doc
-- For tables & other outputs generated with attack data, see: Module:Attacks/Tables
local p = {}
local GameData = require('Module:GameData')
local Shared = require('Module:Shared')
p.effectDefinition = {
["Burn"] = {
{
["effectType"] = 'Burn'
},
-- Alternative definition specifically for familiars like Dragon
{
["type"] = 'Modifier',
["subtype"] = 'Familiar',
["modifiers"] = {
["include"] = { 'increasedChanceToApplyBurn' }
}
}
},
["Poison"] = {
["effectType"] = 'Poison'
},
["Deadly Poison"] = {
["effectType"] = 'DeadlyPoison'
},
["Slow"] = {
["type"] = 'Modifier',
["modifiers"] = {
["include"] = { 'increasedAttackIntervalPercent' },
["exclude"] = { 'increasedFrostburn' }
}
},
["Bleed"] = {
["type"] = 'DOT',
["subtype"] = 'Bleed'
},
["Frostburn"] = {
["effectType"] = 'Frostburn'
},
["Mark of Death"] = {
["type"] = 'Stacking',
["modifiers"] = {
["include"] = { 'decreasedDamageReductionPercent' }
}
},
["Affliction"] = {
{ ["effectType"] = 'Affliction' },
{
["type"] = 'Modifier',
["modifiers"] = {
["include"] = { 'increasedAfflictionChance' }
}
}
},
["Sleep"] = {
{
["type"] = 'Sleep'
},
-- Alternative definition specifically for familiars like Siren
{
["type"] = 'Modifier',
["subtype"] = 'Familiar',
["modifiers"] = {
["include"] = { 'increasedGlobalSleepChance' }
}
}
},
["Slow"] = {
["effectType"] = 'Slow'
},
["Freeze"] = {
["type"] = 'Stun',
["flavour"] = 'Freeze'
},
["Stun"] = {
["type"] = 'Stun',
["flavour"] = 'Stun'
},
["Regen"] = {
["type"] = 'DOT',
["subtype"] = 'Regen'
},
["Lifesteal"] = {
["attFunc"] = function(attack)
return type(attack.lifesteal) == 'number' and attack.lifesteal > 0
end
}
-- TODO Implement curses
}
function p.getAttackByID(ID)
return GameData.getEntityByID('attacks', ID)
end
function p.getAttack(name)
name = string.gsub(name, "%%27", "'")
name = string.gsub(name, "'", "'")
name = string.gsub(name, "'", "'")
return GameData.getEntityByName('attacks', name)
end
function p.getAttacks(checkFunc)
return GameData.getEntities('attacks', checkFunc)
end
function p.getAttackEffects(attack)
local attackEffects = {}
for effectName, effectDefn in pairs(p.effectDefinition) do
if p.attackHasEffect(attack, effectDefn) then
table.insert(attackEffects, effectName)
end
end
return attackEffects
end
-- Convert effect definition into list of definitions to be checked
function p.getEffectDefnList(effectDefn)
-- Some effects (e.g. Burn) have multiple definitions, so handle these correctly
if type(effectDefn[1]) == 'table' then
-- Definition is actually multiple definitions
return effectDefn
else
-- Definition is singular, wrap it within a table so we can iterate
return { effectDefn }
end
end
-- Determines if attack applies the effect defined in effectDefinition
function p.attackHasEffect(attack, effectDefnRaw)
if type(attack) == 'table' and type(effectDefnRaw) == 'table' then
local effectDefnList = p.getEffectDefnList(effectDefnRaw)
for i, effectDefn in pairs(effectDefnList) do
if effectDefn.attFunc ~= nil then
-- Attack level check, for effects like lifesteal
if effectDefn.attFunc(attack) then
return true
end
else
-- Process pre-hit effects
for i, effect in ipairs(attack.prehitEffects) do
if p.effectMatchesDefn(effect, effectDefn) then
return true
end
end
-- Process on hit effects
for i, effect in ipairs(attack.onhitEffects) do
if p.effectMatchesDefn(effect, effectDefn) then
return true
end
end
end
end
end
return false
end
function p.effectMatchesDefn(effect, effectDefn)
if effectDefn.type ~= effect.type then
-- Effect's type doesn't match that of the effect definition
return false
elseif (effectDefn.effectType ~= nil and (effect.effectType == nil or effect.effectType ~= effectDefn.effectType)) then
return false
elseif (effectDefn.subtype ~= nil and (effect.subtype == nil or effect.subtype ~= effectDefn.subtype))
or (effectDefn.flavour ~= nil and (effect.flavour == nil or effect.flavour ~= effectDefn.flavour)) then
-- Effect's subtype or flavour doesn't match that of the effect definition
return false
elseif type(effectDefn.modifiers) == 'table' and (effectDefn.modifiers.include ~= nil or effectDefn.modifiers.exclude ~= nil) then
-- Definition contains modifiers which need to be checked
local modsIncl, modsExcl = (effectDefn.modifiers.include or {}), (effectDefn.modifiers.exclude or {})
local modsInclFound = {}
if Shared.tableCount(modsIncl) > 0 and (type(effect.modifiers) ~= 'table' or Shared.tableCount(effect.modifiers) < Shared.tableCount(modsIncl)) then
-- Definition has 1+ included modifiers but effect has fewer modifiers than the definition
return false
end
for modName, modVal in pairs(effect.modifiers) do
if Shared.contains(modsExcl, modName, false) then
-- Effect contains a modifier on the exclusion list
return false
elseif Shared.contains(modsIncl, modName, false) then
-- Flag included modifier as found
modsInclFound[modName] = true
end
end
if Shared.tableCount(modsInclFound) < Shared.tableCount(modsIncl) then
-- Effect doesn't have all of the included modifiers
return false
end
end
return true
end
return p