Modding Help Is it possible to animate sitCoverImage?

Discussion in 'Starbound Modding' started by bohric, Feb 12, 2017.

  1. bohric

    bohric Scruffy Nerf-Herder

    I'm trying to create a furniture object that plays an animation when a character sits on it. I need this animation to be layered in front of the player, though, so I can't just animate the object itself. I've been trying to find a way to animate the cover image. I haven't found a way to make a .frames file work, and I have no clue how to use .animation files. Am I missing something, or can someone point me to an animation file tutorial that actually makes sense?
     
  2. The | Suit

    The | Suit Agent S. Forum Moderator

    I doubt it is possible without knowing Lua.
     
  3. bohric

    bohric Scruffy Nerf-Herder

    Do you know of any good resources or tutorials regarding using Lua to code animations?
     
  4. The | Suit

    The | Suit Agent S. Forum Moderator

    You need to learn lua fully - not piece meal. Or you will have a hard time writing the code properly.
    Once you learn it - you will be able to make sense of the animation file by looking at vanillia files such as alarm and turrets.
     
  5. bohric

    bohric Scruffy Nerf-Herder

    I've figured out how to control the state of an animation with Lua, as well as read an object's parameters. What I haven't been able to do is set the cover image. I can read it, but not set it. Is it even possible to set sitCoverImage in a Lua script?
     
  6. The | Suit

    The | Suit Agent S. Forum Moderator

    You would likely have to remake the entire object do to it in lua.
    Separate the image into parts and set animation to zlevel to 3 or 4. ( I think player is 3. )
     
  7. bk3k

    bk3k Oxygen Tank

    If you use cover image, that I believe is handled by the engine specially and probably can't have the flexibility you need especially the ability to use animation(possibly I'm wrong). You will want a separate "part" that can be independently manipulated.

    But if all you really needed was the ability to reference a different png file, that could probably be easily done with tags. Tags can replace parts of strings, can be defaulted in .animation files, and changed easily in LUA.
     
  8. bohric

    bohric Scruffy Nerf-Herder

    I set the animation to a higher zLevel, as far as I can tell, but the part I'm trying to animate is still appearing behind the player. Also, I'm trying to stop the animation when the player stands up from the object, but that's a different problem. Things are not documented very well.
     
  9. bk3k

    bk3k Oxygen Tank

    This is for the bath, right?

    I'll give you a useful example. This is an object(modified from original) that's part of an old ship mod I'm restoring(well really done).

    Code:
    {
      "objectName" : "vepr_lextoilet",
      "rarity" : "uncommon",
    
      "description" : "Waste isn't a problem anymore! The LV1 compacts it and throws it out of a spacechute for only *299.",
      "shortdescription" : "vepr LEX Toilet",
      "race" : "generic",
      "category" : "furniture",
      "price" : 400,
      "colonyTags" : [ "pretty",  "misc" ],
      "objectType" : "loungeable",
    
      "apexDescription" : "Let me guess: it's bigger on the inside?",
      "avianDescription" : "A place for droppings.",
      "floranDescription" : "Ssshiny smooth brain bowl.",
      "glitchDescription" : "Interest. No longer will disposal of waste material be an issue.",
      "humanDescription" : "Why do so many spaceships not have this!?",
      "hylotlDescription" : "Elegant toilet.",
    
      "inventoryIcon" : "vepr_lextoileticon.png",
      "orientations" : [
        {
          "image" : "vepr_lextoilet.png:default.<frame>",
          "direction" : "left",
          "flipImages" : true,
          "sitPosition" : [-5, 23],
          "imagePosition" : [0, 0],
      
          "frames" : 12,
          "animationCycle" : 2.0,
          "spaceScan" : 0.1,
          "anchors" : [ "bottom" ]
        },
        {
          "image" : "vepr_lextoilet.png:default.<frame>",
          "direction" : "right",
          "flipImages" : false,
          "sitPosition" : [14, 23],
          "imagePosition" : [0, 0],
      
          "frames" : 12,
          "animationCycle" : 2.0,
          "spaceScan" : 0.1,
          "anchors" : [ "bottom" ]
        }
      ],
    
      "sitStatusEffects" : [
        "nude"
      ],
    
      "sitFlipDirection" : false,
      "scripts" : [ "autoFlush.lua" ],
      "scriptDelta" : 10,
      "animation" : "toilet.animation",
      "animationParts" : {
        "toilet" : "vepr_lextoilet.png"
      },
      "animationCustom" : {
        "sounds" : {
          "flush" : [ "/sfx/objects/toilet_flush.ogg" ],
          "wait" : [ ]
        }
      },
      "animationPosition" : [0, 0]
    }
    



    Code:
    {
      "globalTagDefaults" : {
        "direction" : "Right"
      },
    
      "animatedParts" : {
        "stateTypes" : {
          "toilet" : {
            "default" : "wait",
            "states" : {
              "wait" : {
                "frames" : 1,
                "cycle" : 0.15
              },
              "flush" : {
                "frames" : 1,
                "cycle" : 0.15,
                "mode" : "transition",
                "transition" : "wait"
              }
            }
          }
        },
    
        "parts" : {
          "toilet" : {
            "properties" : {
              "centered" : false
            },
    
            "partStates" : {
              //"toiletState" : {
              "toilet" : {
                "wait" : {
                  "properties" : {
                    "image" : "<partImage>:default.<frame>"
                  }
                },
            
                "flush" : {
                  "properties" : {
                    "image" : "<partImage>:default.<frame>"
                  }
                }
              }
            }
          }
        }
      },
    
      "sounds" : {
        "wait" : [],
        "flush" : [ "/sfx/objects/toilet_flush.ogg" ]
      }
    }
    




    Code:
    function init()
      storage.id = entity.id()
      storage.autoFlush = false
      storage.cooldown = 0
    end
    
    
    function update(dt)
      local occupied = world.loungeableOccupied(storage.id)
      if storage.autoFlush and occupied then
        --nothing
      elseif occupied then
        storage.autoFlush = true
        storage.cooldown = 1.50
      elseif storage.cooldown > 0 then
        storage.cooldown = storage.cooldown - dt
      elseif storage.autoFlush then
        storage.autoFlush = false
        animator.setAnimationState("toilet", "flush", false)
        animator.playSound("flush", 1)
      end
    end
    


    I made that simple script because changing objects that make sounds on interaction to objects that you can "sit" on causes the sounds to no longer play, or at least it did with this toilet(which was not originally scripted). I also made sure the flush only occurs after(plus a small delay) the object is no longer in use. So that might be useful to you, but doesn't directly answer your question.

    But it does help because you see examples of some key functions here that you need. Before that, even if you don't know any languages, I'll tell you this. Functions often have "returns" aka after completion they can pass data back.
    world.loungeableOccupied() would return if the object is in use or not. The return is either true or false


    entity.id() returns the id of the object itself and you need to feed that info... like I did (setting storage.id) or like this
    occupied = world.loungeableOccupied( entity.id() )
    The return of entity.id() is fed into world.loungeableOccupied()
    occupied
    is either true or false, and that is how you would know if the object is in use. Now you just need to know how to change the animation based off knowing this.

    animator.setAnimationState("example_partName", "example_stateName", false)

    or in my case(as you can see from the object and animation files) I have a part named "toilet" and (from the animation file) my part has a "partState" named "flush" that this function is switching to. That's how you'll trigger your animation.

    In case you're wondering about that false being sent to animator.setAnimationState()
    The documentation states that this "If ... true, restart the animation loop if it's already active." and I don't find that desirable behavior, but that's just me.

    My script there is pretty simple and you still don't need most of it
    Lets say in your animation file you have 2 parts "tub" and "shower"
    Lets say you defined 2 states for "shower" called "inUse" and "idle"
    Lets say that "inUse" is animated, and may even have particles too (once you learn about particles).

    At a minimum, all you need for a script is this

    Code:
    function init()
    
    end
    
    
    function update()
      local occupied = world.loungeableOccupied( entity.id() )
      if occupied then
        animator.setAnimationState("shower", "inUse", false)
      else
        animator.setAnimationState("shower", "idle", false)
      end
    end
    
    But why be satisfied with just that? Play with the code. Read code from the assets and other mods, attempt to emulate and/or modify the code you find. Break things and otherwise fail. Learn from it.

    LUA vastly expands what is possible for your mods. It is well worth getting a handle on, even if a bit at a time.
    For example you could have the showerhead spawning water droplets(rather than just animated fake water), and the drain area actively draining them - either all the time, once the water level reached a certain height, and also once the object was no longer in use.

    edit: I should have read closer... you already figured that much out. My bad.
     
  10. Cyel

    Cyel Scruffy Nerf-Herder


    This tutorial
    mentions that renderLayer, to be above the player, should be "player+1"?, and that "zLevel doesnt directly relate to the player, only renderLayer does => The exact spot the Player occupies basically is renderLayer "player" without any adjustment or zLevel."

    To "stop the animation when the player stands up", I think you should put your animation-stopping thingy in a function called "uninit()", or maybe use object.isTouching(playerId) (implying you're storing the playerId when interacted) to know when the player "leaves" your object (it wouldn't be immediate tho)
     

Share This Page