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

How to use a timer with a hash table?

Level 6
Joined
Aug 26, 2016
Messages
99
I’m trying to work with a hash table and timers and I can start it, but I can’t disable it.

JASS:
function Timer_Test takes nothing returns nothing
call DisplayTextToForce( GetPlayersAll(), "Ok))" )
endfunction


function Timer_On takes nothing returns nothing


    local timer t = GetExpiredTimer()
    local unit u = LoadUnitHandle(Hash, GetHandleId(t), 0)
    call FlushChildHashtable(Hash, GetHandleId(t))
    call PauseTimer(t)
    call DestroyTimer(t)
   
    set t = null
    set u = null
call DisplayTextToForce( GetPlayersAll(), "Off" )


endfunction
function Timer_Off takes nothing returns nothing

local timer t = CreateTimer()
    local unit u = GetTriggerUnit()
    call SaveUnitHandle(Hash, GetHandleId(t), 0, u)
    call TimerStart (t, 0.20, true, function Timer_Test)
    set t = null
    set u = null
call DisplayTextToForce( GetPlayersAll(), "On" )

endfunction





function Trig_Shoot_Conditions takes nothing returns boolean
 return BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT
endfunction




//===========================================================================
function InitTrig_Shoot takes nothing returns nothing
    local trigger On = CreateTrigger()
    local trigger Off = CreateTrigger()
    call TriggerRegisterPlayerMouseEventBJ( On, Player(0), bj_MOUSEEVENTTYPE_UP )
    call TriggerAddCondition( On, Condition( function Trig_Shoot_Conditions ) )
    call TriggerAddAction( On, function Timer_On )
    call TriggerRegisterPlayerMouseEventBJ( Off, Player(0), bj_MOUSEEVENTTYPE_DOWN )
    call TriggerAddCondition( Off, Condition( function Trig_Shoot_Conditions ) )
    call TriggerAddAction( Off, function Timer_Off )
    set On = null
    set Off = null
endfunction
endlibrary
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
So there's a lot going on here that doesn't make sense.

1) In Timer_Off, who exactly is the (Triggering unit) supposed to be?
vJASS:
local unit u = GetTriggerUnit()
Because your Event has nothing to do with Units. It's a Player Event based around Mouse input so there isn't going to be any GetTriggerUnit().

2) In Timer_On, you're using another Event Response that doesn't match your Event:
vJASS:
local timer t = GetExpiredTimer()
Again, your Event has nothing to do with Timers, it's a Player Event, so there isn't going to be any GetExpiredTimer().

I think you're misunderstanding how Events and Event Responses work. The only thing you can get from TriggerRegisterPlayerMouseEventBJ is the Player that fired the event and information about the Mouse like which button was pressed and it's current position.

There's some other issues but I want to figure out what your goal is before bringing them up.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
I need timer that I turned on to turn off.
That doesn't answer any of my questions.

I'm going to assume that each Player in your map has a single "shooter", most likely a Hero that the camera is locked to which can fire bullets when you right click your mouse.

The first thing you should do is keep track of this Hero using a Unit array:
vJASS:
globals
    unit array Player_Hero
endglobals

function RegisterPlayerHero takes unit u returns nothing
   local player p = GetOwningPlayer(u)
   local integer id = GetPlayerId(p)
   set Player_Hero[id] = u
   set p = null
endfunction

function Example takes nothing returns nothing
   // Register each player's hero
   local unit u = CreateUnit(...)
   call RegisterPlayerHero(u)
   set u = null
endfunction

Now we have access to a Player's hero at all times. The next step is to allow each Player to shoot from this Hero:

vJASS:
    // Shoot every 0.20 seconds
    function Timer_Test takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = LoadInteger(Hash, GetHandleId(t), 0)
 
        call DisplayTextToForce( GetPlayersAll(), "Shoot bullets pew pew!" )
        call KillUnit(Player_Hero[id]) // Change this to something useful

        set t = null
    endfunction

    // Start shooting process
    function Timer_On takes nothing returns nothing
        local timer t = CreateTimer()
        local integer id = GetPlayerId(GetTriggerPlayer())

        call SaveTimerHandle(Hash, id, 0, t)
        call SaveInteger(Hash, GetHandleId(t), 0, id)
        call TimerStart (t, 0.20, true, function Timer_Test)
        call DisplayTextToForce( GetPlayersAll(), "On" )

        set t = null
    endfunction

    // Stop shooting process
    function Timer_Off takes nothing returns nothing
        local integer id = GetPlayerId(GetTriggerPlayer())
        local timer t = LoadTimerHandle(Hash, id, 0)

        call FlushChildHashtable(Hash, id)
        call FlushChildHashtable(Hash, GetHandleId(t))
        call PauseTimer(t)
        call DestroyTimer(t)
        call DisplayTextToForce( GetPlayersAll(), "Off" )
 
        set t = null
    endfunction
 
    function Trig_Shoot_Conditions takes nothing returns boolean
        return BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT
    endfunction
 
    //===========================================================================
    function InitTrig_Shoot takes nothing returns nothing
        local trigger on = CreateTrigger()
        local trigger off = CreateTrigger()

        // When pressing right click, turn on the shoot timer
        call TriggerRegisterPlayerMouseEventBJ( on, Player(0), bj_MOUSEEVENTTYPE_DOWN )
        call TriggerAddCondition( on, Condition( function Trig_Shoot_Conditions ) )
        call TriggerAddAction( on, function Timer_On )

        // When releasing right click, turn off the shoot timer
        call TriggerRegisterPlayerMouseEventBJ( off, Player(0), bj_MOUSEEVENTTYPE_UP )
        call TriggerAddCondition( off, Condition( function Trig_Shoot_Conditions ) )
        call TriggerAddAction( off, function Timer_Off )

        set on = null
        set off = null
    endfunction
I didn't test the code but I'm pretty sure it's correct.

It may seem complicated but it's pretty simple. We're using an Array as well as saving data in a Hashtable to create a relationship:

1) The Player's Hero is linked to their Player Id. So if you have a Player, you'll be able to GET their Player Id, and as a result you will have access to their Hero.

2) The Timer is linked to the Player Id. So if you have a Player, you'll be able to GET their Player Id, and as a result you will also have access to their Timer.

3) The Timer's handle id is linked to it's owner's Player Id. So if you have a Timer, you'll be able to GET it's Handle Id, and as a result you will have access to the Player Id of whoever the Timer belongs to. This also gets you access to their Hero.

In other words:
Player gets us Timer and Hero
Timer gets us Player which then gets us Hero
 
Last edited:
Level 6
Joined
Aug 26, 2016
Messages
99
I have no idea how to periodically turn this function off and on
JASS:
 // Start shooting process
    function Timer_On takes nothing returns nothing
        local timer t = CreateTimer()
        local integer id = GetPlayerId(GetTriggerPlayer())

        call SaveTimerHandle(Hash, id, 0, t)
        call SaveInteger(Hash, GetHandleId(t), 0, id)
        call TimerStart (t, 0.20, true, function Timer_Test)
        call DisplayTextToForce( GetPlayersAll(), "On" )

        set t = null
    endfunction
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
Turn it off and on? You can't turn a function off or on. You choose when to call a function.

But you could control when it's called using conditions:
vJASS:
if SomeBoolean == true then
    call Timer_On()
endif

Or you could turn off the trigger associated with running the function:
vJASS:
globals
    On_Trigger = CreateTrigger()
endglobals

function Example takes nothing returns nothing
        call TriggerRegisterPlayerMouseEventBJ( On_Trigger, Player(0), bj_MOUSEEVENTTYPE_DOWN )
        call TriggerAddCondition( On_Trigger, Condition( function Trig_Shoot_Conditions ) )
        call TriggerAddAction( On_Trigger, function Timer_On )

        // Turn off the trigger
        call DisableTrigger( On_Trigger )
endfunction

Did you try the code I posted earlier? I tested it and it should do what you want.

Although, I'm still not entirely sure what you're trying to do since you don't want to tell me.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
Your code works, I'm making a shooter system and I need to know how to make shooting with a delay and not on every mouse click.
The code doesn't fire every mouse click. It fires once every 0.20 seconds:
vJASS:
  // Shoot every 0.20 seconds
    function Timer_Test takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = LoadInteger(Hash, GetHandleId(t), 0)
 
        call DisplayTextToForce( GetPlayersAll(), "Shoot bullets pew pew!" )
        call KillUnit(Player_Hero[id]) // Change this to something useful

        set t = null
    endfunction
This runs whenever the timer expires, which is once every 0.20 seconds. You can adjust the interval when starting the timer to change the rate of fire.

You can further control it by Destroying the timer and disabling the Timer_On trigger. Like maybe while reloading or when out of ammo.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
This should work for a single Gun per Player:
vJASS:
    globals
        hashtable Gun_Hash = InitHashtable()
        unit array Player_Hero
        boolean array Mouse_Is_Down
        boolean array Gun_Is_Ready
        timer array Gun_Timer
    endglobals

    function GunFinishCooldown takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = LoadInteger(Gun_Hash, GetHandleId(t), 0)

        set Gun_Is_Ready[id] = true
        call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " finished cooldown." )

        // Fire again if you're still holding down the mouse button
        if Mouse_Is_Down[id] then
            call GunFireBullet(id)
        endif
 
        set t = null
    endfunction

    function GunStartCooldown takes integer id returns nothing
        set Gun_Is_Ready[id] = false
        call TimerStart(Gun_Timer[id], 0.20, false, function GunFinishCooldown)
        call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " started cooldown." )
    endfunction

    function GunFireBullet takes integer id returns nothing
        // Fire the gun if it's ready
        if Gun_Is_Ready[id] then
            call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " fired gun." )
            // Put code for firing bullets here
            call GunStartCooldown(id)
        endif
    endfunction

    function MouseIsDown takes nothing returns nothing
        local integer id = GetPlayerId(GetTriggerPlayer())
        set Mouse_Is_Down[id] = true
 
        // Try to to fire a bullet
        call GunFireBullet(id)
    endfunction

    function MouseIsUp takes nothing returns nothing
        set Mouse_Is_Down[GetPlayerId(GetTriggerPlayer())] = false
    endfunction
 
    function MouseConditions takes nothing returns boolean
        return BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT
    endfunction
 
    function GunSetup takes nothing returns nothing
        local trigger on = CreateTrigger()
        local trigger off = CreateTrigger()
        local integer id = 0 // player id

        // Create and save the player's gun timer
        set Gun_Timer[id] = CreateTimer()
        call SaveTimerHandle(Gun_Hash, id, 0, Gun_Timer[id])
        call SaveInteger(Gun_Hash, GetHandleId(Gun_Timer[id]), 0, id)

        // Track when right click is pressed and try to fire the gun (if ready)
        call TriggerRegisterPlayerMouseEventBJ( on, Player(id), bj_MOUSEEVENTTYPE_DOWN )
        call TriggerAddCondition( on, Condition( function MouseConditions ) )
        call TriggerAddAction( on, function MouseIsDown )

        // Track when right click is released
        call TriggerRegisterPlayerMouseEventBJ( off, Player(id), bj_MOUSEEVENTTYPE_UP )
        call TriggerAddCondition( off, Condition( function MouseConditions ) )
        call TriggerAddAction( off, function MouseIsUp )

        set on = null
        set off = null
    endfunction
You'll have to expand it so that Gun_Is_Ready and Gun_Timer exist for each type of gun. Storing this data in a Hashtable would be smart so that you can use less variables and reuse a lot of this code. Or if your map is only singleplayer then you could make the [index] of these Arrays act as the Gun Type. [1] = Pistol, [2] = Shotgun, [3] = Machine Gun, etc.

This is just my quick and easy approach. A more advanced and better design is definitely out there, Structs come to mind (or using Lua).
 
Last edited:
Level 6
Joined
Aug 26, 2016
Messages
99
there's a mistake somewhere
 

Attachments

  • 12.jpg
    12.jpg
    427.1 KB · Views: 4

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
there's a mistake somewhere
Try moving the GunFireBullet() function to the top of the list of functions:
vJASS:
    endglobals

    function GunFireBullet takes integer id returns nothing
        // Fire the gun if it's ready
        if Gun_Is_Ready[id] then
            call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " fired gun." )
            // Put code for firing bullets here
            call GunStartCooldown(id)
        endif
    endfunction

    function GunFinishCooldown takes nothing returns nothing
Jass can be picky about where you place things.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
error again
Jass is terrible, here's how I got it working:
vJASS:
globals
    hashtable Gun_Hash = InitHashtable()
    unit array Player_Hero
    boolean array Mouse_Is_Down
    boolean array Gun_Is_Ready
    timer array Gun_Timer
endglobals

function GunLaunchBullet takes integer id returns nothing
    // Put code for firing bullets here
    call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " is firing a bullet." )
endfunction

function GunFinishCooldown takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = LoadInteger(Gun_Hash, GetHandleId(t), 0)

    set Gun_Is_Ready[id] = true
    call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " finished cooldown." )

    // Fire again if you're still holding down the mouse button
    if Mouse_Is_Down[id] then
        // Start the cooldown again
        set Gun_Is_Ready[id] = false
        call TimerStart(Gun_Timer[id], 0.20, false, function GunFinishCooldown)
        call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " started cooldown." )
        // Fire an actual bullet
        call GunLaunchBullet(id)
    else
        set Gun_Is_Ready[id] = true
    endif

    set t = null
endfunction

function GunStartCooldown takes integer id returns nothing
    set Gun_Is_Ready[id] = false
    call TimerStart(Gun_Timer[id], 0.20, false, function GunFinishCooldown)
    call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " started cooldown." )
endfunction

function GunTryToFire takes integer id returns nothing
    call DisplayTextToForce( GetPlayersAll(), "Player " + I2S(id) + " is trying to fire." )
    // Fire the gun if it's ready
    if Gun_Is_Ready[id] then
        // Fire an actual bullet
        call GunLaunchBullet(id)
        // Start the cooldown for when you can fire again
        call GunStartCooldown(id)
    endif
endfunction

function MouseIsDown takes nothing returns nothing
    local integer id = GetPlayerId(GetTriggerPlayer())
    set Mouse_Is_Down[id] = true

    // Try to to fire a bullet
    call GunTryToFire(id)
endfunction

function MouseIsUp takes nothing returns nothing
    set Mouse_Is_Down[GetPlayerId(GetTriggerPlayer())] = false
endfunction

function MouseConditions takes nothing returns boolean
    return BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT
endfunction

function GunSetup takes nothing returns nothing
    local trigger on = CreateTrigger()
    local trigger off = CreateTrigger()
    local integer id = 0 // player id

    // Create and save the player's gun timer
    set Gun_Timer[id] = CreateTimer()
    call SaveTimerHandle(Gun_Hash, id, 0, Gun_Timer[id])
    call SaveInteger(Gun_Hash, GetHandleId(Gun_Timer[id]), 0, id)

    // Track when right click is pressed and try to fire the gun (if ready)
    call TriggerRegisterPlayerMouseEventBJ( on, Player(id), bj_MOUSEEVENTTYPE_DOWN )
    call TriggerAddCondition( on, Condition( function MouseConditions ) )
    call TriggerAddAction( on, function MouseIsDown )

    // Track when right click is released
    call TriggerRegisterPlayerMouseEventBJ( off, Player(id), bj_MOUSEEVENTTYPE_UP )
    call TriggerAddCondition( off, Condition( function MouseConditions ) )
    call TriggerAddAction( off, function MouseIsUp )

    set on = null
    set off = null
endfunction
 
Level 6
Joined
Aug 26, 2016
Messages
99
It's strange but it works.This is an unfixed option.


JASS:
function ColdownEnd takes nothing returns nothing
local integer id = GetPlayerId(GetTriggerPlayer())
local boolean CS = true
call SaveBoolean(Hash,id,0,CS)

call DisplayTextToForce( GetPlayersAll(), "End" )




endfunction




function ColdownTimer takes nothing returns nothing
local boolean CS = false
local integer id = GetPlayerId(GetTriggerPlayer())
local integer wp = GetPlayerId(GetTriggerPlayer())
local integer end = GetPlayerId(GetTriggerPlayer())
local timer tt = LoadTimerHandle(Hash,wp, 0)
local timer tend = CreateTimer()
call SaveTimerHandle(Hash, end, 0, tend)
call SaveBoolean(Hash,id,0,CS)
call FlushChildHashtable(Hash, wp)
call PauseTimer(tt)
call DestroyTimer(tt)


call TimerStart (tend, 0.80, false, function ColdownEnd)

set tt = null
set tend = null
endfunction

function ColdownStart takes nothing returns nothing
local integer id = GetPlayerId(GetTriggerPlayer())
local boolean CS = LoadBoolean(Hash,id,0)
call SaveBoolean(Hash,id,0,CS)
if CS == true then
call DisplayTextToForce( GetPlayersAll(), "True" )
call Trig_Shoot2M249_Actions()

call ColdownTimer()
endif


endfunction
 

Attachments

  • Warcraft III Reforged 2023.12.31 - 12.21.22.01.mp4
    92.6 MB
Last edited:
Top