Есть задача крепить к обьекту ссылки на множество триггеров, точное кол-во которых не известно. Своего рода "Group", только для триггеров.
Собственно структуры vJass позволяют создавать структуры с массивами на N элементов, а так же есть хештаблицы с ключам id + N.
Хотелось бы послушать народ о способах реализации этой задачи, мб я что-нибудь упустил?

У меня к каждому юниту крепится ссылка на объект структуры, в которой есть отдельные списки для разных типов баффов. Если же бафф имеет два разных эффекта, то ссылка на него хранится в двух списках. При удалении конфликтов не возникает, потому что при удалении бафф удаляет все ячейки, которые были к нему привязаны, а ячейки удаляются линейно.
Ячейка списка
struct MBSNode
    readonly MinionBuff buf
    readonly integer number
    thistype prev = 0
    thistype next = 0
    
    method basicInit takes MinionBuff buf, integer number returns nothing
        static if ErrorDetectionEnabled_MBSNode then
            if this == 0 then
                call ErrorOccurred("Error: MBSNode struct has run out of indexes. Current amount of indexes is 8190", "MBSNodeError")
            endif
        endif
        set this.buf = buf
        set this.number = number
    endmethod
    
    method removeFromList takes nothing returns nothing
        if prev != 0 then
            set prev.next = next
        endif
        if next != 0 then
            set next.prev = prev
        endif
    endmethod
endstruct

struct MBSNodeReal extends MBSNode
    real value = 0.
endstruct

struct MBSNodeInt extends MBSNode
    integer value = 0
endstruct

struct MBSNodeIntNReal extends MBSNode
    integer value = 0
    real rvalue = 0.
endstruct
Структура, в которой содержится списки, называется MBS, в ней есть счётчик количества баффов, это нужно для определения, какой бафф был добавлен раньше.
Списки в основном задаются таким макросом.
Список
// Name - имя типа баффа
// ntyp - тип ячейки
// values - если есть значения у баффа
// bool - если значения типа boolean
// null - нуль для заданного типа значений.

//! textmacro MBSList takes Name, ntyp, values, type, bool, null
    private MBSNode$ntyp$ head$Name$
    readonly integer buffsAmount$Name$ = 0
    
    method add$Name$Buff takes MinionBuff obj returns MBSNode$ntyp$
        local MBSNode$ntyp$ node = MBSNode$ntyp$.create()
        if buffsAmount$Name$ == 0 then
            set head$Name$ = node
        else
            set node.next = head$Name$
            set head$Name$.prev = node
            set head$Name$ = node
        endif
        set BuffCounter = BuffCounter + 1
        set buffsAmount$Name$ = buffsAmount$Name$ + 1
        call node.basicInit(obj, BuffCounter)
        //call DebugMsg("Current amount of $Name$ buffs is " + I2S(buffsAmount$Name$) + ".")
        return node
    endmethod
    
    method remove$Name$Buff takes MBSNode$ntyp$ node returns nothing
        set buffsAmount$Name$ = buffsAmount$Name$ - 1
        if head$Name$ == node then
            set head$Name$ = node.next
        endif
        static if $values$ then
            static if $bool$ then
                set booleanAmount$Name$ = booleanAmount$Name$ - node.value
                set total$Name$Bonus = booleanAmount$Name$ > 0
            else
                set total$Name$Bonus = total$Name$Bonus - node.value
            endif
        endif
        call node.removeFromList()
        call node.destroy()
        //call DebugMsg("Current amount of $Name$ buffs is " + I2S(buffsAmount$Name$) + ".")
    endmethod
    
    static if $values$ then
        static if $bool$ then
            private integer booleanAmount$Name$ = 0
            readonly boolean total$Name$Bonus = false
        else
            readonly $type$ total$Name$Bonus = $null$
        endif
        
        method change$Name$Value takes MBSNode$ntyp$ node, $type$ value returns nothing
        static if $bool$ then
            local integer v = B2I(value)
            set booleanAmount$Name$ = booleanAmount$Name$ - node.value + v
            set total$Name$Bonus = booleanAmount$Name$ > 0
            set node.value = v
        else
            set total$Name$Bonus = total$Name$Bonus - node.value + value
            set node.value = value
        endif
        endmethod
    endif
//! endtextmacro
В этой же системе определяется текущее кол-во брони, уязвимый ли он или нет, уровень замедления или ускорения, и пр.
При удалении объекта системы удаляются и все навешанные баффы.
Удаление
// Макрос ниже также используется для удаления баффов конкретного типа при каких-то обстоятельствах.
// pos - бафф положительный.
// Ячейки удаляются с удалением баффа.

//! textmacro DeleteBuffs takes Name, first, pos
        static if $first$ then
        local MBSNode node = head$Name$
        local MBSNode temp
        else
        set node = head$Name$
        endif
        loop
            exitwhen node == 0
            set temp = node.next
            $pos$if node.buf.buffdata.negative then
                call node.buf.destroy()
            $pos$endif
            set node = temp
        endloop
//! endtextmacro
...
    method onDestroy takes nothing returns nothing
        //! runtextmacro DeleteBuffs("EoT", "true", "//# ")
        //! runtextmacro DeleteBuffs("Shield", "false", "//# ")
        //! runtextmacro DeleteBuffs("MoveSpeed", "false", "//# ")
        //! runtextmacro DeleteBuffs("Stun", "false", "//# ")
        //! runtextmacro DeleteBuffs("Persistence", "false", "//# ")
        //! runtextmacro DeleteBuffs("PhysArmor", "false", "//# ")
        //! runtextmacro DeleteBuffs("MagcArmor", "false", "//# ")
        //! runtextmacro DeleteBuffs("Armor", "false", "//# ")
        //! runtextmacro DeleteBuffs("PhysImmune", "false", "//# ")
        //! runtextmacro DeleteBuffs("MagcImmune", "false", "//# ")
        //! runtextmacro DeleteBuffs("Protection", "false", "//# ")
        //! runtextmacro DeleteBuffs("Invul", "false", "//# ")
        call DebugMsg("MinionBuffStorage " + I2S(this) + " is deleted.")
    endmethod

Сами баффы.
Каждый бафф - отдельная структура, которая наследуется от общей.
Бафф
struct MinionBuff
    integer typ
    Minion target
    unit source
    CustomPlayer owner
    MinionBuffData buffdata
    integer currticks = 0
    boolean isRunning = false
    MBSNode node1
    MBSNode node2
    MBSNode node3
    
//! runtextmacro CustomTimer("t", "Timer", "MinionBuff", "false")

    method onDestroy takes nothing returns nothing
        call DeleteTimer()
        call RemoveSavedInteger(Hash, target.id, typ)
        call UnitRemoveAbility(target.minion, buffdata.effectid)
        set source = null
        call DebugMsg("MinionBuff " + I2S(this) + " is deleted.")
    endmethod
    
    method basicInit takes Minion host, unit src, CustomPlayer p, integer T returns nothing
        static if ErrorDetectionEnabled_MinionBuff then
            if this == 0 then
                call ErrorOccurred("Error: MinionBuff struct has run out of indexes. Current amount of indexes is 8190", "MinionBuffError")
            endif
        endif
        set typ = T
        set target = host
        set source = src
        set owner = p
        set buffdata = p.minion_buffdata[T]
        call InitTimer()
        call SaveInteger(Hash, target.id, T, this)
        //call DebugMsg("MinionBuff " + I2S(this) + " is created.")
    endmethod

...

endstruct
Может быть всего 3 разных типа эффекта у баффа, пока максимум было 2.
Примеры баффов
struct BuffMultishotDoT extends MinionBuff
    method onCreate takes nothing returns nothing
        set node1 = target.addEoTBuff(this)
    endmethod
    
    method onDestroy takes nothing returns nothing
        call target.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, false)
            return currticks == 0
        endif
        return true
    endmethod
    
    static method launch takes Minion m, Tower caster returns nothing
        //                     target,     sourceunit,          owner,           name,    neg,  disabl, chkowne, args
        //! runtextmacro InitBuff("m", "caster.tower", "caster.owner", "MultishotDoT", "true", "false", "false", "")
        call startPeriodic(buffdata.startticks)
    endmethod
endstruct

struct BuffMultishotSlow extends MinionBuff
    method onCreate takes real slow returns nothing
        set node1 = target.addMoveSpeedBuff(this, slow)
    endmethod
    
    method onDestroy takes nothing returns nothing
        call target.removeMoveSpeedBuff(node1)
    endmethod
    
    static method launch takes Minion m, Tower caster, real slow returns nothing
        //! runtextmacro InitBuff("m", "caster.tower", "caster.owner", "MultishotSlow", "true", "true", "false", "slow")
        call start()
    endmethod
endstruct
Макрос InitBuff инициализирует любой бафф, но вопрос не об устройстве баффов, потому опустим его полное устройство. При вызове баффа (метод launch) делаются некоторые проверки, если всё хорошо, то создаётся объект и вызывается его метод onCreate, где сохраняются ссылки на его ячейки.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
30
Я так понимаю, вы с динамическими триггерами работаете и вам нужно массово добавлять к ним события/условия/действия?
33
а какую инфу от объекта нужно в эти триггеры передавать?
13
Из самой примитивности, что напрашивается: заносить в хэш-таблицу по уникальному ключу - id хендла триггеры (можно сделать так, чтобы это был либо id первого элемента - либо генерировать этот номер каким-то другим способом), после чего заносить триггеры. Запись будет выглядеть так: SaveTriggerHandle(Hashtable,idOfTriggerGroup,triggerIndex,trigger). А внутри объекта хранить номер на эту последовательность (UserData либо тоже заносить в хэндл, если UserData будет не достаточно).
P.S. Как-то давно я так реализовывал группу деструбов и группу предметов.
29
Структура с массивом триггеров. Индекс структуры хранишь в таблице по хендлу юнита
По надобности добавляешь в структуру методы для манипуляций с массивом
32
Да работа с динамическими триггерами, но условия события и действия добавлять им ненужно, нужно сделать их экзекут (всех) по нужному событию, или невсех в зависимости от параметра, на одном юните может висеть сразу несколько триггеров, а их нужно экзекутнуть.
Есть в краце - есть много, баффов\дебафов реазизованных на мемхаке, и требуется единая бд для работы с ними, чтобы реализовать сложение\подавление\рассеивание эффектов. До пока их было мало я просто делал ифами, но щяс их стало уже довольно много, и я решил привести все это дело в порядок, предварительно я решил посоветоваться с людьми, мб кто то уже делал что то подобное и реализовал это как-нибудь иначе?
17
У меня сделано через структуры +таймеры.
На таймер крепятся все бафы/дебафы, когда он истекает = статы пропадают
Отдельная ХТ под всю эту систему, пока хз, как будет проседать под сильной нагрузкой
На хендл юнита крепятся таймеры (в свободные ячейки(пока всего лишь 30))
Каждый новый баф кидается в новую ячейку, и потом лупом перебираются те, что ещё есть.
Скрины:
Правда с таким способом, вам наверно придётся переделывать всё под один шаблон...
Загруженные файлы
30
Да работа с динамическими триггерами, но условия события и действия добавлять им ненужно, нужно сделать их экзекут (всех) по нужному событию, или невсех в зависимости от параметра, на одном юните может висеть сразу несколько триггеров, а их нужно экзекутнуть.
Тут уже налицо проблема архитектуры. Если нужно совершить действие над группой юнитов - берём группу и совершаем действие. Если нужно добавить юниту прослушивание события, то добавляем прослушивание события...
ИМХО, динамические триггеры это злейшее зло, от которого нужно избавляться. Ибо в идеальной вселенной существуют только события/условия/действия, а вы ещё плодите ненужную сущность в виде триггеров, на которую у движка есть ещё и лимит.
32
Для корректной работы многих фишек мемхака нам нужно следить за юнитом особенно тщательно, нужно отслеживать смерть, порой урон, приказы и многое другое, ну а раз уж есть триггер то и в нем есть функция утилизации при экзекуте, так реализован диспел. Примерно так же сделаны стандарные баффы близзардов, это внутриигровые триггеры которые крепятся за юнитом и следят за ним.
Для того же стана - не годятся таймеры, не говоря про псевдоконтроль. Идея хранить все эти триггеры-баффы в одном месте и удобно взаимодействовать с ними.
28
Для того же стана - не годятся таймеры, не говоря про псевдоконтроль.
А почему?
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.