Module:Sandbox/Illviljan/Item acquisition

-- -- Imports --

local getArgs = require('Module:Arguments').getArgs local m_game = require('Module:Game') local m_util = require('Module:Util') local m_cargo = require('Module:Cargo') local m_quest_reward = require('Module:Quest reward')._shared local f_item_link = require('Module:Item link').item_link

-- -- 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 = { acquisition = { header = 'Item acquisition', link = '', drop_leagues = 'This item is restricted to areas with the league flag for %s active. There are additional ways to acquire league-restricted items, see League-specific items for more details.', drop_disabled = 'This item is drop disabled.', area_header = 'Area restrictions', area_legacy_header = 'Legacy area restrictions', area = 'This item can be acquired in the following areas:', monster = 'This item can be acquired from the following monsters:', monster_header = 'Monster restrictions', upgraded_from_header = 'Upgrade paths', upgraded_from = 'This item can be acquired through the following upgrade paths or vendor recipes:', ingredient_header = 'Usage in upgrade paths', ingredient = 'This item is used by upgrade paths or vendor recipes to create the following items:', },   upgraded_from_table = { outcome = 'Outcome', ingredient = 'Ingredient', ingredient_amount = 'Amount', ingredient_notes = 'Ingredient Notes', recipe_notes = 'General Notes', type = 'Type', automatic = m_util.html.abbr('Automatic', 'Automatically added by the item template based on the item\'s attributes.'), manual = m_util.html.abbr('Manual', 'Manually added to the item\'s wiki page.'), old_data = m_util.html.abbr('Old data', 'Old data - please null-edit the item\'s wiki page.'), },   quest_reward = { quest_rewards_header = 'Quest reward', quest_rewards_intro = 'This item is given as quest reward for the following quests:', vendor_rewards_header = 'Vendor reward', vendor_rewards_intro = 'This item can be bought at the listed npc vendors after completing the following quests:', }, }

-- -- Globals --

local c = {}

c.MAX_ITEMS = 100 c.COLLAPSE_TABLE = 30

-- -- Helper functions --

local h = {} function h.head(str, level) local head = mw.html.create('h' .. (level or 3)) head:wikitext(str) return tostring(head) end

function h.fetch_upgraded_from_sets(where_set, where_group) if where_set == "" or where_group == "" then return {}, {} end local sets = {} local results = m_cargo.query(       {'upgraded_from_sets'},        {            'upgraded_from_sets._pageName',            'upgraded_from_sets.set_id',            'upgraded_from_sets.text',            'upgraded_from_sets.automatic',        },        {            where=where_set,        }    ) for _, row in ipairs(results) do       row.groups = {} sets[row['upgraded_from_sets._pageName'] .. tonumber(row['upgraded_from_sets.set_id'])] = row end results = m_cargo.query(       {'upgraded_from_groups'},        {            'upgraded_from_groups._pageName',            'upgraded_from_groups.set_id',            'upgraded_from_groups.group_id',            'upgraded_from_groups.notes',            'upgraded_from_groups.amount',            'upgraded_from_groups.item_name',            'upgraded_from_groups.item_page',        },        {            where=where_group,        }    ) for _, row in ipairs(results) do       sets[row['upgraded_from_groups._pageName'] .. tonumber(row['upgraded_from_groups.set_id'])].groups[tonumber(row['upgraded_from_groups.group_id'])] = row end local sets_sort = {} for key, _ in pairs(sets) do       sets_sort[#sets_sort+1] = key end table.sort(sets_sort, function (a, b)       return tonumber(sets[a]['upgraded_from_sets.set_id']) < tonumber(sets[b]['upgraded_from_sets.set_id'])    end) return sets, sets_sort end

function h.upgraded_from_table(tpl_args, sets, set_order, data_type, out, item_pages) -- Count the number of item pages: local item_pages_count = 0 for _,__ in pairs(item_pages) do        item_pages_count = item_pages_count + 1 end -- Avoid creating headers only when no data is given if #set_order <= 0 then return end -- Prevent showing either group or set notes when no rows have any to save a bit of vertical space local set_notes = false local group_notes = false for _, set_key in ipairs(set_order) do       if set_notes and group_notes then break end local set = sets[set_key] if set['upgraded_from_sets.text'] ~= nil then set_notes = true end if #set.groups > 0 then for i, group in ipairs(set.groups) do               if group['upgraded_from_groups.notes'] ~= nil then group_notes = true break end end end end local tbl = mw.html.create('table') -- sorting will mess up the table because of the rowspawns local table_class = 'wikitable mw-collapsible mw-expanded' if item_pages_count > c.COLLAPSE_TABLE then table_class = 'wikitable mw-collapsible mw-collapsed' end tbl:attr('class', table_class) --   -- Header --    local tr = tbl:tag('tr') if data_type == 'ingredient' then tr           :tag('th') :wikitext(i18n.upgraded_from_table.outcome) end tr       :tag('th') :wikitext(i18n.upgraded_from_table.ingredient_amount) :done :tag('th') :wikitext(i18n.upgraded_from_table.ingredient) :done if group_notes then tr           :tag('th') :wikitext(i18n.upgraded_from_table.ingredient_notes) :done end if set_notes then tr           :tag('th') :wikitext(i18n.upgraded_from_table.recipe_notes) :done end tr       :tag('th') :wikitext(i18n.upgraded_from_table.type) :done --   -- Rows --   for _, set_key in ipairs(set_order) do        local set = sets[set_key] if #set.groups > 0 then tr = tbl:tag('tr') tr:attr('class', 'upgraded-from-set') if data_type == 'ingredient' then local str if tpl_args.no_link == nil and item_pages_count < c.MAX_ITEMS then item_data = item_pages[set['upgraded_from_sets._pageName']][1] str = f_item_link{page=set['upgraded_from_sets._pageName'], name=item_data['items.name'], inventory_icon=item_data['items.inventory_icon'] or , html=item_data['items.html'] or , skip_query=true} else str = string.format('%s', set['upgraded_from_sets._pageName']) end tr                   :tag('td') :attr('rowspan', #set.groups) :wikitext(str) :done end for i, group in ipairs(set.groups) do               if i <= 1 then tr2 = tr               else tr2 = tbl:tag('tr') end local str if tpl_args.no_link == nil and item_pages_count < c.MAX_ITEMS then item_data = item_pages[group['upgraded_from_groups.item_page']][1] str = f_item_link{ page=group['upgraded_from_groups.item_page'], name=item_data['items.name'], inventory_icon=item_data['items.inventory_icon'] or '', html=item_data['items.html'] or '', skip_query=true }               else str = string.format('%s', group['upgraded_from_groups.item_page']) end tr2 :tag('td') :attr('data-sort-type', 'number') :wikitext(group['upgraded_from_groups.amount']) :done :tag('td') :wikitext(str) :done if group_notes then if group['upgraded_from_groups.notes'] then tr2 :tag('td') :wikitext(group['upgraded_from_groups.notes']) else tr2:node(m_util.html.td.na{as_tag=true}) end end end if set_notes then if set['upgraded_from_sets.text'] then tr                       :tag('td') :attr('rowspan', #set.groups) :wikitext(set['upgraded_from_sets.text']) else tr                       :tag('td') :attr('class', 'table-na') :attr('rowspan', #set.groups) :wikitext('N/A') :done end end local t           if set['upgraded_from_sets.automatic'] == nil then t = i18n.upgraded_from_table.old_data elseif set['upgraded_from_sets.automatic'] == '1' then t = i18n.upgraded_from_table.automatic else t = i18n.upgraded_from_table.manual end tr               :tag('td') :attr('rowspan', #set.groups) :wikitext(t) end end --   -- print --   if data_type == 'ingredient' then out[#out+1] = h.head(i18n.acquisition.ingredient_header) out[#out+1] = i18n.acquisition.ingredient else out[#out+1] = h.head(i18n.acquisition.upgraded_from_header) out[#out+1] = i18n.acquisition.upgraded_from end out[#out+1] = ' ' out[#out+1] = tostring(tbl) end

function h.reward_table(data, rtbl) if #data == 0 then return end local tbl = m_quest_reward.reward_tbl_head for _, row in ipairs(data) do       local classes if row[rtbl .. '.classes'] then classes = {} for _, class in ipairs(m_util.string.split(row[rtbl .. '.classes'], '�')) do               classes[class] = true end end local tr = tbl:tag('tr') m_quest_reward.reward_tbl_row_head(tr, rtbl, row) local cell = { [0] = {               value = '✗', sort = 0, class = 'table-cell-xmark', },           [1] = {                value = '✓', sort = 1, class = 'table-cell-checkmark', },       }        if rtbl == 'quest_rewards' then local value = m_quest_reward.reward_tbl_extra_info(row) -- If there isn't any extra information the checkmark will do           if value ~= '' then cell[1].value = value cell[1].class = nil cell[1].css = 'text-align: center;' end end if classes then for _, class in ipairs(m_game.constants.characters_order) do               local cell_value if classes[m_game.constants.characters[class].name] then cell_value = cell[1] else cell_value = cell[0] end tr:tag('td') :attr('class', cell_value.class or '') :attr('style', cell_value.css or '') :attr('table-sort-value', cell_value.sort) :wikitext(cell_value.value) end else tr:tag('td') :attr('colspan', 7) :attr('class', cell[1].class or '') :attr('style', cell[1].css or '') :attr('table-sort-value', cell[1].sort) :wikitext(cell[1].value) end end return h.head(i18n.quest_reward[rtbl .. '_header']) .. i18n.quest_reward[rtbl .. '_intro'] .. tostring(tbl) end

-- -- Templates --

local p = {}

-- -- Template: Item acquisition -- function p.item_acquisition (frame) --   Duplicates the information from the infobox in a more readable     manner on the page.    Example    ---    = p.item_acquisition{page='The Rite of Elements'}

-- Get args local tpl_args = getArgs(frame, {       parentFirst = true    }) frame = m_util.misc.get_frame(frame) tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle) local out = {} local results local query out[#out+1] = tpl_args.acquisition_insert -- fetch general item drop information that is used in multiple places in a   -- single query to reduce performance hit local item_data = m_cargo.query(       {'items'},        {            'items.drop_text',            'items.drop_leagues',            'items.drop_areas__full',            'items.drop_monsters',            'items.drop_enabled',        },        {            where=string.format('items._pageName="%s"', tpl_args.page),            limit=1,        }    ) if #item_data > 0 then item_data = item_data[1] end --    -- Drop disabled item --    local drop_enabled = m_util.cast.boolean(item_data['items.drop_enabled']) if not drop_enabled then out[#out+1] = i18n.acquisition.drop_disabled out[#out+1] = ' ' end --    -- Drop restrictions by league --    if item_data['items.drop_leagues'] and drop_enabled then out[#out+1] = string.format(i18n.acquisition.drop_leagues, item_data['items.drop_leagues']) out[#out+1] = ' ' end --    -- Drop restrictions by text --    if item_data['items.drop_text'] and drop_enabled then out[#out+1] = item_data['items.drop_text'] end --    -- Drop restrictions by area --    local area_ids if item_data['items.drop_areas__full'] then area_ids = m_util.string.split(item_data['items.drop_areas__full'], ',') else area_ids = {} end -- Handle legacy areas: local legacy_area_ids = { -- 'MapWar%', 'MapAtlas%', 'Map2%', 'MapTier%', }   local condition_current = {} local condition_legacy = {} local order_legacy = {} for i,v in ipairs(legacy_area_ids) do        condition_current[#condition_current+1] = string.format('areas.id NOT LIKE "%s"', v)        condition_legacy[#condition_legacy+1] = string.format('areas.id LIKE "%s"', v)        order_legacy[#order_legacy+1] = string.format('WHEN areas.id LIKE "%s" THEN %d', v,i) end local query_sets = { {           header=i18n.acquisition.area_header, condition=string.format('%s', table.concat(condition_current, ' AND ')), order='areas.name ASC', },       {            header=i18n.acquisition.area_legacy_header, condition=string.format('%s', table.concat(condition_legacy, ' OR ')), order=string.format(               CASE                     %s                    ELSE %d                 END ASC,                 areas.name ASC           ,             table.concat(order_legacy, ' '),             #order_legacy+1            ), },   }    if #area_ids > 0 then for _, query_set in ipairs(query_sets) do           local results = m_cargo.query(                {'areas'},                {                    'areas._pageName',                    'areas.id',                    'areas.name',                    'areas.main_page',                },                {                    where=string.format('(%s) AND areas.id IN ("%s")', query_set.condition, table.concat(area_ids, '","')),                    orderBy=query_set.order,                    groupBy='areas.id',                }            ) if #results > 0 then local ul = mw.html.create('ul') for _, row in ipairs(results) do                   ul:tag('li') :wikitext(string.format('%s', row['areas.main_page'] or row['areas._pageName'], row['areas.name'])) end out[#out+1] = h.head(query_set.header) out[#out+1] = i18n.acquisition.area out[#out+1]= ' ' out[#out+1] = tostring(ul) end end end --    -- Drop restrictions by monster --

local monster_metadata_ids = {} if item_data['items.drop_monsters'] then monster_metadata_ids = m_util.string.split(item_data['items.drop_monsters'], ',%s*') end if #monster_metadata_ids > 0 then local results = m_cargo.query(           {'monsters', 'main_pages'},            {                'monsters._pageName',                'monsters.metadata_id',                'monsters.name',                -- 'monsters.main_page',                'main_pages._pageName',            },            {                join='monsters.metadata_id=main_pages.id',                where=string.format('monsters.metadata_id IN ("%s")', table.concat(monster_metadata_ids, '","')),                orderBy='monsters.name',                groupBy='monsters.metadata_id',            }        ) if #results > 0 then local ul = mw.html.create('ul') for _, row in ipairs(results) do               ul:tag('li') :wikitext(string.format( '%s', row['monsters.main_page'] or row['main_pages._pageName'] or row['monsters._pageName'], row['monsters.name'] ))           end out[#out+1] = h.head(i18n.acquisition.monster_header) out[#out+1] = i18n.acquisition.monster out[#out+1]= ' ' out[#out+1] = tostring(ul) end end --    -- Vendor recipes/upgrades handling --    --    -- Query set data --   local obtained_sets, obtained_sets_order = h.fetch_upgraded_from_sets(        string.format('upgraded_from_sets._pageName="%s"', tpl_args.page),        string.format('upgraded_from_groups._pageName="%s"', tpl_args.page)    ) results = m_cargo.query(       {'upgraded_from_groups'},        {            'upgraded_from_groups._pageID',            'upgraded_from_groups.set_id',        },        {            where=string.format('upgraded_from_groups.item_page="%s"', tpl_args.page),            -- only need one result set for the where clause            groupBy='upgraded_from_groups._pageID, upgraded_from_groups.set_id',        }    ) local where = {sets={}, groups={}} for key, data in pairs(where) do       for _, row in ipairs(results) do            data[#data+1] = string.format('upgraded_from_%s.set_id="%s" AND upgraded_from_%s._pageID="%s"', key, row['upgraded_from_groups.set_id'], key, row['upgraded_from_groups._pageID']) end where[key] = table.concat(data, ' OR ') end local ingredient_sets, ingredient_sets_order = h.fetch_upgraded_from_sets(where.sets, where.groups) --   -- Query bulk item info for item linking --   local item_pages = {assoc={}} for _, set_data in ipairs({{obtained_sets, obtained_sets_order}, {ingredient_sets, ingredient_sets_order}}) do       for _, set_key in ipairs(set_data[2]) do            local set = set_data[1][set_key] for _, group in ipairs(set.groups) do               item_pages.assoc[group['upgraded_from_groups.item_page']] = true end item_pages.assoc[set['upgraded_from_sets._pageName']] = true end end -- remove duplicates for name, _ in pairs(item_pages.assoc) do       item_pages[#item_pages+1] = name end if #item_pages < c.MAX_ITEMS then item_pages = m_cargo.map_results_to_id{ results=m_cargo.array_query{ tables={'items'}, fields={'items.name', 'items.inventory_icon', 'items.html'}, id_array = item_pages, id_field = 'items._pageName', query = { limit=5000 },           },            field='items._pageName', keep_id_field=true, }   end --    -- Output for being obtained via vendor recipe --   h.upgraded_from_table(tpl_args, obtained_sets, obtained_sets_order, 'obtained', out, item_pages) --    -- Ingredient of vendor recipes/upgrades --    h.upgraded_from_table(tpl_args, ingredient_sets, ingredient_sets_order, 'ingredient', out, item_pages) out[#out+1] = tpl_args.ingredient_append --    -- Obtained via quest or vendor reward --    local quest_rewards = m_cargo.query(        {'quest_rewards'},        {            'quest_rewards.quest',            'quest_rewards.act',            'quest_rewards.classes',            'quest_rewards.sockets',            'quest_rewards.item_level',            'quest_rewards.rarity',        },        {            where=string.format('quest_rewards.reward="%s"', tpl_args.page),            orderBy='quest_rewards.act ASC, quest_rewards.quest_id ASC',        }    ) out[#out+1] = h.reward_table(quest_rewards, 'quest_rewards') local vendor_rewards = m_cargo.query(       {'vendor_rewards'},        {            'vendor_rewards.quest',            'vendor_rewards.act',            'vendor_rewards.classes',            'vendor_rewards.npc',        },        {            where=string.format('vendor_rewards.reward="%s"', tpl_args.page),            orderBy='vendor_rewards.act ASC, vendor_rewards.quest_id ASC',        }    ) out[#out+1] = h.reward_table(vendor_rewards, 'vendor_rewards') --    -- output --    mw.logObject(tpl_args)

local head = mw.html.create('h2') head:wikitext(i18n.acquisition.header .. i18n.acquisition.link) return tostring(head) .. table.concat(out) end

-- -- Return --

return p