Добавлен , опубликован
Способ реализации:
Версия Warcraft:
Библиотека, позволяющая отменять входящий урон.
код
library NegateDamageLib
globals
    private constant group Group = CreateGroup()
    private constant timer Timer = CreateTimer()

    public keyword MaxLifeBonusAbility
    public keyword MaxLifeBonus
endglobals

native UnitAlive takes unit id returns boolean

function GetUnitMaxLife takes unit u returns real
    return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitAbilityLevel(u, MaxLifeBonusAbility) * MaxLifeBonus
endfunction

private function ProcessUnits takes nothing returns nothing
    local unit u
    local real life2set

    loop
        set u = FirstOfGroup(Group)
        exitwhen u == null
        call GroupRemoveUnit(Group, u)

        if UnitAlive(u) then
            set life2set = GetWidgetLife(u)
            call UnitRemoveAbility(u, MaxLifeBonusAbility)
            call SetWidgetLife(u, life2set)
        else
            call UnitRemoveAbility(u, MaxLifeBonusAbility)
        endif
    endloop
endfunction

function NegateDamage takes unit u, real negated returns nothing
    local real life2set = GetWidgetLife(u) + negated
    local boolean add_ability = life2set > GetUnitState(u, UNIT_STATE_MAX_LIFE)

    if add_ability then
        call GroupAddUnit(Group, u)
        call UnitAddAbility(u, MaxLifeBonusAbility)
    endif

    call SetWidgetLife(u, life2set)

    if add_ability then
        call TimerStart(Timer, 0., false, function ProcessUnits)
    endif
endfunction

endlibrary

Установка

  1. Скачать .j файл и переместить в Директория-JNGP/jass.
  2. В коде карты прописать импорт библиотеки.
//! import "NegateDamageLibrary.j"
  1. Создать способность (далее NegateDamageAbil), которая увеличивает максимальное здоровье юнитов. Рекомендуется создать способность на основе Item Life Bonus [AIlf] с увеличением здоровья на 1 миллион или больше.
  2. Указать равкод способности и значение увеличения здоровья в константах по шаблону ниже.
globals
    constant integer NegateDamageLib_MaxLifeBonusAbility = 'A000'
    constant integer NegateDamageLib_MaxLifeBonus = 1000000
endglobals

Использование

NegateDamage
call NegateDamage(damaged_unit, damage_to_negate)
Отменяет damage_to_negate для damaged_unit. damage_to_negate должно быть больше 0. Если это значение больше входящего урона, то юнит будет вылечен на разницу этих значений. Если вызывать функцию не в триггере получения урона, то damaged_unit будет вылечен на damage_to_negate.
Отмена может не произойти в следующих случаях:
  • Входящий урон больше, чем бонус здоровья способности NegateDamageAbil.
  • У юнита здоровья меньше чем 1.
GetUnitMaxLife
set max_life = GetUnitMaxLife(any_unit)
Возвращает максимальное здоровье any_unit без учёта бонуса от NegateDamageAbil.

Особенности

Функция NegateDamage меняет текущее и максимальное здоровье юнита во время вызова и через некоторое время ещё раз:
  • Триггеры с событием Unit - Life (TriggerRegisterUnitLifeEvent) могут срабатывать во время изменений здоровья.
  • Чтобы узнавать максимальное здоровье без учёта изменений от NegateDamage, используйте функцию GetUnitMaxLife.

Примеры

Имунные рабочие

Карта
Рабочие игнорируют любой урон до миллиона включительно. Урон более миллиона может убить, а может и не убить.

Щит

Карта
У пехотинца есть способность Shield, которая даёт ему щиты. Щиты поглощают весь входящий урон, пока не израсходуются.

Обновления

08.12.2024
  • Настраиваемые константы (MaxLifeBonusAbility и MaxLifeBonus) теперь можно определить вне библиотеки.
    • Это позволяет импортировать библиотеку из файла, а не копировать её в код карты.
    • В связи с этим изменён способ установки и настройки.
  • Тестовые карты были обновлены для соответствия изменениям выше.
01.12.2024
  • Таймер запускается, только если способность NegateDamageAbil была добавлена.
  • Тестовые карты были обновлены для соответствия изменениям выше.
  • В карте NegateDamage-shield занулены поля Art - Animation - Cast Backswing и Art - Animation - Cast Point у пехотинца.
27.01.2022
  • Теперь способность добавляется в случаях, если сумма текущего здоровья и отменяемого урона больше максимального запаса здоровья.
  • GetUnitMaxHealth переименована в GetUnitMaxLife.
  • Тестовые карты были обновлены для соответствия изменениям выше.
25.12.2021
  • Убрано требование "библиотеки" AINativesLib, так как тот ресурс был обновлён.
  • Определение UnitAlive было добавлено в код самой библиотеки.
  • Обновлены тестовые карты дабы соответствовать изменениям выше.
23.03.2021
  • Добавлена функция GetUnitMaxHealth.
  • Некоторые константы стали публичными.
  • Добавлена ещё одна карта-пример.
21.03.2021 - 3
Полная переработка библиотеки:
  • Делает всё тоже самое, но лучше. Например, теперь регенерация здоровья верно работают у юнитов, которые игнорируют весь входящий урон и находятся под непрерывным огнём.
  • Убрана целая куча настроек и требований.
21.03.2021 - 2
  • В требования добавлена AI natives library.
  • Изменена проверка того, что юнит жив.
21.03.2021 - 1
  • Обновлён способ отключения других триггеров, отслеживающих урон.
  • Обновлена карта-пример.
  • Улучшена обработка случая, когда юнит умирает от полученного урона.
16.03.2021
Исправлены критические ошибки.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
28
Обновил проверку того, что юнит жив, заменив на более надёжную.
15
во время выполнения функции у юнита может быть миллион хп
Это откуда они у него возьмутся.
28
GetLocalPlayer, ну так ему ставится миллион хп + текущее хп во время добавления способности.
15
То есть, ты исцеляешь юнита на весь миллион, который дает способность. А зачем? С одной стороны, переменная HealthAmount не несет особого смысла, ведь ты можешь просто вычислить то количество здоровья, которое дается юниту. Взять макс. здоровье до вручения способности и макс. здоровье после. Одно вычитаем из другого и вот оно значение. То есть тут открывается простор для ошибок со стороны конечного пользователя, если он решит отредактировать способность но забудет внести соответствующую правку в код.
С другой стороны, библиотека подразумевает уменьшение полученного урона, верно? Соответственно, тебе достаточно добавить юниту текущее здоровье, в размере уменьшенного урона. То есть не
call SetWidgetLife(u, life + HealthAmount)
а
call SetWidgetLife(u, life + negated)
28
GetLocalPlayer, я проверил, вроде всё пашет как надо. Это здорово всё упрощает.

Да что-то мне кажется, что я кому делал ровно так, как и описал GetLocalPlayer, но когда решил выложить, намутил дичи.
28
Обновлено, теперь стало ещё круче. Даже на гуи использовать можно. И без UMSWE. Спасибо GetLocalPlayer.
15
Ты падажи, у меня еще вопросы остались.
А что если пользователь совершает какие-то операции с максимальным запасом здоровья юнита, после вызова твоей функции? Ведь в таком случае, он будет работать с максимальным запасом здоровья, увеличенным способностью из твоей библиотеки.
Например, у пользователя в карте есть такая способность
Карающее воздаяние мстительного возмездия (пассивное)
Всякий урон против героя провоцирует взрывную волну, которая наносит урон всем противникам в размере 20% от максимального запаса здоровья героя.
Перезарядка: 10 секунд.
Предположим, у владельца способности 1000 ед. здоровья. Если пользовательский триггер, отвечающий за эту способность, срабатывает ДО вызова твоей функции, то все работает ожидаемо и противникам вокруг наносятся запланированные 200 ед. урона. Однако, если очередь триггеров выстроилась таким образом, что триггер, отвечающий за способность срабатывает ПОСЛЕ вызова твоей функции, то из-за того что твоя функция увеличивает макс. запас здоровья на 1 000 000, владелец способности сеет вокруг кровавый апокалипсис нанося более 200000 урона за раз,
Та же невообразимая хурма может получится со всякими щитами, которые накладываются на юнита в момент получения урона и зависят от макс. запаса здоровья. Да и вообще с этой механикой можно наворотить кучу всего.
28
GetLocalPlayer, это просто исправляется введением функции, которая возвращает максимум хп без учёта этой способности.
function GetUnitMaxHealth takes unit u returns real
    local real life = GetWidgetLife(u)
    local real maxlife
    local boolean removed = UnitRemoveAbility(u, HealthAbility)
    set maxlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
    if removed then
        call UnitAddAbility(u, HealthAbility)
        call SetWidgetLife(u, life)
    endif
    return maxlife
endfunction
Можно написать иначе, если известно, сколько здоровья абилка даёт.
function GetUnitMaxHealth takes unit u returns real
    return GetUnitState(u, UNIT_STATE_MAX_LIFE) - 1000000 * GetUnitAbilityLevel(u, HealthAbility)
endfunction
Эту функцию можно добавить в саму либу, а можно, чтобы юзер её сам написал. Мне пока никто с такими проблемами не обращался, потому что либу никто не юзает.
Уверен, тут может быть много подводных камней, но для начинающих вполне сойдёт, а там дальше они своё напишут, или пересядут на мемхак/1.29+.

Именно по этому в WispTD я сделал своё здоровье, и просто процентно отражал его в реальном здоровье.
28
Сразу скажу наперёд - да, будут проблемы с событиями "Жизни у юнита становятся таким-то". Но без изменения здоровья тут никак.
15
А что делать, если пользователь использует другие библиотеки, затрагивающие максимальный запас здоровья? Чужие наработки, сделанные на заказ. Да и что делать тогда ГУИшнику, если в его карте уже тонна обращений к дефолным бж-функциям?
28
GetLocalPlayer, есть идеи, как отменять урон без добавления максимального хп?
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.