Jump to content
Main menu
Main menu
move to sidebar
hide
Discord
PRO Wiki Discord
PRO Wiki
Main page
Rules
Recent changes
Random page
Help
Pokémon Revolution Online
About
Downloads
Events
FAQs
The Staff Team
Current Pokétime
Official PRO Resources
Website
Forums
PRO Discord
Twitter
Reddit
Instagram
YouTube
Search
Search
Appearance
Log in
Personal tools
Log in
View source for Module:Pokemon
Module
Discussion
English
Read
View source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
View source
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
←
Module:Pokemon
You do not have permission to edit this page.
You can view and copy the source of this page.
local p = {} local DATA_SOURCE = 'data' local DATA_FILE = 'PokemonRawList.csv' local DATA_FORMAT = 'CSV with header' local COLUMNS = { number = 'Pokedex Number', name = 'Name', type1 = 'Type1', type2 = 'Type2', ability1 = 'Ability 1', ability2 = 'Ability 2', ha = 'Hidden Ability', generation = 'Generation', hp = 'HP', atk = 'Attack', def = 'Defense', spatk = 'Special Attack', spdef = 'Special Defense', spd = 'Speed', catch = 'Catch Rate', evhp = 'EVHP', evatk = 'EVATK', evdef = 'EVDEF', evspa = 'EVSPA', evspd = 'EVSPD', evsp = 'EVSP', male = 'Male Ratio', height = 'Height', weight = 'Weight', npc = 'NPC', exp = 'Base Experience', rarity = 'Tier', } local GEN_NUMERALS = { 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' } local STATS = { { key = 'hp', label = 'HP', template = 'HP' }, { key = 'atk', label = 'Atk', template = 'Attack' }, { key = 'def', label = 'Def', template = 'Defense' }, { key = 'spatk', label = 'Sp.Atk', template = 'Special Attack' }, { key = 'spdef', label = 'Sp.Def', template = 'Special Defense' }, { key = 'spd', label = 'Spd', template = 'Speed' }, } local EV_KEYS = { 'evhp', 'evatk', 'evdef', 'evspa', 'evspd', 'evsp' } local DATA_SPEC do local parts = {} for key, header in pairs(COLUMNS) do parts[#parts + 1] = key .. '=' .. header end DATA_SPEC = table.concat(parts, ',') end local function nonEmpty(s) if s == nil then return nil end s = mw.text.trim(tostring(s)) if s == '' then return nil end return s end local function templateArgs(frame) local parent = frame:getParent() return (parent and parent.args) or frame.args end local function padNumber(n) n = tonumber(n) or 0 return string.format('%03d', n) end local function fetchRow(filterColumn, filterValue) local ok, values, errors = pcall(function() return mw.ext.externalData.getFileData{ source = DATA_SOURCE, ['file name'] = DATA_FILE, format = DATA_FORMAT, filters = filterColumn .. '=' .. filterValue, data = DATA_SPEC, } end) if not ok then error('Module:Pokemon: External Data call failed (' .. tostring(values) .. '). Is Legacy mode enabled (mw.ext.externalData.getFileData)?') end if not values or values.name == nil then return nil end return values end local function byName(name) return fetchRow('Name', name) end local function byNumber(number) return fetchRow('Pokedex Number', number) end local PokemonForms = require('Module:PokemonForms') local FORM_SUFFIXES = PokemonForms.FORM_SUFFIXES local STRIP_SUFFIXES = PokemonForms.STRIP_SUFFIXES local allRowsCache = nil local function loadAllRows() if allRowsCache then return allRowsCache end local csvData = mw.ext.externalData.getExternalData{ url = 'https://wiki.pokemonrevolution.net/index.php?title=Special:GetData/PokemonRawList', format = DATA_FORMAT, data = COLUMNS, } allRowsCache = {} if csvData then for _, row in ipairs(csvData) do if nonEmpty(row.name) then allRowsCache[row.name] = row end end end return allRowsCache end local function hyphenSpaceVariant(s) if mw.ustring.find(s, '-', 1, true) then return (mw.ustring.gsub(s, '%-', ' ')) end return (mw.ustring.gsub(s, ' ', '-')) end -- Page/raw-list name -> base name the form candidates are derived from -- ("Alolan Rattata" -> "Rattata", "Eiscue-Ice Face" -> "Eiscue"). local function formStem(name) for _, form in ipairs(FORM_SUFFIXES) do local head = form.label .. ' ' if mw.ustring.sub(name, 1, mw.ustring.len(head)) == head then return mw.ustring.sub(name, mw.ustring.len(head) + 1) end end for _, suffix in ipairs(STRIP_SUFFIXES) do for _, sep in ipairs({ '-', ' ' }) do local tail = sep .. suffix if mw.ustring.sub(name, -mw.ustring.len(tail)) == tail then return mw.ustring.sub(name, 1, -mw.ustring.len(tail) - 1) end end end return name end local function rawListFormName(form, stem) if form.rawList == 'suffix' then return stem .. '-' .. form.suffix elseif form.rawList == 'prefix-hyphen' then return form.label .. '-' .. stem elseif form.rawList == 'suffix-label' then return stem .. '-' .. form.label elseif form.rawList == 'suffix-space' then return stem .. ' ' .. form.label end return form.label .. ' ' .. stem end local function typeColors(frame, pokeType) return { light = frame:expandTemplate{ title = pokeType .. ' color light' }, base = frame:expandTemplate{ title = pokeType .. ' color' }, dark = frame:expandTemplate{ title = pokeType .. ' color dark' }, } end local function statColors(frame, statTemplate) return { base = frame:expandTemplate{ title = statTemplate .. ' color' }, dark = frame:expandTemplate{ title = statTemplate .. ' color dark' }, } end local function navTableHtml(frame, data, colors, pageName) local number = tonumber(data.number) local prevCell = '' if number and number > 1 then local prev = byNumber(number - 1) if prev then prevCell = ' [[File:' .. (number - 1) .. 'Icon.png|' .. prev.name .. '|link=' .. prev.name .. ']] #' .. padNumber(prev.number) end end local nextCell = '' if number then local nxt = byNumber(number + 1) if nxt then nextCell = '#' .. padNumber(nxt.number) .. ' [[File:' .. (number + 1) .. 'Icon.png|' .. nxt.name .. '|link=' .. nxt.name .. ']]' end end return '<table style="margin-left: auto; margin-right: auto; margin-bottom: 15px; height: 60px; ' .. 'border: 4px #' .. colors.dark .. ' solid; font-weight: bold; width: 65%; color: #' .. colors.dark .. '; border-radius: 20px; padding: 3px; background: #' .. colors.base .. ';">' .. '<tr>' .. '<td style="text-align: left; width: 36%; background: #' .. colors.light .. '; border-radius: 15px 1px 1px 15px; border: 1px solid #' .. colors.dark .. ';">' .. prevCell .. '</td>' .. '<td style="text-align: center; width: 28%; background: #' .. colors.light .. '; border: 1px solid #' .. colors.dark .. ';">#' .. padNumber(number) .. ' - ' .. pageName .. '</td>' .. '<td style="text-align: right; width: 36%; padding-right: 10px; background: #' .. colors.light .. '; border: 1px solid #' .. colors.dark .. '; border-radius: 1px 15px 15px 1px;">' .. nextCell .. '</td>' .. '</tr></table>' end function p.navTable(frame) local args = templateArgs(frame) local pageName = mw.title.getCurrentTitle().text local name = nonEmpty(args.Name) or pageName local data = byName(name) if not data then return '<strong class="error">PokeNavTable: no data found for "' .. name .. '"</strong>' end return navTableHtml(frame, data, typeColors(frame, data.type1), pageName) end local function infoCell(colors, labelHtml, valueHtml) return '<td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px; padding: 5px;">' .. '<p style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">' .. labelHtml .. '</p>' .. '<span style="border-radius: 20px; padding: 6px; border: 4px solid #' .. colors.dark .. '; background-color: #FFFFFF;"> ' .. valueHtml .. ' </span></td>' end local function abilityCell(colors, label, ability, widthPercent) if not ability then return '' end local style = 'border-radius: 20px; padding: 6px; border: 4px solid #' .. colors.dark .. '; background-color: #FFFFFF;' if widthPercent then style = 'width: ' .. widthPercent .. '%; ' .. style end return '<td style="' .. style .. '"><b>' .. label .. '</b><br>[[' .. ability .. '_(ability)|' .. ability .. ']]</td>' end local function abilitiesRow(colors, data) local entries = { { 'Ability 1', nonEmpty(data.ability1) }, { 'Ability 2', nonEmpty(data.ability2) }, { 'Hidden Ability', nonEmpty(data.ha) }, } local present = {} for _, entry in ipairs(entries) do if entry[2] then present[#present + 1] = entry end end local widthPercent = (#present == 2) and 50 or nil local cells = {} for _, entry in ipairs(present) do cells[#cells + 1] = abilityCell(colors, entry[1], entry[2], widthPercent) end return table.concat(cells) end local function statValueCell(frame, stat, value) local colors = statColors(frame, stat.template) return '<td style="border: 4px solid #' .. colors.dark .. '; background-color: #' .. colors.base .. '; border-radius: 15px; padding: 5px; font-size: 11px;">\'\'\'' .. stat.label .. '\'\'\'<br>' .. value .. '</td>' end local function genderRatioHtml(male) if male == 'Genderless' then return '<span style="color:#000000; background-color: #FFFFFF; border-radius: 15px; ' .. 'border: 4px solid #000000; padding: 5px;"> Genderless </span>' end -- ExternalData drops cells whose value is '0' (PHP empty('0') is true), -- so a nil/blank ratio on an otherwise-complete row means 0% male. local m = tonumber(male) if m == nil then m = nonEmpty(male) and 100 or 0 end if m >= 100 then return '<span style="color:#0000FF; border-radius: 15px; background-color: #FFFFFF; padding: 5px; ' .. 'border: 4px solid #0000FF;"> 100% </span>' elseif m <= 0 then return '<span style="color:#FF6060; background-color: #FFFFFF; border-radius: 15px; ' .. 'border: 4px solid #FF6060; padding: 5px;"> 100% </span>' else return '<span style="color:#0000FF; border-radius: 15px; background-color: #FFFFFF; padding: 5px; ' .. 'border: 4px solid #0000FF;"> ' .. m .. '% </span>' .. '<span style="color:#FF6060; background-color: #FFFFFF; border-radius: 15px; ' .. 'border: 4px solid #FF6060; padding: 5px;"> ' .. (100 - m) .. '% </span>' end end local function categories(data, gen, bstCategory, args) local cats = { '[[Category:Pokémon species]]', '[[Category:Generation ' .. gen .. ' Pokémon]]', '[[Category:' .. data.type1 .. '-type Pokémon]]', } if nonEmpty(data.type2) then cats[#cats + 1] = '[[Category:' .. data.type2 .. '-type Pokémon]]' end cats[#cats + 1] = '[[Category:' .. data.rarity .. ' Pokémon]]' if nonEmpty(data.npc) then cats[#cats + 1] = '[[Category:NPC-obtainable Pokémon]]' end if not nonEmpty(args.EvolvesFrom) then cats[#cats + 1] = '[[Category:Base-stage Pokémon]]' end cats[#cats + 1] = '[[Category:' .. bstCategory .. '-plus-BST Pokémon]]' return table.concat(cats, ' ') end local function infoboxHtml(frame, args, data, displayName, width, pageName, includeNav) local colors = typeColors(frame, data.type1) local gen = GEN_NUMERALS[tonumber(data.generation)] -- Types pill(s). local typesHtml if nonEmpty(data.type2) then local type2Colors = typeColors(frame, data.type2) typesHtml = '<td style="width: 50%; background-color: #' .. colors.dark .. '; border-radius: 25px 1px 1px 25px;">' .. '[[' .. data.type1 .. '_(type)|<span style="color: #FFFFFF;">' .. data.type1 .. '</span>]]</td>' .. '<td style="width: 50%; background-color: #' .. type2Colors.dark .. '; border-radius: 1px 25px 25px 1px;">' .. '[[' .. data.type2 .. '_(type)|<span style="color: #FFFFFF;">' .. data.type2 .. '</span>]]</td>' else typesHtml = '<td style="width:100%; background-color: #' .. colors.dark .. '; border-radius: 25px;">' .. '[[' .. data.type1 .. '_(type)|<span style="color: #FFFFFF;">' .. data.type1 .. '</span>]]</td>' end -- Base stats local statCells, bst = {}, 0 for _, stat in ipairs(STATS) do local value = tonumber(data[stat.key]) or 0 bst = bst + value statCells[#statCells + 1] = statValueCell(frame, stat, value) end local bstCategory = math.floor(bst / 100 + 0.5) * 100 -- EV yield. local evCells, evTotal = {}, 0 for i, stat in ipairs(STATS) do local ev = tonumber(data[EV_KEYS[i]]) or 0 evTotal = evTotal + ev evCells[#evCells + 1] = statValueCell(frame, stat, ev) end -- Catch rate. local catchRaw = tonumber(data.catch) or 0 local catchPercent = string.format('%.2f', catchRaw / 255 / 3 * 100) local buf = {} if includeNav then buf[#buf + 1] = navTableHtml(frame, data, colors, pageName) end buf[#buf + 1] = '<table align="right" class="informational-box" cellpadding="4" cellspacing="4" ' .. 'style="width: ' .. width .. '%; border: 4px solid #' .. colors.dark .. '; background-color: #' .. colors.base .. '; padding: 4px; border-radius: 15px;">' -- Header: dex number + icon, name. buf[#buf + 1] = '<tr><td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px;"><span style="margin-bottom: 7px; font-weight: bold; color:#' .. colors.dark .. ';">#' .. padNumber(data.number) .. '</span> [[File:' .. data.number .. 'Icon.png]]</td><td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px;"><span style="margin-bottom: 7px; font-weight: bold; color:#' .. colors.dark .. ';">' .. displayName .. '</span></td></tr>' -- Artwork. buf[#buf + 1] = '<tr><td colspan="3" style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px; padding: 5px;"><div style="border-radius: 15px; ' .. 'text-align: center; padding: 5px; background-color: #FFFFFF;">[[File:' .. data.number .. data.name .. '.png|150px]]</div></td></tr>' -- Types + Generation. buf[#buf + 1] = '<tr><td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; padding: 11px; border-radius: 15px;"><p style="margin-bottom: 7px; font-weight: bold; ' .. 'color: #' .. colors.dark .. ';">Types</p><table align="center" style="width: 75%; font-weight: bold; ' .. 'padding: 3px;"><tr>' .. typesHtml .. '</tr></table></td>' .. '<td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px;"><p style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">Generation</p><span style="border-radius: 20px; padding: 6px; border: 4px solid #' .. colors.dark .. '; background-color: #FFFFFF;"> [[:Category:Generation ' .. gen .. ' Pokémon|<span style="color:#' .. colors.dark .. ';">' .. gen .. '</span>]] </span></td></tr>' -- Abilities. buf[#buf + 1] = '<tr><td colspan="6" style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; padding: 11px; border-radius: 15px; width: 100%;"><table width="100%">' .. '<tr><th colspan="6"><p style="margin-bottom: 7px; font-weight: bold; color:#' .. colors.dark .. ';">[[Abilities|<span style="color:#' .. colors.dark .. ';">Abilities</span>]]</p></th></tr>' .. '<tr>' .. abilitiesRow(colors, data) .. '</tr></table></td></tr>' -- Rarity + Catch rate. buf[#buf + 1] = '<tr>' .. infoCell(colors, '[[List of Pokémon by Rarity Tier|<span style="color:#' .. colors.dark .. ';">Rarity Tier</span>]]', '[[:Category:' .. data.rarity .. ' Pokémon|<span style="color:#' .. colors.dark .. ';">' .. data.rarity .. '</span>]]') .. infoCell(colors, '[[Catch Rates|<span style="color:#' .. colors.dark .. ';">Catch rate</span>]]', data.catch .. '/255 (' .. catchPercent .. '%)') .. '</tr>' -- Base stats. buf[#buf + 1] = '<tr><td colspan="6" style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px; padding: 5px; width: 100%"><table width="100%">' .. '<tr><th colspan="6"><p style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">[[Base stats|<span style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">Base stats</span>]]</p></th></tr><tr>' .. table.concat(statCells) .. '</tr>' .. '<tr><td colspan="6"><div style="margin: auto; background-color:#FFFFFF; border: 3px solid #' .. colors.dark .. '; padding: 6px; border-radius: 25px; margin-bottom: 5px; width: 50%;">' .. 'Base-stat total: ' .. bst .. '</div></td></tr></table></td></tr>' -- EV yield. buf[#buf + 1] = '<tr><td colspan="6" style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px; padding: 5px; width: 100%"><table width="100%">' .. '<tr><th colspan="6">[[EVs|<span style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">EV Yield</span>]]</th></tr><tr>' .. table.concat(evCells) .. '</tr>' .. '<tr><td colspan="6"><div style="margin: auto; background-color:#FFFFFF; border: 3px solid #' .. colors.dark .. '; padding: 6px; border-radius: 25px; margin-bottom: 5px; width: 50%;">' .. 'EV-yield total: ' .. evTotal .. '</div></td></tr></table></td></tr>' -- Base experience + Gender ratio. buf[#buf + 1] = '<tr>' .. infoCell(colors, '[[Experience System#Base Experience|<span style="color:#' .. colors.dark .. ';">Base Experience</span>]]', data.exp) .. '<td style="border: 3px solid #' .. colors.dark .. '; background-color: #' .. colors.light .. '; border-radius: 15px; padding: 5px;"><p style="margin-bottom: 7px; font-weight: bold; color: #' .. colors.dark .. ';">Gender Ratio</p> ' .. genderRatioHtml(data.male) .. ' </td>' .. '</tr>' -- Height + Weight. buf[#buf + 1] = '<tr>' .. infoCell(colors, 'Height', data.height .. 'm') .. infoCell(colors, 'Weight', data.weight .. 'kg') .. '</tr>' buf[#buf + 1] = '</table>' buf[#buf + 1] = categories(data, gen, bstCategory, args) return table.concat(buf) end function p.infobox(frame) local args = templateArgs(frame) local pageName = mw.title.getCurrentTitle().text local name = nonEmpty(args.Name) or pageName local width = nonEmpty(args.Width) or '45' local data = byName(name) if not data then return '<strong class="error">PokemonInfo: no data found for "' .. name .. '"</strong>' end return infoboxHtml(frame, args, data, name, width, pageName, true) end -- Like infobox, but detects alternate forms via Module:PokemonForms and, when -- any exist, renders one nav table plus a {{TabbedInfoBoxes}} block with one -- infobox per form. Falls back to the plain single infobox otherwise. function p.infoboxes(frame) local args = templateArgs(frame) local pageName = mw.title.getCurrentTitle().text local name = nonEmpty(args.Name) or pageName local rows = loadAllRows() local function rowFor(n) return rows[n] or rows[hyphenSpaceVariant(n)] end local stem = formStem(name) local baseData = rowFor(stem) or rowFor(name) if not baseData then return '<strong class="error">PokemonInfo: no data found for "' .. name .. '"</strong>' end local baseName = baseData.name local forms = {} local seen = { [baseName] = true } for _, form in ipairs(FORM_SUFFIXES) do local row = rowFor(rawListFormName(form, stem)) if row and not seen[row.name] then seen[row.name] = true forms[#forms + 1] = { label = form.label, data = row, baseLabel = form.baseLabel } end end if #forms == 0 then local width = nonEmpty(args.Width) or '45' return infoboxHtml(frame, args, baseData, baseName, width, pageName, true) end local baseLabel = 'Normal' for _, f in ipairs(forms) do if f.baseLabel then baseLabel = f.baseLabel break end end local tabWidth = nonEmpty(args.Width) or '100' local tabHeaders = '{{Tabs:MultiformTab|Form=' .. baseLabel .. '}}' local infoBoxes = '{{Tabs:MultiformTable|Table=' .. infoboxHtml(frame, args, baseData, baseName, tabWidth, pageName, false) .. '}}' for _, f in ipairs(forms) do tabHeaders = tabHeaders .. '{{Tabs:MultiformTab|Form=' .. f.label .. '}}' infoBoxes = infoBoxes .. '{{Tabs:MultiformTable|Form=' .. f.label .. '|Table=' .. infoboxHtml(frame, args, f.data, f.data.name, tabWidth, pageName, false) .. '}}' end return navTableHtml(frame, baseData, typeColors(frame, baseData.type1), pageName) .. frame:preprocess('{{TabbedInfoBoxes|TabHeaders=' .. tabHeaders .. '|InfoBoxes=\n' .. infoBoxes .. '\n}}') end return p
Page included on this page:
Module:Pokemon/doc
(
view source
)
Return to
Module:Pokemon
.
Search
Search
View source for Module:Pokemon
Add topic