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

Attaching Timers to IndexedUnits

Status
Not open for further replies.
Level 22
Joined
Sep 24, 2005
Messages
4,821
I'm assuming you're using a unit indexing system. If that system has a function to get a unit's index then use that index to attach a timer:

JASS:
set Timer[GetUnitIndex(GetTriggerUnit())]=CreateTimer()
// ...
call StartTimer(..)
//...
// on End cast triggeraction
call PauseTimer(Timer[GetUnitIndex(GetTriggerUnit())])
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
  • Harvest Resource Start
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
    • Actions
      • Countdown Timer - Start UDexTimer[(Custom value of (Triggering unit))] as a One-shot timer that will expire in 10.00 seconds
      • Game - Display to (All players) the text: Timer Started!
  • Harvest Resource End
    • Events
      • Unit - A unit Stops casting an ability
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String((Elapsed time for UDexTimer[(Custom value of (Triggering unit))])))
      • Countdown Timer - Pause UDexTimer[(Custom value of (Triggering unit))]
Doesn't work? Only displays 0.00 and I'm 100% sure the units are in the index.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
So I can have 8119 (or w/e the number was) timers? Glad to hear it!

That would be 8191 instances, and I'm not sure about what you're saying... You can only attach a single timer per unit if you use the indexes directly using a single timer array. If you're using the index for multiple spells which only need a single timer then it will work if you declare a new timer array per spell.

Using a single timer array to attach multiple timers on a unit should requires some simple hashing function.

@Post above:
You should create a timer first, then on the end cast, destroy the timer you used to recycle memory. Don't forget to null the timer variable on the on end cast trigger.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
JASS:
function ReturnHarvest takes nothing returns nothing
    // HARVEST COMPLETED 
    call PauseTimer(udg_UDexTimer[GetUnitUserData(GetTriggerUnit())])
    call DestroyTimer(udg_UDexTimer[GetUnitUserData(GetTriggerUnit())])
    set udg_UDexTimer[GetUnitUserData(GetTriggerUnit())] = null
    call DisplayTextToForce( GetPlayersAll(), "Timer Was Destroyed!" )
endfunction

function Trig_Harvest_Resource_Start_Actions takes nothing returns nothing
    local integer u_id = GetUnitUserData(GetTriggerUnit())
    call DisplayTextToForce( GetPlayersAll(), "INDEX" + I2S(u_id) )


    // Start Harvesting Command
    if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) then

        if (TimerGetElapsed(udg_UDexTimer[u_id]) == 0.00) then // IF NO PREVIOUS HARVEST START HARVEST
            set udg_UDexTimer[u_id] = CreateTimer()
            call TimerStart(udg_UDexTimer[u_id], 10, false, function ReturnHarvest)
            call DisplayTextToForce( GetPlayersAll(), "timer started at: " + R2S(TimerGetElapsed(udg_UDexTimer[u_id])) )

        else // RESUME PREVIOUS HARVEST
            call ResumeTimer(udg_UDexTimer[u_id])
            call DisplayTextToForce( GetPlayersAll(), "timer resumed at: " + R2S(TimerGetElapsed(udg_UDexTimer[u_id])) ) 
        endif

    // Stop Harvesting Command
    elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST) then
        call PauseTimer(udg_UDexTimer[u_id])
        call DisplayTextToForce( GetPlayersAll(), "timer paused at: " + R2S(TimerGetElapsed(udg_UDexTimer[u_id])) ) 
    endif
endfunction

//===========================================================================
function InitTrig_Harvest takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST ) 
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddAction( t, function Trig_Harvest_Resource_Start_Actions )
    set t = null
endfunction

Currently it displays the following:
"timer started at: 0.000" OK
"timer paused at: 1.733" OK
"timer resumed at: 0.000" WTF
"timer paused at: 3.773" I GUESS OK?
"Timer was destroyed!" OK WHEN FINISHED
"Timer paused at 0.688" WTF AFTER IT FINISHED

I want to do the following:
IF EVENT: EVENT_PLAYER_UNIT_SPELL_CAST then
Check for existing timer. If no existing timer Create one, otherwise resume existing timer.
ELSEIF EVENT: EVENT_PLAYER_UNIT_SPELL_ENDCAST
Pause timer.

When timer reaches 0 destroy it.
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
@ReturnHarvest:
GetTriggerUnit() will not work, use a unit array to store the units and you also need to attach the unit to the timer. This means that GetUnitUserData always returns 0, so that must be the cause of the error.

Also, could you explain in detail what your spell is supposed to do, I think you're doing it wrong. Thanks.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
@ReturnHarvest:
GetTriggerUnit() will not work, use a unit array to store the units and you also need to attach the unit to the timer. This means that GetUnitUserData always returns 0, so that must be the cause of the error.

Also, could you explain in detail what your spell is supposed to do, I think you're doing it wrong. Thanks.

Well, its a worker ability to harvest a resource (drain life from unit).

If the timer is 0 it has not harvested anything and the timer should start.
If there is already a timer that is paused just resume it.
if it stops harvesting but haven't reached end of timer pause timer.
When timer ends display message "Harvest completed"

This always return the correct number:
JASS:
local integer u_id = GetUnitUserData(GetTriggerUnit())
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Ahaha XD, I'm sorry but I'm not good at understanding things... Could you provide the testmap you are using?
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Here you go: http://www.epicwar.com/maps/245155/ Use the ability "Harvest Resource" on obvious resource with obvious unit. Trigger Category Resources

Added comments to above code, perhaps u can better understand how I mean now.

** IF EVENT_CAST_START then
**** IF TIMER == NULL then
****** CREATE & START TIMER
**** ELSE
****** RESUME TIMER
**** ENDIF
** ELSEIF EVENT_CAST_STOP
**** PAUSE TIMER
** ENDIF

** IF TIME REMAINING == 0 then
**** DISPLAY MSG "HARVEST COMPLETED"
**** CHANGE COLOR OF UNIT (HARVESTER)
** ENDIF

The timer is only destroyed when a) the unit dies or b) the unit returns the resources.
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
I just saw the map, you did indeed needed to attach the unit to the timer. I added a hashtable and used it to attach the timer to the unit. Also, I think the TimerGetElapsed function is bugged, it returns the wrong value after a pause, so I changed it to TimerGetRemaining.

I also didn't didn't find those conditions (a and b conditions) on script, so you still need to put them there.

Anyway here is the script I edited:
JASS:
function ReturnHarvest takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local unit u=LoadUnitHandle(udg_GameHashTable,GetHandleId(t),0)
    
    call PauseTimer(t)
    call DestroyTimer(t)
    
    call FlushChildHashtable(udg_GameHashTable,GetHandleId(t))
    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Timer destroyed.")
    
    if (u==null) then
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"No unit retrieved.")
    else
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,GetUnitName(u)+" @ index "+I2S(GetUnitUserData(u)))
        call SetUnitColor(u,PLAYER_COLOR_BLUE)
    endif
    
    set t=null
    set u=null
    set udg_UDexTimer[GetUnitUserData(u)]=null
endfunction

function Trig_Harvest_Resource_Start_Actions takes nothing returns nothing
    local integer u_id = GetUnitUserData(GetTriggerUnit())
    call DisplayTextToForce( GetPlayersAll(), "INDEX" + I2S(u_id) )

    if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) then

        if (TimerGetElapsed(udg_UDexTimer[u_id]) == 0.00) then
            set udg_UDexTimer[u_id] = CreateTimer()
            call SaveUnitHandle(udg_GameHashTable,GetHandleId(udg_UDexTimer[u_id]),0,GetTriggerUnit())// attach the unit to the timer
            call TimerStart(udg_UDexTimer[u_id], 10, false, function ReturnHarvest)
            call DisplayTextToForce( GetPlayersAll(), "timer started at: " + R2S(TimerGetRemaining(udg_UDexTimer[u_id])) )
        else
            call ResumeTimer(udg_UDexTimer[u_id])
            call DisplayTextToForce( GetPlayersAll(), "timer resumed at: " + R2S(TimerGetRemaining(udg_UDexTimer[u_id])) ) 
        endif
        
    elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST) then
        call PauseTimer(udg_UDexTimer[u_id])
        call DisplayTextToForce( GetPlayersAll(), "timer paused at: " + R2S(TimerGetRemaining(udg_UDexTimer[u_id])) ) 
    endif
    
endfunction

//===========================================================================
function InitTrig_Harvest takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST ) 
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddAction( t, function Trig_Harvest_Resource_Start_Actions )
    set t = null
    
    set udg_GameHashTable=InitHashtable()
endfunction

I've attached the testmap too.
 

Attachments

  • TownConstruction 0.02.w3x
    391.7 KB · Views: 43
Level 15
Joined
Nov 30, 2007
Messages
1,202
Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You! Thank You!
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Use call DisplayTextToPlayer(GetLocalPlayer(), (...) ) instead of call DisplayTextToForce( GetPlayersAll(), (...) ).

And btw, apply execution code via conditions rather than actions.

Of course, those are just suggestion which are not really required if you don't want to.

I was just noticing I had overlooked that and the messages are debugs but gonna keep that in mind.

I decided to use damage detection in this case though, perhaps wiser then attaching timers to a unknown amount of workers.
 
Last edited:
Status
Not open for further replies.
Top