"Бафф" в ковычках, т.к. мне требуется только таймер, и ссылка на функцию.
Что то вроде:
код
есть родительский класс бафф
инициализация чилд классов
BuffIni(){
Buff(1,func)
Buff(2,func)
..//ид класса, функция
}
функция добавления бафа юниту:
AddBuff(integer unitid, integer buffid, real time){
если юнит имеет бафф то продлеваем жизнь таймера
if IsUnitHasBuff(unitid,buffid) {
добавить время таймеру
}else{
создать бафф с таймером
создать эффект и привязать к ид юнита (юзер дата)
выполнить функцию бафа
}
Функция удаления
DestroyBuff(integer unitid, integer buffid){
выполнить функциию удаления бафа
}
Использую подобную системку, слегка кривую.. для способностей, реализованную через хеш и массив триггеров с передачей аргументов через глобалки.

Баффов же у меня было до этого былр немного, и использовались примитивные заглушки. Однако в последнее время их кол-во стало разрастается, поэтому появилась необходимость в нормальной системе.

Отсюда вопрос. Может я велосипед изобретаю, и есть ли готовые решения?

Не особо смотрел раньше в сторону структур, хотя может и зря, но проблема то в основном с ссылкой на функцию, так как тип code с массивом не работает.

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

Нет универсальных решений нет и врятли будут, тут все надо своими руками, лучше для некоторых баффов юзать триггеры чтобы отследить смерть событием TriggerRegisterDeathEvent а так же применение диспелящих способностей на цель, либо самой целью.
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
19
6 лет назад
0
Похожие вопросы:

ответ
Ты никак не заставишь одиноаковые заклинания стакнуться. Делай спеллы-пустышки, баффы вешай аурами, которые потом будешь удалять, эффекты заклинания делай триггерно. Иначе никак.
Единственное, что ещё, применимое именно к лечению - можно создавать несколько фонтанов жизни или юнитов с их способностью и регенить сильнее за счёт одной и той же способности.
ответ
Darknessay:
quq_CCCP:
что мешает юзнуть берсерк, манащит, канал, да хоть виндвалк?
Мешает то, что берсерк и виндвалк уже использованы, переключаемые абилки - хурма, а канал не моментальный.
Ты станишь юнита, какая тебе разница?
Канал пойдет с тем же успехом, юнит его кастанет в любом случае.
Переключаемые абилки работают нормально, если уметь немного искать и читать, как их юзать в качестве простых есть инфа в статьях на форуме.
Еще всякие вееры ножей, не сбивают приказ идти куда либо. Юзай его в качестве основы.
ответ
Pick every unit in range [250] matching condition [бафф спелла]
Всё что находится внутри данного блока произойдёт столько раз, сколько воинов находится внутри выбранной группы. Обратиться к воину можно через Picked Unit ( GetEnumUnit( ) ). Если Вам нужно добавить событие на смерть именно этих воинов, то прямо здесь внутри блока добавляете их в событие триггера, выбрав их через Picked Unit, или сохраняете их в переменные.

Также можно не сохранять каждого воина, а занести их в отдельную группу. А вместо проверки:
Условие: Dying unit = Aim [Integer A]
проверяете, находится ли воин в группе.
ответ
8gabriel8:
Это тот же принцип для второго пункта, мне казалось, что могут быть способы изящнее. Лучше по другим пунктам подскажите.
первый вопрос так и не понял, насчет третьего вопроса:
там весь прикол в ивентах, есть начало когда наводишь на цель, тогда даже анимация не проигрывается и мана не забирается.
Приводит в действие это уже когда ману сняло и проигралась анимация.
Где то был текст на всё это, но я не могу найти.
Ну а далее чекаешь уровень бафа у цели, если он больше 0 - значит хиляешь и удаляешь баф
Хотя возможно это я неправильно понял, можно просто отловить начало каста абилки и восстанавливать ману триггерно.

0
32
6 лет назад
0
Нет универсальных решений нет и врятли будут, тут все надо своими руками, лучше для некоторых баффов юзать триггеры чтобы отследить смерть событием TriggerRegisterDeathEvent а так же применение диспелящих способностей на цель, либо самой целью.
Принятый ответ
1
21
6 лет назад
1
Не особо смотрел раньше в сторону структур, хотя может и зря, но проблема то в основном с ссылкой на функцию, так как тип code с массивом не работает.
Можно использовать function interface вместо code array, в нём можно сохранять даже функции, которые требуют что-то.
1
28
6 лет назад
1
Зависит от того, что тебе нужно от этой системы.
В карте, что я сейчас делаю, отличная система кастомных баффов (баффы WarCraft 3 я не использую вообще). Её я собираюсь выложить и рассказать, когда создам проект на XGM.
Система долго эволюционировала, потому человеку, который её никогда не видел, возможно будет сложно её понять.

Вкратце.
Есть структура MinionBuff, в ней есть следующие функции.
раскрыть
    stub method tickAction takes nothing returns boolean
        return true
    endmethod
    
    static method tickEnd takes nothing returns nothing
        local thistype this = GetCustomTimer()
        set currticks = currticks - 1
        if tickAction() then
            call destroy()
        endif
    endmethod
    
    method extendTicks takes integer addticks returns nothing
        set currticks = currticks + addticks
        if currticks > buffdata.maxticks then
            set currticks = buffdata.maxticks
        endif
    endmethod
    
    method startWithPeriod takes real period returns nothing
        set currticks = buffdata.startticks
        call UnitAddAbility(target.minion, buffdata.effectid)
        call TimerStart(t, period, buffdata.periodic, function thistype.tickEnd)
        call DebugMsgIf(isStopped(), "MinionBuff " + I2S(this) + " is started.")
        set isRunning = true
    endmethod
    
    method start takes nothing returns nothing
        call startWithPeriod(buffdata.period)
    endmethod
    
    method startPeriodic takes integer addticks returns nothing
        if isRunning then
            call extendTicks(addticks)
        else
            call start()
        endif
    endmethod
endstruct
Объект этой структуры не создаётся, создаются объекты конкретных баффов (которые тоже структуры). Есть два типа баффов - периодический (максимальное количество тиков больше 1) и непериодический. Я знаю, какой бафф периодический, а какой нет, потому периодические баффы стартуются функцией startPeriodic, а непериодические - start. Их таймер запускается в функцию tickEnd, где вызывается tickAction. Если у баффа эта функция перезаписана, то выполнится его версия функции. Если нет, то вернётся правда, и бафф будет удалён.
Примеры баффов.
раскрыть
struct BuffMultishotDoT extends MinionBuff
    method onCreate takes nothing returns nothing
        set node1 = target.buffs.addEoTBuff(this)
    endmethod
    
    method onDestroy takes nothing returns nothing
        call target.buffs.removeEoTBuff(node1)
    endmethod

    method tickAction takes nothing returns boolean
        local Tower dd
        if UnitExists(source) then
            set dd = GetUnitUserData(source)
            call dd.dealDamage(target, AbilMultishot(dd.abil).dot, AbilMultishot.dottype)
            return currticks == 0
        endif
        return true
    endmethod
    
    static method launch takes Minion m, Tower caster returns nothing
        //! runtextmacro InitBuff("m", "caster.tower", "caster.owner", "MultishotDoT", "true", "false", "false", "")
        call startPeriodic(buffdata.startticks)
    endmethod
endstruct

struct BuffMultishotSlow extends MinionBuff
    method onCreate takes nothing returns nothing
        set node1 = target.buffs.addMoveSpeedBuff(this)
    endmethod
    
    method onDestroy takes nothing returns nothing
        call target.buffs.removeMoveSpeedBuff(node1)
        call target.updateMoveSpeed()
    endmethod
    
    static method launch takes Minion m, Tower caster, real slow returns nothing
        //! runtextmacro InitBuff("m", "caster.tower", "caster.owner", "MultishotSlow", "true", "true", "false", "")
        call m.alterMoveSpeed(node1, slow)
        call start()
    endmethod
endstruct

Можешь использовать перезапись stub метода, а можешь использовать интерфейс функций, реализованы они одинаково: в обоих используется массив триггеров.
0
17
6 лет назад
0
Есть два типа баффов - периодический (максимальное количество тиков больше 1) и непериодический.
Не проще было бы задавать бафф просто длительностью и интервалом между периодическим эффектом? В 6 секунд длительностью и 1.5 секундным интервалом срабатывает 4 раза. Вроде на порядок проще получается.
0
28
6 лет назад
Отредактирован PT153
0
Не проще было бы задавать бафф просто длительностью и интервалом между периодическим эффектом? В 6 секунд длительностью и 1.5 секундным интервалом срабатывает 4 раза. Вроде на порядок проще получается.
Я задаю длительностью, максимальным количеством тиков и начальным количеством тиков. Период вычисляется делением длительности на начальное количество тиков. Такие параметры необходимы для того, что система позволяла создавать баффы, которые изначально имеют 1 тик, но при повторном наложении могут увеличивать кол-во тиков вплоть до указанного максимума.
Что же касается вопроса простоты и удобства - дело личное. Мне было удобнее сделать так.
Я не показал БД, где прописываются свойства баффов, потому что не посчитал это нужным. Вопрос ведь про готовые системы баффов. Я ответил, что наличие такой системы зависит от требований к ней. Также я хотел привести рабочий пример псевдомассива code, о чём также спрашивалось.
2
26
6 лет назад
2
как написал ссср, единого универсального решения нет и не будет, нужно делать систему нужную под конкретно твои задачи
дополню своим примером такой системы
сначала я сделал описание всех свойств бафов, затем сделал базу данных самих бафов, а затем уже к ней систему наложения и снятия
на баф делается своя структура
struct MyBuffData
        real duration, tick = 0.
        int cell, level, state, total = 0
        bool Unpause = false
        unit source, victim
        
        real effect_over_time_value, effect_over_time_delay
        bool effect_over_time_type
        string effect_over_time_sfx, effect_over_time_sfx_point
        
        int param[5]
        real param_value[5]
        
        
        
        void get_level_data(int level){
            int c = 0
                this.level = level
                    if this.level == 1 {
                        while(c++ < 5) {
                            this.param[c-1] = WhichState_1[this.cell][c]
                            this.param_value[c-1] = WhichStateValue_1[this.cell][c]
                        }
                    }
                    elseif this.level == 2 {
                        while(c++ < 5) {
                            this.param[c-1] = WhichState_2[this.cell][c]
                            this.param_value[c-1] = WhichStateValue_2[this.cell][c]
                        }
                    }
                    elseif this.level == 3 {
                        while(c++ < 5) {
                            this.param[c-1] = WhichState_3[this.cell][c]
                            this.param_value[c-1] = WhichStateValue_3[this.cell][c]
                        }
                    }
                    elseif this.level == 4 {
                       while(c++ < 5) {
                            this.param[c-1] = WhichState_4[this.cell][c]
                            this.param_value[c-1] = WhichStateValue_4[this.cell][c]
                        }
                    }
                    elseif this.level == 5 {
                        while(c++ < 5) {
                            this.param[c-1] = WhichState_5[this.cell][c]
                            this.param_value[c-1] = WhichStateValue_5[this.cell][c]
                        }
                    }
        }
        
        void set_level(int level){
            if level > 0 {
                SetUnitAbilityLevel(this.victim, BuffId[this.cell][1], level)
                SetUnitAbilityLevel(this.victim, BuffId[this.cell][2], level)
                this.get_level_data(level)
            }
        }
        
        void set_duration(real time){
                if time == -1. {
                    time = BuffTime[this.cell][this.level]
                }
            this.duration = time
        }
        
        
        static thistype create(unit a, unit b, int l){
            thistype this = thistype.allocate()
                this.level = l
                this.source = a
                this.victim = b
                this.Unpause = false
                this.tick = 0.
                this.total = 0
            return this
        }
        
    endstruct
и делается типа менеджера который учитывает все активные баффы и смотрит нужно ли им еще висеть и производить эффект
кат
private void BuffsUpdate(){
        int index = 0, C1 = 1
        UD src
        
        if Stack == 0 { return }
        
            loop {
                exitwhen index >= Stack
                index++
                    if BuffStack[index].victim == null {
                        BuffStack[index].destroy()
                        BuffStack[index] = BuffStack[Stack]
                        Stack--
                    }
            }
            
            index = 0
            loop {
                exitwhen index >= Stack
                index++
                
                src = GetData(BuffStack[index].victim)
				if (src > 0 and (BuffStack[index].duration <= 0. or GetHp(BuffStack[index].victim) <= 0.045 or GetUnitAbilityLevel(BuffStack[index].victim, BuffId[BuffStack[index].cell][2]) == 0)) {
				
				и так далее...

в итоге выглядит типа
//=================================
        // curse
            BuffId[17][1] = 'A00F'; BuffId[17][2] = 'B01E'
            BuffType[17] = TYPE_MAGICAL
            BuffPolar[17] = BUFF_TYPE_NEGATIVE
            BuffTime[17][1] = 12.
            BuffRank[17] = 5
            WhichState_1[17][1] = OUTPUT_DAMAGE_SCALE
            WhichStateValue_1[17][1] = 0.5
            DebuffChance[17][1] = 75.
        //=================================
проклятие которое уменьшает исходящий урон цели на 50%, OUTPUT_DAMAGE_SCALE описана в структуре юнита и влияется через воздействие бафа
...

там где мне нужно я делаю просто
AddBuffToUnit(target, caster,  'A00F', 1, false)

карту прикрепил если понадобится
Загруженные файлы
1
25
6 лет назад
1
Всем спасибо! По изучаю, что как работает.

Для непериодических баффов у меня были обычные жасовые функции с изменением нужных переменных героя, и аттач эфекта с таймером удаления.
Для остальных юзалась другая логика. В общем, не то что бы прям мешанина была, для большинства я тупо копировал шаблон и менял атрибуты.
Но столкнулся с проблемой что бафф может быть наложен дважды, и получался просто стак. Но минус в том что висели лишние эффекты.
А простая проверка на имеющийся бафф требовала нахождения таймера который уже мог быть запущен, то бишь нужно было хранить его хендл, а там еще всякие коллизии начинали возникать.
Поэтому захотелось все таки покурить структуры и придумать более изящный вариант.
0
28
6 лет назад
Отредактирован PT153
0
А простая проверка на имеющийся бафф требовала нахождения таймера который уже мог быть запущен, то бишь нужно было хранить его хендл, а там еще всякие коллизии начинали возникать.
У меня это проверяется просто, я сохраняю к хеш-таблицу созданный бафф (объект структуры, он же integer) под handleid юнита и id баффа (у каждого баффа есть свой id). Когда я запускаю бафф, достаю из хеша это значение, если оно 0 (то есть бафф ещё не был создан), создаю бафф и сохраняю его в хеш.

Вот пара примеров из моей старой версии карты, где вообще нет никакой системы, но используется тот же подход по запрету нового наложения баффа. Тут все таймеры создаются заранее и не удаляются по истечению баффа.
А ещё тут пробелы не расставлены.
раскрыть
Непериодический бафф.
function FrostArmorEnd takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local integer tH=GetHandleId(t)
    local unit m=LoadUnitHandle(Hash,tH,0)
    local integer mH=GetHandleId(m)
    local effect e=LoadEffectHandle(Hash,tH,1)
    call RemoveSavedBoolean(Hash,mH,'A01F')
    call RemoveSavedHandle(Hash,tH,1)
    call DestroyEffect(e)
    set t=null
    set m=null
    set e=null
endfunction

function FrostArmor takes nothing returns nothing
    local unit m=GetSpellTargetUnit()
    local integer mH=GetHandleId(m)
    local timer t=LoadTimerHandle(Hash,mH,'A01F')
    local integer tH=GetHandleId(t)
    local effect e
    if not HaveSavedHandle(Hash,tH,1)then
        call SaveBoolean(Hash,mH,'A01F',true)
        set e=AddSpecialEffectTarget("Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorTarget.mdx",m,"chest")
        call SaveEffectHandle(Hash,tH,1,e)
        set e=null
    endif
    call TimerStart(t,2,false,function FrostArmorEnd)
    set m=null
    set t=null
endfunction

function Trig_FrostArmor_Conditions takes nothing returns boolean
    return GetSpellAbilityId()=='A01F'
endfunction

function InitTrig_FrostArmor takes nothing returns nothing
    set gg_trg_FrostArmor=CreateTrigger()
    call TriggerRegisterPlayerUnitEvent(gg_trg_FrostArmor,Player(11),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
    call TriggerAddCondition(gg_trg_FrostArmor,function Trig_FrostArmor_Conditions)
    call TriggerAddAction(gg_trg_FrostArmor,function FrostArmor)
endfunction
Периодический бафф.
function RejuvenationEnd takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local integer tH=GetHandleId(t)
    local unit m=LoadUnitHandle(Hash,tH,0)
    local integer mH=GetHandleId(m)
    local effect e=LoadEffectHandle(Hash,tH,1)
    local integer times=LoadInteger(Hash,mH,'A030')
    call AddHPPerc(m,25)
    call SaveInteger(Hash,mH,'A030',times+1)
    if(times==2)then
        call RemoveSavedInteger(Hash,mH,'A030')
        call RemoveSavedHandle(Hash,tH,1)
        call DestroyEffect(e)
        call PauseTimer(t)
    endif
    set t=null
    set m=null
    set e=null
endfunction

function Rejuvenation takes nothing returns nothing
    local unit m=GetSpellTargetUnit()
    local integer mH=GetHandleId(m)
    local integer times=LoadInteger(Hash,mH,'A030')
    local timer t=LoadTimerHandle(Hash,mH,'A030')
    local integer tH=GetHandleId(t)
    local effect e
    if(times==0)then
        set e=AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Rejuvenation\\RejuvenationTarget.mdx",m,"chest")
        call SaveEffectHandle(Hash,tH,1,e)
        set e=null
        call TimerStart(t,1,true,function RejuvenationEnd)
    endif
    call AddHPPerc(m,20)
    call SaveInteger(Hash,mH,'A030',1)
    set m=null
    set t=null
endfunction

function Trig_Rejuvenation_Conditions takes nothing returns boolean
    return GetSpellAbilityId()=='A030'
endfunction

function InitTrig_Rejuvenation takes nothing returns nothing
    set gg_trg_Rejuvenation=CreateTrigger()
    call TriggerRegisterPlayerUnitEvent(gg_trg_Rejuvenation,Player(11),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
    call TriggerAddCondition(gg_trg_Rejuvenation,function Trig_Rejuvenation_Conditions)
    call TriggerAddAction(gg_trg_Rejuvenation,function Rejuvenation)
endfunction
3
25
6 лет назад
3
PT153
Немного поэкспериментировал с boolexpr, получилось создать массив функций для баффов через них, не используя даже кэш, раньше делал массив функций через массив триггеров с экзекутом + кеш, для прокачки и использования триггерных спеллов.

Есть конечно ограничение по кол-ву юнитов, но мне больше и не нужно.
BuffLibrary
library BuffLib uses TextLib
globals
constant integer BMax = 50 Макслимальное бафов, 8192/UntitMax = BMax
boolexpr array BCode Функция бафа
timer array BTimer Таймер бафа
integer array BUnitIdArray ид юнита прикрепояемый к тайммеру
integer array BBuffIdArray ид бафа прикрепояемый к бафу
integer array BEffectArray ид эффекта прикрепояемого к бафу
boolean BDestroy = true аттрибут_функции_Удаления бафа
integer BUnitId = 0 аттрибут_функции_Ид юнита
force BForce = CreateForce()
endglobals
nothing BuffEnd (){
timer t = GetExpiredTimer( )
integer i = R2I(TimerGetRemaining(t)+0.5)
BUnitId = BUnitIdArray[i]
BDestroy = true
ForceEnumPlayers(BForce, BCode[BBuffIdArray[i]])
DestroyTimer (t)
}
nothing BTimerEx (timer t, integer b, real d){
t = CreateTimer()
TimerStart(t, I2R(b), false, null)
PauseTimer(t)
TimerStart(t, d, false, function BuffEnd )
ForceEnumPlayers (BForce, BCode[BBuffIdArray[b]])
}
nothing AddBuff (integer unitid, integer buffid, real time){
integer id = unitid*BMax+buffid
BDestroy = false
BUnitId = unitid
BUnitIdArray [id] = unitid
BBuffIdArray [id] = buffid
BTimerEx(BTimer[id],id,time)
}
nothing BuffIni (integer i, code c){ BCode[i] = Condition (c) }
endlibrary
BuffData
library BuffData uses BuffLib
boolean Buff1 (){
if BDestroy {
Msg("Buff1_Destroy"+" for unit "+I2S(BUnitId))
}else{
Msg("Buff1"+" for unit "+I2S(BUnitId))
}
return false
}
boolean Buff2 (){
if BDestroy {
Msg("Buff2_Destroy"+" for unit "+I2S(BUnitId))
}else{
Msg("Buff2"+" for unit "+I2S(BUnitId))
}
return false
}
nothing BCodeIni(){
BuffIni(1,function Buff1)
BuffIni(2,function Buff2)
}
endlibrary
Загруженные файлы
2
28
6 лет назад
Отредактирован PT153
2
раньше делал массив функций через массив триггеров с экзекутом
Это автоматически делает function interface в vJass.
Boolexp тоже неплохой вариант. Они могут даже ничего не возвращать (по идее, должны возвращать boolean, но можно возвращать что угодно, всё равно все типы в Jass занимают 4 байта и возвращают то, что записано напротив return. nothing возвращает 0).
force BForce = CreateForce()
Можно использовать bj переменную, ты же всё равно ничего не добавляешь в клан.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.