Mudanças entre as edições de "Módulo:Gb"

De Wiki Gla
Ir para navegação Ir para pesquisar
 
(46 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
--[[
Module:CalculadoraTier
Calculadora de recursos (berry e fragmentos) para evoluir um personagem por Tiers e Estrelas.
✔ Como usar na wiki (Wikitext):
  {{#invoke:CalculadoraTier|calc
    |deTier=bronze
    |deEstrelas=2
    |paraTier=diamante
    |paraEstrelas=4
  }}
Parâmetros opcionais:
  |moeda=berry            -- nome exibido para a moeda (default: "berry")
  |frag=fragmentos        -- nome exibido para os fragmentos (default: "fragmentos")
  |mostrarPassos=sim      -- mostra o passo a passo (sim/não)
📌 Importante: SUBSTITUA os valores de custo abaixo (tabela DATA.custos) pelos valores reais do seu jogo.
A estrutura já está pronta para você só trocar os números.
]]
local p = {}
local p = {}


-- =======================
function p.main(frame)
-- CONFIGURAÇÃO DE DADOS
     local args = frame:getParent().args
-- =======================
-- Nome dos tiers em ordem de progressão.
-- Use chaves em minúsculas, sem acento, para evitar problemas.
local DATA = {
  tiers = { "bronze", "prata", "gold", "diamante" },
 
  -- Custo para subir 1 estrela DENTRO do mesmo tier.
  -- Para cada tier (chave), defina uma lista de 5 valores que representam o custo para ir:
  --  0 ➜ 1, 1 ➜ 2, 2 ➜ 3, 3 ➜ 4, 4 ➜ 5 estrelas.
  -- Exemplo abaixo usa NÚMEROS DE PLACEHOLDER! Troque pelos números reais.
  custos = {
     bronze = {
      estrelas = {
        { berry = 100, frag = 2 },  -- 0->1
        { berry = 150, frag = 3 },  -- 1->2
        { berry = 200, frag = 4 },  -- 2->3
        { berry = 250, frag = 5 },  -- 3->4
        { berry = 300, frag = 6 },  -- 4->5
      },
      -- Custo para PROMOVER do tier atual para o próximo (depois de 5★).
      promover = { berry = 500, frag = 10 },
    },
 
    prata = {
      estrelas = {
        { berry = 400, frag = 4 },
        { berry = 500, frag = 5 },
        { berry = 600, frag = 6 },
        { berry = 700, frag = 7 },
        { berry = 800, frag = 8 },
      },
      promover = { berry = 1200, frag = 16 },
    },
 
    gold = {
      estrelas = {
        { berry = 900,  frag = 9 },
        { berry = 1100, frag = 10 },
        { berry = 1300, frag = 11 },
        { berry = 1500, frag = 12 },
        { berry = 1700, frag = 13 },
      },
      promover = { berry = 2500, frag = 24 },
    },
 
    diamante = {
      estrelas = {
        { berry = 2000, frag = 15 },
        { berry = 2300, frag = 16 },
        { berry = 2600, frag = 17 },
        { berry = 2900, frag = 18 },
        { berry = 3200, frag = 19 },
      },
      -- Se diamante for o último tier, manter promover = nil ou {}.
      promover = nil,
    },
  },
}
 
-- =======================
-- FUNÇÕES AUXILIARES
-- =======================
 
local function norm(s)
  if type(s) ~= "string" then return s end
  s = s:lower()
  s = s:gsub("[áàâã]","a"):gsub("[éê]","e"):gsub("[í]","i"):gsub("[óôõ]","o"):gsub("[ú]","u"):gsub("ç","c")
  return s
end
 
local function findTierIndex(tier)
  local t = norm(tier)
  for i, name in ipairs(DATA.tiers) do
    if norm(name) == t then return i end
  end
  return nil
end
 
local function validateInputs(args)
  local deTier = args.deTier or args.de or args.fromTier
  local paraTier = args.paraTier or args.para or args.toTier
  local deEstrelas = tonumber(args.deEstrelas or args.estrelas or args.fromStars) or 0
  local paraEstrelas = tonumber(args.paraEstrelas or args.toStars) or 0
 
  if not deTier or not paraTier then
    return nil, "Informe os parâmetros deTier e paraTier."
  end
 
  local deIdx = findTierIndex(deTier)
  local paraIdx = findTierIndex(paraTier)
  if not deIdx then return nil, "Tier inicial inválido: " .. tostring(deTier) end
  if not paraIdx then return nil, "Tier de destino inválido: " .. tostring(paraTier) end
 
  if deEstrelas < 0 or deEstrelas > 5 then
    return nil, "deEstrelas deve estar entre 0 e 5."
  end
  if paraEstrelas < 0 or paraEstrelas > 5 then
    return nil, "paraEstrelas deve estar entre 0 e 5."
  end
 
  -- Também não permitir alvo "antes" da origem.
  if paraIdx < deIdx or (paraIdx == deIdx and paraEstrelas < deEstrelas) then
    return nil, "O destino deve ser maior que a origem."
  end
 
  return {
    deIdx = deIdx,
    paraIdx = paraIdx,
    deTier = DATA.tiers[deIdx],
    paraTier = DATA.tiers[paraIdx],
    deEstrelas = deEstrelas,
    paraEstrelas = paraEstrelas,
    moeda = args.moeda or "berry",
    frag = args.frag or "fragmentos",
    mostrarPassos = norm(tostring(args.mostrarPassos or "nao")) == "sim",
  }
end
 
-- Soma custos de estrelas dentro de um tier, do nível deEstrelas até (exclusivo) alvoEstrelas.
local function somarEstrelas(tierKey, deEstrelas, alvoEstrelas)
  local cust = DATA.custos[tierKey]
  local total = { berry = 0, frag = 0 }
  local passos = {}
  if not cust or not cust.estrelas then return total, passos end
  for s = deEstrelas, alvoEstrelas - 1 do
    local step = cust.estrelas[s + 1] -- +1 porque tabela começa no 1
    if step then
      total.berry = total.berry + (step.berry or 0)
      total.frag  = total.frag  + (step.frag or 0)
      table.insert(passos, { tipo = "estrela", from = s, to = s + 1, tier = tierKey, berry = step.berry or 0, frag = step.frag or 0 })
    end
  end
  return total, passos
end
 
-- =======================
-- LÓGICA PRINCIPAL
-- =======================
 
local function calcular(args)
  local ok, errOrCfg = pcall(validateInputs, args)
  if not ok then return nil, errOrCfg end
  if not errOrCfg then return nil, "Parâmetros inválidos." end
  if type(errOrCfg) == "string" then return nil, errOrCfg end
  local cfg = errOrCfg
 
  local total = { berry = 0, frag = 0 }
  local passos = {}
 
  for idx = cfg.deIdx, cfg.paraIdx do
    local tierKey = DATA.tiers[idx]
 
    -- Definir início e fim das estrelas para este tier
    local startStar = (idx == cfg.deIdx) and cfg.deEstrelas or 0
    local endStar  = (idx == cfg.paraIdx) and cfg.paraEstrelas or 5
 
    -- Somar estrelas do tier atual
    local parc, pss = somarEstrelas(tierKey, startStar, endStar)
    total.berry = total.berry + parc.berry
    total.frag  = total.frag  + parc.frag
    for _, x in ipairs(pss) do table.insert(passos, x) end
 
    -- Se não for o último tier de destino e atingiu 5 estrelas, pagar promoção
    if endStar == 5 and idx < cfg.paraIdx then
      local promo = (DATA.custos[tierKey] and DATA.custos[tierKey].promover) or { berry = 0, frag = 0 }
      total.berry = total.berry + (promo.berry or 0)
      total.frag  = total.frag  + (promo.frag or 0)
      table.insert(passos, { tipo = "promocao", tier = tierKey, para = DATA.tiers[idx + 1], berry = promo.berry or 0, frag = promo.frag or 0 })
    end
  end
 
  return { total = total, passos = passos, cfg = cfg }
end


-- =======================
    local normal_nivel    = mw.text.trim(args['normal_nivel']    or '140')
-- RENDERIZAÇÃO (HTML simples / Wikitext)
    local normal_equip    = mw.text.trim(args['normal_equip']    or 'Set +8')
-- =======================
    local hard_personagem = mw.text.trim(args['hard_personagem'] or 'personagem 4 ou 5 estrelas (Diamante)')
    local hard_equip      = mw.text.trim(args['hard_equip']      or 'Set +16')


local function formatNumero(n)
    local root = mw.html.create('div')
  -- separador de milhar simples
     root:addClass('gb-wrap')
  local s = tostring(math.floor(n or 0))
  local k
  while true do
     s, k = s:gsub("^(%d+)(%d%d%d)", "%1.%2")
    if k == 0 then break end
  end
  return s
end


local function renderResultado(res)
    -- CSS via TemplateStyles (recomendado) ou inline
  local moeda = res.cfg.moeda
    -- Botões (tabs)
  local fragN = res.cfg.frag
    local btns = root:tag('div'):addClass('gb-btns')


  local linhas = {}
    btns:tag('button')
  table.insert(linhas, "{| class=\"wikitable\" style=\"width:420px\"\n! colspan=2 | Totais\n|-")
        :addClass('gb-btn'):addClass('active')
  table.insert(linhas, string.format("| %s || %s\n|-", moeda, formatNumero(res.total.berry)))
        :attr('onclick', "gbSwitch('n',this)")
  table.insert(linhas, string.format("| %s || %s\n|}", fragN, formatNumero(res.total.frag)))
        :wikitext('Normal ')
        :tag('span'):addClass('gb-badge'):addClass('badge-n')
        :wikitext('Nível ' .. normal_nivel)


  if res.cfg.mostrarPassos and #res.passos > 0 then
     btns:tag('button')
     table.insert(linhas, "\n\n{| class=\"wikitable\" style=\"width:100%%\"\n! Passo !! Detalhe !! " .. moeda .. " !! " .. fragN .. "\n|-")
        :addClass('gb-btn')
    for _, pss in ipairs(res.passos) do
         :attr('onclick', "gbSwitch('h',this)")
      if pss.tipo == "estrela" then
        :wikitext('Hard ')
         table.insert(linhas, string.format("| Estrela || %s: %d★ ➜ %d★ || %s || %s\n|-",
         :tag('span'):addClass('gb-badge'):addClass('badge-h')
          pss.tier, pss.from, pss.to, formatNumero(pss.berry), formatNumero(pss.frag)))
        :wikitext(hard_equip)
      else
         table.insert(linhas, string.format("| Promoção || %s ➜ %s || %s || %s\n|-",
          pss.tier, pss.para, formatNumero(pss.berry), formatNumero(pss.frag)))
      end
    end
    table.insert(linhas, "|}")
  end


  return table.concat(linhas, "\n")
    -- Painel Normal
end
    local pn = root:tag('div'):addClass('gb-panel'):addClass('active'):attr('id', 'gbpanel-n')
    pn:tag('div'):addClass('gb-title'):wikitext('Requisitos — Normal')
    local ln = pn:tag('ul'):addClass('gb-list')
    ln:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-n'):done()
        :tag('span'):wikitext('Nível mínimo: '):tag('b'):wikitext(normal_nivel)
    ln:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-n'):done()
        :tag('span'):wikitext('Equipamento recomendado: '):tag('b'):wikitext(normal_equip)


-- =======================
    -- Painel Hard
-- ENTRADA PÚBLICA (Scribunto)
    local ph = root:tag('div'):addClass('gb-panel'):attr('id', 'gbpanel-h')
-- =======================
    ph:tag('div'):addClass('gb-title'):wikitext('Requisitos — Hard')
    local lh = ph:tag('ul'):addClass('gb-list')
    lh:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-h'):done()
        :tag('span'):wikitext('Recomendado utilizar um '):tag('b'):wikitext(hard_personagem)
    lh:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-h'):done()
        :tag('span'):wikitext('Equipamento recomendado: '):tag('b'):wikitext(hard_equip)


function p.calc(frame)
    -- Script
  local args = frame.args
    local script = mw.html.create('script')
  -- permitir também Template: wrapper com parent
    script:wikitext([[
  if frame:getParent() then
function gbSwitch(tab,btn){
    for k, v in pairs(frame:getParent().args) do
['n','h'].forEach(function(t){document.getElementById('gbpanel-'+t).classList.remove('active');});
      if v ~= '' and args[k] == nil then args[k] = v end
document.querySelectorAll('.gb-btn').forEach(function(b){b.classList.remove('active');});
    end
document.getElementById('gbpanel-'+tab).classList.add('active');
  end
btn.classList.add('active');
}]])


  local res, err = calcular(args)
    -- CSS inline
  if not res then
    local style = mw.html.create('style')
    return string.format("'''Erro:''' %s", err)
    style:wikitext([[
  end
.gb-wrap{font-family:sans-serif;max-width:600px}
  return renderResultado(res)
.gb-btns{display:flex;border-bottom:2px solid #a2a9b1;margin-bottom:0}
end
.gb-btn{padding:8px 22px;font-size:14px;font-weight:bold;color:#54595d;background:transparent;border:none;border-bottom:3px solid transparent;margin-bottom:-2px;cursor:pointer;display:flex;align-items:center;gap:6px}
.gb-btn:hover{color:#202122}
.gb-btn.active{color:#202122;border-bottom-color:#3680b0}
.gb-badge{font-size:11px;padding:2px 8px;border-radius:20px;font-weight:bold}
.badge-n{background:#ddeeff;color:#185FA5}
.badge-h{background:#fce8e8;color:#a32d2d}
.gb-panel{display:none;padding:14px 2px}
.gb-panel.active{display:block}
.gb-title{font-size:11px;font-weight:bold;color:#72777d;text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}
.gb-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px}
.gb-list li{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:#202122;line-height:1.5}
.gb-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-top:5px}
.dot-n{background:#3680b0}
.dot-h{background:#e24b4a}
@media(max-width:480px){.gb-btn{padding:8px 14px;font-size:13px}}]])


-- =======================
    return tostring(style) .. tostring(root) .. tostring(script)
-- UTIL: DEPURAÇÃO VIA CONSOLE (opcional)
-- =======================
-- Você pode rodar no console do Scribunto para testar:
-- =p._test{deTier="bronze", deEstrelas=2, paraTier="diamante", paraEstrelas=4, mostrarPassos="sim"}
function p._test(args)
  local res, err = calcular(args)
  if not res then return "Erro: " .. err end
  return renderResultado(res)
end
end


return p
return p

Edição atual tal como às 04h47min de 17 de março de 2026

A documentação para este módulo pode ser criada em Módulo:Gb/doc

local p = {}

function p.main(frame)
    local args = frame:getParent().args

    local normal_nivel    = mw.text.trim(args['normal_nivel']    or '140')
    local normal_equip    = mw.text.trim(args['normal_equip']    or 'Set +8')
    local hard_personagem = mw.text.trim(args['hard_personagem'] or 'personagem 4 ou 5 estrelas (Diamante)')
    local hard_equip      = mw.text.trim(args['hard_equip']      or 'Set +16')

    local root = mw.html.create('div')
    root:addClass('gb-wrap')

    -- CSS via TemplateStyles (recomendado) ou inline
    -- Botões (tabs)
    local btns = root:tag('div'):addClass('gb-btns')

    btns:tag('button')
        :addClass('gb-btn'):addClass('active')
        :attr('onclick', "gbSwitch('n',this)")
        :wikitext('Normal ')
        :tag('span'):addClass('gb-badge'):addClass('badge-n')
        :wikitext('Nível ' .. normal_nivel)

    btns:tag('button')
        :addClass('gb-btn')
        :attr('onclick', "gbSwitch('h',this)")
        :wikitext('Hard ')
        :tag('span'):addClass('gb-badge'):addClass('badge-h')
        :wikitext(hard_equip)

    -- Painel Normal
    local pn = root:tag('div'):addClass('gb-panel'):addClass('active'):attr('id', 'gbpanel-n')
    pn:tag('div'):addClass('gb-title'):wikitext('Requisitos — Normal')
    local ln = pn:tag('ul'):addClass('gb-list')
    ln:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-n'):done()
        :tag('span'):wikitext('Nível mínimo: '):tag('b'):wikitext(normal_nivel)
    ln:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-n'):done()
        :tag('span'):wikitext('Equipamento recomendado: '):tag('b'):wikitext(normal_equip)

    -- Painel Hard
    local ph = root:tag('div'):addClass('gb-panel'):attr('id', 'gbpanel-h')
    ph:tag('div'):addClass('gb-title'):wikitext('Requisitos — Hard')
    local lh = ph:tag('ul'):addClass('gb-list')
    lh:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-h'):done()
        :tag('span'):wikitext('Recomendado utilizar um '):tag('b'):wikitext(hard_personagem)
    lh:tag('li')
        :tag('span'):addClass('gb-dot'):addClass('dot-h'):done()
        :tag('span'):wikitext('Equipamento recomendado: '):tag('b'):wikitext(hard_equip)

    -- Script
    local script = mw.html.create('script')
    script:wikitext([[
function gbSwitch(tab,btn){
['n','h'].forEach(function(t){document.getElementById('gbpanel-'+t).classList.remove('active');});
document.querySelectorAll('.gb-btn').forEach(function(b){b.classList.remove('active');});
document.getElementById('gbpanel-'+tab).classList.add('active');
btn.classList.add('active');
}]])

    -- CSS inline
    local style = mw.html.create('style')
    style:wikitext([[
.gb-wrap{font-family:sans-serif;max-width:600px}
.gb-btns{display:flex;border-bottom:2px solid #a2a9b1;margin-bottom:0}
.gb-btn{padding:8px 22px;font-size:14px;font-weight:bold;color:#54595d;background:transparent;border:none;border-bottom:3px solid transparent;margin-bottom:-2px;cursor:pointer;display:flex;align-items:center;gap:6px}
.gb-btn:hover{color:#202122}
.gb-btn.active{color:#202122;border-bottom-color:#3680b0}
.gb-badge{font-size:11px;padding:2px 8px;border-radius:20px;font-weight:bold}
.badge-n{background:#ddeeff;color:#185FA5}
.badge-h{background:#fce8e8;color:#a32d2d}
.gb-panel{display:none;padding:14px 2px}
.gb-panel.active{display:block}
.gb-title{font-size:11px;font-weight:bold;color:#72777d;text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}
.gb-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px}
.gb-list li{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:#202122;line-height:1.5}
.gb-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-top:5px}
.dot-n{background:#3680b0}
.dot-h{background:#e24b4a}
@media(max-width:480px){.gb-btn{padding:8px 14px;font-size:13px}}]])

    return tostring(style) .. tostring(root) .. tostring(script)
end

return p