<br />
<b>Deprecated</b>:  Use of MediaWiki\Parser\Parser::$tabsData was deprecated in MediaWiki 1.42. [Called from Tabs::init in /var/www/html/extensions/Tabs/includes/Tabs.php at line 35] in <b>/var/www/html/includes/debug/MWDebug.php</b> on line <b>372</b><br />
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://wiki.siberiaserver.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AReagents</id>
	<title>Модуль:Reagents - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.siberiaserver.ru/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AReagents"/>
	<link rel="alternate" type="text/html" href="https://wiki.siberiaserver.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:Reagents&amp;action=history"/>
	<updated>2026-05-17T13:30:46Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.siberiaserver.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:Reagents&amp;diff=8398&amp;oldid=prev</id>
		<title>M 9SCO: Новая страница: «-- Module:Reagents -- Renders reagent cards from game prototype JSON data. -- -- Data source:  Module:Reagents/reagent.json  (511 reagents) --               Module:Reagents/reaction.json (414 reactions) -- -- Usage: --   {{#invoke:Reagents|group|Medicine}}       — all cards in a group --   {{#invoke:Reagents|card|Bicaridine}}      — single card by prototype id --   {{#invoke:Reagents|reactions|Medicine}}    — reactions producing reagents in group --...»</title>
		<link rel="alternate" type="text/html" href="https://wiki.siberiaserver.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:Reagents&amp;diff=8398&amp;oldid=prev"/>
		<updated>2026-05-17T08:13:40Z</updated>

		<summary type="html">&lt;p&gt;Новая страница: «-- Module:Reagents -- Renders reagent cards from game prototype JSON data. -- -- Data source:  Module:Reagents/reagent.json  (511 reagents) --               Module:Reagents/reaction.json (414 reactions) -- -- Usage: --   {{#invoke:Reagents|group|Medicine}}       — all cards in a group --   {{#invoke:Reagents|card|Bicaridine}}      — single card by prototype id --   {{#invoke:Reagents|reactions|Medicine}}    — reactions producing reagents in group --...»&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Module:Reagents&lt;br /&gt;
-- Renders reagent cards from game prototype JSON data.&lt;br /&gt;
--&lt;br /&gt;
-- Data source:  Module:Reagents/reagent.json  (511 reagents)&lt;br /&gt;
--               Module:Reagents/reaction.json (414 reactions)&lt;br /&gt;
--&lt;br /&gt;
-- Usage:&lt;br /&gt;
--   {{#invoke:Reagents|group|Medicine}}       — all cards in a group&lt;br /&gt;
--   {{#invoke:Reagents|card|Bicaridine}}      — single card by prototype id&lt;br /&gt;
--   {{#invoke:Reagents|reactions|Medicine}}    — reactions producing reagents in group&lt;br /&gt;
--   {{#invoke:Reagents|count|Medicine}}        — count of reagents in group&lt;br /&gt;
&lt;br /&gt;
local reagents  = mw.loadJsonData( &amp;#039;Module:Reagents/reagent.json&amp;#039; )&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- ── helpers ─────────────────────────────────────────────────&lt;br /&gt;
&lt;br /&gt;
local function attr( s )&lt;br /&gt;
    return s:gsub( &amp;#039;&amp;quot;&amp;#039;, &amp;#039;&amp;amp;quot;&amp;#039; )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function cssColor( raw )&lt;br /&gt;
    if not raw or raw == &amp;#039;&amp;#039; then return nil end&lt;br /&gt;
    if raw:sub(1,1) == &amp;#039;#&amp;#039; then return raw end&lt;br /&gt;
    return raw&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Translate damage type names to Russian&lt;br /&gt;
local dmgNames = {&lt;br /&gt;
    Brute        = &amp;#039;Физ.&amp;#039;,&lt;br /&gt;
    Burn         = &amp;#039;Ожог&amp;#039;,&lt;br /&gt;
    Heat         = &amp;#039;Тепло&amp;#039;,&lt;br /&gt;
    Cold         = &amp;#039;Холод&amp;#039;,&lt;br /&gt;
    Shock        = &amp;#039;Шок&amp;#039;,&lt;br /&gt;
    Caustic      = &amp;#039;Кислот.&amp;#039;,&lt;br /&gt;
    Poison       = &amp;#039;Токсин&amp;#039;,&lt;br /&gt;
    Radiation    = &amp;#039;Рад.&amp;#039;,&lt;br /&gt;
    Asphyxiation = &amp;#039;Удушье&amp;#039;,&lt;br /&gt;
    Bloodloss    = &amp;#039;Кровопот.&amp;#039;,&lt;br /&gt;
    Cellular     = &amp;#039;Клеточн.&amp;#039;,&lt;br /&gt;
    Piercing     = &amp;#039;Прокол&amp;#039;,&lt;br /&gt;
    Slash        = &amp;#039;Режущ.&amp;#039;,&lt;br /&gt;
    Blunt        = &amp;#039;Дробящ.&amp;#039;,&lt;br /&gt;
    Structural   = &amp;#039;Структ.&amp;#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function trDmg( name )&lt;br /&gt;
    return dmgNames[name] or name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Format a damage value: &amp;quot;−Физ. 2.5&amp;quot; or &amp;quot;+Токсин 1.0&amp;quot;&lt;br /&gt;
local function fmtDmg( name, val )&lt;br /&gt;
    local sign = val &amp;lt; 0 and &amp;#039;−&amp;#039; or &amp;#039;+&amp;#039;&lt;br /&gt;
    local abs = math.abs( val )&lt;br /&gt;
    local num = abs == math.floor( abs ) and tostring( math.floor( abs ) ) or string.format( &amp;#039;%.1f&amp;#039;, abs )&lt;br /&gt;
    return sign .. trDmg( name ) .. &amp;#039; &amp;#039; .. num&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse HealthChange effects from metabolism data.&lt;br /&gt;
local function parseEffects( metabs )&lt;br /&gt;
    local heal = {}&lt;br /&gt;
    local odHeal = {}&lt;br /&gt;
    local odThreshold = nil&lt;br /&gt;
    local rate = nil&lt;br /&gt;
    local special = {}&lt;br /&gt;
&lt;br /&gt;
    if not metabs then return heal, odHeal, odThreshold, rate, special end&lt;br /&gt;
&lt;br /&gt;
    for _, metaData in pairs( metabs ) do&lt;br /&gt;
        if metaData.rate then&lt;br /&gt;
            rate = metaData.rate&lt;br /&gt;
        end&lt;br /&gt;
        for _, eff in ipairs( metaData.effects or {} ) do&lt;br /&gt;
            local etype = eff.type or &amp;#039;&amp;#039;&lt;br /&gt;
            if etype:find( &amp;#039;HealthChange&amp;#039; ) then&lt;br /&gt;
                local isOD = false&lt;br /&gt;
                local threshold = nil&lt;br /&gt;
                for _, cond in ipairs( eff.conditions or {} ) do&lt;br /&gt;
                    if (cond.type or &amp;#039;&amp;#039;):find( &amp;#039;ReagentThreshold&amp;#039; ) then&lt;br /&gt;
                        threshold = cond.min&lt;br /&gt;
                        if threshold and threshold &amp;gt; 0 then&lt;br /&gt;
                            isOD = true&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
                local dmg = eff.damage or {}&lt;br /&gt;
                local target = isOD and odHeal or heal&lt;br /&gt;
                if isOD and threshold then&lt;br /&gt;
                    odThreshold = odThreshold and math.min( odThreshold, threshold ) or threshold&lt;br /&gt;
                end&lt;br /&gt;
                for gName, gVal in pairs( dmg.groups or {} ) do&lt;br /&gt;
                    target[#target + 1] = fmtDmg( gName, gVal )&lt;br /&gt;
                end&lt;br /&gt;
                for tName, tVal in pairs( dmg.types or {} ) do&lt;br /&gt;
                    target[#target + 1] = fmtDmg( tName, tVal )&lt;br /&gt;
                end&lt;br /&gt;
            elseif etype:find( &amp;#039;SuppressPain&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;подавляет боль&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;Jitter&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;дрожь&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;Drunk&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;опьянение&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;ChemVomit&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;рвота&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;AdjustTemperature&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;изм. температуры&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;Emote&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;эмоция&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;MovespeedModifier&amp;#039; ) then&lt;br /&gt;
                special[#special + 1] = &amp;#039;изм. скорости&amp;#039;&lt;br /&gt;
            elseif etype:find( &amp;#039;GenericStatusEffect&amp;#039; ) or etype:find( &amp;#039;StatusEffect&amp;#039; ) then&lt;br /&gt;
                local proto = eff.effectProto or eff.key or &amp;#039;&amp;#039;&lt;br /&gt;
                if proto:find( &amp;#039;Stun&amp;#039; ) then special[#special + 1] = &amp;#039;стан&amp;#039;&lt;br /&gt;
                elseif proto:find( &amp;#039;Sleep&amp;#039; ) then special[#special + 1] = &amp;#039;сон&amp;#039;&lt;br /&gt;
                elseif proto:find( &amp;#039;Mute&amp;#039; ) then special[#special + 1] = &amp;#039;немота&amp;#039;&lt;br /&gt;
                elseif proto:find( &amp;#039;Blind&amp;#039; ) then special[#special + 1] = &amp;#039;слепота&amp;#039;&lt;br /&gt;
                elseif proto:find( &amp;#039;Rainbow&amp;#039; ) then special[#special + 1] = &amp;#039;галлюцинации&amp;#039;&lt;br /&gt;
                end&lt;br /&gt;
            elseif etype:find( &amp;#039;AdjustReagent&amp;#039; ) then&lt;br /&gt;
                -- skip: internal reagent conversion&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return heal, odHeal, odThreshold, rate, special&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Mixer/method translation&lt;br /&gt;
local mixerNames = {&lt;br /&gt;
    Centrifuge   = &amp;#039;Центрифуга&amp;#039;,&lt;br /&gt;
    Electrolysis = &amp;#039;Электролиз&amp;#039;,&lt;br /&gt;
    Shake        = &amp;#039;Встряхивание&amp;#039;,&lt;br /&gt;
    Stir         = &amp;#039;Размешивание&amp;#039;,&lt;br /&gt;
    Holy         = &amp;#039;Освящение&amp;#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Build recipe HTML from a reagent&amp;#039;s recipes array&lt;br /&gt;
-- Returns recipeLine, methodTag&lt;br /&gt;
local function buildRecipe( r )&lt;br /&gt;
    local recipes = r.recipes&lt;br /&gt;
    if not recipes or not recipes[1] then return nil, nil end&lt;br /&gt;
    local rec = recipes[1]  -- use first recipe&lt;br /&gt;
&lt;br /&gt;
    local parts = {}&lt;br /&gt;
    for rId, rData in pairs( rec.reactants or {} ) do&lt;br /&gt;
        local amt = rData.amount or 1&lt;br /&gt;
        local name = rData.name or rId&lt;br /&gt;
        local cat = rData.catalyst and &amp;#039; &amp;lt;span class=&amp;quot;ss-recipe-cat&amp;quot;&amp;gt;кат.&amp;lt;/span&amp;gt;&amp;#039; or &amp;#039;&amp;#039;&lt;br /&gt;
        -- Wrap name in clickable span if the reagent exists in our data&lt;br /&gt;
        local nameHtml&lt;br /&gt;
        if reagents[rId] then&lt;br /&gt;
            nameHtml = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-link&amp;quot; data-target=&amp;quot;reagent-&amp;#039; .. rId .. &amp;#039;&amp;quot;&amp;gt;&amp;#039; .. name .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
        else&lt;br /&gt;
            nameHtml = name&lt;br /&gt;
        end&lt;br /&gt;
        parts[#parts + 1] = {&lt;br /&gt;
            text = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-amount&amp;quot;&amp;gt;&amp;#039; .. amt .. &amp;#039;&amp;lt;/span&amp;gt;\194\160&amp;#039; .. nameHtml .. cat,&lt;br /&gt;
            sort = name&lt;br /&gt;
        }&lt;br /&gt;
    end&lt;br /&gt;
    table.sort( parts, function(a,b) return a.sort &amp;lt; b.sort end )&lt;br /&gt;
&lt;br /&gt;
    local inputs = {}&lt;br /&gt;
    for _, p2 in ipairs( parts ) do&lt;br /&gt;
        inputs[#inputs + 1] = p2.text&lt;br /&gt;
    end&lt;br /&gt;
    local line = table.concat( inputs, &amp;#039; + &amp;#039; )&lt;br /&gt;
&lt;br /&gt;
    -- Products&lt;br /&gt;
    local prodParts = {}&lt;br /&gt;
    for pId, pData in pairs( rec.products or {} ) do&lt;br /&gt;
        local amt = pData.amount or 1&lt;br /&gt;
        local name = pData.name or pId&lt;br /&gt;
        prodParts[#prodParts + 1] = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-amount&amp;quot;&amp;gt;&amp;#039; .. amt&lt;br /&gt;
            .. &amp;#039;&amp;lt;/span&amp;gt;\194\160&amp;#039; .. name&lt;br /&gt;
    end&lt;br /&gt;
    if #prodParts &amp;gt; 0 then&lt;br /&gt;
        line = line .. &amp;#039; &amp;lt;span class=&amp;quot;ss-recipe-arrow&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;#039;&lt;br /&gt;
            .. table.concat( prodParts, &amp;#039; + &amp;#039; )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Build method tag(s)&lt;br /&gt;
    local tags = {}&lt;br /&gt;
&lt;br /&gt;
    -- Mixer type&lt;br /&gt;
    local mixer = rec.mixer&lt;br /&gt;
    if mixer then&lt;br /&gt;
        for _, m in ipairs( mixer ) do&lt;br /&gt;
            local label = mixerNames[m] or m&lt;br /&gt;
            tags[#tags + 1] = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-method ss-recipe-method--mixer&amp;quot;&amp;gt;&amp;#039; .. label .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Temperature conditions&lt;br /&gt;
    if rec.minTemp and rec.minTemp &amp;gt; 0 then&lt;br /&gt;
        tags[#tags + 1] = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-method ss-recipe-method--heat&amp;quot;&amp;gt;&amp;amp;#9650; &amp;#039; .. rec.minTemp .. &amp;#039; K&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    if rec.maxTemp and rec.maxTemp &amp;gt; 0 then&lt;br /&gt;
        tags[#tags + 1] = &amp;#039;&amp;lt;span class=&amp;quot;ss-recipe-method ss-recipe-method--cool&amp;quot;&amp;gt;&amp;amp;#9660; &amp;#039; .. rec.maxTemp .. &amp;#039; K&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local methodTag = #tags &amp;gt; 0 and table.concat( tags, &amp;#039; &amp;#039; ) or nil&lt;br /&gt;
    return line, methodTag&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Format a stat cell&lt;br /&gt;
local function statCell( label, value, wide )&lt;br /&gt;
    local cls = wide and &amp;#039;ss-reagent-stat ss-reagent-stat--wide&amp;#039; or &amp;#039;ss-reagent-stat&amp;#039;&lt;br /&gt;
    return &amp;#039;&amp;lt;div class=&amp;quot;&amp;#039; .. cls .. &amp;#039;&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
        .. &amp;#039;&amp;lt;span class=&amp;quot;ss-reagent-stat-k&amp;quot;&amp;gt;&amp;#039; .. label .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
        .. &amp;#039;&amp;lt;span class=&amp;quot;ss-reagent-stat-v&amp;quot;&amp;gt;&amp;#039; .. value .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
        .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Render a single reagent card&lt;br /&gt;
local function renderCard( id, r )&lt;br /&gt;
    local color = cssColor( r.color )&lt;br /&gt;
    local html = &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-card&amp;quot; id=&amp;quot;reagent-&amp;#039; .. id .. &amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
    if color then&lt;br /&gt;
        html = html .. &amp;#039; style=&amp;quot;--primary:&amp;#039; .. attr( color ) .. &amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    html = html .. &amp;#039;&amp;gt;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    -- head&lt;br /&gt;
    html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-head&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
    html = html .. &amp;#039;&amp;lt;span class=&amp;quot;ss-reagent-name&amp;quot;&amp;gt;&amp;#039; .. ( r.name or id ) .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    html = html .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    -- recipe (Elements come from dispenser, skip their decomposition recipes)&lt;br /&gt;
    local recipeLine, methodTag&lt;br /&gt;
    if r.group == &amp;#039;Elements&amp;#039; then&lt;br /&gt;
        recipeLine = &amp;#039;Хим-раздатчик&amp;#039;&lt;br /&gt;
    else&lt;br /&gt;
        recipeLine, methodTag = buildRecipe( r )&lt;br /&gt;
    end&lt;br /&gt;
    if recipeLine then&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-recipe&amp;quot;&amp;gt;&amp;#039; .. recipeLine .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
    if methodTag then&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-method&amp;quot;&amp;gt;&amp;#039; .. methodTag .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- description&lt;br /&gt;
    if r.desc and r.desc ~= &amp;#039;&amp;#039; then&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-desc&amp;quot;&amp;gt;&amp;#039; .. r.desc .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- parse metabolism&lt;br /&gt;
    local heal, odHeal, odThreshold, rate, special = parseEffects( r.metabolisms )&lt;br /&gt;
&lt;br /&gt;
    -- Build effects string&lt;br /&gt;
    local effectParts = {}&lt;br /&gt;
    for _, h in ipairs( heal ) do effectParts[#effectParts + 1] = h end&lt;br /&gt;
    for _, s in ipairs( special ) do effectParts[#effectParts + 1] = s end&lt;br /&gt;
    local effectStr = #effectParts &amp;gt; 0 and table.concat( effectParts, &amp;#039; · &amp;#039; ) or nil&lt;br /&gt;
&lt;br /&gt;
    -- Build overdose string&lt;br /&gt;
    local odParts = {}&lt;br /&gt;
    for _, h in ipairs( odHeal ) do odParts[#odParts + 1] = h end&lt;br /&gt;
    local odStr = #odParts &amp;gt; 0 and table.concat( odParts, &amp;#039; · &amp;#039; ) or nil&lt;br /&gt;
&lt;br /&gt;
    -- Stats bar&lt;br /&gt;
    local hasStats = rate or odThreshold or effectStr&lt;br /&gt;
    if hasStats then&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-stats&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
        if rate then&lt;br /&gt;
            html = html .. statCell( &amp;#039;МЕТАБ.&amp;#039;, rate .. &amp;#039;u/тик&amp;#039;, false )&lt;br /&gt;
        end&lt;br /&gt;
        if odThreshold then&lt;br /&gt;
            html = html .. statCell( &amp;#039;ПЕРЕДОЗ.&amp;#039;, odThreshold .. &amp;#039;u&amp;#039;, false )&lt;br /&gt;
        end&lt;br /&gt;
        if effectStr then&lt;br /&gt;
            html = html .. statCell( &amp;#039;ЭФФЕКТ&amp;#039;, effectStr, true )&lt;br /&gt;
        end&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Overdose effects as secondary row&lt;br /&gt;
    if odStr then&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-od&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;span class=&amp;quot;ss-reagent-stat-k&amp;quot;&amp;gt;ПРИ ПЕРЕДОЗ.&amp;lt;/span&amp;gt; &amp;#039;&lt;br /&gt;
        html = html .. odStr&lt;br /&gt;
        html = html .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    html = html .. &amp;#039;&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
    return html&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Collect and sort reagents for a group&lt;br /&gt;
local function getGroupEntries( groupId )&lt;br /&gt;
    local entries = {}&lt;br /&gt;
    for id, r in pairs( reagents ) do&lt;br /&gt;
        if r.group == groupId then&lt;br /&gt;
            entries[#entries + 1] = { id = id, data = r }&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    table.sort( entries, function(a, b)&lt;br /&gt;
        local aHas = a.data.group == &amp;#039;Elements&amp;#039; or (a.data.recipes ~= nil and a.data.recipes[1] ~= nil)&lt;br /&gt;
        local bHas = b.data.group == &amp;#039;Elements&amp;#039; or (b.data.recipes ~= nil and b.data.recipes[1] ~= nil)&lt;br /&gt;
        if aHas ~= bHas then return aHas end&lt;br /&gt;
        return (a.data.name or a.id) &amp;lt; (b.data.name or b.id)&lt;br /&gt;
    end )&lt;br /&gt;
    return entries&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ── public ──────────────────────────────────────────────────&lt;br /&gt;
&lt;br /&gt;
function p.group( frame )&lt;br /&gt;
    local args = frame.args&lt;br /&gt;
    local groupId = mw.text.trim( args[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    if groupId == &amp;#039;&amp;#039; then&lt;br /&gt;
        groupId = mw.text.trim( (frame:getParent().args or {})[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    end&lt;br /&gt;
    if groupId == &amp;#039;&amp;#039; then&lt;br /&gt;
        return &amp;#039;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;Reagents: group id required&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local entries = getGroupEntries( groupId )&lt;br /&gt;
    if #entries == 0 then&lt;br /&gt;
        return &amp;#039;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;Reagents: no entries for group &amp;quot;&amp;#039; .. mw.text.nowiki( groupId ) .. &amp;#039;&amp;quot;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local cards = {}&lt;br /&gt;
    for _, e in ipairs( entries ) do&lt;br /&gt;
        cards[#cards + 1] = renderCard( e.id, e.data )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return &amp;#039;&amp;lt;div class=&amp;quot;ss-reagent-grid&amp;quot;&amp;gt;\n&amp;#039; .. table.concat( cards, &amp;#039;\n&amp;#039; ) .. &amp;#039;\n&amp;lt;/div&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.card( frame )&lt;br /&gt;
    local args = frame.args&lt;br /&gt;
    local id = mw.text.trim( args[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    if id == &amp;#039;&amp;#039; then&lt;br /&gt;
        id = mw.text.trim( (frame:getParent().args or {})[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local r = reagents[id]&lt;br /&gt;
    if r then&lt;br /&gt;
        return renderCard( id, r )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Try finding by name&lt;br /&gt;
    for rId, rData in pairs( reagents ) do&lt;br /&gt;
        if rData.name == id then&lt;br /&gt;
            return renderCard( rId, rData )&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return &amp;#039;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;Reagents: &amp;quot;&amp;#039; .. mw.text.nowiki( id ) .. &amp;#039;&amp;quot; not found&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.count( frame )&lt;br /&gt;
    local groupId = mw.text.trim( frame.args[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    if groupId == &amp;#039;&amp;#039; then&lt;br /&gt;
        groupId = mw.text.trim( (frame:getParent().args or {})[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
    end&lt;br /&gt;
    local n = 0&lt;br /&gt;
    for _, r in pairs( reagents ) do&lt;br /&gt;
        if r.group == groupId then n = n + 1 end&lt;br /&gt;
    end&lt;br /&gt;
    return tostring( n )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>M 9SCO</name></author>
	</entry>
</feed>