|  |   | 
| (4 intermediate revisions by 2 users not shown) | 
| Line 1: | Line 1: | 
|  | -- Cargo reworked item module |  | -- This is not an actual module. Do not add code. | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- TODO
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Items
 |  | 
|  | -- -----
 |  | 
|  | -- Check if abyss jewels actually have radius modifiers
 |  | 
|  | -- 
 |  | 
|  | -- Aggregate ids from skill id from modifiers
 |  | 
|  | -- 
 |  | 
|  | -- DROP restriction improvements:
 |  | 
|  | --  drop monster type(s)
 |  | 
|  | --
 |  | 
|  | -- unique items:
 |  | 
|  | --  3D art
 |  | 
|  | --  supporter attribution
 |  | 
|  | --
 |  | 
|  | -- Maps: 
 |  | 
|  | --  Area level can be retrieved eventually
 |  | 
|  | --
 |  | 
|  | -- Essence: 
 |  | 
|  | --  type column
 |  | 
|  | --  monster modifier info
 |  | 
|  | --
 |  | 
|  | -- random modifier
 |  | 
|  | -- -> sell price must consider random mods
 |  | 
|  | --
 |  | 
|  | -- prophecy base item -> i18n
 |  | 
|  | -- ----------
 |  | 
|  | -- Item class
 |  | 
|  | -- ----------
 |  | 
|  | --
 |  | 
|  | -- remove the ul if name_list is not provided
 |  | 
|  | -- maybe smw
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Imports
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | local xtable = require('Module:Table')
 |  | 
|  | local m_util = require('Module:Util')
 |  | 
|  | local getArgs = require('Module:Arguments').getArgs
 |  | 
|  | local m_game = require('Module:Game')
 |  | 
|  | local m_skill = require('Module:Skill')
 |  | 
|  | local m_area = require('Module:Area')
 |  | 
|  | local m_cargo = require('Module:Cargo')
 |  | 
|  | local f_item_link = require('Module:Item link').item_link
 |  | 
|  |   |  | 
|  | local p = {}
 |  | 
|  | local c = {}
 |  | 
|  | c.image_size = 39
 |  | 
|  | c.image_size_full = c.image_size * 2
 |  | 
|  | c.query_default = 50
 |  | 
|  | c.query_max = 200
 |  | 
|  |   |  | 
|  | c.lang = mw.language.new('en')
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Strings
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- This section contains strings used by this module.
 |  | 
|  | -- Add new strings here instead of in-code directly, this will help other
 |  | 
|  | -- people to correct spelling mistakes easier and help with translation to
 |  | 
|  | -- other PoE wikis.
 |  | 
|  | --
 |  | 
|  | -- TODO: Maybe move this out to a separate sub-page module
 |  | 
|  | local i18n = {
 |  | 
|  |     inventory_icon = 'File:%s inventory icon.png',
 |  | 
|  |     status_icon = 'File:%s status icon.png',
 |  | 
|  |     divination_card_art = 'File:%s card art.png',
 |  | 
|  |     gem_tag_category = '[[:Category:%s (gem tag)|%s]]',
 |  | 
|  |   |  | 
|  |     categories = {
 |  | 
|  |         -- maintenance cats
 |  | 
|  |         improper_modifiers = 'Items with improper modifiers',
 |  | 
|  |         missing_release_version = 'Items without a release version',
 |  | 
|  |         broken_upgraded_from_reference = 'Items with broken item references in upgraded from parameters', 
 |  | 
|  |         duplicate_query_area_ids = 'Items with duplicate area ids from queries',
 |  | 
|  |         base_items = 'Base items',
 |  | 
|  |         derived_items = 'Derived items',
 |  | 
|  |         sell_prices_override = 'Items with sell prices overrides',
 |  | 
|  |   |  | 
|  |         -- regular cats
 |  | 
|  |         alternate_artwork = 'Items with alternate artwork',
 |  | 
|  |   |  | 
|  |         -- misc
 |  | 
|  |         gem_tag_affix = '%s (gem tag)',
 |  | 
|  |         unique_affix = 'Unique %s',
 |  | 
|  |         
 |  | 
|  |         prophecies = 'Prophecies',
 |  | 
|  |         talismans = 'Talismans',
 |  | 
|  |         essences = 'Essences',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |     stat_skip_patterns = {
 |  | 
|  |         maps = {
 |  | 
|  |             '%d+%% increased Quantity of Items found in this Area',
 |  | 
|  |             '%d+%% increased Rarity of Items found in this Area',
 |  | 
|  |             '%+%d+%% Monster pack size',
 |  | 
|  |             -- ranges
 |  | 
|  |             '%(%d+%-%d+%)%% increased Quantity of Items found in this Area',
 |  | 
|  |             '%(%d+%-%d+%)%% increased Rarity of Items found in this Area',
 |  | 
|  |             '%+%(%d+%-%d+%)%% Monster pack size',
 |  | 
|  |         },
 |  | 
|  |         jewels = {
 |  | 
|  |             'Limited to %d+ %(Hidden%)',
 |  | 
|  |             'Jewel has a radius of %d+ %(Hidden%)',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |   |  | 
|  |     help_text_defaults = {
 |  | 
|  |         active_gem = 'Place into an item socket of the right colour to gain this skill. Right click to remove from a socket.',
 |  | 
|  |         support_gem = 'This isa Support Gem. It does notgrant a bonus to your character, but skills in sockets connected to it. Place into anitem socket connected to a socket containing the Active Skill Gem you wish to augment. Right click to remove from a socket.',
 |  | 
|  |         hideout_doodad = 'Right click on this item then left click on a location on the ground to create the object.',
 |  | 
|  |         jewel = 'Place into an allocated Jewel Socket on the Passive Skill Tree. Right click to remove from the Socket.',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |     -- Used by the item info box
 |  | 
|  |     tooltips = {
 |  | 
|  |         corrupted = 'Corrupted',
 |  | 
|  |         support_icon = 'Icon: %s',
 |  | 
|  |         radius = 'Radius: %s',
 |  | 
|  |         mana_reserved = 'Mana Reserved: %s',
 |  | 
|  |         mana_cost = 'Mana Cost: %s',
 |  | 
|  |         mana_multiplier = 'Mana Multiplier: %s',
 |  | 
|  |         vaal_souls_per_use = 'Souls Per Use: %s',
 |  | 
|  |         stored_uses = 'Can Store %s Use(s)',
 |  | 
|  |         vaal_soul_gain_prevention_time = 'Soul Gain Prevention: %s',
 |  | 
|  |         cooldown_time = 'Cooldown Time: %s',
 |  | 
|  |         cast_time = 'Cast Time: %s',
 |  | 
|  |         critical_strike_chance = 'Critical Strike Chance: %s',
 |  | 
|  |         damage_effectiveness = 'Damage Effectiveness: %s',
 |  | 
|  |         projectile_speed = 'Projectile Speed: %s',
 |  | 
|  |         quality = 'Quality: %s',
 |  | 
|  |         physical_damage = 'Physical Damage: %s',
 |  | 
|  |         elemental_damage = 'Elemental Damage:%s',
 |  | 
|  |         chaos_damage = 'Chaos Damage: %s',
 |  | 
|  |         attacks_per_second = 'Attacks per Second: %s',
 |  | 
|  |         weapon_range = 'Weapon Range: %s',
 |  | 
|  |         map_level = 'Map Level: %s',
 |  | 
|  |         map_tier = 'Map Tier: %s',
 |  | 
|  |         map_guild_character = m_util.html.abbr('Guild Character', 'When used in guild creation, this map can be used for the listed character') .. ': %s',
 |  | 
|  |         item_quantity = 'Item Quantity: %s',
 |  | 
|  |         item_rarity = 'Item Rarity: %s',
 |  | 
|  |         monster_pack_size = 'Monster Pack Size: %s',
 |  | 
|  |         limited_to = 'Limited to: %s',
 |  | 
|  |         flask_mana_recovery = 'Recovers %s Mana over %s seconds',
 |  | 
|  |         flask_life_recovery = 'Recovers %s Life over %s seconds',
 |  | 
|  |         flask_duration = 'Lasts %s Seconds',
 |  | 
|  |         flask_charges_per_use = 'Consumes %s of %s Charges on use',
 |  | 
|  |         chance_to_block = 'Chance to Block: %s',
 |  | 
|  |         armour = 'Armour: %s',
 |  | 
|  |         evasion = 'Evasion: %s',
 |  | 
|  |         energy_shield = 'Energy Shield: %s',
 |  | 
|  |         talisman_tier = 'Talisman Tier: %s',
 |  | 
|  |         stack_size = 'Stack Size: %s',
 |  | 
|  |         essence_level = 'Essence Level: %s',
 |  | 
|  |         requires = 'Requires %s',
 |  | 
|  |         level_inline = 'Level %s',
 |  | 
|  |         level = 'Level: %s',
 |  | 
|  |         gem_quality = 'Per 1% Quality:',
 |  | 
|  |         variation_singular = 'Variation',
 |  | 
|  |         variation_plural = 'Variations',
 |  | 
|  |         favour_cost = 'Favour cost: %s',
 |  | 
|  |         seal_cost = 'Seal Cost: <br>%s',
 |  | 
|  |         cannot_be_traded_or_modified = 'Cannot be traded or modified',
 |  | 
|  |         
 |  | 
|  |         item_class_map = {
 |  | 
|  |             ['Staff'] = 'Staff',
 |  | 
|  |             ['Bow'] = 'Bow',
 |  | 
|  |             ['Wand'] = 'Wand',
 |  | 
|  |             ['Two Hand Axe'] = 'Two Handed Axe',
 |  | 
|  |             ['Two Hand Sword'] = 'Two Hand Sword',
 |  | 
|  |             ['Two Hand Mace'] = 'Two Hand Mace',
 |  | 
|  |             ['Sceptre'] = 'One Handed Mace',
 |  | 
|  |             ['One Hand Mace'] = 'One Handed Mace',
 |  | 
|  |             ['One Hand Axe'] = 'One Handed Axe',
 |  | 
|  |             ['One Hand Sword'] = 'One Handed Sword',
 |  | 
|  |             ['Thrusting One Hand Sword'] = 'One Handed Sword',
 |  | 
|  |             ['Claw'] = 'Claw',
 |  | 
|  |             ['Dagger'] = 'Dagger',
 |  | 
|  |             ['FishingRod'] = 'Fishing Rod',
 |  | 
|  |         },
 |  | 
|  |         
 |  | 
|  |         random_mod = '<<random modifier %s>>',
 |  | 
|  |         
 |  | 
|  |         --
 |  | 
|  |         -- secondary infobox
 |  | 
|  |         --
 |  | 
|  |         drop_restrictions = 'Acquisition',
 |  | 
|  |         league_restriction = m_util.html.abbr('League(s):', 'Item can be obtained in relation to these league(s)') .. ' %s',
 |  | 
|  |         drop_disabled = 'DROP DISABLED',
 |  | 
|  |         
 |  | 
|  |         purchase_costs = m_util.html.abbr('Purchase Costs', 'Cost of purchasing an item of this type at NPC vendors. This does not indicate whether NPCs actually sell the item.'),
 |  | 
|  |         sell_price = m_util.html.abbr('Sell Price', 'Items or currency received when selling this item at NPC vendors. Certain vendor recipes may override this value.'),
 |  | 
|  |         
 |  | 
|  |         damage_per_second = 'Weapon DPS',
 |  | 
|  |         physical_dps = 'Physical',
 |  | 
|  |         fire_dps = 'Fire',
 |  | 
|  |         cold_dps = 'Cold',
 |  | 
|  |         lightning_dps = 'Lightning',
 |  | 
|  |         chaos_dps = 'Chaos',
 |  | 
|  |         elemental_dps = 'Elemental',
 |  | 
|  |         poison_dps = 'Phys+Chaos',
 |  | 
|  |         dps = 'Total',
 |  | 
|  |         
 |  | 
|  |         misc = 'Miscellaneous',
 |  | 
|  |         item_class = 'Item class: %s',
 |  | 
|  |         metadata_id = 'Metadata ID: %s',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |     item_class_infobox = {
 |  | 
|  |         page = '[[Item class]]',
 |  | 
|  |         info = m_util.html.abbr('(?)', 'Item classes categorize items. Classes are often used to restrict items or skill gems to a specific class or by item filters'),
 |  | 
|  |         also_referred_to_as = 'Also referred to as:',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |     debug = {
 |  | 
|  |         base_item_field_not_found = 'Base item property not found: %s.%s',
 |  | 
|  |         field_value_mismatch = 'Value for argument "%s" is set to something else then default: %s',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  |     errors = {
 |  | 
|  |         missing_base_item = 'Rarity is set to above normal, but base item is not set. A base item for rarities above normal is required!',
 |  | 
|  |         missing_rarity = 'Base item parameter is set, but rarity is set to normal. A rarity above normal is required!',
 |  | 
|  |         missing_amount = 'Item amount is missing or not a number (%s)',
 |  | 
|  |         upgraded_from_broken_reference = 'Item reference in %s is broken (results: %s)',
 |  | 
|  |         duplicate_base_items = 'More then one result found for the specified base item. Consider using base_item_page or base_item_id to narrow down the results.',
 |  | 
|  |         base_item_not_found = 'Base item could not be found in the database. Check for spelling mistakes or whether the base item page exists on the wiki. If the base item page exists on the wiki, but it can not be found please null-edit the page.',
 |  | 
|  |         invalid_league = '%s is not a recognized league',
 |  | 
|  |         invalid_tag = '%s is not a valid tag',
 |  | 
|  |         generic_argument_parameter = 'Unrecognized %s parameter "%s"',
 |  | 
|  |         invalid_item_class = 'Invalid item class',
 |  | 
|  |         non_unique_relic = 'Only unique items can be be relics',
 |  | 
|  |         duplicate_area_id_from_query = 'Query found duplicate area ids that do notneed to be set on the item. Duplicate ids: "%s"',
 |  | 
|  |         duplicate_metadata = 'Duplicate metadata id "%s" on page "%s"',
 |  | 
|  |         invalid_class = 'The item class name "%s" is invalid.',
 |  | 
|  |         invalid_class_id = 'The item class id "%s" is invalid. Using the correct item class id is required for the template to work correctly.',
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Other stuff
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | local h = {}
 |  | 
|  |   |  | 
|  | function h.debug(tpl_args, func)
 |  | 
|  |     if tpl_args.debug ==  nil then
 |  | 
|  |         return
 |  | 
|  |     end
 |  | 
|  |     func()
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function h.na_or_val(tr, value, func)
 |  | 
|  |     if value == nil then
 |  | 
|  |         tr:wikitext(m_util.html.td.na())
 |  | 
|  |     else
 |  | 
|  |         local raw_value = value
 |  | 
|  |         if func ~= nil then
 |  | 
|  |             value = func(value)
 |  | 
|  |         end
 |  | 
|  |         tr
 |  | 
|  |             :tag('td')
 |  | 
|  |                 :attr('data-sort-value', raw_value)
 |  | 
|  |                 :wikitext(value)
 |  | 
|  |                 :done()
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- helper to loop over the range variables easier
 |  | 
|  | h.range_map = {
 |  | 
|  |     min = {
 |  | 
|  |         var = '_range_minimum',
 |  | 
|  |     },
 |  | 
|  |     max = {
 |  | 
|  |         var = '_range_maximum',
 |  | 
|  |     },
 |  | 
|  |     avg = {
 |  | 
|  |         var = '_range_average',
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | h.range_fields = {
 |  | 
|  |     {
 |  | 
|  |         field = '_range_minimum',
 |  | 
|  |         type = nil,
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         field = '_range_maximum',
 |  | 
|  |         type = nil,
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         field = '_range_average',
 |  | 
|  |         type = nil,
 |  | 
|  |     },
 |  | 
|  |         {
 |  | 
|  |         field = '_range_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         field = '_range_colour',
 |  | 
|  |         type = 'String',
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         field = '_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |     },
 |  | 
|  |   |  | 
|  | }
 |  | 
|  |   |  | 
|  | function h.handle_range_args(tpl_args, frame, argument_key, field, value, fmt_options)
 |  | 
|  |     fmt_options = mw.clone(fmt_options)
 |  | 
|  |     fmt_options.return_color = true
 |  | 
|  |     local html, colour = m_util.html.format_value(tpl_args, frame, value, fmt_options)
 |  | 
|  |     tpl_args[argument_key .. '_html'] = html
 |  | 
|  |     tpl_args[field .. '_html'] = html
 |  | 
|  |     tpl_args[field .. '_range_colour'] = colour
 |  | 
|  |     
 |  | 
|  |     fmt_options = mw.clone(fmt_options)
 |  | 
|  |     fmt_options.no_color = true
 |  | 
|  |     tpl_args[field .. '_range_text'] = m_util.html.format_value(tpl_args, frame, value, fmt_options)
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function h.stats_update(tpl_args, id, value, modid, key)
 |  | 
|  |     if tpl_args[key][id] == nil then
 |  | 
|  |         tpl_args[key][id] = {
 |  | 
|  |             references = {modid},
 |  | 
|  |             min = value.min,
 |  | 
|  |             max = value.max,
 |  | 
|  |             avg = value.avg,
 |  | 
|  |         }
 |  | 
|  |     else
 |  | 
|  |         if modid ~= nil then
 |  | 
|  |             table.insert(tpl_args[key][id].references, modid)
 |  | 
|  |         end
 |  | 
|  |         tpl_args[key][id].min = tpl_args[key][id].min + value.min
 |  | 
|  |         tpl_args[key][id].max = tpl_args[key][id].max + value.max
 |  | 
|  |         tpl_args[key][id].avg = tpl_args[key][id].avg + value.avg
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | h.stat = {}
 |  | 
|  | function h.stat.add(value, stat_cached) 
 |  | 
|  |     value.min = value.min + stat_cached.min
 |  | 
|  |     value.max = value.max + stat_cached.max
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function h.stat.more (value, stat_cached)
 |  | 
|  |     value.min = value.min * (1 + stat_cached.min / 100)
 |  | 
|  |     value.max = value.max * (1 + stat_cached.max / 100)
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function h.stat.more_inverse (value, stat_cached)
 |  | 
|  |     value.min = value.min / (1 + stat_cached.min / 100)
 |  | 
|  |     value.max = value.max / (1 + stat_cached.max / 100)
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- core
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | local core = {}
 |  | 
|  |   |  | 
|  | function core.build_cargo_data(tpl_args, frame)
 |  | 
|  |     for _, table_name in ipairs(core.item_classes[tpl_args.class_id].tables) do
 |  | 
|  |         for k, field_data in pairs(core.cargo[table_name].fields) do
 |  | 
|  |             field_data.table = table_name
 |  | 
|  |             for _, stat_data in pairs(core.stat_map) do
 |  | 
|  |                 if stat_data.field == k then
 |  | 
|  |                     for _, range_field in ipairs(h.range_fields) do
 |  | 
|  |                         local field_name = stat_data.field .. range_field.field
 |  | 
|  |                         
 |  | 
|  |                         local data = {
 |  | 
|  |                             no_copy = true,
 |  | 
|  |                             table = table_name,
 |  | 
|  |                             field = field_name,
 |  | 
|  |                             -- if the type is nil, use the parent type
 |  | 
|  |                             -- this is set integer/float values correctly
 |  | 
|  |                             type = range_field.type or field_data.type,
 |  | 
|  |                         }
 |  | 
|  |                         
 |  | 
|  |                         core.cargo[table_name].fields[field_name] = data
 |  | 
|  |                         core.map[field_name] = data
 |  | 
|  |                     end
 |  | 
|  |                     break
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             if table_name == 'weapons' then
 |  | 
|  |                 for _, dps_data in ipairs(core.dps_map) do
 |  | 
|  |                     for _, range_field in ipairs(h.range_fields) do
 |  | 
|  |                         local field_name = dps_data.field .. range_field.field
 |  | 
|  |                         
 |  | 
|  |                         local data = {
 |  | 
|  |                             no_copy = true,
 |  | 
|  |                             table = table_name,
 |  | 
|  |                             field = field_name,
 |  | 
|  |                             -- dps values are floating points
 |  | 
|  |                             type = range_field.type or 'Float',
 |  | 
|  |                         }
 |  | 
|  |                     
 |  | 
|  |                         core.cargo[table_name].fields[field_name] = data
 |  | 
|  |                         core.map[field_name] = data
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.validate_mod(tpl_args, frame, args)
 |  | 
|  |     -- args:
 |  | 
|  |     --  key   - implict or explicit
 |  | 
|  |     --  i
 |  | 
|  |     --  value
 |  | 
|  |     local prefix = args.key .. args.i
 |  | 
|  |     local value = tpl_args[prefix]
 |  | 
|  |     local out = {
 |  | 
|  |         result=nil,
 |  | 
|  |         -- commited to cargo at a later point
 |  | 
|  |         id=nil,
 |  | 
|  |         stat_text=nil,
 |  | 
|  |         is_implicit=(args.key == 'implicit'),
 |  | 
|  |         is_random=nil,
 |  | 
|  |     }
 |  | 
|  |     
 |  | 
|  |     if value ~= nil then
 |  | 
|  |         out.id = value
 |  | 
|  |         out.stat_text = tpl_args[prefix .. '_text']
 |  | 
|  |         out.is_random = false
 |  | 
|  |         table.insert(tpl_args._mods, out)
 |  | 
|  |         
 |  | 
|  |         return true
 |  | 
|  |     elseif tpl_args[prefix .. '_random_list'] then
 |  | 
|  |         tpl_args._flags.random_mods = true
 |  | 
|  |         value = m_util.string.split(tpl_args[prefix ..  '_random_list'], ',%s*')
 |  | 
|  |         for _, mod_id in ipairs(value) do
 |  | 
|  |             table.insert(tpl_args._mods, {
 |  | 
|  |                 result = nil,
 |  | 
|  |                 id = mod_id,
 |  | 
|  |                 stat_text = tpl_args[prefix .. '_text'] or string.format(i18n.tooltips.random_mod, args.i),
 |  | 
|  |                 is_implicit = (args.key == 'implicit'),
 |  | 
|  |                 is_random = true,
 |  | 
|  |             })
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         return true
 |  | 
|  |     elseif tpl_args[prefix .. '_text'] then
 |  | 
|  |         value = tpl_args[prefix .. '_text']
 |  | 
|  |         tpl_args._flags.text_modifier = true
 |  | 
|  |         out.result = value
 |  | 
|  |         out.stat_text = value
 |  | 
|  |         out.is_random = false
 |  | 
|  |         table.insert(tpl_args._mods, out)
 |  | 
|  |         
 |  | 
|  |         return true
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     return false
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.process_smw_mods(tpl_args, frame)
 |  | 
|  |     if #tpl_args._mods > 0 then 
 |  | 
|  |         local mods = {}
 |  | 
|  |         local mod_ids = {}
 |  | 
|  |         local non_random_mod_ids = {}
 |  | 
|  |         for _, mod_data in ipairs(tpl_args._mods) do
 |  | 
|  |             if mod_data.result == nil then
 |  | 
|  |                 mods[mod_data.id] = mod_data
 |  | 
|  |                 mod_ids[#mod_ids+1] = mod_data.id
 |  | 
|  |                 if not mod_data.is_random then
 |  | 
|  |                     table.insert(non_random_mod_ids, mod_data.id)
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                 _table = 'item_mods',
 |  | 
|  |                 id = mod_data.id,
 |  | 
|  |                 text = mod_data.stat_text,
 |  | 
|  |                 is_implicit = mod_data.is_implicit,
 |  | 
|  |                 is_random = mod_data.is_random,
 |  | 
|  |             }
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         local results = m_cargo.array_query{
 |  | 
|  |             tables={'mods'},
 |  | 
|  |             fields={'mods._pageName', 'mods.id', 'mods.required_level', 'mods.stat_text'},
 |  | 
|  |             id_field='mods.id',
 |  | 
|  |             id_array=mod_ids,
 |  | 
|  |         }
 |  | 
|  |         
 |  | 
|  |         for _, data in ipairs(results) do
 |  | 
|  |             local mod_data = mods[data['mods.id']]
 |  | 
|  |             mod_data.result = data
 |  | 
|  |             
 |  | 
|  |             if mod_data.is_random == false then
 |  | 
|  |                  -- update item level requirement
 |  | 
|  |                 local keys = {'required_level_final'}
 |  | 
|  |                 -- only update base item requirement if this is an implicit
 |  | 
|  |                 if mod_data.key == 'implicit' then
 |  | 
|  |                     keys[#keys+1] = 'required_level'
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 for _, key in ipairs(keys) do
 |  | 
|  |                     local req = math.floor(tonumber(data['mods.required_level']) * 0.8)
 |  | 
|  |                     if req > tpl_args[key] then
 |  | 
|  |                         tpl_args[key] = req
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         -- fetch stats
 |  | 
|  |         
 |  | 
|  |         results = m_cargo.query(
 |  | 
|  |             {'mods', 'mod_stats'},
 |  | 
|  |             {'mods.id', 'mod_stats.id', 'mod_stats.min', 'mod_stats.max'},
 |  | 
|  |             {
 |  | 
|  |                 join='mods._pageID=mod_stats._pageID',
 |  | 
|  |                 where=string.format('mod_stats.id IS NOT NULL AND mods.id IN ("%s")', table.concat(mod_ids, '", "')),
 |  | 
|  |             }
 |  | 
|  |         )
 |  | 
|  |         for _, data in ipairs(results) do
 |  | 
|  |             -- Stat subobject
 |  | 
|  |             local mod_data = mods[data['mods.id']]
 |  | 
|  |             if mod_data.result.stats == nil then
 |  | 
|  |                 mod_data.result.stats = {data, }
 |  | 
|  |             else
 |  | 
|  |                 mod_data.result.stats[#mod_data.result.stats+1] = data
 |  | 
|  |             end
 |  | 
|  |         
 |  | 
|  |             local id = data['mod_stats.id']
 |  | 
|  |             local value = {
 |  | 
|  |                 min = tonumber(data['mod_stats.min']),
 |  | 
|  |                 max = tonumber(data['mod_stats.max']),
 |  | 
|  |             }
 |  | 
|  |             value.avg = (value.min+value.max)/2
 |  | 
|  |             
 |  | 
|  |             local prefix = ''
 |  | 
|  |             if mod_data.is_random then
 |  | 
|  |                 prefix = '_random'
 |  | 
|  |             end
 |  | 
|  |             h.stats_update(tpl_args, id, value, mod_data.result['mods.id'], prefix .. '_stats')
 |  | 
|  |             if mod_data.is_implicit then
 |  | 
|  |                 h.stats_update(tpl_args, id, value, mod_data.result['mods.id'], prefix .. '_implicit_stats')
 |  | 
|  |             else
 |  | 
|  |                 h.stats_update(tpl_args, id, value, mod_data.result['mods.id'], prefix .. '_explicit_stats')
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if tpl_args._flags.sell_prices_override ~= true then
 |  | 
|  |             -- fetch sell prices
 |  | 
|  |             results = m_cargo.query(
 |  | 
|  |                 {'mods', 'mod_sell_prices'},
 |  | 
|  |                 {'mods.id', 'mod_sell_prices.amount', 'mod_sell_prices.name'},
 |  | 
|  |                 {
 |  | 
|  |                     join='mods._pageID=mod_sell_prices._pageID',
 |  | 
|  |                     -- must be non random mods to avoid accumulating sell prices of randomized modifiers
 |  | 
|  |                     where=string.format('mod_sell_prices.amount IS NOT NULL AND mods.id IN ("%s")', table.concat(non_random_mod_ids, '", "')),
 |  | 
|  |                 }
 |  | 
|  |             )
 |  | 
|  |             
 |  | 
|  |             for _, data in ipairs(results) do
 |  | 
|  |                 local mod_data = mods[data['mods.id']]
 |  | 
|  |                 if not mod_data.is_implicit then
 |  | 
|  |                     local values = {
 |  | 
|  |                         name = data['mod_sell_prices.name'],
 |  | 
|  |                         amount = tonumber(data['mod_sell_prices.amount']), 
 |  | 
|  |                     }
 |  | 
|  |                     -- sell_prices is defined in tpl_args.sell_prices_override
 |  | 
|  |                     tpl_args.sell_prices[values.name] = (tpl_args.sell_prices[values.name] or 0) + values.amount
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |          end
 |  | 
|  |     end
 |  | 
|  |     if tpl_args._flags.sell_prices_override ~= true then
 |  | 
|  |         local missing_sell_price = true
 |  | 
|  |         for _, _ in pairs(tpl_args.sell_prices) do
 |  | 
|  |             missing_sell_price = false
 |  | 
|  |             break
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if missing_sell_price then
 |  | 
|  |             tpl_args.sell_prices['Scroll Fragment'] = 1
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         -- Set sell price on page
 |  | 
|  |         for name, amount in pairs(tpl_args.sell_prices) do
 |  | 
|  |             -- sell_price_order is defined in tpl_args.sell_prices_override
 |  | 
|  |             tpl_args.sell_price_order[#tpl_args.sell_price_order+1] = name
 |  | 
|  |             tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                 _table = 'item_sell_prices',
 |  | 
|  |                 amount = amount,
 |  | 
|  |                 name = name,
 |  | 
|  |             }
 |  | 
|  |         end
 |  | 
|  |         table.sort(tpl_args.sell_price_order)
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.process_base_item(tpl_args, frame, args)
 |  | 
|  |     local where
 |  | 
|  |     if tpl_args.base_item_id ~= nil then
 |  | 
|  |         where = string.format('items.metadata_id="%s"', tpl_args.base_item_id)
 |  | 
|  |     elseif tpl_args.base_item_page ~= nil then
 |  | 
|  |         where = string.format('items._pageName="%s"' , tpl_args.base_item_page)
 |  | 
|  |     elseif tpl_args.base_item ~= nil then
 |  | 
|  |         where = string.format('items.name="%s"' , tpl_args.base_item)
 |  | 
|  |     elseif tpl_args.rarity ~= 'Normal' then
 |  | 
|  |         error(m_util.html.error{msg=i18n.errors.missing_base_item})
 |  | 
|  |     else
 |  | 
|  |         return
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if where ~= nil and tpl_args.rarity == 'Normal' and not tpl_args._flags.is_prophecy then
 |  | 
|  |         error(m_util.html.error{msg=i18n.errors.missing_rarity})
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     where = string.format('%s AND items.class_id="%s" AND items.rarity="Normal"', where, tpl_args.class_id)
 |  | 
|  |     
 |  | 
|  |     local join = {}
 |  | 
|  |     
 |  | 
|  |     for _, table_name in ipairs(core.item_classes[tpl_args.class_id].tables) do
 |  | 
|  |         if table_name ~= 'items' then
 |  | 
|  |             join[#join+1] = string.format('items._pageID=%s._pageID', table_name)
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local fields = {
 |  | 
|  |         'items._pageName',
 |  | 
|  |         'items.name',
 |  | 
|  |         'items.metadata_id',
 |  | 
|  |     }
 |  | 
|  |     
 |  | 
|  |     for _, k in ipairs(tpl_args._base_item_args) do
 |  | 
|  |         local data = core.map[k]
 |  | 
|  |         if data.field ~= nil then
 |  | 
|  |             fields[#fields+1] = string.format('%s.%s', data.table, data.field)
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local result = m_util.cargo.query(
 |  | 
|  |         core.item_classes[tpl_args.class_id].tables,
 |  | 
|  |         fields,
 |  | 
|  |         {
 |  | 
|  |             where=where,
 |  | 
|  |             join=table.concat(join, ','),
 |  | 
|  |             groupBy='items._pageID',
 |  | 
|  |         }
 |  | 
|  |     )
 |  | 
|  |     
 |  | 
|  |     if #result > 1 then
 |  | 
|  |         error(m_util.html.error{msg=i18n.errors.duplicate_base_items})
 |  | 
|  |         -- TODO be more explicit in the error?
 |  | 
|  |     elseif #result == 0 then
 |  | 
|  |         error(m_util.html.error{msg=i18n.errors.base_item_not_found})
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     result = result[1]
 |  | 
|  |     
 |  | 
|  |     tpl_args.base_item_data = result
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array={'base_item', 'base_item_page', 'base_item_id'}})
 |  | 
|  |   |  | 
|  |     --Copy values..
 |  | 
|  |     for _, k in ipairs(tpl_args._base_item_args) do
 |  | 
|  |         local data = core.map[k]
 |  | 
|  |         if data.field ~= nil and data.func_fetch == nil then
 |  | 
|  |             local value = result[string.format('%s.%s', data.table, data.field)]
 |  | 
|  |             -- I can just use data.default since it will be nil if not provided (nil == nil). Neat! ;)
 |  | 
|  |             if value ~= nil and (tpl_args[k] == data.default or type(data.default) == 'function') then
 |  | 
|  |                 tpl_args[k] = value
 |  | 
|  |                 if data.func ~= nil then
 |  | 
|  |                     data.func(tpl_args, frame)
 |  | 
|  |                 end
 |  | 
|  |                 if data.func_copy ~= nil then
 |  | 
|  |                     data.func_copy(tpl_args, frame)
 |  | 
|  |                 end
 |  | 
|  |             elseif value == nil then
 |  | 
|  |                 h.debug(tpl_args, function ()
 |  | 
|  |                     mw.logObject(string.format(i18n.debug.base_item_field_not_found, data.table, data.field))
 |  | 
|  |                 end)
 |  | 
|  |             elseif tpl_args[k] ~= data.default then
 |  | 
|  |                 h.debug(tpl_args, function ()
 |  | 
|  |                     mw.logObject(string.format(i18n.debug.field_value_mismatch, k, tostring(tpl_args[k])))
 |  | 
|  |                 end)
 |  | 
|  |             end
 |  | 
|  |         elseif data.func_fetch ~= nil then
 |  | 
|  |             data.func_fetch(tpl_args, frame)
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.process_arguments(tpl_args, frame, args)
 |  | 
|  |     for _, k in ipairs(args.array) do
 |  | 
|  |         local data = core.map[k]
 |  | 
|  |         if data == nil then
 |  | 
|  |             error(string.format('Invalid key or missing data for "%s"', k))
 |  | 
|  |         end
 |  | 
|  |         if data.no_copy == nil then
 |  | 
|  |             table.insert(tpl_args._base_item_args, k)
 |  | 
|  |         end
 |  | 
|  |         if data.func ~= nil then
 |  | 
|  |             data.func(tpl_args, frame)
 |  | 
|  |             --[[local status, err = pcall(data.func)
 |  | 
|  |             -- an error was raised, return the error string instead of the template
 |  | 
|  |             if not status then
 |  | 
|  |                 return err
 |  | 
|  |             end
 |  | 
|  |             ]]--
 |  | 
|  |         end
 |  | 
|  |         if tpl_args[k] == nil then
 |  | 
|  |             if tpl_args.class_id and core.item_classes[tpl_args.class_id].defaults ~= nil and core.item_classes[tpl_args.class_id].defaults[k] ~= nil then
 |  | 
|  |                 tpl_args[k] = core.item_classes[tpl_args.class_id].defaults[k]
 |  | 
|  |             elseif data.default ~= nil then
 |  | 
|  |                 if type(data.default) == 'function' then
 |  | 
|  |                     tpl_args[k] = data.default()
 |  | 
|  |                 else
 |  | 
|  |                     tpl_args[k] = data.default
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |         
 |  | 
|  | function core.process_mod_stats(tpl_args, args)
 |  | 
|  |     local lines = {}
 |  | 
|  |     
 |  | 
|  |     local skip = core.class_specifics[tpl_args.class_id]
 |  | 
|  |     if skip then
 |  | 
|  |         skip = skip.skip_stat_lines
 |  | 
|  |     end 
 |  | 
|  |     
 |  | 
|  |     local random_mods = {}
 |  | 
|  |     
 |  | 
|  |     for _, modinfo in ipairs(tpl_args._mods) do
 |  | 
|  |         if modinfo.is_implicit == args.is_implicit then
 |  | 
|  |             if modinfo.is_random == true then
 |  | 
|  |                 if random_mods[modinfo.stat_text] then
 |  | 
|  |                     table.insert(random_mods[modinfo.stat_text], modinfo)
 |  | 
|  |                 else
 |  | 
|  |                     random_mods[modinfo.stat_text] = {modinfo}
 |  | 
|  |                 end
 |  | 
|  |             else
 |  | 
|  |                 if modinfo.id == nil then
 |  | 
|  |                     table.insert(lines, modinfo.result)
 |  | 
|  |                 -- Allows the override of the SMW fetched mod texts for this modifier via <modtype><id>_text parameter
 |  | 
|  |                 elseif modinfo.text ~= nil then
 |  | 
|  |                      table.insert(lines, modinfo.text)
 |  | 
|  |                 else
 |  | 
|  |                     for _, line in ipairs(m_util.string.split(modinfo.result['mods.stat_text'] or '', '<br>')) do
 |  | 
|  |                         if line ~= '' then
 |  | 
|  |                             if skip == nil then
 |  | 
|  |                                 table.insert(lines, line)
 |  | 
|  |                             else
 |  | 
|  |                                 local skipped = false
 |  | 
|  |                                 for _, pattern in ipairs(skip) do
 |  | 
|  |                                     if string.match(line, pattern) then
 |  | 
|  |                                         skipped = true
 |  | 
|  |                                         break
 |  | 
|  |                                     end
 |  | 
|  |                                 end
 |  | 
|  |                                 if not skipped then
 |  | 
|  |                                     table.insert(lines, line)
 |  | 
|  |                                 end
 |  | 
|  |                             end
 |  | 
|  |                         end
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     for stat_text, modinfo_list in pairs(random_mods) do
 |  | 
|  |         local text = {}
 |  | 
|  |         for _, modinfo in ipairs(modinfo_list) do
 |  | 
|  |             table.insert(text, modinfo.result['mods.stat_text'])
 |  | 
|  |         end
 |  | 
|  |     
 |  | 
|  |         local tbl = mw.html.create('table')
 |  | 
|  |         tbl
 |  | 
|  |             :attr('class', 'random-modifier-stats mw-collapsed')
 |  | 
|  |             :attr('style', 'text-align: left')
 |  | 
|  |             :tag('tr')
 |  | 
|  |                 :tag('th')
 |  | 
|  |                     :attr('class', 'mw-customtoggle-31')
 |  | 
|  |                     :wikitext(stat_text)
 |  | 
|  |                     :done()
 |  | 
|  |                 :done()
 |  | 
|  |             :tag('tr')
 |  | 
|  |                 :attr('class', 'mw-collapsible mw-collapsed')
 |  | 
|  |                 :attr('id', 'mw-customcollapsible-31')
 |  | 
|  |                 :tag('td')
 |  | 
|  |                     :wikitext(table.concat(text, '<hr style="width: 20%">'))
 |  | 
|  |                     :done()
 |  | 
|  |                 :done()
 |  | 
|  |         table.insert(lines, tostring(tbl))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if #lines == 0 then
 |  | 
|  |         return
 |  | 
|  |     else
 |  | 
|  |         return table.concat(lines, '<br>')
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.process_upgraded_from(tpl_args, frame)
 |  | 
|  |     local sets = {}
 |  | 
|  |     local setid = 1
 |  | 
|  |     local set
 |  | 
|  |     repeat
 |  | 
|  |         local prefix = string.format('upgraded_from_set%s_', setid)
 |  | 
|  |         local groupid = 1
 |  | 
|  |         local group
 |  | 
|  |         set = {
 |  | 
|  |             groups = {},
 |  | 
|  |             optional = m_util.cast.boolean(tpl_args[prefix .. 'optional']),
 |  | 
|  |             text = m_util.cast.text(tpl_args[prefix .. 'text']),
 |  | 
|  |         }
 |  | 
|  |         repeat 
 |  | 
|  |             local group_prefix = string.format('%sgroup%s_', prefix, groupid)
 |  | 
|  |             group = {
 |  | 
|  |                 item_name = tpl_args[group_prefix .. 'item_name'],
 |  | 
|  |                 item_id = tpl_args[group_prefix .. 'item_id'], 
 |  | 
|  |                 item_page = tpl_args[group_prefix .. 'item_page'], 
 |  | 
|  |                 amount = tonumber(tpl_args[group_prefix .. 'amount']),
 |  | 
|  |                 notes = m_util.cast.text(tpl_args[group_prefix .. 'notes']),
 |  | 
|  |             }
 |  | 
|  |             
 |  | 
|  |             if group.item_name ~= nil or group.item_id ~= nil or group.item_page ~= nil then
 |  | 
|  |                 if group.amount == nil then
 |  | 
|  |                     error(string.format(i18n.errors.missing_amount, group_prefix .. 'amount'))
 |  | 
|  |                 else
 |  | 
|  |                     -- for verification purposes 
 |  | 
|  |                     local where
 |  | 
|  |                     if group.item_id then
 |  | 
|  |                         where = string.format('items.metadata_id="%s"', group.item_id)
 |  | 
|  |                     elseif group.item_page then
 |  | 
|  |                         where = string.format('items._pageName="%s"', group.item_page)
 |  | 
|  |                     elseif group.item_name then
 |  | 
|  |                         where = string.format('items.name="%s"', group.item_name)
 |  | 
|  |                     end
 |  | 
|  |                     
 |  | 
|  |                     local results = m_util.cargo.query(
 |  | 
|  |                         {'items'},
 |  | 
|  |                         {'items._pageName',  'items.name', 'items.metadata_id'},
 |  | 
|  |                         {
 |  | 
|  |                             where=where,
 |  | 
|  |                         }
 |  | 
|  |                     )
 |  | 
|  |                     if #results ~= 1 then
 |  | 
|  |                         tpl_args._errors[#tpl_args._errors+1] = string.format(i18n.errors.upgraded_from_broken_reference, string.sub(group_prefix, 1, -2), #results)
 |  | 
|  |                         tpl_args._flags.broken_upgraded_from_reference = true
 |  | 
|  |                     else
 |  | 
|  |                         results = results[1]
 |  | 
|  |                         if results['items.metadata_id'] then
 |  | 
|  |                             group.item_id = results['items.metadata_id']
 |  | 
|  |                         end
 |  | 
|  |                         group.item_name = results['items.name']
 |  | 
|  |                         group.page = results['items._pageName']
 |  | 
|  |                         set.groups[#set.groups+1] = group
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             groupid = groupid + 1
 |  | 
|  |         until group.item_name == nil and group.item_id == nil and group.item_page == nil
 |  | 
|  |         
 |  | 
|  |         -- set was empty, can terminate safely
 |  | 
|  |         if #set.groups == 0 then
 |  | 
|  |             set = nil
 |  | 
|  |         else
 |  | 
|  |             setid = setid + 1
 |  | 
|  |             sets[#sets+1] = set
 |  | 
|  |         end
 |  | 
|  |     until set == nil
 |  | 
|  |     
 |  | 
|  |     if #sets == 0 then
 |  | 
|  |         return
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     tpl_args.upgrade_from_sets = sets
 |  | 
|  |     
 |  | 
|  |     -- set upgraded_from data
 |  | 
|  |     for i, set in ipairs(sets) do
 |  | 
|  |         tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |             _table = 'upgraded_from_sets',
 |  | 
|  |             set_id = i,
 |  | 
|  |             text = set.text,
 |  | 
|  |             --'optional' = set.optional,
 |  | 
|  |         }
 |  | 
|  |         
 |  | 
|  |         for j, group in ipairs(set.groups) do
 |  | 
|  |             tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                 _table = 'upgraded_from_groups',
 |  | 
|  |                 group_id = j,
 |  | 
|  |                 set_id = i,
 |  | 
|  |                 item_name = group.item_name,
 |  | 
|  |                 item_id = group.item_id,
 |  | 
|  |                 item_page = group.page,
 |  | 
|  |                 amount = group.amount,
 |  | 
|  |                 notes = group.notes,
 |  | 
|  |             }
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- function factory
 |  | 
|  | --
 |  | 
|  | core.factory = {}
 |  | 
|  |   |  | 
|  | function core.factory.cast_text(k, args)
 |  | 
|  |     args = args or {}
 |  | 
|  |     return function (tpl_args, frame)
 |  | 
|  |         tpl_args[args.key_out or k] = m_util.cast.text(tpl_args[k])
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.factory.display_value(args)
 |  | 
|  |     -- args:
 |  | 
|  |     --  type: Type of the keys (nil = regular, gem = skill gems, stat = stats)
 |  | 
|  |     --  options<Array>:
 |  | 
|  |     --   key: key to use
 |  | 
|  |     --   allow_zero: allow zero values
 |  | 
|  |     --   hide_default: hide the value if this is set
 |  | 
|  |     --   hide_default_key: key to use if it isn't equal to the key parameter
 |  | 
|  |     --   -- from m_util.html.format_value --
 |  | 
|  |     --   fmt: formatter to use for the value instead of valfmt
 |  | 
|  |     --   fmt_range: formatter to use for the range values. Default: (%s to %s)
 |  | 
|  |     --   insert: insert results into this object
 |  | 
|  |     --   func: Function to adjust the value with before output
 |  | 
|  |     --   color: colour codefor m_util.html.poe_color, overrides mod colour
 |  | 
|  |     --   no_color: set to true to ingore colour entirely
 |  | 
|  |     --   truncate: set to true to truncate the line
 |  | 
|  |     
 |  | 
|  |     for k, default in pairs({options = {}}) do
 |  | 
|  |         if args[k] == nil then
 |  | 
|  |             args[k] = default
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     return function (tpl_args, frame)
 |  | 
|  |         local base_values = {}
 |  | 
|  |         local temp_values = {}
 |  | 
|  |         if args.type == 'gem' then
 |  | 
|  |             if not core.class_groups.gems.keys[tpl_args.class_id] then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             for i, data in ipairs(args.options) do
 |  | 
|  |                 local value = tpl_args.skill_levels[0][data.key]
 |  | 
|  |                 if value ~= nil then
 |  | 
|  |                     base_values[#base_values+1] = value
 |  | 
|  |                     temp_values[#temp_values+1] = {value={min=value,max=value}, index=i}
 |  | 
|  |                 else
 |  | 
|  |                     value = {
 |  | 
|  |                         min=tpl_args.skill_levels[1][data.key],
 |  | 
|  |                         max=tpl_args.skill_levels[tpl_args.max_level][data.key], 
 |  | 
|  |                     }
 |  | 
|  |                     if value.min == nil or value.max == nil then
 |  | 
|  |                     else
 |  | 
|  |                         base_values[#base_values+1] = value.min
 |  | 
|  |                         temp_values[#temp_values+1] = {value=value, index=i}
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         elseif args.type == 'stat' then
 |  | 
|  |             for i, data in ipairs(args.options) do
 |  | 
|  |                 local value = tpl_args._stats[data.key]
 |  | 
|  |                 if value ~= nil then
 |  | 
|  |                     base_values[i] = value.min
 |  | 
|  |                     temp_values[#temp_values+1] = {value=value, index=i}
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         else
 |  | 
|  |             for i, data in ipairs(args.options) do
 |  | 
|  |                 base_values[i] = tpl_args[data.key]
 |  | 
|  |                 local value = {}
 |  | 
|  |                 if tpl_args[data.key .. '_range_minimum'] ~= nil then
 |  | 
|  |                     value.min = tpl_args[data.key .. '_range_minimum']
 |  | 
|  |                     value.max = tpl_args[data.key .. '_range_maximum']
 |  | 
|  |                 elseif tpl_args[data.key] ~= nil then
 |  | 
|  |                     value.min = tpl_args[data.key]
 |  | 
|  |                     value.max = tpl_args[data.key]
 |  | 
|  |                 end
 |  | 
|  |                 if value.min == nil then
 |  | 
|  |                 else
 |  | 
|  |                     temp_values[#temp_values+1] = {value=value, index=i}
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         local final_values = {}
 |  | 
|  |         for i, data in ipairs(temp_values) do
 |  | 
|  |             local opt = args.options[data.index]
 |  | 
|  |             local insert = false
 |  | 
|  |             if opt.hide_default == nil then
 |  | 
|  |                 insert = true
 |  | 
|  |             elseif opt.hide_default_key == nil then
 |  | 
|  |                 local v = data.value
 |  | 
|  |                 if opt.hide_default ~= v.min and opt.hide_default ~= v.max then
 |  | 
|  |                     insert = true
 |  | 
|  |                 end
 |  | 
|  |             else
 |  | 
|  |                 local v = {
 |  | 
|  |                     min = tpl_args[opt.hide_default_key .. '_range_minimum'],
 |  | 
|  |                     max = tpl_args[opt.hide_default_key .. '_range_maximum'],
 |  | 
|  |                 }
 |  | 
|  |                 if v.min == nil or v.max == nil then
 |  | 
|  |                     if opt.hide_default ~= tpl_args[opt.hide_default_key] then
 |  | 
|  |                         insert = true
 |  | 
|  |                     end
 |  | 
|  |                 elseif opt.hide_default ~= v.min and opt.hide_default ~= v.max then
 |  | 
|  |                     insert = true
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if insert == true then
 |  | 
|  |                 table.insert(final_values, data)
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         -- all zeros = dont display and return early
 |  | 
|  |         if #final_values == 0 then
 |  | 
|  |             return nil
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         local out = {}
 |  | 
|  |         
 |  | 
|  |         for i, data in ipairs(final_values) do
 |  | 
|  |             local value = data.value
 |  | 
|  |             value.base = base_values[data.index]
 |  | 
|  |             
 |  | 
|  |             local options = args.options[data.index]
 |  | 
|  |             
 |  | 
|  |             if options.color == nil and args.type == 'gem' then
 |  | 
|  |                 value.color = 'value'
 |  | 
|  |             end
 |  | 
|  |             if options.truncate then
 |  | 
|  |                 options.class = 'u-truncate-line'
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             out[#out+1] = m_util.html.format_value(tpl_args, frame, value, options)
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if args.inline then
 |  | 
|  |             return m_util.html.poe_color('default', string.format(args.inline, unpack(out)))
 |  | 
|  |         else
 |  | 
|  |             return table.concat(out, '')
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.factory.display_value_only(key)
 |  | 
|  |     return function(tpl_args, frame)
 |  | 
|  |         return tpl_args[key]
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.factory.descriptor_value(args)
 |  | 
|  |     -- Arguments:
 |  | 
|  |     --  key
 |  | 
|  |     --  tbl
 |  | 
|  |     args = args or {}
 |  | 
|  |     return function (tpl_args, frame, value)
 |  | 
|  |         args.tbl = args.tbl or tpl_args
 |  | 
|  |         if args.tbl[args.key] then
 |  | 
|  |             value = m_util.html.abbr(value, args.tbl[args.key])
 |  | 
|  |         end
 |  | 
|  |         return value
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.factory.damage_html(args)
 |  | 
|  |     return function(tpl_args, frame)
 |  | 
|  |         if args.key ~= 'physical' then
 |  | 
|  |            args.color = args.key
 |  | 
|  |            args.no_color = true
 |  | 
|  |         end
 |  | 
|  |         local keys = {
 |  | 
|  |             min=args.key .. '_damage_min',
 |  | 
|  |             max=args.key .. '_damage_max',
 |  | 
|  |         }
 |  | 
|  |         local value = {}
 |  | 
|  |         
 |  | 
|  |         for ktype, key in pairs(keys) do
 |  | 
|  |             value[ktype] = core.factory.display_value{options={ [1] = {
 |  | 
|  |                 key = key,
 |  | 
|  |                 no_color = args.no_color,
 |  | 
|  |                 hide_default = 0
 |  | 
|  |             }}}(tpl_args, frame)
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if value.min and value.max then
 |  | 
|  |             value = value.min .. '–' .. value.max
 |  | 
|  |             if args.color ~= nil then
 |  | 
|  |                 value = m_util.html.poe_color(args.color, value)
 |  | 
|  |             end
 |  | 
|  |             tpl_args[args.key .. '_damage_html'] = value
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | core.display = {}
 |  | 
|  | function core.display.make_main_container(tpl_args, frame, container_type)
 |  | 
|  |     local container = mw.html.create('span')
 |  | 
|  |     :attr( 'class', 'item-box -' .. tpl_args.frame_type)
 |  | 
|  |     
 |  | 
|  |     if tpl_args.class_id == 'DivinationCard' then
 |  | 
|  |         container
 |  | 
|  |             :tag('span')
 |  | 
|  |                 :attr( 'class', 'divicard-wrapper')
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-art')
 |  | 
|  |                     :wikitext( '[[' .. tpl_args.card_art .. '|link=|alt=]]' )
 |  | 
|  |                     :done()
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-frame')
 |  | 
|  |                     :wikitext( '[[File:Divination card frame.png|link=|alt=]]' )
 |  | 
|  |                     :done()
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-header')
 |  | 
|  |                     :wikitext(tpl_args.name)
 |  | 
|  |                     :done()
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-stack')
 |  | 
|  |                     :wikitext(tpl_args.stack_size)
 |  | 
|  |                     :done()
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-reward')
 |  | 
|  |                     :tag('span')
 |  | 
|  |                         :wikitext(tpl_args.description)
 |  | 
|  |                         :done()
 |  | 
|  |                     :done()
 |  | 
|  |                 :tag('span')
 |  | 
|  |                     :attr('class', 'divicard-flavour text-color -flavour')
 |  | 
|  |                     :tag('span')
 |  | 
|  |                         :wikitext(tpl_args.flavour_text)
 |  | 
|  |                         :done()
 |  | 
|  |                     :done()
 |  | 
|  |                 :done()
 |  | 
|  |         --TODO Extras?
 |  | 
|  |     else
 |  | 
|  |         local header_css
 |  | 
|  |         if tpl_args.base_item and tpl_args.rarity ~= 'Normal' then
 |  | 
|  |             line_type = 'double'
 |  | 
|  |         else
 |  | 
|  |             line_type = 'single'
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         local name_line = tpl_args.name
 |  | 
|  |         if tpl_args.base_item and not tpl_args._flags.is_prophecy then
 |  | 
|  |             name_line = name_line .. '<br>' .. tpl_args.base_item
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         container
 |  | 
|  |             :tag('span')
 |  | 
|  |                 :attr( 'class', 'header -' .. line_type )
 |  | 
|  |                 :wikitext( name_line )
 |  | 
|  |             :done()
 |  | 
|  |             
 |  | 
|  |         core.display.add_to_container_from_map(tpl_args, frame, container, core.item_display_groups, container_type)
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.skill_icon ~= nil then
 |  | 
|  |         container:wikitext(string.format('[[%s]]', tpl_args.skill_icon))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     return container
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.display.add_to_container_from_map(tpl_args, frame, container, mapping, container_type)
 |  | 
|  |     local grpcont
 |  | 
|  |     local valid
 |  | 
|  |     local statcont = mw.html.create('span')
 |  | 
|  |     statcont
 |  | 
|  |         :attr('class', 'item-stats')
 |  | 
|  |         :done()
 |  | 
|  |         
 |  | 
|  |     local count = 0
 |  | 
|  |         
 |  | 
|  |     for _, group in ipairs(mapping) do
 |  | 
|  |         grpcont = {}
 |  | 
|  |         if group.func == nil then
 |  | 
|  |             for _, disp in ipairs(group) do
 |  | 
|  |                 valid = true
 |  | 
|  |                 if container_type == 'inline' and disp.inline == false then
 |  | 
|  |                     valid = false
 |  | 
|  |                 else
 |  | 
|  |                 -- No args to verify which means always valid
 |  | 
|  |                     if disp.args == nil then
 |  | 
|  |                     elseif type(disp.args) == 'table' then
 |  | 
|  |                         for _, key in ipairs(disp.args) do
 |  | 
|  |                             if tpl_args[key] == nil then
 |  | 
|  |                                 valid = false
 |  | 
|  |                                 break
 |  | 
|  |                             end
 |  | 
|  |                         end
 |  | 
|  |                     elseif type(disp.args) == 'function' then
 |  | 
|  |                         valid = disp.args(tpl_args, frame, container_type)
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 if valid then
 |  | 
|  |                     grpcont[#grpcont+1] = disp.func(tpl_args, frame, container_type)
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         else
 |  | 
|  |             grpcont = group.func(tpl_args, frame, container_type)
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if #grpcont > 0 then
 |  | 
|  |             count = count + 1
 |  | 
|  |             local header = ''
 |  | 
|  |             if group.header == nil then
 |  | 
|  |             elseif type(group.header) == 'function' then
 |  | 
|  |                 header = group.header()
 |  | 
|  |             else
 |  | 
|  |                 header = string.format('<em class="header">%s</em><br>', group.header)
 |  | 
|  |             end
 |  | 
|  |             statcont
 |  | 
|  |                 :tag('span')
 |  | 
|  |                 :attr('class', 'group ' .. (group.css_class or ''))
 |  | 
|  |                 :wikitext(header .. table.concat(grpcont, '<br>'))
 |  | 
|  |                 :done()
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- Don't add empty containers
 |  | 
|  |     if count > 0 then
 |  | 
|  |         container:node(statcont)
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | function core.display.strip_random_stats(tpl_args, frame, stat_text, container_type)
 |  | 
|  |     if tpl_args._flags.random_mods and container_type == 'inline' then
 |  | 
|  |         repeat
 |  | 
|  |             local text = string.match(stat_text, '<th class="mw%-customtoggle%-31">(.+)</th>')
 |  | 
|  |             if text ~= nil then
 |  | 
|  |                 stat_text = string.gsub(stat_text, '<table class="random%-modifier%-stats.+</table>', text, 1)
 |  | 
|  |             end
 |  | 
|  |         until text == nil
 |  | 
|  |     end
 |  | 
|  |     return stat_text
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- argument mapping
 |  | 
|  | --
 |  | 
|  | -- format:
 |  | 
|  | -- tpl_args key = {
 |  | 
|  | --   no_copy = true or nil           -- When loading an base item, dont copy this key 
 |  | 
|  | --   property = 'prop',              -- Property associated with this key
 |  | 
|  | --   property_func = function or nil -- Function to unpack the property into a native lua value. 
 |  | 
|  | --                                      If not specified, func is used. 
 |  | 
|  | --                                      If neither is specified, value is copied as string
 |  | 
|  | --   func = function or nil          -- Function to unpack the argument into a native lua value and validate it. 
 |  | 
|  | --                                      If not specified, value will not be set.
 |  | 
|  | --   default = object                -- Default value if the parameter is nil
 |  | 
|  | -- }
 |  | 
|  | core.map = {
 |  | 
|  |     -- special params
 |  | 
|  |     html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     implicit_stat_text = {
 |  | 
|  |         field = 'implicit_stat_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.implicit_stat_text = core.process_mod_stats(tpl_args, {is_implicit=true})
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     explicit_stat_text = {
 |  | 
|  |         field = 'explicit_stat_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.explicit_stat_text = core.process_mod_stats(tpl_args, {is_implicit=false})
 |  | 
|  |             
 |  | 
|  |             if tpl_args.is_talisman or tpl_args.is_corrupted then
 |  | 
|  |                 if tpl_args.explicit_stat_text == nil or tpl_args.explicit_stat_text == '' then
 |  | 
|  |                     tpl_args.explicit_stat_text = m_util.html.poe_color('corrupted', i18n.tooltips.corrupted)
 |  | 
|  |                 else
 |  | 
|  |                     tpl_args.explicit_stat_text = (tpl_args.explicit_stat_text or '') .. '<br>' .. m_util.html.poe_color('corrupted', i18n.tooltips.corrupted)
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     stat_text = {
 |  | 
|  |         field = 'stat_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local sep = ''
 |  | 
|  |             if tpl_args.implicit_stat_text and tpl_args.explicit_stat_text then
 |  | 
|  |                 sep = string.format('<span class="item-stat-separator -%s"></span>', tpl_args.frame_type)
 |  | 
|  |             end
 |  | 
|  |             local text = (tpl_args.implicit_stat_text or '') .. sep .. (tpl_args.explicit_stat_text or '')
 |  | 
|  |             
 |  | 
|  |             if string.len(text) > 0 then
 |  | 
|  |                 tpl_args.stat_text = text
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     class = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'class',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function (tpl_args, frame)
 |  | 
|  |             tpl_args.class = m_game.constants.item.classes[tpl_args.class_id]['long_upper']
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- processed in core.build_item_classes
 |  | 
|  |     class_id = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'class_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function (tpl_args, frame)
 |  | 
|  |             if m_game.constants.item.classes[tpl_args.class_id] == nil then
 |  | 
|  |                 error(string.format(i18n.errors.invalid_class_id, tostring(tpl_args.class_id)))
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     },
 |  | 
|  |     -- generic
 |  | 
|  |     rarity = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'rarity',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = m_util.cast.factory.table('rarity', {key={'full', 'long_lower'}, tbl=m_game.constants.item.rarity, rtrkey='full'}),
 |  | 
|  |     },
 |  | 
|  |     name = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'name',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     size_x = {
 |  | 
|  |         field = 'size_x',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('size_x'),
 |  | 
|  |     },
 |  | 
|  |     size_y = {
 |  | 
|  |         field = 'size_y',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('size_y'),
 |  | 
|  |     },
 |  | 
|  |     drop_enabled = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_enabled',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = m_util.cast.factory.boolean('drop_enabled'),
 |  | 
|  |         default = true,
 |  | 
|  |     },
 |  | 
|  |     drop_level = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_level',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('drop_level'),
 |  | 
|  |     },
 |  | 
|  |     drop_level_maximum = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_level_maximum',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('drop_level_maximum'),
 |  | 
|  |     },
 |  | 
|  |     drop_leagues = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_leagues',
 |  | 
|  |         type = 'List (,) of String',
 |  | 
|  |         func = m_util.cast.factory.assoc_table('drop_leagues', {tbl=m_game.constants.leagues, errmsg=i18n.errors.invalid_league}),
 |  | 
|  |     },
 |  | 
|  |     drop_areas = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_areas',
 |  | 
|  |         type = 'List (,) of String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.drop_areas ~= nil then
 |  | 
|  |                 tpl_args.drop_areas = m_util.string.split(tpl_args.drop_areas, ',%s*')
 |  | 
|  |                 tpl_args.drop_areas_data = m_cargo.array_query{
 |  | 
|  |                     tables={'areas'},
 |  | 
|  |                     fields={'areas._pageName', 'areas.id', 'areas.name', 'areas.main_page'},
 |  | 
|  |                     id_field='areas.id',
 |  | 
|  |                     id_array=tpl_args.drop_areas,
 |  | 
|  |                     query={limit=5000},
 |  | 
|  |                 }
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             -- find areas based on item tags for atlas bases
 |  | 
|  |             local query_data
 |  | 
|  |             for _, tag in ipairs(tpl_args.tags or {}) do
 |  | 
|  |                 if string.match(tag, '%s*atlas%s*') ~= nil and tag ~= 'atlas_base_type' then
 |  | 
|  |                     query_data = m_util.cargo.query(
 |  | 
|  |                         {'areas', 'spawn_weights'},
 |  | 
|  |                         {'areas._pageName', 'areas.id', 'areas.name', 'areas.main_page'},
 |  | 
|  |                         {
 |  | 
|  |                             join='areas._pageID=spawn_weights._pageID',
 |  | 
|  |                             where=string.format('spawn_weights.tag = "%s" AND spawn_weights.weight > 0', tag),
 |  | 
|  |                             groupBy='areas.id',
 |  | 
|  |                         }
 |  | 
|  |                     )
 |  | 
|  |                     break
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if query_data ~= nil then
 |  | 
|  |                 -- in case no manual drop areas have been set
 |  | 
|  |                 if tpl_args.drop_areas == nil then
 |  | 
|  |                     tpl_args.drop_areas = {}
 |  | 
|  |                     tpl_args.drop_areas_data = {}
 |  | 
|  |                 end
 |  | 
|  |                 local drop_areas_assoc = {}
 |  | 
|  |                 for _, id in ipairs(tpl_args.drop_areas) do
 |  | 
|  |                     drop_areas_assoc[id] = true
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 local duplicates = {}
 |  | 
|  |                 
 |  | 
|  |                 for _, row in ipairs(query_data) do
 |  | 
|  |                     if drop_areas_assoc[row['areas.id']] == nil then
 |  | 
|  |                         tpl_args.drop_areas[#tpl_args.drop_areas+1] = row['areas.id']
 |  | 
|  |                         tpl_args.drop_areas_data[#tpl_args.drop_areas_data+1] = row
 |  | 
|  |                     else
 |  | 
|  |                         duplicates[#duplicates+1] = row['areas.id']
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 if #duplicates > 0 then
 |  | 
|  |                     tpl_args._errors[#tpl_args._errors+1] = string.format(i18n.errors.duplicate_area_id_from_query, table.concat(duplicates, ', '))
 |  | 
|  |                     tpl_args._flags.duplicate_query_area_ids = true
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     drop_text = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('drop_text'),
 |  | 
|  |     },
 |  | 
|  |     required_level = {
 |  | 
|  |         field = 'required_level_base',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('required_level'),
 |  | 
|  |         default = 1,
 |  | 
|  |     },
 |  | 
|  |     required_level_final = {
 |  | 
|  |         field = 'required_level',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.required_level_final = tpl_args.required_level
 |  | 
|  |         end,
 |  | 
|  |         default = 1,
 |  | 
|  |     },
 |  | 
|  |     required_dexterity = {
 |  | 
|  |         field = 'required_dexterity',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('required_dexterity'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     required_strength = {
 |  | 
|  |         field = 'required_strength',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('required_strength'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     required_intelligence = {
 |  | 
|  |         field = 'required_intelligence',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('required_intelligence'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     inventory_icon = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'inventory_icon',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.class_id == 'DivinationCard' then
 |  | 
|  |                 tpl_args.inventory_icon = tpl_args.inventory_icon or 'Divination card'
 |  | 
|  |             end
 |  | 
|  |             tpl_args.inventory_icon_id = tpl_args.inventory_icon or tpl_args.name
 |  | 
|  |             tpl_args.inventory_icon = string.format(i18n.inventory_icon, tpl_args.inventory_icon_id) 
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- note: this must be called after inventory item to work correctly as it depends on tpl_args.inventory_icon_id being set
 |  | 
|  |     alternate_art_inventory_icons = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'alternate_art_inventory_icons',
 |  | 
|  |         type = 'List (,) of String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local icons = {}
 |  | 
|  |             if tpl_args.alternate_art_inventory_icons ~= nil then
 |  | 
|  |                 local names = m_util.string.split(tpl_args.alternate_art_inventory_icons, ',%s*')
 |  | 
|  |                 
 |  | 
|  |                 for _, name in ipairs(names) do
 |  | 
|  |                     icons[#icons+1] = string.format(i18n.inventory_icon, string.format('%s %s', tpl_args.inventory_icon_id, name))
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             tpl_args.alternate_art_inventory_icons = icons
 |  | 
|  |         end,
 |  | 
|  |         default = function () return {} end,
 |  | 
|  |     },
 |  | 
|  |     cannot_be_traded_or_modified = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'cannot_be_traded_or_modified',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = m_util.cast.factory.boolean('cannot_be_traded_or_modified'),
 |  | 
|  |         default = false,
 |  | 
|  |     },
 |  | 
|  |     help_text = {
 |  | 
|  |         field = 'help_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('help_text'),
 |  | 
|  |     },
 |  | 
|  |     flavour_text = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'flavour_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('flavour_text'),
 |  | 
|  |     },
 |  | 
|  |     tags = {
 |  | 
|  |         field = 'tags',
 |  | 
|  |         type = 'List (,) of String',
 |  | 
|  |         func = m_util.cast.factory.assoc_table('tags', {
 |  | 
|  |             tbl = m_game.constants.tags,
 |  | 
|  |             errmsg = i18n.errors.invalid_tag,
 |  | 
|  |         }),
 |  | 
|  |     },
 |  | 
|  |     metadata_id = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'metadata_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         --type = 'String(unique; size=200)',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.metadata_id == nil then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             local results = m_util.cargo.query(
 |  | 
|  |                 {'items'},
 |  | 
|  |                 {'items._pageName'},
 |  | 
|  |                 {
 |  | 
|  |                     where=string.format('items.metadata_id="%s" AND items._pageName != "%s"', tpl_args.metadata_id, mw.title.getCurrentTitle().fullText)
 |  | 
|  |                 }
 |  | 
|  |             )
 |  | 
|  |             if #results > 0 and tpl_args.debug_id == nil and not tpl_args.debug then
 |  | 
|  |                 error(string.format(i18n.errors.duplicate_metadata, tpl_args.metadata_id, results[1]['items._pageName']))
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     is_corrupted = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'is_corrupted',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = m_util.cast.factory.boolean('is_corrupted'), 
 |  | 
|  |         default = false,
 |  | 
|  |     },
 |  | 
|  |     is_relic = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'is_relic',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             m_util.cast.factory.boolean('is_relic')(tpl_args, frame)
 |  | 
|  |             if tpl_args.is_relic == true and tpl_args.rarity ~= 'Unique' then
 |  | 
|  |                 error(i18n.errors.non_unique_relic)
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |         default = false,
 |  | 
|  |     }, 
 |  | 
|  |     is_prophecy = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = nil,
 |  | 
|  |         type = nil,
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args._flags.is_prophecy = (tpl_args.metadata_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy' or tpl_args.base_item == 'Prophecy' or tpl_args.base_item_page == 'Prophecy' or tpl_args.base_item_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy')
 |  | 
|  |         end
 |  | 
|  |     },
 |  | 
|  |     purchase_costs = {
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local purchase_costs = {}
 |  | 
|  |             for _, rarity_names in ipairs(m_game.constants.item.rarity) do
 |  | 
|  |                 local rtbl = {}
 |  | 
|  |                 local prefix = string.format('purchase_cost_%s', rarity_names.long_lower)
 |  | 
|  |                 local i = 1
 |  | 
|  |                 while i ~= -1 do
 |  | 
|  |                     prefix = prefix .. i
 |  | 
|  |                     local values = {
 |  | 
|  |                         name = tpl_args[prefix .. '_name'],
 |  | 
|  |                         amount = tonumber(tpl_args[prefix .. '_amount']),
 |  | 
|  |                         rarity = rarity_names.long_upper,
 |  | 
|  |                     }
 |  | 
|  |                     if values.name ~= nil and values.amount ~= nil then
 |  | 
|  |                         rtbl[#rtbl+1] = values
 |  | 
|  |                         i = i + 1
 |  | 
|  |                         
 |  | 
|  |                         tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                             _table = 'item_purchase_costs',
 |  | 
|  |                             amount = values.amount,
 |  | 
|  |                             name = values.name,
 |  | 
|  |                             rarity = values.rarity,
 |  | 
|  |                         }
 |  | 
|  |                     else
 |  | 
|  |                         i = -1
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 purchase_costs[rarity_names.long_lower] = rtbl
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             tpl_args.purchase_costs = purchase_costs
 |  | 
|  |         end,
 |  | 
|  |         func_fetch = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.rarity ~= 'Unique' then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             local results = m_util.cargo.query(
 |  | 
|  |                 {'items' ,'item_purchase_costs'},
 |  | 
|  |                 {'item_purchase_costs.amount', 'item_purchase_costs.name', 'item_purchase_costs.rarity'},
 |  | 
|  |                 {
 |  | 
|  |                     join = 'items._pageID=item_purchase_costs._pageID',
 |  | 
|  |                     where = string.format('items._pageName="%s" AND item_purchase_costs.rarity="Unique"', tpl_args.base_item_page),
 |  | 
|  |                 }
 |  | 
|  |             )
 |  | 
|  |             
 |  | 
|  |             for _, row in ipairs(results) do
 |  | 
|  |                 local values = {
 |  | 
|  |                     rarity = row['item_purchase_costs.rarity'],
 |  | 
|  |                     name = row['item_purchase_costs.name'],
 |  | 
|  |                     amount = tonumber(row['item_purchase_costs.amount']),
 |  | 
|  |                 }
 |  | 
|  |                 local datavar = tpl_args.purchase_costs[string.lower(values.rarity)]
 |  | 
|  |                 datavar[#datavar+1] = values
 |  | 
|  |                 
 |  | 
|  |                 tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                     _table = 'item_purchase_costs',
 |  | 
|  |                     amount = values.amount,
 |  | 
|  |                     name = values.name,
 |  | 
|  |                     rarity = values.rarity,
 |  | 
|  |                 }
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     sell_prices_override = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             -- these variables are also used by mods when setting automatic sell prices
 |  | 
|  |             tpl_args.sell_prices = {}
 |  | 
|  |             tpl_args.sell_price_order = {}
 |  | 
|  |             
 |  | 
|  |             
 |  | 
|  |             local name
 |  | 
|  |             local amount
 |  | 
|  |             local i = 0
 |  | 
|  |             repeat
 |  | 
|  |                 i = i + 1
 |  | 
|  |                 name = tpl_args[string.format('sell_price%s_name', i)]
 |  | 
|  |                 amount = tpl_args[string.format('sell_price%s_amount', i)]
 |  | 
|  |                 
 |  | 
|  |                 if name ~= nil and amount ~= nil then
 |  | 
|  |                     tpl_args.sell_price_order[#tpl_args.sell_price_order+1] = name
 |  | 
|  |                     tpl_args.sell_prices[name] = amount
 |  | 
|  |                     tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                         _table = 'item_sell_prices',
 |  | 
|  |                         amount = amount,
 |  | 
|  |                         name = name,
 |  | 
|  |                     }
 |  | 
|  |                 end
 |  | 
|  |             until name == nil or amount == nil 
 |  | 
|  |             
 |  | 
|  |             -- if sell prices are set, the override is active
 |  | 
|  |             for _, _ in pairs(tpl_args.sell_prices) do
 |  | 
|  |                 tpl_args._flags.sell_prices_override = true
 |  | 
|  |                 break
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     --
 |  | 
|  |     -- specific section
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     -- Most item classes
 |  | 
|  |     quality = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'quality',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         -- Can be set manually, but default to Q20 for unique weapons/body armours
 |  | 
|  |         -- Also must copy to stat for the stat adjustments to work properly
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local quality = tonumber(tpl_args.quality)
 |  | 
|  |             -- 
 |  | 
|  |             if quality == nil then
 |  | 
|  |                 if tpl_args.rarity ~= 'Unique' then
 |  | 
|  |                     quality = 0
 |  | 
|  |                 elseif core.class_groups.weapons.keys[tpl_args.class_id] or core.class_groups.armor.keys[tpl_args.class_id] then
 |  | 
|  |                     quality = 20
 |  | 
|  |                 else
 |  | 
|  |                     quality = 0
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             tpl_args.quality = quality
 |  | 
|  |             
 |  | 
|  |             local stat = {
 |  | 
|  |                 min = quality,
 |  | 
|  |                 max = quality,
 |  | 
|  |                 avg = quality,
 |  | 
|  |             }
 |  | 
|  |             
 |  | 
|  |             h.stats_update(tpl_args, 'quality', stat, nil, '_stats')
 |  | 
|  |             
 |  | 
|  |             if tpl_args.class_id == 'UtilityFlask' or tpl_args.class_id == 'UtilityFlaskCritical' then
 |  | 
|  |                 h.stats_update(tpl_args, 'quality_flask_duration', stat, nil, '_stats')
 |  | 
|  |             -- quality is added to quantity for maps
 |  | 
|  |             elseif tpl_args.class_id == 'Map' then
 |  | 
|  |                 h.stats_update(tpl_args, 'map_item_drop_quantity_+%', stat, nil, '_stats')
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- amulets
 |  | 
|  |     is_talisman = {
 |  | 
|  |         field = 'is_talisman',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = m_util.cast.factory.boolean('is_talisman'),
 |  | 
|  |         default = false,
 |  | 
|  |     },
 |  | 
|  |     
 |  | 
|  |     talisman_tier = {
 |  | 
|  |         field = 'talisman_tier',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('talisman_tier'),
 |  | 
|  |     },
 |  | 
|  |     
 |  | 
|  |     -- flasks
 |  | 
|  |     charges_max = {
 |  | 
|  |         field = 'charges_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('charges_max'),
 |  | 
|  |     },
 |  | 
|  |     charges_per_use = {
 |  | 
|  |         field = 'charges_per_use',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('charges_per_use'),
 |  | 
|  |     },
 |  | 
|  |     flask_mana = {
 |  | 
|  |         field = 'mana',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('flask_mana'),
 |  | 
|  |     },
 |  | 
|  |     flask_life = {
 |  | 
|  |         field = 'life',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('flask_life'),
 |  | 
|  |     },
 |  | 
|  |     flask_duration = {
 |  | 
|  |         field = 'duration',
 |  | 
|  |         type = 'Float',
 |  | 
|  |         func = m_util.cast.factory.number('flask_duration'),
 |  | 
|  |     },
 |  | 
|  |     buff_id = {
 |  | 
|  |         field = 'id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     buff_values = {
 |  | 
|  |         field = 'buff_values',
 |  | 
|  |         type = 'List (,) of Integer',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local values = {}
 |  | 
|  |             local i = 0
 |  | 
|  |             repeat 
 |  | 
|  |                 i = i + 1
 |  | 
|  |                 local key = 'buff_value' .. i
 |  | 
|  |                 values[i] = tonumber(tpl_args[key])
 |  | 
|  |                 tpl_args[key] = nil
 |  | 
|  |             until values[i] == nil
 |  | 
|  |             
 |  | 
|  |             -- needed so the values copyied from unique item base isn't overriden
 |  | 
|  |             if #values >= 1 then
 |  | 
|  |                 tpl_args.buff_values = values
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |         func_copy = function(tpl_args, frame)
 |  | 
|  |             tpl_args.buff_values = m_util.string.split(tpl_args.buff_values, ',%s*')
 |  | 
|  |         end,
 |  | 
|  |         default = function () return {} end,
 |  | 
|  |     },
 |  | 
|  |     buff_stat_text = {
 |  | 
|  |         field = 'stat_text',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     buff_icon = {
 |  | 
|  |         field = 'icon',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.buff_icon = string.format(i18n.status_icon, tpl_args.name) 
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     
 |  | 
|  |     -- weapons
 |  | 
|  |     critical_strike_chance = {
 |  | 
|  |         field = 'critical_strike_chance',
 |  | 
|  |         type = 'Float',
 |  | 
|  |         func = m_util.cast.factory.number('critical_strike_chance'),
 |  | 
|  |     },
 |  | 
|  |     attack_speed = {
 |  | 
|  |         field = 'attack_speed',
 |  | 
|  |         type = 'Float',
 |  | 
|  |         func = m_util.cast.factory.number('attack_speed'),
 |  | 
|  |     },
 |  | 
|  |     weapon_range = {
 |  | 
|  |         field = 'weapon_range',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('weapon_range'),
 |  | 
|  |     },
 |  | 
|  |     physical_damage_min = {
 |  | 
|  |         field = 'physical_damage_min',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('physical_damage_min'),
 |  | 
|  |     },
 |  | 
|  |     physical_damage_max = {
 |  | 
|  |         field = 'physical_damage_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('physical_damage_max'),
 |  | 
|  |     },
 |  | 
|  |     fire_damage_min = {
 |  | 
|  |         field = 'fire_damage_min',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('fire_damage_min'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     fire_damage_max = {
 |  | 
|  |         field = 'fire_damage_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('fire_damage_max'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     cold_damage_min = {
 |  | 
|  |         field = 'cold_damage_min',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('cold_damage_min'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     cold_damage_max = {
 |  | 
|  |         field = 'cold_damage_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('cold_damage_max'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     lightning_damage_min = {
 |  | 
|  |         field = 'lightning_damage_min',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('lightning_damage_min'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     lightning_damage_max = {
 |  | 
|  |         field = 'lightning_damage_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('lightning_damage_max'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     chaos_damage_min = {
 |  | 
|  |         field = 'chaos_damage_min',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('chaos_damage_min'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     chaos_damage_max = {
 |  | 
|  |         field = 'chaos_damage_max',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('chaos_damage_max'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     -- armor-type stuff
 |  | 
|  |     armour = {
 |  | 
|  |         field = 'armour',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('armour'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     energy_shield = {
 |  | 
|  |         field = 'energy_shield',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('energy_shield'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     evasion = {
 |  | 
|  |         field = 'evasion',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('evasion'),
 |  | 
|  |         default = 0,
 |  | 
|  |     },
 |  | 
|  |     -- shields
 |  | 
|  |     block = {
 |  | 
|  |         field = 'block',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('block'),
 |  | 
|  |     },
 |  | 
|  |     -- skill gem stuff
 |  | 
|  |     gem_description = {
 |  | 
|  |         field = 'gem_description',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('gem_description'),
 |  | 
|  |     },
 |  | 
|  |     dexterity_percent = {
 |  | 
|  |         field = 'dexterity_percent',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.percentage('dexterity_percent'),
 |  | 
|  |     },
 |  | 
|  |     strength_percent = {
 |  | 
|  |         field = 'strength_percent',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.percentage('strength_percent'),
 |  | 
|  |     },
 |  | 
|  |     intelligence_percent = {
 |  | 
|  |         field = 'intelligence_percent',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.percentage('intelligence_percent'),
 |  | 
|  |     },
 |  | 
|  |     primary_attribute = {
 |  | 
|  |         field = 'primary_attribute',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             for _, attr in ipairs(m_game.constants.attributes) do
 |  | 
|  |                 local val = tpl_args[attr.long_lower .. '_percent'] 
 |  | 
|  |                 if val and val >= 60 then
 |  | 
|  |                     tpl_args['primary_attribute'] = attr.long_upper
 |  | 
|  |                     return
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             tpl_args['primary_attribute'] = 'None'
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     gem_tags = {
 |  | 
|  |         field = 'gem_tags',
 |  | 
|  |         type = 'List (,) of String',
 |  | 
|  |         -- TODO: default rework
 |  | 
|  |         func = m_util.cast.factory.array_table('gem_tags', {
 |  | 
|  |             tbl = m_game.constants.item.gem_tags,
 |  | 
|  |             errmsg = i18n.errors.invalid_tag,
 |  | 
|  |         }),
 |  | 
|  |         default = function () return {} end,
 |  | 
|  |     },
 |  | 
|  |     -- Support gems only
 |  | 
|  |     support_gem_letter = {
 |  | 
|  |         field = 'support_gem_letter',
 |  | 
|  |         type = 'String(size=1)',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     support_gem_letter_html = {
 |  | 
|  |         field = 'support_gem_letter_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.support_gem_letter == nil then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |         
 |  | 
|  |             -- TODO replace this with a loop possibly
 |  | 
|  |             local css_map = {
 |  | 
|  |                 strength = 'red',
 |  | 
|  |                 intelligence = 'blue',
 |  | 
|  |                 dexterity = 'green',
 |  | 
|  |             }
 |  | 
|  |             local id
 |  | 
|  |             for k, v in pairs(css_map) do
 |  | 
|  |                 k = string.format('%s_percent', k)
 |  | 
|  |                 if tpl_args[k] and tpl_args[k] > 50 then
 |  | 
|  |                     id = v
 |  | 
|  |                     break
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if id ~= nil then
 |  | 
|  |                 local container = mw.html.create('span')
 |  | 
|  |                 container
 |  | 
|  |                     :attr('class', string.format('support-gem-id-%s', id))
 |  | 
|  |                     :wikitext(tpl_args.support_gem_letter)
 |  | 
|  |                     :done()
 |  | 
|  |                 tpl_args.support_gem_letter_html = tostring(container)
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- Maps
 |  | 
|  |     map_tier = {
 |  | 
|  |         field = 'tier',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('tier'),
 |  | 
|  |     },
 |  | 
|  |     map_guild_character = {
 |  | 
|  |         field = 'guild_character',
 |  | 
|  |         type = 'String(size=1)',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     map_area_id = {
 |  | 
|  |         field = 'area_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil, -- TODO: Validate against a query?
 |  | 
|  |     },
 |  | 
|  |     map_area_level = {
 |  | 
|  |         field = 'area_level',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('map_area_level'),
 |  | 
|  |     },
 |  | 
|  |     unique_map_guild_character = {
 |  | 
|  |         field = 'unique_guild_character',
 |  | 
|  |         type = 'String(size=1)',
 |  | 
|  |         func_copy = function(tpl_args, frame)
 |  | 
|  |             tpl_args.map_guild_character = tpl_args.unique_map_guild_character
 |  | 
|  |         end,
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     unique_map_area_id = {
 |  | 
|  |         field = 'unique_area_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil, -- TODO: Validate against a query?
 |  | 
|  |         func_copy = function(tpl_args, frame)
 |  | 
|  |             tpl_args.map_area_id = tpl_args.unique_map_area_id
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     unique_map_area_level = {
 |  | 
|  |         field = 'unique_area_level',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('unique_map_area_level'),
 |  | 
|  |         func_copy = function(tpl_args, frame)
 |  | 
|  |             tpl_args.map_area_level = tpl_args.unique_map_area_level
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     --
 |  | 
|  |     -- Currency-like items
 |  | 
|  |     --
 |  | 
|  |     stack_size = {
 |  | 
|  |         field = 'stack_size',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('stack_size'),
 |  | 
|  |     },
 |  | 
|  |     stack_size_currency_tab = {
 |  | 
|  |         field = 'stack_size_currency_tab',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('stack_size_currency_tab'),
 |  | 
|  |     },
 |  | 
|  |     description = {
 |  | 
|  |         field = 'description',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('description'),
 |  | 
|  |     },
 |  | 
|  |     cosmetic_type = {
 |  | 
|  |         field = 'cosmetic_type',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = core.factory.cast_text('cosmetic_type'),
 |  | 
|  |     },
 |  | 
|  |     -- for essences
 |  | 
|  |     is_essence = {
 |  | 
|  |         field = nil,
 |  | 
|  |         func = m_util.cast.factory.boolean('is_essence'),
 |  | 
|  |         default = false,
 |  | 
|  |     },
 |  | 
|  |     essence_level_restriction = {
 |  | 
|  |         field = 'level_restriction',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('essence_level_restriction'),
 |  | 
|  |     },
 |  | 
|  |     essence_level = {
 |  | 
|  |         field = 'level',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('essence_level'),
 |  | 
|  |     },
 |  | 
|  |     --
 |  | 
|  |     -- hideout doodads (HideoutDoodads.dat)
 |  | 
|  |     --
 |  | 
|  |     is_master_doodad = {
 |  | 
|  |         field = 'is_master_doodad',
 |  | 
|  |         type = 'Boolean',
 |  | 
|  |         func = m_util.cast.factory.boolean('is_master_doodad'),
 |  | 
|  |     },
 |  | 
|  |     master = {
 |  | 
|  |         field = 'master',
 |  | 
|  |         type = 'String',
 |  | 
|  |         -- todo validate against list of master names
 |  | 
|  |         func = m_util.cast.factory.table('master', {key='full', tbl=m_game.constants.masters}),
 |  | 
|  |     },
 |  | 
|  |     master_level_requirement = {
 |  | 
|  |         field = 'level_requirement',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('master_level_requirement'),
 |  | 
|  |     },
 |  | 
|  |     master_favour_cost = {
 |  | 
|  |         field = 'favour_cost',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('master_favour_cost'),
 |  | 
|  |     },
 |  | 
|  |     variation_count = {
 |  | 
|  |         field = 'variation_count',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('variation_count'),
 |  | 
|  |     },
 |  | 
|  |     -- Propehcy
 |  | 
|  |     prophecy_id = {
 |  | 
|  |         field = 'prophecy_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = nil,
 |  | 
|  |     },
 |  | 
|  |     prediction_text = {
 |  | 
|  |         field = 'prediction_text',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('prediction_text'),
 |  | 
|  |     },
 |  | 
|  |     seal_cost = {
 |  | 
|  |         field = 'seal_cost',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('seal_cost'),
 |  | 
|  |     },
 |  | 
|  |     prophecy_reward = {
 |  | 
|  |         field = 'reward',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.cast_text('prophecy_reward'),
 |  | 
|  |     },
 |  | 
|  |     prophecy_objective = {
 |  | 
|  |         field = 'objective',
 |  | 
|  |         type = 'Text',
 |  | 
|  |        func = core.factory.cast_text('prophecy_objective'),
 |  | 
|  |     },
 |  | 
|  |     -- Divination cards
 |  | 
|  |     card_art = {
 |  | 
|  |         field = 'card_art',
 |  | 
|  |         type = 'Page',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.card_art = string.format(i18n.divination_card_art, tpl_args.name)
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- derived stats
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     
 |  | 
|  |     -- For rarity != normal, rarity already verified
 |  | 
|  |     base_item = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'base_item',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.base_item = tpl_args.base_item_data['items.name']
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     base_item_id = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'base_item_id',
 |  | 
|  |         type = 'String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.base_item_id = tpl_args.base_item_data['items.metadata_id']
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     base_item_page = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'base_item_page',
 |  | 
|  |         type = 'Page',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             tpl_args.base_item_page = tpl_args.base_item_data['items._pageName']
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     name_list = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'name_list',
 |  | 
|  |         type = 'List (�) of String',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.name_list ~= nil then
 |  | 
|  |                 tpl_args.name_list = m_util.string.split(tpl_args.name_list, ',%s*')
 |  | 
|  |                 tpl_args.name_list[#tpl_args.name_list+1] = tpl_args.name
 |  | 
|  |             else
 |  | 
|  |                 tpl_args.name_list = {tpl_args.name}
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     frame_type = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'frame_type',
 |  | 
|  |         type = 'String',
 |  | 
|  |         property = nil,
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args._flags.is_prophecy then
 |  | 
|  |                 tpl_args.frame_type = 'prophecy'
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |         
 |  | 
|  |             local var = core.class_specifics[tpl_args.class_id]
 |  | 
|  |             if var ~= nil and var.frame_type ~= nil then
 |  | 
|  |                 tpl_args.frame_type = var.frame_type
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if tpl_args.is_relic then
 |  | 
|  |                 tpl_args.frame_type = 'relic'
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             tpl_args.frame_type = string.lower(tpl_args.rarity)
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     --
 |  | 
|  |     -- args populated by mod validation
 |  | 
|  |     -- 
 |  | 
|  |     mods = {
 |  | 
|  |         default = function () return {} end,
 |  | 
|  |         func_fetch = function (tpl_args, frame)
 |  | 
|  |             local results = m_cargo.query(
 |  | 
|  |                 {'items' ,'item_mods'},
 |  | 
|  |                 {'item_mods.id', 'item_mods.is_implicit', 'item_mods.is_random', 'item_mods.text'},
 |  | 
|  |                 {
 |  | 
|  |                     join = 'items._pageID=item_mods._pageID',
 |  | 
|  |                     where = string.format('items._pageName="%s" AND item_mods.is_implicit=1', tpl_args.base_item_page),
 |  | 
|  |                 }
 |  | 
|  |             )
 |  | 
|  |             for _, row in ipairs(results) do
 |  | 
|  |                 -- Handle text-only mods
 |  | 
|  |                 local result
 |  | 
|  |                 if row['item_mods.id'] == nil then
 |  | 
|  |                     result = row['item_mods.text']
 |  | 
|  |                 end
 |  | 
|  |                 tpl_args._mods[#tpl_args._mods+1] = {
 |  | 
|  |                     result=result,
 |  | 
|  |                     id=row['item_mods.id'],
 |  | 
|  |                     stat_text=row['item_mods.text'],
 |  | 
|  |                     is_implicit=m_util.cast.boolean(row['item_mods.is_implicit']),
 |  | 
|  |                     is_random=m_util.cast.boolean(row['item_mods.is_random']),
 |  | 
|  |                 }
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     physical_damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'physical_damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.damage_html{key='physical'},
 |  | 
|  |     },
 |  | 
|  |     fire_damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'fire_damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.damage_html{key='fire'},
 |  | 
|  |     },
 |  | 
|  |     cold_damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'cold_damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.damage_html{key='cold'},
 |  | 
|  |     },
 |  | 
|  |     lightning_damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'lightning_damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.damage_html{key='lightning'},
 |  | 
|  |     },
 |  | 
|  |     chaos_damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'chaos_damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = core.factory.damage_html{key='chaos'},
 |  | 
|  |     },
 |  | 
|  |     damage_avg = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'damage_avg',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local dmg = {min=0, max=0}
 |  | 
|  |             for key, _ in pairs(dmg) do
 |  | 
|  |                 for _, data in ipairs(m_game.constants.damage_types) do
 |  | 
|  |                     dmg[key] = dmg[key] + tpl_args[string.format('%s_damage_%s_range_average', data.short_lower, key)]
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             dmg = (dmg.min + dmg.max) / 2
 |  | 
|  |             
 |  | 
|  |             tpl_args.damage_avg = dmg
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     damage_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'damage_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local text = {}
 |  | 
|  |             for _, data in ipairs(m_game.constants.damage_types) do
 |  | 
|  |                 local value = tpl_args[data.short_lower .. '_damage_html']
 |  | 
|  |                 if value ~= nil then
 |  | 
|  |                     text[#text+1] = value
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             if #text > 0 then
 |  | 
|  |                 tpl_args.damage_html = table.concat(text, '<br>')
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     item_limit = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'item_limit',
 |  | 
|  |         type = 'Integer',
 |  | 
|  |         func = m_util.cast.factory.number('item_limit'),
 |  | 
|  |     },
 |  | 
|  |     jewel_radius_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'radius_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             local radius = tpl_args._stats.local_jewel_effect_base_radius
 |  | 
|  |             if radius then
 |  | 
|  |                 radius = radius.min
 |  | 
|  |                 tpl_args.jewel_radius_html = string.format('%s (%i)', (m_game.constants.item.jewel_radius_to_size[radius] or '?'), radius)
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     drop_areas_html = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'drop_areas_html',
 |  | 
|  |         type = 'Text',
 |  | 
|  |         func = function(tpl_args, frame)
 |  | 
|  |             if tpl_args.drop_areas_data == nil then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if tpl_args.drop_areas_html ~= nil then
 |  | 
|  |                 return
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             local areas = {}
 |  | 
|  |             for _, data in pairs(tpl_args.drop_areas_data) do
 |  | 
|  |                 -- skip legacy maps in the drop html listing
 |  | 
|  |                 if not (string.match(data['areas.id'], '^MapAtlas.*') or string.match(data['areas.id'], '^Map2.*')) then
 |  | 
|  |                     local page
 |  | 
|  |                     if data['areas.main_page'] then
 |  | 
|  |                         page = data['areas.main_page']
 |  | 
|  |                     else
 |  | 
|  |                         page = data['areas._pageName']
 |  | 
|  |                     end
 |  | 
|  |                     areas[#areas+1] = string.format('[[%s]]', page)
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             tpl_args.drop_areas_html = table.concat(areas, ' • ')
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     release_version = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'release_version',
 |  | 
|  |         type = 'String'
 |  | 
|  |     },
 |  | 
|  |     removal_version = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = 'removal_version',
 |  | 
|  |         type = 'String',
 |  | 
|  |     },
 |  | 
|  |     --
 |  | 
|  |     -- args governing use of the template itself
 |  | 
|  |     -- 
 |  | 
|  |     suppress_improper_modifiers_category = {
 |  | 
|  |         no_copy = true,
 |  | 
|  |         field = nil,
 |  | 
|  |         func = m_util.cast.factory.boolean('suppress_improper_modifiers_category'),
 |  | 
|  |         default = false,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.stat_map = {
 |  | 
|  |     required_level_final = {
 |  | 
|  |         field = 'required_level',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_level_requirement_+',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=1, max=1},
 |  | 
|  |         },
 |  | 
|  |         minimum = 1,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     weapon_range = {
 |  | 
|  |         field = 'weapon_range',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_weapon_range_+',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     physical_damage_min = {
 |  | 
|  |         field = 'physical_damage_min',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_minimum_added_physical_damage',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_physical_damage_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     physical_damage_max = {
 |  | 
|  |         field = 'physical_damage_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_maximum_added_physical_damage',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_physical_damage_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     fire_damage_min = {
 |  | 
|  |         field = 'fire_damage_min',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_minimum_added_fire_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'fire',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     fire_damage_max = {
 |  | 
|  |         field = 'fire_damage_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_maximum_added_fire_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'fire',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     cold_damage_min = {
 |  | 
|  |         field = 'cold_damage_min',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_minimum_added_cold_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'cold',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     cold_damage_max = {
 |  | 
|  |         field = 'cold_damage_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_maximum_added_cold_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'cold',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     lightning_damage_min = {
 |  | 
|  |         field = 'lightning_damage_min',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_minimum_added_lightning_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'lightning',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     lightning_damage_max = {
 |  | 
|  |         field = 'lightning_damage_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_maximum_added_lightning_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'lightning',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     chaos_damage_min = {
 |  | 
|  |         field = 'chaos_damage_min',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_minimum_added_chaos_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'chaos',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     chaos_damage_max = {
 |  | 
|  |         field = 'chaos_damage_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_maximum_added_chaos_damage',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'chaos',
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     critical_strike_chance = {
 |  | 
|  |         field = 'critical_strike_chance',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_critical_strike_chance',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_critical_strike_chance_+%',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_weapon_always_crit'] = {min=100, max=100},
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%.2f%%',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     attack_speed = {
 |  | 
|  |         field = 'attack_speed',
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_attack_speed_+%',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%.2f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     flask_life = {
 |  | 
|  |         field = 'life',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_flask_life_to_recover',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_flask_life_to_recover_+%',
 |  | 
|  |             'local_flask_amount_to_recover_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     flask_mana = {
 |  | 
|  |         field = 'mana',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_flask_mana_to_recover',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_flask_mana_to_recover_+%',
 |  | 
|  |             'local_flask_amount_to_recover_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     flask_duration = {
 |  | 
|  |         field = 'duration',
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_flask_duration_+%',
 |  | 
|  |             -- regular quality isn't used here because it doesn't increase duration of life/mana/hybrid flasks
 |  | 
|  |             'quality_flask_duration',
 |  | 
|  |         },
 |  | 
|  |         stats_increased_inverse = {
 |  | 
|  |             'local_flask_recovery_speed_+%',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%.2f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     charges_per_use = {
 |  | 
|  |         field = 'charges_per_use',
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_charges_used_+%',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     charges_max = {
 |  | 
|  |         field = 'charges_max',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_extra_max_charges',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_max_charges_+%',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     block = {
 |  | 
|  |         field = 'block',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_additional_block_chance_%',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i%%',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     armour = {
 |  | 
|  |         field = 'armour',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_base_physical_damage_reduction_rating',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_physical_damage_reduction_rating_+%',
 |  | 
|  |             'local_armour_and_energy_shield_+%',
 |  | 
|  |             'local_armour_and_evasion_+%',
 |  | 
|  |             'local_armour_and_evasion_and_energy_shield_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     evasion = {
 |  | 
|  |         field = 'evasion',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_base_evasion_rating',
 |  | 
|  |             'local_evasion_rating_and_energy_shield',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_evasion_rating_+%',
 |  | 
|  |             'local_evasion_and_energy_shield_+%',
 |  | 
|  |             'local_armour_and_evasion_+%',
 |  | 
|  |             'local_armour_and_evasion_and_energy_shield_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     energy_shield = {
 |  | 
|  |         field = 'energy_shield',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_energy_shield',
 |  | 
|  |             'local_evasion_rating_and_energy_shield',
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_energy_shield_+%',
 |  | 
|  |             'local_armour_and_energy_shield_+%',
 |  | 
|  |             'local_evasion_and_energy_shield_+%',
 |  | 
|  |             'local_armour_and_evasion_and_energy_shield_+%',
 |  | 
|  |             'quality',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     required_dexterity = {
 |  | 
|  |         field = 'required_dexterity',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_dexterity_requirement_+'
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_dexterity_requirement_+%',
 |  | 
|  |             'local_attribute_requirements_+%',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
 |  | 
|  |             ['local_no_attribute_requirements'] = {min=0, max=0},
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     required_intelligence = {
 |  | 
|  |         field = 'required_intelligence',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_intelligence_requirement_+'
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_intelligence_requirement_+%',
 |  | 
|  |             'local_attribute_requirements_+%',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
 |  | 
|  |             ['local_no_attribute_requirements'] = {min=0, max=0},
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     required_strength = {
 |  | 
|  |         field = 'required_strength',
 |  | 
|  |         stats_add = {
 |  | 
|  |             'local_strength_requirement_+'
 |  | 
|  |         },
 |  | 
|  |         stats_increased = {
 |  | 
|  |             'local_strength_requirement_+%',
 |  | 
|  |             'local_attribute_requirements_+%',
 |  | 
|  |         },
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
 |  | 
|  |             ['local_no_attribute_requirements'] = {min=0, max=0},
 |  | 
|  |         },
 |  | 
|  |         minimum = 0,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             fmt = '%i',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     map_area_level = {
 |  | 
|  |         field = 'map_area_level',
 |  | 
|  |         stats_override = {
 |  | 
|  |             ['map_item_level_override'] = true,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.dps_map = {
 |  | 
|  |     {
 |  | 
|  |         name = 'physical_dps',
 |  | 
|  |         field = 'physical_dps',
 |  | 
|  |         damage_args = {'physical_damage', },
 |  | 
|  |         label_infobox = i18n.tooltips.physical_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'value',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'fire_dps',
 |  | 
|  |         field = 'fire_dps',
 |  | 
|  |         damage_args = {'fire_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.fire_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'fire',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'cold_dps',
 |  | 
|  |         field = 'cold_dps',
 |  | 
|  |         damage_args = {'cold_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.cold_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'cold',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'lightning_dps',
 |  | 
|  |         field = 'lightning_dps',
 |  | 
|  |         damage_args = {'lightning_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.lightning_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'lightning',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'chaos_dps',
 |  | 
|  |         field = 'chaos_dps',
 |  | 
|  |         damage_args = {'chaos_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.chaos_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'chaos',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'elemental_dps',
 |  | 
|  |         field = 'elemental_dps',
 |  | 
|  |         damage_args = {'fire_damage', 'cold_damage', 'lightning_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.elemental_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'value',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'poison_dps',
 |  | 
|  |         field = 'poison_dps',
 |  | 
|  |         damage_args = {'physical_damage', 'chaos_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.poison_dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'value',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         name = 'dps',
 |  | 
|  |         field = 'dps',
 |  | 
|  |         damage_args = {'physical_damage', 'fire_damage', 'cold_damage', 'lightning_damage', 'chaos_damage'},
 |  | 
|  |         label_infobox = i18n.tooltips.dps,
 |  | 
|  |         html_fmt_options = {
 |  | 
|  |             color = 'value',
 |  | 
|  |             fmt = '%.1f',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo = {}
 |  | 
|  | core.cargo.items = {
 |  | 
|  |     table = 'items',
 |  | 
|  |     fields = {
 |  | 
|  |         html = core.map.html,
 |  | 
|  |         implicit_stat_text = core.map.implicit_stat_text,
 |  | 
|  |         explicit_stat_text = core.map.explicit_stat_text,
 |  | 
|  |         stat_text = core.map.stat_text,
 |  | 
|  |         class = core.map.class,
 |  | 
|  |         class_id = core.map.class_id,
 |  | 
|  |         rarity = core.map.rarity,
 |  | 
|  |         name = core.map.name,
 |  | 
|  |         size_x = core.map.size_x,
 |  | 
|  |         size_y = core.map.size_y,
 |  | 
|  |         drop_enabled = core.map.drop_enabled,
 |  | 
|  |         drop_level = core.map.drop_level,
 |  | 
|  |         drop_level_maximum = core.map.drop_level_maximum,
 |  | 
|  |         drop_leagues = core.map.drop_leagues,
 |  | 
|  |         drop_areas = core.map.drop_areas,
 |  | 
|  |         drop_areas_html = core.map.drop_areas_html,
 |  | 
|  |         drop_text = core.map.drop_text,
 |  | 
|  |         required_level = core.map.required_level,
 |  | 
|  |         required_level_final = core.map.required_level_final,
 |  | 
|  |         required_dexterity = core.map.required_dexterity,
 |  | 
|  |         required_strength = core.map.required_strength,
 |  | 
|  |         required_intelligence = core.map.required_intelligence,
 |  | 
|  |         inventory_icon = core.map.inventory_icon,
 |  | 
|  |         alternate_art_inventory_icons = core.map.alternate_art_inventory_icons,
 |  | 
|  |         buff_icon = core.map.buff_icon,
 |  | 
|  |         cannot_be_traded_or_modified = core.map.cannot_be_traded_or_modified,
 |  | 
|  |         help_text = core.map.help_text,
 |  | 
|  |         flavour_text = core.map.flavour_text,
 |  | 
|  |         tags = core.map.tags,
 |  | 
|  |         metadata_id = core.map.metadata_id,
 |  | 
|  |         is_corrupted = core.map.is_corrupted,
 |  | 
|  |         is_relic = core.map.is_relic,
 |  | 
|  |         quality = core.map.quality,
 |  | 
|  |         base_item = core.map.base_item,
 |  | 
|  |         base_item_id = core.map.base_item_id,
 |  | 
|  |         base_item_page = core.map.base_item_page,
 |  | 
|  |         frame_type = core.map.frame_type,
 |  | 
|  |         name_list = core.map.name_list,
 |  | 
|  |         description = core.map.description,
 |  | 
|  |         release_version = core.map.release_version,
 |  | 
|  |         removal_version = core.map.removal_version,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.item_sell_prices = {
 |  | 
|  |     table = 'item_sell_prices',
 |  | 
|  |     fields = {
 |  | 
|  |         amount = {
 |  | 
|  |             field = 'amount',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         name = {
 |  | 
|  |             field = 'name',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  |   |  | 
|  | core.cargo.item_purchase_costs = {
 |  | 
|  |     table = 'item_purchase_costs',
 |  | 
|  |     fields = {
 |  | 
|  |         amount = {
 |  | 
|  |             field = 'amount',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         name = {
 |  | 
|  |             field = 'name',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |         rarity = {
 |  | 
|  |             field = 'rarity',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.item_mods = {
 |  | 
|  |     table = 'item_mods',
 |  | 
|  |     fields = {
 |  | 
|  |         id = {
 |  | 
|  |             field = 'id',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |         stat_text = {
 |  | 
|  |             field = 'text',
 |  | 
|  |             type = 'Text',
 |  | 
|  |         },
 |  | 
|  |         is_implicit = {
 |  | 
|  |             field = 'is_implicit',
 |  | 
|  |             type = 'Boolean',
 |  | 
|  |         },
 |  | 
|  |         is_random = {
 |  | 
|  |             field = 'is_random',
 |  | 
|  |             type = 'Boolean',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.item_stats = {
 |  | 
|  |     table = 'item_stats',
 |  | 
|  |     fields = {
 |  | 
|  |         id = {
 |  | 
|  |             field = 'id',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |         min = {
 |  | 
|  |             field = 'min',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         max = {
 |  | 
|  |             field = 'max',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         avg = {
 |  | 
|  |             field = 'avg',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         is_implicit = {
 |  | 
|  |             field = 'is_implicit',
 |  | 
|  |             type = 'Boolean',
 |  | 
|  |         },
 |  | 
|  |         is_random = {
 |  | 
|  |             field = 'is_random',
 |  | 
|  |             type = 'Boolean',
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | -- There probably will be a table named "buffs" in the future, so "item_buffs" is the best solution here
 |  | 
|  | core.cargo.item_buffs = {
 |  | 
|  |     table = 'item_buffs',
 |  | 
|  |     fields = {
 |  | 
|  |         id = core.map.buff_id,
 |  | 
|  |         buff_values = core.map.buff_values,
 |  | 
|  |         stat_text = core.map.buff_stat_text,
 |  | 
|  |         icon = core.map.buff_icon,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.upgraded_from_sets = {
 |  | 
|  |     table = 'upgraded_from_sets',
 |  | 
|  |     fields = {
 |  | 
|  |         set_id = {
 |  | 
|  |             field = 'set_id',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         text = {
 |  | 
|  |             field = 'text',
 |  | 
|  |             type = 'Text',
 |  | 
|  |         },
 |  | 
|  |     }
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.upgraded_from_groups = {
 |  | 
|  |     table = 'upgraded_from_groups',
 |  | 
|  |     fields = {
 |  | 
|  |         group_id = {
 |  | 
|  |             field = 'group_id',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         set_id = {
 |  | 
|  |             field = 'set_id',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         item_id = {
 |  | 
|  |             field = 'item_id',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |         item_name = {
 |  | 
|  |             field = 'item_name',
 |  | 
|  |             type = 'String',
 |  | 
|  |         },
 |  | 
|  |         item_page = {
 |  | 
|  |             field = 'item_page',
 |  | 
|  |             type = 'Page',
 |  | 
|  |         },
 |  | 
|  |         integer = {
 |  | 
|  |             field = 'amount',
 |  | 
|  |             type = 'Integer',
 |  | 
|  |         },
 |  | 
|  |         notes = {
 |  | 
|  |             field = 'notes',
 |  | 
|  |             type = 'Text',
 |  | 
|  |         },
 |  | 
|  |     }
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.amulets = {
 |  | 
|  |     table = 'amulets',
 |  | 
|  |     fields = {
 |  | 
|  |         is_talisman = core.map.is_talisman,
 |  | 
|  |         talisman_tier = core.map.talisman_tier,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.flasks = {
 |  | 
|  |     table = 'flasks',
 |  | 
|  |     fields = {
 |  | 
|  |         -- All flasks
 |  | 
|  |         duration = core.map.flask_duration,
 |  | 
|  |         charges_max = core.map.charges_max,
 |  | 
|  |         charges_per_use = core.map.charges_per_use,
 |  | 
|  |         -- Life/Mana/Hybrid flasks
 |  | 
|  |         life = core.map.flask_life,
 |  | 
|  |         mana = core.map.flask_mana,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.weapons = {
 |  | 
|  |     table = 'weapons',
 |  | 
|  |     fields = {
 |  | 
|  |         critical_strike_chance = core.map.critical_strike_chance,
 |  | 
|  |         attack_speed = core.map.attack_speed,
 |  | 
|  |         weapon_range = core.map.weapon_range,
 |  | 
|  |         physical_damage_min = core.map.physical_damage_min,
 |  | 
|  |         physical_damage_max = core.map.physical_damage_max,
 |  | 
|  |         physical_damage_html = core.map.physical_damage_html,
 |  | 
|  |         fire_damage_html = core.map.fire_damage_html,
 |  | 
|  |         cold_damage_html = core.map.cold_damage_html,
 |  | 
|  |         lightning_damage_html = core.map.lightning_damage_html,
 |  | 
|  |         chaos_damage_html = core.map.chaos_damage_html,
 |  | 
|  |         damage_avg = core.map.damage_avg,
 |  | 
|  |         damage_html = core.map.damage_html,
 |  | 
|  |         
 |  | 
|  |         -- Values added via stat population
 |  | 
|  |         fire_damage_min = core.map.fire_damage_min,
 |  | 
|  |         fire_damage_max = core.map.fire_damage_max,
 |  | 
|  |         cold_damage_min = core.map.cold_damage_min,
 |  | 
|  |         cold_damage_max = core.map.cold_damage_max,
 |  | 
|  |         lightning_damage_min = core.map.lightning_damage_min,
 |  | 
|  |         lightning_damage_max = core.map.lightning_damage_max,
 |  | 
|  |         chaos_damage_min = core.map.chaos_damage_min,
 |  | 
|  |         chaos_damage_max = core.map.chaos_damage_max,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.armours = {
 |  | 
|  |     table = 'armours',
 |  | 
|  |     fields = {
 |  | 
|  |         armour = core.map.armour,
 |  | 
|  |         energy_shield = core.map.energy_shield,
 |  | 
|  |         evasion = core.map.evasion,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.shields = {
 |  | 
|  |     table = 'shields',
 |  | 
|  |     fields = {
 |  | 
|  |         block = core.map.block,
 |  | 
|  |     }
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.skill_gems = {
 |  | 
|  |     table = 'skill_gems',
 |  | 
|  |     fields = {
 |  | 
|  |         gem_description = core.map.gem_description,
 |  | 
|  |         dexterity_percent = core.map.dexterity_percent,
 |  | 
|  |         strength_percent = core.map.strength_percent,
 |  | 
|  |         intelligence_percent = core.map.intelligence_percent,
 |  | 
|  |         primary_attribute = core.map.primary_attribute,
 |  | 
|  |         gem_tags = core.map.gem_tags,
 |  | 
|  |         -- Support Skill Gems
 |  | 
|  |         support_gem_letter = core.map.support_gem_letter,
 |  | 
|  |         support_gem_letter_html = core.map.support_gem_letter_html,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.maps = {
 |  | 
|  |     table = 'maps',
 |  | 
|  |     fields = {
 |  | 
|  |         tier = core.map.map_tier,
 |  | 
|  |         guild_character = core.map.map_guild_character,
 |  | 
|  |         unique_guild_character = core.map.unique_map_guild_character,
 |  | 
|  |         area_id = core.map.map_area_id,
 |  | 
|  |         unique_area_id = core.map.unique_map_area_id,
 |  | 
|  |         
 |  | 
|  |         -- REMOVE?
 |  | 
|  |         area_level = core.map.map_area_level,
 |  | 
|  |         unique_area_level = core.map.unique_map_area_level,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.stackables = {
 |  | 
|  |     table = 'stackables',
 |  | 
|  |     fields = {
 |  | 
|  |         stack_size = core.map.stack_size,
 |  | 
|  |         stack_size_currency_tab = core.map.stack_size_currency_tab,
 |  | 
|  |         cosmetic_type = core.map.cosmetic_type,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.essences = {
 |  | 
|  |     table = 'essences',
 |  | 
|  |     fields = {
 |  | 
|  |         level_restriction = core.map.essence_level_restriction,
 |  | 
|  |         level = core.map.essence_level,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.hideout_doodads = {
 |  | 
|  |     table = 'hideout_doodads',
 |  | 
|  |     fields = {
 |  | 
|  |         is_master_doodad = core.map.is_master_doodad,
 |  | 
|  |         master = core.map.master,
 |  | 
|  |         master_level_requirement = core.map.master_level_requirement,
 |  | 
|  |         master_favour_cost = core.map.master_favour_cost,
 |  | 
|  |         variation_count = core.map.variation_count,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.prophecies = {
 |  | 
|  |     table = 'prophecies',
 |  | 
|  |     fields = {
 |  | 
|  |         prophecy_id = core.map.prophecy_id,
 |  | 
|  |         prediction_text = core.map.prediction_text,
 |  | 
|  |         seal_cost = core.map.seal_cost,
 |  | 
|  |         objective = core.map.prophecy_objective,
 |  | 
|  |         reward = core.map.prophecy_reward,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.divination_cards = {
 |  | 
|  |     table = 'divination_cards',
 |  | 
|  |     fields = {
 |  | 
|  |         card_art = core.map.card_art,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.cargo.jewels = {
 |  | 
|  |     table = 'jewels',
 |  | 
|  |     fields = {
 |  | 
|  |         item_limit = core.map.item_limit,
 |  | 
|  |         radius_html = core.map.jewel_radius_html,
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | -- TODO: Second pass for i18n item classes
 |  | 
|  | -- base item is default, but will be validated later
 |  | 
|  | -- Notes:
 |  | 
|  | --  inventory_icon must always be before alternate_art_inventory_icons 
 |  | 
|  | --  drop_areas after tags
 |  | 
|  | --  is_relic after rarity
 |  | 
|  | core.default_args = {
 |  | 
|  |     'rarity', 'name', 'name_list', 'size_x', 'size_y', 
 |  | 
|  |     'drop_enabled', 'drop_level', 'drop_level_maximum', 'drop_leagues',  'drop_text', 'required_level', 'required_level_final', 
 |  | 
|  |     'inventory_icon', 'alternate_art_inventory_icons', 'flavour_text', 
 |  | 
|  |     'cannot_be_traded_or_modified', 'help_text', 'tags', 'metadata_id', 'is_corrupted', 'is_relic', 'purchase_costs', 'sell_prices_override', 'mods',
 |  | 
|  |     'drop_areas', 'drop_areas_html',
 |  | 
|  |     
 |  | 
|  |     'suppress_improper_modifiers_category',
 |  | 
|  |     'is_prophecy',
 |  | 
|  |     'class',
 |  | 
|  | }
 |  | 
|  | -- frame_type is needed in stat_text
 |  | 
|  | core.late_args = {'frame_type', 'implicit_stat_text', 'explicit_stat_text', 'stat_text'}
 |  | 
|  | core.prophecy_args = {'prophecy_id', 'prediction_text', 'seal_cost', 'prophecy_objective', 'prophecy_reward'}
 |  | 
|  | core.tables = {'items'}
 |  | 
|  | core.class_groups = {
 |  | 
|  |     flasks = {
 |  | 
|  |         tables = {'flasks'},
 |  | 
|  |         keys = {['LifeFlask'] = true, ['ManaFlask'] = true, ['HybridFlask'] = true, ['UtilityFlask'] = true, ['UtilityFlaskCritical'] = true},
 |  | 
|  |         args = {'quality', 'flask_duration', 'charges_max', 'charges_per_use'},
 |  | 
|  |     },
 |  | 
|  |     weapons = {
 |  | 
|  |         tables = {'weapons'},
 |  | 
|  |         keys = {['Claw'] = true, ['Dagger'] = true, ['Wand'] = true, ['One Hand Sword'] = true, ['Thrusting One Hand Sword'] = true, ['One Hand Axe'] = true, ['One Hand Mace'] = true, ['Bow'] = true, ['Staff'] = true, ['Two Hand Sword'] = true, ['Two Hand Axe'] = true, ['Two Hand Mace'] = true, ['Sceptre'] = true, ['FishingRod'] = true},
 |  | 
|  |         args = {'quality', 'required_dexterity', 'required_intelligence', 'required_strength', 'critical_strike_chance', 'attack_speed', 'physical_damage_min', 'physical_damage_max', 'lightning_damage_min', 'lightning_damage_max', 'cold_damage_min', 'cold_damage_max', 'fire_damage_min', 'fire_damage_max', 'chaos_damage_min', 'chaos_damage_max', 'weapon_range'},
 |  | 
|  |         late_args = {'physical_damage_html', 'fire_damage_html', 'cold_damage_html', 'lightning_damage_html', 'chaos_damage_html', 'damage_avg', 'damage_html'},
 |  | 
|  |     },
 |  | 
|  |     gems = {
 |  | 
|  |         tables = {'skill_gems'},
 |  | 
|  |         keys = {['Active Skill Gem'] = true, ['Support Skill Gem'] = true},
 |  | 
|  |         args = {'dexterity_percent', 'strength_percent', 'intelligence_percent', 'primary_attribute', 'gem_tags'},
 |  | 
|  |     },
 |  | 
|  |     armor = {
 |  | 
|  |         tables = {'armours'},
 |  | 
|  |         keys = {['Gloves'] = true, ['Boots'] = true, ['Body Armour'] = true, ['Helmet'] = true, ['Shield'] = true},
 |  | 
|  |         args = {'quality', 'required_dexterity', 'required_intelligence', 'required_strength', 'armour', 'energy_shield', 'evasion'},
 |  | 
|  |     },
 |  | 
|  |     stackable = {
 |  | 
|  |         tables = {'stackables'},
 |  | 
|  |         keys = {['Currency'] = true, ['StackableCurrency'] = true, ['HideoutDoodad'] = true, ['Microtransaction'] = true, ['DivinationCard'] = true, ['DelveSocketableCurrency'] = true},
 |  | 
|  |         args = {'stack_size', 'stack_size_currency_tab', 'description', 'cosmetic_type'},
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | core.class_specifics = {
 |  | 
|  |     ['Amulet'] = {
 |  | 
|  |         tables = {'amulets'},
 |  | 
|  |         args = {'is_talisman', 'talisman_tier'},
 |  | 
|  |     },
 |  | 
|  |     ['LifeFlask'] = {
 |  | 
|  |         args = {'flask_life'},
 |  | 
|  |     },
 |  | 
|  |     ['ManaFlask'] = {
 |  | 
|  |         args = {'flask_mana'},
 |  | 
|  |     },
 |  | 
|  |     ['HybridFlask'] = {
 |  | 
|  |         args = {'flask_life', 'flask_mana'},
 |  | 
|  |     },
 |  | 
|  |     ['UtilityFlask'] = {
 |  | 
|  |         tables = {'item_buffs'},
 |  | 
|  |         args = {'buff_id', 'buff_values', 'buff_stat_text', 'buff_icon'},
 |  | 
|  |     },
 |  | 
|  |     ['UtilityFlaskCritical'] = {
 |  | 
|  |         tables = {'item_buffs'},
 |  | 
|  |         args = {'buff_id', 'buff_values', 'buff_stat_text', 'buff_icon'},
 |  | 
|  |     },
 |  | 
|  |     ['Active Skill Gem'] = {
 |  | 
|  |         defaults = {
 |  | 
|  |             help_text = i18n.help_text_defaults.active_gem,
 |  | 
|  |             size_x = 1,
 |  | 
|  |             size_y = 1,
 |  | 
|  |         },
 |  | 
|  |         frame_type = 'gem',
 |  | 
|  |     },
 |  | 
|  |     ['Support Skill Gem'] = {
 |  | 
|  |         args = {'support_gem_letter', 'support_gem_letter_html'},
 |  | 
|  |         defaults = {
 |  | 
|  |             help_text = i18n.help_text_defaults.support_gem,
 |  | 
|  |             size_x = 1,
 |  | 
|  |             size_y = 1,
 |  | 
|  |         },
 |  | 
|  |         frame_type = 'gem',
 |  | 
|  |     },
 |  | 
|  |     ['Shield'] = {
 |  | 
|  |         tables = {'shields'},
 |  | 
|  |         args = {'block'},
 |  | 
|  |     },
 |  | 
|  |     ['Map'] = {
 |  | 
|  |         tables = {'maps'},
 |  | 
|  |         args = {'quality', 'map_tier', 'map_guild_character', 'map_area_id', 'map_area_level', 'unique_map_area_id', 'unique_map_area_level', 'unique_map_guild_character'},
 |  | 
|  |         skip_stat_lines = i18n.stat_skip_patterns.maps,
 |  | 
|  |     },
 |  | 
|  |     ['Currency'] = {
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['StackableCurrency'] = {
 |  | 
|  |         tables = {'essences', 'prophecies'},
 |  | 
|  |         args = {'is_essence', 'essence_level_restriction', 'essence_level'},
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['Microtransaction'] = {
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['HideoutDoodad'] = {
 |  | 
|  |         tables = {'hideout_doodads'},
 |  | 
|  |         args = {'is_master_doodad', 'master', 'master_level_requirement', 'master_favour_cost', 'variation_count'},
 |  | 
|  |         defaults = {
 |  | 
|  |             help_text = i18n.help_text_defaults.hideout_doodad,
 |  | 
|  |         },
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['Jewel'] = {
 |  | 
|  |         tables = {'jewels'},
 |  | 
|  |         late_args = {'item_limit', 'jewel_radius_html'},
 |  | 
|  |         defaults = {
 |  | 
|  |             help_text = i18n.help_text_defaults.jewel,
 |  | 
|  |         },
 |  | 
|  |         skip_stat_lines = i18n.stat_skip_patterns.jewels,
 |  | 
|  |     },
 |  | 
|  |     ['AbyssJewel'] = {
 |  | 
|  |         tables = {'jewels'},
 |  | 
|  |         late_args = {'item_limit', 'jewel_radius_html'},
 |  | 
|  |         skip_stat_lines = i18n.stat_skip_patterns.jewels,
 |  | 
|  |     },
 |  | 
|  |     ['QuestItem'] = {
 |  | 
|  |         args = {'description'},
 |  | 
|  |         frame_type = 'quest',
 |  | 
|  |     },
 |  | 
|  |     ['DivinationCard'] = {
 |  | 
|  |         tables = {'divination_cards'},
 |  | 
|  |         args = {'card_art',},
 |  | 
|  |         frame_type = 'divicard',
 |  | 
|  |     },
 |  | 
|  |     ['LabyrinthItem'] = {
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['LabyrinthTrinket'] = {
 |  | 
|  |         tables = {'item_buffs'},
 |  | 
|  |         args = {'description', 'buff_icon'},
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  |     ['PantheonSoul'] = {
 |  | 
|  |         defaults = {
 |  | 
|  |             cannot_be_traded_or_modified = true,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     ['IncursionItem'] = {
 |  | 
|  |         frame_type = 'currency',
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | -- add defaults from class specifics and class groups
 |  | 
|  | core.item_classes = {}
 |  | 
|  | core.item_classes_extend = {'tables', 'args', 'late_args'}
 |  | 
|  | function core.build_item_classes(tpl_args, frame)
 |  | 
|  |     core.map.class.func(tpl_args, frame)
 |  | 
|  |     
 |  | 
|  |     -- Skip building for anything but the specified class.
 |  | 
|  |     
 |  | 
|  |     for id, data in pairs(m_game.constants.item.classes) do
 |  | 
|  |         if id == tpl_args.class_id then
 |  | 
|  |             core.item_classes[id] = {
 |  | 
|  |                 tables = xtable:new(),
 |  | 
|  |                 args = xtable:new(),
 |  | 
|  |                 late_args = xtable:new(),
 |  | 
|  |                 defaults = {},
 |  | 
|  |             }
 |  | 
|  |             
 |  | 
|  |             core.item_classes[id].tables:insertT(core.tables)
 |  | 
|  |             break
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     for _, row in pairs(core.class_groups) do
 |  | 
|  |         for class, _ in pairs(row.keys) do
 |  | 
|  |             if class == tpl_args.class_id then
 |  | 
|  |                 for _, k in ipairs(core.item_classes_extend) do
 |  | 
|  |                     if row[k] ~= nil then
 |  | 
|  |                         core.item_classes[class][k]:insertT(row[k])
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 break
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     local class_specifics = core.class_specifics[tpl_args.class_id]
 |  | 
|  |     if class_specifics then
 |  | 
|  |         for _, k in ipairs(core.item_classes_extend) do
 |  | 
|  |             if class_specifics[k] ~= nil then
 |  | 
|  |                 core.item_classes[tpl_args.class_id][k]:insertT(class_specifics[k])
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         if class_specifics.defaults ~= nil then
 |  | 
|  |             for key, value in pairs(class_specifics.defaults) do
 |  | 
|  |                 core.item_classes[tpl_args.class_id].defaults[key] = value
 |  | 
|  |             end
 |  | 
|  |        end
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- GroupTable -> RowTable -> formatter function 
 |  | 
|  | --
 |  | 
|  | --
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- Contents here are meant to resemble the ingame infobox of items
 |  | 
|  | --
 |  | 
|  | core.item_display_groups = {
 |  | 
|  |     -- Tags, stats, level, etc
 |  | 
|  |     -- group
 |  | 
|  |     --  group[n].args   - 
 |  | 
|  |     --  group[n].func   -
 |  | 
|  |     --  group[n].inline - if set to false, don't add to html for the inline infobox, but still show this field on the item page
 |  | 
|  |     {
 |  | 
|  |         {
 |  | 
|  |             args = {'cosmetic_type'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'cosmetic_type',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         color = 'default'
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 if tpl_args.class_id == nil then 
 |  | 
|  |                     return false 
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return core.class_groups.weapons.keys[tpl_args.class_id] ~= nil
 |  | 
|  |             end,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local v = i18n.tooltips.item_class_map[tpl_args.class_id]
 |  | 
|  |                 return m_util.html.format_value(tpl_args, frame, {min=v, max=v}, {color = 'default'})
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'gem_tags'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local out = {}
 |  | 
|  |                 for i, tag in ipairs(tpl_args.gem_tags) do
 |  | 
|  |                     out[#out+1] = string.format(i18n.gem_tag_category, tag, tag)
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return table.concat(out, ', ')
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'support_gem_letter_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'support_gem_letter_html',
 |  | 
|  |                         inline = i18n.tooltips.support_icon,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'radius'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'radius',
 |  | 
|  |                         inline = i18n.tooltips.radius,
 |  | 
|  |                         func = core.factory.descriptor_value{key='radius_description'},
 |  | 
|  |                     },
 |  | 
|  |                     [2] = {
 |  | 
|  |                         key = 'radius_secondary',
 |  | 
|  |                         inline = ' / %s',
 |  | 
|  |                         func = core.factory.descriptor_value{key='radius_secondary_description'},
 |  | 
|  |                     },
 |  | 
|  |                     [3] = {
 |  | 
|  |                         key = 'radius_tertiary',
 |  | 
|  |                         inline = ' / %s',
 |  | 
|  |                         func = core.factory.descriptor_value{key='radius_tertiary_description'},
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- TODO: gem level here. Maybe put max level here?
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'mana_cost',
 |  | 
|  |                         hide_default = 100,
 |  | 
|  |                         fmt = function (tpl_args, frame)
 |  | 
|  |                             if tpl_args.has_percentage_mana_cost then
 |  | 
|  |                                 return '%i%%'
 |  | 
|  |                             else
 |  | 
|  |                                 return '%i'
 |  | 
|  |                             end
 |  | 
|  |                         end,
 |  | 
|  |                         inline = function (tpl_args, frame)
 |  | 
|  |                             if tpl_args.has_reservation_mana_cost then
 |  | 
|  |                                 return i18n.tooltips.mana_reserved
 |  | 
|  |                             else
 |  | 
|  |                                 return i18n.tooltips.mana_cost
 |  | 
|  |                             end
 |  | 
|  |                         end,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'mana_multiplier',
 |  | 
|  |                         hide_default = 100,
 |  | 
|  |                         fmt = '%i%%',
 |  | 
|  |                         inline = i18n.tooltips.mana_multiplier,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- TODO: i18n
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'vaal_souls_requirement',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%i (N) / ',
 |  | 
|  |                         inline = i18n.tooltips.vaal_souls_per_use,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'vaal_stored_uses',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.stored_uses,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'stored_uses',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.stored_uses,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'vaal_soul_gain_prevention_time',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         -- Technically it rounds to nearest, but it is given in milliseconds in the data, 
 |  | 
|  |                         fmt = '%.3f sec',
 |  | 
|  |                         inline = i18n.tooltips.vaal_soul_gain_prevention_time,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'cooldown', 
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%.2f sec',
 |  | 
|  |                         inline = i18n.tooltips.cooldown_time,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'cast_time'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'cast_time',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%.2f sec',
 |  | 
|  |                         inline = i18n.tooltips.cast_time,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'critical_strike_chance',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         fmt = '%.2f%%',
 |  | 
|  |                         inline = i18n.tooltips.critical_strike_chance,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type='gem',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'damage_effectiveness',
 |  | 
|  |                         hide_default = 100,
 |  | 
|  |                         fmt = '%i%%',
 |  | 
|  |                         inline = i18n.tooltips.damage_effectiveness,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'projectile_speed'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'projectile_speed',
 |  | 
|  |                         inline = i18n.tooltips.projectile_speed,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Quality is before item stats, but after gem stuff and item class
 |  | 
|  |         {
 |  | 
|  |             args = {'quality'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'quality',
 |  | 
|  |                         fmt = '+%i%%',
 |  | 
|  |                         color = 'mod',
 |  | 
|  |                         inline = i18n.tooltips.quality,
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Weapon only
 |  | 
|  |         {
 |  | 
|  |             args = {'physical_damage_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'physical_damage_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.physical_damage,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },       
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local text = ''
 |  | 
|  |                 for _, dtype in ipairs({'fire_damage_html', 'cold_damage_html', 'lightning_damage_html'}) do
 |  | 
|  |                     local value = tpl_args[dtype]
 |  | 
|  |                     if value ~= nil then
 |  | 
|  |                         text = text .. ' ' .. value
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 if text ~= '' then
 |  | 
|  |                     return string.format(i18n.tooltips.elemental_damage, text)
 |  | 
|  |                 else
 |  | 
|  |                     return
 |  | 
|  |                 end
 |  | 
|  |             end,      
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'chaos_damage_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'chaos_damage_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.chaos_damage,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },       
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'critical_strike_chance_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'critical_strike_chance_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.critical_strike_chance,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'attack_speed_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'attack_speed_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.attacks_per_second,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'weapon_range_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'weapon_range_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.weapon_range,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Map only
 |  | 
|  |         {
 |  | 
|  |             args = {'map_area_level'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_area_level',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.map_level,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'map_tier'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_tier',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.map_tier,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 return tpl_args.map_guild_character ~= nil and tpl_args.rarity == 'Normal'
 |  | 
|  |             end,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_guild_character',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.map_guild_character,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 return tpl_args.unique_map_guild_character ~= nil and tpl_args.rarity == 'Unique'
 |  | 
|  |             end,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'unique_map_guild_character',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.map_guild_character,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type = 'stat',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_item_drop_quantity_+%',
 |  | 
|  |                         fmt = '+%i%%',
 |  | 
|  |                         color = 'mod',
 |  | 
|  |                         inline = i18n.tooltips.item_quantity,
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type = 'stat',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_item_drop_rarity_+%',
 |  | 
|  |                         fmt = '+%i%%',
 |  | 
|  |                         color = 'mod',
 |  | 
|  |                         inline = i18n.tooltips.item_rarity,
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 type = 'stat',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'map_pack_size_+%',
 |  | 
|  |                         fmt = '+%i%%',
 |  | 
|  |                         color = 'mod',
 |  | 
|  |                         inline = i18n.tooltips.monster_pack_size,
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Jewel Only
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'item_limit',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.limited_to,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'jewel_radius_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'jewel_radius_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.radius,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Flask only
 |  | 
|  |         {
 |  | 
|  |             args = {'flask_mana_html', 'flask_duration_html'},
 |  | 
|  |             --func = core.factory.display_flask('flask_mana'),
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 inline = i18n.tooltips.flask_mana_recovery,
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'flask_mana_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                     [2] = {
 |  | 
|  |                         key = 'flask_duration_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                 }
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'flask_life_html', 'flask_duration_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 inline = i18n.tooltips.flask_life_recovery,
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'flask_life_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                     [2] = {
 |  | 
|  |                         key = 'flask_duration_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                 }
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             -- don't display for mana/life flasks
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 for _, k in ipairs({'flask_life_html', 'flask_mana_html'}) do
 |  | 
|  |                     if tpl_args[k] ~= nil then
 |  | 
|  |                         return false
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return tpl_args['flask_duration_html'] ~= nil
 |  | 
|  |             end,
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 inline = i18n.tooltips.flask_duration,
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'flask_duration_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'charges_per_use_html', 'charges_max_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 inline = i18n.tooltips.flask_charges_per_use,
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'charges_per_use_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                     [2] = {
 |  | 
|  |                         key = 'charges_max_html',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'buff_stat_text'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'buff_stat_text',
 |  | 
|  |                         color = 'mod',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- armor
 |  | 
|  |         {
 |  | 
|  |             args = {'block_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'block_html',
 |  | 
|  |                         inline = i18n.tooltips.chance_to_block,
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         hide_default_key = 'block',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'armour_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'armour_html',
 |  | 
|  |                         inline = i18n.tooltips.armour,
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         hide_default_key = 'armour',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'evasion_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'evasion_html',
 |  | 
|  |                         inline = i18n.tooltips.evasion,
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         hide_default_key = 'evasion',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'energy_shield_html'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'energy_shield_html',
 |  | 
|  |                         inline = i18n.tooltips.energy_shield,
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         hide_default_key = 'energy_shield',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Amulet only
 |  | 
|  |         {
 |  | 
|  |             args = {'talisman_tier'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'talisman_tier',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.talisman_tier,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Misc
 |  | 
|  |         {
 |  | 
|  |             args = {'stack_size'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'stack_size',
 |  | 
|  |                         hide_default = 1,
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.stack_size,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         -- Essence stuff
 |  | 
|  |         {
 |  | 
|  |             args = {'essence_level'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'essence_level',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.essence_level,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Requirements
 |  | 
|  |     {
 |  | 
|  |         -- TODO: i18n Master name?
 |  | 
|  |         {
 |  | 
|  |             args = {'master', 'master_level_requirement'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 -- masters have been validated before
 |  | 
|  |                 local data
 |  | 
|  |                 for i, rowdata in ipairs(m_game.constants.masters) do
 |  | 
|  |                     if tpl_args.master == rowdata.full then
 |  | 
|  |                         data = rowdata
 |  | 
|  |                         break
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return m_util.html.poe_color('default', i18n.tooltips.requires, string.format('[[%s|%s %s]]', data.full, data.short_upper, tpl_args.master_level_requirement))
 |  | 
|  |             end
 |  | 
|  |         },
 |  | 
|  |         -- Instead of item level, show drop level if any
 |  | 
|  |         {
 |  | 
|  |             args = nil,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local opt = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'required_level_final_html',
 |  | 
|  |                         hide_default = 1,
 |  | 
|  |                         hide_default_key = 'required_level_final',
 |  | 
|  |                         inline = i18n.tooltips.level_inline,
 |  | 
|  |                         inline_color = false,
 |  | 
|  |                     },
 |  | 
|  |                 }
 |  | 
|  |                 
 |  | 
|  |                 for _, attr in ipairs(m_game.constants.attributes) do
 |  | 
|  |                     opt[#opt+1] = {
 |  | 
|  |                         key = string.format('required_%s_html', attr['long_lower']),
 |  | 
|  |                         hide_default = 0,
 |  | 
|  |                         hide_default_key = string.format('required_%s', attr['long_lower']),
 |  | 
|  |                         inline = ', %s ' .. attr['short_upper'],
 |  | 
|  |                         inline_color = false,
 |  | 
|  |                     }
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 local requirements = core.factory.display_value{options = opt}(tpl_args, frame)
 |  | 
|  |                 
 |  | 
|  |                 -- return early
 |  | 
|  |                 if requirements == nil then
 |  | 
|  |                     return
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 requirements = string.gsub(requirements, '^, ', '')
 |  | 
|  |                 
 |  | 
|  |                 return m_util.html.poe_color('default', string.format(i18n.tooltips.requires, requirements))
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Gem description
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -gemdesc',
 |  | 
|  |         {
 |  | 
|  |             args = {'gem_description'},
 |  | 
|  |             func = core.factory.display_value_only('gem_description'),
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Gem Quality Stats
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -mod',
 |  | 
|  |         {
 |  | 
|  |             args = {'quality_stat_text'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 lines = {}
 |  | 
|  |                 lines[#lines+1] = m_util.html.poe_color('default', i18n.tooltips.gem_quality)
 |  | 
|  |                 lines[#lines+1] = tpl_args.quality_stat_text
 |  | 
|  |                 
 |  | 
|  |                 return table.concat(lines, '<br>')
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Gem Implicit Stats
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -mod',
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 return core.class_groups.gems.keys[tpl_args.class_id] and tpl_args.stat_text
 |  | 
|  |             end,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 lines = {}
 |  | 
|  |                 lines[#lines+1] = tpl_args.stat_text
 |  | 
|  |                 if tpl_args.gem_tags:contains('Vaal') then
 |  | 
|  |                     lines[#lines+1] = m_util.html.poe_color('corrupted', i18n.tooltips.corrupted) 
 |  | 
|  |                 end
 |  | 
|  |                 return table.concat(lines, '<br>')
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Implicit Stats
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -mod',
 |  | 
|  |         func = function(tpl_args, frame, container_type)
 |  | 
|  |             if tpl_args.implicit_stat_text then
 |  | 
|  |                 return {core.display.strip_random_stats(tpl_args, frame, tpl_args.implicit_stat_text, container_type)}
 |  | 
|  |             else
 |  | 
|  |                 return {}
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- Stats
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -mod',
 |  | 
|  |         func = function(tpl_args, frame, container_type)
 |  | 
|  |             if tpl_args.explicit_stat_text then
 |  | 
|  |                 return {core.display.strip_random_stats(tpl_args, frame, tpl_args.explicit_stat_text, container_type)}
 |  | 
|  |             else
 |  | 
|  |                 return {}
 |  | 
|  |             end
 |  | 
|  |         end,
 |  | 
|  |     },
 |  | 
|  |     -- Experience
 |  | 
|  |     --[[{
 |  | 
|  |         {
 |  | 
|  |             args = {'experience'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 key = 'experience',
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |     },]]--
 |  | 
|  |     -- Description (currency, doodads)
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -mod',
 |  | 
|  |         {
 |  | 
|  |             args = {'description'},
 |  | 
|  |             func = core.factory.display_value_only('description'),
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Variations (for doodads)
 |  | 
|  |     {
 |  | 
|  |        css_class = 'tc -mod',
 |  | 
|  |         {
 |  | 
|  |             args = {'variation_count'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local txt
 |  | 
|  |                 if tpl_args.variation_count == 1 then
 |  | 
|  |                     txt = i18n.tooltips.variation_singular
 |  | 
|  |                 else
 |  | 
|  |                     txt = i18n.tooltips.variation_plural
 |  | 
|  |                 end
 |  | 
|  |                 return string.format('%i %s', tpl_args.variation_count, txt)
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Flavour Text
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -flavour',
 |  | 
|  |         {
 |  | 
|  |             args = {'flavour_text'},
 |  | 
|  |             func = core.factory.display_value_only('flavour_text'),
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Prophecy text
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -value',
 |  | 
|  |         {
 |  | 
|  |             args = {'prediction_text'},
 |  | 
|  |             func = core.factory.display_value_only('prediction_text'),
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Can not be traded or modified
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -canttradeormodify',
 |  | 
|  |         {
 |  | 
|  |             args = {'cannot_be_traded_or_modified'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 if tpl_args.cannot_be_traded_or_modified == true then
 |  | 
|  |                     return i18n.tooltips.cannot_be_traded_or_modified
 |  | 
|  |                 end
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Help text
 |  | 
|  |     {
 |  | 
|  |         css_class = 'tc -help',
 |  | 
|  |         {
 |  | 
|  |             args = {'help_text'},
 |  | 
|  |             func = core.factory.display_value_only('help_text'),
 |  | 
|  |             --inline = false,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Cost (i.e. vendor costs)
 |  | 
|  |     {
 |  | 
|  |         --css_class = '',
 |  | 
|  |         {
 |  | 
|  |             args = {'master_favour_cost'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'master_favour_cost',
 |  | 
|  |                         inline = i18n.tooltips.favour_cost,
 |  | 
|  |                         color = 'currency',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'seal_cost'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'seal_cost',
 |  | 
|  |                         fmt = '%dx ',
 |  | 
|  |                         color = 'currency',
 |  | 
|  |                         inline = function (tpl_args, frame) 
 |  | 
|  |                             return i18n.tooltips.seal_cost .. f_item_link{item_name_exact='Silver Coin', html=''}
 |  | 
|  |                         end,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- This is meant to show additional information about the item in a separate infobox
 |  | 
|  | --
 |  | 
|  | core.extra_display_groups = {
 |  | 
|  |     -- Drop info
 |  | 
|  |     {
 |  | 
|  |         header = i18n.tooltips.drop_restrictions,
 |  | 
|  |         css_class = '',
 |  | 
|  |         {
 |  | 
|  |             args = {'drop_enabled'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'drop_level',
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = i18n.tooltips.level,
 |  | 
|  |                     },
 |  | 
|  |                     [2] = {
 |  | 
|  |                         key = 'drop_level_maximum',
 |  | 
|  |                         hide_default = 100,
 |  | 
|  |                         fmt = '%i',
 |  | 
|  |                         inline = ' / %s',
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'drop_leagues'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 return string.format(i18n.tooltips.league_restriction, m_util.html.poe_color('value', table.concat(tpl_args.drop_leagues, ', ')))
 |  | 
|  |             end
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'drop_areas_html'},
 |  | 
|  |             func = core.factory.display_value_only('drop_areas_html'),
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'drop_text'},
 |  | 
|  |             func = core.factory.display_value_only('drop_text'),
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 if tpl_args.drop_enabled == true then
 |  | 
|  |                     return false
 |  | 
|  |                 end
 |  | 
|  |                 return true
 |  | 
|  |             end,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local span = mw.html.create('span')
 |  | 
|  |                 span
 |  | 
|  |                     :attr('class', 'infobox-disabled-drop')
 |  | 
|  |                     :wikitext(i18n.tooltips.drop_disabled)
 |  | 
|  |                     :done()
 |  | 
|  |                 return tostring(span)
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         header = i18n.tooltips.purchase_costs,
 |  | 
|  |         {
 |  | 
|  |             args = function(tpl_args, frame)
 |  | 
|  |                 for rarity, data in pairs(tpl_args.purchase_costs) do
 |  | 
|  |                     if #data > 0 then
 |  | 
|  |                         return true
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 return false
 |  | 
|  |             end,
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local tbl = mw.html.create('table')
 |  | 
|  |                 tbl
 |  | 
|  |                     --:attr('class', 'wikitable')
 |  | 
|  |                     :attr('style', 'width: 100%; margin-top: 0px;')
 |  | 
|  |                     
 |  | 
|  |                 for _, rarity_names in ipairs(m_game.constants.item.rarity) do
 |  | 
|  |                     local data = tpl_args.purchase_costs[rarity_names.long_lower]
 |  | 
|  |                     if #data > 0 then
 |  | 
|  |                         local tr = tbl:tag('tr')
 |  | 
|  |                         tr
 |  | 
|  |                             :tag('td')
 |  | 
|  |                                 :wikitext(rarity_names.long_upper)
 |  | 
|  |                         local td = tr:tag('td')
 |  | 
|  |                         for _, purchase_data in ipairs(data) do
 |  | 
|  |                             td:wikitext(string.format('%dx [[%s]]<br />', purchase_data.amount, purchase_data.name))
 |  | 
|  |                         end
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return tostring(tbl)
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         header = i18n.tooltips.sell_price,
 |  | 
|  |         {
 |  | 
|  |             args = {'sell_price_order'},
 |  | 
|  |             func = function(tpl_args, frame)
 |  | 
|  |                 local out = {}
 |  | 
|  |                 for _, item_name in ipairs(tpl_args.sell_price_order) do
 |  | 
|  |                     out[#out+1] = string.format('%dx [[%s]]', tpl_args.sell_prices[item_name], item_name)
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 return table.concat(out, '<br />')
 |  | 
|  |             end,
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  |     -- Damage per second
 |  | 
|  |     {
 |  | 
|  |         header = i18n.tooltips.damage_per_second,
 |  | 
|  |         -- Autoinsert here from dps map
 |  | 
|  |     },
 |  | 
|  |     {
 |  | 
|  |         header = i18n.tooltips.misc,
 |  | 
|  |         {
 |  | 
|  |             args = {'class'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'class',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.item_class,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |         {
 |  | 
|  |             args = {'metadata_id'},
 |  | 
|  |             func = core.factory.display_value{
 |  | 
|  |                 options = {
 |  | 
|  |                     [1] = {
 |  | 
|  |                         key = 'metadata_id',
 |  | 
|  |                         fmt = '%s',
 |  | 
|  |                         inline = i18n.tooltips.metadata_id,
 |  | 
|  |                         truncate = true,
 |  | 
|  |                     },
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |     },
 |  | 
|  | }
 |  | 
|  |   |  | 
|  | for i, data in ipairs(core.dps_map) do
 |  | 
|  |     table.insert(core.extra_display_groups[4], {
 |  | 
|  |         args = {data.name .. '_html'},
 |  | 
|  |         func = core.factory.display_value{
 |  | 
|  |             options = {
 |  | 
|  |                 [1] = {
 |  | 
|  |                     key = data.name .. '_html',
 |  | 
|  |                     inline = data.label_infobox .. ': %s',
 |  | 
|  |                     fmt = '%s',
 |  | 
|  |                     -- the html already contains the colour
 |  | 
|  |                     no_color = true,
 |  | 
|  |                 },
 |  | 
|  |             },
 |  | 
|  |         },
 |  | 
|  |     })
 |  | 
|  |     if i == 5 then
 |  | 
|  |         table.insert(core.extra_display_groups[4], {
 |  | 
|  |             args = function (tpl_args, frame) 
 |  | 
|  |                 return tpl_args.elemental_dps_html ~= nil or tpl_args.poison_dps_html ~= nil
 |  | 
|  |             end,
 |  | 
|  |             func = function (tpl_args, frame)
 |  | 
|  |                 return ''
 |  | 
|  |             end,
 |  | 
|  |         })
 |  | 
|  |     elseif i == 7 then
 |  | 
|  |         table.insert(core.extra_display_groups[4], {
 |  | 
|  |             args = {'dps_html'},
 |  | 
|  |             func = function (tpl_args, frame)
 |  | 
|  |                 return ''
 |  | 
|  |             end,
 |  | 
|  |         })
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Tables
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | function cargo_declare(data)
 |  | 
|  |     return function(frame)
 |  | 
|  |         local tpl_args = getArgs(frame, {
 |  | 
|  |             parentFirst = true
 |  | 
|  |         })
 |  | 
|  |         frame = m_util.misc.get_frame(frame)
 |  | 
|  |         
 |  | 
|  |         local dcl_args = {}
 |  | 
|  |         dcl_args._table = data.table
 |  | 
|  |         for k, field_data in pairs(data.fields) do
 |  | 
|  |             if field_data.field then
 |  | 
|  |                 dcl_args[field_data.field] = field_data.type
 |  | 
|  |                 for _, stat_data in pairs(core.stat_map) do
 |  | 
|  |                     if stat_data.field == k then
 |  | 
|  |                         for _, range_fields in ipairs(h.range_fields) do
 |  | 
|  |                             -- if the type is nil, use the parent type
 |  | 
|  |                             -- so this is set integer/float values correctly
 |  | 
|  |                             dcl_args[stat_data.field .. range_fields.field] = range_fields.type or field_data.type
 |  | 
|  |                         end
 |  | 
|  |                         break
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         if dcl_args._table == 'weapons' then
 |  | 
|  |             for _, dps_data in ipairs(core.dps_map) do
 |  | 
|  |                 for _, range_fields in ipairs(h.range_fields) do
 |  | 
|  |                     -- since there is no parent, the default is float
 |  | 
|  |                     dcl_args[dps_data.field .. range_fields.field] = range_fields.type or 'Float'
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |         if tpl_args.debug then
 |  | 
|  |             mw.logObject(dcl_args)
 |  | 
|  |         end
 |  | 
|  |         return m_util.cargo.declare(frame, dcl_args)
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | p.table_items = cargo_declare(core.cargo.items)
 |  | 
|  | p.table_item_sell_prices = cargo_declare(core.cargo.item_sell_prices)
 |  | 
|  | p.table_item_purchase_costs = cargo_declare(core.cargo.item_purchase_costs)
 |  | 
|  | p.table_item_mods = cargo_declare(core.cargo.item_mods)
 |  | 
|  | p.table_item_stats = cargo_declare(core.cargo.item_stats)
 |  | 
|  | p.table_item_buffs = cargo_declare(core.cargo.item_buffs)
 |  | 
|  | p.table_upgraded_from_sets = cargo_declare(core.cargo.upgraded_from_sets)
 |  | 
|  | p.table_upgraded_from_groups = cargo_declare(core.cargo.upgraded_from_groups)
 |  | 
|  | p.table_amulets = cargo_declare(core.cargo.amulets)
 |  | 
|  | p.table_flasks = cargo_declare(core.cargo.flasks)
 |  | 
|  | p.table_weapons = cargo_declare(core.cargo.weapons)
 |  | 
|  | p.table_armours = cargo_declare(core.cargo.armours)
 |  | 
|  | p.table_shields = cargo_declare(core.cargo.shields)
 |  | 
|  | p.table_skill_gems = cargo_declare(core.cargo.skill_gems)
 |  | 
|  | p.table_maps = cargo_declare(core.cargo.maps)
 |  | 
|  | p.table_stackables = cargo_declare(core.cargo.stackables)
 |  | 
|  | p.table_essences = cargo_declare(core.cargo.essences)
 |  | 
|  | p.table_hideout_doodads = cargo_declare(core.cargo.hideout_doodads)
 |  | 
|  | p.table_prophecies = cargo_declare(core.cargo.prophecies)
 |  | 
|  | p.table_divination_cards = cargo_declare(core.cargo.divination_cards)
 |  | 
|  | p.table_jewels = cargo_declare(core.cargo.jewels)
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Page views
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- Template:Item
 |  | 
|  | --
 |  | 
|  |   |  | 
|  | function p.itembox (frame)
 |  | 
|  |     --
 |  | 
|  |     -- Args/Frame
 |  | 
|  |     --
 |  | 
|  |     local t = os.clock()
 |  | 
|  |     
 |  | 
|  |     local tpl_args = getArgs(frame, {
 |  | 
|  |         parentFirst = true
 |  | 
|  |     })
 |  | 
|  |     frame = m_util.misc.get_frame(frame)
 |  | 
|  |     
 |  | 
|  |     --
 |  | 
|  |     -- Shared args
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     tpl_args._flags = {}
 |  | 
|  |     tpl_args._base_item_args = {}
 |  | 
|  |     tpl_args._mods = {}
 |  | 
|  |     for _, k in ipairs({'', '_random'}) do
 |  | 
|  |         for _, prefix in ipairs({'', '_implicit', '_explicit'}) do
 |  | 
|  |             tpl_args[k .. prefix .. '_stats'] = {}
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     tpl_args._subobjects = {}
 |  | 
|  |     tpl_args._properties = {}
 |  | 
|  |     tpl_args._errors = {}
 |  | 
|  |     
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array={'class_id'}})
 |  | 
|  |     core.build_item_classes(tpl_args, frame)
 |  | 
|  |     core.build_cargo_data(tpl_args, frame)
 |  | 
|  |     -- TODO: Found out wtf is going on. Need to call this twice to fix
 |  | 
|  |     core.build_cargo_data(tpl_args, frame)
 |  | 
|  |     
 |  | 
|  |     -- Using general purpose function to handle release and removal versions
 |  | 
|  |     m_util.args.version(tpl_args, {frame=frame, set_properties=true})
 |  | 
|  |     
 |  | 
|  |     -- Must validate some argument early. It is required for future things
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array=core.default_args})
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array=core.item_classes[tpl_args.class_id].args})
 |  | 
|  |     
 |  | 
|  |     -- Base Item
 |  | 
|  |   |  | 
|  |     core.process_base_item(tpl_args, frame)
 |  | 
|  |     
 |  | 
|  |     -- Prophecy special snowflake
 |  | 
|  |     if tpl_args._flags.is_prophecy then
 |  | 
|  |         err = core.process_arguments(tpl_args, frame, {array=core.prophecy_args})
 |  | 
|  |         if err then
 |  | 
|  |             return err
 |  | 
|  |         end
 |  | 
|  |         
 |  | 
|  |         tpl_args.inventory_icon = string.format(i18n.inventory_icon, 'Prophecy')
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- Mods
 |  | 
|  |   |  | 
|  |     for _, k in ipairs({'implicit', 'explicit'}) do
 |  | 
|  |         local success = true
 |  | 
|  |         local i = 1
 |  | 
|  |         while success do
 |  | 
|  |             success = core.validate_mod(tpl_args, frame, {key=k, i=i})
 |  | 
|  |             i = i + 1
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     core.process_smw_mods(tpl_args, frame)
 |  | 
|  |         
 |  | 
|  |     -- Add stats - this is for when mods are not set, but we still need stats to calcuate new armour values etc
 |  | 
|  |     m_util.args.stats(tpl_args, {prefix='extra_'})
 |  | 
|  |     for _, stat in ipairs(tpl_args.extra_stats) do
 |  | 
|  |         if stat.value ~= nil then
 |  | 
|  |             stat.min = stat.value
 |  | 
|  |             stat.max = stat.value
 |  | 
|  |             stat.avg = stat.value
 |  | 
|  |         end
 |  | 
|  |         h.stats_update(tpl_args, stat.id, stat, nil, '_stats')
 |  | 
|  |         h.stats_update(tpl_args, stat.id, stat, nil, '_explicit_stats')
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     -- Transpose stats into cargo data
 |  | 
|  |     for _, random_prefix in ipairs({'', '_random'}) do
 |  | 
|  |         for _, type_prefix in ipairs({'', '_implicit', '_explicit'}) do
 |  | 
|  |             for id, data in pairs(tpl_args[random_prefix .. type_prefix .. '_stats']) do
 |  | 
|  |                 local is_implicit
 |  | 
|  |                 if type_prefix == '_implicit' then
 |  | 
|  |                     is_implicit = true
 |  | 
|  |                 elseif type_prefix == '_explicit' then
 |  | 
|  |                     is_implicit = false
 |  | 
|  |                 end
 |  | 
|  |                 tpl_args._subobjects[#tpl_args._subobjects+1] = {
 |  | 
|  |                     _table = 'item_stats',
 |  | 
|  |                     id = id,
 |  | 
|  |                     min = data.min,
 |  | 
|  |                     max = data.max,
 |  | 
|  |                     avg = data.avg,
 |  | 
|  |                     is_implicit = is_implicit,
 |  | 
|  |                     is_random = random_prefix == '_random',
 |  | 
|  |                 }
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- Handle extra stats (for gems)
 |  | 
|  |     
 |  | 
|  |     if core.class_groups.gems.keys[tpl_args.class_id] then
 |  | 
|  |         m_skill.skill(frame, tpl_args)
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     --
 |  | 
|  |     -- Handle local stats increases/reductions/additions
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     local skip = {}
 |  | 
|  |     
 |  | 
|  |     -- general stats
 |  | 
|  |     for k, data in pairs(core.stat_map) do
 |  | 
|  |         local value = tpl_args[k]
 |  | 
|  |         
 |  | 
|  |         if value ~= nil and skip[k] == nil then
 |  | 
|  |             value = {min=value, max=value, base=value}
 |  | 
|  |             -- If stats are overriden we scan save some CPU time here
 |  | 
|  |             local overridden = false
 |  | 
|  |             if data.stats_override ~= nil then
 |  | 
|  |                 for stat_id, override_value in pairs(data.stats_override) do
 |  | 
|  |                     local stat_value = tpl_args._stats[stat_id]
 |  | 
|  |                     if stat_value ~= nil then
 |  | 
|  |                         -- Use the value of stat
 |  | 
|  |                         if override_value == true then
 |  | 
|  |                             value.min = stat_value.min
 |  | 
|  |                             value.max = stat_value.max
 |  | 
|  |                             overridden = true
 |  | 
|  |                         elseif stat_value ~= 0 then
 |  | 
|  |                             value.min = override_value.min
 |  | 
|  |                             value.max = override_value.max
 |  | 
|  |                             overridden = true
 |  | 
|  |                         end
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |            end
 |  | 
|  |                 
 |  | 
|  |            if overridden == false then
 |  | 
|  |                 -- The simple cases; this must be using ipairs as "add" must apply before
 |  | 
|  |                 for _, operator in ipairs({'add', 'more'}) do
 |  | 
|  |                     local st = data['stats_' .. operator]
 |  | 
|  |                     if st ~= nil then
 |  | 
|  |                         for _, statid in ipairs(st) do
 |  | 
|  |                             if tpl_args._stats[statid] ~= nil then
 |  | 
|  |                                 h.stat[operator](value, tpl_args._stats[statid])
 |  | 
|  |                             end
 |  | 
|  |                         end
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 -- For increased stats we need to add them up first
 |  | 
|  |                 for stat_key, stat_func in pairs({increased=h.stat.more, increased_inverse=h.stat.more_inverse}) do
 |  | 
|  |                     local st = data['stats_' .. stat_key]
 |  | 
|  |                     if st ~= nil then
 |  | 
|  |                         local total_increase = {min=0, max=0}
 |  | 
|  |                         for _, statid in ipairs(st) do
 |  | 
|  |                             if tpl_args._stats[statid] ~= nil then
 |  | 
|  |                                 for var, current_value in pairs(total_increase) do
 |  | 
|  |                                     total_increase[var] = current_value + tpl_args._stats[statid][var]
 |  | 
|  |                                 end
 |  | 
|  |                             end
 |  | 
|  |                         end
 |  | 
|  |                         stat_func(value, total_increase)
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 if data.minimum ~= nil then
 |  | 
|  |                     for _, key in ipairs({'min', 'max'}) do
 |  | 
|  |                         if value[key] < data.minimum then
 |  | 
|  |                             value[key] = data.minimum 
 |  | 
|  |                         end
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             else
 |  | 
|  |   |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             value.avg = (value.min + value.max) / 2
 |  | 
|  |             
 |  | 
|  |             -- don't add the properties unless we need to
 |  | 
|  |             if (data.default ~= nil and (value.min ~= data.default or value.max ~= data.default)) or data.default == nil then
 |  | 
|  |                 for short_key, range_data in pairs(h.range_map) do
 |  | 
|  |                     tpl_args[data.field .. range_data.var] = value[short_key]
 |  | 
|  |                 end
 |  | 
|  |                 
 |  | 
|  |                 -- process to HTML to use on list pages or other purposes
 |  | 
|  |                 h.handle_range_args(tpl_args, frame, k, data.field, value, data.html_fmt_options or {})
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             for short_key, range_data in pairs(h.range_map) do
 |  | 
|  |                 tpl_args[k .. range_data.var] = value[short_key]
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     -- calculate and handle weapon dps
 |  | 
|  |     if core.class_groups.weapons.keys[tpl_args.class_id] then
 |  | 
|  |         for _, data in ipairs(core.dps_map) do
 |  | 
|  |             local damage = {
 |  | 
|  |                 min = {},
 |  | 
|  |                 max = {},
 |  | 
|  |             }
 |  | 
|  |         
 |  | 
|  |             for var_type, value in pairs(damage) do
 |  | 
|  |                 -- covers the min/max/avg range
 |  | 
|  |                 for short_key, range_data in pairs(h.range_map) do
 |  | 
|  |                     value[short_key] = 0
 |  | 
|  |                     for _, damage_key in ipairs(data.damage_args) do
 |  | 
|  |                         value[short_key] = value[short_key] + (tpl_args[string.format('%s_%s%s', damage_key, var_type, range_data.var)] or 0)
 |  | 
|  |                     end
 |  | 
|  |                 end
 |  | 
|  |             end
 |  | 
|  |   |  | 
|  |             local value = {}
 |  | 
|  |             for short_key, range_data in pairs(h.range_map) do
 |  | 
|  |                 local result = (damage.min[short_key] + damage.max[short_key]) / 2 * tpl_args[string.format('attack_speed%s', range_data.var)]
 |  | 
|  |                 value[short_key] = result
 |  | 
|  |                 tpl_args[string.format('%s%s', data.field, range_data.var)] = result
 |  | 
|  |             end
 |  | 
|  |             
 |  | 
|  |             if value.avg > 0 then
 |  | 
|  |                 h.handle_range_args(tpl_args, frame, data.name, data.field, value, data.html_fmt_options or {})
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- late processing
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array=core.late_args})
 |  | 
|  |     core.process_arguments(tpl_args, frame, {array=core.item_classes[tpl_args.class_id].late_args})
 |  | 
|  |     
 |  | 
|  |     -- Handle upgrade from restrictions/info
 |  | 
|  |     core.process_upgraded_from(tpl_args, frame)
 |  | 
|  |     
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- Infobox handling 
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- Store the infobox so it can be accessed with ease on other pages
 |  | 
|  |     tpl_args.html = tostring(core.display.make_main_container(tpl_args, frame, 'inline'))
 |  | 
|  |     local container = core.display.make_main_container(tpl_args, frame, 'infobox')
 |  | 
|  |     
 |  | 
|  |     if tpl_args.inventory_icon ~= nil and tpl_args.class_id ~= 'DivinationCard' then
 |  | 
|  |         container:wikitext(string.format('[[%s|%sx%spx]]', tpl_args.inventory_icon, c.image_size_full*tpl_args.size_x, c.image_size_full*tpl_args.size_y))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     --
 |  | 
|  |     -- Secondary infobox
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     local extra_infobox = mw.html.create('span')
 |  | 
|  |         :attr( 'class', 'item-box -' .. tpl_args.frame_type)
 |  | 
|  |         
 |  | 
|  |     core.display.add_to_container_from_map(tpl_args, frame, extra_infobox, core.extra_display_groups)
 |  | 
|  |     
 |  | 
|  |     -- 
 |  | 
|  |     -- Output 
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     local infobox = mw.html.create('span')
 |  | 
|  |     infobox
 |  | 
|  |         :attr('class', 'infobox-page-container')
 |  | 
|  |         :node(container)
 |  | 
|  |         :node(extra_infobox)
 |  | 
|  |         
 |  | 
|  |     -- skill_screenshot is set in skill module
 |  | 
|  |     if tpl_args.skill_screenshot then
 |  | 
|  |         infobox:wikitext(string.format('<br>[[%s|300px]]', tpl_args.skill_screenshot))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local out = tostring(infobox)
 |  | 
|  |     
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- Category handling 
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     
 |  | 
|  |     local cats = {}
 |  | 
|  |     if tpl_args.rarity == 'Unique' then
 |  | 
|  |         cats[#cats+1] = string.format(i18n.categories.unique_affix, tpl_args.class)
 |  | 
|  |     elseif tpl_args._flags.is_prophecy then
 |  | 
|  |         cats[#cats+1] = i18n.categories.prophecies
 |  | 
|  |     elseif tpl_args.is_talisman then
 |  | 
|  |         cats[#cats+1] = i18n.categories.talismans
 |  | 
|  |     elseif tpl_args.is_essence then
 |  | 
|  |         cats[#cats+1] = i18n.categories.essences
 |  | 
|  |     else
 |  | 
|  |         cats[#cats+1] = tpl_args.class
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.rarity ~= 'Normal' or tpl_args.base_item == 'Prophecy' or tpl_args.base_item_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy' then
 |  | 
|  |         cats[#cats+1] = i18n.categories.derived_items
 |  | 
|  |     else
 |  | 
|  |         cats[#cats+1] = i18n.categories.base_items
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     for _, attr in ipairs(m_game.constants.attributes) do
 |  | 
|  |         if tpl_args[attr.long_lower .. '_percent'] then
 |  | 
|  |             cats[#cats+1] = string.format('%s %s', attr.long_upper, tpl_args.class)
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local affix
 |  | 
|  |     if tpl_args.class_id == 'Active Skill Gem' or tpl_args.class_id == 'Support Skill Gem' then
 |  | 
|  |         affix = i18n.categories.gem_tag_affix
 |  | 
|  |     end
 |  | 
|  |     if affix ~= nil then
 |  | 
|  |         for _, tag in ipairs(tpl_args.gem_tags) do
 |  | 
|  |             cats[#cats+1] = string.format(affix, tag)
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if #tpl_args.alternate_art_inventory_icons > 0 then
 |  | 
|  |         cats[#cats+1] = i18n.categories.alternate_artwork
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- TODO: add maintenance categories
 |  | 
|  |     
 |  | 
|  |     if tpl_args.release_version == nil then
 |  | 
|  |         cats[#cats+1] = i18n.categories.missing_release_version
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args._flags.text_modifier and not tpl_args.suppress_improper_modifiers_category then
 |  | 
|  |         cats[#cats+1] = i18n.categories.improper_modifiers
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- 
 |  | 
|  |     for _, k in ipairs({'broken_upgraded_from_reference', 'duplicate_query_area_ids', 'sell_prices_override'}) do
 |  | 
|  |         if tpl_args._flags[k] then
 |  | 
|  |             cats[#cats+1] = i18n.categories[k]
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     out = out .. m_util.misc.add_category(cats, {ingore_blacklist=tpl_args.debug})
 |  | 
|  |     
 |  | 
|  |     --
 |  | 
|  |     --  Misc
 |  | 
|  |     --
 |  | 
|  |      
 |  | 
|  |     -- Also show the infobox for areas right away for maps, since they're both on the same page
 |  | 
|  |     local query_id
 |  | 
|  |     if tpl_args.rarity == 'Normal' and tpl_args.map_area_id ~= nil then
 |  | 
|  |         query_id = tpl_args.map_area_id
 |  | 
|  |     elseif tpl_args.rarity == 'Unique' and tpl_args.unique_map_area_id ~= nil then
 |  | 
|  |         query_id = tpl_args.unique_map_area_id
 |  | 
|  |     end
 |  | 
|  |   |  | 
|  |     if query_id then
 |  | 
|  |         out = out .. m_area.query_area_info{cats=yes, where=string.format('areas.id="%s"', query_id)}
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- Store cargo data
 |  | 
|  |     -- ------------------------------------------------------------------------
 |  | 
|  |     -- Map argument values for cargo storage
 |  | 
|  |     for _, table_name in ipairs(core.item_classes[tpl_args.class_id].tables) do
 |  | 
|  |         tpl_args._subobjects[table_name] = {
 |  | 
|  |             _table = table_name,
 |  | 
|  |         }
 |  | 
|  |     end
 |  | 
|  |     for k, v in pairs(tpl_args) do
 |  | 
|  |         local data = core.map[k]
 |  | 
|  |         if data ~= nil then
 |  | 
|  |             if data.table ~= nil and data.field ~= nil then
 |  | 
|  |                 tpl_args._subobjects[data.table][data.field] = v
 |  | 
|  |             elseif data.table == nil and data.field ~= nil then
 |  | 
|  |                 error(string.format('Missing table for field "%s", key "%s", \nvalue:\n "%s" \ndata:\n%s', data.field, k, mw.dumpObject(v), mw.dumpObject(data)))
 |  | 
|  |             elseif data.table ~= nil and data.field == nil then
 |  | 
|  |                 error(string.format('Missing field for table "%s", key "%s", \nvalue:\n "%s" \ndata:\n%s', data.table, k, mw.dumpObject(v), mw.dumpObject(data)))
 |  | 
|  |             end
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- Don't actually save data in testing mode
 |  | 
|  |     if not tpl_args.test then
 |  | 
|  |         for _, data in pairs(tpl_args._subobjects) do
 |  | 
|  |             m_util.cargo.store(frame, data, {
 |  | 
|  |                 debug=tpl_args.debug,
 |  | 
|  |                 sep={
 |  | 
|  |                     name_list='�',
 |  | 
|  |                 },
 |  | 
|  |             })
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     -- Show additional error messages in console to help fixing them
 |  | 
|  |     if #tpl_args._errors > 0 then
 |  | 
|  |         mw.logObject(table.concat(tpl_args._errors, '\n'))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.test then
 |  | 
|  |         tpl_args.out = out
 |  | 
|  |         return tpl_args
 |  | 
|  |     else
 |  | 
|  |         mw.logObject(os.clock() - t)
 |  | 
|  |         return out
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Result formatting templates for SMW queries
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | --
 |  | 
|  | -- Template:
 |  | 
|  | --
 |  | 
|  |   |  | 
|  | function p.simple_item_list(frame)
 |  | 
|  |     -- Args
 |  | 
|  |     local tpl_args = getArgs(frame, {
 |  | 
|  |         parentFirst = true
 |  | 
|  |     })
 |  | 
|  |     frame = m_util.misc.get_frame(frame)
 |  | 
|  |     
 |  | 
|  |     local query = {}
 |  | 
|  |     for key, value in pairs(tpl_args) do 
 |  | 
|  |         if string.sub(key, 0, 2) == 'q_' then
 |  | 
|  |             query[string.sub(key, 3)] = value
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local fields = {
 |  | 
|  |         'items._pageName',
 |  | 
|  |         'items.name',
 |  | 
|  |     }
 |  | 
|  |     
 |  | 
|  |     if tpl_args.no_icon == nil then
 |  | 
|  |         fields[#fields+1] = 'items.inventory_icon'
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.no_html == nil then
 |  | 
|  |         fields[#fields+1] = 'items.html'
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local tables = m_util.string.split(tpl_args.q_tables or '', ',%s*')
 |  | 
|  |     table.insert(tables, 'items')
 |  | 
|  |     
 |  | 
|  |     query.groupBy = query.groupBy or 'items._pageID'
 |  | 
|  |     
 |  | 
|  |     local results = m_util.cargo.query(
 |  | 
|  |         tables,
 |  | 
|  |         fields,
 |  | 
|  |         query
 |  | 
|  |     )
 |  | 
|  |     
 |  | 
|  |     local out = {}
 |  | 
|  |     for _, row in ipairs(results) do
 |  | 
|  |         local link = f_item_link{page=row['items._pageName'], name=row['items.name'], inventory_icon=row['items.inventory_icon'] or '', html=row['items.html'] or '', skip_query=true}
 |  | 
|  |         if tpl_args.format == nil then
 |  | 
|  |             out[#out+1] = string.format('* %s', link)
 |  | 
|  |         elseif tpl_args.format == 'none' then
 |  | 
|  |             out[#out+1] = link
 |  | 
|  |         elseif tpl_args.format == 'li' then
 |  | 
|  |             out[#out+1] = string.format('<li>%s</li>', link)
 |  | 
|  |         else
 |  | 
|  |             error(string.format(i18n.errors.generic_argument_parameter, 'format', tpl_args.format))
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.format == nil then
 |  | 
|  |         return table.concat(out, '\n')
 |  | 
|  |     elseif tpl_args.format == 'none' then
 |  | 
|  |         return table.concat(out, '\n')
 |  | 
|  |     elseif tpl_args.format == 'li' then
 |  | 
|  |         return table.concat(out)
 |  | 
|  |     end
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Misc. Item templates
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | -- 
 |  | 
|  | -- Template:Item class
 |  | 
|  | --
 |  | 
|  |   |  | 
|  | function p.item_class (frame)
 |  | 
|  |     -- Get args
 |  | 
|  |     local tpl_args = getArgs(frame, {
 |  | 
|  |         parentFirst = true
 |  | 
|  |     })
 |  | 
|  |     frame = m_util.misc.get_frame(frame)
 |  | 
|  |     
 |  | 
|  |     if not doInfoCard then
 |  | 
|  |         doInfoCard = require('Module:Infocard')._main
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     local err = true
 |  | 
|  |     for _, row in pairs(m_game.constants.item.classes) do
 |  | 
|  |         if row['full'] == tpl_args.name then
 |  | 
|  |             err = false
 |  | 
|  |             break
 |  | 
|  |         end
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if err then
 |  | 
|  |         error(string.format(i18n.errors.invalid_class, tostring(tpl_args.name)))
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     if tpl_args.name_list ~= nil then
 |  | 
|  |         tpl_args.name_list = m_util.string.split(tpl_args.name_list, ',%s*')
 |  | 
|  |     else
 |  | 
|  |         tpl_args.name_list = {}
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |     --
 |  | 
|  |     
 |  | 
|  |     local ul = mw.html.create('ul')
 |  | 
|  |     for _, item in ipairs(tpl_args.name_list) do
 |  | 
|  |         ul
 |  | 
|  |             :tag('li')
 |  | 
|  |                 :wikitext(item)
 |  | 
|  |                 :done()
 |  | 
|  |     end
 |  | 
|  |     
 |  | 
|  |   |  | 
|  |     -- Output Infocard
 |  | 
|  |     
 |  | 
|  |     local tplargs = {
 |  | 
|  |         ['header'] = tpl_args.name,
 |  | 
|  |         ['subheader'] = i18n.item_class_infobox.page .. i18n.item_class_infobox.info,
 |  | 
|  |         [1] = i18n.item_class_infobox.also_referred_to_as .. tostring(ul),
 |  | 
|  |     }
 |  | 
|  |     
 |  | 
|  |     -- cats
 |  | 
|  |     
 |  | 
|  |     local cats = {
 |  | 
|  |         'Item classes',
 |  | 
|  |         tpl_args.name,
 |  | 
|  |     }
 |  | 
|  |     
 |  | 
|  |     -- Done
 |  | 
|  |     
 |  | 
|  |     return doInfoCard(tplargs) .. m_util.misc.add_category(cats, {ingore_blacklist=tpl_args.debug})
 |  | 
|  | end
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Debug stuff
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | p.debug = {}
 |  | 
|  |   |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  | -- Return
 |  | 
|  | -- ----------------------------------------------------------------------------
 |  | 
|  |   |  | 
|  | return p
 |  |