Анализ поведения погод программно
Данный файл основывает свою аналитику на анализе исходного кода следующих погод из следующих модификаций:
| Погода | Мод | Версия при анализе |
|---|---|---|
| HEATWAVE | LethalElementsForky | v1.4.2 |
| SOLAR FLARE | LethalElementsForky | v1.4.2 |
| SNOWFALL | LethalElementsForky | v1.4.2 |
| BLIZZARD | LethalElementsForky | v1.4.2 |
| TOXIC SMOG | LethalElementsForky | v1.4.2 |
| FIRESTORM | CodeRebirth | v1.6.9 |
| HURRICANE | Wesley's Weathers / CodeRebirth | v1.2.11 / v1.6.9 |
| TORNADO | CodeRebirth | v1.6.9 |
| METEOR SHOWER | CodeRebirth | v1.6.9 |
| GODRAYS | CodeRebirth | v1.6.9 |
| NIGHTSHIFT | CodeRebirth | v1.6.9 |
| Моды "Kenji's Weathers" и "Wesley's Weathers" распространяются по закрытому коду, поэтому анализировать логику погод/узнать подробную информацию об их поведении не является доступным. |
Анализ погод
Внизу указан всех публичных и доступных обычно погод, упоминаемых в инфобуке о погодах.
HEATWAVE
- Зона перегрева — триггер размером с уровень, реагирует только на игрока (не на технику или сущностей).
- Время до теплового удара рандомизируется один раз при входе в погоду.
- Статус "в зоне жары" сбрасывается моментально при выходе из триггера — накопительного эффекта вне зоны нет.
- Интенсивность частиц завязана на яркость солнца — жара слабее к вечеру/в тени.
Подробное описание
Анализируемый файл:
-
Триггер-зона теплового удара — не весь уровень, а
BoxColliderсheatwaveTrigger.isTrigger = true, размеромLevelBounds.size(SetupHeatwaveWeather(), ~строки 58–66). Маска коллайдера явно исключает всё, кроме игрока:excludeLayers = ~LayerMask.GetMask("Player", "PlayerRagdoll"), т.е. машины и сущности зону не триггерят (в коде есть закомментированная заглушка под круизерVehicleHeatwaveHandler, она не активна). -
Время до теплового удара рандомизируется один раз при заходе в погоду:
timeInHeatZoneMax = SeededRandom.NextDouble(TimeUntilStrokeMin, TimeUntilStrokeMax), т.е. у каждого дня своя длительность "запаса", а не фиксированный таймер. -
Статус "в зоне жары" держится, только пока коллайдер триггерится (
OnTriggerStay→PlayerEffectsManager.isInHeatZone = true), и сбрасывается моментально при выходе (OnTriggerExit→heatTransferRate = 1f), т.е. отступление в помещение/тень снимает эффект сразу, накопительного "остывания" не предусмотрено вне зоны. -
Интенсивность частиц марева и сила эффекта завязаны на реальную яркость солнца через кривую:
CooldownHeatwaveVFX()берётsunLightData.intensity / startingSunIntensity, прогоняет черезheatwaveIntensityCurve, и результат (reductionFactor) записывается вHeatwaveWeather.Instance.timeOfDayFactor. Жара реально слабее к вечеру/в тени.
SOLAR FLARE
- Одна из 4 интенсивностей выбирается один раз на весь цикл вспышки, определяет набор возможных поломок.
- Электрические "поломки" идут тактами, а не постоянным фоном: ~раз в 43 сек (3%) игрового дня.
- Затрагивает не только связь, но и врагов/ловушки: олдбёрды ("Old bird") глушатся и оглушаются, турель уходит в берсерк-режим, мины детонируют дистанционно, гнездо олдбёрдов может пересоздать нового олдбёрда сверх дневного лимита.
- Двери ломаются вдвое реже (раз в 6% дня) и только у хоста.
- Радар глушится через специальный параметр и при любой интенсивности.
Подробное описание
Анализируемый файл:
-
Интенсивность выбирается случайно из 4 значений
FlareIntensity(Weak/Mild/Average/Strong) при входе в погоду (строка ~124), и от неё зависит, какие конкретноIsDoorMalfunction/IsTurretMalfunction/IsRadMechMalfunctionвключены воFlareData— то есть тип "поломок" является фиксированным набором. -
Раз в 3% игрового дня (
normalizedTimeOfDay % 0.03f < 1e-4,Update()строка 213) — это примерно раз в ~43 сек за 24-мин игровой день — запускается моргание всех запитанных ламп (LightFlickerCoroutine) и попытка электрической поломки (ElectricalMalfunctionCoroutine). -
Эта попытка проходится по каждому найденному турели/RadMech-гнезду/мине (собраны один раз при входе в погоду через
FindObjectsByType), и для каждого типа — свой шанс и свой флаг:RadMechMalfunctionChance,TurretMalfunctionChance,LandmineMalfunctionChance— все настраиваемы через конфиг. -
Что происходит при "поломке" (
ElectricMalfunctionCoroutine, строки 308–395):- RadMech:
моргает "лицом", на
RadMechStunDurationсекунд оглушается, потом снова включает прожектор. - Турель:
получает
Hit(1, ...), что переводит её в берсерк-режим (турель начинает стрелять хаотично) до конца случайного тайминга. Имитация удара лопатой по турели. - Гнездо RadMech (
EnemyAINestSpawnObject): гнездо уничтожается и на его месте заново спавнится RadMech, если позволяет лимитcurrentMaxOutsidePower, т.е. вспышка может внепланово "родить" дополнительного RadMech-а сверх обычного дневного лимита спавна. - Мина:
получает
Hit(1, ...), детонируя дистанционно. Имитация эффекта будто игрок пробежался по мине/ударил её. - Двери:
раз в 6% дня (
% 0.06f) запускаетсяDoorMalfunctionCoroutine, притом только у хоста (isHostingGame) и только еслиflareData.IsDoorMalfunctionвключён для текущей интенсивности. Каждая большая дверь получает случайное состояниеopen/closedнезависимо от того, что нажимали игроки.
- RadMech:
моргает "лицом", на
-
Радар глушится техническим Custom Pass Volume (CPV) на камеру радара при входе в погоду (
GlitchRadarMap()) и не связано с интенсивностью — глюк на радаре есть при любой силе вспышки.
SNOWFALL / BLIZZARD
- Замедление от снега начинается не с нуля, а с порога толщины 0.4, дальше растёт линейно до потолка ~2.5.
- "Под снегом" считается по высоте глаз игрока — можно буквально утонуть в сугробе с головой.
- Занижение конфига урона от обморожения компенсируется дополнительным замедлением, а не просто "смягчает" снег.
- Каждый шаг игрока протаптывает тропу, ускоряющую движение по ней. Протаптывание игроком троп равнозначно любому изменению луны руками.
- Ветер определяется линкастом от головы игрока — простое укрытие за любым объектом полностью убирает эффект.
- Направление ветра меняется циклично с плавной интерполяцией с определенным интервалом.
- Ледяные волны летят строго по направлению ветра на момент запуска и автоматически не действуют внутри помещений — не нужно прятаться от них внутри комплекса вообще.
- В машине скорость охлаждения снижена (в 20 раз) — частичная защита без полного укрытия.
Подробное описание
Анализируемые файлы:
-
Замедление от снега — не бинарное "в снегу/не в снегу", а формула с порогом:
snowMovementHindranceMultiplier = 1 + (5 * Mathf.Clamp01((snowThickness + metaSnowThickness - 0.4f) / 2.1f))(SnowfallVFXManager.Update()) — штраф начинает копиться только после толщины снега 0.4, дальше растёт линейно и упирается в потолок при толщине ~2.5, т.е. мелкий снег в начале дня практически не мешает. -
"Под снегом" (
isUnderSnow) считается по высоте глаз игрока:feetPositionY + snowThickness >= localPlayerEyeY - eyeBias— если накопившийся снег доходит выше уровня глаз минус 0.2, экран засвечивается ("забелен"). Буквально означает, что можно физически утонуть в снегу с головой, если сугроб вырос достаточно. -
Обморожение зависит от настроенного конфига урона: если в настройках снизить
FrostbiteDamageотносительно стандартных10— движок компенсирует это дополнительным замедлением, а не просто уменьшает урон линейно (metaSnowThicknessв формуле выше). -
В метель (BLIZZARD) добавляется отдельная механика ветра:
isLocalPlayerInWindвычисляется рейкастом/линкастом от головы игрока в сторону источника метели на 20 метров (FixedUpdate()) — если по пути нет преград (стен, географии), считается что игрок обдувается, и тогдаexternalForces += windDirection * windForceфизически толкает игрока. Простое укрытие за любым объектом ландшафта полностью убирает эффект ветра — необязательно помещение, подойдёт любая скала/стена. -
Направление ветра меняется циклично,
windChangeIntervalсекунд (по умолчанию поле30), плавной интерполяцией поворота (ChangeWindDirectionCoroutine). -
Ледяные волны (
GenerateChillWaveCoroutine) идут строго по прямой через весь уровень против направления ветра на момент их запуска — то есть волна всегда летит "оттуда, откуда дует", а не случайным образом; количество волн и интервал между сериями рероллятся каждый раз (MinWaveCount..MaxWaveCount,MinWaveInterval..MaxWaveInterval), и волна автоматически скрывается, если игрок находится внутри помещения (!localPlayerController.isInsideFactoryвwhile-цикле). -
В машине (
physicsParent != null) скорость охлаждения снижена (heatTransferRate = 0.05fпротив1fна улице) — машина частично защищает от холода ветра, даже без полного укрытия.
TOXIC SMOG
- Ядовитые облака — не сплошная зона, а дискретные точечные ловушки, раздельно снаружи/внутри, с масштабом количества под размер подземелья.
- Спавн облаков гарантированно избегает входов (20 м) и не кучкуется теснее 5 м друг к другу.
- Общий туман (не облака) — отдельный объёмный туман с рандомной плотностью каждый день.
Подробное описание
Анализируемые файлы:
-
Облака ядовитых испарений — не сплошная зона, а **дискретные точечные ловушки (
hazardPrefab), которые спавнятся отдельно снаружи (fumesContainerOutside, привязаны кoutsideAINodes) и отдельно внутри комплекса (fumesContainerInside, привязаны кinsideAINodes), причём количество внутри — не то же самое, а домножено на модификатор размера подземелья:factoryFumesAmount = Ceil(fumesAmount * FactoryAmountMultiplier * dungeonSize)— на больших подземельях облаков внутри пропорционально больше. -
Спавн облаков гарантированно избегает входов:
minDistanceFromBlockers = 20fотEntranceTeleport(и от границ корабля — снаружи), и облака не спавнятся друг к другу ближеminDistanceBetweenHazards = 5f(IsPositionValid) — то есть облака физически не могут "затопить" точку входа/выхода целиком, почти всегда останется безопасный коридор прямо у входа. -
При переключении между "снаружи" и "внутри" контейнеры физически включаются/выключаются реципрокно (
OnEnable/OnDisable): пока игрок снаружи, внутренние облака выключены — то есть переход в помещение полностью убирает нагрузку от внешних испарений, они не тикают в фоне, пока неактивны. -
Туман (не облака, а общая дымка) генерируется как
LocalVolumetricFogна весь уровень с рандомной плотностью (smogFreePathмеждуMinFreePath/MaxFreePathиз конфига) — плотность каждый день своя.
TORNADO
- Три типа с разным поведением: Water вдвое медленнее и без урона (заглушка), Fire — урон горением на средней зоне статус-эффекта, Smoke — единственный тип, который физически подбрасывает игрока, и его зона статус-эффекта самая дальняя.
- Подбрасывает только Smoke-тип, при накоплении таймера у "глаза" торнадо (≤10 м), фиксированно на 10 секунд, с последующей спиральной силой вокруг точки броска.
- Притяжение к торнадо не обнуляется без прямой видимости — падает до 12.5%, а не до нуля; Smoke тянет в 3.75 раза сильнее Fire/Water.
- Полный иммунитет даёт нахождение в здании, в корабле, на ховерборде/технике/лестнице.
Подробное описание
Анализируемые файлы:
Сама погода (TornadoWeather.cs) — простая обёртка: -3/+6/-3 лимиты, ждёт 20 сек, находит точку через GetRandomTargetPosition и спавнит одно торнадо-энтити (Tornados).
- Три типа торнадо (
TornadoType: FIRE/SMOKE/WATER), у каждого свой набор эффектов:- WATER: скорость движения торнадо сразу делится пополам при инициализации (
initialSpeed /= 2) — самый медленный тип. Урон по игроку не реализован (// Drown the player.— комментарий-заглушка), статус-эффектWATERдействует только в ближней зоне (closeRange = 10f). - FIRE: статус-эффект действует в средней зоне (
midRange = 20f), при близком контакте (см. ниже) наносит 3 урона с причиной смертиBurning. - SMOKE: статус-эффект действует дальше всех (
longRange = 30f), наносит 2 урона, и это единственный тип, для которого вообще проверяется затягивание внутрь и подбрасывание игрока (HandleLocalPlayerInsideTornadoвызывается толькоif (tornadoType == TornadoType.Smoke)) — FIRE и WATER торнадо никогда физически не подбрасывают игрока, только тянут и жгут/окуривают.
- WATER: скорость движения торнадо сразу делится пополам при инициализации (
- Затягивание (только SMOKE): если игрок ближе 10 м от "глаза" торнадо и выполняются базовые условия (
TornadoConditionsAreMet), таймерtimeSinceBeingInsideTornadoкопится, иначе плавно спадает; при достижении конфиг-порогаConfigTornadoInsideBeforeThrowигрока подбрасывает (SetPlayerFlingingServerRpc) на фиксированные 10 секунд (StopFlingingPlayerждёт ровно 10 сек), после чего он крутится по спирали вокругthrowingPoint(CalculateSpiralForce= крест-произведение направления к центру и направления вверх, плюс сама тяга к центру). - Притяжение к торнадо действует всегда (кроме WATER — там весь
HandleTornadoDamageAndPullingвообще не вызывается:if (tornadoType != TornadoType.Water) HandleTornadoDamageAndPulling(...)), и сила зависит от прямой видимости через несколько "глаз" (eyes[]): если есть чистая линия обзора хотя бы до одного глаза — тяга полная, если её нет ни у одного — тяга всё равно есть, но всего 12.5% от полной (0.125f * ConfigTornadoPullStrength). Дополнительно у Smoke-типа общий множитель тяги в 3.75 раза больше, чем у Fire/Water (0.75fпротив0.2f) — Smoke-торнадо реально доминирующий тип по притяжению. - Урон применяется только на расстоянии ≤4 м прямой видимости (
bestDistanceLOS > 4f— досрочный выход), раз в 0.5 сек (DamageTimer). - Полный иммунитет к торнадо даёт нахождение внутри фабрики, внутри корабля, в вагонетке/анимации с транспортом, на ховерборде, на лестнице, в анимации с врагом или уже во время самого подбрасывания (
TornadoConditionsAreMet— все эти флаги должны бытьfalse, иначе торнадо игрока полностью игнорирует).
Погода: METEOR SHOWER ("Метеоритный дождь")
- Фоновые "красивые" метеоры в небе декоративны и не падают — угрозу несут только метеоры, запускаемые отдельным таймером через 25 сек после старта.
- Направление атаки фиксируется один раз на весь день (если не выбран Random) — все метеоры дня летят с одной стороны света.
- Спавн новых метеоров прекращается по истечении доли дня, но уже летящие долетают.
- Удар может заспавнить один из 6 видов кристаллов и создаёт взрыв + уничтожение деревьев в радиусе 15 м.
Подробное описание
Анализируемые файлы:
-
При активации сразу спавнит декоративные фоновые метеоры высоко в небе (
SpawnOverheadVisualMeteors, 15–45 штук + один "большой" с масштабом 7.5–12.5) — они не падают и не угрожают, только летают по кругу (SetupAsLooping,StopMoving()). -
Опасные метеоры начинают падать только через 25 секунд после старта погоды (
MeteorSpawnerHandler), пачками отminMeteorsPerSpawnдоmaxMeteorsPerSpawnштук с рандомной паузой между пачками. -
Направление атаки выбирается один раз за весь день случайно из 5 вариантов (Random/East/West/North/South), если выпало не Random, то все метеоры этого дня будут падать с одной и той же стороны света (
CalculateSkyOrigin), что даёт возможность заранее понять, откуда ждать угрозу, посмотрев на пару первых падений. -
Спавн новых метеоров автоматически прекращается по достижении
normalisedTimeToLeave(доля игрового дня, конфигурируема), но уже летящие в этот момент метеоры долетают и падают как обычно, просто новых больше не появляется. -
При ударе о землю: шанс (
ConfigMeteorShowerMeteoriteSpawnChance) заспавнить один из 6 видов кристаллов-руды (Аметист/Сапфир/Изумруд/Кварц/Рубин/Топаз) прямо в точке падения, плюс взрыв (CRUtilities.CreateExplosion, радиус 15, сила 4) и уничтожение деревьев в радиусе 15 м, плюс кратер, который висит 60 секунд и потом исчезает.
Анализ скрытых погод
Искомые погоды никак не анализируются.
FIRESTORM / HURRICANE
Важное открытие сразу: у FireStormWeather и HurricaneWeather фактическое создание торнадо — мёртвый код. В обоих файлах метод SpawnTornado() содержит только пустой for-цикл с закомментированной строкой создания энтити (// var tornado = RoundManager.Instance.SpawnEnemyGameObject(...)). Рабочая версия спавна есть только в TornadoWeather.cs: RoundManager.Instance.SpawnEnemyGameObject(target, -1, -1, LethalContent.Enemies[CodeRebirthEnemyKeys.Tornado].EnemyType). Это стоит явно пометить в инфобуке как "на момент разбора кода не функционирует / вероятно WIP-заглушка", а не описывать так, будто механика активна.
Анализируемый файл:
-
Меняет лимиты спавна врагов:
outsidePower: -3, insidePower: +6, dayTimePower: -3, т.е. меньше уличных угроз, но заметно больше внутренних, и это единственный реально работающий эффект на данный момент. -
При включении собирает список
outsideAINodes, отфильтрованных по близости (CullNodesByProximity(nodes, 5.0f, true, true)), и через 20 секунд после старта дня пытается выбрать точку спавна — но дальше упирается в пустой цикл.
GODRAYS
Исходный код:
-
Каждый
GodRayописывается верхней точкой в "небе" (TopPosition) и нижней точкой на земле (BottomPosition) с своими радиусами —GodRaySpotlightDataвычисляет через теорему косинусов угол и позицию света так, чтобы луч визуально сходился в правильную точку неба. -
У основания каждого луча стоит триггер-коллайдер с уроном (
DoTheThingWithCollider→BetterCooldownTrigger), настроенный на смерть от сгорания (DeathAnimation.Burnt,CauseOfDeath.Burning), с сообщением игроку"Melting..." / "Your body is melting.........". -
Меш триггера строится как конус из
pointCount(по умолчанию 40) точек вокруг основания луча — то есть зона поражения не точка, а полноценная окружность заданногоBottomRadius, и стоять на самом краю круга физически безопаснее, чем в центре, но триггер есть по всей площади круга. -
Луч динамически подстраивается под положение и
farClipPlaneкамеры игрока (OnBeginCameraRendering) — рескейлится каждый раз, когда меняется дальность прорисовки или позиция камеры.