Mediawiki:scmcchemistry.js

Вернуться

Строка 32: Строка 32:
     };
     };
     var EFFECT_LABELS = {
     var EFFECT_LABELS = {
         AdjustReagent: 'Изменяет количество реагента',
         Anticorrosive: 'Противокислотное действие',
         AdjustTemperature: 'Изменяет температуру',
         Antihallucinogenic: 'Подавление галлюцинаций',
         AreaReactionEffect: 'Создаёт эффект в области',
         Antitoxic: 'Антитоксическое действие',
         CauseStatusEffect: 'Вызывает статусный эффект',
         Biocidic: 'Восстановление клеток',
        Carcinogenic: 'Канцерогенное действие',
        ChemHealEyeDamage: 'Лечит повреждения глаз',
         ChemVomit: 'Вызывает рвоту',
         ChemVomit: 'Вызывает рвоту',
         CureDisease: 'Лечит заболевание',
         Corrosive: 'Разъедающее действие',
        CureZombieInfection: 'Лечит зомби-инфекцию',
        CauseZombieInfection: 'Заражает зомби-инфекцией',
         Drunk: 'Вызывает опьянение',
         Drunk: 'Вызывает опьянение',
         Electrocute: 'Поражает электричеством',
         Electrogenetic: 'Электрогенетическое действие',
         Emote: 'Вызывает эмоцию',
         Emote: 'Вызывает непроизвольную реакцию',
         ExplosionReactionEffect: 'Вызывает взрыв',
         EqualHealthChange: 'Изменяет здоровье',
        FlammableReaction: 'Повышает воспламеняемость',
        Flowing: 'Изменяет текучесть крови',
        Focusing: 'Повышает концентрацию',
        Fueling: 'Топливное действие',
         GenericStatusEffect: 'Накладывает статусный эффект',
         GenericStatusEffect: 'Накладывает статусный эффект',
         HealthChange: 'Изменяет здоровье',
         HealthChange: 'Изменяет здоровье',
         Ignite: 'Поджигает',
         Hemogenic: 'Восстанавливает кровь',
         ModifyBloodLevel: 'Изменяет уровень крови',
         Hypoxemic: 'Вызывает кислородное голодание',
        Jitter: 'Вызывает дрожь',
        Ketogenic: 'Снижает запас питательных веществ',
        ModifyStatusEffect: 'Изменяет статусный эффект',
         MovespeedModifier: 'Изменяет скорость движения',
         MovespeedModifier: 'Изменяет скорость движения',
         Polymorph: 'Изменяет форму',
         Neogenetic: 'Восстановление тканей',
        Nutritious: 'Питательное действие',
        Oxidizing: 'Окисляющее действие',
        Oxygenating: 'Насыщает организм кислородом',
         PopupMessage: 'Показывает сообщение',
         PopupMessage: 'Показывает сообщение',
        ResetNarcolepsy: 'Снимает приступ нарколепсии',
         SatiateHunger: 'Утоляет голод',
         SatiateHunger: 'Утоляет голод',
         SatiateThirst: 'Утоляет жажду',
         SatiateThirst: 'Утоляет жажду',
         SensitiveReactionExplosionEffect: 'Может вызвать взрыв',
         Thermostabilizing: 'Стабилизирует температуру тела',
         SetSolutionTemperature: 'Задаёт температуру раствора',
        Toxic: 'Токсическое действие',
         Sleep: 'Усыпляет'
        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 describeValue(value) {
     function action(text, kind) {
         if (value === null || value === undefined || 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 (typeof value !== 'object') {
         return 'при выполнении дополнительного условия';
             return String(value);
    }
 
    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)));
         }
         }
         return Object.keys(value).filter(function (key) {
 
            return !['conditions', 'showInGuidebook', 'showInChat'].includes(key);
         (value.conditions || []).forEach(function (condition) {
        }).map(function (key) {
             var text = conditionText(condition, reagent, tier !== 'normal' && effectType(condition) === 'ReagentThreshold');
             var item = value[key];
            if (text) {
            if (item && typeof item === 'object') {
                 add(card, el('div', 'scmc-chem__effect-condition', 'Условие: ' + text));
                 return key + ': ' + JSON.stringify(item);
             }
             }
             return key + ': ' + item;
        });
         }).join(' · ');
        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 || {};
             var harmful = /Damage|Ignite|Explosion|Electrocute|Vomit|Poison/i.test(type);
             if (type === 'PlantCryoxadone') {
            var card = el('div', 'scmc-chem__effect' + (harmful ? ' scmc-chem__effect--harmful' : ''));
                var special = el('div', 'scmc-chem__effect scmc-chem__effect--beneficial');
            add(card, el('div', 'scmc-chem__effect-name', EFFECT_LABELS[type] || type));
                add(special, el('div', 'scmc-chem__effect-name', 'Криогенное восстановление'));
            var details = describeValue(value);
                 add(special, line(action('Омолаживает', 'beneficial'), ' растение при низкой температуре'));
            if (details) {
                add(list, special);
                 add(card, el('div', 'scmc-chem__effect-details', details));
                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;