Molotov/Incendiary Grenade

Molotov/Incendiary Grenade 1.1.0

Нет прав для скачивания

medusa

Скриптер
Проверенный
Сообщения
3
Реакции
27
Баллы
13
medusa добавил(а) новый ресурс:

Molotov/Incendiary Grenade - Плагин добавляет Коктейль молотова и Зажигательную гранату

Плагин добавляет полноценные зажигательные гранаты Коктейль молотова (Террористы) и Зажигательной граната (Контр-Террористы).

Посмотреть вложение 1391Посмотреть вложение 1393

Геймплей​

  • Добавляется в слот гранаты (слот 4)
  • Взрывается при ударе о землю, создаёт зону горения
  • Нарастающий урон — чем дольше игрок в огне, тем больше урон (до...

Узнать больше об этом ресурсе...
 

Хронология доработки прошлой версии коктейля молотова.

line-mlt.png
line-mlt.png


Этап 1. Аудит и критические исправления

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

Что было найдено:
  • Энтити с бесконечными Think-циклами не имели механизма самоочистки — при определённых условиях они жили вечно и съедали лимит edicts
  • Отсутствовала проверка is_nullent() перед обращением к энтити — race condition с FL_KILLME флагом (движок не удаляет энтити мгновенно, Think может вызваться повторно после постановки флага)
  • Зоны тушения дымом SmokeRadius не имели самоочистки

Что было сделано:
  • Добавлена валидация указателей перед всеми операциями с энтити
  • Реализована самоочистка SmokeRadius через SetThink с таймером
  • Добавлена проверка линии прямой видимости (line-of-sight) для режима урона 1
  • Внедрена система EnableHookChain / DisableHookChain — хуки урона активны только при наличии живых зон огня, в остальное время отключены для экономии ресурсов

Ключевое решение: хранение состояния через entity var fields var_iuser1..var_iuser4, var_fuser1..var_fuser3, var_vuser1..var_vuser2. Это стандартный паттерн GoldSrc — каждая энтити несёт свои данные в встроенных полях entvars_t, без внешних массивов.


Этап 2. Зажигательная граната и разделение по командам​

Главная фича этапа — у ТТ и КТ разные гранаты с разными моделями, спрайтами и визуальными эффектами.

Реализовано:
  • ТТ получают молотов: горящий фитиль в полёте env_sprite + var_aiment, вспышки при удержании, оранжево-фиолетовые спрайты огня
  • КТ получают зажигательную гранату: без фитиля и вспышек, другие спрайты огня, эффект искр TE_SPARKS (12.5% шанс за тик)
  • Разные HUD-иконки: weapon_molotov.txt для ТТ, weapon_incendiary.txt для КТ
  • Нарастающий урон: чем дольше игрок стоит в огне, тем сильнее урон (множитель растёт от 1.0 до molotov_damage_max_mult)
  • Дымовая граната тушит огонь, зона тушения живёт molotov_smoke_duration секунд

Проблема: идентификация типа гранаты. Первая реализация определяла тип по текущей команде владельца get_member(id, m_iTeam). Это ломалось, когда КТ подбирал молотов ТТ — граната становилась зажигательной.

Решение: тип записывается один раз при выдаче в var_iuser4 на энтити оружия и «путешествует» по цепочке: оружие в инвентаре → летящая граната → зона огня. Всё поведение читает из var_iuser4, никогда не из команды текущего владельца.

Проблема: WeaponList и HUD-иконки. GoldSrc отправляет WeaponList MSG_INIT один раз для всех клиентов при старте карты. Нельзя отправить разные иконки разным игрокам через MSG_INIT.

Решение: при выдаче гранаты конкретному игроку отправляется WeaponList MSG_ONE с правильным именем оружия. Клиент обновляет определение слота только у себя. Перехватчик HookWeaponList трогает только MSG_INIT, пропуская MSG_ONE без изменений.


Этап 3. Вода и грабли движка​

Реализация поведения гранаты в воде оказалась сложнее, чем казалось.

Проблема: SOLID_TRIGGER иногда не срабатывал в воде. Видимо в GoldSrc энтити с SOLID_TRIGGER могут тонуть в воде, а Touch коллбэки перестают вызываться. Граната, упавшая в воду, просто лежала на дне и не могла быть подобрана.

Решение: для надежности заменили Touch на SetThink с поллингом — каждые 0.3 секунды проверяем ближайших игроков через FindEntityInSphere. Это надёжнее, чем коллизия.

Проблема: модель гранаты в воде.** КТ граната, упавшая в воду, показывала модель молотова вместо зажигательной.

Решение: тип гранаты читается из var_iuser4 при создании визуала, не из текущей команды владельца (тот же паттерн, что и везде).

Этап 4. Цилиндрическая модель урона​

Стандартная сферическая проверка урона в GoldSrc FindEntityInSphere наносила урон игрокам этажом выше или ниже зоны огня.

Проблема: игрок на втором этаже получал урон от огня на первом, потому что его 3D-расстояние до центра огня было меньше радиуса.

Решение: цилиндрическая проверка:
Горизонтальное расстояние: sqrt(dx*dx + dy*dy) <= flRadius + 32
Вертикальное окно: dz >= -64 и dz <= +120

Почему именно [-64, +120]: центр огня на уровне пола, центр модели стоящего игрока на ~36 юнитов выше пола. Ступеньки добавляют 32-64 юнита. Второй этаж — обычно 128+ юнитов. Окно +120 покрывает все одноуровневые ситуации, но не задевает этаж выше.

Та же проверка применена к размещению визуальных эффектов — спрайты дебриса и модели огня не рисуются на поверхностях за пределами вертикального окна.


Этап 5. Режим разгорания​

Новая механика: после половины времени горения радиус зоны огня увеличивается.

Реализация:
var_fuser2 на зоне огня хранит текущий эффективный радиус
var_fuser3 хранит gametime, когда нужно расшириться (или -1.0 если уже расширились / режим выключен)
- В ThinkFire проверяется: если flCurTime >= flSpreadTime — радиус увеличивается, SetSize обновляется, var_fuser3 ставится в -1.0

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


Этап 6. Равномерное распределение частиц​

Спрайты дебриса TE_SPRITE должны заполнять весь диск зоны горения равномерно. Первая реализация использовала случайные углы и MakeVectors — точки кластировались по краям.

Попытка 1: спираль Вогеля (Vogel spiral).
r = sqrt((i + 0.5) / N) * R
theta = i * 2.39996 (золотой угол ~137.5 градусов)

Отличный алгоритм для статичного размещения. Но TE_SPRITE — одноразовый клиентский эффект, обновляется каждый тик. С фиксированными координатами из спирали получилось 4 неподвижных кластера, потому что каждый тик рисовались одни и те же 4 точки.

Попытка 2 (финальная): случайная равномерная выборка по диску.
r = sqrt(random_float(0.0, 1.0)) * flRadius
theta = random_float(0.0, 6.28318)


Каждый тик — новые случайные координаты. sqrt(rand) корректирует распределение по площади — без него точки кластировались бы в центре, потому что при малом радиусе длина окружности меньше, но вероятность та же. С sqrt плотность точек одинаковая по всей площади диска.


Этап 7. Случайный поворот моделей пола​

Модели огня на полу выглядели однообразно — все повёрнуты в одну сторону. Добавлен случайный yaw (поворот вокруг вертикальной оси).

Проблема: первая реализация ставила angles[1] = random_float(0.0, 360.0) ПОСЛЕ вызова FloorOriginAngles(). Функция FloorOriginAnglesвычисляет pitch и roll на основе нормали поверхности, используя входящий yaw как опорное направление. Перезапись yaw после расчёта ломала выравнивание — модели начинали «вылезать» из текстур на наклонных поверхностях.

Решение: рандомизировать yaw ДО вызова FloorOriginAngles. Функция использует его как опорный вектор и корректно рассчитывает pitch/roll для наклонной поверхности в этом направлении.


Этап 8. Килфид с разделением по типу​


В киллфиде к нику убийцы добавляется приставка: [ᴍᴏʟᴏᴛᴏᴠ] для ТТ, [ɪɴᴄ] для КТ.

Тип определяется по var_iuser4 инфликтора (зоны огня), не по команде атакующего.


Этап 10. Система покупки и мультиязычность

Массив команд покупки: вместо одного register_clcmd("molotov", ...) — массив BUY_COMMANDS[][], который регистрирует все команды в цикле. Добавить новую команду — одна строка в массиве.

Локализация: все строки вынесены в lang файл с ключами CVAR_* (для описаний кваров) и NTF_* (для уведомлений игрокам). Поддержка [ru] и [en].


Технические паттерны, выбранные для плагина​

Entity var fields. Каждая сущность несёт весь свой контекст: var_iuser4 — тип гранаты, var_fuser2 / var_fuser3 — радиус и время разгорания, var_iuser2 — время конца горения, var_iuser3 — счётчик созданных эффектов. Нет глобальных массивов, привязанных к entity ID.

Цепочка наследования типа. Тип записывается при giveNade и копируется: weapon entity → flying grenade → fire zone. Любое поведение (модели, спрайты, звуки, урон, килфид) читает из entity, не из игрока.

Селективные хуки. HookChain для CBasePlayer_TakeDamage активен только когда на карте есть живые зоны огня. Это убирает overhead от перехвата каждого вызова урона в игре.
 

Кто просматривает тему

  • Назад
    Верх