Module:Enemy

From Secrets of Grindea Wiki
Jump to navigation Jump to search

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

local p = {}

-- Helper functions

local function has_value(tab, val)
  if tab == nil then
    return false
  end
  for index, value in pairs(tab) do
    if value == val then
      return true
    end
  end
  return false
end

local function has_key(tab, val)
  if tab == nil then
    return false
  end
  for index, value in pairs(tab) do
    if value == val then
      return true
    end
  end
  return false
end

-- End

-- Module will signal an error if data is requested for fields other than these
local functionStats = {"HP", "ATK", "DEF", "MSPD"}
local staticStats = {"name", "card", "drops", "level", "enemytype", "elitetype", "sprite"}
local cardDetails = {"sprite", "description", "dropchance"}
local functionFormat = {"none", "eliteWrap"}

local table = mw.loadData('Module:EnemyStats/data')

function p.data(frame)
  local enemy = frame.args[1]
  local stat = frame.args[2]

  if not(table) then
    return "Fatal Error: enemyStats is nil"
  end

  if not(enemy and stat) then
    return "Error: Invalid enemy / stat parameters"
  end

  -- If true, stat needs to be calculated from function table
  local bCall = has_value(functionStats, stat)

  if bCall then
  ---- Call respective function and determine stats based on difficulty and elite status---------
    local functions = require('Module:EnemyFunctionTable')

    -- Nil checks for validation
    if not functions then
      return "Fatal Error: enemyFunctionsTable is nil"
    end
    if not functions[enemy] then
      return "Error: nil entry for " .. enemy .. " in enemyFunctionStats"
    end
    if not functions[enemy][stat] then
      -- Hide stat
      return ""
    end
    
    local diff = tonumber(frame.args[3])
    local elite = tonumber(frame.args[4])
    if not(diff and elite) then
      return "Error: Invalid diff / elite parameters"
    end

    -- Return an empty string if the enemy doesn't have HasElite tag and elite = 1
    -- (AKA can't be elite, or is "empowered" instead and doesn't benefit from elite
    -- stat changes and player count HP scaling)
    if elite == 1 and not(has_value(table[enemy]["tags"], "HasElite")) then
      return ""
    end

    -- Get calculated value
    local returnValue = functions[enemy][stat](diff, elite)

    -- Do additonal processing based on stat
    if stat == "MSPD" then
      returnValue = (returnValue * 100) .. "%"
    elseif stat == "HP" then
      local ratio = 0.1

      if has_value(table[enemy]["tags"], "Boss") then 
        ratio = 0.35
      elseif has_value(table[enemy]["tags"], "MiniBoss") then 
        ratio = 0.3 
      end

      returnValue = math.floor(returnValue + 0.5) * (1 + ratio * diff)
    end

    -- Return result or blank text if somehow ended up with a nil value (unimplemented stat?)
    -- Result is rounded (unless it's not a number anymore)
    if stat ~= "MSPD" then
      returnValue = math.floor(returnValue + 0.5)
    end

    -- Check for format param
    local format = frame.args[5] or "none"

    if not has_value(functionFormat, format) then
      format = "none"
    end

    -- Do format (unless returnValue is nil for some reason, just in case)
    if returnValue ~= nil then
      if format == "eliteWrap" and elite == 1 then
        returnValue = "  <b>( " .. returnValue .. " )</b>"
      end
    end

    return returnValue or ""
  else
  --------- Grab information from stats table ---------------------------------------------------
    -- Integrity check (nil card and drop stats is acceptable)
    if not table[enemy] then
      return "Error: nil entry for " .. enemy .. " in enemyStats"
    end

    if not has_value(staticStats, stat) then
      return "Error: Requested data for invalid stat " .. stat
    end

    -- Get information
    local returnValue = table[enemy][stat]

    -- Do additional processing based on stat
    if stat == "card" then
      if returnValue == nil then
        -- Hide stat if card section is nil
        return ""
      end

      -- Grab third parameter
      local detail = frame.args[3]

      if not detail then
        return "Error: Invalid detail parameter"
      end
      if not has_value(cardDetails, detail) then
        return "Error: Requested data for invalid detail " .. detail
      end

      -- Process aliases (in particular, card drop chance)
      if detail == "dropchance" then
        returnValue = returnValue["dropchanceinverse"]
      else
        returnValue = returnValue[detail]
      end

      -- Nil check: if card section is not nil, details must have data
      if not returnValue then
        if stat == "sprite" then
          -- Effectively an error image
          return "Placeholder.png"
        else
          return "Error: nil value for " .. enemy .. ":card:" .. detail
        end
      end

      -- Do additional processing based on detail
      if detail == "dropchance" then
        returnValue = 100.0 / returnValue
        returnValue = math.floor(returnValue * 1000) / 1000
        returnValue = returnValue .. "%"
      end

      -- Result goes out of if chain
    elseif stat == "drops" then
      if returnValue == nil then
        -- Hide stat if drop section is nil
        return ""
      end

      -- This stat is processed into the required wikitext
      -- value[1] = item ID, value[2] = drop chance, value[3] = (optional) duplicate entry count 
      -- N duplicate entries means that the game rolls for another N-1 items of the same type with the same chance

      -- Read each entry and create an ItemLink with data to the right, inside a list
      local wikitext = ""
      for index, value in ipairs(returnValue) do
        -- Appends list wikitext and ItemLink
        wikitext = wikitext .. "* " .. frame:expandTemplate{title = "ItemLink", args = {id = value[1]}}

        -- Appends (chance%), optionally (N items rolled), then a newline
        wikitext = wikitext .. " (" .. value[2] .. "%)"
        if value[3] ~= nil then
          wikitext = wikitext .. " (" .. value[3] .. " items rolled)"
        end
        wikitext = wikitext .. "\n"
      end
      
      -- Swap reference by the power of the holy garbage collector
      returnValue = wikitext
    elseif stat == "enemytype" then
      returnValue = returnValue or "Regular"
      if has_value(table[enemy]["tags"], "Boss") then
        returnValue = "Boss"
      elseif has_value(table[enemy]["tags"], "MiniBoss") then
        returnValue = "Miniboss"
      end
    elseif stat == "elitetype" then
      returnValue = "No"
      if has_value(table[enemy]["tags"], "HasElite") then
        returnValue = "Yes"
      elseif has_value(table[enemy]["tags"], "HasPseudoElite") then
        returnValue = "No (but is Empowered)"
      end
    end

    -- Return result or blank text if somehow ended up with a nil value (unimplemented stat?)
    return returnValue or ""
  end
  -----------------------------------------------------------------------------------------------

  -- Error message in case I suck at coding
  return "Error: Module exited processing code"
 
end

return p