• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[JASS] Music Fade - Timers & Hashtable

Status
Not open for further replies.
Level 16
Joined
Mar 27, 2011
Messages
1,349
I am creating a music fade out system using a periodic timer and a hashtable. Since timers and hashtables in jass are still new to me, I'm using this as reference Changing waits to Timers in Spells. (though I'm not using vjass).

udg_TimerHash is defined in the variable editor as type Hashtable.
udg_MusicVolume is defined in the variable editor as type Integer initialized with value 80.

The values in MusicLocalPlayWF function report null or 0 values. Can anyone see why null or empty values are being loaded from my Hashtable?

JASS:
function MusicLocalPlay takes string musicPath , player p returns nothing

    if GetLocalPlayer() == p then
   call PlayMusic(musicPath)
    endif

endfunction

function MusicLocalPlayWFLoop takes nothing returns nothing

    local timer t = GetExpiredTimer()
    local player p = LoadPlayerHandle(udg_TimerHash, GetHandleId(t), 1)
    local integer musicVolume = LoadInteger(udg_TimerHash, GetHandleId(t), 2)
     
    call BJDebugMsg("musicVolume = " + I2S(LoadInteger(udg_TimerHash, GetHandleId(t), 2)))
    call BJDebugMsg("musicPath = " + (LoadStr(udg_TimerHash, GetHandleId(t), 3)))

    if musicVolume <= 0 then
   call DestroyTimer(t)
   call MusicLocalPlay(LoadStr(udg_TimerHash, GetHandleId(t), 3), p)
    else
   if GetLocalPlayer() == p then
       call SetMusicVolume(musicVolume)
        endif
   call BJDebugMsg("musicVolume is greater than 0")
   set musicVolume = musicVolume - 5
   call SaveInteger(udg_TimerHash, GetHandleId(t), 2, musicVolume)
    endif

    //set player = null

endfunction

function MusicLocalPlayWF takes string musicPath , player p returns nothing


    local integer musicVolume = udg_MusicVolume
    local timer t = CreateTimer()

    call SavePlayerHandle(udg_TimerHash, GetHandleId(t), 1, p)
    call SaveInteger(udg_TimerHash, GetHandleId(t), 2, musicVolume)
    call SaveStr(udg_TimerHash, GetHandleId(t), 3, musicPath)

    call BJDebugMsg("Saving musicInteger = " + I2S(LoadInteger(udg_TimerHash, GetHandleId(t), 2)))
    call BJDebugMsg("Saving musicPath = " + LoadStr(udg_TimerHash, GetHandleId(t), 3))

    call TimerStart( t, 0.1, true, function MusicLocalPlayWFLoop )

endfunction

//===========================================================================
function InitTrig_MusicSystem takes nothing returns nothing
endfunction

On a side note, do I add the below to MusicLocalPlayWFLoop to prevent memory leaks?

JASS:
FlushChildHashtable(udg_TimerHash, GetHandleId(t))

I can imagine the data would otherwise accumulate in my TimerHash if I don't start clearing out the stuff?
 
Level 16
Joined
Mar 27, 2011
Messages
1,349
Ahh it works! Thanks!

If not, paste this line inside your InitTrig function:

JASS:
function InitTrig_MusicSystem takes nothing returns nothing
    set udg_TimerHash = InitHashtable()
endfunction

I'm guessing this is my InitTrig. Why are these needed and does adding stuff inside it perform actions on map load or is it just like any function which can be called?

As for the FlushChildHashtable, only call it once you see fit to clear out the data.

I added it here. The system still seems to work properly. Am I right to clear this data? The system I referenced from doesn't appear to clear out his Hashtable. Wouldn't his leak? Here: Changing waits to Timers in Spells.

JASS:
function MusicLocalPlayWFLoop takes nothing returns nothing

    local timer t = GetExpiredTimer()
    local player p = LoadPlayerHandle(udg_TimerHash, GetHandleId(t), 1)
    local integer musicVolume = LoadInteger(udg_TimerHash, GetHandleId(t), 2)
  
    call BJDebugMsg("musicVolume = " + I2S(LoadInteger(udg_TimerHash, GetHandleId(t), 2)))
    call BJDebugMsg("musicPath = " + (LoadStr(udg_TimerHash, GetHandleId(t), 3)))

    if musicVolume <= 0 then
   call DestroyTimer(t)
   call MusicLocalPlay(LoadStr(udg_TimerHash, GetHandleId(t), 3), p)
   call FlushChildHashtable(udg_TimerHash, GetHandleId(t))
    else
   if GetLocalPlayer() == p then
       call SetMusicVolume(musicVolume)
        endif
   call BJDebugMsg("musicVolume is greater than 0")
   set musicVolume = musicVolume - 5
   call SaveInteger(udg_TimerHash, GetHandleId(t), 2, musicVolume)
    endif

    set p = null
    set t = null

endfunction
 
Level 39
Joined
Feb 27, 2007
Messages
5,034
What WE version are you using? I see no vJASS objects or syntax, but if youre on 1.30 or using 1.29 with WEX you could use libraries with initializers instead of relying on InitTrigs. Food for thought: there are some basic code-management/encapsulation bits vJASS added that are very simple to understand I think you would benefit from using as you are learning how to program in straight JASS without data structures. Libraries, scopes, globals blocks, 2D arrays, and delimited comments.

InitTrig functions were initially used to add the events, conditions, and actions to your trigger. This is because the global trigger variables don't have a value until set to CreateTrigger() and thus they can only have TriggerAddXXX() called on them afree at least run-time. Make any GUI trigger, convert it, and you'll see that's what its InitTrig does. They are all automatically called once as part of the Map Initialization thread.

Their name is always InitTrig_Name_of_Trigger, and if you change that the game will throw an error at you. If you're using WEX (or I believe vanilla 1.30) you can delete them without worry if you don't need them. You can call them like any other function.
I added it here. The system still seems to work properly. Am I right to clear this data?
Yes, because you only call Flush() once when it's time to destroy your time and stop the fade. If you were to clear it prematurely the next time the timer runs it would load a null player into p and a 0 into musicVolume.

Instead of putting ifs inside of elses, you can use multiple elseifs:
JASS:
//Usually do this:
if conditionA then
  call doA()
elseif conditionB then
  call doB()
elseif conditionC then
  call doC()
else
  call BJDebugMsg("A B and C were all false!")
endif

//Instead of this:
if conditionA then
  call doA()
else
  if conditionB then
    call doB()
  else
    if conditionC then
      call doC()
    else
      call BJDebugMsg("A B and C were all false!")
    endif
  endif
endif
 
Level 16
Joined
Mar 27, 2011
Messages
1,349
Thanks for your replies everyone. You're all the best!

What WE version are you using? I see no vJASS objects or syntax, but if youre on 1.30 or using 1.29 with WEX

lol, neither. I just code in the normal World editor. I'll confess, it's a bit harder than using an actual IDE! Though I'm not sure about using mods that add unsupported methods? I don't want any new Warcraft version to break my map or prevent me from upgrading. I remember seeing Blizzard hint at some Jass editing tools to come.
 
I'm using 1.30. I haven't looked into vJass/don't know what it is.
Have a look under topic "vJASS", Elemental Coder Guide.

Like pyrogasm fully explaned, then you're useable to use vjass and its init techniques, since patch 1.30 includes vJASS support natively (but sadly no syntax highleighting).

Here's an overview what it allows: vJASS Manual

.. an example to make a function that automatically runs at map initialization would be:
JASS:
library MyLibrary1 initializer Init
 
    function MusicPlayWF takes string musicPath , player p returns nothing
    endfunction

    private function Init takes nothing returns nothing
        // runs itself
    endfunction
endlibrary
^That would also mean, btw, that function MusicPlayWF can be called just normally through other code, because libraries are meant to be placed above normal triggers.
 
Status
Not open for further replies.
Top