• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] [Needs fix] AuraLevel

Level 29
Joined
Mar 10, 2009
Messages
5,016
I can't find any buff detector(aura buff) levels here, so I made this one...

Intead of making many buff ID's in the object editor, you could use this...

JASS:
/*
===Aura Level Detection v1.2 by Mckill2009

WHAT IS THIS:
- Intead of making many buff ID's in the object editor, you could use this

PARAMETER EXPLANATION and API:
- forUnit (unit) = the unit who has the buff
- auraID (integer) = the BASE unit(spellID) who gives the buff or has the spell
- buffID (integer) = the buff that will be detected
- aoe (real) = if your aura has 900 aoe, this should be 900 as well
- alliedAura (boolean) = checks if it's an aura by ally or enemy

local integer level = GetAuraLevel(forUnit, auraID, buffID, aoe, alliedAura)

CREDITS:
- Bribe (for the IMaxBJ info and modular mode)
*/

library AuraLevel

function GetAuraLevel takes unit forUnit, integer auraID, integer buffID, real aoe, boolean alliedAura returns integer
    local unit first
    local player owner = GetOwningPlayer(forUnit)
    local integer level = 0
    if GetUnitAbilityLevel(forUnit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(forUnit), GetUnitY(forUnit), aoe, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first == null
            if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitAlly(first, owner) == alliedAura then
                set level = IMaxBJ(level, GetUnitAbilityLevel(first, auraID))
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    set owner = null
    return level
endfunction

endlibrary

CREDITS:
- Bribe (for the IMaxBJ info and modular mode)
 

Attachments

  • AuraLevelDetection.w3x
    14.9 KB · Views: 38
Last edited:

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
The local unit that you declare leaks. You should use a global one instead.

But regardless, this function will fail very, very often (sometimes it will fail in more cases than it will work). There is just no point. Start softcoding your buffs using something like the ABuff library, or get over the fact that you can't achieve level-detection of the hardcoded ones.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
show me some instance that this doesnt work?, except fot the BUG that I mentioned above...
  1. Multiple units having the same ability.
  2. The needed unit is not in the specified range of the group enumeration. Use a huge range—this problem becomes less common, but performances degrades and you risk grouping more units that possibly have the same ability. It's a lose–lose situation.
  3. The unit (Hero) levels up and learns a new level of the ability between the time of the buff's application and the buff's level check (the level returned will be higher).
  4. The unit (Hero) unlearns the ability between the time of the buff's application and the buff's level check (the function will return zero).
  5. The unit gets removed (killed under Incinerate (does not apply for hero units); RemoveUnit), gets hidden (HideUnit) or attains the Locust ability. None of these units are enumerated by the GroupEnumUnitsInRange function. A non-hero units that is completely decayed will not be enumerated either (although it can take a lot of time for a unit to decay completely, depending on your gameplay constants).
Is that enough?
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
@Maker
That's the bug Im talking about, Im fixing it...

@Mags
I submit, didnt noticed the return statement stops the nulling...

@BBQ
1) That's the BUG I mentioned, Ima fix that soon...

2) Im gonna add an argument for the AOE so that the user should base that
on the AoE of the spell/aura. That will fix the problem, besides the
enumeration only works if GetUnitAbilityLevel(buffedunit, buffID) > 0...

3-5) If the source levels/learns/unlearn/dies/removed, then oviously the
spellID or aura is leveled up(down) or removed as well, so changes will
surely applies on the returned value...

like I said above, the Title is perhaps misleading, coz it should be AuraDetectionLevel...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
Basically, what I think you want is this:
JASS:
/*
===Aura Buff Level Detection by Mckill2009

WHAT IS THIS?:
- Intead of making many buff ID's in the object editor, you could use this
- Works 100% on Single unit that gives the buff/aura

BUGS:
- Detects the FirstOfGroup unit that gives the buff, so if you have
  3 units, even if your buff is actually 5, the first unit which has a
  lower value detects that.

PARAMETER EXPLANATION:
- buffedunit = the unit who has the buff
- spellID = the BASE unit who gives the buff or the Passive spell
- buffID = the buff that will be detected
*/

library AuraBuffLevel

globals
    private constant real MAX_COLLISION = 172.0
endglobals

function GetAuraBuffLevel takes unit forUnit, integer auraID, integer buffID, real aoe returns integer
    local unit first
    local integer lvl = 0
    if GetUnitAbilityLevel(forUnit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(forUnit), GetUnitY(forUnit), aoe + MAX_COLLISION, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first==null
            if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitInRange(first, forUnit, aoe) then
                set lvl = IMaxBJ(lvl, GetUnitAbilityLevel(first, auraID))
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    return lvl
endfunction

endlibrary
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
actually, I did something similar, I didnt know about the IMaxBJ, lolz, but I dont have the arg AoE yet...
seems working now, I just need to test it and filter the right units...

EDIT: and yours is much more cleaner...

JASS:
/*
===Buff Level Detection by Mckill2009

WHAT IS THIS?:
- Intead of making many buff ID's in the object editor, you could use this
- Works 100% on Single unit that gives the buff/aura

BUGS:
- Detects the FirstOfGroup unit that gives the buff, so if you have
  3 units, even if your buff is actually 5, the first unit which has a
  lower value detects that.

PARAMETER EXPLANATION:
- buffedunit = the unit who has the buff
- spellID = the BASE unit who gives the buff or the Passive spell
- buffID = the buff that will be detected
*/

library BuffLevelDetection

globals
    private constant real   AOE = 1000
endglobals

private function GetAbilityLevel takes integer abilitylevel_1, integer abilitylevel_2 returns integer
    if abilitylevel_1 > abilitylevel_2 then
        return abilitylevel_1
    elseif abilitylevel_2 > abilitylevel_1 then
        return abilitylevel_2
    endif
    return abilitylevel_1
endfunction

function GetBuffLevel takes unit buffedunit, integer spellID, integer buffID returns integer
    local unit first
    local integer level
    if GetUnitAbilityLevel(buffedunit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(buffedunit), GetUnitY(buffedunit), AOE, null)
        set level = 0
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first==null
            if GetUnitAbilityLevel(first, spellID) > 0 then
                set level = GetAbilityLevel(level, GetUnitAbilityLevel(first, spellID))
            endif  
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    return level
endfunction

endlibrary
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
This is inconsistent, which is why I renamed your function. GetAuraBuffLevel was the most sane & relevant function name I could think of.

JASS:
library @AuraLevelDetection@

function @GetBuffLevel@

You can remove the highlighted part here. Think about it. You're not trying to get the unit who has the aura, you're trying to get the highest level aura.

JASS:
            if not IsUnitType(first, UNIT_TYPE_DEAD) @and GetUnitAbilityLevel(first, spellID) > 0@ then
                set level = IMaxBJ(level, GetUnitAbilityLevel(first, spellID))
            endif

A lot of buffs are 1100 as well. I recommend you just use the script I gave you -_-

JASS:
globals
    //usually, buff is under 1000, so this is recommended
    private constant real   AOE = 1000
endglobals

Edit, here, it's even more simplified, because I forgot auras don't count collision size but actual x/y:

JASS:
library AuraLevel

function GetAuraLevel takes unit forUnit, integer auraID, integer buffID, real aoe returns integer
    local unit first
    local integer lvl = 0
    if GetUnitAbilityLevel(forUnit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(forUnit), GetUnitY(forUnit), aoe, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first==null
            if not IsUnitType(first, UNIT_TYPE_DEAD) then
                set lvl = IMaxBJ(lvl, GetUnitAbilityLevel(first, auraID))
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    return lvl
endfunction

endlibrary

Edit 2: Also, this definitely belongs in Small Code Snippets, not here.

http://www.hiveworkshop.com/forums/triggers-scripts-269/small-code-snippets-40758/
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
This doesn't work properly as it could easily get the buff level of an enemy unit.

Here is the scenario
Unit A gives off an aura that is level 3
Unit B, an enemy to unit A, gives off a level that is level 5

Both are close to each other

Unit C, an ally to unit A, is close to unit A and unit B. When this runs, it will end up returning 5 instead of 3.

But that's not the end of it.. auras may target enemies, allies, or both..

You have to take a boolexpr filter or something in order to make it run properly.


Anyways, for now, this needs a fix =). I'll pm mckill so that he knows to fix it when he gets back =P.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
But that's not the end of it.. auras may target enemies, allies, or both..

You have to take a boolexpr filter or something in order to make it run properly.

auras usually from ally units, so i can be fltred if forUnit and first are allies, so I dont think a boolexpr is needed, Ima update it very soon as Im busy at work that I left behind...Thanks for the info...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
McKill, you can't simply filter out enemy units. I think Nestharus is right, that you should filter out units if they fail X criteria. Simply adding a parameter for a boolexpr and passing the boolexpr into the GroupEnumUnitsInRange function will do the trick.

I think the following has the best of both worlds: it keeps the simplicity you have while adding the complexity which is sometimes necessary by adding a boolexpr.

JASS:
native UnitAlive takes unit id returns boolean

function GetAuraLevelEx takes unit forUnit, integer auraID, integer buffID, real aoe, boolexpr filter returns integer
    local unit first
    local integer level = 0
    if GetUnitAbilityLevel(forUnit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(forUnit), GetUnitY(forUnit), aoe, filter)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first == null
            if UnitAlive(first) then
                if filter != null or IsUnitAlly(forUnit, GetOwningPlayer(first)) then
                    set level = IMaxBJ(level, GetUnitAbilityLevel(first, auraID))
                endif
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    return level
endfunction

function GetAuraLevel takes unit forUnit, integer auraID, integer buffID, real aoe returns integer
    return GetAuraLevelEx(forUnit, auraID, buffID, aoe, null)
endfunction
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
I suppose allied/enemy would be the only thing here. Though a boolean wouldn't be enough, because maybe you want an aura to affect both allied and enemies? (wait, that wouldn't make sense though). So I could revise it:

JASS:
native UnitAlive takes unit id returns boolean

function GetAuraLevel takes unit forUnit, integer auraID, integer buffID, real aoe, boolean alliedAura returns integer
    local unit first
    local player owner = GetOwningPlayer(forUnit)
    local integer level = 0
    if GetUnitAbilityLevel(forUnit, buffID) > 0 then
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(forUnit), GetUnitY(forUnit), aoe, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first == null
            if UnitAlive(first) and IsUnitAlly(first, owner) == alliedAura then
                set level = IMaxBJ(level, GetUnitAbilityLevel(first, auraID))
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif
    set owner = null
    return level
endfunction
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
maybe you want an aura to affect both allied and enemies? (wait, that wouldn't make sense though)
no, that's why I explicitly filtered out enemy units in the first post, I dont see the difference of the last code you posted and the first post coz if alliedAura is true it will filter out enemy as well...well, except that 'owner' is faster than the first post...
 
Bribe already suggested the use of his version of the script in order to make this modular, because limiting it to allied doesn't make sense unless you change the name of the function to GetAlliedAuraLevel, because that's what it does.
It gets the aura level for allied units only.

GetAuraLevel doesn't mention the fact that you're filtering out enemy units.

Also, instead of bj_lastCreatedGroup, use your own global group to avoid bugs when GetAuraLevel is called inside a group enumeration block in which bj_lastCreatedGroup is used.
All systems should start using their own groups :/

Time left before graveyarding: 1 week
 
Well, bj_lastCreatedGroup isn't that advisable anyway. If someone uses GUI, chances are that bj_lastCreatedGroup will be destroyed. So this may bug out in such cases.

Yes, it is true that no GUIers bother to use these kinds of scripts, but it is better to just avoid such problems anyway. :)


See post below.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Using bj_lastCreatedGroup is still a terrible practice though.
It's not like we lack of memory available in jass, one group more or less doesn't matter, while a silly error could be done on bj_lastCreatedGroup, and then all resources using it are fucked.
Without mentionning the trouble described by Mag.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
Recursion is an ugly thing. I was naive for promoting the use of bj_lastCreatedGroup instead of a regular group for each resource. In fact, in some cases recursion can hit the same resource, also breaking a FirstOfGroup loop. Creation and destruction of a local group could be best in resources that might cause recursion.
 
Top