Wc3C.net

Wc3C.net (http://www.wc3c.net/forums.php)
-   Systems (http://www.wc3c.net/forumdisplay.php?f=648)
-   -   Intuitive Buff System (http://www.wc3c.net/showthread.php?t=107613)

Rising_Dusk 09-12-2009 07:50 AM

Intuitive Buff System
 
2 Attachment(s)
Zoom (requires log in)
Intuitive Buff System
v1.05

Background:
So, here's a bit of history about this library. The original version was developed for use with my map, Desert of Exile. Eventually, I was asked by Cassiel to work on some things for Tides of Blood. It was for that map that I developed round two of the system, which is the predecessor to the version posted here. Eventually, I was removed from the team for Tides of Blood due to my inability to actually do enough work in a reasonable time frame. Since then, I've had this sitting on my hard drive and just recently managed to motivate myself to clean it up, update it to modern day coding standards, add some new functionality, and write the documentation. The interface for the system is about as intuitive as possible, and I am extremely proud to present it to you, the community, at last. I hope you all find it as useful as I have over the years!

Requirements:Notes:
  • This system only supports up to 8192 buff types and 8192 buffs at a given time. This limit should be reasonable for all intents and purposes.
System Code:
Expand Library:

Attached is a demo map that shows many of the facets of the system being used. There are also sufficiently many examples inside the test map to show anyone how to use it. Enjoy!

Flame_Phoenix 09-12-2009 10:22 AM

Seems quite small... I was doing something like this but Anitarf told me it was a bad idea because his system would be better with more features. Ani's system seems more complete I admit, but I wish you good luck on this.

Tot 09-12-2009 11:50 AM

yipeee....the first buff-system i understand....

sry, Anitarf...

Toadcop 09-12-2009 12:42 PM

Quote:

Intuitive Buff System
iBS xDDD 10101010101
iDusk ^_^

the interface is kind a weird...
yes i don't see any sense what buffs are separate structures... cause you have only periodical action and on end action =) there should be also on begin action... (callback)
otherwise spell stealing will be not possible (for example)

but idk do what you want O_o this db is full of shit anyway xD

Flame_Phoenix 09-12-2009 12:49 PM

Anitarf's system is quite complex but once you understand it you won't need anything else.

Tot 09-12-2009 12:58 PM

Quote:

Originally Posted by Flame_Phoenix
Anitarf's system is quite complex but once you understand it you won't need anything else.


do you see my prob? :emote_smile:

Flame_Phoenix 09-12-2009 01:16 PM

Quote:

Once you understand it
Anitarf is usually busy and he is not the perfect example of patience either. If you need help understanding how to use his system I can give you a short introduction.

Viikuna- 09-12-2009 02:50 PM

It works fine and all, but I got one double free of type: dbuff when spamming those spells to those abos.

Rising_Dusk 09-12-2009 02:52 PM

Quote:

Originally Posted by Flame_Phoenix
Seems quite small... I was doing something like this but Anitarf told me it was a bad idea because his system would be better with more features. Ani's system seems more complete I admit, but I wish you good luck on this.

Just like there are 3 different approved Damage Detection Systems in the database, there is nothing wrong with different approaches to buff systems. Ani likely suggested to you to not make yours because he didn't want to have to review your code. :p
Quote:

Originally Posted by Toadcop
yes i don't see any sense what buffs are separate structures... cause you have only periodical action and on end action =) there should be also on begin action... (callback)

I thought about begin, but I mean, you have to call "UnitAddBuff" so you already know when the buff begins. I didn't think the added argument would really be all that useful. Maybe I should add it for completeness?
Quote:

Originally Posted by Viikuna-
It works fine and all, but I got one double free of type: dbuff when spamming those spells to those abos.

What exactly were you doing in the testmap? I saved it in debug mode in case someone found something like that, but I need to know more about what you were doing.

Here-b-Trollz 09-12-2009 03:15 PM

Quote:

Originally Posted by Rising_Dusk
I thought about begin, but I mean, you have to call "UnitAddBuff" so you already know when the buff begins. I didn't think the added argument would really be all that useful. Maybe I should add it for completeness?


There are cases in which you don't actually know what buff you are getting, such as buff steal or buff push spells, in which case Anitarf has the upper hand on you, because his system has support for 'abstract' onAdd and onRemove.

Provided you aren't doing stuff that involves indefinite buffs, this is the way to go; Intuitive Buff System is indeed intuitive.

Flame_Phoenix 09-12-2009 03:16 PM

Quote:

Just like there are 3 different approved Damage Detection Systems in the database, there is nothing wrong with different approaches to buff systems. Ani likely suggested to you to not make yours because he didn't want to have to review your code. :p
The truth is so painful sometimes ...

Rising_Dusk 09-12-2009 03:28 PM

Quote:

Originally Posted by Here-b-Trollz
There are cases in which you don't actually know what buff you are getting, such as buff steal or buff push spells, in which case Anitarf has the upper hand on you, because his system has support for 'abstract' onAdd and onRemove.

Provided you aren't doing stuff that involves indefinite buffs, this is the way to go; Intuitive Buff System is indeed intuitive.

Hrm, yes, a GetRandomBuff might be valuable, or a GetTopBuff for purposes of hooking and stealing buffs. The system actually supports it happily, it just doesn't give you a way of finding that buff. I mean, I already use Table, a second table call for finding top/random buff wouldn't be a problem.

Viikuna- 09-12-2009 06:18 PM

Quote:

What exactly were you doing in the testmap? I saved it in debug mode in case someone found something like that, but I need to know more about what you were doing.

I can upload my last replay tomorrow, since I havent played anything after that. You can check what happened by yourself.

Rising_Dusk 09-12-2009 06:41 PM

Uh, I guess. My WC3 doesn't run single player or replays that great, so it'd be way easier if you could just explain the circumstances leading up to the doublefree, but whatever.

Here-b-Trollz 09-12-2009 07:06 PM

Quote:

Originally Posted by Rising_Dusk
Hrm, yes, a GetRandomBuff might be valuable, or a GetTopBuff for purposes of hooking and stealing buffs. The system actually supports it happily, it just doesn't give you a way of finding that buff. I mean, I already use Table, a second table call for finding top/random buff wouldn't be a problem.


Sorry, what I meant by that was that onAdd and onRemove and onPeriodic should be defined for a bufftype, not in UnitAddBuff or whatever.

Rising_Dusk 09-12-2009 07:14 PM

I had it that way originally, honestly, but I thought people might like it more if they could change the callbacks for a given bufftype. Maybe I should revert to the old way, though, it'd save arguments on the UnitAddBuff call as well.

Karawasa 09-12-2009 07:25 PM

Quote:

Originally Posted by Rising_Dusk
Requirements:


I strongly believe you should add support for a third indexing script, PUI. I believe it is in far more use than the two you currently support.

Rising_Dusk 09-12-2009 07:29 PM

PUI doesn't share syntax with UIU/AI (Which is the only reason I can support those two, since optional code doesn't exist in vJass yet). Furthermore, PUI is slow compared to both UIU and AI because its GetUnitIndex function doesn't inline down to GetUnitUserData. The red flavor of UIU functions in a very similar manner to PUI, it just doesn't support the textmacro hacks it has. (Rightly so, personally)

I'm sorry that your map uses PUI, if you want to use it with this system, do a search and replace on "GetUnitId" to make it "GetUnitIndex". Then fix the library requirement and that should fix it right up.

Flame_Phoenix 09-12-2009 07:54 PM

Or ... *cough*Table*cough*
lol, I know you are not going to do it and yes, I know there are no conflicts either xD

Rising_Dusk 09-12-2009 08:08 PM

Table should never be used where UnitUserData will suffice. Period.

Karawasa 09-12-2009 08:16 PM

Quote:

Originally Posted by Rising_Dusk
PUI doesn't share syntax with UIU/AI (Which is the only reason I can support those two, since optional code doesn't exist in vJass yet).


Fair enough, that would certainly be a valid reason.

Quote:

Originally Posted by Rising_Dusk
Furthermore, PUI is slow compared to both UIU and AI because its GetUnitIndex function doesn't inline down to GetUnitUserData.


Is that really enough of a difference to come to that conclusion? There is more going on than just the "Get Index" function in all of these systems. I wonder what you end up with if you consider all aspects. You're welcome to PM me if you don't want to hijack the thread.

Quote:

Originally Posted by Rising_Dusk
I'm sorry that your map uses PUI, if you want to use it with this system, do a search and replace on "GetUnitId" to make it "GetUnitIndex". Then fix the library requirement and that should fix it right up.


Good to know that it's just a syntax thing that is getting in the way.

Rising_Dusk 09-12-2009 08:21 PM

Quote:

Originally Posted by Karawasa
Is that really enough of a difference to come to that conclusion? There is more going on than just the "Get Index" function in all of these systems. I wonder what you end up with if you consider all aspects. You're welcome to PM me if you don't want to hijack the thread.

Well, UIU/AI inlining their GetUnitId call makes it as fast as a single native. PUI's GetUnitIndex calls a few natives, performs several array lookups, performs several array sets, and checks at least a few if/then comparisons before returning the value. The difference is monumental, although in practical applications it may not matter. (Depends upon the frequency of your calling it, really)

Karawasa 09-12-2009 08:28 PM

Quote:

Originally Posted by Rising_Dusk
Well, UIU/AI inlining their GetUnitId call makes it as fast as a single native. PUI's GetUnitIndex calls a few natives, performs several array lookups, performs several array sets, and checks at least a few if/then comparisons before returning the value. The difference is monumental, although in practical applications it may not matter. (Depends upon the frequency of your calling it, really)


Yes, I noticed that "GetUnitIndex" is bloated compared to "GetUnitId" function. What I am still wondering about is the recycling and detection aspect of these systems. Is PUI more efficient regarding these other aspects? If so, does it make up for the performance loss described above?

I use PUI extensively and call it a lot. In fact, anytime a tower attacks or a unit is damaged (notice these are not the same) PUI gets called. If the performance difference is actually noticeable and significant (as opposed to some theoretical nanosecond stuff I don't care about) then I have a problem. However, I'd like to know about recycling and detection before changing to another system.

Bobo_The_Kodo 09-12-2009 08:33 PM

For any possible buff(other than auras), you should have callbacks :

onRefresh
onEnd
onExpire
onPurge
onApply
onPeriodic

(Also if you're wondering why your system doublefrees)
If unit dies on last execute of onPeriodic -> doublefree

edit://

Collapse JASS:
private function BuffTimeout takes nothing returns nothing
    local timer t  = GetExpiredTimer()
    local dbuff db = dbuff(GetTimerData(t))
    
    //Different behavior for periodic buffs than one-timers
    if db.isPeriod then
        //Run the onPeriod callback no matter what
        set TempBuff = db
        call db.onPeriod.execute()
        //Check if this iteration kills the buff
        set db.elapsed = db.elapsed + db.period
        if db.elapsed >= db.duration then
            //Kill the buff completely
            call db.destroy() //Timer gets recycled in here!
        endif
    else
        //Kill the buff completely
        call db.destroy() //Timer gets recycled in here!
    endif
    
    set t = null
endfunction
I like that part, using single timer, but you need :

Collapse JASS:
private function BuffTimeout takes nothing returns nothing
    local timer t  = GetExpiredTimer()
    local dbuff db = dbuff(GetTimerData(t))
    
    //Different behavior for periodic buffs than one-timers
    if db.isPeriod then
        //Run the onPeriod callback no matter what
        set TempBuff = db
        call db.onPeriod.execute()
        //Check if this iteration kills the buff
        set db.elapsed = db.elapsed + db.period
        if db.elapsed >= db.duration then
            //Kill the buff completely
            call db.destroy() //Timer gets recycled in here!
        elseif db.elapsed + db.period > db.duration
            set db.isPeriod = false
            call TimerStart(db.tmr, db.duration-db.elapsed, false, function BuffTimeout)
        endif
    else
        //Kill the buff completely
        call db.destroy() //Timer gets recycled in here!
    endif
    
    set t = null
endfunction
-> but the duration could get changed during that timeout -> hmmm

Flame_Phoenix 09-12-2009 08:37 PM

Bobo -> you are descrbing ABuff...

Bobo_The_Kodo 09-12-2009 08:41 PM

I'm describing what any buffsystem should have. I haven't looked at ABuff (in a long long time.)

Rising_Dusk 09-12-2009 08:50 PM

Well I don't particularly want to recreate ABuff, but at the same time I can see the applications for all of those callbacks. Furthermore, it would appear that the only way to have access to the top buff and random buffs on a unit is via a linked list approach. *Sigh* I don't mind the work, but this library just isn't worth it if ABuff already does all of this as best as can be done. (I really just hate ABuff's syntax structure)

Rising_Dusk 09-13-2009 12:05 AM

Updated to 1.01.
Quote:

Originally Posted by Change Log
v1.01:
Moved all of the callbacks and bufftype specific data from the dbuff struct to the bufftype struct. Added the GetUnitBuff function, which gives the user access to the buff data anywhere they want it. Fixed a doublefree bug that happened if a unit died on the last iteration's callback of a periodic buff. Fixed a bug where periodic buffs with uneven durations would last longer than the actual .duration value.

If you happen to test the demo map, don't mind that "CrushingWave_Notice" stuff. That fires in the onPeriodic callback function and is supposed to spam your screen like it does. (This is not a bug)

Flame_Phoenix 09-13-2009 12:06 AM

ABuff has all of this stuff ....
Though I admit I am curious to see where this is heading. Further more you would (probably) also need to link this resource to your famous IDDS so it could allow buffs that act on damage like ABuff + ADamage do.

Rising_Dusk 09-13-2009 12:11 AM

First of all, I don't expect the IBS to ever do something that ABuff doesn't already do. ABuff covers all angles of the buff creation and manipulation process, it just does so in a way that I, as a user, do not like. That is why I am offering my library as an alternative to ABuff; surely there are users out there who are like me in thinking that the syntax of ABuff just isn't for them.

Also, IDDS and IBS are fully compatible. Because of how easy the IDDS and IBS are to use, the IBS won't ever require the IDDS. If you want a buff that happens on damage, then rock on, you can do it already with the version that's up.

Flame_Phoenix 09-13-2009 11:00 AM

What about auras and stun? Are you also going to make them?

DanThanh 09-13-2009 01:09 PM

The thing i like is all Dusk's systems are convenient and comprehensive following by good samples !

Rising_Dusk 09-13-2009 03:02 PM

Quote:

Originally Posted by Flame_Phoenix
What about auras and stun? Are you also going to make them?

Auras are something that I find don't require a custom buff system even. Just add a dummy tornado aura to a unit and uh, voillah, you've got your custom aura with whatever range and stuff you wanted. I don't need to trigger that behavior.

Stun is also something I don't think should be covered in a buff system. The buff system is for handling custom aura ability based buffs, not stun. Stun is something that should have its own library. (And I do plan to submit one soon)

Themerion 09-13-2009 06:24 PM

I like this approach. Simple, clean, and non-oop (remember, Warcraft jass is not oop).

Well done!

---
EDIT
---

Just to check...
Table:
//* In order to make a buff work, one must first create a custom aura ability in
//* the Object Editor and a custom buff for that aura. The raw ids for these
//* objects need to be suppled to the IBS via the DefineBuffType function.
=>Script-disabled Spellbook
Right? (or can it be done in a simpler manner)

Anachron 09-16-2009 08:39 AM

Thanks Rising, this system is very useful indeed. Will use it I think.

There are still room for improvements:
What about making these:

Collapse JASS:

function UnitAddBuffPermanent takes unit source, unit target, integer btype, ...

function GroupAddBuffPermanent takes group allUnits, unit source, integer btype, ...

function GroupAddBuffTimed takes unit source, unit target, integer btype, ..., real duration


Also, if you don't do it, I might do an additional library with the ability to protect buffs.

Rising_Dusk 09-16-2009 02:33 PM

Quote:

Originally Posted by Themerion
Right? (or can it be done in a simpler manner)

You don't need a script disabled spellbook, just base your aura ability on Tornado Slow Aura which already doesn't have an icon.
Quote:

Originally Posted by Anachron
There are still room for improvements:
What about making these:

I do not like the group buff addition functions you have suggested. All the function would do is clone the group and FirstOfGroup loop through it and then restore the group at the end. Really, this is limited in application, because in many cases you will want to do more than just add the buff to a group. You will want to deal damage, play a special effect (could be on onAdd), move the unit, or do something else to the units affected. If you did GroupAddBuffTimed, then you'd be looping through the group twice to accomplish those things, which is inefficient. It is not difficult to just FirstOfGroup loop through the group on your own and do everything you need in one fell swoop, so I strongly recommend doing that.

UnitAddBuffPermanent is a reasonable request, though. What sort of behavior would you want it to have? Persistence on death is rather lame, otherwise you would just add the aura ability to the unit and wouldn't want UnitAddBuff in the first place. I think just having a buff that ended on death with 'infinite' duration would suffice.
Quote:

Originally Posted by Anachron
Also, if you don't do it, I might do an additional library with the ability to protect buffs.

"Protect" buffs?

Anachron 09-16-2009 02:57 PM

Quote:

UnitAddBuffPermanent is a reasonable request, though. What sort of behavior would you want it to have? Persistence on death is rather lame, otherwise you would just add the aura ability to the unit and wouldn't want UnitAddBuff in the first place. I think just having a buff that ended on death with 'infinite' duration would suffice.
You should atleast make it possible to add a check condition that is runned for checking if the buff can be removed. Like:
Only exists till unit has 100 mana!
Something like this.

Also, permanent buffs could be ver y useful indeed, for example they last until a unit is getting hit, attacked, dead, or has attacked. There are many uses of this. Its just one more native function. And I would highly recommend it.

Oh, before I forgot is: Auras are an horrible way to add buffs! They are delayed, bug and ofcourse, are limited in its use. (Since we can't have endless auras)

Quote:

"Protect" buffs?
Yeah, buffs which can not be removed, until something is met. Basicly, I would like to add a condition (as above) to check whether a buff can be removed.

Rising_Dusk 09-16-2009 03:25 PM

Quote:

Originally Posted by Anachron
You should atleast make it possible to add a check condition that is runned for checking if the buff can be removed. Like:
Only exists till unit has 100 mana!
Something like this.

Uh, no, that shouldn't be done by the system, because the user would do that here:
Collapse JASS:
scope MyScope
//...Stuff
private function PeriodicCallback takes nothing returns nothing
    local dbuff db = GetEventBuff()
    if GetUnitState(db.target, UNIT_STATE_MANA) >= 100 then
        //Omg buff should be removed
        call UnitRemoveBuff(db.target, db.btype)
    endif
endfunction
endscope
Quote:

Originally Posted by Anachron
Also, permanent buffs could be ver y useful indeed, for example they last until a unit is getting hit, attacked, dead, or has attacked. There are many uses of this. Its just one more native function. And I would highly recommend it.

It's not a native, and I already agreed with you that they'd be useful. I asked you what behavior you'd want them to have.
Quote:

Originally Posted by Anachron
Oh, before I forgot is: Auras are an horrible way to add buffs! They are delayed, bug and ofcourse, are limited in its use. (Since we can't have endless auras)

...Auras are how this system and all buff systems work. They are not limited, they are not delayed (they apply the buff instantly), and you can have endless auras on a unit..
Quote:

Originally Posted by Anachron
Yeah, buffs which can not be removed, until something is met. Basicly, I would like to add a condition (as above) to check whether a buff can be removed.

Well, obviously you'd create a wrapper unique to your map for UnitRemoveBuff to accomplish that, since there are thousands of conditions you could want to meet, each potentially firing on a different event.

Of course, you could also save a struct inside the db.level member of the dbuff struct, and inside that struct have a boolean on whether a buff can be destroyed or not. (db.level.isDestroyable) Then you could meet your "condition" and set that to true. Just check the boolean before you call UnitRemoveBuff and voillah, done.

Anachron 09-16-2009 03:32 PM

Quote:

It's not a native, and I already agreed with you that they'd be useful. I asked you what behavior you'd want them to have.
Behavior? Just add an boolean isPermanent.

Quote:

...Auras are how this system and all buff systems work. They are not limited, they are not delayed (they apply the buff instantly), and you can have endless auras on a unit..

O.o really? Heared about unit aura stack bug, a few times. So kewl, just learned again something new.

Rising_Dusk 09-16-2009 03:42 PM

Quote:

Originally Posted by Anachron
Behavior? Just add an boolean isPermanent.

Behavior in the sense of how they should act, be overwritten, etc. That boolean would be lame, I'd rather just have .duration = DURATION_INFINITE (where that constant is defined arbitrarily high, or possibly negative so it's out of bounds for the user to use). This'd be a better approach since then I wouldn't have to bloat the struct with more variables. (If you really wanted, could add a method for .isPermanent that returns .duration == DURATION_INFINITE)

The only concern I have is that a permanent buff wouldn't be able to be overwritten without removing it first. The default buff behavior for buff overwriting is only such that longer duration buffs overwrite shorter duration ones. However, since this is INFINITE (logically), nothing could overwrite it.


All times are GMT. The time now is 11:19 AM.

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services