System BuffStruct

Discussion in 'Systems and Snippets' started by Jesus4Lyf, Jan 28, 2010.

  1. 13lade619

    13lade619 is now a game developer :)

    +399 / 0 / -0

    I think Buffstruct starts at a high enough value for a 'regular' user to reach.
  2. Jesus4Lyf

    Jesus4Lyf Good Idea™

    +394 / 0 / -0
    Whilst I agree on principle, it's in the config section for that very reason... it is not difficult to change. Also, I don't see how any script could detect this (especially not correctly) since I can have two buffs with the same name, one positive and one negative, which are different objects. Keeping in mind that buffs made by BuffStruct are never removed, but are purposefully overwritten every time you save, what if you change a buff slightly? I don't believe a script can do this right for BuffStruct.

    I also agree with Blade, but admit it's not perfect.

    And there is absolutely no documentation on that link. Sorry, it is unhelpful as it describes nothing about how it works or what it does.
  3. Nestharus

    Nestharus o-o

    +83 / 0 / -0
    Well, LUA_GET_VAR_OBJECT retrieves values by variables rather than by value, so you can label your values. If you have 2 of the same buff with +-, then make one like BUFF_POS and other BUFF_NEG.

    As it says in the link, it essentially lets you do this
    constant integer UNITS_PEASANT='hpea'

    Where the UNITS_PEASANT is a 100% dynamic variables (the values is generated by lua and the variable is imported into the map).

    Getting a dynamic variable is pretty easy-
    function getvarobject(base, objtype, varname, import)

    getvarobject returns the id generated for the specific variable given a base id, object type, variable name, and whether or not the variable should be imported into the map.

    For example, to get that UNITS_PEASANT (w/ some unknown id)

    id = getvarobject("hpea", "units", "UNITS_PEASANT", true)

    that would return a unique id for an object based on the peasant. It would also import a variable called UNITS_PEASANT that has the id in it. If you were to use this function on a variable that already exists-

    id = getvarobject("hpea", "units", "UNITS_PEASANT", true)
    id = getvarobject("hpea", "units", "UNITS_PEASANT", true)

    It would return the same id each time as it first looks up the variable for the specific map before generating a new id. This means that you ids are safe.

    It keeps a persistent table in the background for each map that catalogs all of the generated ids and their variables, meaning you will never have a collision and deleting a script/objects from the map is safe.

    getvarobjectname(value) will retrieve the variable name of a value. Now this could receive an update to retrieve a variable given a value and an object type since values between object types don't collide, but eh ;D. So yes, this makes it so 0 ids collide, even between object types.

    getvarobjectvalue(objectname) retrieves the value given a variable

    When finished generating objects, you call function updateobjects() to update the table.

    Example from UnitIndexer
    //! externalblock extension=lua ObjectMerger $FILENAME$
        //place the file header
        //! runtextmacro LUA_FILE_HEADER()
        //import the script
        //! i dofile("GetVarObject")
        //get the dynamic object id and import it under ABILITIES_UNIT_INDEXER var
        //! i local id = getvarobject("Adef", "abilities", "ABILITIES_UNIT_INDEXER", true)
        //create the object
        //! i createobject("Adef", id)
        //! i makechange(current, "anam", "Unit Indexing")
        //! i makechange(current, "ansf", "(Unit Indexing)")
        //! i makechange(current, "aart", "")
        //! i makechange(current, "arac", "0")
        //update the persistent table for the map
        //! i updateobjects()
    //! endexternalblock
  4. muzzel

    muzzel New Member

    +1 / 0 / -0
    Too bad the bug with the bufficon removal on multiple buff instances of same type has not been fixed, is there some kind of inofficial fix somewhere? Otherwise im really thinking about fixing it, should be less work then writing a similar system from scratch.
  5. Jesus4Lyf

    Jesus4Lyf Good Idea™

    +394 / 0 / -0
    Totally forgot that bug even exists! :p
  6. Laiev

    Laiev Hey Listen!!

    +187 / 0 / -0

    Thinking about to fix it?
  7. Jesus4Lyf

    Jesus4Lyf Good Idea™

    +394 / 0 / -0
    Ahh.. busy on my project *_*.. if I recall correctly, it wasn't trivial because you can't nest text macros, so I'd have had to use a hashtable to do the attachment, instead of AIDS... don't recall exactly... but I wanted to make AIDS a module, and I don't know if it was possible... so I should have just used a hashtable... zz...

    There was some other thing, you could just use the original buff which was on the unit or something instead of adding it twice to the unit, and reference count it, so you didn't need the fix... which often makes more sense... so long ago... lol

    More keen to work on my current project. :)
  8. Immolation

    Immolation Member

    +20 / 0 / -0
    There should be a way to refresh the buff duration, get the remaining time of the buff, increase buff duration.
    And more event responses maybe? Such as onSpellCast, onSelection, onKill, onXXX ...
  9. muzzel

    muzzel New Member

    +1 / 0 / -0
    As far as i remember this system doesnt even handle the duration of buffs (except the destroyeTimed), but you can code that yourself.

    The problem i see about an integer that just counts the current instances on the unit via struct member is: This way you have only 1 instance of the buff + some information about how often its applied. If you want your buffs to have different values (like a buff that increases the targets damage dependent on the casters intelligence) you cannot store this information in one instance.

    Imo, an easy solution would be:
    Whenever a buff is removed you just loop through all buffs of the unit and search for other instances of that bufftype. The solution is O(n), but seriously, how many buffs do you usually have on a single unit? Im not sure how your data structure looks like, maybe you find a way you can skip some buffs (like only loop through buffs with same alignment).
  10. Jesus4Lyf

    Jesus4Lyf Good Idea™

    +394 / 0 / -0
    See: SpellStruct.
    I used the systems in conjunction. SpellStruct would create/destroy BuffStructs, using its own timed methods, it's super easy, but SpellStruct was before its time, it seems.

    That way you can control your buffs soooo readily and in any way you want. Use Status for status effects, wrap them into BuffStructs, and apply/remove them using SpellStruct. Feel the power!

    Seriously, read the two demonstrations. SpellStruct = mapping on easy mode. It wraps all the nasties of WC3.

    Protip: combine Damage into BuffStruct for some real OP mapping powerz.

    In a handful of lines you can make something like a channeled ability that reduces all damage received by the target by 90% or something, with an icon on the buff tray and everything. And you can make other abilities dispel or steal the buff! :D
  11. luorax

    luorax Invasion in Duskwood

    +67 / 0 / -0
    SpellStruct rocks, I must admit it. Each spell (except passive ones) is based on a SpellStruct (plus some data holder, dummy array structs, where needed), and hell yea, creating a spell is super easy and the output is super efficient. <3 J4L libraries.
    • Like Like x 1
  12. BestOnServer

    BestOnServer New Member

    +0 / 0 / -0
    Never mind found it :)
  13. 13lade619

    13lade619 is now a game developer :)

    +399 / 0 / -0
    I just made a workaround for multi-instancing by having a separate BuffManager struct to count instances.
  14. Grundy

    Grundy Ultra Cool Member

    +35 / 0 / -0
    Is there a way to destroy/remove/dispel a buff without triggering the preDestroy event? Like in the Return example, if the Return buff is dispelled wouldn't dispelling make the unit move to their original position? Seems like that would not be the intended effect of a dispel.

    Or would the right way to make a dispellable Return be to create a timer when the buff is created, when the timer expires make the unit move back to its original position and destroy the buff. Instead of relying on the destroy to cause the effect and a dispel would destroy the buff and end the timer before the unit gets moved back to its original position?
  15. PurgeandFire

    PurgeandFire zxcvmkgdfg

    +513 / 0 / -0
    This is how I would do it. I would have all the stuff that happen when the spell ends in the preDestroy function or w/e. At the end of the timer and for the dispel, just check if the unit has the buff or not. That way, you can avoid the problem of doing the things twice and whatnot.

    If that is not what you meant then explain a bit more and maybe I can offer a solution.
  16. Swan

    Swan New Member

    +2 / 0 / -0
    I know this hasn't been posted in for a while but I'm having a bug using it, the wrong buffs are being appplied. It doesn't actually effect the function of the spells but the wrong buff icon shows in the status tray which is a little annoying. The ability, intended buff and actually applied buff respectively are below.
    //! runtextmacro DurationBuff("HungryPseudopods")
    scope HungryPseudopods initializer Init
        private integer array Pods
        private constant integer ABILID = 'A0HP'
        private constant string PROJID = "HungryPseudopods"
        private constant string PODSON = "immolation"
        private constant string PODSOFF = "unimmolation"
    private struct Data
        unit caster
        boolean on
    private function Core takes nothing returns boolean
        local Data d = TT_GetData()
        local unit u = null
        local integer level = GetUnitAbilityLevel(d.caster,ABILID)
        local Projectile p
        if GetUnitAbilityLevel(d.caster,'B017') > 0 and d.on == true then
            set P = GetOwningPlayer(d.caster)
            call GroupEnumUnitsInRange(G2,GetUnitX(d.caster),GetUnitY(d.caster),450,Condition(function EnemyAliveFilter))
                set u = FirstOfGroup(G2)
                exitwhen u == null
                set p = Projectile.create()
                call p.ProjectileSetId(PROJID)
                call p.ProjectileSetModel("Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayDamage.mdl",50,50,50,255,0.8)
                call p.ProjectileTargetHoming(d.caster,GetUnitX(d.caster),GetUnitY(d.caster),20,u,35)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayDamage.mdl",Projectile_HitUnit,"chest"))
                call GroupRemoveUnit(G2,u)
            set Pods[GetUnitId(d.caster)] = 0
            call d.destroy()
            return true
        return false
    private function ConditionsOn takes nothing returns boolean
        return OrderId2String(GetIssuedOrderId()) == PODSON
    private function ActionsOn takes nothing returns nothing
        local Data d = Data.create()
        set d.caster = GetOrderedUnit()
        set Pods[GetUnitId(d.caster)] = d
        set d.on = true
        call TT_StartEx(function Core,d,1)
    private function ConditionsOff takes nothing returns boolean
        return OrderId2String(GetIssuedOrderId()) == PODSOFF
    private function ActionsOff takes nothing returns nothing
        local Data d = Pods[GetUnitId(GetOrderedUnit())]
        set Pods[GetUnitId(GetOrderedUnit())] = 0
        set d.on = false
    private function HitConditions takes nothing returns boolean
        return Projectile_Id == PROJID
    private function HitActions takes nothing returns nothing
        local HungryPseudopods buf
        local unit BuffCaster = Projectile_Source
        local unit BuffTarget = Projectile_HitUnit
        set BuffAbilId = ABILID
    //    call buf.create(BuffTarget).destroyTimed(1.5)
        call AddBuffHungryPseudopods(BuffCaster,BuffTarget,2,true)
        call Damage_Physical(BuffCaster,BuffTarget,GetUnitAbilityLevel(BuffCaster,ABILID)*2-1+0.1*GetHeroAgi(BuffCaster,true),DARKNESS,false,false)
        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayDamage.mdl",BuffTarget,"chest"))
        set BuffCaster = null
        set BuffTarget = null
    private function Init takes nothing returns nothing
        local trigger Trig = CreateTrigger()
        local trigger Trig2 = CreateTrigger()
        local trigger Hit = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(Trig,EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerAddCondition(Trig,Condition(function ConditionsOn))
        call TriggerAddAction(Trig,function ActionsOn)
        call TriggerRegisterAnyUnitEventBJ(Trig2,EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerAddCondition(Trig2,Condition(function ConditionsOff))
        call TriggerAddAction(Trig2,function ActionsOff)
        call TriggerRegisterEventProjectileHit(Hit)
        call TriggerAddCondition(Hit,Condition(function HitConditions))
        call TriggerAddAction(Hit,function HitActions)
    //! runtextmacro BuffType("HungryPseudopods")
        //! runtextmacro SetBuffName("Hungry Pseudopods")
        //! runtextmacro SetBuffAlignment("NEGATIVE")
        //! runtextmacro SetBuffTooltip("Being dissolved by pseudopods.")
        //! runtextmacro SetBuffIcon("ReplaceableTextures\\PassiveButtons\\PASBTNPoisonSting.blp")
    //! runtextmacro BuffStruct()
        unit caster
        unit target
        integer abilId
        integer armor
        method onCreate takes nothing returns nothing
            set this.abilId = BuffAbilId
            set this.caster = BuffCaster
            set = BuffTarget
        method onApply takes nothing returns nothing
            set armor = GetUnitAbilityLevel(this.caster,this.abilId)*1
            call Status[this.unit].modArmorBonus(-armor)
        method onRemove takes nothing returns nothing
            call Status[this.unit].modArmorBonus(armor)
    //! runtextmacro EndBuff()
    //! runtextmacro BuffType("CalamitousGaze")
        //! runtextmacro SetBuffName("Calamitous Gaze")
        //! runtextmacro SetBuffAlignment("NEGATIVE")
        //! runtextmacro SetBuffTooltip("Bad luck gets you hurt.")
        //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTNEyeofblood.blp")
    //! runtextmacro BuffStruct()
        unit caster
        unit target
        integer attackSpeed
        method onCreate takes nothing returns nothing
            set this.caster = BuffCaster
            set = BuffTarget
    //! runtextmacro EndBuff()
    However I have noticed that each buff being applied is one lower in the buffstruct section than the intended buff. So for example in the attached image Frazzle is applying Crystal Cage.

    Attached Files:

  17. jonas

    jonas Well-Known Member

    +42 / 4 / -0
    Is it possible that you have accidentally created an additional buffstruct somewhere in your code? I haven't ever used this library so I can't really tell what's the error, but I think your best shot is to go through the original JASS code.
  18. Swan

    Swan New Member

    +2 / 0 / -0
    I got it sorted out, my Jass Newgen didn't want to run text macros because of an extra folder in my warcraft install for modding UI. I just directed it to a different warcraft install cleaned of extra folders and everything worked fine.
    • Like Like x 1
  19. Walter Boyd

    Walter Boyd New Member

    +0 / 0 / -0
    I am searching about buff struct and I have many things that I don't really understand about it. Thanks for sharing this article, it is useful.
    photo effects

Share This Page