Module:Item

From Secrets of Grindea Wiki
Jump to navigation Jump to search

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

local p = {}

local func = require('Module:Func')
local data = mw.loadData('Module:Item/data')

local function resolveAlias(alias)
  if alias == nil then return nil end
  return data.aliases[alias] or alias
end

local function getStat(item, stat)
  return data.stats[item][stat]
end

local function getExtraStat(item, stat)
  extraStats = data.extraStats[item]
  return extraStats and extraStats[stat] or nil
end

local function getPrice(item, priceType)
  local tradeable = not func.has_value(cats, "NoDropNoTrade")
  
  if not tradeable then
  	return ""
  end
  
  local value     = tonumber(data.stats[item].value)
  local arcademod = tonumber(data.stats[item].arcademod) or 1
  
  if not value then
  	return ""
  end
  
  local extraStats = data.extraStats[item]
  
  local tags           = extraStats and extraStats.tags or {}
  local cats           = data.stats[item].cat
  local inStory        = not func.has_value(tags, "NotInStory")
  local inArcade       = not func.has_value(tags, "NotInArcade")
  local storyMerchant  = func.has_value(tags, "StoryBuy")
  local arcadeMerchant = func.has_value(tags, "ArcadeBuy")
  local buyOnly        = func.has_value(tags, "BuyOnly")
  
  local equippable = (
  	func.has_value(cats, "Weapon") or
  	func.has_value(cats, "Shoes") or
  	func.has_value(cats, "Shield") or
  	func.has_value(cats, "Hat") or
  	func.has_value(cats, "Facegear") or
  	func.has_value(cats, "Armor") or
  	func.has_value(cats, "Accessory")
  )
  
  if inStory then
	if priceType == "storybuy" then
      return storyMerchant and value or "No Merchant"
	elseif priceType == "storysell" then
	  return buyOnly and "Bought Only" or math.floor(value / 2)
	elseif priceType == "storybuyback" then
	  return buyOnly and "Bought Only" or value * 2
	end
  end
  
  if inArcade then
  	if priceType == "arcadebuy" then
  	  return arcadeMerchant and value * arcademod or "No Merchant"
  	elseif priceType == "arcadesell" then
      -- Equipment has an additional 50% sell penalty in Arcade
      return buyOnly and "Bought Only" or math.floor(0.5 * math.floor(value * arcademod) * (equippable and 0.5 or 1))
    end
  end
  
  return ""
end

local getters = {
  name = getStat,
  desc = getStat,
  sprite = function(item)
  	-- Some items might not have a sprite uploaded yet
  	return getExtraStat(item, 'sprite') or "Placeholder.png"
  end,
  type = function(item) 
    return data.typeToLink[data.stats[item].type] or "Unknown Type"
  end,
  class = function(item) 
    return data.typeToClass[data.stats[item].type] or "Unknown Class"
  end,
  bowdmg = getExtraStat,
  atk = getStat,
  matk = getStat,
  aspd = getStat,
  cspd = getStat,
  maxhp = getStat,
  maxep = getStat,
  epreg = getStat,
  def = getStat,
  shldhp = getStat,
  crit = getStat,
  critdmg = getStat,
  special = function(item) 
    return data.effects[data.stats[item].special] 
  end,
  foodtype = getExtraStat,
  foodexp = getExtraStat,
  pricestory = getExtraStat,
  pricearcade = getExtraStat,
  storybuy = getPrice,
  storysell = getPrice,
  storybuyback = getPrice,
  arcadebuy = getPrice,
  arcadesell = getPrice,
  storyAvailable = function(item) 
  	local extraStats = data.extraStats[item]
  	local exists = not func.has_value(extraStats and extraStats.tags or {}, "NotInStory")
  	return exists and "Yes" or "No"
  end,
  arcadeAvailable = function(item) 
  	local extraStats = data.extraStats[item]
  	local exists = not func.has_value(extraStats and extraStats.tags or {}, "NotInArcade")
  	return exists and "Yes" or "No"
  end
}

function p.data(frame)
  local alias = frame.args[1]
  local stat = frame.args[2]
  
  local item       = resolveAlias(frame.args[1])
  local softErrors = (frame.args.softErrors ~= nil)

  local function err(msg)
	return softErrors and "Error" or ("Error: " .. msg)
  end

  if not item then return err("Missing item") end
  if not stat then return err("Invalid stat") end

  if not data.stats[item] then
  	if alias == item then
      return err("Item " .. item .. " does not exist")
    else
      return err("Item " .. alias .. " (" .. item .. ") does not exist")
    end
  end
  
  local getter = getters[stat]

  if not getter then
    return err("Stat " .. stat .. " does not exist")
  end
  
  return getter(item, stat) or ""
end

--[[

Random Item function

Returns a random item's ID.
The function will try to find a random item that is not nil (i.e. its Template:Itembox renders at least partially correct). 
If it cannot do so in X tries, it will default to Wooden Sword's ID.

--]]

-- Item ban list
-- All matching keys will be skipped
-- Used for problematic items, or items that may spoil story elements by accident

local randomBanList = {
  "default",

-- Additional Stuff
  "arrowquiver",
  "silverpoint",
  "goldpoint",
  "talentpoint",
  "threetalentpoints",
  "healthorb",
  "biggoldcoin",

-- Furniture
  "wisp_furniture"
}

function p.randomitem(frame)
  -- This function is potentially expensive
  mw.incrementExpensiveFunctionCount()
  
  math.randomseed(os.time())

  -- Build key table

  local keys = {}
  local size = 1
  for key, value in pairs(data) do
    if not func.has_value(randomBanList, key) then
      keys[size] = key
      size = size + 1
    end
  end

  local tries = 0
  local maxTries = 20
  while tries < maxTries do
    tries = tries + 1
    local nextkey = keys[math.random(size - 1)]
    local next = data.stats[resolveAlias(nextKey)]
    if next then
      -- OK
      return nextkey
    end
  end

  -- Failed
  return 'woodensword'
end


--[[

Featured Item function
Returns a random item's ID from the attached featuredItems list.

--]]

-- List created by Marioalexsan at 28 Dec 2020

local featuredItems = {
  "bladeofechoes",
  "toyaxe",
  "laserclaymore",
  "staffofslimes",
  "marinosrapier",

  "promo_shield",
  "solemshield",
  "energyshield",
  "crystalshield",

  "hauntedblindfold",
  "daisyarmor",
  "crabbyhelm",
  "purplepetalgarland",
  "crystalpumps",
  "rollerblades",

  "eazsunring",
  "mysteriouscube",
  "kobestag",
  "triskele",
  "magicbattery",
  "skullring",
  
  "hoodofdarkness",
  "redslimekinghat"
}

function p.featureditem(frame)
  -- This function is potentially expensive
  mw.incrementExpensiveFunctionCount()
  math.randomseed(os.time())

  local size = #featuredItems + 1

  local tries = 0
  local maxTries = 20
  while tries < maxTries do
    tries = tries + 1
    local nextkey = featuredItems[math.random(size - 1)]
    local next = data.stats[resolveAlias(nextKey)]
    if next then
      -- OK
      return nextkey
    end
  end

  -- Failed
  return 'woodensword'
end

-- Merchant item list
-- Creates a table with 

function p.merchantitems(frame)
  local title    = func.nil_if_empty(frame.args['title']) or 'Shop sell prices'
  local doGroups = func.nil_if_empty(frame.args['groups']) ~= nil

  local groups = {}

  local trueIndex = 1
  if doGroups then
    for i = 1,8 do
      -- Grab group text and size
      local text = func.nil_if_empty(frame.args["group" .. i])
      local size = func.nil_if_empty(frame.args["group" .. i .. "size"])
      if text ~= nil and size ~= nil then
        groups[trueIndex] = { text, size }
        trueIndex = trueIndex + 1
      end
    end
  end

  local cols = 2

  local result = mw.html.create('table')
    :addClass('SoG-table')
    :css('max-width', '400px')
    :attr('border', '1')
    :attr('align', 'center')

  result
    :tag('tr')
    :tag('th')
    :css('text-align', 'center')
    :attr('colspan', tostring(cols))
    :wikitext(title)
    :done()
    :done()

  if not doGroups then
  	local headers = result
      :tag('tr')
      :tag('th')
      :wikitext('Item')
      :done()
      :tag('th')
      :wikitext('Price')
      :done()
  end

  local groupCounter = 1
  local group = 1

  for i = 1,32 do
    -- Grab 2 elements
	local item = func.nil_if_empty(resolveAlias(frame.args[i]))

    if not item then break end

    item = mw.text.trim(item)
    
    -- Create group row if needed
    if doGroups then
      if group < trueIndex then
        if groupCounter == 1 then
          result
            :tag('tr')
            :tag('td')
            :attr('colspan', cols)
            :css('font-size', '1.25em')
            :css('text-align', 'center')
            :css('padding', '20px')
            :tag('span')
            :wikitext(groups[group][1])
          result
		    :tag('tr')
		    :tag('th')
		    :wikitext('Item')
		    :done()
		    :tag('th')
		    :wikitext('Price')
		    :done()
        end
        groupCounter = groupCounter + 1
        if groupCounter > tonumber(groups[group][2]) then
          groupCounter = 1
          group = group + 1
        end
      elseif groupCounter == 1 then
        groupCounter = 0
        result
	      :tag('tr')
	      :tag('td')
	      :attr('colspan', cols)
	      :css('font-size', '1.25em')
	      :css('text-align', 'center')
	      :css('padding', '20px')
          :tag('span')
          :wikitext('Other')
        result
		  :tag('tr')
		  :tag('th')
		  :wikitext('Item')
		  :done()
		  :tag('th')
		  :wikitext('Price')
		  :done()
      end
    end
    
    -- Create data row
    local datarow = result
      :tag('tr')
      :tag('td')
      :css('padding-left', '16px')
      :css('width', '100%')
      :wikitext(frame:expandTemplate{ title = 'ItemLink', args = { id = item } })
      :done()
      :tag('td')
      :css('white-space', 'nowrap')
      :css('padding-left', '10px')
      :wikitext('[[File:Interface goldicon.png|16x17px]] ')
      :wikitext(p.data({["args"] = {item, "storybuy", ["softErrors"] = true}}))
      :done()
  end
  return result
end

return p