Делаю способность, которая на время даёт юниту-цели пассивную абилку. При использовании добавляю её юниту через UnitAddAbility, затем запускаю таймер и по его истечении - удаляю. Проблема возникает, когда я накладываю эту способность на юнита повторно, до окончания времени действия прошлой. Старый таймер удаляет пассивку, наложенную заново. Но как сделать проверку, запущены ли для одного юнита несколько подобных таймеров? Чтобы удалять пассивку только тогда, когда истекает последний из этих таймеров?
function TimerFunc takes nothing returns nothing
	local timer t = GetExpiredTimer()
	local integer h = GetHandleId(t)
	local unit u = LoadUnitHandle(hash,h,1)
	call UnitRemoveAbility( u, 'A06B' )
	call DestroyTimer(t)
    call FlushChildHashtable(hash,h)
	set t = null
	set u = null
endfunction

function MyAbility takes nothing returns nothing
	local unit u = GetSpellTargetUnit()
	local timer t = CreateTimer()
	local integer h = GetHandleId(t)
	call UnitAddAbility( u, 'A06B' )
	call SaveUnitHandle(hash,h,1,u)
	call TimerStart(t, 25.00, false, function TimerFunc)
	set u = null
	set t = null
endfunction

Принятый ответ

делаю в таких способностях таким таймерам малый период, время остановки определяю через перезаписываемую целочисленную
^ метод Фрога
в MyAbility проверяй есть ли уже абилка 'A06B' у этого юнита
и если есть - ищи таймер и меняй ему время на новое (опять 25 сек - обновление)
0
20
7 лет назад
0
Также как и в случае со станами/инвизами и любыми другими потенциально стакающимися фичами, нужно использовать счетчик. Для станов, инвизов и прочей глобально используемой лабуды - можно использовать переменные в базе данных. Для остальных же случаев - использовать добавление способностей. (накладываешь бафф - если есть у юнита интересующая нас способность-счетчик - увеличить ее лвл, иначе - добавить эту способность)
0
21
7 лет назад
Отредактирован ClotPh
0
Я просто делаю в таких способностях таким таймерам малый период, время остановки определяю через перезаписываемую целочисленную, а пока таймер идет, периодически передобавляю в нем добавленную способностью способность.
То есть если один таймер закончится и удалит способность, но еще существует другой от подобного добавляющего способность заклинания, он ее практически сразу снова добавит.
P. S. Знаю, что несколько не оптимально по производительности. Зато ОЧЕНЬ легко в реализации и понимании.
1
26
7 лет назад
1
что бы определять какие таймеры запущены, тебе их нужно где то запоминать, естественно.
2
26
7 лет назад
Отредактирован Extremator
2
делаю в таких способностях таким таймерам малый период, время остановки определяю через перезаписываемую целочисленную
^ метод Фрога
в MyAbility проверяй есть ли уже абилка 'A06B' у этого юнита
и если есть - ищи таймер и меняй ему время на новое (опять 25 сек - обновление)
Принятый ответ
0
17
7 лет назад
0
тут я вижу 2 варианта
1.глобалки:если добавляеться абилка а таймер работает то обновляем таймер
2.структуры:можно 1 таймером отслеживать всё всё и на всех юнитах
1
26
7 лет назад
1
два варианта
  1. Глобалки
  2. Структуры, которые строятся на глобалках
Ммм... что же выбрать
0
17
7 лет назад
0
Extremator:
два варианта
  1. Глобалки
  2. Структуры, которые строятся на глобалках
Ммм... что же выбрать
вы видели во что они компилируются, если писать это используя обычный jass будет очень много кода,не читаемости и тд,по мне так лутше ездить на этом велосипеде чем строить свой
0
20
7 лет назад
Отредактирован Diaboliko
0
вы видели во что они компилируются, если писать это используя обычный jass будет очень много кода,не читаемости и тд,по мне так лутше ездить на этом велосипеде чем строить свой
Нечитаемость контрится дефайнами.
элсо, не бурите мозг, вариант с добавлением абилки самый верный. Если не хотите делать много таймеров - пилите один глобальный
0
23
7 лет назад
Отредактирован Тимур
0
в MyAbility проверяй есть ли уже абилка 'A06B' у этого юнита
и если есть - ищи таймер и меняй ему время на новое (опять 25 сек - обновление)
Так, а если у меня будет 2 разных действия, запускающие таймер на разное время - в зависимости от уровня способности? Можно ли как-то выяснить, сколько времени осталось до срабатывания таймера, и перезаписывать его только в том случае, если новое значение больше?
0
26
7 лет назад
0
в любом случае тебе нужно перезапускать таймер заново, неважно сколько времени там осталось.
только если времени может быть на ту же пассивку меньше
ledoed:
Extremator:
два варианта
  1. Глобалки
  2. Структуры, которые строятся на глобалках
Ммм... что же выбрать
вы видели во что они компилируются, если писать это используя обычный jass будет очень много кода,не читаемости и тд,по мне так лутше ездить на этом велосипеде чем строить свой
зачем видеть во что это компилируется, вы что редактируете потом в j файле сохранив карту 1 раз?
0
26
7 лет назад
0
Тимур:
Так, а если у меня будет 2 разных действия, запускающие таймер на разное время - в зависимости от уровня способности? Можно ли как-то выяснить, сколько времени осталось до срабатывания таймера, и перезаписывать его только в том случае, если новое значение больше?
Да. Смотришь текущее время таймера, и если оно меньше чем ты хочешь выдать - обновляешь его.
0
21
7 лет назад
Отредактирован ClotPh
0
Сорри за оффтоп: не сильно советую редактировать код в war3map.j файле.
У меня пробовалось прямо не доставая его из карты - открытие файла через mpq, открытие war3map.j и прямо там же его сохранение, даже без экспорт-импорта.
Самое интересное, что вроде карта нормально потом оптимайзнулась и запустилась с функциями нового кода, но потом после открытия ее в WE в ней походу вдруг оказался старый - видимо, JNGP в другом месте как-то инфу для себя читает, что ли, а не напрямую из .j, или .j через MPQ без доставания как-то криво пересохраняется... Это не было замечено сразу, карта сохранилась в редакторе и код откатился... В общем, хорошо, что было откуда бэкапить.
Так что если редактировать напрямую .j, потом стоит внимательно смотреть, что в редакторе откроется.
2
27
7 лет назад
Отредактирован MpW
2
что бы определять какие таймеры запущены, тебе их нужно где то запоминать, естественно.
  1. Можно запоминать логическую переменную (true), ее можно сохранить по хэндлу кастера. Если будешь юзать скилл, проверяй сначала логическую. Иначе, если ничего не сохранял, выдаст false.
код
	local unit u = GetSpellTargetUnit()
	local integer id = GetHandleId(u)
	local boolean b = LoadBoolean(hash,id,1)
	if not b then
		call UnitAddAbility( u, 'A06B' )
		call SaveBoolean(hash,id,1,true)
	endif
  1. Необязательно сохранять логическую. Можно проверять наличие абилки (уровень абилки > 0)
  2. Сохранить таймер по хэндлу кастера, чтобы ссылаться на таймер (когда будешь юзать скилл). Чтобы перезапустить и прочее. Также можно выгружать из хэша в локалку, и проверять локалку пуста ли она и прочее
код
function MyAbility takes nothing returns nothing
	local unit u = GetSpellTargetUnit()
	local integer id = GetHandleId(u)
	local timer t = LoadHandleTimer(hash,id,1)
	local integer h = GetHandleId(t)
	local real timerX = TimerGetRemaining(t)
	if t == null then //если таймер пуст, то ...
		set t = CreateTimer() //создаем таймер
		set h = GetHandleId(t)
		call UnitAddAbility( u, 'A06B' ) //добавляем абилку
	    call SaveHandleTimer(hash,id,1,t)
		call SaveUnitHandle(hash,h,1,u) //запоминаем таймер
		call TimerStart(t, 25.00, false, function TimerFunc) //запускаем таймер
	elseif t != null then //тут еще уровень абилки можно проверить
		call PauseTimer(t)
		call TimerStart(t, 25.00 + timerX, false, function TimerFunc)
	endif

	call SaveUnitHandle(hash,h,1,u) //сохраняем юнита
set u = null
set t = null
endfunction
TimerGetElapsed:- сколько прошло (увеличивается до заданного значения)
TimerGetRemaining - сколько осталось (уменьшается до нуля)
TimerGetTimeout - на сколько запущен таймер (заданное значение не меняется короче)
0
30
7 лет назад
Отредактирован Clamp
0
Делаем один таймер и пару параллельных массивов (юниты и оставшееся время действия), таймером чекаем с низким периодом оставшееся время и уменьшаем его на период.
Логика работы описана в коде ниже, я не стал переделывать под конкретный случай (хотя внёс пару доп. комментов), всё и так понятно.
код ниже


library StunUnit // initializer Init
{
    private timer   periodicTimer = CreateTimer()    // Can be replaced with main map timer

    private unit    unitsStack[]
    private effect  effectsStack[]  // Stun effect storage (в данном случае это не нужно)
    private float   timeRemaining[] // Time of stun left
    private int     stackCounter = 0

    #define private TICK_PERIOD  = 0.05
    #define private STACK_LIMIT  = 8190
    #define private STUN_EFFECT  = "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget.mdl" // и это не нужно
    #define private ORDERID_STOP = 0xD0004  // и вот это


    // Looking for id of specified unit in stack
    private int StackSearch(unit givenUnit) {
        int i = -1;
        while(i++ < stackCounter) {
            if (givenUnit == unitsStack[i]) {
                return i;
            }
        }
        return -1;
    }

    // Add unit to stack with overflow check
    private void StackPush(unit givenUnit, float duration) {
        if(stackCounter < STACK_LIMIT) {
            unitsStack   [stackCounter] = givenUnit;
            effectsStack [stackCounter] = AddSpecialEffectTarget(STUN_EFFECT, givenUnit, "overhead");
            timeRemaining[stackCounter] = duration;
            stackCounter++;
        } else {
            BJDebugMsg("Stunned units stack overflow!");
        }
    }

    // Remove unit from stack
    private void StackPop(int id) {

        DestroyEffect(effectsStack[id]);

        stackCounter--;
        unitsStack   [id] = unitsStack   [stackCounter];
        effectsStack [id] = effectsStack [stackCounter];
        timeRemaining[id] = timeRemaining[stackCounter];

        unitsStack   [stackCounter] = null;
        effectsStack [stackCounter] = null;
        timeRemaining[stackCounter] = 0.0;
    }

    void StunUnit(unit givenUnit, float duration) {

        int stackedUnitId = StackSearch(givenUnit);

        if (stackedUnitId == -1) {
            StackPush(givenUnit, duration);
        } elseif (timeRemaining[stackedUnitId] < duration) {
            timeRemaining[stackedUnitId] = duration;
        }
    }

    private void TimerCallback() {

        int i = -1;

        while(i++ < stackCounter) {

            timeRemaining[i] -= TICK_PERIOD;
            IssueImmediateOrderById(unitsStack[i], ORDERID_STOP);

            if (timeRemaining[i] <= 0.0) {
                StackPop(i);
            }
        }
    }

    private void Init() {
        TimerStart(periodicTimer, TICK_PERIOD, true, function TimerCallback);
    }
}


Это "фича", основывающаяся на неочевидном поведении, использовать её в качестве основы системы - плохая примета.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.