• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Trigger Stops Workinng After Unit Death

Status
Not open for further replies.
So I'm making a small code that creates a unit every X distance covered and ends once the maximum distance has been reached or passed. The problem I'm having is that the relevant variables are indexed to a unit, and if that unit dies while the system is at work, the system breaks entirely and cannot occur again. I've tried checking if the unit is still inside the unit group used for that system, tried preventing the trigger from disabling, etc. Nothing I can think of is working.

The system works fine normally, and is used during a building's construction. If I cancel construction or kill the building while the units are being created, the system dies.

JASS:
function CustomBuild_OnBuildStart takes nothing returns boolean

    local unit Source = null
    local unit dummy = null
    local unit Building = GetTriggerUnit()
    
    local integer dummy_id = 0
    local integer building_id = 0
    local integer source_id = 0
    
    local real SOURCE_X = 0.00
    local real SOURCE_Y = 0.00
    local real TARGET_X = 0.00
    local real TARGET_Y = 0.00
    local real dx = 0.00
    local real dy = 0.00
    local real Angle = 0.00
    local real Distance = 0.00
    
    if /* ---- List of Buildings that will trigger this function ----
    [Moon Well]     */GetUnitTypeId(Building) == 'ffmw' or /*
    [Barracks]      */GetUnitTypeId(Building) == 'ffba' or /*
    [Altar]         */GetUnitTypeId(Building) == 'ffal' /*
    */then
        
        set building_id = GetUnitUserData(Building)
        set dummy = GetStructureBuilder(Building) //This uses Banar's ConstructEvent library
        set dummy_id = GetUnitUserData(dummy)
        set Source = FF_BuildingSource[dummy_id]
        set source_id = GetUnitUserData(Source)
        
        set SOURCE_X = GetUnitX(Source)
        set SOURCE_Y = GetUnitY(Source)
        set TARGET_X = GetUnitX(Building)
        set TARGET_Y = GetUnitY(Building)
        
        set Angle = Atan2(TARGET_Y - SOURCE_Y, TARGET_X - SOURCE_X)
        
        set SOURCE_X = SOURCE_X + Cos(Angle) * (FF_MushroomRadius[source_id] + FF_MUSH_SEGMENT) //This is the radius of the mushroom ring off the Fairy Ring
        set SOURCE_Y = SOURCE_Y + Sin(Angle) * (FF_MushroomRadius[source_id] + FF_MUSH_SEGMENT)
        
        set dx = TARGET_X - SOURCE_X
        set dy = TARGET_Y - SOURCE_Y
        set Distance = SquareRoot(dy*dy + dx*dx)
        
        set FF_IsAFairyBuilding[building_id] = true
        set FF_BuildingSource[building_id] = Source
        
        if FF_AttachmentGroup[building_id] == null then
            set FF_AttachmentGroup[building_id] = CreateGroup()
        endif
        
        set FF_MushTrail_Time[building_id] = FF_BuildTime[building_id]
        set FF_MushTrail_Distance[building_id] = Distance
        set FF_MushTrail_Speed[building_id] = Distance / FF_BuildTime[building_id]
        set FF_MushTrail_Angle[building_id] = Angle
        set FF_MushTrail_X[building_id] = SOURCE_X
        set FF_MushTrail_Y[building_id] = SOURCE_Y
        set FF_MushTrail_Progress[building_id] = 0.0
        set FF_MushTrail_NextSegment[building_id] = FF_MUSH_SEGMENT
        call GroupAddUnit(FF_MushTrail_Group, Building)
        if not IsTriggerEnabled(gg_trg_MushroomTrail) then
            call EnableTrigger(gg_trg_MushroomTrail)
        endif
        
    endif
    
    return false
endfunction

//===========================================================================
function InitTrig_CustomBuild_OnBuildStart takes nothing returns nothing
    set gg_trg_CustomBuild_OnBuildStart = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CustomBuild_OnBuildStart, EVENT_PLAYER_UNIT_CONSTRUCT_START )
    call TriggerAddCondition( gg_trg_CustomBuild_OnBuildStart, function CustomBuild_OnBuildStart )
endfunction

JASS:
function Trig_CustomBuild_OnDeathOrCancel takes nothing returns boolean
    
    local unit Building = GetTriggerUnit()
    local unit u = null
    local integer building_id = GetUnitUserData(Building)
    local integer dummy_id = 0
    local integer PlayerNumber = GetPlayerId(GetOwningPlayer(Building)) + 1
    
    //Problem was occuring without this.
    if IsUnitInGroup(Building, FF_MushTrail_Group) then
        set FF_MushTrail_Distance[building_id] = FF_MushTrail_Progress[building_id]
        call BJDebugMsg("In Mush Trail")
        //call GroupAddUnit(FF_MushTrail_Group, u)
    endif
    
    /*set FF_MushTrail_Time[building_id] = 0.00
    set FF_MushTrail_Distance[building_id] = 0.00
    set FF_MushTrail_Speed[building_id] = 0.00
    set FF_MushTrail_Angle[building_id] = 0.00
    set FF_MushTrail_X[building_id] = 0.00
    set FF_MushTrail_Y[building_id] = 0.00
    set FF_MushTrail_Progress[building_id] = 0.0
    set FF_MushTrail_NextSegment[building_id] = 0.00*/
            
    if FF_IsAFairyBuilding[building_id] then
        
        loop
            set u = FirstOfGroup(FF_AttachmentGroup[building_id])
            exitwhen u == null
            call GroupRemoveUnit(FF_AttachmentGroup[building_id], u)
            
            if GetUnitTypeId(u) == 'ffm1' or /*
            */ GetUnitTypeId(u) == 'ffm2' or /*
            */ GetUnitTypeId(u) == 'ffm3' or /*
            */ GetUnitTypeId(u) == 'ffm4' or /*
            */ GetUnitTypeId(u) == 'ffm5' or /*
            */ GetUnitTypeId(u) == 'ffm6'    /*
            */ then
            
                set dummy_id = GetUnitUserData(u)
                //set udg_SizeChange_Size[id] = 0.00
                set udg_SizeChange_EndSize[dummy_id] = 0.00
                set udg_SizeChange_SizeRate[dummy_id] = 0.015625//udg_SizeChange_Size[dummy_id] / 64
                set udg_SizeChange_Shrink[dummy_id] = true
                set udg_SizeChange_DeathOnEnd[dummy_id] = true
                if not IsUnitInGroup(u, udg_SizeChange_Group) then
                    call GroupAddUnit(udg_SizeChange_Group, u)
                endif
                if not IsTriggerEnabled(gg_trg_SizeChange) then
                    call EnableTrigger(gg_trg_SizeChange)
                endif
                
            endif
            
        endloop
        
        //call DestroyGroup(FF_AttachmentGroup[building_id])
        
        if IsUnitInGroup(Building, udg_CustomBuildField_Group[PlayerNumber]) then
            call GroupRemoveUnit( udg_CustomBuildField_Group[PlayerNumber], Building )
        endif
        
        set FF_BuildingSource[building_id] = null
        set FF_BuildTime[building_id] = 0.00
        set FF_IsAFairyBuilding[building_id] = false
    endif
    
    return false
endfunction

//===========================================================================
function InitTrig_CustomBuild_OnDeathOrCancel takes nothing returns nothing
    set gg_trg_CustomBuild_OnDeathOrCancel = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CustomBuild_OnDeathOrCancel, EVENT_PLAYER_UNIT_DEATH )
    //call TriggerRegisterAnyUnitEventBJ( gg_trg_CustomBuild_OnDeathOrCancel, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL )
    call TriggerAddCondition( gg_trg_CustomBuild_OnDeathOrCancel, function Trig_CustomBuild_OnDeathOrCancel )
    
endfunction

JASS:
function Trig_MushroomTrail takes nothing returns boolean

    local unit u = null
    local unit dummy = null
    
    local integer id = 0
    local integer dummy_id = 0
    local integer RandomInt = 0
    
    local real x = 0.00
    local real y = 0.00
    local real RandomReal = 0.00
    
    if FF_MushTrail_Group == null then
        set FF_MushTrail_Group = CreateGroup()
    endif
    
    if FF_MushTrail_GroupTemp == null then
        set FF_MushTrail_GroupTemp = CreateGroup()
    endif
    
    if FirstOfGroup(FF_MushTrail_Group) != null then
        
        loop
            set u = FirstOfGroup(FF_MushTrail_Group)
            exitwhen u == null
            
            set id = GetUnitUserData(u)
            call GroupRemoveUnit(FF_MushTrail_Group, u)
            
            if not UnitAlive(FF_BuildingSource[id]) then
                call UnitApplyTimedLife(u, 'BTLF', 0.01)
            endif
            
            //if _Progress[id] is less than _Distance[id] and u is alive, continue increasing _Progress[id]
            //else reset all relevant variables to 0.00 and remove u from FF_MushTrail_Group
            if FF_MushTrail_Progress[id] < FF_MushTrail_Distance[id] and UnitAlive(u) then
                call GroupAddUnit(FF_MushTrail_GroupTemp, u)
                set FF_MushTrail_Progress[id] = FF_MushTrail_Progress[id] + FF_MushTrail_Speed[id] / 32 //since the periodic timer runs ever .03125 seconds, 32 represents 1 second.
                if FF_MushTrail_Progress[id] >= FF_MushTrail_NextSegment[id] then
                    
                    set RandomInt = GetRandomInt(1,6)
                    set dummy = CreateUnit(GetOwningPlayer(u), FF_Mush[RandomInt], FF_MushTrail_X[id], FF_MushTrail_Y[id], GetRandomReal(0.00, 360.00))
                    
                    call GroupAddUnit(FF_AttachmentGroup[id], dummy)
                    
                    if RandomInt == 6 then
                        set RandomReal = GetRandomReal(0.4, .55)
                    elseif RandomInt == 5 then
                        set RandomReal = GetRandomReal(.9, 1.15)
                    else 
                        set RandomReal = GetRandomReal(.45, .8)
                    endif
                    
                    // run SizeChange system. This doesn't conflict with this system, I've tried disabling it, but the problem persists.
                    set dummy_id = GetUnitUserData(dummy)
                    set udg_SizeChange_Size[dummy_id] = 0.00
                    set udg_SizeChange_EndSize[dummy_id] = RandomReal
                    set udg_SizeChange_SizeRate[dummy_id] = RandomReal / ((FF_MushTrail_Time[id] * .2) * 32)
                    call GroupAddUnit(udg_SizeChange_Group, dummy)
                    if not IsTriggerEnabled(gg_trg_SizeChange) then
                        call EnableTrigger(gg_trg_SizeChange)
                    endif
                    call PauseUnit(dummy, true)
                    // END Size Change system
                    
                    set FF_MushTrail_NextSegment[id] = FF_MushTrail_NextSegment[id] + FF_MUSH_SEGMENT
                    set FF_MushTrail_X[id] = FF_MushTrail_X[id] + Cos(FF_MushTrail_Angle[id]) * FF_MUSH_SEGMENT
                    set FF_MushTrail_Y[id] = FF_MushTrail_Y[id] + Sin(FF_MushTrail_Angle[id]) * FF_MUSH_SEGMENT
                    
                    set dummy = null
                    
                endif
                
            else
                call BJDebugMsg("Trail End")
                set FF_MushTrail_Time[id] = 0.00
                set FF_MushTrail_Distance[id] = 0.00
                set FF_MushTrail_Speed[id] = 0.00
                set FF_MushTrail_Angle[id] = 0.00
                set FF_MushTrail_X[id] = 0.00
                set FF_MushTrail_Y[id] = 0.00
                set FF_MushTrail_Progress[id] = 0.0
                set FF_MushTrail_NextSegment[id] = 0.00
            endif
        endloop
        
        loop
            set u = FirstOfGroup(FF_MushTrail_GroupTemp)
            exitwhen u == null
            call GroupRemoveUnit(FF_MushTrail_GroupTemp, u)
            call GroupAddUnit(FF_MushTrail_Group, u)
        endloop
    else
        call DisableTrigger(gg_trg_MushroomTrail)
    endif
    
    return false
endfunction

//===========================================================================
function InitTrig_MushroomTrail takes nothing returns nothing
    set gg_trg_MushroomTrail = CreateTrigger()
    call DisableTrigger(gg_trg_MushroomTrail)
    call TriggerRegisterTimerEvent( gg_trg_MushroomTrail, 0.03125, true )
    call TriggerAddCondition( gg_trg_MushroomTrail, function Trig_MushroomTrail )
endfunction
 
Last edited:
Level 13
Joined
Jan 2, 2016
Messages
978
Have you tried using a hashtable instead?
I haven't had problems with those.

EDIT:
You could save the unit's index inside the hashtable (instead of saving it as the unit's custom value). This way you woudln't need to edit your trigger too much.

Or you can just save everything into the hashtable, and stop using the "normal" indexing :p
 
Are hashtables slower than indexing? I read that somewhere...

Anyway, I tested the trigger to see whether the unit's index was not being recycled prematurely and indeed, everything is working fine. But for some reason, some damn reason I'm too dumb to see, the trigger just nopes and stops working altogetherm which it has no reason too.

One thing I noticed that may be of some help; notice this line: call BJDebugMsg("Trail End"). This is supposed to run when the trail ends normally, which is when FF_MushTrail_Progress[id] is greater or equal to FF_MushTrail_Distance[id]. It's calculated to coincide when the building finishes construction, and yknow what? The message shows up as it should. However, if the building dies while the trail is ongoing, this message never shows up. I tried forcing FF_MushTrail_Progress[id] to be greater than FF_MushTrail_Distance[id] on unit death, but that did nothing to fix the issue. It's very weird.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Yes, hashtables are slower, but it's not something you notice if you aren't having tons of triggers runing at the same time.
I can't tell you exactly how much slower hashtables are, but I'm using them for MOST of my triggers, and I have no problems.
 
Level 13
Joined
Jan 2, 2016
Messages
978
I did some research. Not sure if I understood things right, so I'll just post the link to it here..
and some quotes from it:
"I know how it looks, and resolving collisions in native, compiled code is a lot faster than you may think. Probably 3 orders of magnitude faster than single Jass function call. Dont forget his array is nice, but this is basically a competition between native function call + some native code vs a Jass function call, a bunch of Jass comparisions, Jass math operation, array index and then finally Jass return.

Imo thats a lot of operations needed to be done."

"Hashtables could potentially be slower, but the biggest problems with sized arrays are: limited instance count; indices can be any integer with hashtables."

"I guess you have to consider:
1. How large your sized array is.
2. How much data is stored within the hashtable.

But some stuff can't be squeezed easily and safely into arrays, like i.e. handle ids or type ids ( unit, destructable, items, ... ).

I guess that finally, like most times, we are talking about speed differences, which do not affect the
map and gameplay at all.

If I would put my money on sized arrays in case of a speed test.
I mean if you need an array size of 20000, you have to compare it with an hashtable with at least 10000 entries."

===============================================================================

"So Loading from a hashtable takes less than twice the time of loading from UnitUserData? Say what you want, but: This is amazing. I never knew hashtables are THAT fast. I always compared them to some kind of "speeded up gamecaches", but this is so wrong.

I'm not one of those speed junkies. I prefer easier code over speed, usually. Though I personally like Indexing systems better than hashtables (because they tend to be easier in use), I use hashtables anywhere else, where indexing doesnt help.

Hashtables are awesome, alone for the fact, that they are quire simple to use. And they are not THAT slow too. I don't understand why people are so addicted in finding ways to avoid hashtables just to save some nanoseconds of processing power while doubling the code's size. "
This one is from here
 
Level 13
Joined
Jan 2, 2016
Messages
978
I discovered that "Trail End" shows up when the Size Change trigger is disabled. However, the bug still occures, even when the Size Change is disabled.
I could've put more effort into it today if I didn't have an exam tomorrow.
Maybe tomorrow, or the day after that I'll have a solution xP
 
Level 7
Joined
Oct 19, 2015
Messages
286
Looking at it in more detail it seems you haven't posted all your code. Where are you adding units to the group and turning the trigger on?
 
Level 7
Joined
Oct 19, 2015
Messages
286
Can you use vJass? Because I think I know where the problem is and if I'm right then calling GroupRefresh before using FirstOfGroup would fix it, but I don't have time at the moment to convert GroupRefresh to normal JASS.
 
Level 7
Joined
Oct 19, 2015
Messages
286
The thread of GroupRefresh explains in more detail how and why it works. It's basically a problem that occurs when a unit is removed from the game while still in a group - if it was the first unit in that group, FirstOfGroup will return null.

When I read your description of the problem and saw that you were using FoG loops on persistent groups, I immediately suspected that this was the problem. However, on first scroll through the code, I didn't realize your group contained the buildings themselves, so my first suggestion was to remove the line that killed the units from the group. On closer inspection it turned out that that line didn't actually cause any problems because you wouldn't add the unit back into the group when you killed it.

Using GroupRefresh is just a quick and dirty fix. A FoG loop isn't the most suitable method for periodic loops, so ideally you would recode it to use a linked list-based approach. Since you're writing in regular JASS, though, you'd need to pretty much write a vJass struct by hand. If you used vJass, however, doing this would be super easy.
 
Status
Not open for further replies.
Top