Модуль:SpeciesStats: различия между версиями
Halch (обсуждение | вклад) Нет описания правки |
Halch (обсуждение | вклад) Нет описания правки |
||
| (не показана 1 промежуточная версия этого же участника) | |||
| Строка 25: | Строка 25: | ||
-- {{#invoke:SpeciesStats|features|Arachnid|header=Особенности|notes=...}} — особенности (ручные строки + из данных) | -- {{#invoke:SpeciesStats|features|Arachnid|header=Особенности|notes=...}} — особенности (ручные строки + из данных) | ||
-- {{#invoke:SpeciesStats|missing|Human, Arachnid, ...}} — расы проекта, которых нет в списке | -- {{#invoke:SpeciesStats|missing|Human, Arachnid, ...}} — расы проекта, которых нет в списке | ||
-- {{#invoke:SpeciesStats|image|Arachnid}} — имя файла картинки расы (для шторки) | |||
-- {{#invoke:SpeciesStats|nav|Human:Люди, ...}} — сетка иконок рас (сейчас на странице не используется) | |||
-- {{#invoke:SpeciesStats|summary|Human:Люди, ...}} — сводная таблица рас (сейчас не используется) | |||
-- | -- | ||
-- КАК ПРАВИТЬ | -- КАК ПРАВИТЬ | ||
| Строка 39: | Строка 42: | ||
-- * Особенность, которой нет в данных, пишется не здесь, а на самой странице | -- * Особенность, которой нет в данных, пишется не здесь, а на самой странице | ||
-- в параметре notes= у нужной расы. | -- в параметре notes= у нужной расы. | ||
-- * Расцветка: | -- * Расцветка: зелёный — положительное (лечение, преимущества), красный — | ||
-- | -- отрицательное (урон, недостатки). Для урона используйте takenText(), для лечения — | ||
-- | -- healText(), для фраз — tone(). Режим сравнения с человеком — COMPARE_WITH_HUMAN. | ||
-- * Порядок групп урона и органов задают GROUP_ORDER и ORGAN_ORDER ниже. | -- * Порядок групп урона и органов задают GROUP_ORDER и ORGAN_ORDER ниже. | ||
-- Состав групп и органов там не задаётся: его дают данные. | -- Состав групп и органов там не задаётся: его дают данные. | ||
| Строка 230: | Строка 233: | ||
-- Цвет значений, которые совпадают с человеческими (нейтральный жёлтый). | -- Цвет значений, которые совпадают с человеческими (нейтральный жёлтый). | ||
-- Расцветка страницы: | |||
-- зелёный (class="positive") — положительное: лечение, преимущества, баффы; | |||
-- красный (class="negative") — отрицательное: получаемый урон, недостатки, дебаффы. | |||
-- жёлтый (SAME_STYLE) — остальные числа (здоровье, скорость, температура и т. п.). | |||
-- COMPARE_WITH_HUMAN = true включает другой режим: числа красятся по сравнению с человеком | |||
-- (лучше — зелёный, хуже — красный, так же — SAME_STYLE). Сейчас выключен. | |||
local COMPARE_WITH_HUMAN = false | |||
-- Нейтральный цвет чисел. | |||
local SAME_STYLE = "color:#e6c07b;font-weight:bold" | local SAME_STYLE = "color:#e6c07b;font-weight:bold" | ||
-- true, пока выводится сам человек: сравнивать его с собой незачем, значения без цвета. | -- true, пока выводится сам человек: сравнивать его с собой незачем, значения без цвета. | ||
| Строка 243: | Строка 254: | ||
-- fmt — как показать значение человека в подсказке (по умолчанию num). | -- fmt — как показать значение человека в подсказке (по умолчанию num). | ||
local function mark(text, v, base, higherBetter, fmt) | local function mark(text, v, base, higherBetter, fmt) | ||
if not COMPARE_WITH_HUMAN then | |||
-- Обычное число (не урон и не лечение): нейтральный цвет, для красоты. | |||
if type(v) == "number" then | |||
return '<span style="' .. SAME_STYLE .. '">' .. text .. '</span>' | |||
end | |||
return text | |||
end | |||
if type(v) ~= "number" or type(base) ~= "number" then | if type(v) ~= "number" or type(base) ~= "number" then | ||
return text | return text | ||
| Строка 270: | Строка 288: | ||
end | end | ||
local function value(v, b) | local function value(v, b) | ||
if base then | if base and COMPARE_WITH_HUMAN then | ||
return mark(num(v), v, b or 0, higherBetter) | return mark(num(v), v, b or 0, higherBetter) | ||
elseif base then | |||
-- Без сравнения: урон, который получает раса, — красный, лечение — зелёный. | |||
return tone(num(v), higherBetter) | |||
end | end | ||
return num(v) | return num(v) | ||
| Строка 1414: | Строка 1435: | ||
} | } | ||
end | end | ||
-- Подзаголовок раздела внутри блока расы. Это не заголовок вики (== ==), | |||
-- а жирная строка: так в оглавление попадают только сами расы. | |||
-- level= (например «===») возвращает обычные заголовки, если они нужны. | |||
local function subHeading(title, level) | |||
if level and level ~= "" then | |||
return level .. " " .. title .. " " .. level | |||
end | |||
return '<div style="font-weight:bold;font-size:1.15em;margin:0.8em 0 0.3em;' | |||
.. 'border-bottom:1px solid #3d3d4a;padding-bottom:2px">' .. title .. '</div>' | |||
end | |||
-- Разделы, которые по умолчанию свёрнуты (раскрываются по клику «показать»). | |||
-- Сейчас пусто: раса и так лежит в шторке, вложенные шторки не нужны. | |||
-- Пример: { ["Внутренние органы"] = true }. | |||
local COLLAPSED = {} | |||
function p.stats(frame) | function p.stats(frame) | ||
| Строка 1422: | Строка 1459: | ||
return "" | return "" | ||
end | end | ||
local orgs = organs(mob) | local orgs = organs(mob) | ||
local out = {} | local out = {} | ||
local function section(title, lines) | local function section(title, lines) | ||
if #lines | if #lines == 0 then | ||
out[#out + 1] = | return | ||
end | |||
if COLLAPSED[title] and not args.level then | |||
-- Встроенное сворачивание MediaWiki: заголовок виден всегда, список — по клику. | |||
out[#out + 1] = '<div class="mw-collapsible mw-collapsed" data-expandtext="показать" data-collapsetext="скрыть">' | |||
.. subHeading(title) | |||
out[#out + 1] = '<div class="mw-collapsible-content">' | |||
out[#out + 1] = table.concat(lines, "\n") | |||
out[#out + 1] = '</div></div>' | |||
else | |||
out[#out + 1] = subHeading(title, args.level) | |||
out[#out + 1] = table.concat(lines, "\n") | out[#out + 1] = table.concat(lines, "\n") | ||
end | end | ||
| Строка 1440: | Строка 1486: | ||
end | end | ||
-- Список особенностей расы по её компонентам | -- Список особенностей расы по её компонентам. | ||
-- notes= — строки, написанные вручную (идут первыми); header= — | -- notes= — строки, написанные вручную (идут первыми); header= — подзаголовок раздела, | ||
-- который выводится, только если есть хоть одна строка. | -- который выводится, только если есть хоть одна строка. | ||
function p.features(frame) | function p.features(frame) | ||
| Строка 1464: | Строка 1510: | ||
local text = table.concat(lines, "\n") | local text = table.concat(lines, "\n") | ||
if args.header and args.header ~= "" then | if args.header and args.header ~= "" then | ||
text = subHeading(args.header, args.level) .. "\n" .. text | |||
text = | |||
end | end | ||
return text | return text | ||
end | |||
-- Разбор списка рас для nav и summary: «Human:Люди, Skeleton*:Скелеты, …». | |||
-- ID — id прототипа расы, после двоеточия — заголовок её раздела на странице (на него | |||
-- ведёт ссылка). Звёздочка после ID — показывать, даже если раса недоступна при создании | |||
-- персонажа. Расы, которых нет в проекте, пропускаются. | |||
local function raceList(text) | |||
local out = {} | |||
for part in mw.text.gsplit(text or "", ",", true) do | |||
local idPart, title = part:match("^%s*([^:]+):(.-)%s*$") | |||
if idPart then | |||
local any = idPart:sub(-1) == "*" | |||
local id = mw.text.trim(any and idPart:sub(1, -2) or idPart) | |||
if (any and species(id)) or isPlayable(id) then | |||
out[#out + 1] = { id = id, title = mw.text.trim(title) } | |||
end | |||
end | |||
end | |||
return out | |||
end | |||
-- Сетка иконок рас со ссылками на их разделы: | |||
-- {{#invoke:SpeciesStats|nav|Human:Люди, Dwarf:Дворфы, …}} | |||
function p.nav(frame) | |||
local _, args = argId(frame) | |||
local cards = {} | |||
for _, r in ipairs(raceList(args[1])) do | |||
local mob = speciesMob(r.id) | |||
local file = mob and imageFor(mob) | |||
cards[#cards + 1] = '<div style="width:112px;font-size:0.9em;text-align:center;background:#27272e;' | |||
.. 'border:1px solid #3d3d4a;border-radius:6px;padding:4px">' | |||
.. (file and ("[[Файл:" .. file .. "|64px|link=#" .. r.title .. "]]<br>") or "") | |||
.. "[[#" .. r.title .. "|" .. r.title .. "]]</div>" | |||
end | |||
if #cards == 0 then | |||
return "" | |||
end | |||
return '<div style="display:flex;flex-wrap:wrap;gap:8px;margin:0.5em 0 1em">' | |||
.. table.concat(cards) .. '</div>' | |||
end | |||
-- Сводная таблица рас (сортируется по клику на заголовок столбца): | |||
-- {{#invoke:SpeciesStats|summary|Human:Люди, Dwarf:Дворфы, …}} | |||
-- Цвета — как на всей странице: сравнение с человеком. | |||
function p.summary(frame) | |||
local _, args = argId(frame) | |||
local baseMob = speciesMob(BASELINE_SPECIES) | |||
local function bc(name) | |||
return baseMob and comp(name, baseMob) | |||
end | |||
local baseTh = thresholdsOf(baseMob) | |||
local baseMs = bc("MovementSpeedModifier") | |||
local _, baseVol = bloodOf(bc("Bloodstream")) | |||
local bt = bc("TemperatureDamage") or bc("Temperature") | |||
local rows = { | |||
'{| class="wikitable sortable" style="text-align:center"', | |||
"! Раса !! Крит !! Смерть !! Шаг !! Бег !! Кровь, ед. !! Дышит !! Темп. от !! Темп. до", | |||
} | |||
local function cell(sortValue, text) | |||
if sortValue == nil then | |||
return '| data-sort-value="-9999" | —' | |||
end | |||
return '| data-sort-value="' .. sortValue .. '" | ' .. text | |||
end | |||
for _, r in ipairs(raceList(args[1])) do | |||
local mob = speciesMob(r.id) | |||
if mob then | |||
isBaseline = (r.id == BASELINE_SPECIES) | |||
local th = thresholdsOf(mob) | |||
local ms = comp("MovementSpeedModifier", mob) | |||
local walk, run = ms and ms("baseWalkSpeed"), ms and ms("baseSprintSpeed") | |||
local bsComp = comp("Bloodstream", mob) | |||
local _, vol = bloodOf(bsComp) | |||
if not bsComp then | |||
vol = 0 | |||
end | |||
local t = comp("TemperatureDamage", mob) or comp("Temperature", mob) | |||
local cold = t and t("coldDamageThreshold") | |||
local heat = t and t("heatDamageThreshold") | |||
if cold == 0 then | |||
cold = nil | |||
end | |||
local breathText = "не дышит" | |||
if comp("Respirator", mob) then | |||
local types = lungTypes(organs(mob)) | |||
local b = types and reagentList((gases(types))) or {} | |||
breathText = #b > 0 and table.concat(b, ", ") or "—" | |||
end | |||
rows[#rows + 1] = "|-" | |||
rows[#rows + 1] = "| style=\"text-align:left\" | [[#" .. r.title .. "|" .. r.title .. "]]" | |||
rows[#rows + 1] = cell(th.Critical, th.Critical and mark(num(th.Critical), th.Critical, baseTh.Critical, true)) | |||
rows[#rows + 1] = cell(th.Dead, th.Dead and mark(num(th.Dead), th.Dead, baseTh.Dead, true)) | |||
rows[#rows + 1] = cell(walk, walk and mark(num(walk), walk, baseMs and baseMs("baseWalkSpeed"), true)) | |||
rows[#rows + 1] = cell(run, run and mark(num(run), run, baseMs and baseMs("baseSprintSpeed"), true)) | |||
rows[#rows + 1] = cell(vol, vol and (vol == 0 and "нет" or mark(num(vol), vol, baseVol, true))) | |||
rows[#rows + 1] = "| " .. breathText | |||
rows[#rows + 1] = cell(cold, cold and mark(kelvinToC(cold), cold, bt and bt("coldDamageThreshold"), false, kelvinToC)) | |||
rows[#rows + 1] = cell(heat, heat and mark(kelvinToC(heat), heat, bt and bt("heatDamageThreshold"), true, kelvinToC)) | |||
end | |||
end | |||
isBaseline = false | |||
rows[#rows + 1] = "|}" | |||
return table.concat(rows, "\n") | |||
end | |||
-- Имя файла картинки расы (спрайт бота) или file=, если задан; пусто, если картинки нет. | |||
-- {{#invoke:SpeciesStats|image|Arachnid|file=Арахнид.png}} | |||
function p.image(frame) | |||
local id, args = argId(frame) | |||
if args.file and args.file ~= "" then | |||
return args.file | |||
end | |||
local mob = speciesMob(id) | |||
return mob and imageFor(mob) or "" | |||
end | end | ||