"Бафф" в ковычках, т.к. мне требуется только таймер, и ссылка на функцию.
Что то вроде:
код
есть родительский класс бафф
инициализация чилд классов
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
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 переменную, ты же всё равно ничего не добавляешь в клан.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.