• 🏆 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!

[vJASS] Linking a unit to a struct

Status
Not open for further replies.
Level 20
Joined
Jul 6, 2009
Messages
1,885
I'm wondering what are the ways of attaching a unit to a struct.
The spell i'm making has a struct that represents spell instance and has a member of unit type.
Now, the problem is i want to destroy the instance once the unit stops channeling the spell, however i don't know how would i, given the unit on event, know which instance to destroy.

I thought about using a hashtable to attach the struct instance as integer, but...i don't really want to use a hashtable if i'm already using a struct for spell instances (I don't like mixing hashtables with structs). So any other way except with a hashtable?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
Any form of map will do.
1. linear search (find instance with attached unit), slow but works.
2. Hashtable, I use this method as it can be quite neat.
3. A simplified hashtable using a bucket array and an algorthim of handle ID - null ID, fastest but limits number of handles on map at any time and will break with leaks.
4. Trigger value of unit, fast but can only store 1 value on a unit (could have collisions).
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Personally I like AutoIndex.

It's really easy to use.

JASS:
struct s_unit
    
    implement AutoCreate
    implement AutoDestroy
endstruct

Implementing the AutoCreate module will automatically create a struct s_unit for every unit, and implementing AutoDestroy will automatically destroy the struct when the unit has been removed from the game.

In order to reference the implementing s_unit struct, it's as simple as:

JASS:
local unit u = GetTriggerUnit()
local s_unit uData = s_unit[ u ]

This makes the two types (in my case, the s_unit type and unit type) in O(1) time, which is one of the fastest ways you can go about unit indexing. There are other systems that perform the same task, but I personally like AutoIndex.

It's neat because if you simply implement AutoData (though you have to manually setup the me member yourself) then you can associate a struct with a unit for an indefinite amount of time. When the association is no longer needed the struct can be destroyed. If multiple associations are made to the same unit, then instead of doing s_unit[ u ] you would have a 2D array syntax s_unit[ u ][ 0 ] to differentiate between the instances.

In general it's very well made, and very easy to implement.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Hmm...one more thing.
If i have an 'if' with multiple conditions and Condition1 has much higher chance to return false than Condition2 and Condition2 has higher chance than Condition3, is it better to do it like this
JASS:
if Condition1 then
    if Condition2 then
        if Condition3 then

        endif
    endif
endif
than putting all condition under 1 'if'? (Those checks are used within unit enumeration and units that pass all checks will, on next enum, fail first check so that's why the first condition will return false in most cases)
Especially considering the fact that Condition3 is a custom function that calls multiple functions.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I'm pretty sure that when JASS executes if-statements it evaluates them left-to-right.

JASS:
if IsUnitType(unit, UNIT_TYPE_FLYING) and IsUnitType(unit, UNIT_TYPE_ANCIENT) then

If the unit is of UNIT_TYPE_FLYING then the IsUnitType(unit, UNIT_TYPE_ANCIENT) part does not need to be evaluated and thus will not even be executed.

Yea so I can confirm this. If the if-statement no longer needs evaluation then it will not proceed to evaluate boolean values when unnecessary. I tried it with a simple script:

JASS:
function filterA takes nothing returns boolean
    call BJDebugMsg("filterA")
    return false
endfunction

function filterB takes nothing returns boolean
    call BJDebugMsg("filterB")
    return GetPlayerId(Player(5)) == 5
endfunction

function Init takes nothing returns nothing
    if filterB() or filterA() then
        // You do not really need to do anything here.
    endif
endfunction

Now if you have filterA or filterB then you'll notice once filterB evaluates to true the if-statement no longer needs to perform evaluations, so it stops. If you were to swap the positions of the two you would notice they are both executed, since the false evaluation could become true under the circumstances of the or operator.

This basically means:

JASS:
function CrashGame takes nothing returns boolean
    call Player(16)
    return false
endfunction

function onInit takes nothing returns nothing
    if (true) or (CrashGame()) then
        // The function "CrashGame()" will never be called.
    endif
endfunction
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
function CrashGame takes nothing returns boolean
call ExecuteFunc("CrashGame Plox")
endfunction

function onInit takes nothing returns nothing
if (true) or (CrashGame()) then
// The function "CrashGame()" will never be called.
endif
endfunction

That does not even compile due to the fact you forget to return a boolean in the one function. Please make correct example code.

call ExecuteFunc("CrashGame Plox")
Also that function will no longer crash the game as blizzard patched it.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
No just that part. It could still confuse someone if they try it to see and it does not work.

Luckilly other ways do exist to crash WC3. Like adding a player event to a trigger with the product of Player(16). Not too sure if its the Player(16) that crashes though or just the event hook.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Actually I'm pretty sure referencing any player above 15 will crash the game. There are other ways I could crash the game too. Initially I was going to create a timer that recursively calls a function that creates a timer, etc. That lags for a second then crashes.

Changed it from call ExecuteFunc() to call Player(16). Seems to do the trick. Also added the return value.

If you want to test it here:
JASS:
scope DEB initializer onInit
function CrashGame takes nothing returns boolean
    call Player(16)
    return false
endfunction

function onInit takes nothing returns nothing
    if (true) or (CrashGame) then
        // The function "CrashGame()" will never be called.
    endif
endfunction
endscope
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Actually I'm pretty sure referencing any player above 15 will crash the game. There are other ways I could crash the game too. Initially I was going to create a timer that recursively calls a function that creates a timer, etc. That lags for a second then crashes.

Changed it from call ExecuteFunc() to call Player(16). Seems to do the trick. Also added the return value.

If you want to test it here:
JASS:
scope DEB initializer onInit
function CrashGame takes nothing returns boolean
    call Player(16)
    return false
endfunction

function onInit takes nothing returns nothing
    if (true) or (CrashGame) then
        // The function "CrashGame()" will never be called.
    endif
endfunction
endscope
Wouldn't work either since it should be if (true) or (CrashGame()) then ;D (Brackets after CrashGame function)
Lol just kidding, it's true, though.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Perhaps I'm missing something, but shouldn't a coded channeled spell be on a periodic timer? If so, you're already looping through every struct index--just check if the channeling has ended as part of the code in that loop, and if so destroy the struct.

Also, AutoIndex presumably uses a hashtable.
 
Level 20
Joined
Jul 6, 2009
Messages
1,885
Perhaps I'm missing something, but shouldn't a coded channeled spell be on a periodic timer? If so, you're already looping through every struct index--just check if the channeling has ended as part of the code in that loop, and if so destroy the struct.

Also, AutoIndex presumably uses a hashtable.
Yeah, i'm kinda doing it that way.
Upon channel start, i add the triggering unit to a group and remove it upon end of channel. In the struct loop, i check whether the unit member is within the group, if it isn't, the instance is destroyed.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Yeah, i'm kinda doing it that way.
Upon channel start, i add the triggering unit to a group and remove it upon end of channel. In the struct loop, i check whether the unit member is within the group, if it isn't, the instance is destroyed.
Easier way: upon the start of channeling, create the struct and add it to the stack.

During the loop through the stack, check if the unit associated with each index has the appropriate order. If not, delete the index. Otherwise, continue as per usual.
 
Status
Not open for further replies.
Top