<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://station14.ru/history/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats?feed=atom</id>
	<title>Модуль:MedicalStats - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://station14.ru/history/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats?feed=atom"/>
	<link rel="alternate" type="text/html" href="https://station14.ru/history/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats"/>
	<updated>2026-10-13T12:32:58Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://station14.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats&amp;diff=775588&amp;oldid=prev</id>
		<title>Halch в 10:00, 13 октября 2026</title>
		<link rel="alternate" type="text/html" href="https://station14.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats&amp;diff=775588&amp;oldid=prev"/>
		<updated>2026-10-13T10:00:16Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ru&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Предыдущая версия&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Версия от 13:00, 13 октября 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l81&quot;&gt;Строка 81:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 81:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;-- Перевод по ключу. Если перевода нет в выгрузке бота, возвращает nil,&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;-- и вызывающий код показывает ID прототипа вместо ключа вида «metabolizer-type-…».&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function loc(key)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function loc(key)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if not key then&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if not key then&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l89&quot;&gt;Строка 89:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Строка 91:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		v = v._value&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		v = v._value&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return v &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;or key&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return v&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Halch</name></author>
	</entry>
	<entry>
		<id>https://station14.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats&amp;diff=775587&amp;oldid=prev</id>
		<title>Halch: Новая страница: «-- Модуль:MedicalStats -- Автоматическая памятка медика: сколько лечит/наносит урона лекарство за 1u, -- с какой дозы начинается передозировка, что делают бинты, мази, койки и дефибриллятор. -- -- Все цифры берутся из выгрузки IanComradeBot: --   prototype/Reagent.json        — метаболиз...»</title>
		<link rel="alternate" type="text/html" href="https://station14.ru/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:MedicalStats&amp;diff=775587&amp;oldid=prev"/>
		<updated>2026-10-13T09:53:27Z</updated>

		<summary type="html">&lt;p&gt;Новая страница: «-- Модуль:MedicalStats -- Автоматическая памятка медика: сколько лечит/наносит урона лекарство за 1u, -- с какой дозы начинается передозировка, что делают бинты, мази, койки и дефибриллятор. -- -- Все цифры берутся из выгрузки IanComradeBot: --   prototype/Reagent.json        — метаболиз...»&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Модуль:MedicalStats&lt;br /&gt;
-- Автоматическая памятка медика: сколько лечит/наносит урона лекарство за 1u,&lt;br /&gt;
-- с какой дозы начинается передозировка, что делают бинты, мази, койки и дефибриллятор.&lt;br /&gt;
--&lt;br /&gt;
-- Все цифры берутся из выгрузки IanComradeBot:&lt;br /&gt;
--   prototype/Reagent.json        — метаболизм реагентов (эффекты, условия, metabolismRate)&lt;br /&gt;
--   component/Healing.json        — бинты, мази, наборы (компонент Healing)&lt;br /&gt;
--   component/HealOnBuckle.json   — кровати и койки (компонент HealOnBuckle)&lt;br /&gt;
--   component/Defibrillator.json  — дефибрилляторы&lt;br /&gt;
--   prototype/DamageType.json, prototype/DamageGroup.json — типы и группы урона&lt;br /&gt;
--   loc.json                      — русская локализация (ru-RU)&lt;br /&gt;
--   entity prototypes.json        — названия сущностей&lt;br /&gt;
--&lt;br /&gt;
-- Как считается «за 1u»: раз в секунду (тик метаболизма) из крови убирается&lt;br /&gt;
-- metabolismRate единиц реагента (по умолчанию 0.5u) и эффекты применяются один раз.&lt;br /&gt;
-- Значит, эффект на 1u = значение эффекта / metabolismRate.&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
local JsonPaths = require(&amp;#039;Module:JsonPaths&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
local lang = mw.getContentLanguage()&lt;br /&gt;
&lt;br /&gt;
-- В игре metabolismRate по умолчанию 0.5 (ReagentEffectsEntry.MetabolismRate).&lt;br /&gt;
-- Бот не выгружает значения по умолчанию, поэтому оно продублировано здесь.&lt;br /&gt;
local DEFAULT_METABOLISM_RATE = 0.5&lt;br /&gt;
&lt;br /&gt;
-- Значения DataField по умолчанию для эффектов, которые бот не выгружает.&lt;br /&gt;
local EFFECT_DEFAULTS = {&lt;br /&gt;
	ModifyBleed = { amount = -1 },&lt;br /&gt;
	ModifyBloodLevel = { amount = 1 },&lt;br /&gt;
	CleanBloodstream = { cleanseRate = 3 },&lt;br /&gt;
	EyeDamage = { amount = -1 },&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Порядок групп урона при выводе (только порядок, сами группы и их типы — из данных бота).&lt;br /&gt;
local GROUP_ORDER = { &amp;quot;Brute&amp;quot;, &amp;quot;Burn&amp;quot;, &amp;quot;Airloss&amp;quot;, &amp;quot;Toxin&amp;quot;, &amp;quot;Genetic&amp;quot;, &amp;quot;Metaphysical&amp;quot; }&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Загрузка данных (mw.loadData кэширует страницу на всё время рендера)&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local cache = {}&lt;br /&gt;
&lt;br /&gt;
local function load(rel)&lt;br /&gt;
	if cache[rel] == nil then&lt;br /&gt;
		local ok, data = pcall(mw.loadData, JsonPaths.get(rel))&lt;br /&gt;
		cache[rel] = (ok and type(data) == &amp;quot;table&amp;quot;) and data or {}&lt;br /&gt;
	end&lt;br /&gt;
	return cache[rel]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Пустая ли таблица. Функция next и оператор # для таблиц из mw.loadData не работают&lt;br /&gt;
-- (это прокси только для чтения), поэтому проверяем через pairs.&lt;br /&gt;
local function isEmpty(t)&lt;br /&gt;
	for _ in pairs(t) do&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Запись прототипа/компонента из файла формата { default = {...}, id = { Id = {...} } }.&lt;br /&gt;
-- Возвращает функцию-геттер поля с подстановкой default.&lt;br /&gt;
local function getEntry(rel, id)&lt;br /&gt;
	local data = load(rel)&lt;br /&gt;
	local ids = data.id or {}&lt;br /&gt;
	local entry = ids[id]&lt;br /&gt;
	if entry == nil then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local default = data.default or {}&lt;br /&gt;
	return function(field)&lt;br /&gt;
		local v = entry[field]&lt;br /&gt;
		-- бот иногда выгружает переопределение пустой таблицей — тогда берём default&lt;br /&gt;
		if v == nil or (type(v) == &amp;quot;table&amp;quot; and isEmpty(v)) then&lt;br /&gt;
			v = default[field]&lt;br /&gt;
		end&lt;br /&gt;
		return v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function loc(key)&lt;br /&gt;
	if not key then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local v = load(&amp;quot;loc.json&amp;quot;)[key]&lt;br /&gt;
	if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
		v = v._value&lt;br /&gt;
	end&lt;br /&gt;
	return v or key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ucfirst(s)&lt;br /&gt;
	return s and lang:ucfirst(s) or s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Форматирование&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Число с округлением до сотых, десятичная запятая по правилам ru.&lt;br /&gt;
local function num(x)&lt;br /&gt;
	x = math.floor(math.abs(x) * 100 + 0.5) / 100&lt;br /&gt;
	return lang:formatNum(x)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function good(x)&lt;br /&gt;
	return &amp;#039;&amp;lt;span style=&amp;quot;color:green&amp;quot;&amp;gt;&amp;#039; .. num(x) .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function bad(x)&lt;br /&gt;
	return &amp;#039;&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;&amp;#039; .. num(x) .. &amp;#039;&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function typeName(typeId)&lt;br /&gt;
	local dt = getEntry(&amp;quot;prototype/DamageType.json&amp;quot;, typeId)&lt;br /&gt;
	return ucfirst(loc(dt and dt(&amp;quot;name&amp;quot;))) or typeId&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function groupName(groupId)&lt;br /&gt;
	local dg = getEntry(&amp;quot;prototype/DamageGroup.json&amp;quot;, groupId)&lt;br /&gt;
	return ucfirst(loc(dg and dg(&amp;quot;name&amp;quot;))) or groupId&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function groupTypes(groupId)&lt;br /&gt;
	local dg = getEntry(&amp;quot;prototype/DamageGroup.json&amp;quot;, groupId)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	for _, t in ipairs(dg and dg(&amp;quot;damageTypes&amp;quot;) or {}) do&lt;br /&gt;
		list[#list + 1] = t&lt;br /&gt;
	end&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reagentName(id)&lt;br /&gt;
	local r = getEntry(&amp;quot;prototype/Reagent.json&amp;quot;, id)&lt;br /&gt;
	return ucfirst(loc(r and r(&amp;quot;name&amp;quot;))) or id&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reagentLink(id)&lt;br /&gt;
	return &amp;quot;[[Химия#chem_&amp;quot; .. id .. &amp;quot;|&amp;quot; .. reagentName(id) .. &amp;quot;]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function entityName(id)&lt;br /&gt;
	local overrides = load(&amp;quot;entity name overrides.json&amp;quot;)&lt;br /&gt;
	if overrides[id] then&lt;br /&gt;
		return overrides[id]&lt;br /&gt;
	end&lt;br /&gt;
	local ent = load(&amp;quot;entity prototypes.json&amp;quot;)[id]&lt;br /&gt;
	return ent and ent.name or id&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Урон: накопление и вывод&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Порядок ключей урона: группы по GROUP_ORDER, внутри — типы в порядке группы.&lt;br /&gt;
local function damageOrder()&lt;br /&gt;
	if cache._order then&lt;br /&gt;
		return cache._order&lt;br /&gt;
	end&lt;br /&gt;
	local order, n = {}, 0&lt;br /&gt;
	for _, g in ipairs(GROUP_ORDER) do&lt;br /&gt;
		n = n + 1&lt;br /&gt;
		order[&amp;quot;g:&amp;quot; .. g] = n&lt;br /&gt;
		for _, t in ipairs(groupTypes(g)) do&lt;br /&gt;
			n = n + 1&lt;br /&gt;
			order[&amp;quot;t:&amp;quot; .. t] = n&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	cache._order = order&lt;br /&gt;
	return order&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- acc = { list = { key, ... }, val = { key = число } }&lt;br /&gt;
local function newAcc()&lt;br /&gt;
	return { list = {}, val = {} }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accAdd(acc, key, amount)&lt;br /&gt;
	if amount == 0 then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	if acc.val[key] == nil then&lt;br /&gt;
		acc.list[#acc.list + 1] = key&lt;br /&gt;
		acc.val[key] = 0&lt;br /&gt;
	end&lt;br /&gt;
	acc.val[key] = acc.val[key] + amount&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- DamageSpecifier: { types = {...}, groups = {...} }. Группа делится поровну между её типами.&lt;br /&gt;
local function addSpecifier(acc, spec, k)&lt;br /&gt;
	if type(spec) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	for t, v in pairs(spec.types or {}) do&lt;br /&gt;
		accAdd(acc, &amp;quot;t:&amp;quot; .. t, v * k)&lt;br /&gt;
	end&lt;br /&gt;
	for g, v in pairs(spec.groups or {}) do&lt;br /&gt;
		local types = groupTypes(g)&lt;br /&gt;
		for _, t in ipairs(types) do&lt;br /&gt;
			accAdd(acc, &amp;quot;t:&amp;quot; .. t, v * k / #types)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Название ключа урона с числом.&lt;br /&gt;
-- Тип: «Ушибы 9».&lt;br /&gt;
-- Группа (EvenHealthChange): «Механические 3 (по 1 на Ушибы, Порезы, Уколы)» —&lt;br /&gt;
-- в игре лечение группы делится поровну между её типами (DamageableSystem.HealEvenly),&lt;br /&gt;
-- а если часть типов не повреждена, остаток уходит в повреждённые.&lt;br /&gt;
local function keyText(key, v, colorize)&lt;br /&gt;
	local kind, id = key:match(&amp;quot;^(%a):(.+)$&amp;quot;)&lt;br /&gt;
	if kind ~= &amp;quot;g&amp;quot; then&lt;br /&gt;
		return typeName(id) .. &amp;quot; &amp;quot; .. colorize(v)&lt;br /&gt;
	end&lt;br /&gt;
	local types = groupTypes(id)&lt;br /&gt;
	local text = groupName(id) .. &amp;quot; &amp;quot; .. colorize(v)&lt;br /&gt;
	if #types &amp;gt; 1 then&lt;br /&gt;
		local names = {}&lt;br /&gt;
		for _, t in ipairs(types) do&lt;br /&gt;
			names[#names + 1] = typeName(t)&lt;br /&gt;
		end&lt;br /&gt;
		text = text .. &amp;quot; (по &amp;quot; .. num(v / #types) .. &amp;quot; на &amp;quot; .. table.concat(names, &amp;quot;, &amp;quot;) .. &amp;quot;)&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Возвращает строки «Лечит: …» и «Наносит: …».&lt;br /&gt;
local function renderDamage(acc)&lt;br /&gt;
	local order = damageOrder()&lt;br /&gt;
	local keys = {}&lt;br /&gt;
	for _, key in ipairs(acc.list) do&lt;br /&gt;
		if math.abs(acc.val[key]) &amp;gt;= 0.005 then&lt;br /&gt;
			keys[#keys + 1] = key&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(keys, function(a, b)&lt;br /&gt;
		local oa, ob = order[a] or 1000, order[b] or 1000&lt;br /&gt;
		if oa ~= ob then&lt;br /&gt;
			return oa &amp;lt; ob&lt;br /&gt;
		end&lt;br /&gt;
		return a &amp;lt; b&lt;br /&gt;
	end)&lt;br /&gt;
&lt;br /&gt;
	local heals, deals = {}, {}&lt;br /&gt;
	for _, key in ipairs(keys) do&lt;br /&gt;
		local v = acc.val[key]&lt;br /&gt;
		if v &amp;lt; 0 then&lt;br /&gt;
			heals[#heals + 1] = keyText(key, v, good)&lt;br /&gt;
		else&lt;br /&gt;
			deals[#deals + 1] = keyText(key, v, bad)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local out = {}&lt;br /&gt;
	if #heals &amp;gt; 0 then&lt;br /&gt;
		out[#out + 1] = &amp;quot;лечит: &amp;quot; .. table.concat(heals, &amp;quot;, &amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if #deals &amp;gt; 0 then&lt;br /&gt;
		out[#out + 1] = &amp;quot;наносит: &amp;quot; .. table.concat(deals, &amp;quot;, &amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Реагенты&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Разбор записи эффекта бота: { [&amp;quot;!type:HealthChange&amp;quot;] = { ... } } -&amp;gt; &amp;quot;HealthChange&amp;quot;, { ... }&lt;br /&gt;
local function unwrap(obj)&lt;br /&gt;
	for key, val in pairs(obj) do&lt;br /&gt;
		local t = type(key) == &amp;quot;string&amp;quot; and key:match(&amp;quot;^!type:(.+)$&amp;quot;)&lt;br /&gt;
		if t then&lt;br /&gt;
			return t, val&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Старые имена эффектов (до переезда на EntityEffects; так их пока выгружает бот для Goob)&lt;br /&gt;
local EFFECT_ALIASES = {&lt;br /&gt;
	ChemVomit = &amp;quot;Vomit&amp;quot;,&lt;br /&gt;
	ChemHealEyeDamage = &amp;quot;EyeDamage&amp;quot;,&lt;br /&gt;
	ChemCleanBloodstream = &amp;quot;CleanBloodstream&amp;quot;,&lt;br /&gt;
	ModifyBleedAmount = &amp;quot;ModifyBleed&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function field(effType, eff, name)&lt;br /&gt;
	local v = eff[name]&lt;br /&gt;
	if v == nil and EFFECT_DEFAULTS[effType] then&lt;br /&gt;
		v = EFFECT_DEFAULTS[effType][name]&lt;br /&gt;
	end&lt;br /&gt;
	return v&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Условия эффекта: { self = {min=, max=}, others = { &amp;quot;текст&amp;quot;, ... } }&lt;br /&gt;
-- Старые/форковые имена условий -&amp;gt; имена, которые понимает модуль.&lt;br /&gt;
-- ReagentThreshold, Temperature, TotalDamage, OrganType — старая схема (так их пока выгружает бот для Goob).&lt;br /&gt;
local CONDITION_ALIASES = {&lt;br /&gt;
	ReagentThreshold = &amp;quot;ReagentCondition&amp;quot;,&lt;br /&gt;
	Temperature = &amp;quot;TemperatureCondition&amp;quot;,&lt;br /&gt;
	TotalDamage = &amp;quot;TotalDamageCondition&amp;quot;,&lt;br /&gt;
	OrganType = &amp;quot;MetabolizerTypeCondition&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function parseConditions(eff, reagentId)&lt;br /&gt;
	local res = { others = {} }&lt;br /&gt;
	for _, c in ipairs(eff.conditions or {}) do&lt;br /&gt;
		local ct, cond = unwrap(c)&lt;br /&gt;
		ct = CONDITION_ALIASES[ct] or ct&lt;br /&gt;
		cond = cond or {}&lt;br /&gt;
		local inv = cond.inverted&lt;br /&gt;
		if ct == &amp;quot;ReagentCondition&amp;quot; and cond.reagent == nil then&lt;br /&gt;
			-- в ReagentThreshold реагент по умолчанию — сам метаболизируемый реагент&lt;br /&gt;
			cond = { reagent = reagentId, min = cond.min, max = cond.max, inverted = cond.inverted }&lt;br /&gt;
		end&lt;br /&gt;
		if ct == &amp;quot;ReagentCondition&amp;quot; and cond.reagent == reagentId and not inv then&lt;br /&gt;
			if cond.min and cond.min &amp;gt; 0 then&lt;br /&gt;
				res.selfMin = cond.min&lt;br /&gt;
			end&lt;br /&gt;
			if cond.max then&lt;br /&gt;
				res.selfMax = cond.max&lt;br /&gt;
			end&lt;br /&gt;
		elseif ct == &amp;quot;ReagentCondition&amp;quot; then&lt;br /&gt;
			local what = reagentLink(cond.reagent)&lt;br /&gt;
			local text&lt;br /&gt;
			if cond.min and cond.min &amp;gt; 1 then&lt;br /&gt;
				text = &amp;quot;если в крови от &amp;quot; .. num(cond.min) .. &amp;quot;u вещества &amp;quot; .. what&lt;br /&gt;
			else&lt;br /&gt;
				text = &amp;quot;если в крови есть &amp;quot; .. what&lt;br /&gt;
			end&lt;br /&gt;
			if cond.max then&lt;br /&gt;
				text = text .. &amp;quot; (не больше &amp;quot; .. num(cond.max) .. &amp;quot;u)&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			res.others[#res.others + 1] = inv and (&amp;quot;кроме случая: &amp;quot; .. text) or text&lt;br /&gt;
		elseif ct == &amp;quot;MobStateCondition&amp;quot; then&lt;br /&gt;
			local state = cond.mobstate or &amp;quot;Alive&amp;quot;&lt;br /&gt;
			local names = {&lt;br /&gt;
				Alive = &amp;quot;не в критическом состоянии&amp;quot;,&lt;br /&gt;
				Critical = &amp;quot;в критическом состоянии&amp;quot;,&lt;br /&gt;
				Dead = &amp;quot;у мёртвых&amp;quot;,&lt;br /&gt;
			}&lt;br /&gt;
			local s = names[state] or state&lt;br /&gt;
			res.others[#res.others + 1] = (inv and &amp;quot;только не &amp;quot; or &amp;quot;только &amp;quot;) .. s&lt;br /&gt;
		elseif ct == &amp;quot;TemperatureCondition&amp;quot; then&lt;br /&gt;
			local parts = {}&lt;br /&gt;
			if cond.min then&lt;br /&gt;
				parts[#parts + 1] = &amp;quot;выше &amp;quot; .. num(cond.min) .. &amp;quot; K (&amp;quot; .. (cond.min &amp;lt; 273.15 and &amp;quot;−&amp;quot; or &amp;quot;&amp;quot;) .. num(cond.min - 273.15) .. &amp;quot; °C)&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			if cond.max then&lt;br /&gt;
				parts[#parts + 1] = &amp;quot;ниже &amp;quot; .. num(cond.max) .. &amp;quot; K (&amp;quot; .. (cond.max &amp;lt; 273.15 and &amp;quot;−&amp;quot; or &amp;quot;&amp;quot;) .. num(cond.max - 273.15) .. &amp;quot; °C)&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			res.others[#res.others + 1] = &amp;quot;при температуре тела &amp;quot; .. table.concat(parts, &amp;quot; и &amp;quot;)&lt;br /&gt;
		elseif ct == &amp;quot;TotalDamageCondition&amp;quot; then&lt;br /&gt;
			local parts = {}&lt;br /&gt;
			if cond.min then&lt;br /&gt;
				parts[#parts + 1] = &amp;quot;от &amp;quot; .. num(cond.min)&lt;br /&gt;
			end&lt;br /&gt;
			if cond.max then&lt;br /&gt;
				parts[#parts + 1] = &amp;quot;до &amp;quot; .. num(cond.max)&lt;br /&gt;
			end&lt;br /&gt;
			res.others[#res.others + 1] = (inv and &amp;quot;кроме случая: &amp;quot; or &amp;quot;&amp;quot;) .. &amp;quot;при общем уроне &amp;quot; .. table.concat(parts, &amp;quot; &amp;quot;)&lt;br /&gt;
		elseif ct == &amp;quot;MetabolizerTypeCondition&amp;quot; then&lt;br /&gt;
			-- Исключения для отдельных рас (например, «кроме Yowie» в Goob) в памятке не показываем,&lt;br /&gt;
			-- а «только для …» показываем.&lt;br /&gt;
			local excluded = inv or cond.shouldHave == false&lt;br /&gt;
			if not excluded then&lt;br /&gt;
				local types = cond.type&lt;br /&gt;
				if type(types) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
					types = { types }&lt;br /&gt;
				end&lt;br /&gt;
				local names = {}&lt;br /&gt;
				for _, t in ipairs(types) do&lt;br /&gt;
					local mt = getEntry(&amp;quot;prototype/MetabolizerType.json&amp;quot;, t)&lt;br /&gt;
					names[#names + 1] = ucfirst(loc(mt and mt(&amp;quot;name&amp;quot;))) or t&lt;br /&gt;
				end&lt;br /&gt;
				res.others[#res.others + 1] = &amp;quot;только для метаболизма: &amp;quot; .. table.concat(names, &amp;quot;, &amp;quot;)&lt;br /&gt;
			end&lt;br /&gt;
		elseif ct then&lt;br /&gt;
			res.others[#res.others + 1] = &amp;quot;при особом условии (&amp;quot; .. ct .. &amp;quot;)&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Текст «прочих» (не уронных) эффектов. k — множитель «за 1u».&lt;br /&gt;
local function otherEffectText(effType, eff, k, reagentId)&lt;br /&gt;
	if effType == &amp;quot;ModifyBleed&amp;quot; then&lt;br /&gt;
		local a = field(effType, eff, &amp;quot;amount&amp;quot;) * k&lt;br /&gt;
		if a &amp;lt; 0 then&lt;br /&gt;
			return &amp;quot;уменьшает кровотечение на &amp;quot; .. good(a)&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;quot;усиливает кровотечение на &amp;quot; .. bad(a)&lt;br /&gt;
	elseif effType == &amp;quot;ModifyBloodLevel&amp;quot; then&lt;br /&gt;
		local a = field(effType, eff, &amp;quot;amount&amp;quot;) * k&lt;br /&gt;
		if a &amp;gt; 0 then&lt;br /&gt;
			return &amp;quot;восполняет кровь на &amp;quot; .. good(a) .. &amp;quot;u&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;quot;уменьшает объём крови на &amp;quot; .. bad(a) .. &amp;quot;u&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;CleanBloodstream&amp;quot; then&lt;br /&gt;
		local a = field(effType, eff, &amp;quot;cleanseRate&amp;quot;) * k&lt;br /&gt;
		return &amp;quot;выводит из крови по &amp;quot; .. good(a) .. &amp;quot;u остальных веществ&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;AdjustReagent&amp;quot; then&lt;br /&gt;
		local a = (eff.amount or 0) * k&lt;br /&gt;
		if eff.reagent == reagentId then&lt;br /&gt;
			return nil -- сам себя выводит/добавляет — для памятки не важно&lt;br /&gt;
		end&lt;br /&gt;
		if a &amp;lt; 0 then&lt;br /&gt;
			return &amp;quot;выводит &amp;quot; .. good(a) .. &amp;quot;u вещества &amp;quot; .. reagentLink(eff.reagent)&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;quot;добавляет &amp;quot; .. bad(a) .. &amp;quot;u вещества &amp;quot; .. reagentLink(eff.reagent)&lt;br /&gt;
	elseif effType == &amp;quot;EyeDamage&amp;quot; then&lt;br /&gt;
		local a = field(effType, eff, &amp;quot;amount&amp;quot;) * k&lt;br /&gt;
		if a &amp;lt; 0 then&lt;br /&gt;
			return &amp;quot;лечит повреждение глаз на &amp;quot; .. good(a)&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;quot;повреждает глаза на &amp;quot; .. bad(a)&lt;br /&gt;
	elseif effType == &amp;quot;CureZombieInfection&amp;quot; then&lt;br /&gt;
		if eff.innoculate then&lt;br /&gt;
			return &amp;quot;излечивает [[зомби]]-вирус и даёт иммунитет к нему&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;quot;излечивает [[зомби]]-вирус&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;AdjustTemperature&amp;quot; then&lt;br /&gt;
		return (eff.amount or 0) &amp;lt; 0 and &amp;quot;охлаждает тело&amp;quot; or &amp;quot;нагревает тело&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;ModifyKnockdown&amp;quot; or effType == &amp;quot;ModifyStatusEffect&amp;quot; or effType == &amp;quot;ModifyParalysis&amp;quot; then&lt;br /&gt;
		if eff.type == &amp;quot;Remove&amp;quot; then&lt;br /&gt;
			return &amp;quot;сокращает оглушение и сбивание с ног&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		return nil&lt;br /&gt;
	elseif effType == &amp;quot;SuppressPain&amp;quot; then&lt;br /&gt;
		return &amp;quot;подавляет боль&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;Vomit&amp;quot; then&lt;br /&gt;
		return &amp;quot;рвота&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;Jitter&amp;quot; then&lt;br /&gt;
		return &amp;quot;дрожь&amp;quot;&lt;br /&gt;
	elseif effType == &amp;quot;Drunk&amp;quot; then&lt;br /&gt;
		return &amp;quot;опьянение&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	-- PopupMessage, Emote, SatiateHunger и т.п. в памятке не показываем&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isDamageEffect(effType)&lt;br /&gt;
	return effType == &amp;quot;HealthChange&amp;quot; or effType == &amp;quot;EvenHealthChange&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Есть ли у уронного эффекта положительный (вредный) урон&lt;br /&gt;
local function hasHarm(effType, eff)&lt;br /&gt;
	local d = eff.damage or {}&lt;br /&gt;
	if effType == &amp;quot;EvenHealthChange&amp;quot; then&lt;br /&gt;
		for _, v in pairs(d) do&lt;br /&gt;
			if v &amp;gt; 0 then return true end&lt;br /&gt;
		end&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	for _, part in pairs({ d.types or {}, d.groups or {} }) do&lt;br /&gt;
		for _, v in pairs(part) do&lt;br /&gt;
			if v &amp;gt; 0 then return true end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Эффекты, которые при пороговой дозе являются полезными (а не передозировкой)&lt;br /&gt;
local BENEFICIAL = { CureZombieInfection = true, SuppressPain = true, EyeDamage = true, ModifyBleed = true, ModifyBloodLevel = true }&lt;br /&gt;
&lt;br /&gt;
-- Группа эффектов с одинаковыми условиями: { label = &amp;quot;…&amp;quot;, acc = урон, extra = { &amp;quot;…&amp;quot; } }&lt;br /&gt;
local function bucketFor(buckets, label)&lt;br /&gt;
	for _, b in ipairs(buckets) do&lt;br /&gt;
		if b.label == label then&lt;br /&gt;
			return b&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local b = { label = label, acc = newAcc(), extra = {} }&lt;br /&gt;
	buckets[#buckets + 1] = b&lt;br /&gt;
	return b&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function addExtra(b, text)&lt;br /&gt;
	if not text then return end&lt;br /&gt;
	for _, e in ipairs(b.extra) do&lt;br /&gt;
		if e == text then return end&lt;br /&gt;
	end&lt;br /&gt;
	b.extra[#b.extra + 1] = text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderBucket(b)&lt;br /&gt;
	local parts = renderDamage(b.acc)&lt;br /&gt;
	for _, e in ipairs(b.extra) do&lt;br /&gt;
		parts[#parts + 1] = e&lt;br /&gt;
	end&lt;br /&gt;
	if #parts == 0 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local text = table.concat(parts, &amp;quot;; &amp;quot;)&lt;br /&gt;
	if b.label ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		return ucfirst(b.label) .. &amp;quot;: &amp;quot; .. text .. &amp;quot;.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return ucfirst(text) .. &amp;quot;.&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Метаболизм реагента: Bloodstream, иначе первый найденный (например, Digestion у напитков)&lt;br /&gt;
local function pickMetabolism(metabolisms)&lt;br /&gt;
	if type(metabolisms) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Bloodstream — Corvax/апстрим, Medicine — группа метаболизма лекарств в Goob&lt;br /&gt;
	for _, key in ipairs({ &amp;quot;Bloodstream&amp;quot;, &amp;quot;Medicine&amp;quot; }) do&lt;br /&gt;
		if metabolisms[key] then&lt;br /&gt;
			return metabolisms[key]&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local firstKey&lt;br /&gt;
	for key in pairs(metabolisms) do&lt;br /&gt;
		if not firstKey or key &amp;lt; firstKey then&lt;br /&gt;
			firstKey = key&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return firstKey and metabolisms[firstKey]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Полный разбор реагента&lt;br /&gt;
local function analyzeReagent(id)&lt;br /&gt;
	local r = getEntry(&amp;quot;prototype/Reagent.json&amp;quot;, id)&lt;br /&gt;
	if not r then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local m = pickMetabolism(r(&amp;quot;metabolisms&amp;quot;))&lt;br /&gt;
	local res = { rate = DEFAULT_METABOLISM_RATE, normal = {}, overdose = {}, threshold = nil }&lt;br /&gt;
	if not m then&lt;br /&gt;
		return res&lt;br /&gt;
	end&lt;br /&gt;
	res.rate = m.metabolismRate or DEFAULT_METABOLISM_RATE&lt;br /&gt;
	local k = 1 / res.rate&lt;br /&gt;
&lt;br /&gt;
	bucketFor(res.normal, &amp;quot;&amp;quot;) -- безусловные эффекты всегда первыми&lt;br /&gt;
&lt;br /&gt;
	local harmThreshold, anyThreshold&lt;br /&gt;
	for _, wrapped in ipairs(m.effects or {}) do&lt;br /&gt;
		local effType, eff = unwrap(wrapped)&lt;br /&gt;
		effType = EFFECT_ALIASES[effType] or effType&lt;br /&gt;
		if effType and eff then&lt;br /&gt;
			local cond = parseConditions(eff, id)&lt;br /&gt;
			local label = table.concat(cond.others, &amp;quot;, &amp;quot;)&lt;br /&gt;
			if cond.selfMax then&lt;br /&gt;
				label = (label ~= &amp;quot;&amp;quot; and (label .. &amp;quot;, &amp;quot;) or &amp;quot;&amp;quot;) .. &amp;quot;пока в крови не больше &amp;quot; .. num(cond.selfMax) .. &amp;quot;u&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			if eff.probability and eff.probability &amp;lt; 1 then&lt;br /&gt;
				label = (label ~= &amp;quot;&amp;quot; and (label .. &amp;quot;, &amp;quot;) or &amp;quot;&amp;quot;) .. &amp;quot;с шансом &amp;quot; .. num(eff.probability * 100) .. &amp;quot;% в секунду&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local isOverdose = false&lt;br /&gt;
			if cond.selfMin then&lt;br /&gt;
				if isDamageEffect(effType) then&lt;br /&gt;
					isOverdose = hasHarm(effType, eff)&lt;br /&gt;
				else&lt;br /&gt;
					isOverdose = not BENEFICIAL[effType]&lt;br /&gt;
				end&lt;br /&gt;
				if not isOverdose then&lt;br /&gt;
					label = (label ~= &amp;quot;&amp;quot; and (label .. &amp;quot;, &amp;quot;) or &amp;quot;&amp;quot;) .. &amp;quot;от &amp;quot; .. num(cond.selfMin) .. &amp;quot;u в крови&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local b&lt;br /&gt;
			if isOverdose then&lt;br /&gt;
				local odLabel = &amp;quot;от &amp;quot; .. num(cond.selfMin) .. &amp;quot;u&amp;quot; .. (label ~= &amp;quot;&amp;quot; and (&amp;quot;, &amp;quot; .. label) or &amp;quot;&amp;quot;)&lt;br /&gt;
				b = bucketFor(res.overdose, odLabel)&lt;br /&gt;
				b.min = cond.selfMin&lt;br /&gt;
				if isDamageEffect(effType) and (not harmThreshold or cond.selfMin &amp;lt; harmThreshold) then&lt;br /&gt;
					harmThreshold = cond.selfMin&lt;br /&gt;
				end&lt;br /&gt;
				if not anyThreshold or cond.selfMin &amp;lt; anyThreshold then&lt;br /&gt;
					anyThreshold = cond.selfMin&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				b = bucketFor(res.normal, label)&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			-- Передозировка показывается «в секунду» (пока доза выше порога), остальное — «за 1u»&lt;br /&gt;
			local mult = isOverdose and 1 or k&lt;br /&gt;
			if effType == &amp;quot;HealthChange&amp;quot; then&lt;br /&gt;
				addSpecifier(b.acc, eff.damage, mult)&lt;br /&gt;
			elseif effType == &amp;quot;EvenHealthChange&amp;quot; then&lt;br /&gt;
				for g, v in pairs(eff.damage or {}) do&lt;br /&gt;
					accAdd(b.acc, &amp;quot;g:&amp;quot; .. g, v * mult)&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				addExtra(b, otherEffectText(effType, eff, mult, id))&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.sort(res.overdose, function(a, b) return a.min &amp;lt; b.min end)&lt;br /&gt;
	res.threshold = harmThreshold or anyThreshold&lt;br /&gt;
	return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reagentEffectsText(id, a)&lt;br /&gt;
	local lines = {}&lt;br /&gt;
	for _, b in ipairs(a.normal) do&lt;br /&gt;
		local t = renderBucket(b)&lt;br /&gt;
		if t then&lt;br /&gt;
			lines[#lines + 1] = t&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if #a.overdose &amp;gt; 0 then&lt;br /&gt;
		local od = {}&lt;br /&gt;
		for _, b in ipairs(a.overdose) do&lt;br /&gt;
			local t = renderBucket(b)&lt;br /&gt;
			if t then&lt;br /&gt;
				od[#od + 1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. t&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if #od &amp;gt; 0 then&lt;br /&gt;
			lines[#lines + 1] = &amp;quot;&amp;lt;small&amp;gt;&amp;#039;&amp;#039;&amp;#039;Передозировка&amp;#039;&amp;#039;&amp;#039; (каждую секунду, пока доза выше порога):&amp;quot; .. table.concat(od) .. &amp;quot;&amp;lt;/small&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(lines, &amp;quot;&amp;lt;br&amp;gt;&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spriteCell(img, label)&lt;br /&gt;
	return &amp;#039;! &amp;lt;span class=sprite&amp;gt;[[Файл:&amp;#039; .. img .. &amp;#039;|64px|центр]]&amp;lt;/span&amp;gt; &amp;lt;center&amp;gt;&amp;#039; .. label .. &amp;#039;&amp;lt;/center&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Публичные функции&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Строка таблицы памятки для реагента.&lt;br /&gt;
-- {{#invoke:MedicalStats|reagent|Bicaridine|img=bicaridine.png|note=…}}&lt;br /&gt;
function p.reagent(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local id = mw.text.trim(args[1] or &amp;quot;&amp;quot;)&lt;br /&gt;
	local a = analyzeReagent(id)&lt;br /&gt;
	if not a then&lt;br /&gt;
		return &amp;quot;|-\n| colspan=\&amp;quot;3\&amp;quot; | &amp;lt;span class=\&amp;quot;error\&amp;quot;&amp;gt;Реагент «&amp;quot; .. id .. &amp;quot;» не найден в выгрузке бота&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local dose = a.threshold and (num(a.threshold) .. &amp;quot;u&amp;quot;) or &amp;quot;Нет&amp;quot;&lt;br /&gt;
	local text = reagentEffectsText(id, a)&lt;br /&gt;
	text = (text ~= &amp;quot;&amp;quot; and (text .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;) or &amp;quot;&amp;quot;) .. &amp;quot;&amp;lt;small&amp;gt;Усваивается по &amp;quot; .. num(a.rate) .. &amp;quot;u в секунду.&amp;lt;/small&amp;gt;&amp;quot;&lt;br /&gt;
	if args.note then&lt;br /&gt;
		text = text .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. args.note&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat({&lt;br /&gt;
		&amp;quot;|-&amp;quot;,&lt;br /&gt;
		spriteCell(args.img or (id .. &amp;quot;.png&amp;quot;), &amp;quot;[[Химия#chem_&amp;quot; .. id .. &amp;quot;|&amp;quot; .. (args.name or reagentName(id)) .. &amp;quot;]]&amp;quot;),&lt;br /&gt;
		&amp;quot;| &amp;lt;center&amp;gt;&amp;quot; .. dose .. &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;,&lt;br /&gt;
		&amp;quot;| &amp;quot; .. text,&lt;br /&gt;
	}, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Только порог передозировки: {{#invoke:MedicalStats|overdose|Bicaridine}} -&amp;gt; 15&lt;br /&gt;
function p.overdose(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local a = analyzeReagent(mw.text.trim(args[1] or &amp;quot;&amp;quot;))&lt;br /&gt;
	return (a and a.threshold) and num(a.threshold) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Только описание эффектов реагента: {{#invoke:MedicalStats|reagentEffects|Bicaridine}}&lt;br /&gt;
function p.reagentEffects(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local id = mw.text.trim(args[1] or &amp;quot;&amp;quot;)&lt;br /&gt;
	local a = analyzeReagent(id)&lt;br /&gt;
	return a and reagentEffectsText(id, a) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Строка таблицы для предмета с компонентом Healing (бинты, мази, наборы, пакет крови).&lt;br /&gt;
-- {{#invoke:MedicalStats|healing|Brutepack|img=Brutepack.png}}&lt;br /&gt;
function p.healing(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local id = mw.text.trim(args[1] or &amp;quot;&amp;quot;)&lt;br /&gt;
	local h = getEntry(&amp;quot;component/Healing.json&amp;quot;, id)&lt;br /&gt;
	if not h then&lt;br /&gt;
		return &amp;quot;|-\n| colspan=\&amp;quot;3\&amp;quot; | &amp;lt;span class=\&amp;quot;error\&amp;quot;&amp;gt;У «&amp;quot; .. id .. &amp;quot;» нет компонента Healing в выгрузке бота&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local acc = newAcc()&lt;br /&gt;
	addSpecifier(acc, h(&amp;quot;damage&amp;quot;), 1)&lt;br /&gt;
	local parts = renderDamage(acc)&lt;br /&gt;
&lt;br /&gt;
	local bleed = h(&amp;quot;bloodlossModifier&amp;quot;) or 0&lt;br /&gt;
	if bleed &amp;lt; 0 then&lt;br /&gt;
		parts[#parts + 1] = &amp;quot;уменьшает кровотечение на &amp;quot; .. good(bleed)&lt;br /&gt;
	elseif bleed &amp;gt; 0 then&lt;br /&gt;
		parts[#parts + 1] = &amp;quot;усиливает кровотечение на &amp;quot; .. bad(bleed)&lt;br /&gt;
	end&lt;br /&gt;
	local blood = h(&amp;quot;modifyBloodLevel&amp;quot;) or 0&lt;br /&gt;
	-- В Goob знак инвертирован: HealingSystem вызывает TryModifyBloodLevel(-ModifyBloodLevel)&lt;br /&gt;
	-- (Goob-Station, Content.Shared/Medical/Healing/HealingSystem.cs, «Goobedit»).&lt;br /&gt;
	if JsonPaths.project() == &amp;quot;Goob&amp;quot; then&lt;br /&gt;
		blood = -blood&lt;br /&gt;
	end&lt;br /&gt;
	if blood &amp;gt; 0 then&lt;br /&gt;
		parts[#parts + 1] = &amp;quot;восполняет кровь на &amp;quot; .. good(blood) .. &amp;quot;u&amp;quot;&lt;br /&gt;
	elseif blood &amp;lt; 0 then&lt;br /&gt;
		parts[#parts + 1] = &amp;quot;уменьшает объём крови на &amp;quot; .. bad(blood) .. &amp;quot;u&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local text = ucfirst(table.concat(parts, &amp;quot;; &amp;quot;)) .. &amp;quot;.&amp;quot;&lt;br /&gt;
	local delay = h(&amp;quot;delay&amp;quot;)&lt;br /&gt;
	if delay then&lt;br /&gt;
		text = text .. &amp;quot;&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Наложение занимает &amp;quot; .. num(delay) .. &amp;quot; с&amp;quot;&lt;br /&gt;
			.. (((h(&amp;quot;selfHealPenaltyMultiplier&amp;quot;) or 0) &amp;gt; 1) and &amp;quot;, на себе — дольше&amp;quot; or &amp;quot;&amp;quot;) .. &amp;quot;.&amp;lt;/small&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if args.note then&lt;br /&gt;
		text = text .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. args.note&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat({&lt;br /&gt;
		&amp;quot;|-&amp;quot;,&lt;br /&gt;
		spriteCell(args.img or (id .. &amp;quot;.png&amp;quot;), args.name or entityName(id)),&lt;br /&gt;
		&amp;quot;| &amp;lt;center&amp;gt;Нет&amp;lt;/center&amp;gt;&amp;quot;,&lt;br /&gt;
		&amp;quot;| &amp;quot; .. text,&lt;br /&gt;
	}, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Строка таблицы для дефибриллятора.&lt;br /&gt;
-- {{#invoke:MedicalStats|defib|Defibrillator|img=Defib.png|note=…}}&lt;br /&gt;
function p.defib(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local id = mw.text.trim(args[1] or &amp;quot;Defibrillator&amp;quot;)&lt;br /&gt;
	local d = getEntry(&amp;quot;component/Defibrillator.json&amp;quot;, id)&lt;br /&gt;
	if not d then&lt;br /&gt;
		return &amp;quot;|-\n| colspan=\&amp;quot;3\&amp;quot; | &amp;lt;span class=\&amp;quot;error\&amp;quot;&amp;gt;У «&amp;quot; .. id .. &amp;quot;» нет компонента Defibrillator в выгрузке бота&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local acc = newAcc()&lt;br /&gt;
	addSpecifier(acc, d(&amp;quot;zapHeal&amp;quot;), 1)&lt;br /&gt;
	local parts = renderDamage(acc)&lt;br /&gt;
	local zap = d(&amp;quot;zapDamage&amp;quot;)&lt;br /&gt;
	if zap and zap &amp;gt; 0 then&lt;br /&gt;
		parts[#parts + 1] = &amp;quot;бьёт током на &amp;quot; .. bad(zap) .. &amp;quot; урона&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	local text = &amp;quot;За разряд &amp;quot; .. table.concat(parts, &amp;quot;; &amp;quot;) .. &amp;quot;.&amp;quot;&lt;br /&gt;
	if args.note then&lt;br /&gt;
		text = text .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. args.note&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat({&lt;br /&gt;
		&amp;quot;|-&amp;quot;,&lt;br /&gt;
		spriteCell(args.img or (id .. &amp;quot;.png&amp;quot;), args.name or entityName(id)),&lt;br /&gt;
		&amp;quot;| &amp;lt;center&amp;gt;Нет&amp;lt;/center&amp;gt;&amp;quot;,&lt;br /&gt;
		&amp;quot;| &amp;quot; .. text,&lt;br /&gt;
	}, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Текст лечения кроватью/койкой (компонент HealOnBuckle).&lt;br /&gt;
-- {{#invoke:MedicalStats|bed|MedicalBed}} -&amp;gt; «Лечит: Ушибы 0,2, … каждую секунду»&lt;br /&gt;
function p.bed(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local id = mw.text.trim(args[1] or &amp;quot;&amp;quot;)&lt;br /&gt;
	local b = getEntry(&amp;quot;component/HealOnBuckle.json&amp;quot;, id)&lt;br /&gt;
	if not b then&lt;br /&gt;
		return &amp;#039;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;У «&amp;#039; .. id .. &amp;#039;» нет компонента HealOnBuckle в выгрузке бота&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local acc = newAcc()&lt;br /&gt;
	addSpecifier(acc, b(&amp;quot;damage&amp;quot;), 1)&lt;br /&gt;
	local parts = renderDamage(acc)&lt;br /&gt;
	if #parts == 0 then&lt;br /&gt;
		return &amp;quot;Не лечит.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	local period = b(&amp;quot;healTime&amp;quot;) or 1&lt;br /&gt;
	local text = ucfirst(table.concat(parts, &amp;quot;; &amp;quot;))&lt;br /&gt;
		.. (period == 1 and &amp;quot; каждую секунду&amp;quot; or (&amp;quot; каждые &amp;quot; .. num(period) .. &amp;quot; с&amp;quot;))&lt;br /&gt;
	local sleep = b(&amp;quot;sleepMultiplier&amp;quot;)&lt;br /&gt;
	if sleep and sleep ~= 1 then&lt;br /&gt;
		text = text .. &amp;quot;, во сне — в &amp;quot; .. num(sleep) .. &amp;quot; раза больше&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return text .. &amp;quot;.&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Множитель стазис-кровати: {{#invoke:MedicalStats|stasis|StasisBed}} -&amp;gt; 10&lt;br /&gt;
function p.stasis(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local s = getEntry(&amp;quot;component/StasisBed.json&amp;quot;, mw.text.trim(args[1] or &amp;quot;StasisBed&amp;quot;))&lt;br /&gt;
	return s and num(s(&amp;quot;multiplier&amp;quot;) or 0) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Базовые показатели организма (для текста страницы)&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Порог состояния: {{#invoke:MedicalStats|threshold|MobHuman|Critical}} -&amp;gt; 100.&lt;br /&gt;
-- Если такого состояния у сущности нет (например, PreCritical вне Wega) — пустая строка,&lt;br /&gt;
-- поэтому вызов можно оборачивать в {{#if:}}.&lt;br /&gt;
function p.threshold(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local t = getEntry(&amp;quot;component/MobThresholds.json&amp;quot;, mw.text.trim(args[1] or &amp;quot;MobHuman&amp;quot;))&lt;br /&gt;
	local state = mw.text.trim(args[2] or &amp;quot;Critical&amp;quot;)&lt;br /&gt;
	if not t then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	for dmg, st in pairs(t(&amp;quot;thresholds&amp;quot;) or {}) do&lt;br /&gt;
		if st == state then&lt;br /&gt;
			return num(tonumber(dmg))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Нормальный объём крови в u. В новой схеме — bloodReferenceSolution, в старой (Goob) — bloodMaxVolume.&lt;br /&gt;
local function bloodVolume(id)&lt;br /&gt;
	local b = getEntry(&amp;quot;component/Bloodstream.json&amp;quot;, id)&lt;br /&gt;
	if not b then&lt;br /&gt;
		return nil, nil&lt;br /&gt;
	end&lt;br /&gt;
	local ref = b(&amp;quot;bloodReferenceSolution&amp;quot;)&lt;br /&gt;
	local vol = type(ref) == &amp;quot;table&amp;quot; and ref.maxVol or nil&lt;br /&gt;
	if not vol and type(ref) == &amp;quot;table&amp;quot; then&lt;br /&gt;
		for _, r in ipairs(ref.reagents or {}) do&lt;br /&gt;
			vol = (vol or 0) + (r.Quantity or 0)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	vol = vol or b(&amp;quot;bloodMaxVolume&amp;quot;)&lt;br /&gt;
	return vol, b&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- {{#invoke:MedicalStats|blood|MobHuman}} -&amp;gt; 600 (норма крови)&lt;br /&gt;
-- {{#invoke:MedicalStats|blood|MobHuman|pale}} -&amp;gt; 540 (ниже — «выглядит бледно» и урон от кровопотери)&lt;br /&gt;
-- {{#invoke:MedicalStats|blood|MobHuman|palePercent}} -&amp;gt; 90&lt;br /&gt;
-- {{#invoke:MedicalStats|blood|MobHuman|maxBleed}} -&amp;gt; 10&lt;br /&gt;
function p.blood(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local vol, b = bloodVolume(mw.text.trim(args[1] or &amp;quot;MobHuman&amp;quot;))&lt;br /&gt;
	if not b then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	local mode = mw.text.trim(args[2] or &amp;quot;volume&amp;quot;)&lt;br /&gt;
	local thr = b(&amp;quot;bloodlossThreshold&amp;quot;) or 0.9&lt;br /&gt;
	if mode == &amp;quot;pale&amp;quot; then&lt;br /&gt;
		return vol and num(vol * thr) or &amp;quot;&amp;quot;&lt;br /&gt;
	elseif mode == &amp;quot;palePercent&amp;quot; then&lt;br /&gt;
		return num(thr * 100)&lt;br /&gt;
	elseif mode == &amp;quot;maxBleed&amp;quot; then&lt;br /&gt;
		return num(b(&amp;quot;maxBleedAmount&amp;quot;) or 10)&lt;br /&gt;
	end&lt;br /&gt;
	return vol and num(vol) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Сколько разных веществ орган обрабатывает за тик: {{#invoke:MedicalStats|organLimit|OrganHumanHeart}} -&amp;gt; 2.&lt;br /&gt;
-- В игре по умолчанию 3 (MetabolizerComponent.MaxReagentsProcessable); бот дефолт не всегда выгружает.&lt;br /&gt;
function p.organLimit(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local m = getEntry(&amp;quot;component/Metabolizer.json&amp;quot;, mw.text.trim(args[1] or &amp;quot;OrganHumanHeart&amp;quot;))&lt;br /&gt;
	if not m then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return num(m(&amp;quot;maxReagents&amp;quot;) or 3)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Задержка переваривания желудка в секундах (есть только в старой схеме, например Goob).&lt;br /&gt;
-- {{#invoke:MedicalStats|digestionDelay|OrganHumanStomach}} -&amp;gt; 20 или пустая строка.&lt;br /&gt;
function p.digestionDelay(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
	local st = getEntry(&amp;quot;component/Stomach.json&amp;quot;, mw.text.trim(args[1] or &amp;quot;OrganHumanStomach&amp;quot;))&lt;br /&gt;
	local d = st and st(&amp;quot;digestionDelay&amp;quot;)&lt;br /&gt;
	return d and num(d) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Halch</name></author>
	</entry>
</feed>