System BuffStruct

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.
 
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
 
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.
 
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. :)
 
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 ...
 
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).
 
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
 
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.
 
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.
 
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?
 
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.
 
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: 686
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.
 
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.
  • Varine Varine:
    A probate is usually done with a will, yes? If so I am sorry for your loss
    +1
  • The Helper The Helper:
    Yeah Tom, me too sorry for your loss buddy my mom told me she finds out her olds friend died from Google searching them. She had not talked to one of her old friends in a year and found out she died from Google. Also another one in the same session. RIP all of them my sincere condolences Tom
    +1
  • Varine Varine:
    We have some elderly guests that regularly come hang out at the bar at the end of the night, and every once in a while we don't see someone for a few weeks and then someone shows up with their obituary.
  • Varine Varine:
    We usually let them do their memorials there in the morning if they want to and I'll make them some snacks and drinks. There was one guy named Tom that came in like every night and would sit by himself and get a bunch of soup and a glass of wine. idk why but he LOVED our fucking soup, like he would order a fucking quart of it at a time and would always get so sad when we stop doing it for the summer.
    +1
  • Varine Varine:
    But he also loved our calamari, which is another thing I hate but it sells super well so I can't change it. There was one day he came in and was asking me how to make it, because he tried to at home once in the off season when we stop running it and he really wanted it lol
  • Varine Varine:
    I think he's one of the only people I've made recipes for for free because he really wanted a broccoli cheddar, and it was like dude I don't have a recipe, it's just whatever I have, but here, this is how you do it
  • Varine Varine:
    I don't think he ever figured out how to do the calamari in a pan though, like idk how to do that either. He was afraid of the at home deep fryers though and it's like yeah, that's fair, I am too
  • Varine Varine:
    He was just such a sweet old man, we had two servers pregnant and they held a baby shower together, he was soooooo fucking excited to get to see a baby. Unfortunately he died a month or so before they were born
  • The Helper The Helper:
    So I decided to Google some people that I had not seen or heard from in a while and sure enough one of my old best friends, we had a falling out years ago but whatever, find out he died of Pancreatic Cancer in January. I have also lost a few of my closer acquaintances from growing up the last year. Getting old - people die - I kinda thought it was going to be this way a few years ago....
    +2
  • The Helper The Helper:
    Forum running super slow again
  • Ghan Ghan:
    Not really clear from the stats as to what is causing the slowness.
  • Ghan Ghan:
    We get a lot of guest traffic so it may just be the load is getting too high and not from any particular source.
  • Ghan Ghan:
    Looks like the server is maxed out on CPU.
  • Ghan Ghan:
    Oh it looks like a lot of the traffic is Silkroad Forums. That domain isn't protected by Cloudflare.
  • Ghan Ghan:
    But the old Silkroad site is still on its own server. I just had a test site set up on this server for it.
  • Ghan Ghan:
    I just disabled that test site. Let's see if that helps the load.
  • Ghan Ghan:
    Looks much better already.
  • The Helper The Helper:
    I had actually forgot about the Silkroad site. I had asked
  • The Helper The Helper:
    SD Ryoko about it and he said the couple of people left on there really like it, that was a few years ago, maybe I should check back
  • jonas jonas:
    I guess when you're getting old, and the last day of soup season draws near, you start wondering
  • jonas jonas:
    will I make it to the start of the next season? or was this the last time I'll ever have my favorite dish?
  • The Helper The Helper:
    I am doing my first Vibe Coding project. In installed the environment and tools according to instructions but it is all chat doing this for me at my direction. It is fun really and holy shit I might finish in 2 hours what it would have taken a day to in my Access and this would be an electron app complete new
  • Ghan Ghan:
    Good stuff.
  • Ghan Ghan:
    Just make sure it is secure. :)
  • The Helper The Helper:
    It will only be on internal network

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials
      Top