Modding Help Method to get random String value

Discussion in 'Starbound Modding' started by odkupiciel375, Feb 26, 2017.

  1. odkupiciel375

    odkupiciel375 Starship Captain

    I've just returned to doing some modding stuff and i wanted to make my jukebox to play "random" songs but the problem is that "soundeffect" accepts only strings and pool returns object
    Here is what i thought may work
    Code:
    "soundEffect" : {"pool" : [
            {"weight" : 0.34, "string" : "/objects/fallout/fjukebox/FiveStarsAtomBombBaby.ogg"},
            {"weight" : 0.33, "string" : "/objects/fallout/fjukebox/enclaveradioamericathebeautiful.ogg"},
            {"weight" : 0.33, "string" : "/objects/fallout/fjukebox/EltonBrittUraniumFever.ogg"}
          ]},
    
    and i've tried this too (nope... crash)
    Code:
    "soundEffect" : ["/objects/fallout/fjukebox/FiveStarsAtomBombBaby.ogg","/objects/fallout/fjukebox/EltonBrittUraniumFever.ogg","/objects/fallout/fjukebox/enclaveradioamericathebeautiful.ogg"],
    
    My question is:
    Is there any way to return "random" String or force pool to return as String rather than Object (or just a one property of that object)?
     
  2. The | Suit

    The | Suit Agent S. Forum Moderator

    Don't use the soundEffect parameter - and rewrite the entire Music play code in Lua.
    You will also likely need to add an animation file to do the music rotations.
     
    odkupiciel375 likes this.
  3. bk3k

    bk3k Oxygen Tank

    I have my own weighted random function that returns strings... At home. I'm on mobile rather than at home.

    I believe this is exactly what you have in mind, ready to use.
     
  4. odkupiciel375

    odkupiciel375 Starship Captain

    Whats the problem with soundeffect besides that i'm currently having czu vanilla assets are using it (standard record player)
    but ok ill try lua if i'll find time
    EDIT: it has animations file as i'm using that:
    Code:
    "properties" : {
                  "particleEmittersOn" : [ "music" ]
    
    that's exactly what i need - a function to return String type value from an array or object etc
     
  5. odkupiciel375

    odkupiciel375 Starship Captain

    i've got it almost done...
    here is lua code (ive got rid of that soundeffect and made some use of that animation file that i had)
    Code:
    function init(args)
      if storage.state == nil then storage.state = config.getParameter("defaultfjukeboxState", false) end
      object.setInteractive(true)
      switchState(storage.state)
      storage.state = not storage.state;
    end
    
    function onInteraction(args)
            switchState(storage.state)
            storage.state = not storage.state;
    end
    
    function switchState(currState)
        if currState then
            if animator.hasSound("start") then
                animator.playSound("start")
            end
            animator.setAnimationState("fjukeboxState","on")
            if animator.hasSound("on") then
                animator.playSound("on")
            end
        else
            if animator.hasSound("on") then
                animator.stopAllSounds("on")
            end
            animator.setAnimationState("fjukeboxState","off")
            if animator.hasSound("off") then
                animator.playSound("off")
            end
        end
    end
    
    but now the problem is that when i turn it ON and then destroy it then that sound wont stop and it ll play even when ill quit to main menu... it seems like i need some sort of a destroy event listener that would stop all sounds... something like this
    Code:
    function onDestroy(args)
       if animmator.hasSound("name") then
          animator.stopAllSounds("name")
       end...
    end
    
     
  6. The | Suit

    The | Suit Agent S. Forum Moderator

    You can use function uninit()

    Code:
    function uninit()
      animator.stopAllSounds("on")
    end
    
    Uninit is called when the object is unloaded from memory \ removed.
    That means - it happens if you walk to some place else - and no longer "loaded" uninit is also called right before unloaded from memory.

    There used to be function die()
    When broken, not sure if it still exists.
    die used to be called only when object is physically removed and or destroyed.

    You can see an example here,
    https://github.com/xxswatelitexx/Starbound/tree/master/mad_Exp/data_storage
     
    Last edited: Feb 26, 2017
    Cyel likes this.
  7. bk3k

    bk3k Oxygen Tank

    Still not home yet. Anyhow you could probably have a simple animation file that switches songs by switching tags as in <currentSong> etc. Just swap the tag with the "/path/song.ogg" you want.
     
    The | Suit likes this.
  8. The | Suit

    The | Suit Agent S. Forum Moderator

    That is true - but animators give you a lot more control
     
  9. odkupiciel375

    odkupiciel375 Starship Captain

    i've added
    Code:
    function die()
        ifHasSoundPlayIt("off")
        ifHasSoundStopIt("on")
    end
    
    and it works but only for physical breaking object (with matter manipulator) but when i quit to main menu the song will keep playing
    i've tried also uninit but it didn't work at all (neither for physical breaking and for quitting to main menu)

    Code:
    function uninit()
        ifHasSoundPlayIt("off") //this function just check if there is such sound and just play it <i didn had those comments in the code while testing>
        ifHasSoundStopIt("on") //this function just check if there is such sound and just stops it <i didn had those comments in the code while testing>
    end
    
     
  10. The | Suit

    The | Suit Agent S. Forum Moderator

    You don't need to have a function to check if it is playing
    on uninit just use stop all sounds.

    Code:
    #### `void` animator.stopAllSounds(`String` soundName)
    
    Keep in mind if it is on the ship - it is always initialized.
    So you will want to - create a special code for the ship.
     
  11. odkupiciel375

    odkupiciel375 Starship Captain

    just to clear things out i know that i don't need those functions but since i'm using that "check if there is something like this than use it that way" and it was very messy i decided to create dedicated function that would play or stop with that "if" implemented
    Code:
    function init(args)
        if storage.state == nil then storage.state = config.getParameter("defaultfjukeboxState", false) end
        object.setInteractive(true)
        switchState(storage.state)
        storage.state = not storage.state;
    end
    
    function die()
        ifHasSoundPlayIt("off")
        ifHasSoundStopIt("on")
    end
    
    function onInteraction(args)
        switchState(storage.state)
        storage.state = not storage.state;
    end
    
    function switchState(currState)
        if currState then
            ifHasSoundPlayIt("start")           //without that function it would look like a total mess and it helps me while reading this "code"
            animator.setAnimationState("fjukeboxState","on")
            ifHasSoundPlayIt("on")
        else
            ifHasSoundStopIt("on")
            animator.setAnimationState("fjukeboxState","off")
            ifHasSoundPlayIt("off")
        end
    end
    
    function ifHasSoundPlayIt(name)
        if animator.hasSound(name) then
            animator.playSound(name)
        end
    end
    
    function ifHasSoundStopIt(name)
        if animator.hasSound(name) then
            animator.stopAllSounds(name)
        end
    end
    
    all the test were done while i was on my spaceship and the only problem that i have right now is that when i quit the game while playing song it will continue even in main menu and im not sure is there something wrong with the lua script that i wrote or its a game bug that it's not "clearing that part of memory" - the only solution to that "bug" is to restart the game or wait for the end(not sure about waiting for the end)
     
  12. The | Suit

    The | Suit Agent S. Forum Moderator

    Lua scripts only unload from memory after 10 seconds.
    You might want to do something which checks if a player entity is naerby - and only works if that is the case.
     
  13. odkupiciel375

    odkupiciel375 Starship Captain

    that seems a bit too heavy as for furniture does it?
    idk why does the "chair" needs to make sure that theres anyone who can seat on it rather than that "object" that wants to use that makes sure that the "chair" is nearby and its "usable" and what type of use it can perform
    cuz in first case when u create entire hallway of chairs than all of them would start checking their surroundings for things that can use them and in second case "object" will make sure that he can see/ touch it and by that use it
    (i hope that i said that right :) )
    But back to the problem:
    i've done some test on the planet and that works slightly better cuz when i'll turn the machine ON and teleport to the ship it will stop that song... but when i'll teleport back it will "unpause" the song and it can't be stopped sooo. (machine then is "OFF" and when interacted it ll start new song)..
     
  14. The | Suit

    The | Suit Agent S. Forum Moderator

    You can just use the proximity scanner script and make sure it is specific to a player entity.
     
  15. odkupiciel375

    odkupiciel375 Starship Captain

    just to make sure You want me to check if in X range there are no other players so i can turn the machine off? if so that doesn't solve the problem
    1# if i'll stop the song that way then i'll have to manually turn it back again (or by script) but then it may play different song(random)
    2#when i'm teleporting back to my ship while standing next to that machine will it have enough time (time can be set but i think that may need like single frame precision sooo... every update?) to detect that im not there to turn itself off before that planet will be "turned off" - because from what i understand when i'll leave the planet game will "Stop idle world" and doesn't that mean that all scripts that were working on that planet are stopped too?
    3# judging by monster behavior when the player is teleporting to the ship he is still standing in that same spot but he is "disabled" so i think that may still cause "sensor" to find that player
     
  16. odkupiciel375

    odkupiciel375 Starship Captain

    maybe i should try to "store" songs in "sounds" and play it through soundEffect? cuz in light.lua script there is no die() or uninit() functions and it seems like soundEffect is automatically stopped by the game
     
  17. The | Suit

    The | Suit Agent S. Forum Moderator

    soundEffect is hardcoded and is stopped.

    You can see the Apex Jukebox
     
  18. bk3k

    bk3k Oxygen Tank

    I forgot to leave what I mentioned.


    Code:
    pickSong = function(playlist)
      return playlist[weightedChoice(playlist)][2]
    end
    
    
    math.fRandom = function(fMin, fMax, precision)
      --precision should be 10, 100, 1000, etc with a zero for every digit you want in precision
      --if not utilized, default to 100
      precision = precision or 100
      --this returns a random float number between min and max
      --with the exception that the arguments with be effectively reduced to 2 digits of precision.
    
      return math.random( math.floor(fMin * precision), math.floor(fMax * precision) ) / precision
    end
    
    
    
    weightedChoice = function(group)
      local groups = #group
      if groups == 1 then
        return 1
      end
    
      local weight = 0
    
      for _, iWeight in ipairs(group) do
        weight = weight + iWeight[1]
      end
    
      local winner = math.fRandom(0, weight)
    
      local i = 0
      local set = {}
      while i < groups do
        i = i + 1
        set = group[i]
        winner = winner - set[1]
        if winner < 0 then
          return i
        end
     
      end
    
      --failsafe for fRandom return of 0.00, return last group
      return #group
    end
    The lists you send would look like this
    Code:
    [
      [0.10 , "/music/arctic-battle1-loop.ogg"],
      [0.10 , "/music/arctic-battle2-loop.ogg"],
      [0.10 , "/music/arctic-battle3-loop.ogg"],
      [0.10 , "/music/crystal-battle1-loop.ogg"],
      [0.10 , "/music/desert-battle-2.ogg"],
      [0.10 , "/music/forest-battle2.ogg"],
      [0.10 , "/music/ocean-battle1-loop.ogg"],
      [0.10 , "/music/scorian-flow.ogg"],
      [0.10 , "/music/tentacle-battle1-loop.ogg"]
    ]

    That aside, this inspired me to write a more feature rich modder's resource for musical objects and standardizing the import of music tracks for mods. Working on that distracted me from actually replying. Then I passed out.

    Part of it would be something like this example player.config.patch file
    Code:
    [
      [
        {
          "op" : "test",
          "path" : "/modEnvironment",
          "inverse" : true
        },
        {
          "op" : "add",
          "path" : "/modEnvironment",
          "value" : {}
        }
      ],
     
      [
        {
          "op" : "test",
          "path" : "/modEnvironment/musicTrack_list",
          "inverse" : true
        },
        {
          "op" : "add",
          "path" : "/modEnvironment/musicTrack_playlists",
          "value" : { }
        }
      ],
     
      [
        {
          "op" : "test",
          "path" : "/modEnvironment/music_allPlaylists",
          "inverse" : true
        },
        {
          "op" : "add",
          "path" : "/modEnvironment/music_allPlaylists",
          "value" : []
        }
      ],
     
      [
        {
          "op" : "add",
          "path" : "/modEnvironment/musicTrack_playlists/odkupiciel375_battleMusic",
          "value" : [
            [0.10 , "/music/arctic-battle1-loop.ogg"],
            [0.10 , "/music/arctic-battle2-loop.ogg"],
            [0.10 , "/music/arctic-battle3-loop.ogg"],
            [0.10 , "/music/crystal-battle1-loop.ogg"],
            [0.10 , "/music/desert-battle-2.ogg"],
            [0.10 , "/music/forest-battle2.ogg"],
            [0.10 , "/music/ocean-battle1-loop.ogg"],
            [0.10 , "/music/scorian-flow.ogg"],
            [0.10 , "/music/tentacle-battle1-loop.ogg"]
          ]
        },
        {
          "op" : "add",
          "path" : "/modEnvironment/music_allPlaylists/-",
          "value" : [0.10 , "odkupiciel375_battleMusic"]
        }
      ]
    
    ]
    I'm currently messing with the lua and animation part of it. I think your "no player around" problem could be solved with world.regionActive()

    The biggest headache I have is figuring out how to determine the length of the songs themselves. That will handicap the ability to swap tracks at the end of the track. Repeating the same track I can do.
     
    Last edited: Feb 27, 2017
  19. odkupiciel375

    odkupiciel375 Starship Captain

    i've tried
    Code:
    function update()
        if not world.regionActive() then
            animator.setAnimationState("fjukeboxState","off")
            ifHasSoundPlayIt("off")
            ifHasSoundStopIt("on")
        end
    end
    
    but i think that i was right (sadly) that when i'm teleporting to the ship im still there and when comes "loading screen" that world will be stopped and since it's stopped then nothing is going on there so i can't detect that it has being stopped
    i think what i need is a "function" that is called when world is going to be stopped like uninit but it needs to work :) cuz for some reason this
    Code:
    function uninit(args)       //tried with and without that args and no difference
        animator.setAnimationState("fjukeboxState","off")
        ifHasSoundPlayIt("off")
        ifHasSoundStopIt("on")
    end
    
    isnt going to work (idk why)

    Code:
    function init(args)
        if storage.state == nil then storage.state = config.getParameter("defaultfjukeboxState", false) end
        object.setInteractive(true)
        switchState(storage.state)
    end
    
    function die()
        switchState(false)
    end
    
    function onInteraction(args)
        switchState(storage.state)
    end
    
    function switchState(currentState)
        if currentState then
            ifHasSoundPlayIt("start")
            animator.setAnimationState("fjukeboxState","on")
            ifHasSoundPlayIt("on")
        else
            ifHasSoundStopIt("on")
            animator.setAnimationState("fjukeboxState","off")
            ifHasSoundPlayIt("off")
        end
        storage.state = not storage.state
    end
    
    function ifHasSoundPlayIt(name)    
        if animator.hasSound(name) then
            animator.playSound(name)
        end
    end
    
    function ifHasSoundStopIt(name)    
        if animator.hasSound(name) then
            animator.stopAllSounds(name)
        end
    end
    

    is the init() only called when object is placed? if so what function is called when world is "resumed" or that chunk will be loaded
     
  20. bk3k

    bk3k Oxygen Tank

    I should be specific about that function.
    in init I'm going to do this

    Code:
      storage.position = object.position()
      storage.region = {
        storage.position[1] - 8,
        storage.position[2] - 8,
        storage.position[1] + 8,
        storage.position[2] - 8
      }
    
    in update check like this

    Code:
    if not world.regionActive(storage.region) then
        pause()
      else
        --toDo
        if storage.paused then
          unpause()
        end
      
      end
    In the animation file, try making a state like this

    Code:
    "pause" : {
      "properties" : {
        "immediateSound" : ""
      }
    },
    
    You can see I figured out(found in other files) that you can play your sounds without actually calling animator.playSound
    I think (haven't tested yet but it looks that way) that telling it to play "" stops the previous sound playing. So attaching such a thing to a state intended to stop it should help.

    In other news, it occurs to me that you could create projectiles that play music.
    Thus a whole new era in PVP has begun. Never gonna give you up, never gonna let you down

    And now I'm having flashbacks to Macross 7... a ridiculous musical mech must be made.
     

Share This Page