Mediawiki:scmcchemistry.js
| Строка 32: | Строка 32: | ||
}; | }; | ||
var EFFECT_LABELS = { | var EFFECT_LABELS = { | ||
Anticorrosive: 'Противокислотное действие', | |||
Antihallucinogenic: 'Подавление галлюцинаций', | |||
Antitoxic: 'Антитоксическое действие', | |||
Biocidic: 'Восстановление клеток', | |||
Carcinogenic: 'Канцерогенное действие', | |||
ChemHealEyeDamage: 'Лечит повреждения глаз', | |||
ChemVomit: 'Вызывает рвоту', | ChemVomit: 'Вызывает рвоту', | ||
Corrosive: 'Разъедающее действие', | |||
CureZombieInfection: 'Лечит зомби-инфекцию', | |||
CauseZombieInfection: 'Заражает зомби-инфекцией', | |||
Drunk: 'Вызывает опьянение', | Drunk: 'Вызывает опьянение', | ||
Electrogenetic: 'Электрогенетическое действие', | |||
Emote: 'Вызывает | Emote: 'Вызывает непроизвольную реакцию', | ||
EqualHealthChange: 'Изменяет здоровье', | |||
FlammableReaction: 'Повышает воспламеняемость', | |||
Flowing: 'Изменяет текучесть крови', | |||
Focusing: 'Повышает концентрацию', | |||
Fueling: 'Топливное действие', | |||
GenericStatusEffect: 'Накладывает статусный эффект', | GenericStatusEffect: 'Накладывает статусный эффект', | ||
HealthChange: 'Изменяет здоровье', | HealthChange: 'Изменяет здоровье', | ||
Hemogenic: 'Восстанавливает кровь', | |||
Hypoxemic: 'Вызывает кислородное голодание', | |||
Jitter: 'Вызывает дрожь', | |||
Ketogenic: 'Снижает запас питательных веществ', | |||
ModifyStatusEffect: 'Изменяет статусный эффект', | |||
MovespeedModifier: 'Изменяет скорость движения', | MovespeedModifier: 'Изменяет скорость движения', | ||
Neogenetic: 'Восстановление тканей', | |||
Nutritious: 'Питательное действие', | |||
Oxidizing: 'Окисляющее действие', | |||
Oxygenating: 'Насыщает организм кислородом', | |||
PopupMessage: 'Показывает сообщение', | PopupMessage: 'Показывает сообщение', | ||
SatiateHunger: 'Утоляет голод', | SatiateHunger: 'Утоляет голод', | ||
SatiateThirst: 'Утоляет жажду', | SatiateThirst: 'Утоляет жажду', | ||
Thermostabilizing: 'Стабилизирует температуру тела', | |||
Toxic: 'Токсическое действие', | |||
Viscous: 'Замедляющее действие', | |||
AdjustReagent: 'Изменяет количество реагента' | |||
}; | |||
var DAMAGE_LABELS = { | |||
Airloss: 'кислородного голодания', | |||
Asphyxiation: 'кислородного голодания', | |||
Bloodloss: 'кровопотери', | |||
Blunt: 'тупого', Brute: 'механического', Burn: 'термического', | |||
Caustic: 'кислотного', Cellular: 'клеточного', Cold: 'холодового', | |||
Genetic: 'генетического', Heat: 'ожогового', Piercing: 'колотого', | |||
Poison: 'токсического', Radiation: 'радиационного', Slash: 'режущего', | |||
Toxin: 'токсического' | |||
}; | |||
var DAMAGE_CLASSES = { | |||
Airloss: 'oxygen', Asphyxiation: 'oxygen', Bloodloss: 'oxygen', | |||
Blunt: 'physical', Brute: 'physical', Piercing: 'physical', Slash: 'physical', | |||
Burn: 'burn', Heat: 'burn', Cold: 'cold', Poison: 'toxin', Toxin: 'toxin', | |||
Caustic: 'acid', Genetic: 'genetic', Cellular: 'cellular', Radiation: 'radiation' | |||
}; | |||
var STATUS_LABELS = { | |||
Drowsiness: 'сонливость', Jitter: 'дрожь', RMCUnconscious: 'потерю сознания', | |||
ScrambledAccent: 'невнятную речь', StaminaModifier: 'изменение выносливости', | |||
StatusEffectSeeingRainbow: 'галлюцинации', Unconscious: 'потерю сознания' | |||
}; | |||
var EMOTE_LABELS = { | |||
Cough: 'кашель', Gasp: 'судорожный вдох', Laugh: 'смех', | |||
Scream: 'крик', Sneeze: 'чихание', Yawn: 'зевоту' | |||
}; | |||
var PLANT_LABELS = { | |||
PlantAdjustGrowth: 'возраст растения', PlantAdjustHealth: 'здоровье растения', | |||
PlantAdjustMutationLevel: 'уровень мутации', PlantAdjustMutationMod: 'шанс мутации', | |||
PlantAdjustNutrition: 'питание растения', PlantAdjustPests: 'число вредителей', | |||
PlantAdjustPotency: 'потенцию растения', PlantAdjustToxins: 'уровень токсинов', | |||
PlantAdjustWater: 'уровень воды', PlantAdjustWeeds: 'число сорняков' | |||
}; | |||
var ORGAN_LABELS = { | |||
Animal: 'животного', Bloodsucker: 'кровососа', Human: 'человека', | |||
Rat: 'крысы', Slime: 'слайма', Vox: 'вокса', Xeno: 'ксеноморфа' | |||
}; | |||
var MOB_STATE_LABELS = { | |||
Alive: 'жив', Critical: 'критическое состояние', Dead: 'мёртв' | |||
}; | |||
var REAGENT_LABELS = { | |||
UncookedAnimalProteins: 'сырые животные белки' | |||
}; | }; | ||
var state = { | var state = { | ||
| Строка 120: | Строка 176: | ||
function getName(id) { | function getName(id) { | ||
var reagent = getReagent(id); | var reagent = getReagent(id); | ||
return capitalizeFirst(reagent && reagent.name ? reagent.name : id); | return capitalizeFirst(reagent && reagent.name ? reagent.name : REAGENT_LABELS[id] || id); | ||
} | } | ||
| Строка 304: | Строка 360: | ||
} | } | ||
function | function action(text, kind) { | ||
if (value === | return el('span', 'scmc-chem__effect-action scmc-chem__effect-action--' + kind, text); | ||
return ''; | } | ||
function damageType(type) { | |||
var className = DAMAGE_CLASSES[type] || 'physical'; | |||
return el('span', 'scmc-chem__damage-type scmc-chem__damage-type--' + className, DAMAGE_LABELS[type] || type); | |||
} | |||
function line() { | |||
var node = el('div', 'scmc-chem__effect-details'); | |||
Array.prototype.slice.call(arguments).forEach(function (part) { | |||
node.appendChild(part && part.nodeType ? part : document.createTextNode(String(part))); | |||
}); | |||
return node; | |||
} | |||
function probabilityText(value) { | |||
return value === undefined ? '' : ' · вероятность ' + formatNumber(value * 100) + '%'; | |||
} | |||
function damageEntries(value) { | |||
var damage = value && value.damage; | |||
var result = []; | |||
if (Array.isArray(damage)) { | |||
damage.forEach(function (item) { | |||
Object.keys(item || {}).forEach(function (type) { | |||
result.push({ type: type, amount: item[type] }); | |||
}); | |||
}); | |||
} else if (damage && typeof damage === 'object') { | |||
['groups', 'types'].forEach(function (section) { | |||
Object.keys(damage[section] || {}).forEach(function (type) { | |||
result.push({ type: type, amount: damage[section][type] }); | |||
}); | |||
}); | |||
if (!damage.groups && !damage.types) { | |||
Object.keys(damage).forEach(function (type) { | |||
if (typeof damage[type] === 'number') { | |||
result.push({ type: type, amount: damage[type] }); | |||
} | |||
}); | |||
} | |||
} | |||
return result; | |||
} | |||
function effectKind(type, value) { | |||
var beneficial = /^(Anticorrosive|Antihallucinogenic|Antitoxic|Biocidic|ChemHealEyeDamage|CureZombieInfection|Electrogenetic|Focusing|Hemogenic|Neogenetic|Nutritious|Oxygenating|SatiateHunger|SatiateThirst|Thermostabilizing)$/; | |||
var harmful = /^(Carcinogenic|CauseZombieInfection|ChemVomit|Corrosive|Fueling|Hypoxemic|Jitter|Ketogenic|Oxidizing|Toxic|Viscous)$/; | |||
if (type === 'HealthChange' || type === 'EqualHealthChange') { | |||
var damage = damageEntries(value); | |||
return damage.length && damage.every(function (item) { return item.amount <= 0; }) ? 'beneficial' : | |||
damage.length && damage.every(function (item) { return item.amount >= 0; }) ? 'harmful' : 'warning'; | |||
} | |||
if (beneficial.test(type)) { | |||
return 'beneficial'; | |||
} | |||
if (harmful.test(type)) { | |||
return 'harmful'; | |||
} | |||
return 'warning'; | |||
} | |||
function conditionText(condition, reagent, hideDoseCondition) { | |||
var type = effectType(condition); | |||
var value = condition.value || {}; | |||
if (type === 'ReagentThreshold') { | |||
if (hideDoseCondition) { | |||
return ''; | |||
} | |||
return 'при содержании ' + (value.reagent ? getName(value.reagent) + ' ' : '') + | |||
(value.min !== undefined ? 'от ' + formatAmount(value.min) : '') + | |||
(value.max !== undefined ? ' до ' + formatAmount(value.max) : ''); | |||
} | |||
if (type === 'Temperature') { | |||
return 'при температуре ' + | |||
(value.min !== undefined ? 'от ' + formatNumber(value.min) + ' K' : '') + | |||
(value.max !== undefined ? 'до ' + formatNumber(value.max) + ' K' : ''); | |||
} | |||
if (type === 'MobStateCondition') { | |||
var stateName = value.mobstate || value.state || 'особое'; | |||
return 'если цель находится в состоянии: ' + (MOB_STATE_LABELS[stateName] || stateName); | |||
} | |||
if (type === 'OrganType') { | |||
var organ = value.type || value.organType || 'подходящего типа'; | |||
return 'для организма ' + (ORGAN_LABELS[organ] || organ); | |||
} | } | ||
if ( | return 'при выполнении дополнительного условия'; | ||
} | |||
function renderEffect(effect, reagent, tier) { | |||
var type = effectType(effect); | |||
var value = effect.value && typeof effect.value === 'object' ? effect.value : {}; | |||
var kind = effectKind(type, value); | |||
var card = el('div', 'scmc-chem__effect scmc-chem__effect--' + kind); | |||
add(card, el('div', 'scmc-chem__effect-name', EFFECT_LABELS[type] || ('Эффект «' + type + '»'))); | |||
var damage = damageEntries(value); | |||
if (damage.length) { | |||
damage.forEach(function (item) { | |||
var healing = item.amount < 0; | |||
var oxygenLike = /^(Airloss|Asphyxiation|Bloodloss)$/.test(item.type); | |||
add(card, oxygenLike ? line( | |||
action(healing ? 'Лечит' : 'Наносит', healing ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(Math.abs(item.amount)) + ' ед. урона от ', damageType(item.type) | |||
) : line( | |||
action(healing ? 'Лечит' : 'Наносит', healing ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(Math.abs(item.amount)) + ' ед. ', damageType(item.type), ' урона' | |||
)); | |||
}); | |||
} else if (/^(Antitoxic|Toxic)$/.test(type)) { | |||
add(card, line(action(type === 'Antitoxic' ? 'Лечит' : 'Наносит', type === 'Antitoxic' ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(value.potency || 1) + ' ед. ', damageType('Toxin'), ' урона')); | |||
} else if (/^(Biocidic|Carcinogenic)$/.test(type)) { | |||
add(card, line(action(type === 'Biocidic' ? 'Лечит' : 'Наносит', type === 'Biocidic' ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(value.potency || 1) + ' ед. ', damageType('Cellular'), ' урона')); | |||
} else if (/^(Neogenetic|Electrogenetic)$/.test(type)) { | |||
add(card, line(action('Лечит', 'beneficial'), ' ' + formatNumber(value.potency || 1) + ' ед. ', damageType(type === 'Neogenetic' ? 'Brute' : 'Genetic'), ' урона')); | |||
} else if (/^(Anticorrosive|Corrosive)$/.test(type)) { | |||
add(card, line(action(type === 'Anticorrosive' ? 'Лечит' : 'Наносит', type === 'Anticorrosive' ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(value.potency || 1) + ' ед. ', damageType('Caustic'), ' урона')); | |||
} else if (/^(Oxygenating|Hypoxemic)$/.test(type)) { | |||
add(card, line(action(type === 'Oxygenating' ? 'Лечит' : 'Наносит', type === 'Oxygenating' ? 'beneficial' : 'harmful'), | |||
' ' + formatNumber(value.potency || 1) + ' ед. урона от ', damageType('Airloss'))); | |||
} else if (type === 'Oxidizing') { | |||
add(card, line(action('Наносит', 'harmful'), ' ' + formatNumber(value.potency || 1) + ' ед. ', damageType('Burn'), ' урона')); | |||
} else if (type === 'Hemogenic') { | |||
add(card, line(action('Восстанавливает', 'beneficial'), ' кровь · сила ' + formatNumber(value.potency || 1))); | |||
} else if (type === 'AdjustReagent') { | |||
var amount = Number(value.amount || 0); | |||
add(card, line(action(amount < 0 ? 'Удаляет' : 'Добавляет', amount < 0 ? 'beneficial' : 'utility'), | |||
' ' + formatAmount(Math.abs(amount)) + ' реагента «' + getName(value.reagent) + '»')); | |||
} else if (type === 'GenericStatusEffect' || type === 'ModifyStatusEffect') { | |||
var status = value.component || value.key || value.effectProto || 'статусный эффект'; | |||
add(card, line(action(value.type === 'Remove' ? 'Снимает' : 'Вызывает', value.type === 'Remove' ? 'beneficial' : 'harmful'), | |||
' ' + (STATUS_LABELS[status] || status) + (value.time !== undefined ? ' на ' + formatNumber(value.time) + ' с' : ''))); | |||
} else if (type === 'Emote') { | |||
add(card, line(action('Вызывает', 'warning'), ' ' + (EMOTE_LABELS[value.emote] || value.emote || 'непроизвольную реакцию') + probabilityText(value.probability))); | |||
} else if (type === 'Jitter') { | |||
add(card, line(action('Вызывает', 'harmful'), ' дрожь' + probabilityText(value.probability))); | |||
} else if (type === 'ChemVomit') { | |||
add(card, line(action('Вызывает', 'harmful'), ' рвоту' + probabilityText(value.probability))); | |||
} else if (type === 'SatiateThirst' || type === 'SatiateHunger') { | |||
add(card, line(action(type === 'SatiateThirst' ? 'Утоляет жажду' : 'Утоляет голод', 'beneficial'), | |||
value.factor !== undefined ? ' · коэффициент ' + formatNumber(value.factor) : '')); | |||
} else if (type === 'Drunk') { | |||
add(card, line(action('Опьяняет', 'warning'), value.boozePower !== undefined ? ' · сила ' + formatNumber(value.boozePower) : '')); | |||
} else if (type === 'MovespeedModifier') { | |||
add(card, line(action('Изменяет скорость', 'utility'), | |||
' · ходьба ×' + formatNumber(value.walkSpeedModifier || 1) + ' · бег ×' + formatNumber(value.sprintSpeedModifier || 1))); | |||
} else if (type === 'CureZombieInfection' || type === 'CauseZombieInfection') { | |||
add(card, line(action(type === 'CureZombieInfection' ? 'Лечит' : 'Заражает', type === 'CureZombieInfection' ? 'beneficial' : 'harmful'), | |||
(type === 'CureZombieInfection' ? ' зомби-инфекцию' : ' зомби-инфекцией') + (value.innoculate ? ' и создаёт иммунитет' : ''))); | |||
} else if (type === 'PopupMessage') { | |||
add(card, line(action('Показывает', 'utility'), ' игровое сообщение' + probabilityText(value.probability))); | |||
} else { | |||
var potency = value.potency !== undefined ? ' · сила ' + formatNumber(value.potency) : ''; | |||
var multiplier = value.multiplier !== undefined ? ' · множитель ' + formatNumber(value.multiplier) : ''; | |||
add(card, line(action(EFFECT_LABELS[type] || 'Воздействует', kind === 'beneficial' ? 'beneficial' : kind === 'harmful' ? 'harmful' : 'utility'), potency + multiplier + probabilityText(value.probability))); | |||
} | } | ||
(value.conditions || []).forEach(function (condition) { | |||
var text = conditionText(condition, reagent, tier !== 'normal' && effectType(condition) === 'ReagentThreshold'); | |||
var | if (text) { | ||
add(card, el('div', 'scmc-chem__effect-condition', 'Условие: ' + text)); | |||
} | } | ||
return | }); | ||
}). | return card; | ||
} | |||
function effectDose(effect, reagent) { | |||
var conditions = effect.value && effect.value.conditions || []; | |||
var mins = conditions.filter(function (condition) { | |||
var value = condition.value || {}; | |||
return effectType(condition) === 'ReagentThreshold' && | |||
(!value.reagent || value.reagent === reagent.id) && value.min !== undefined; | |||
}).map(function (condition) { return Number(condition.value.min); }); | |||
return mins.length ? Math.max.apply(Math, mins) : null; | |||
} | } | ||
function renderEffects(effects) { | function effectTier(effect, reagent) { | ||
var dose = effectDose(effect, reagent); | |||
var properties = reagent.properties || {}; | |||
if (dose !== null && properties.criticalOverdose !== undefined && dose >= properties.criticalOverdose) { | |||
return 'critical'; | |||
} | |||
if (dose !== null && properties.overdose !== undefined && dose >= properties.overdose) { | |||
return 'overdose'; | |||
} | |||
return 'normal'; | |||
} | |||
function tierTitle(tier, reagent) { | |||
var properties = reagent.properties || {}; | |||
if (tier === 'critical') { | |||
return 'Критическая передозировка' + (properties.criticalOverdose !== undefined ? ' · от ' + formatAmount(properties.criticalOverdose) : ''); | |||
} | |||
if (tier === 'overdose') { | |||
return 'Передозировка' + (properties.overdose !== undefined ? ' · от ' + formatAmount(properties.overdose) : ''); | |||
} | |||
return 'Обычная доза' + (properties.overdose !== undefined ? ' · до ' + formatAmount(properties.overdose) : ''); | |||
} | |||
function renderEffects(effects, reagent) { | |||
var list = el('div', 'scmc-chem__effect-list'); | var list = el('div', 'scmc-chem__effect-list'); | ||
if (!effects || !effects.length) { | if (!effects || !effects.length) { | ||
| Строка 328: | Строка 570: | ||
return list; | return list; | ||
} | } | ||
var tiers = { normal: [], overdose: [], critical: [] }; | |||
effects.forEach(function (effect) { | effects.forEach(function (effect) { | ||
tiers[effectTier(effect, reagent)].push(effect); | |||
}); | |||
['normal', 'overdose', 'critical'].forEach(function (tier) { | |||
if (!tiers[tier].length) { | |||
return; | |||
} | |||
var block = el('div', 'scmc-chem__effect-tier scmc-chem__effect-tier--' + tier); | |||
add(block, el('div', 'scmc-chem__effect-tier-title', tierTitle(tier, reagent))); | |||
tiers[tier].forEach(function (effect) { | |||
add(block, renderEffect(effect, reagent, tier)); | |||
}); | |||
add(list, block); | |||
}); | |||
return list; | |||
} | |||
function plantEffectKind(type, amount) { | |||
if (/Mutation/.test(type)) { | |||
return 'warning'; | |||
} | |||
var harmfulWhenPositive = /Toxins|Weeds|Pests/.test(type); | |||
return (harmfulWhenPositive ? amount <= 0 : amount >= 0) ? 'beneficial' : 'harmful'; | |||
} | |||
function renderPlantEffects(effects) { | |||
var list = el('div', 'scmc-chem__effect-list'); | |||
(effects || []).forEach(function (effect) { | |||
var type = effectType(effect); | var type = effectType(effect); | ||
var value = effect.value || {}; | var value = effect.value || {}; | ||
if (type === 'PlantCryoxadone') { | |||
var special = el('div', 'scmc-chem__effect scmc-chem__effect--beneficial'); | |||
add(special, el('div', 'scmc-chem__effect-name', 'Криогенное восстановление')); | |||
add(special, line(action('Омолаживает', 'beneficial'), ' растение при низкой температуре')); | |||
add(list, special); | |||
add( | return; | ||
} | } | ||
var amount = Number(value.amount || 0); | |||
var kind = plantEffectKind(type, amount); | |||
var card = el('div', 'scmc-chem__effect scmc-chem__effect--' + kind); | |||
add(card, el('div', 'scmc-chem__effect-name', capitalizeFirst(PLANT_LABELS[type] || ('Эффект «' + type + '»')))); | |||
add(card, line( | |||
action(amount >= 0 ? 'Повышает' : 'Снижает', kind === 'beneficial' ? 'beneficial' : kind === 'harmful' ? 'harmful' : 'warning'), | |||
' ' + (PLANT_LABELS[type] || 'показатель') + ' на ' + formatNumber(Math.abs(amount)) + | |||
probabilityText(value.probability) | |||
)); | |||
add(list, card); | add(list, card); | ||
}); | }); | ||
| Строка 359: | Строка 638: | ||
title += ' · ' + formatAmount(metabolism.metabolismRate) + '/с'; | title += ' · ' + formatAmount(metabolism.metabolismRate) + '/с'; | ||
} | } | ||
add(block, el('div', 'scmc-chem__metabolism-title', title), renderEffects(metabolism.effects)); | add(block, el('div', 'scmc-chem__metabolism-title', title), renderEffects(metabolism.effects, reagent)); | ||
add(wrapper, block); | add(wrapper, block); | ||
}); | }); | ||
return wrapper; | |||
} | |||
function renderPlantMetabolism(reagent) { | |||
var effects = reagent.properties && reagent.properties.plantMetabolism; | |||
if (!Array.isArray(effects) || !effects.length) { | |||
return null; | |||
} | |||
var wrapper = el('div', 'scmc-chem__card-section'); | |||
var block = el('div', 'scmc-chem__metabolism'); | |||
add(wrapper, el('h4', 'scmc-chem__card-subtitle', 'Воздействие на растения')); | |||
add(block, el('div', 'scmc-chem__metabolism-title', 'Метаболизм растений'), renderPlantEffects(effects)); | |||
add(wrapper, block); | |||
return wrapper; | return wrapper; | ||
} | } | ||
| Строка 409: | Строка 701: | ||
} | } | ||
add(body, renderMetabolisms(reagent)); | add(body, renderMetabolisms(reagent)); | ||
add(body, renderPlantMetabolism(reagent)); | |||
add(details, summary, body); | add(details, summary, body); | ||
return details; | return details; | ||