Добавлен , опубликован
Способ реализации:
Версия Warcraft:

Божественный щит

MUI: да
Импорт: нет
Требования: JNGP
Идея : Bergi_Bear
Описание: Пассивно активируется божественный щит всякий раз, когда здоровье героя падает ниже отметки в 10%. Перезарядка: 20, длительность: 5.

Скринншот

Технические подробности

Перенос в свою карту
Способности
  • 'Asds' Божественный щит (заклинание)
Предметы
  • 'Idsp' Божественный щит
  • 'Idsa' Божественный щит
Триггеры
  • ItemDivineShield
Настройки
constant integer ITEM_ACTIVE = 'Idsa'; // Активный предмет
constant integer ITEM_PASSIVE = 'Idsp'; // Пассивный предмет
constant real ITEM_ABILITY_COOLDOWN = 20; // Перезарядка способности предмета

trigger DAMAGE_DETECT_TRIGGER = CreateTrigger(); // Не трогать
region MAP_REGION;  // Не трогать

hashtable HT = InitHashtable(); // Хэштаблица, можете вписать туда вашу, например:
// hashtable HT = udg_HashTable;
Код заклинания
// !nocjass
//! zinc
library ItemDivineShield {
    
    constant integer ITEM_ACTIVE = 'Idsa'; // Активный предмет
    constant integer ITEM_PASSIVE = 'Idsp'; // Пассивный предмет
    constant real ITEM_ABILITY_COOLDOWN = 20; // Перезарядка способности предмета

    trigger DAMAGE_DETECT_TRIGGER = CreateTrigger(); // Не трогать
    region MAP_REGION;  // Не трогать
    
    hashtable HT = InitHashtable(); // Хэштаблица, можете вписать туда вашу, например:
    // hashtable HT = udg_HashTable;
    
    
    /*
    *
    *
    *
    *
    *
    */
    
    function isUnitAlive(unit target) -> boolean {
        return GetWidgetLife(target) > 0.405;
    }
    
    function itemReplaceInstantly(unit u, integer from, integer to){
        integer i, slot[], slotI = -1;
        item it;
        for(0 <= i < bj_MAX_INVENTORY){
            it = UnitItemInSlot(u, i);
            if (GetItemTypeId(it) == from){
                RemoveItem(it);
                slotI = slotI + 1;
                slot[slotI] = i;
            }
        }
        for(0 <= i <= slotI){
            UnitAddItemToSlotById(u, to, slot[i]);
        }
        it = null;
    }
    
    function itemReplaceDelayed(unit u, integer from, integer to){
        timer t = CreateTimer();
        integer pk = GetHandleId(t);
        SaveUnitHandle(HT, pk, 0, u);
        SaveInteger(HT, pk, 0, from);
        SaveInteger(HT, pk, 1, to);
        TimerStart(t, 0.03125, true, function(){
            timer t = GetExpiredTimer();
            integer pk = GetHandleId(t);
            unit u = LoadUnitHandle(HT, pk, 0);
            if (isUnitAlive(u)){
                itemReplaceInstantly(u, LoadInteger(HT, pk, 0), LoadInteger(HT, pk, 1));
                FlushChildHashtable(HT, pk);
                PauseTimer(t); DestroyTimer(t);
            }
            u = null;
            t = null;
        });
        t = null;
    }
    
    function itemReplace(unit u, integer from, integer to){
        if (isUnitAlive(u)){
            itemReplaceInstantly(u, from, to);
        } else {
            itemReplaceDelayed(u, from, to);
        }
    }
    
    function itemCount(unit u, integer c) -> integer {
        integer i, count = 0;
        item it;
        for(0 <= i < bj_MAX_INVENTORY){
            it = UnitItemInSlot(u, i);
            if(GetItemTypeId(it) == c){
                count = count + 1;
            }
        }
        it = null;
        return count;
    }

    function addUnitToDetect() -> boolean {
        unit u = GetFilterUnit();
        if (IsUnitType(u, UNIT_TYPE_HERO)){
            TriggerRegisterUnitEvent(DAMAGE_DETECT_TRIGGER, u, EVENT_UNIT_DAMAGED);
        }
        u = null;
        return false;
    }
    
    function onInit(){
        trigger t[];
        integer i;
        group g = CreateGroup();
        
        for(0 <= i <= 2){ t[i] = CreateTrigger(); }
        
        MAP_REGION = CreateRegion();
        RegionAddRect(MAP_REGION, bj_mapInitialPlayableArea);
        
        t[0] = CreateTrigger();
        TriggerRegisterEnterRegion(t[0], MAP_REGION, null);
        TriggerAddCondition(t[0], function addUnitToDetect);
        
        GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, function addUnitToDetect);
        GroupClear(g); DestroyGroup(g); g = null;
        
        TriggerAddCondition(DAMAGE_DETECT_TRIGGER, Condition(function() -> boolean {
            unit u = GetTriggerUnit();
            item it;
            integer i, pk;
            real dmg = GetEventDamage();
            timer t;
            if (
                dmg > 0
                &&
                GetUnitCurrentOrder(u) != 851973
                &&
                itemCount(u, ITEM_PASSIVE) > 0
                &&
                GetWidgetLife(u) - dmg <= GetUnitState(u, UNIT_STATE_MAX_LIFE) * 0.1
            ){                
                itemReplace(u, ITEM_PASSIVE, ITEM_ACTIVE);
                for(0 <= i < bj_MAX_INVENTORY){
                    it = UnitItemInSlot(u, i);
                    if(GetItemTypeId(it) == ITEM_ACTIVE){
                        UnitUseItem(u, it);
                        break;
                    }
                }
                
                t = CreateTimer();
                pk = GetHandleId(t);
                SaveUnitHandle(HT, pk, 0, u);
                TimerStart(t, ITEM_ABILITY_COOLDOWN - 0.01, false, function(){
                    timer t = GetExpiredTimer();
                    integer pk = GetHandleId(t);
                    unit u = LoadUnitHandle(HT, pk, 0);
                    itemReplace(u, ITEM_ACTIVE, ITEM_PASSIVE);
                    u = null;
                    FlushChildHashtable(HT, pk);
                    PauseTimer(t); DestroyTimer(t); t = null;
                });
            }
            
            u = null;
            it = null;
            t = null;
            return false;
        }));
        
        for(0 <= i < bj_MAX_PLAYER_SLOTS){
            TriggerRegisterPlayerUnitEvent(t[1], Player(i), EVENT_PLAYER_UNIT_PICKUP_ITEM, null);
            TriggerRegisterPlayerUnitEvent(t[2], Player(i), EVENT_PLAYER_UNIT_DEATH, null);
        }
        TriggerAddCondition(t[1], Condition(function() -> boolean {
            unit u = GetTriggerUnit();
            item it = GetManipulatedItem();
            
            if (GetItemTypeId(it) == ITEM_PASSIVE && itemCount(u, ITEM_ACTIVE) > 0){
                itemReplace(u, ITEM_PASSIVE, ITEM_ACTIVE);
            }
            u = null;
            it = null;
            return false;
        }));
        TriggerAddCondition(t[2], Condition(function() -> boolean {
            unit u = GetTriggerUnit();
            if (itemCount(u, ITEM_ACTIVE) > 0){
                itemReplace(u, ITEM_ACTIVE, ITEM_PASSIVE);
            }
            u = null;
            return false;
        }));
        
        for(0 <= i <= 2){t[i] = null;}
    }
}
//! endzinc
// !endnocjass
`
ОЖИДАНИЕ РЕКЛАМЫ...
33
руна неуязвимость да, эффект можно получить, а как вы кд предмету запустить собираетесь?
не только защитный амулет, подменять его, даммиком кидать спелл который собьёт амулет и запустит кд
30
не сбивая приказ можно на основе какой нибудь Книги
Пробовал, но таким образом текут хэндлы и пришлось создавать дополнительную способность для кд предмета и дополнительный предмет для вручения герою.
11
NazarPunk, люди играют в карты по 1.5-2 часа нонстопом, когда в коде там такая жесть, что про небольшие утечки я вообще молчу (пример та же Дота и всякие Хиро Дефенсы). А каким образом у тебя будут "течь" хэндлы, если все чистить за собой?
30
respect_gg, пусть играют во что им вздумается, я код пишу и он работать должен. Хэндлы утекали когда я добавлял герою руну с божественным щитом, а если бы не текли, то всёравно нужно создвать два дополнительных объекта в РО, что напрягает.
11
NazarPunk, Даешь Айтем с абилкой, которая 0 агилити/силы/инты дает, триггерно юзаешь ее и все, в свойствах ставишь, чтобы предмет не кончался и все, офк даешь 'Avul' триггерно, эффект какой надо повесь. Параллельно таймер запускаешь, как таймер истек - свапаешь айтемы обратно. Писать такую кучу триггеров ради этого я хз зачем. По крайней мере так самый важный момент будет работать - не будет сбиваться текущий приказ, а это куда важнее.
33
respect_gg, как ты запустишь кд предмет?, в этом сама и соль же!
11
Bergi_Bear, Активная способность у ПОвышение ловкости/силы/инты не сбивает приказ
33
respect_gg, подробнее...., не сбиает в каком случае ? берсерк тоже не сбивает приказ, но если его триггерно отдать, то сбивает, а тут надо у предмета да ещё и перезарядку запустить
26
Bergi_Bear:
как ты запустишь кд предмет?, в этом сама и соль же!
Вариант А - каст через руну ладно, тебе оно не нравится
Вариант Б - грёбаный амулет, который запускается с каста даммика
Вариант В - промутить с эксгумацией я на предметах не пробовал, но на юнитах норм, так что не даю 100%
Вариант Г - mh
и чисто кастомная адоптация от меня
Вариант Д - заменять предмет неактивной копией (примерно как у тебя), и отображать числовое КД предмета за счёт уменьшения на нём зарядов (вообще так можно сделать параллельное истечение КД на одинаковых предметах)
Да, заряды это такое себе... но! я делал дотовские транквиллы, которые отрубают от получения героем любого урона на 8 сек, и отображение по-секундного КД никак негативно не сказалось на визуале
33
Extremator, я в курсе что ты знаешь, и спрашивал respect_gg, , темболее ты описал всё то что я сам предложил в сообщениях выше, очень смешно, что ты мне в качестве ответа кидаешь, мои же предложения
28
виджет лайф > .405 ............ серьёзно?
Ну а то тут не так, хотя UnitAlive гораздо лучше.
26
PT153, то что есть проверка статус смерти юнита, а речь не о UnitAlive
Bergi_Bear:
я в курсе что ты знаешь, и спрашивал respect_gg
я чуть тупанул, мне показалось что это написал NazarPunk
28
то что есть проверка статус смерти юнита
Да какая разница, IsUnitType и GetWidgetLife в данной ситуации одинаковы, первая функция берёт 2 аргумента, а вторая требует сравнения с 0 или с .405, UnitAlive всё равно лучше обеих.
26
PT153:
Да какая разница, IsUnitType и GetWidgetLife в данной ситуации одинаковы, первая функция берёт 2 аргумента, а вторая требует сравнения с 0 или с .405, UnitAlive всё равно лучше обеих.
функция IsUnitAliveBJ возвращает ответ функции IsUnitDeadBJ с приставкой not
функция IsUnitDeadBJ возвращает - ( ХП юнита <= 0 )
используя GetUnitState(whichUnit, UNIT_STATE_LIFE)
ты же говорить - про эффективность числа 0.405 в том же сравнении
При этом есть такой факт, что ХП юнита может быть БОЛЬШЕ 1 ед., в тот момент времени когда юнит по факту является МЁРТВЫМ - в таком случае все ваши проверки вернут "да, юнит жив", хотя это будет не так.
Кажется фигня? а потом в карте появляется странный, но до невыносимости знакомый баг - при смерти из героя выпадают некоторые предметы (а то и все). Даже в той же DotA в своё время (имею ввиду время её актуальности до 2015 года, пока её обновляли) этот баг - Тараска и Даггер выпадали из героев, и поэтому руна-книжка всегда создавалась на базе игрока, а потом ему приходилось её подбирать...
30
Вот все функции для проверки жив ли юнит
function IsUnitAliveBJ takes unit whichUnit returns boolean
    return not IsUnitDeadBJ(whichUnit)
endfunction
function IsUnitDeadBJ takes unit whichUnit returns boolean
    return GetUnitState(whichUnit, UNIT_STATE_LIFE) <= 0
endfunction
function UnitAlive takes unit u returns boolean
    return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0
endfunction
Так что их можно суммировать и написать мегафункцию
function IsUnitAlive(unit target) -> boolean {
    return GetWidgetLife(target) > 0.405 && !IsUnitType(target, UNIT_TYPE_DEAD) && GetUnitTypeId(target) != 0;
}
30
ScopteRectuS:
NazarPunk, UnitAlive - нативная функция же.
блин, по запаре не то скопривовал, хотел
native UnitAlive takes unit u returns boolean
Написал тест для проверки проверки
native UnitAlive takes unit u returns boolean
// !nocjass
//! zinc
library Start {

    unit U;
    
    function msg(string s, boolean b){
        string hp = R2S(GetWidgetLife(U));
        if (b) {BJDebugMsg(s + " - |cff00ff00Жив|r: |cff909090" + hp+"|r" ); }
        else { BJDebugMsg(s + " - |cffff0000Мёртв: |cff909090" + hp+"|r"); }
    }
    
    function IsUnitAlive(unit target) -> boolean {
        return GetWidgetLife(target) > 0.405 && !IsUnitType(target, UNIT_TYPE_DEAD) && GetUnitTypeId(target) != 0;
    }
    
    function test(string s){
        BJDebugMsg("|c55000099------"+s+"|r");
        msg("UnitAlive", UnitAlive(U));
        msg("IsUnitAliveBJ", IsUnitAliveBJ(U));
        msg("GetWidgetLife(U) > 0.405", GetWidgetLife(U) > 0.405);
        msg("IsUnitAlive",IsUnitAlive(U));
    }

    function onInit(){
        TimerStart(CreateTimer(), 0.01, false, function(){
            U = CreateUnit(Player(0), 'hfoo', GetStartLocationX(0), GetStartLocationY(0), GetRandomReal(0, 360));
            test("CreateUnit");
            KillUnit(U);
            test("KillUnit");
            SetWidgetLife(U, 30);
            test("SetWidgetLife(U, 30)");
            RemoveUnit(U);
            test("RemoveUnit(U)");
        });
        
    }
}
//! endzinc
// !endnocjass
Загруженные файлы
28
При этом есть такой факт, что ХП юнита может быть БОЛЬШЕ 1 ед., в тот момент времени когда юнит по факту является МЁРТВЫМ
Это не так, юнит умирает только если хп меньше 0.405, доказанный тестами факт. Такие приколы возникают только в момент получения урона, ведь урон ещё не был нанесён, потому и хп больше 1.
функция IsUnitAliveBJ возвращает ответ функции IsUnitDeadBJ с приставкой not
функция IsUnitDeadBJ возвращает - ( ХП юнита <= 0 )
используя GetUnitState(whichUnit, UNIT_STATE_LIFE)
ты же говорить - про эффективность числа 0.405 в том же сравнении
И к чему это? Тут кто-то говорил про использование этих функций? Зачем их вообще их использовать, если есть IsUnitType, GetWidgetLife и UnitAlive, которые ТОЧНО лучше этих BJ?

Так что их можно суммировать и написать мегафункцию
Тоже самое делает UnitAlive.

Вот нормальные функции на все случаи жизни.
раскрыть
UnitDead и UnitIsAlive определённо сомнительные функции.
native UnitAlive takes unit id returns boolean

function UnitDead takes unit u returns boolean
    return not UnitAlive(u)  // returns true, if unit does not exist.
endfunction

function UnitExists takes unit u returns boolean
    return GetUnitTypeId(u) != 0
endfunction

function UnitDoesNotExist takes unit u returns boolean
    return GetUnitTypeId(u) == 0
endfunction

function UnitIsAlive takes unit u returns boolean
    return not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction

function UnitIsDead takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_DEAD)  // returns false, if unit does not exist.
endfunction
16
UnitAlive лучше всех:
если юнита не существует, его хп ==0, но IsUnitType(DEAD)==false (потому что юнита нет)
если юнит мертв, но его хп поднялись, то хп >0 при UnitType(DEAD) == true
и UnitAlive работает четко во всех этих ситуациях
30
и UnitAlive работает четко во всех этих ситуациях
Хорошо, что объявлять нативки можно по несколько раз, и ненужно заботится о redeclared.
26
PT153:
Это не так, юнит умирает только если хп меньше 0.405, доказанный тестами факт.
Речь о том что юнит мёртв, а игра сообщает обратное. И это так.
PT153:
И к чему это? Тут кто-то говорил про использование этих функций? Зачем их вообще их использовать, если есть IsUnitType, GetWidgetLife и UnitAlive, которые ТОЧНО лучше этих BJ?
Так ты и написал про UnitAlive, а т.к. в 1.26 нет функции UnitAlive, то за неё было принято IsUnitAliveBJ, т.к. является единственно подходящим по контексту (в заголовке указана именно 1.26 версия). Ты ничего не путаешь? Или ты специально вводишь всех в заблуждение?
28
Ты ничего не путаешь? Или ты специально вводишь всех в заблуждение?
Открываем common.ai и смотрим. Чтобы использовать в обычных картах, её нужно определить после блока глобалок. Если есть vJass, то в любом удобном месте.
Речь о том что юнит мёртв, а игра сообщает обратное. И это так.
В момент перед получением урона (то есть пока триггер на получение урона работает) он ещё считается живым.

И я так и не понял самые первые придирки к реализации, они же ни о чём. Был бы полный комментарий, как сделать лучше, то и разговор был бы другой.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.