Добавлен , опубликован
Способ реализации:
Версия 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
Исправлены критические ошибки.
`
ОЖИДАНИЕ РЕКЛАМЫ...
15
А что это даст? Событие всё равно добавится.
Если порядок исполнения зависит от порядка регистрации событий, то можно было бы зарегистрировать свой триггер на олов урона после регистрации всех пользовательских. Код этого триггера исполнялся бы после всех пользовательских и это хороший момент для выдачи способности/запуска таймера.
Но это прокатило бы если бы хуки вызывали хук-функцию после оригинальной, а не перед.
28
Либа обновлена, подробности в посте. Из важного - добавлена ещё одна карта-пример.
15
Опять же, не нужна константа MaxLifeBonus. Присвоить этой переменной значение 0, в теле функции проверить, если оно равно 0, то выщитать MaxLifeBonus при добавлении способности.
И хуки с предупреждением в дебаг моде на функции GetUnitState, GetUnitStateSwap, GetUnitStatePercent, GetUnitLife все таки нужны.
20
Делюсь измененной либой. Проблема исходной в скачущей полосе (и значения) здоровья при блокировании дамага. Тут это немного исправлено. Абилка будет выдаваться лишь тогда, когда отменённый урон больше чем максимальное здоровье. Если урон больше чем недостающее здоровье, то юнит хилится на максимум, а дохил будет после нанесения урона. В других случаях юнит просто хилится на отмененный урон. Позволяет немного убрать недостатки либы.
Вам нужна инициализированная хэш-таблица.
код
library NegateDamageLib uses HashId

    globals
        private constant group Group = CreateGroup()
        private constant timer Timer = CreateTimer()
        
        // MUST BE SPECIFIED
        public constant integer MaxLifeBonusAbility = 'A002'
        public constant integer MaxLifeBonus = 1000000
    endglobals

    private function ProcessUnitAbility 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

    private function ProcessUnit takes nothing returns nothing
        local integer tmId = GetHandleId(GetExpiredTimer())
        local unit u = LoadUnitHandle(HT, tmId, 0)
        local real lifeBonus = LoadReal(HT, tmId, 1)
            
        if UnitAlive(u) then
            call SetWidgetLife(u, GetUnitState(u, UNIT_STATE_LIFE)  + lifeBonus)
        endif
        
        set u = null
        call FlushChildHashtable(HT, tmId)
        call PauseTimer(GetExpiredTimer())
        call DestroyTimer(GetExpiredTimer())
    endfunction

    function NegateDamage takes unit u, real negated returns nothing
        local real life = GetWidgetLife(u)
        local real a
        local timer tm
        set a = GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitState(u, UNIT_STATE_LIFE)
        if (GetUnitState(u, UNIT_STATE_MAX_LIFE) < negated) then
            call GroupAddUnit(Group, u)
            call UnitAddAbility(u, MaxLifeBonusAbility)
            call SetWidgetLife(u, life + negated)
            call TimerStart(Timer, 0., false, function ProcessUnitAbility)
        elseif (a < negated) then
            call SetWidgetLife(u, life + a)
            set tm = CreateTimer()
            call SaveUnitHandle(HT, GetHandleId(tm), 0, u)
            call SaveReal(HT, GetHandleId(tm), 1, negated - a)
            call TimerStart(tm, 0., false, function ProcessUnit)
        else
            call SetWidgetLife(u, life + negated)
        endif
        set tm = null
    endfunction

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

endlibrary

И в дополнение макрос для перехвата функции, жаль cJass.
    define GetUnitState(u, state) = GetUnitStateHook(u, state)
    
    //! nocjass
    function GetUnitStateHook takes unit u, unitstate state returns real
        if state == UNIT_STATE_MAX_LIFE then
            return GetUnitState(u, UNIT_STATE_MAX_LIFE) - MaxLifeBonus * GetUnitAbilityLevel(u, MaxLifeBonusAbility)
        endif
        return GetUnitState(u, state)
    endfunction
    //! endnocjass
Этот комментарий удален
28
Если урон больше чем недостающее здоровье, то юнит хилится на максимум, а дохил будет после нанесения урона.
Такой способ может уменьшать и увеличивать хп на небольшое значение (однако через длительное время всё может появится существенная разница), а также никак не решает проблему скачков полоски хп. Поэтому в этом случае лучше добавлять абилку. Первое предложение учту.
20
PT153, Но скачки будут гараздо реже и при обычной игре не заметны, да есть небольшая ошибка из-за real. Но в целом если у вас нет юнитов с 1 хп, то все будет норм.
23
GetLocalPlayer, Простите, что вмешиваюсь в диалог, но насчёт многопоточности в Варкрафт мне кажется, Вы не правы. Я у себя проводил стресс-тест, с Handle Counter, с триггером на событие Юнит атакован, где в функции действия было создание локального дамми, и выдачи через 3 секунды юнитам баффа через даммикаст. Затем натравил 600 юнитов друг на друга (2 игрока было). Так вот, во время боя количество созданных дамми превышало 600. То есть, запускалось одновременно несколько сотен потоков, и не было никакой очередности.
28
EugeAl, очерёдность всегда есть.

Такой способ может уменьшать и увеличивать хп на небольшое значение (однако через длительное время всё может появится существенная разница), а также никак не решает проблему скачков полоски хп. Поэтому в этом случае лучше добавлять абилку. Первое предложение учту.
Исправлю свой комментарий.
Если урон больше чем недостающее здоровье, то юнит хилится на максимум, а дохил будет после нанесения урона.
Такой способ может уменьшать хп на небольшое значение, из-за чего через длительное время может появится существенная разница, а также никак не решает проблему скачков полоски хп. Поэтому в этом случае лучше добавлять абилку. С ней тоже хп может уменьшаться, но может и увеличиваться, поэтому через длительное время большой разницу не будет.
Первое предложение учту.
12
KaneThaumaturge, Большое спасибо за доработку системы. Как раз использовал похожий код и заметил некоторые проблемы с ним по поводу дохила после нанесения урона.Не понимал как точно это работает
28
Daro, "доработанная" система будет плохо работать, если юнит будет под постоянным огнём. Почему - читай мой комментарий выше. Если же такой ситуации не предвидится - используй вариант Кейна. Протестировать можно в первой карте-примере.
А так я учёл первое предложение Кейна, уже с января 2022 способность добавляется не всегда.
28
Вышла новая версия! Прокрутить к ресурсу
01.12.2024
  • Таймер запускается, только если способность была добавлена.
  • Тестовые карты были обновлены для соответствия изменениям выше.
  • В карте NegateDamage-shield занулены поля Art - Animation - Cast Backswing и Art - Animation - Cast Point у пехотинца.
28
Вышла новая версия! Прокрутить к ресурсу
  • Настраиваемые константы (MaxLifeBonusAbility и MaxLifeBonus) теперь можно определить вне библиотеки.
    • Это позволяет импортировать библиотеку из файла, а не копировать её в код карты.
    • В связи с этим изменён способ установки и настройки.
  • Тестовые карты были обновлены для соответствия изменениям выше.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.