Module:Attacks

The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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