System BuffStruct

Jesus4Lyf

Good Idea™
Reaction score
397
So if someone has [ljass]'A000'[/LJASS] in their map and you overwrite it, they can't get it back unless it's backed up, and there's no warning it will be overwritten, so it's ... bad.
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.

Edit:
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.
 

Nestharus

o-o
Reaction score
84
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
[ljass]constant integer UNITS_PEASANT='hpea'[/ljass]

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
JASS:

//! 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
 

muzzel

New Member
Reaction score
1
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.
 

Jesus4Lyf

Good Idea™
Reaction score
397
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. :)
 

Immolation

Member
Reaction score
20
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 ...
 

muzzel

New Member
Reaction score
1
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).
 

Jesus4Lyf

Good Idea™
Reaction score
397
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 ...
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
 

luorax

Invasion in Duskwood
Reaction score
67
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.
 

13lade619

is now a game developer :)
Reaction score
400
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.

I just made a workaround for multi-instancing by having a separate BuffManager struct to count instances.
 

Grundy

Ultra Cool Member
Reaction score
35
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?
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
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.
 

Swan

New Member
Reaction score
1
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.
Code:
//TESH.scrollpos=9
//TESH.alwaysfold=0
//! runtextmacro DurationBuff("HungryPseudopods")
scope HungryPseudopods initializer Init
globals
    private integer array Pods
    private constant integer ABILID = 'A0HP'
    private constant string PROJID = "HungryPseudopods"
    private constant string PODSON = "immolation"
    private constant string PODSOFF = "unimmolation"
endglobals

private struct Data
    unit caster
    boolean on
endstruct

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))
        loop
            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)
        endloop
    else
        set Pods[GetUnitId(d.caster)] = 0
        call d.destroy()
        return true
    endif
    return false
endfunction

private function ConditionsOn takes nothing returns boolean
    return OrderId2String(GetIssuedOrderId()) == PODSON
endfunction

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)
endfunction

private function ConditionsOff takes nothing returns boolean
    return OrderId2String(GetIssuedOrderId()) == PODSOFF
endfunction

private function ActionsOff takes nothing returns nothing
    local Data d = Pods[GetUnitId(GetOrderedUnit())]
    set Pods[GetUnitId(GetOrderedUnit())] = 0
    set d.on = false
endfunction

private function HitConditions takes nothing returns boolean
    return Projectile_Id == PROJID
endfunction

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
endfunction

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)
endfunction

endscope
Code:
//! 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 this.target = BuffTarget
    endmethod
    method onApply takes nothing returns nothing
        set armor = GetUnitAbilityLevel(this.caster,this.abilId)*1
        call Status[this.unit].modArmorBonus(-armor)
   
    endmethod
    method onRemove takes nothing returns nothing
        call Status[this.unit].modArmorBonus(armor)
    endmethod
//! runtextmacro EndBuff()
Code:
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! 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 this.target = BuffTarget
    endmethod
//! 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.
 

Attachments

  • buffs section.png
    buffs section.png
    26.9 KB · Views: 473

jonas

You can change this now in User CP.
Reaction score
64
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.
 

Swan

New Member
Reaction score
1
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.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top