Module:Ingredient Utilization

From Ylvapedia

Documentation for this module may be created at Module:Ingredient Utilization/doc

local p = {}
local utils = require('Module:Utils')
local items = require('Module:Item')
local inline = require('Module:CraftInline')
local i18n = mw.loadData('Module:Ingredient Utilization/i18n')
local categories = mw.loadData('Module:Item/Categories')

--★Elin:アイテム名 num skin+Numの場合もある
local function checkFullNameAndMatchPageName(fullName, name)
  local fullNameName = string.gsub( fullName, 'Elin:%s*', '' )
  if fullNameName ~= name then
    local pattern1 = '^(.+) ([0-9]+)$'
    local match1_1, match1_2 = mw.ustring.match(fullNameName, pattern1)
    --mw.log('match1_1: ' , tostring(match1_1))
    --mw.log('match1_2: ' , tostring(match1_2))
    return match1_1, match1_2, fullNameName
  end
  return nil, nil, fullNameName
end

--valueをname,mat,skin,varietyに分離
local function getResultItem(value)
  local name = value
  local num
  local mat
  local skin
  local var
  local ele
  if not name then
    return
  end
  if string.find(name, '/') then
    name, num = name:match('([^/]+)/([^/]+)')
  end
  if string.find(name, '@') then
    name, mat = name:match('([^@]+)@([^@]+)')
  end
  if string.find(name, ':') then
    name, skin = name:match('([^:]+):([^:]+)')
  end
  if string.find(name, '_') then
    name, var = name:match('([^_]+)_([^_]+)')
  end
  if string.find(name, '%+') then
    name, element = name:match("([^%+]+)%+([^%+]+)")
  end
  return name, num, mat, skin, var, ele
end

function p.ask(frame)
  local lang = utils.nilIfEmpty(frame.args['lang']) or 'JA'
  if not mw.smw then
    error(tostring(i18n[lang]['error_mwsmw']))
  end

  local currentPage = mw.title.getCurrentTitle()
  local pageName = currentPage.text
  local fullPageName  = currentPage.fullText
  local parentArgs = frame:getParent().args
  local pageCut = parentArgs['pageCut'] or true
  local icon = utils.nilIfEmpty(parentArgs['icon'])
  local highlight = parentArgs['highlight'] or 'true'
  local args = {}
  args[1] = pageName
  args[2] = pageName..'+'

  local highlightParent = {}
  if parentArgs then
    local nextIndex = 3
    for key, value in pairs(parentArgs) do
      if type(key) == 'number' then
        args[nextIndex] = value
        nextIndex = nextIndex + 1
        if highlight == 'true' then
          table.insert(highlightParent, value)
        end
      else
        args[key] = value
      end
    end
    if highlight == 'true' then
      table.insert(highlightParent, pageName)
      --mw.log('highlightParent: ',tostring(highlightParent))
    end
  end

  if args == nil then
    error(tostring(i18n[lang]['error_arg']))
  end

  local allResults = {}
  local mode = frame.args['mode'] or utils.nilIfEmpty(args['mode']) or 'table'
  if mode ~= 'table' and mode ~= 'list' and mode ~= 'item' and mode ~= 'craftstation' then
    error(tostring(i18n[lang]['error_mode']))
  end

  for key, value in pairs(frame.args) do
    if type(key) == 'number' then
      args[key] = value
    end
  end

  --加工設備除外リスト
  local ExcludeProcessor
  local ResultProcessorList = {}
  if args['case'] and type(args['case']) == 'string' then
    ExcludeProcessor = string.find(args['case'],'noProcessor')
    if ExcludeProcessor then
      local ProcessorList = {
        'klin',
        'jewelybench',
        'factory_sculpture',
        'dyemaker',
        'factory_barrel',
        'beehive',
        'shelf_aging',
        'brewery',
        'dry_brick',
        'dry_rack',
        'sawmill',
        'spinner',
        'grindstone',
        'stonecutter',
        'smelter',
        'millstone',
        'millstone_wood',
        'incubator',
        'rationmaker',
        '453'
      }

      if lang == 'JA' then
        local dataAliases = mw.loadData('Module:Item/JaAliases')
        for _, processor in ipairs(ProcessorList) do
          for jaAlias, idAlias in pairs(dataAliases) do
            if idAlias == processor then
              table.insert(ResultProcessorList, jaAlias)
              break
            end
          end
        end
      elseif lang == 'EN' then
        local dataAliases = mw.loadData('Module:Item/IDAliases')
        for _, processor in ipairs(ProcessorList) do
          for enAlias, idAlias in pairs(dataAliases) do
            if idAlias == processor then
              table.insert(ResultProcessorList, enAlias)
              break
            end
          end
        end
      else
        ResultProcessorList = ProcessorList
      end
    end
  end

  --除外用アイテムのテーブル作成
  local excludeArg = utils.nilIfEmpty(args[i18n[lang]['ExcludedItems']])
  local excludedItemsList = {}

  --呼び出し元テンプレートに設定された除外アイテムの追加
  if excludeArg then
    local splitResult = {}
    splitResult = mw.text.split(excludeArg, ',')
    if type(splitResult) ~= 'table' then
      splitResult = { splitResult }
    end
    for _, item in ipairs(splitResult) do
      table.insert(excludedItemsList, mw.text.trim(item))
    end
  end
  --呼び出し元のフルページ名に設定された除外アイテムの追加
  local query = string.format('[[%s]]|?%s|?%s', fullPageName, 'CraftingExcludedItems', 'Name')
  local result = mw.smw.ask(query)
  local PropertyName
  local splitExcludedItems
  if result then
    PropertyName = result[1]['Name']
    splitExcludedItems = result[1]['CraftingExcludedItems'] and mw.text.split(result[1]['CraftingExcludedItems'], '%s*,%s*') or result[1]['CraftingExcludedItems']
  end
  if splitExcludedItems then
    if type(splitExcludedItems) ~= 'table' then
    splitExcludedItems = { splitExcludedItems }
    end
    for _, value in ipairs(splitExcludedItems) do
      table.insert(excludedItemsList, value)
    end
  end

  -- excludedItemsList内の重複削除
  if #excludedItemsList > 0 then
    local seen = {}
    local uniqueExcludeItems = {}
    for _, item in ipairs(excludedItemsList) do
      if not seen[item] then
        seen[item] = true
        table.insert(uniqueExcludeItems, item)
      end
    end
    excludedItemsList = uniqueExcludeItems
  end
  mw.log('除外アイテムリスト')
  mw.logObject(excludedItemsList)

  for i = 1, #args do
    if args[i] then
      mw.log('label'..i..':'..args[i])
      local query
      if mode == 'craftstation' then
        query = '[[Category:Elin '..i18n[lang]['ITEM']..']] [[Category:Elin '..args[i]..']]'
      else
        if string.find(args[i], '%+') then
        query = '[[Category:Elin '..i18n[lang]['ITEM']..']] [[IngredientAll::'..args[i]..'*]] OR [[IngredientAll::~*,'..args[i]..'*]] OR  [[IngredientAll::~*'..args[i]..'*]]'
        else
        query = '[[Category:Elin '..i18n[lang]['ITEM']..']] [[IngredientAll::'..args[i]..']] OR [[IngredientAll::~*,'..args[i]..'*]] OR  [[IngredientAll::~*'..args[i]..',*]]'
        end
      end
      local resultN = mw.smw.ask{
        query,
        '?ID',
        '?Name',
        '?Full Name',
        '?ItemLv',
        '?Type',
        '?CraftingStation',
        '?Ingredient',
        '?IngredientNum',
        '?IngredientExtra',
        '?CraftingStation2',
        '?Ingredient2',
        '?IngredientNum2',
        '?IngredientExtra2',
        '?CraftingStation3',
        '?Ingredient3',
        '?IngredientNum3',
        '?IngredientExtra3',
        '?CraftingResult',
        '?CraftingResult2',
        '?CraftingResult3',
        '?ElementNote',
        limit = 99,
        mainlabel = '-'
      } or {}

      --excludedItemsListの除外アイテムをresultNから省いて新しくテーブルを作成する
      for _, row in pairs(resultN) do
        local shouldInsert = true
        if row['Full Name'] == fullPageName and pageCut == true then
          shouldInsert = false
        elseif #excludedItemsList > 0 and table.indexOf(excludedItemsList, tostring((row['Full Name'] or ''):gsub('Elin:%s*', ''))) then
          shouldInsert = false
        elseif ExcludeProcessor then
          local checkStations = {}
          local stationStrings = {row.CraftingStation, row.CraftingStation2, row.CraftingStation3}
          for _, s in ipairs(stationStrings) do
            if s then
              local splitStations = mw.text.split(s, ',')
              for _, st in ipairs(splitStations) do
                table.insert(checkStations, mw.text.trim(st))
              end
            end
          end
          if checkStations then
            for _, station in ipairs(checkStations) do
              if station and table.indexOf(ResultProcessorList, mw.text.trim(station)) then
                shouldInsert = false
                break
              end
            end
          end
        end
        --allResults挿入確定
        if shouldInsert then
          --sortValの追加
          if row.Type then
            for catKey, catValue in pairs(categories) do
              if lang == 'JA' and row.Type == catValue.name_JP or
              lang == 'EN' and row.Type == catValue.name then
                row.sortVal = catValue.sortVal
                break
              end
            end
          end
          if not row.sortVal then
            row.sortVal = 0
          end
          table.insert(allResults, row)
        end
      end
    end
  end
  -- allResults 重複削除
  local uniqueResults = {}
  local names = {}
  for _, row in pairs(allResults) do
    if row['Full Name'] then
      local itemName = tostring(row['Full Name'])
      if not names[itemName] then
        table.insert(uniqueResults, row)
        names[itemName] = true
      end
    end
  end

  if #uniqueResults > 0 then
    --modeの強制変更 テーブル型だと実時間が長くなりやすい。アイテムページが増えすぎた場合に行う
    --if #uniqueResults > 30 then
    --  mode = 'list'
    --end

    --プロパティとカテゴリ追加用
    local addcategory = ''
    if currentPage.nsText == 'Elin' then
      utils.setPropertyData('Use',i18n[lang]['use'])
      addcategory = i18n[lang]['category']
    end

    --ソート
    local function sortByTypeAndItemLv(a, b)
      if a.sortVal ~= b.sortVal then
        return a.sortVal < b.sortVal
      else
        local itemLvA = tonumber(a.ItemLv) or 0
        local itemLvB = tonumber(b.ItemLv) or 0
        return itemLvA < itemLvB
      end
    end

    local function sortByItemLv(a, b)
      local itemLvA = tonumber(a.ItemLv) or 0
      local itemLvB = tonumber(b.ItemLv) or 0
      return itemLvA < itemLvB
    end

    if mode == 'craftstation' then
      table.sort(uniqueResults, sortByItemLv)
    else
      table.sort(uniqueResults, sortByTypeAndItemLv)
    end

    if mode == 'list' then
    --=====リストで表示=====
      local listResult = ''
      local stationItems = {}

      for _, row in pairs(uniqueResults) do
        local name = row.Name or ''
        local fullName = row['Full Name'] or ''
        local craftingStation = row.CraftingStation or ''

        local stations = string.find(craftingStation, ',') and mw.text.split(craftingStation, ',') or {mw.text.trim(craftingStation)}
        for _, station in ipairs(stations) do
          local trimmedStation = mw.text.trim(station)
          if stationItems[trimmedStation] then
            table.insert(stationItems[trimmedStation], '[['..fullName..'|'..name..']]')
          else
            stationItems[trimmedStation] = { '[['..fullName..'|'..name..']]' }
          end
        end
      end

      for station, items in pairs(stationItems) do
        local separator = i18n[lang]['list_separator']
        local formatString = i18n[lang]['list_format']
        local itemString = table.concat(items, separator)
        if lang == 'EN' and #items > 1 then
          local lastItem = table.remove(items)
          itemString = table.concat(items, separator)..i18n[lang]['list_and']..lastItem
        end
        listResult = listResult..'*'..string.format(formatString, station, itemString)..'<br>\n'
      end
    
      return listResult, addcategory
    elseif mode == 'table' or mode == 'craftstation'  then
    --=====テーブルで表示=====
      local html = mw.html.create('div')
    
      if #uniqueResults > 10 then
        html:wikitext(frame:expandTemplate{ title = 'Collapse top', args = { lang = lang, expanded = 1, width = 'auto' } })
      end

      local table = html:tag('table'):addClass('wikitable sortable')
      local headerRow = table:tag('tr')
      headerRow:tag('th'):wikitext(i18n[lang]['th_Craft'])
      headerRow:tag('th'):wikitext(i18n[lang]['th_Ingredient'])
      if mode == 'table' then headerRow:tag('th'):wikitext(i18n[lang]['th_CraftingStation']) end
      headerRow:tag('th'):wikitext(i18n[lang]['th_Type'])
      headerRow:tag('th'):wikitext(i18n[lang]['th_Lv'])
    
      for _, row in pairs(uniqueResults) do
        local Name = row.Name or ''
        local fullName = row['Full Name'] or ''
        local ingredient = row.Ingredient or ''
        local ingredientNum = row.IngredientNum or ''
        local ingredientExtra = row.IngredientExtra or ''
        local ingredient2 = row.Ingredient2 or ''
        local ingredientNum2 = row.IngredientNum2 or ''
        local ingredientExtra2 = row.IngredientExtra2 or ''
        local ingredient3 = row.Ingredient3 or ''
        local ingredientNum3 = row.IngredientNum3 or ''
        local ingredientExtra3 = row.IngredientExtra3 or ''
        local craftingStation = row.CraftingStation or ''
        local craftingStation2 = row.CraftingStation2 or ''
        local craftingStation3 = row.CraftingStation3 or ''
        local craftingResult = row.CraftingResult or ''
        local craftingResult2 = row.CraftingResult2 or ''
        local craftingResult3 = row.CraftingResult3 or ''
        local ElementNote = row.ElementNote or ''
        local itemType = row.Type or ''
        local itemLv = row.ItemLv or ''

        local setItem, setItems
        local itemName, itemNum, itemMat, itemSkin, itemVar, itemEle
        local fullNameName

        --クラフト結果
        if mode == 'craftstation' then
          if craftingStation == pageName then
            setItem = craftingResult
          elseif craftingStation2 == pageName then
            setItem = craftingResult2
          elseif craftingStation3 == pageName then
            setItem = craftingResult3
          end
        else
          --クラフト結果の中に対象アイテム名が含まれている場合にクラフト結果を渡す
          if utils.nilCheck(craftingResult) and string.find(craftingResult, Name) then
            setItem = craftingResult
          elseif utils.nilCheck(craftingResult2) and string.find(craftingResult, Name) then
            setItem = craftingResult2
          elseif utils.nilCheck(craftingResult3) and string.find(craftingResult, Name) then
            setItem = craftingResult3
          end
        end

        if utils.nilCheck(setItem) then
          if string.find(setItem, ',') then
            setItems = mw.text.split(setItem, '%s*,%s*') or {}
            for _, item in ipairs(setItems) do
              local parts = mw.text.split(item, '/')
              if parts[1] == Name then
                setItem = item
                break
              end
            end
          end
          itemName, itemNum, itemMat, itemSkin, itemVar, itemEle = getResultItem(setItem)
        else
          --craftingResultが設定されていなければ、ページ名とアイテム名を比較して分離代入
          --例えば"牛乳 2"と"牛乳"、"繊維 (雑貨)"と"繊維"、"障子 skin2"と"障子"
          --★いずれスキンのページが出た場合はパラメーターskinを入れる。いまはvarietyだけ
          itemName, itemVar, fullNameName = checkFullNameAndMatchPageName(fullName, Name)
        end

        local itemTemplate = items.generateItem({
          lang = lang,
          name = itemName or fullNameName or Name,
          num = itemNum,
          itemMat = itemMat,
          skin = itemSkin,
          variety = itemVar,
          element = itemEle,
          link = fullName,
          icon = icon
        })

        --材料結果
        local stringLength = string.len(ingredient..ingredient2..ingredient3)
        local craftTemplate
        if row.ID == 'cheese' or row.ID == 'jerky' or row.ID == '48' then
          local match1_1, match1_2, fullNameName = checkFullNameAndMatchPageName(pageName, PropertyName)
          craftTemplate = '<span class="item-highlight">'..items.generateItem({ lang = lang, name = match1_1 or fullNameName, variety = match1_2, link = fullPageName, icon = icon })..'</span>'..i18n[lang]['andMore']
        elseif row.ID == 'fish_slice' then
          craftTemplate = '<span class="item-highlight">'..items.generateItem({ lang = lang, name = pageName, link = fullPageName, icon = icon })..'</span>'
        else
          local inlineData = {
            lang = lang,
            mode = 'inlinehr',
            icon = icon,
            highlight = true,
            highlightParent = highlightParent,
            ElementNote = ElementNote,
            ['1-2'] = ingredient,
            ['1-3'] = ingredientNum,
            ['1-4'] = ingredientExtra,
            ['2-2'] = ingredient2,
            ['2-3'] = ingredientNum2,
            ['2-4'] = ingredientExtra2,
            ['3-2'] = ingredient3,
            ['3-3'] = ingredientNum3,
            ['3-4'] = ingredientExtra3
          }
          if mode == 'craftstation' then
            local foundPrefix = nil
            if craftingStation == pageName then
              foundPrefix = '1-'
            elseif craftingStation2 == pageName then
              foundPrefix = '2-'
            elseif craftingStation3 == pageName then
              foundPrefix = '3-'
            end
            if foundPrefix then
              inlineData['1-2'] = inlineData[foundPrefix .. '2']
              inlineData['1-3'] = inlineData[foundPrefix .. '3']
              inlineData['1-4'] = inlineData[foundPrefix .. '4']
              inlineData['2-2'] = nil; inlineData['2-3'] = nil; inlineData['2-4'] = nil;
              inlineData['3-2'] = nil; inlineData['3-3'] = nil; inlineData['3-4'] = nil;
            end
          end
          craftTemplate = inline.generatecraftInline(inlineData)
        end
        --設備結果
        local inlineData = {
          lang = lang,
          mode = 'inlinehr',
          icon = icon,
          highlight = true,
          highlightParent = highlightParent,
          ['1-1'] = craftingStation,
          ['2-1'] = craftingStation2,
          ['3-1'] = craftingStation3
        }
        if mode == 'craftstation' then
          local foundPrefix = nil
          if craftingStation == pageName then
            foundPrefix = '1-'
          elseif craftingStation2 == pageName then
            foundPrefix = '2-'
          elseif craftingStation3 == pageName then
            foundPrefix = '3-'
          end
          if foundPrefix then
            inlineData['1-1'] = inlineData[foundPrefix .. '1']
            inlineData['2-1'] = nil; inlineData['3-1'] = nil
          end
        end
        local stationTemplate = inline.generatecraftInline(inlineData)
    
        local itemTypeString
        if itemType == 'アイテム' or itemType == '魚' or itemType == 'きのこ' then
          itemTypeString = '[[:Category:Elin '..itemType..' (分類)|'..itemType..']]'
        else
          itemTypeString = '[[:Category:Elin '..itemType..'|'..itemType..']]'
        end
        local dataRow = table:tag('tr')
        dataRow:tag('td'):wikitext(itemTemplate)
        dataRow:tag('td'):wikitext(craftTemplate)
        if mode == 'table' then dataRow:tag('td'):wikitext(stationTemplate) end
        dataRow:tag('td'):attr('data-sort-value', row.sortVal):wikitext(itemTypeString)
        dataRow:tag('td'):wikitext(itemLv)
      end
    
      if #uniqueResults > 10 then
        html:wikitext(frame:expandTemplate{ title = 'Collapse bottom' })
      end
    
      return tostring(html), addcategory
    elseif mode == 'item' then
    --=====アイテムで表示=====
      local maxNameLength = 0
      local itemTemplates = {}
      for _, row in pairs(uniqueResults) do
        local name = row.Name or ''
        local fullName = row['Full Name'] or ''
        local nameLength = mw.ustring.len(name)
        if nameLength > maxNameLength then maxNameLength = nameLength end
        local match1_1, match1_2, fullNameName = checkFullNameAndMatchPageName(fullName, name)
        table.insert(itemTemplates, items.generateItem({ lang = lang, name = match1_1 or name, variety = match1_2, link = fullName, icon = icon }))
      end
      local columnWidth = (maxNameLength + 1)..'em'
      local itemListArgs = { style = 'column-width:'..columnWidth..';' }
      for i, template in ipairs(itemTemplates) do itemListArgs[i] = template end
      local itemList = frame:expandTemplate{title = 'Itemlist', args = itemListArgs}
      return itemList, addcategory
    end
  else
    return i18n[lang]['error_NoResult']
  end
end

function table.indexOf(tab, val)
  for i, v in ipairs(tab) do
    if v == val then return i end
  end
  return nil
end

return p