Делаю способность, которая на время даёт юниту-цели пассивную абилку. При использовании добавляю её юниту через 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
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);
    }
}


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