モジュール:Wand
This documentation is transcluded from Module:Wand/doc (edit).
This is a template for display of wand builds, and/or detailed information about wand stats. This new template replaces the {{Wand}}
and {{Wand Card}}
templates, duplicating and extending their functionality. The template includes a link to view and edit the wand on the Wand Simulator site, which can also export back to this template format for inclusion into wiki articles.
Spells are now specified using their ID, rather than name, which simplifies exporting/importing Wand builds to mods and other tools.
Some useful resources:
- List of spell names and ids: Special:CargoTables/Spells
- List of wand icons: Category:Wand icons
- List of legacy Wand templates (to be converted): Category:Pages with wand templates
{{SpellName}}
- map spell IDs to names
{{SpellTypeClass}}
{{SpellTypeColour}}
- spell type classes and CSS variables for styling
{{SpellCategory}}
- spell groupings based on tags
This demonstrates leaving gaps in the wand's spell listing. The spells can be formatted any way you want - whitespace is ignored.
{{Wand2
| wandPic = Wand 0413.png
| tooltips = Yes
| capacity = 16
| shuffle = No
| spellsCast = 1
| alwaysCasts = HOMING
| spells = MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,,,
RECHARGE,RECHARGE,RECHARGE,RECHARGE,,,
DIVIDE_10,DAMAGE,BUCKSHOT
}}
This demonstrates leaving gaps in the wand's spell listing. The spells can be formatted any way you want - whitespace is ignored.
{{Wand2
| wandPic = Wand 0413.png
| vertical = Yes
| tooltips = Yes
| capacity = 16
| shuffle = No
| spellsCast = 1
| alwaysCasts = HOMING
| spells = MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,,,
RECHARGE,RECHARGE,RECHARGE,RECHARGE,,,
DIVIDE_10,DAMAGE,BUCKSHOT
}}
Also shows use of multiple always cast spells (up to 4 are allowed).
{{Wand2
| vertical = No
| wandCard = Yes
| hideLink = No
| tooltips = No
| wandName = Second Wand of Examples
| wandPic = Wand 0413.png
| shuffle = No
| spellsCast = 2
| alwaysCasts = INFESTATION,HOMING,LASER,DAMAGE
| spells = MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
RECHARGE,
RECHARGE,
RECHARGE,
RECHARGE,
DIVIDE_10,
DAMAGE,
BUCKSHOT
| castDelay = 0.50
| rechargeTime = 1.00
| manaMax = 500
| manaCharge = 500
| capacity = 24
| spread = 0
| speed = 1
}}
Also shows use of multiple always cast spells (up to 4 are allowed).
{{Wand2
| vertical = Yes
| wandCard = Yes
| hideLink = No
| tooltips = No
| wandName = Vertical Wand of Examples
| wandPic = Wand 0413.png
| shuffle = No
| spellsCast = 2
| alwaysCasts = INFESTATION,HOMING,LASER,DAMAGE
| spells = MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
RECHARGE,
RECHARGE,
RECHARGE,
RECHARGE,
DIVIDE_10,
DAMAGE,
BUCKSHOT
| castDelay = 0.50
| rechargeTime = 1.00
| manaMax = 500
| manaCharge = 500
| capacity = 24
| spread = 0
| speed = 1
}}
Also shows use of multiple always cast spells (up to 4 are allowed).
{{Wand2
| vertical = No
| wandCard = Yes
| hideLink = No
| tooltips = No
| wandName = Ranged Wand of Examples
| wandPic = Wand 0413.png
| shuffle = No
| spellsCast = 2
| alwaysCasts = HOMING,DAMAGE
| spells = MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
MANA_REDUCE,
RECHARGE,
RECHARGE,
RECHARGE,
RECHARGE,
DIVIDE_10,
DAMAGE,
BUCKSHOT
| castDelay = 0.50,1.2
| rechargeTime = 1.00,2.00
| manaMax = 500,7000
| manaCharge = 5,2000
| capacity = 2,16
| spread = -20,80
| speed = 1,5
}}
{{Wand2
| tooltips = No
| wandPic = Wand 0413.png
| capacity = 12
| shuffle = No
| spellsCast = 1
| alwaysCasts = INFESTATION,INFESTATION,INFESTATION,INFESTATION
| spells = MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,RECHARGE,RECHARGE,RECHARGE,RECHARGE,DIVIDE_10,DAMAGE,BUCKSHOT
}}
{{Wand2
| tooltips = Yes
| wandPic = Wand 0413.png
| capacity = 12
| shuffle = No
| spellsCast = 1
| spells = MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,MANA_REDUCE,RECHARGE,RECHARGE,RECHARGE,RECHARGE,DIVIDE_10,DAMAGE,BUCKSHOT
}}
Parameters
- All parameters are optional.
- Parameters without a default value are omitted from the output entirely.
- Both templates
{{Wand Card}}
and{{Wand}}
share the same parameter names and they may be used interchangeably. - The numbering of the
spell[X]
entries is to make them unique, and does not affect the order of spells in the wand. Spells will appear in the order they are listed, and blank spells will only appear where there is an empty entry. - Ranges for a value may be displayed by enclosing them in brackets, e.g.:
(0.17 - 0.34)
. Values with ranges will be omitted from the Wand Simulator link url.
Parameter | Default | Description |
---|---|---|
Wand configuration | ||
wandName |
Omitted | The name of the wand. If this field is blank, or missing, the wand name is omitted. [notes 1] |
wandPic |
Wand_0821.png | Specifies the wand image. Must include the filetype extension, e.g.: Wand handgun.png [notes 1]
|
shuffle |
No | Whether or not the wand is a shuffle-type wand. Can be Yes or No. |
spellsCast |
1 | The number of spells cast per cast. |
castDelay |
0.00 | The cast delay between each spell in the wand (in seconds). |
rechargeTime |
0.00 | The recharge delay between each cast (in seconds). |
manaMax |
500 | The maximum mana capacity of the wand. |
manaRecharge |
250 | The mana recharge rate of the wand (in mana per second). |
capacity |
1 | The capacity of the wand. Spell slots numbered greater than this will not be shown. |
spread |
0.0 | The spread of the wand's casts. |
speed |
1 | The speed multiplier of the wand's casts. [notes 1] |
alwaysCasts |
Omitted | The spell that the wand always casts. [notes 1] |
spell1 , spell2 ... spell(n) |
Comprises a list of the case sensitive names of the spells on the wand. The numbering must be unique, but has no effect on spell order - spells will appear on the wand in the order they're listed. However, spells numbered greater than specified wand capacity will not appear. An empty entry in the list produces an empty spell slot.
| |
Template configuration | ||
hideLink |
No | Set to Yes to hide the Wand Simulator link.
|
vertical |
Auto | Set to Yes to always display in a vertical (mobile-friendly) layout format (this is also enabled automatically on screens with width of less than 640px.) Set to No to disable the vertical mode entirely.
|
local p = {}
local cargo = mw.ext.cargo
-- Scribunto (Lua main): https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual
-- Cargo Lua support: https://www.mediawiki.org/wiki/Extension:Cargo/Other_features#Lua_support
local MAX_ALWAYSCAST = 4
local framesPerSecond = 60
local NNBSP = ' '
-- Trims whitespace off of the left and right sides of a string
local function trim(str)
return str:gsub("^%s*(.-)%s*$", "%1")
end
-- Basically is null, is empty, or is whitespace
local function is_empty(str)
return str == nil or type(str) == 'string' and string.len(trim(str)) == 0
end
local function table_empty(tbl)
return next(tbl) == nil
end
-- If a given string is empty or nil, return nil, otherwise give back the value
local function nil_or_value(str)
if str == nil or type(str) ~= 'string' or str == '' then
return nil
end
return str
end
local function minAndMax(a, b)
if a == nil then
return b, b
end
if b == nil then
return a, a
end
return math.min(a, b), math.max(a, b)
end
-- For sim url, convert seconds to frames (@60fps)
local function formatSeconds(seconds)
return ("%u"):format(tonumber(seconds or 0 * framesPerSecond))
end
local function formatRange(minV, maxV, fSingle, fRange)
if minV == nil and maxV == nil then
return '--'
end
if minV == maxV or maxV == nil then
return string.format(fSingle, minV)
end
if minV == nil then
return string.format(fSingle, maxV)
end
return string.format(fRange, minV, maxV)
end
local function bothIfDifferent(a, b)
if a == nil and b == nil then
return nil
end
if a == b or b == nil then
return a
end
if a == nil then
return b
end
return a .. ',' .. b
end
-- Normally you'd do `table.insert(table, item)` to add to the end, but this
-- does not appear to be working in this Lua implementation so here's a workaround.
local function append_table(tbl, data)
tbl[#tbl + 1] = data
end
-- Split a string by comma, return each item trimmed in a table.
local function split(str, by)
by = by or ','
local result = {}
if str == nil then
return result
end
local pattern = string.format("([^%s]+)", by)
for token in string.gmatch(str, pattern) do
append_table(result, trim(token))
end
return result
end
local function map(source, func, ...)
local result = {}
for key, value in pairs(source) do
result[key] = func(value, unpack(arg))
end
return result
end
local truthy = {"^1", "^y", "^Y", "^true", "^True"}
local falsey = {"^0", "^n", "^N", "^false", "^False"}
local function seek_truth(predicate, default)
if not is_empty(predicate) then
if type(predicate) == 'boolean' then
return predicate
end
if type(predicate) == 'number' then
return not predicate == 0
end
if type(predicate) == 'string' then
for _, truth in ipairs(truthy) do
if predicate:find(truth) then
return true
end
end
for _, falsehood in ipairs(falsey) do
if predicate:find(falsehood) then
return false
end
end
end
end
return default
end
---------------------------------------------- G(et) ARGuments from frames -----
--- If sep is not nil it specifies the character that delimits multiple values
---
local function garg_base(frame, name, default, sep, parseFunc, ...)
local args = frame.args
local parentArgs = frame:getParent().args
local valFromArgs = nil_or_value(parentArgs[name]) or nil_or_value(args[name])
if valFromArgs == nil then
return { default, default }
end
if parseFunc == nil or not type(parseFunc) == 'function' then
return { valFromArgs, valFromArgs }
end
if not is_empty(sep) then
return map(split(valFromArgs, sep), parseFunc, unpack(arg))
end
local parsedVal = parseFunc(valFromArgs, unpack(arg))
return { parsedVal, parsedVal }
end
-- local shuffle = garg_boo("shuffle", pArgs, args, false )
-- GARG for strings
local function garg_str(frame, name, default, sep)
return unpack(garg_base(frame, name, default, sep))
end
-- GARG for numbers
local function garg_num(frame, name, default, sep)
return unpack(garg_base(frame, name, default, sep, tonumber, 10))
end
-- GARG for booleans
local function garg_boo(frame, name, default, sep)
return unpack(garg_base(frame, name, default, sep, seek_truth, default))
end
------------------------------------------------------- Cargo spell lookup -----
-- Remove invalid characters from potential spell ID
-- Valid ones are [A-Za-z1-9_], then uppercased
local function sanitizeSpellId(str)
if is_empty(str) then
return nil
end
return str:gsub("[^%w_]+", ""):upper()
end
-- Split a string of SPELL_IDS,BY,COMMA preserving holes "A,,B" -> ["A","","B"]
local function splitSpells(str)
local result = {}
if is_empty(str) then
return result
end
for token in string.gmatch(str, "([^,]*)[,]?") do
append_table(result, sanitizeSpellId(token) or '')
end
return result
end
-- Check if a split spell list actually contains any spells (or just holes)
local function isEmptySpellList(spellList)
for _, item in ipairs(spellList) do
if not is_empty(item) then
return false
end
end
return true
end
-- Build a simple '(id=X or id=Y or...)' query clause
local function makeQuery(spellList, alwaysCastList)
local result = {}
for _, item in ipairs(spellList) do
if not is_empty(item) then
append_table(result, string.format("id=\"%s\"", item))
end
end
for _, item in ipairs(alwaysCastList) do
if not is_empty(item) then
append_table(result, string.format("id=\"%s\"", item))
end
end
return "(" .. table.concat(result, " " .. "OR" .. " ") .. ")"
end
-- Make a new table indexed by the specified key of each item
-- reindex({ 1: {id: 'q', }, 2: {id: 'f', }, 3: {id: 'm', }}, 'id')
-- = {'q': {id: 'q', }, 'f': {id: 'f', }, 'm': {id: 'm', }}
local function reindex(from, key)
local to = {}
for _, item in ipairs(from) do
to[item[key]] = item
end
return to
end
-- Look up all the spells on the wand in one query
-- Return them as a lookup table keyed by spell ID
local function doSpellQuery(spellIdList, acIdList)
if isEmptySpellList(spellIdList) then
return {}
end
-- Perform a single cargo query for all spell details
local query = {}
query.where = makeQuery(spellIdList, acIdList)
query.limit = 200
if tooltips then
query.fields = [[
_pageName,image,name,id,description,type,tags,manaDrain,uses,
damageProjectile,damageMelee,damageElectric,damageFire,
damageExplosion,damageIce,damageSlice,damageDrill,
damageHealing,damageHoly,
speed,castDelay,rechargeDelay,bounces,effect
]]
else
query.fields = "_pageName,image,name,id,description,type,tags"
end
return reindex(cargo.query("Spells", query.fields, query), 'id')
end
--------------------------------------------------- Spell helper functions -----
local SpellTypeClassMap = {
["放射物"] = "spellProjectile",
["静電放射物"] = "spellStatic",
["放射物調整盤"] = "spellModifier",
["マルチキャスト"] = "spellMulticast",
["資材"] = "spellMaterial",
["その他"] = "spellOther",
["ユーティリティー"] = "spellUtility",
["受け身"] = "spellPassive",
}
local function getSpellTypeClass(type)
return SpellTypeClassMap[string.lower(type)] or "spellUnknown"
end
-------------------------------------------------------------- HTML output -----
local function addSpell(parent, spell, tooltips)
local card = parent:tag('div'):attr('class', 'wand2-spell')
if type(spell) == "table" then
card:tag('div')
:attr('class',
string.format('spellBorder spellBackground %s',
getSpellTypeClass(spell.type)))
:wikitext(
string.format('[[File:%s|link=%s|alt=%s|128px]]',
spell.image, spell._pageName, spell.name))
if tooltips then
card:addClass('wand2-spelltip-target')
local tip = card:tag('div'):attr('class', 'wand2-spelltip')
tip:tag('div'):attr('class', 'wand2-spelltip-name')
:wikitext(string.format('%s', spell.name))
tip:tag('div'):attr('class', 'wand2-spelltip-desc')
:wikitext(string.format('%s', spell.description))
tip:tag('div'):attr('class', 'wand2-spelltip-key wand2-spelltip-sub')
:wikitext(string.format('タイプ'))
tip:tag('div'):attr('class', 'wand2-spelltip-value wand2-spelltip-sub')
:wikitext(string.format('%s', spell.type or ''))
tip:tag('div'):attr('class', 'wand2-spelltip-key')
:wikitext(string.format('マナ流出'))
tip:tag('div'):attr('class', 'wand2-spelltip-value')
:wikitext(string.format('%s', spell.manaDrain or ''))
tip:tag('div'):attr('class', 'wand2-spelltip-img')
:wikitext(string.format(
'[[File:%s|link=%s|alt=%s|128px]]',
spell.image, spell._pageName, spell.name))
end
end
end
---------------------------------------------- Main entry point for module -----
function p.Wand(frame)
local vertical = garg_boo(frame, "vertical", false )
local wandCard = garg_boo(frame, "wandCard", false )
-- Card wraps at 10 vb2x10 +6
-- Mini wraps at 26 10x 26
-- Vertical wraps at 6 (6x4 +2)
local defaultWrap = vertical and 6 or wandCard and 10 or 26
-- Display options
local wrapCount = garg_num(frame, "wrapCount", defaultWrap )
local hideLink = garg_boo(frame, "hideLink", false )
local hideName = garg_boo(frame, "hideName", false )
local hideSpells = garg_boo(frame, "hideSpells", false )
local tooltips = garg_boo(frame, "tooltips", true )
-- Wand stats
-- Always show
local alwaysCasts = garg_str(frame, "alwaysCasts" )
local spells = garg_str(frame, "spells" )
local shuffle = garg_boo(frame, "shuffle", false )
local pCastMin, pCastMax = minAndMax(garg_num(frame, "spellsCast", 1, ','))
-- Shown on expanded + cardviews
local delayMin, delayMax = minAndMax(garg_num(frame, "castDelay", 0.17, ','))
local rTimeMin, rTimeMax = minAndMax(garg_num(frame, "rechargeTime", 0.48, ','))
local manaMin, manaMax = minAndMax(garg_num(frame, "manaMax", 900, ','))
local regenMin, regenMax = minAndMax(garg_num(frame, "manaCharge", 700, ','))
local capMin, capMax = minAndMax(garg_num(frame, "capacity", 26, ','))
local spreadMin, spreadMax = minAndMax(garg_num(frame, "spread", -2.00, ','))
-- Shown on expanded + cardviews (Not shown in-game)
local speedMin, speedMax = minAndMax(garg_num(frame, "speed", 1.00, ','))
-- Customisation
local wandName = garg_str(frame, "wandName", "" )
local wandPic = garg_str(frame, "wandPic", "Wand_0821.png" )
local wandPicId = garg_str(frame, "wandPicId", "0821" )
local cap = capMax or capMin or 26
-- Constants
local picSize = "165px"
-- Create tables from the CSV strings
local spellIdList = splitSpells(spells)
local acIdList = splitSpells(alwaysCasts)
local results = doSpellQuery(spellIdList, acIdList)
-- Build output HTML
local root = mw.html.create('div')
:attr('class', string.format('%s %s',
wandCard and 'wand2-card' or 'wand2-mini',
vertical and 'wand2-vertical' or ''))
:cssText(string.format('--wand2-cap: %d; --wand2-wrap: %d;', cap, wrapCount))
-- Wand name
if not hideName and not is_empty(wandName) then
root:tag('div'):attr('class', 'wand2-name')
:wikitext(wandName)
end
-- Wand image
if not hidePic then
root:tag('div'):attr('class', 'wand2-sprite')
:cssText(string.format('max-width: %s;', picSize))
:wikitext(string.format('[[File:%s|link=]]', wandPic))
end
-- Basic wand attributes
root:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'shuffle')
:attr('data-value', shuffle and '1' or '0')
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon gun shuffle.png]] シャッフル')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(string.format('%s', shuffle and 'はい' or 'いいえ'))
root:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'spells-per-cast')
:attr('data-value-min', pCastMin)
:attr('data-value-max', pCastMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon gun actions per round.png]] 同時詠唱数')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(pCastMin, pCastMax, '%s', '( %s - %s )'))
-- Extended wand attributes (initially hidden for mini view)
local details = root:tag('div'):attr('class', 'wand2-details')
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'cast-delay')
:attr('data-value-min', delayMin)
:attr('data-value-max', delayMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon fire rate wait.png]]] 詠唱遅延')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(delayMin, delayMax, '%1.2f 秒', '( %1.2f - %1.2f ) 秒'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-recharge')
:attr('data-value-min', rTimeMin)
:attr('data-value-max', rTimeMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon gun reload time.png]] リチャージ時間')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(rTimeMin, rTimeMax, '%1.2f 秒', '( %1.2f - %1.2f ) 秒'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-max')
:attr('data-value-min', manaMin)
:attr('data-value-max', manaMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon mana max.png]] 最大マナ')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(manaMin, manaMax, '%d', '( %d - %d )'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-charge')
:attr('data-value-min', regenMin)
:attr('data-value-max', regenMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon mana charge speed.png]] マナチャージ速度')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(regenMin, regenMax, '%d', '( %d - %d )'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-cap')
:attr('data-value-min', capMin)
:attr('data-value-max', capMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon gun capacity.png]] 呪文容量')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(capMin, capMax, '%d', '( %d - %d )'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-spread')
:attr('data-value-min', spreadMin)
:attr('data-value-max', spreadMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon spread degrees.png]] 拡散')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(spreadMin, spreadMax, '%1.1f 度', '( %1.1f - %1.1f ) 度'))
details:tag('div'):attr('class', 'wand2-stat')
:attr('data-name', 'wand2-stat-speed')
:attr('data-value-min', speedMin)
:attr('data-value-max', speedMax)
:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon speed multiplier.png]] 速度')
:done()
:tag('div'):attr('class', 'wand2-value')
:wikitext(formatRange(speedMin, speedMax, '× %1.2f', '× ( %1.2f - %1.2f )'))
-- Simulator link
local simURI = mw.uri.new({
protocol = "https",
host = "tinker-with-wands-online.vercel.app",
query = {
a = bothIfDifferent(pCastMin, pCastMax),
d = bothIfDifferent(formatSeconds(delayMin),formatSeconds(delayMax)),
r = bothIfDifferent(formatSeconds(rTimeMin),formatSeconds(rTimeMax)),
m = bothIfDifferent(manaMin, manaMax),
c = bothIfDifferent(regenMin, regenMax),
l = bothIfDifferent(capMin, capMax),
q = bothIfDifferent(spreadMin, spreadMax),
v = bothIfDifferent(speedMin, speedMax),
x = shuffle and 1 or 0,
n = wandName,
p = wandPic,
w = table.concat(acIdList, ","),
s = table.concat(spellIdList, ","),
},
})
root:tag('div')
:attr('class', 'wand2-simlink ' .. (hideLink and 'hidden' or ''))
:tag('div'):attr('class', 'wand2-simlink-link')
:wikitext(string.format('[%s Tinker]', tostring(simURI)))
:done()
:tag('div'):attr('class', 'wand2-simlink-desc')
:tag('div'):wikitext('Visit the Wand Simulator site')
:tag('div'):wikitext('to view & edit this wand')
-- Always casts
if not hideSpells and not table_empty(acIdList) then
local alwaysCount = 0
local alwaysContainer = nil
for _, acId in ipairs(acIdList) do
if alwaysCount < MAX_ALWAYSCAST and not is_empty(acId) then
if alwaysCount < 1 then
local always = root:tag('div'):attr('class', 'wand2-stat wand2-always')
always:tag('div'):attr('class', 'wand2-label')
:wikitext('[[File:Inventory Icon gun permanent actions.png]]常に呪文を詠唱')
alwaysContainer = always:tag('div'):attr('class', 'wand2-value')
end
addSpell(alwaysContainer, results[acId] or nil, tooltips)
end
alwaysCount = alwaysCount + 1
end
end
-- Main spells
if not hideSpells then
local spellsContainer = root:tag('div'):attr('class', 'wand2-spells')
local count = 0
for _, spellId in ipairs(spellIdList) do
if count < cap then
addSpell(spellsContainer, results[spellId] or nil, tooltips)
end
count = count + 1
end
while count < cap do
addSpell(spellsContainer, nil, tooltips)
count = count + 1
end
end
root:allDone()
return tostring(root)
end
return p