You are viewing a potentially older version of this package. View all versions.
RomanTS-SunMirror-0.1.6 icon

SunMirror

Catch the sun with a frisbee-mirror and blind a teammate: they cover their face, freeze with spiral eyes, and can't move until you look away. Hold the beam on their eyes long enough and they drop.

Date uploaded 2 weeks ago
Version 0.1.6
Download link RomanTS-SunMirror-0.1.6.zip
Downloads 59
Dependency string RomanTS-SunMirror-0.1.6

This mod requires the following mods to function

BepInEx-BepInExPack_PEAK-5.4.75301 icon
BepInEx-BepInExPack_PEAK

BepInEx pack for PEAK. Preconfigured and ready to use.

Preferred version: 5.4.75301

README

Sun Mirror ☀️🪞

Catch the sun with a frisbee and blind your teammate with the reflection.

Sun Mirror in action

Pick up a frisbee, hold left mouse button, and a bright spot of sunlight crawls across whatever you point at. Walk it up a teammate's body to their eyes and they get dazzled: hands up over the face, frozen in that pose, spiral eyes, dazed mouth, unable to move or look away until you point the mirror somewhere else.

Hold the beam on their eyes for five seconds and they drop, stunned for a moment. Then they get up. Nobody dies, and the same target cannot be blinded again for four seconds — a frisbee should not be able to switch a person off.

What the victim actually sees

with the mod vanilla, no mod
Hands up over the face yes yes
Head flinches back yes yes
Spiral eyes, dazed mouth yes no
Frozen in place, look locked yes no
Blinding flare on their screen yes no
Falls over after five seconds yes yes

The pose and the flinch travel through the game's own networking, so strangers who have never heard of this mod still react visibly. Everything else needs the mod on their side.

There is no way around that, and there should not be. Delivering the rest means running code on someone else's machine, and nothing should ever do that unless they installed it themselves. If you want the whole effect, everyone installs the mod.

Requirements

  • BepInEx (BepInExPack_PEAK) — your mod manager pulls this in automatically.
  • Daylight. The beam only works when the sun actually reaches you: not at night, not in shade, not indoors. That is deliberate.

Install

Use a mod manager (r2modman / Gale / Thunderstore Mod Manager): find Sun Mirror, click Install, launch modded.

Manual: drop Roman.SunMirror.dll into BepInEx/plugins/. Nothing else is needed.

Configuration

BepInEx/config/Roman.SunMirror.cfg, or an in-game config manager.

  • Spot — Size, SpreadPerMeter, SpriteBrightness: size and punch of the light spot.
  • Emote — BlindHoldSeconds, BlindMouthName, PoseFreezeAt: how long the daze lasts, which mouth the victim gets, and which frame of the animation the pose holds on.
  • Emote — VanillaFlinch, VanillaFlinchForce: the head flinch that reaches players without the mod. Set the force low; high values knock people over.
  • Reflection — RealMirror, RealMirrorResolution: the actual mirror surface. Lower the resolution if you are tight on performance.
  • Debug — DebugKeys: developer keys, off by default.

Notes

The mirror is the game's own frisbee, reskinned. It is held low with both hands so it never blocks your view, and the face turned towards your target is a real reflective surface rendered with the game's own mirror shader. You see the back of it, they see the glare — like a real signal mirror.

Credits

Built with PEAKLib and BepInEx. No third-party models or textures are bundled: everything is either generated at runtime or reused from the game's own assets.

Feedback

Something broken, or an idea? Open an issue: https://github.com/romatstrv-stack/SunMirror/issues

Thunderstore has no comments, so that repo is the only place I can hear you. Bug reports with your BepInEx/LogOutput.log attached are the most useful.

CHANGELOG

Changelog

0.1.22

  • Клавиша-дубль (Controls/UseKey, по умолчанию F) снова работает. Она требовала _held != null — ссылку на старый предмет-проп, — а с нынешним зеркалом как viewmodel-клоном _held пуст. В логе это выглядело как «F press: held=НЕТ holdingMirror=True active=False»: нажатие проходило, калитка не открывалась. Теперь условие принимает оба случая.

0.1.21

  • Тряска ещё сильнее: Effect/FallShake 10 (как срыв на стене у самой игры), Emote/RootFallShake 5. Напоминание про потолок: у игрока с настройкой «меньше тряски» значение делится на 2 и обрезается до 3, выше 6 смысла нет.
  • Отладка, чтобы поймать пропажу зайчика: раньше калитка луча молчала. Условие включения — (_holdingMirror && ЛКМ) || (_held != null && клавиша F), и по логу нельзя было отличить «не нажимал» от «нажимал, но калитка закрыта»: клавиша F не логировалась вовсе. Теперь логируются нажатие F (с состоянием held, holdingMirror, active) и два молчавших выхода UpdateSpot — «персонажа или камеры нет» и «луч ни во что не попал».

0.1.20

  • Тряска камеры поднята до масштаба игры. В игре её не чувствовалось вовсе, и дело оказалось не в доставке, а в единицах. RPCA_Fall зовёт AddPerlinShake(shake, 0.4f), а сама игра работает так: приземление — 5, срыв на стене — 10, лава — 5, удары по интерфейсу — до 30. Наши 0.6 и 1 были в разы ниже самой слабой штатной тряски, то есть неотличимы от нуля. Новые значения по умолчанию: Effect/FallShake 5 (было 0.6), Emote/RootFallShake 2.5 (было 1; оно обновляется каждые 0.5 с, поэтому мягче). Выше 6 ставить бессмысленно: у игрока с настройкой «меньше тряски» значение делится на 2 и обрезается до 3 (amount *= 0.5f; amount = Mathf.Min(amount, 3f)). Поправка к записи 0.1.16: там сказано, что тряска «теперь доходит». Доходила она и тогда — просто амплитудой, которой не видно.
  • Проверено в игре на 0.1.19: падение на четвёртой секунде работает, удержание держит, вбок больше не уйти. Якорь из 0.1.18 и убранный UnFall из 0.1.19 подтверждены живым прогоном.

0.1.19

  • Починено: на четвёртой секунде жертва не падала в стан. Падение отправлялось и тут же отменялось — своим же вызовом. Снятие по потолку идёт TryFireCap → DropVictim (RPCA_Fall на 1.6 с) → RestoreBlind → UnrootVictim, а тот с 0.1.16 шлёт RPCA_UnFall, который обнуляет fallSeconds. Всё в одном кадре и в том же порядке по сети: UnFall приходил последним и гасил падение. В логе это видно прямо — строка «Падение ... на 1,6 с, по сети» и сразу за ней «Blind RESTORE». В игре выглядело так: эмоуты прекращаются, стана нет, можно убежать. RPCA_UnFall был нужен в 0.1.16, когда само удержание шло падением. После возврата на крепление в 0.1.17 он стал чистым вредом и убран: fallSeconds стекает сам, а удержание снимается чисткой сустава.

0.1.18

  • Починено: от прибитого зайчика можно было уйти, просто давя вбок. Крепление обновлялось каждые полсекунды по ТЕКУЩЕМУ месту бедра — то есть работало храповиком: человек, упираясь в сторону, за полсекунды сдвигался на шаг, а следующее крепление принимало этот шаг за новую норму. Так и выходил из луча, медленно, но беспрепятственно. Теперь якорь берётся один раз на эпизод, и все обновления тянут к одной и той же точке. Если жертву всё же унесло дальше 2.5 м (упала, столкнули), якорь переносится к ней — тащить человека через полкарты на верёвке незачем.
  • Починено: спиральные глаза оставались навсегда, даже когда луч не светит. Виноваты два обстоятельства, наложившиеся друг на друга. Первое: оба метода игры, которые ставят и снимают спираль, заперты за одним флагом — CharacterPassedOut и OnRevive_RPC начинаются с if (!forcePresetEyes), — а мод этот флаг сам поднимает, чтобы игра не возвращала обычные глаза каждый кадр. Второе: BeginBlind срабатывал повторно поверх живого эффекта (в логе два «Blind begin» подряд без снятия между ними), и новое состояние затирало прежнее. Вместе это давало замок: forcePrev записывался как true, потому что флаг уже был поднят первым вызовом, а бэкап лица снимался с уже подменённых текстур — «оригиналом» становилась спираль. После такого возврат ставил спираль обратно, а игре возврат был заперт поднятым флагом. Теперь повторный BeginBlind сначала честно снимает прежний эффект, а в бэкап не попадает то, что уже подменено.
  • Отладка: F1 ответил на вопрос, открытый с 1 ноября. Ни один префаб игры не несёт ослепления в полях — Affliction_Blind игроку БЕЗ мода действительно недостижим. Канала, который передал бы его, в игре нет.

0.1.17

  • Обездвиживание возвращено на прибитие: падение убивало всё зрелище. Проверка в живой игре показала то, чего не видно по коду: с RPCA_Fall жертву «станит сразу», без эмоутов и без нарастания. Причина в устройстве персонажа. Падение обнуляет контроль над телом (GetTargetRagdollControll() возвращает 0), а персонаж в PEAK — физическая кукла: аниматор только задаёт ей цели, которые отрабатывают мышцы. При нулевом контроле цели ни на что не влияют, и руки к лицу, «нет-нет», удержание позы — всё пропадает. Остаётся мгновенно обмякший человек. Выбор здесь жёсткий, одно взамен другого: либо полноэкранный пост-эффект игры и обмякшее тело, либо поза и мимика. Поза важнее, в ней весь мод, поэтому удержание снова идёт через RPCA_Stick, а пост-эффект остаётся на финал — в падении на четвёртой секунде, где он и задуман.
  • Механизм теперь один на все случаи: крепление не роняет ни стоящего, ни висящего на стене, так что разделение «на земле иначе, чем в воздухе» не нужно. Emote/RootFallSeconds убран — он относился только к падению. Тряска камеры осталась: RPCA_Fall(0f, shake) счётчик не поднимает (if (seconds > data.fallSeconds)), а AddPerlinShake зовётся, то есть трясёт, не обмякая.
  • Уточнение к записи 0.1.16: там сказано, что переприбивание из 0.1.15 «лечило криво». Это было сказано зря — ту починку на живом ванильном клиенте не проверяли ни разу. Лазейка с прыжком известна по коду игры (JumpStickEffect добавляет 0.5–1.5 с при пороге 4 с), но новое крепление счётчик обнуляет, а обновление идёт каждые 0.5 с, так что напрыгать побег за полсекунды нечем. Проверять это надо в игре, а не в рассуждении.

0.1.16

  • Обездвиживание переведено с прибития на падение. Прибитие (RPCA_Stick) держало плохо по причине, заложенной в самой игре: jumpAction подписан на JumpStickEffect, и каждый прыжок приближает освобождение на 0.5–1.5 секунды при пороге в 4. Переприбивание каждые 0.8 секунды из 0.1.15 это лечило, но криво. Теперь идёт RPCA_Fall, и он делает на клиенте жертвы сразу три вещи: fallSeconds обнуляет контроль над телом (GetTargetRagdollControll() возвращает 0, как у потерявшего сознание), поднимает вес Volume у FallPost — то есть у жертвы идёт полноэкранный пост-эффект самой игры, — и трясёт камеру. Прыжком это не обойти: при нулевом контроле прыгать нечем. Урона нет. Настройки: Emote/RootFallSeconds (1.2), Emote/RootFallShake (1.0), Emote/RootRepeatSeconds (0.5 — частота обновления).
  • Починено: тряска экрана никогда не доходила до жертвы. Падение в конце ослепления шло через Character.Fall, а тот молча глушит тряску для чужих персонажей: if (!refs.view.IsMine || screenShake < 1E-05f) screenShake = 0f;. Настройка Effect/FallShake существовала с самого начала и не делала ничего. Теперь тряска уходит отдельным адресным вызовом — лично жертве, а не всему лобби: GamefeelHandler в игре синглтон, и общий вызов затряс бы камеры всем.
  • На стене и в воздухе теперь не отпускает, а держит. Первая версия этой правки просто не трогала висящих — ронять их нельзя, в игре про подъём на гору это означает сорваться. Но и отпускать незачем, так что для них идёт другой механизм: RPCA_Stick — крепление к миру с запертым перемещением и свободным вращением. Человек остаётся жёстким и висит там, где был, состояние климба игра при этом не трогает вовсе. Падения нет и быть не может: пока крепление держит, HandleStickUpdate каждый кадр зовёт ClampSinceGrounded(0.5f), так что счётчик полёта не растёт и урон от падения не копится. Тряска до них доходит тоже — RPCA_Fall(0f, shake). Ноль секунд счётчик не поднимает, внутри стоит if (seconds > data.fallSeconds), а AddPerlinShake зовётся всё равно: получается чистая тряска камеры без обмякшего тела.
  • Отладка: F1 теперь, кроме списка имён, ищет префабы, у которых состояние прописано в полях — компоненты AOE и Action_ApplyAffliction. Это нужно, чтобы закрыть вопрос про своё ослепление игры (см. CLAUDE.md): передать его игроку без мода можно только через префаб, в котором оно уже настроено, а по файлам данных это не проверяется.
  • Состояние игры (BlindStatusType, BlindStatusAmount) вынесено в отдельный метод и по-прежнему выключено по умолчанию. Оно больше не привязано к механизму обездвиживания.

0.1.15

  • Ослеплённый больше не уходит прыжками. Главное обещание мода — «не может двигаться, пока на него светят» — у игроков БЕЗ мода не выполнялось: человек спокойно уходил в сторону, останавливаясь лишь на секунду анимации эмоута. Причина в самой игре: каждый прыжок приближает освобождение от удержания на 0.5–1.5 секунды при пороге в 4, а мод прибивал жертву ровно один раз и больше к этому не возвращался. Теперь прибивает заново, пока держится луч. Частота — Emote/RootRepeatSeconds, по умолчанию 0.8 секунды; 0 возвращает прежнее поведение. Потолок в 4 секунды и пауза после него не тронуты, окно для побега у жертвы остаётся.
  • Новое: экранный эффект можно передать игроку без мода. Раньше считалось, что это невозможно в принципе. Оказалось, у игры есть свой канал: RPCA_Stick несёт состояние вместе с удержанием, и клиент жертвы сам играет звук, частицы и эффект на экране. Включается настройками Emote/BlindStatusType (по умолчанию Drowsy) и Emote/BlindStatusAmount. По умолчанию выключено: состояния в PEAK наносят настоящий урон и копятся, величину надо подбирать с малого. Spores не ставьте — при 0.5 начинается превращение в зомби.
  • Отладка: дамп префабов переехал с G на F1. На G по умолчанию висит Controls/EquipKey, и одно нажатие делало сразу два дела — брало зеркало и сыпало в лог список префабов.
  • Отладка: описание Debug/DebugKeys в конфиге приведено к тому, что на самом деле обрабатывается. Прежний список отстал от кода.
  • Отладка: починено рукопожатие двух клиентов на одной машине — терялся регион Photon и подменялась аутентификация. Правда, выяснилось, что двум клиентам с одного Steam-аккаунта в одну комнату не попасть никак: сервер выдаёт идентификатор по Steam-билету. Нужны два аккаунта.

0.1.14

  • Зеркало больше не светит само по себе. У вспышки на диске висел настоящий точечный светильник — яркость 12, радиус 8 метров, во все стороны, прямо в руке у игрока: вокруг тебя подсвечивались земля, камни и стены. Светить должно туда, куда направлен зайчик, и больше никуда. Светильник выключен, светящаяся картинка вспышки осталась. Вернуть можно в конфиге, Spot/SignalLightBrightness.
  • Блик и вспышку больше не видно с задней стороны зеркала. Тот, кто держит зеркало, видел у себя в руках яркую звезду, хотя смотрит диску в спину. Теперь обе вещи считают лицевую сторону по лучу: их видит только тот, кто стоит со стороны зайчика.
  • Пульсация яркости свелась к одной настройке Model/FlashPulseAmount. Раньше таких синусоид было три в разных местах, и выключение одной ничего не меняло. 0 — совершенно ровное свечение.
  • Для отладки: клавиша Y (при Debug/DebugKeys = true) выдаёт боту зеркало и заставляет светить в тебя — чтобы посмотреть на ослепление глазами жертвы.

0.1.13

  • Свой сетевой вызов мода теперь уходит только игрокам, у которых мод стоит. Раньше он рассылался всем, и у игроков без мода Photon писал в лог ошибку «метод не найден» на каждую посылку луча. Мод отмечается в каждой комнате свойством игрока и шлёт свой вызов только отмеченным.
  • Для игроков без мода ничего не изменилось: руки к лицу, удержание на месте, спиральные глаза и падение по-прежнему доставляются родными вызовами игры.

0.1.12

  • Проверено на PEAK 3.0.1 (обновление игры 24.10.2026): мод загружается без ошибок, зеркало, луч и стенд F4 работают. Код не менялся.

0.1.11

Игроки БЕЗ мода теперь видят почти весь эффект. Было три пункта из восьми, стало пять. Нового кода для этого почти не потребовалось: нужно было найти в самой игре сетевые методы, делающие то же самое, — их исполняет клиент жертвы своим кодом, и мод ему не нужен.

  • Обездвиживание (Emote/RootVictim). Родной RPCA_Stick прибивает цель на месте. Урона нет: внутри стоит проверка на величину статуса, мы передаём ноль. Зависнуть нельзя — игра сама снимает удержание через 4 секунды, причём командует этим клиент самой жертвы, так что вылет стрелка ничего не ломает. На карабкающихся не применяется.
  • Спиральные глаза (Emote/SpiralEyesForEveryone). Родной CharacterCustomization.CharacterPassedOut — чисто косметический: ставит текстуру и включает вращение. Настоящая отключка живёт в другом методе и не затрагивается: человек не теряет сознание, у него просто крутятся глаза.
  • Потолок сведён к 4 секундам. Было пять, и получался разрыв: на 4-й секунде игра снимала удержание, жертва секунду стояла свободной и только потом падала. Теперь освобождение и падение совпадают.

Чего передать нельзя и почему: рот и засвет экрана сетевого пути в игре не имеют, а вспышки света нет среди ресурсов — в них 285 префабов, и подходящего не нашлось. Это предел, а не недоделка.

0.1.10

  • У мода появился свой Discord: https://discord.gg/ARuemW9sbB — ссылка бессрочная, проверено через API Discord (expires_at = null). Обычные приглашения живут сутки, и в описании мода такая ссылка быстро стала бы битой. GitHub Issues остаётся вторым каналом для тех, кому так привычнее.
  • Отладка: клавиша G печатает список префабов, которые игра может создать у ЛЮБОГО клиента по сети — в том числе у игрока без мода. Нужно для работы над тем, чтобы эффект был виден всем, а не только владельцам мода. Автоматический вывод при запуске — Debug/PrefabDumpSecondsAfterStart.

0.1.9

Падение в конце ослепления наконец работает так, как написано. Три причины, по которым оно не наступало, — все три в нашем коде.

  • Потолок не зависит от сети. Проверка жила только в обработчике обновления эффекта, а он вызывается, пока стрелок шлёт ослепление по сети. При удержании луча обновление шло каждые 0.25 с и всё работало; при одном попадании потолок не проверялся вовсе и падение не наступало никогда. Теперь проверка идёт каждый кадр, независимо от сетевых посылок.
  • «Фитиль» горел вдвое медленнее реального времени. При накоплении слив вычитался и за текущий кадр тоже: прибавили dt и тут же отняли dt × BlindChargeDrain. Потолок в 5 секунд набирался за 10. Теперь сливается только то время, что цель была без луча.
  • Debug/SelfTarget больше не переживает перезапуск. Это тестовый тумблер (клавиша J), но он сохранялся в файл: одно случайное нажатие — и мод молча начинал слепить хозяина вместо цели, причём в логе всё выглядело исправным. Теперь гасится при каждом запуске, и если в конфиге стояло «включено», об этом пишется предупреждение.

Проверено на лезущем по скале: ослепление держится, падение срабатывает, персонаж не срывается и не гибнет. Защиту «не ронять карабкающихся» решено не добавлять — она оказалась не нужна.

Первая строка описания возвращена к прежней: вариант про уступ обещал, что ослеплённый сорвётся вниз, а это неправда.

0.1.8

  • Новая иконка: рисунок в стиле карточек PEAK вместо прежнего скриншота. Старая была собрана из далёкого кадра, где голова персонажа занимала около 80 пикселей, и растянута до 256 — отсюда каша. Новая собрана уменьшением один раз из рисунка 1254x1254, поэтому резкая, и читается даже в списке модов, где иконку показывают размером с ноготь.
  • Гифка на странице стала 800x450 вместо 480x270 при том же весе: страница показывает её без растягивания.

0.1.7

  • Страница мода переписана. Видимая часть втрое короче, технические подробности убраны в сворачиваемые блоки, фидбек поднят из подвала наверх. Заголовок таблицы совместимости теперь прямо заявляет главное: эффект работает и на тех, у кого мода нет.
  • Spot/UseLight — отдельный выключатель прожектора в точке попадания. Нужен, чтобы проверять причастность света к мерцанию соседних поверхностей без пересборки мода.

0.1.6

  • На странице мода появился ролик: пятно приходит по песку к лицу, жертва закрывается руками, спиральные глаза, и через пять секунд падение.
  • Свечение зеркала больше не раскачивает автоэкспозицию игры. Оно пульсировало синусоидой с периодом 0.52 с, зеркало занимает низ экрана и светится ярче белого — экспозиция шла за этой пульсацией, и вместе с зеркалом «дышала» вся картинка. Со стороны это читалось как мерцание поверхностей, хотя поверхности были ни при чём. Пульсация и яркость вынесены в настройки: Model/FlashPulseAmount, Model/FlashEmission, Model/FlashPulseSpeed.
  • Прожектор зайчика больше не подсвечивает то, что стоит за целью. Его дальность задавалась с запасом «размер + 0.5 м»: на песке запас уходил под землю и был незаметен, а при попадании в лицо за поверхностью оказывалась стена, и на ней загоралось второе, паразитное пятно (Spot/LightReachBeyond).

0.1.5

  • Падение включено по умолчанию. Жертва валится с ног, накопив пять секунд ослепления в глаза, и встаёт через полторы. Повторно ослепить ту же цель нельзя ещё четыре секунды.

    До этой версии падение было выключено: оно летит штатным RPC игры и валит даже тех, у кого мода нет. Автор решил, что так и надо — зайчик в глаза, от которого можно просто уйти, не читается как шутка. Выключается в Emote/FallAtMaxBlind.

0.1.4

Зайчик стал вести себя так, как от него и ждёшь: слепит глаза, а не тело, и гаснет вместе с лучом.

  • Слепит только попадание в глаза. Раньше ослепление вызывало попадание в любую часть тела — хватало луча по ноге. Теперь цель определяется по попаданию в голову. Пятно при этом по-прежнему ползёт по телу и его видно со стороны: оно идёт по земле, поднимается по человеку и слепит, только дойдя до лица. Точка глаз берётся у самой игры, а не высчитывается по кости головы — кость смещена относительно глаз.
  • Зайчик работает только когда зеркало реально в руках. Прежде мод считал зеркало взятым, если тарелочка просто нашлась в сцене: лежащая на земле или чужая в руках соседа тоже подходили, и луч светил с пустыми руками.
  • Жертва жмурится непрерывно, пока светят. Поза больше не держится на одном кадре: участок эмоута прокручивается по кругу всё время ослепления. Старое поведение осталось на Emote/PoseLoop = false.
  • Глаза открываются сразу, как луч ушёл. Спиральные глаза держались ещё три секунды после ухода луча, а нативная пелена — две с половиной. Теперь и то, и другое гаснет почти сразу (BeamLingerSeconds, BlindAfflictionSeconds).
  • Падение в конце ослепления наконец срабатывает. Потолок считался от непрерывного ослепления, поэтому после правки выше любой разрыв луча обнулял счёт и пять секунд подряд стали недостижимы. Теперь по цели копится «фитиль»: он растёт, пока луч в глазах, и стекает вдвое медленнее, когда луч ушёл (Emote/BlindChargeDrain). По-прежнему выключено по умолчанию — см. ниже.
  • Свой звук при попадании в глаза — колокольчик (synth:bell). Ни один из 885 звуков игры не подошёл: все они про удар, падение или интерфейс. Клип синтезируется в коде, поэтому в пакете не появилось ни одного аудиофайла. Громкость и высота правятся на ходу: SynthVolume, SynthPitch. Есть ещё пять вариантов характера: shing, sparkle, wobble, zap, hum.
  • Порядок реакции стал правильным: сначала жертва закрывается руками, и только потом начинает отмахиваться (no no no). Прежде чередование перекидывало на второй эмоут на первом же кадре, и человек отмахивался от того, чего ещё не было. Задержка настраивается: Emote/EmoteFirstHoldSeconds. Само чередование теперь включено по умолчанию — имя второго состояния подтверждено в игре.
  • Падение работало только на сетевых персонажах. Character.Fall отправляет RPC через PhotonView, и у персонажа без регистрации в сети он уходил в пустоту — молча, без ошибки. Теперь для таких целей вызов идёт напрямую.
  • Удар головой и звук повторяются не чаще раза в 1.2 с (Emote/ReactionEverySeconds) — иначе при поводке луча по лицу голова дёргалась несколько раз в секунду, а звук превращался в треск.

0.1.3

Совместимость с PEAK 3.0.0 и инструменты отладки.

  • Пересобрано против новой версии игры. Проверено: мод грузится, рескин, эмоут, ослепление и сеть работают. Из того, чем мод пользуется, обновление задело одно: у NetworkingUtilities.ConnectToNetwork добавился параметр — это ломало только отладочный автовход, не обычную игру.
  • Отладка: F4 собирает испытательный стенд одной клавишей (зеркало, полдень, бот), F3 ослепляет ближайшего персонажа без прицеливания — чтобы проверять вид эффекта отдельно от попадания луча.
  • Debug/FreezeNoon — держать игровое время на полудне.

0.1.2

Исправление: поза «руки к лицу» не проигрывалась вообще.

Значение Emote/EmoteName по умолчанию было русской локализованной подписью («ну хватит»). Сопоставление идёт по локализации игры, поэтому у игрока с английским (то есть почти у всех) имя не находилось, и в аниматор уходил сам текст настройки. Состояния с таким именем нет — поза молча не проигрывалась, а в логе не было ни слова.

  • Умолчание переведено на устойчивый английский ключ Defeat.
  • Если имя не сопоставилось, мод теперь пишет об этом в лог вместо молчания.

0.1.1

  • Реакция ослеплённого стала живой: поза больше не прибита к одному кадру, руки у лица шевелятся (Emote/PoseSwing). Замершая фигура со стороны читалась как зависший персонаж, а не как человек, которому бьют в глаза.
  • Жертву пошатывает, пока держится ослепление, и встряхивает в конце (StaggerForce, WobbleForce, ShakeOffForce). Толчки идут штатными сетевыми методами игры, поэтому их видно и у игроков БЕЗ мода.
  • Появилась возможность чередовать позу со вторым эмоутом (BlindEmoteB) и играть звук из набора самой игры при попадании (BlindSfxName). Оба по умолчанию выключены — см. ниже.
  • Добавлена ссылка на репозиторий: теперь есть куда написать об ошибке.

0.1.0

Первая версия. Зеркало-тарелочка ловит солнце и пускает зайчик в тиммейта.

  • Предмет — игровая тарелочка (Frisbee): держится двумя руками внизу. Поверхность, обращённая к тиммейту, настоящее зеркало (шейдер игры GD/Mirror).
  • Зайчик расходится на 9 мм с каждым метром — настоящая расходимость солнечного зайчика (угловой размер Солнца 0,53°). На 60 метрах пятно около метра. Работает только когда солнце реально падает на игрока: день и не в тени.
  • Попадание в глаза: жертва закрывает лицо руками, спиральные глаза, «поплывший» рот, не может двигаться и отвести взгляд.
  • Защита от удержания: не дольше 5 секунд подряд, затем 4 секунды по той же цели ничего не проходит. Зеркалом нельзя выключить человека из игры.
  • Ослепление летит по сети. У кого мод — видит эффект целиком. У кого мода нет — всё равно видит позу и получает отдёргивание головы.

Настройки, выключенные по умолчанию

Выключены не случайно: это то, что я не смог подтвердить сам.

  • Emote/EmoteSwapSeconds — чередование позы со вторым эмоутом. Имя второго состояния в бою подтвердить не удалось; при опечатке поза пропадала бы через раз.
  • Spot/ContrastRing — тёмный ободок вокруг зайчика для читаемости на ярком песке. Собран, но в игре не проверен.

Известное ограничение

Спиральные глаза, рот и обездвиживание доставляются своим RPC, поэтому у игрока без мода их не будет. Это ограничение Photon, обойти нельзя. Жертва с предметом в руках не показывает позу «руки к лицу» — анимация удержания предмета её перебивает.