Module:Items

local type = type local error = error local pairs = pairs local ipairs = ipairs local require = require local string = string local table = table local mw = mw

local p = {}

-- Metadata of the item data parameters. p.param = { order = { 'image', 'name', 'kind', 'rarity', 'health', 'attack_damage', 'attack_speed', 'move_speed', 'range', 'bullet', 'knockback', 'critical_chance', 'critical_damage', 'life_steal', 'effect', 'value', 'areas', 'notes', },   name = { image = 'Image', name = 'Name', kind = 'Type', rarity = 'Rarity', health = 'Health', attack_damage = 'Attack Damage', attack_speed = 'Attack Speed', move_speed = 'Move Speed', range = 'Range', bullet = 'Bullet', knockback = 'Knockback / Pull', critical_chance = 'Critical Chance', critical_damage = 'Critical Damage', life_steal = 'Life Steal', effect = 'Effect', value = 'Sell value', areas = 'Area', notes = 'Notes', },   is_percentage = { critical_chance=true, critical_damage=true, life_steal=true, },   can_be_number = { health=true, attack_damage=true, attack_speed=true, move_speed=true, range=true, knockback=true, critical_chance=true, critical_damage=true, life_steal=true, value=true, },   can_be_string = { image=true, name=true, kind=true, rarity=true, health=true, -- noname only attack_damage=true, -- noname only attack_speed=true, -- noname only move_speed=true, -- noname only bullet=true, critical_damage=true, -- noname only life_steal=true, -- noname only effect=true, },   can_be_string_table = { areas=true, notes=true, }, }

-- Item data. p.data = { -   -- Consumables -- -   {        image = "Ancient_Rune.png", name = "Ancient Rune", kind = "Consumable", rarity = "Legendary", effect = "immediately sends all Heroes to a secret area", value = 5000, areas = {"Wildlands", "Land of the Dead", "Draco's Lair", "Dragon Nest"}, },   {        image = "Damage_Elixir.png", name = "Damage Elixir", kind = "Consumable", rarity = "Epic", effect = "doubles the attack damage for 10s", value = 10000, areas = {"Void", "Land of the Dead"}, },   {        image = "Fast_Forward.png", name = "Fast Forward", kind = "Consumable", rarity = "Uncommon", effect = "Sends all Heroes to the next floor", value = 5000, areas = {"Void"}, },   {        image = "Great_Healing.png", name = "Great Healing", kind = "Consumable", rarity = "Uncommon", effect = "Heals the Hero (100%)", value = 5000, areas = {"Land of the Dead", "King's Keep"}, },   {        image = "Haste_Elixir.png", name = "Haste Elixir", kind = "Consumable", rarity = "Uncommon", effect = "doubles the move and attack speed for 10s", value = 10000, areas = {"Wildlands", "Void"}, },   {        image = "Healing_Potion.png", name = "Healing Potion", kind = "Consumable", rarity = "Uncommon", effect = "Heals the Hero (50%)", value = 1000, areas = {"Wildlands", "Void"}, },   {        image = "Invitation_Ticket.png", name = "Invitation Ticket", kind = "Consumable", rarity = "Legendary", effect = "Boss party admission ticket", value = 50000, areas = {"Wildlands", "Void", "Land of the Dead", "King's Keep", "Exit", "Boss Rush"}, notes = {"is this a boss drop?"}, },   {        image = "Protection_Elixir.png", name = "Protection Elixir", kind = "Consumable", rarity = "Legendary", effect = "Halves damage taken for 10s", value = 10000, areas = {"King's Keep"}, },   {        image = "Resurrection.png", name = "Resurrection", kind = "Consumable", rarity = "Epic", effect = "A Heroe comes back to live", value = 5000, areas = {"Void", "Land of the Dead", "King's Keep"}, },   -    -- Weapons -- -   {        image = "Items-weapon-weapon_bardiche.png", name = "Bardiche", kind = "Weapon", rarity = "Common", attack_damage = 1500, value = 50000, areas = {"King's Keep"}, },   {        image = "Items-weapon-weapon_battleaxe.png", name = "Battle Axe", kind = "Weapon", rarity = "Common", attack_damage = 300, attack_speed = 0.2, knockback = 1, value = 2000, areas = {"Void"}, },   {        image = "Items-weapon-weapon_staff.png", name = "Black Staff", kind = "Weapon", rarity = "Legendary", attack_damage = 800, range = 3, bullet = "firesword", effect = "Deals damage over 3s (5% every 15s)", value = 200000, areas = {"King's Keep"}, notes = {"boss drop"}, },   {        image = "Items-weapon-weapon_bow.png", name = "Bow", kind = "Weapon", rarity = "Epic", attack_damage = 180, range = 3, bullet = "arrow", critical_damage = 30, value = 800, areas = {"Wildlands"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-weapon_broadaxe.png", name = "Broad Axe", kind = "Weapon", rarity = "Legendary", attack_damage = 300, knockback = 1, critical_damage = 55, value = 900, areas = {"Wildlands"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-weapon_crossbow.png", name = "Crossbow", kind = "Weapon", rarity = "Uncommon", attack_damage = 800, attack_speed = 0.75, move_speed = 0.75, range = 3, bullet = "bolt", value = 80000, areas = {"King's Keep"}, notes = {"Wielder keeps enemies at range"}, },   {        image = "Items-weapon-weapon_dagger.png", name = "Dagger", kind = "Weapon", rarity = "Uncommon", attack_damage = 50, attack_speed = 1.35, value = 700, areas = {"Wildlands"}, },   {        image = "Items-weapon-dragon_staff.png", name = "Dragon Staff", kind = "Weapon", rarity = "Legendary", attack_damage = 1800, range = 2, bullet = "fireball", effect = "Deals damage over 3s (15% every 15s)", value = 200000, areas = {"Dragon Nest"}, },   {        image = "Items-weapon-weapon_falchion.png", name = "Falchion", kind = "Weapon", rarity = "Uncommon", attack_damage = 300, attack_speed = 0.4, critical_chance = 20, value = 5000, areas = {"Void"}, },   {        image = "Items-weapon-weapon_fancy.png", name = "Fancy Staff", kind = "Weapon", rarity = "Uncommon", attack_damage = 500, range = 4, bullet = "purple", effect = "Heal (10% every 2s)", value = 20000, areas = {"Land of the Dead"}, },   {        image = "Items-weapon-weapon_flail.png", name = "Flail", kind = "Weapon", rarity = "Legendary", attack_damage = 1500, attack_speed = -0.35, knockback = 2, critical_chance = 100, value = 10000, areas = {"Void"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-weapon_gold.png", name = "Gold Staff", kind = "Weapon", rarity = "Legendary", attack_damage = 600, range = 5, bullet = "shatter", effect = "Deals damage over 3s (800 every 5s)", value = 10000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "Items-weapon-weapon_greatsword.png", name = "Greatsword", kind = "Weapon", rarity = "Common", attack_damage = 1000, value = 10000, areas = {"Land of the Dead"}, },   {        image = "Items-weapon-weapon_katana.png", name = "Katana", kind = "Weapon", rarity = "Common", attack_damage = 500, attack_speed = 0.7, critical_chance = 20, value = 10000, areas = {"Land of the Dead"}, },   {        image = "Items-weapon-weapon_longbow.png", name = "Longbow", kind = "Weapon", rarity = "Epic", attack_damage = 1000, range = 5, bullet = "arrow", knockback = 1, critical_damage = 40, value = 120000, areas = {"King's Keep"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-weapon_longsword.png", name = "Longsword", kind = "Weapon", rarity = "Uncommon", attack_damage = 150, attack_speed = 0.3, value = 700, areas = {"Wildlands"}, },   {        image = "Items-weapon-weapon_morningstar.png", name = "Mace", kind = "Weapon", rarity = "Common", attack_damage = 110, value = 500, areas = {"Wildlands"}, },   {        image = "Items-weapon-weapon_orcish.png", name = "Orcish Sword", kind = "Weapon", rarity = "Epic", attack_damage = 400, attack_speed = 0.5, life_steal = 14, value = 8000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "Items-weapon-weapon_ruby.png", name = "Ruby Staff", kind = "Weapon", rarity = "Epic", attack_damage = 400, range = 4, bullet = "fireball", knockback = -1, effect = "Deals damage over 3s (300 every 5s)", value = 8000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "Items-weapon-weapon_scythe.png", name = "Scythe", kind = "Weapon", rarity = "Epic", attack_damage = 1300, range = 1, knockback = -1, life_steal = 20, value = 40000, areas = {"Land of the Dead"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-weapon_spear.png", name = "Spear", kind = "Weapon", rarity = "Uncommon", attack_damage = 150, critical_chance = 40, value = 700, areas = {"Wildlands"}, },   {        image = "Items-weapon-weapon_teleport.png", name = "Teleport Wand", kind = "Weapon", rarity = "Epic", attack_damage = 400, range = 6, bullet = "eletric", effect = "Teleports an adjacent enemy to a random room (every 10s)", value = 8000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "Items-weapon-weapon_wrath.png", name = "Wrath of God", kind = "Weapon", rarity = "Legendary", attack_damage = 1999, knockback = 3, critical_damage = 99, life_steal = -30, value = 80000, areas = {"Land of the Dead"}, notes = {"two-handed", "boss drop"}, },   {        image = "Items-weapon-none.png", name = "noname", kind = "Weapon", rarity = "Common", health = "random", attack_damage = "random", attack_speed = "random", move_speed = "random", critical_damage = "random", life_steal = "random", value = 100000, areas = {"UNKNOWN"}, notes = {"everything random is optional except attack damage"}, },   -    -- Shields -- -   {        image = "Buckler.png", name = "Buckler", kind = "Shield", rarity = "Uncommon", health = 300, value = 500, areas = {"Wildlands"}, },   {        image = "Dragon_Shield.png", name = "Dragon Shield", kind = "Shield", rarity = "Legendary", health = 5000, value = 100000, areas = {"Draco's Lair"}, notes = {"Wielder taunts nearby enemies"}, },   {        image = "Red_Shield.png", name = "Red Shield", kind = "Shield", rarity = "Uncommon", health = 500, life_steal = 6, value = 5000, areas = {"Void"}, },   {        image = "Tower_Shield.png", name = "Tower Shield", kind = "Shield", rarity = "Epic", health = 1500, value = 120000, areas = {"King's Keep"}, },   {        image = "Unicorn_Shield.png", name = "Unicorn Shield", kind = "Shield", rarity = "Uncommon", health = 1000, move_speed = 1, critical_chance = 40, value = 80000, areas = {"King's Keep"}, },   {        image = "Windforge_Shield.png", name = "Windforge Shield", kind = "Shield", rarity = "Uncommon", health = 500, attack_speed = 0.35, move_speed = 0.4, value = 5000, areas = {"Void"}, },   ---    -- Armor -- ---   {        image = "Assassin_Robe.png", name = "Assassin Robe", kind = "Armor", rarity = "Uncommon", health = 500, attack_speed = 0.3, move_speed = 0.75, critical_chance = 10, critical_damage = 25, value = 20000, areas = {"Land of the Dead"}, },   {        image = "Chainmail.png", name = "Chainmail", kind = "Armor", rarity = "Common", health = 1500, value = 2000, areas = {"Void"}, },   {        image = "Dragon_Armor.png", name = "Dragon Armor", kind = "Armor", rarity = "Legendary", health = "5000", value = 100000, areas = {"Draco's Lair"}, },   {        image = "Emerald_Armor.png", name = "Emerald Armor", kind = "Armor", rarity = "Epic", health = 800, move_speed = 0.3, effect = "Heal (10% every 3s)", value = 120000, areas = {"King's Keep"}, notes = {"boss drop"}, },   {        image = "Hide_Shirt.png", name = "Hide Shirt", kind = "Armor", rarity = "Common", health = 150, attack_speed = 0.4, move_speed = 0.7, value = 500, areas = {"Wildlands"}, },   {        image = "Iron_Platemail.png", name = "Iron Platemail", kind = "Armor", rarity = "Uncommon", health = 3000, move_speed = -0.2, value = 20000, areas = {"Land of the Dead"}, notes = {"Wielder taunts nearby enemies"}, },   {        image = "Leather_Armor.png", name = "Leather Armor", kind = "Armor", rarity = "Epic", health = 600, move_speed = 0.4, critical_chance = 50, value = 900, areas = {"Wildlands"}, notes = {"boss drop"}, },   {        image = "Robe.png", name = "Robe", kind = "Armor", rarity = "Uncommon", health = 800, move_speed = 0.5, effect = "Heal (5% every 3.5s)", value = 5000, areas = {"Void"}, notes = {"Wearer flees when endangered"}, },   ---    -- Boots -- ---   {        image = "Assassin_Boots.png", name = "Assassin Boots", kind = "Boots", rarity = "Epic", attack_speed = 0.2, move_speed = 1, critical_damage = 15, value = 40000, areas = {"Land of the Dead"}, notes = {"boss drop"}, },   {        image = "Brown_Boots.png", name = "Brown Boots", kind = "Boots", rarity = "Common", health = 100, move_speed = 0.4, value = 500, areas = {"Wildlands"}, },   {        image = "Green_Boots.png", name = "Green Boots", kind = "Boots", rarity = "Uncommon", health = 350, move_speed = 0.65, value = 5000, areas = {"Void"}, },   {        image = "Iron_Boots.png", name = "Iron Boots", kind = "Boots", rarity = "Common", health = 1500, value = 10000, areas = {"Land of the Dead"}, },   {        image = "Stripped_Boots.png", name = "Stripped Boots", kind = "Boots", rarity = "Common", health = 350, move_speed = 2, critical_chance = 40, value = 50000, areas = {"King's Keep"}, },   -    -- Helmets .. -   {        image = "Black_Iron_Helmet.png", name = "Black Iron Helmet", kind = "Helmet", rarity = "Common", health = 1500, value = 20000, areas = {"Land of the Dead"}, },   {        image = "Iron_Helmet.png", name = "Iron Helmet", kind = "Helmet", rarity = "Epic", health = 300, value = 800, areas = {"Wildlands"}, notes = {"boss drop"}, },   {        image = "Risk_Breaker.png", name = "Risk Breaker", kind = "Helmet", rarity = "Uncommon", health = 1000, attack_speed = 0.3, move_speed = 1, value = 80000, areas = {"King's Keep"}, notes = {"Wielder taunts nearby enemies & flees when endangered"}, },   {        image = "Winged_Helm.png", name = "Winged Helm", kind = "Helmet", rarity = "Epic", health = 600, move_speed = 0.3, critical_chance = 30, value = 8000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "Wizard_Hat.png", name = "Wizard Hat", kind = "Helmet", rarity = "Common", health = 300, effect = "Heal (15% every 3s)", value = 50000, areas = {"King's Keep"}, },   -    -- Accessories -- -   {        image = "Assassin's_Bracelet.png", name = "Assassin's Bracelet", kind = "Accessory", rarity = "Common", critical_chance = 10, critical_damage = 10, value = 700, areas = {"Wildlands"}, },   {        image = "Blood_Sucker.png", name = "Blood Sucker", kind = "Accessory", rarity = "Epic", attack_speed = 0.15, life_steal = 4, value = 8000, areas = {"Void"}, notes = {"boss drop"}, },   {        image = "King's_Ring.png", name = "King's ring", kind = "Accessory", rarity = "Legendary", health = 500, move_speed = 0.4, value = 900, areas = {"Wildlands"}, notes = {"Wielder taunts nearby enemies", "boss drop"}, },   {        image = "Morning_Light.png", name = "Morning Light", kind = "Accessory", rarity = "Legendary", effect = "Heals the whole team (5% every 5s)", value = 80000, areas = {"Land of the Dead"}, notes = {"boss drop"}, },   {        image = "Phoenix_Ring.png", name = "Phoenix Ring", kind = "Accessory", rarity = "Uncommon", health = 1500, value = 20000, areas = {"Land of the Dead"}, notes = {"Wearer uses a Resurrection instead of dying"}, },   {        image = "Potion_Chugger.png", name = "Potion Chugger", kind = "Accessory", rarity = "Uncommon", health = 1000, value = 5000, areas = {"Void"}, notes = {"Wearer drinks potion when endangered"}, },   {        image = "Ring_of_Haste.png", name = "Ring of Haste", kind = "Accessory", rarity = "Common", attack_speed = 0.3, move_speed = 0.3, value = 500, areas = {"Wildlands"}, },   {        image = "Shield_Ring.png", name = "Shield ring", kind = "Accessory", rarity = "Legendary", health = 2000, knockback = -1, value = 80000, areas = {"King's Keep"}, notes = {"Wielder taunts nearby enemies", "boss drop"}, },   {        image = "Starred_Sky_Ring.png", name = "Starred Sky Ring", kind = "Accessory", rarity = "Legendary", effect = "Heals the Hero (15% every 4s)", value = 200000, areas = {"King's Keep"}, notes = {"boss drop"}, }, }

-- Matches an item against a filter. -- The filter contains parameters that must match the value (string) or must match one of it's values (table). -- @param table|nil item contains data to filter -- @param table|nil filter contains paramaters that must match -- @return the item data or nil function p._match( item, filter ) if item == nil or filter == nil then return item -- nothing to filter or not filtered end for param,values in pairs(filter) do       if type(values) == "string" then values = { values } end local is_match = false for _,value in ipairs(values) do           if type(value) ~= "string" then error(string.format("p._match: filter['%s'] contains a non-string value: type(value)='%s'", param, type(value))) end local item_values = item[param] or {} if type(item_values) == "string" then item_values = { item_values } end for _,item_value in ipairs(item_values) do               is_match = is_match or item_value == value end end if not is_match then return nil -- not a match end end return item -- a match end

-- Gets all items that match the filter function p._getItems( filter ) local items = {} for index,item in ipairs(p.data) do       if p._match(item, filter) ~= nil then table.insert(items, item) end end return items end

-- Returns a table with the arguments in the frame -- @param table frame -- @return table args function p._getArgs( frame ) local frame_args = require('Dev:Arguments').getArgs(frame) local args = {} for string_keys,string_values in pairs(frame_args) do       local keys = mw.text.split(string_keys, '%s') local values = mw.text.split(string_values, ';') local parent = args for index,key in ipairs(keys) do           if index == #keys then parent[key] = values else -- intermediate key if parent[key] == nil then parent[key] = {} elseif type(parent[key]) ~= "table" then error(string.format("p.renderWikitable: key '%s' of '%s' is not a table: type(...)=%s", key, string_keys, type(parent[key]))) end parent = parent[key] end end end return args end

-- Renders a wikitable with items. -- The optional argument 'hide' is a list of parameters we should not render. -- The optional argument 'filter' is a table with a list of values for each parameter. -- At least one value of each filter parameter must match. -- @param table args function arguments -- @return string wikitable text function p._renderWikitable( args ) local items = p._getItems(args.filter) local param_order = { 'image', 'name', 'kind', 'rarity', 'health', 'attack_damage', 'attack_speed', 'move_speed', 'range', 'bullet', 'knockback', 'critical_chance', 'critical_damage', 'life_steal', 'effect', 'value', 'areas', 'notes', }   local show_param = {} for _,item in ipairs(items) do       for param,_ in pairs(item) do            show_param[param] = true end end if args.hide then local hide_params = args.hide or "" if type(hide_params) == "string" then hide_params = { hide_params } end for _,param in ipairs(hide_params) do           show_param[param] = false end end local percent_param = { critical_chance=true, critical_damage=true, life_steal=true, }   local str = {} table.insert(str, '{| class="wikitable sortable"\n') table.insert(str, '|-\n') for _,param in ipairs(p.param.order) do       if show_param[param] then local extra = "" if p.param.can_be_number[param] then extra = 'data-sort-type="number" '; end table.insert(str, string.format('! scope="col" %s| %s\n', extra, p.param.name[param])) end end for _,item in ipairs(items) do       table.insert(str, '|-\n') for _,param in ipairs(p.param.order) do           if show_param[param] then if item[param] == nil then table.insert(str, '|\n') elseif param == "image" then table.insert(str, string.format('| \n', item[param])) elseif param == "areas" then local area_link = { ["King's Keep"] = "King's Keep", ["Draco's Lair"] = "Draco's Lair", }                   local values = {} for _,area in ipairs(item[param]) do                       if area_link[area] then table.insert(values, area_link[area]) else table.insert(values, string.format('%s', area)) end end table.insert(str, '| ' .. table.concat(values, ', ') .. '\n') elseif p.param.can_be_string_table[param] and type(item[param]) == "table" then table.insert(str, '| ' .. table.concat(item[param], ', ') .. '\n') elseif p.param.is_percentage[param] then table.insert(str, string.format('| %s%%\n', item[param])) else table.insert(str, string.format('| %s\n', item[param])) end end end end table.insert(str, "|}\n") return table.concat(str, '') end

-- Returns the number of items -- The optional argument 'hide' is a list of parameters we should not render. -- The optional argument 'filter' is a table with a list of values for each parameter. -- At least one value of each filter parameter must match. -- @param table args function arguments -- @return string wikitable text function p._countItems( args ) local items = p._getItems(args.filter) return #items end

--- -- Wikitext interface, called with the following text: -- ---

-- Renders a wikitable with items. -- Arguments: -- 'hide= ; ; ' generates a list of parameters we should not render. -- 'filter = ; ; ' generate a table with a list of values for each parameter. -- At least one value of each filter parameter must match. -- @return string wikitable text function p.renderWikitable( frame ) local args = p._getArgs(frame) return p._renderWikitable(args) end

-- Renders the number of items. -- Arguments: -- 'hide= ; ; ' generates a list of parameters we should not render. -- 'filter = ; ; ' generate a table with a list of values for each parameter. -- At least one value of each filter parameter must match. -- @return string wikitable text function p.countItems( frame ) local args = p._getArgs(frame) return p._countItems(args) end

-- Validates the item data. -- This function will either throw an error or return nothing. function p.validate( frame ) if type(p.data) ~= "table" then error("p.validate: p.data is not a table") end for index, item in ipairs(p.data) do       if type(item) ~= "table" then error(string.format("p.validate: item '%d' is not a table", index)) end for k,v in pairs(item) do           local is_valid = false if p.param.can_be_number[k] then is_valid = is_valid or (type(v) == "number") end if p.param.can_be_string[k] then is_valid = is_valid or (type(v) == "string") end if p.param.can_be_string_table[k] and type(v) == "table" then is_valid = true for _,area in ipairs(v) do                   if type(area) ~= "string" then is_valid = false end end end if not is_valid then error(string.format("p.validate: item '%d' has invalid data: k='%s' type(v)='%s'", index, k, type(v))) end end if item.name == nil then error(string.format("p.validate: item '%d' is missing a name", index)) end end end

return p