Tutorial scripting templates

Discussion in 'Starbound Modding' started by cpeosphoros, Jul 10, 2017.

  1. cpeosphoros

    cpeosphoros Orbital Explorer

    While working on my first mod I had to search a lot of places to get information about object life cycle callbacks, until I found this on http://starbounder.org/Modding:Lua/Hooks/Object. That wiki page now links to a series of vanilla code examples. It's the wiki, so, probably not really up to date, but I've checked this against both vanilla code and some large mods and it seems to be ok - as of the last edit date of this post.

    From there, I started cobbling together both a template for .object's scripts lua files, and the documentation of their behavior and quirks.

    I made the code in ready to use .lua format for my own use, but I thought I could share it with the community.

    Then I had the idea of extending the scope of this post to include easy to use .patch templates, or whatever other code templates that I come by or the community wants to share.

    If anyone knows of callbacks that are not on the lua files, some better info on what we already have, some better way of doing the same things, actually any useful bits and itches of information please feel free to contribute.

    I reserve the rights to curate and edit the original post, but, really, anyone who wants to contribute can share their material here or via PM to me. All due authoring/contributing attribution will be made.

    So far we have:
    • objecttemplate.lua - To be used in the "scripts" field of a .object file. Runs client-side.
    • questtemplate.lua - To be used in the "scripts" field of a .questtemplate. Runs server-side.
    • testaddtemplate.patch - To be used with a path which accepts exclusively a value or an array. Checks for path existence, creates it if necessary then adds a value or an array (for arrays, you have to include square brackets around the last "add" value".
    • testaddtemplate2.patch - contributed by @IHart - To be used with a path which accepts alternatively a value or an array. Checks for path existence, creates it if necessary, move the previous value, if already present, into an array at that path or just creates path + array, and then adds a value to that array.
    • Assorted json Bits - assorted useful bits for json configuration, contributed by @lazarus78. I'll move those bits into more specific places, when and if they come around.
    Link to the MIT License text, applicable to some of the code, as noted in their individual comments.

    About server/client sidedness, @zetaPRIME has contributed this:
    Server/client sidedness is important, besides multiplayer issues, because, according to lua/doc/word.md:
    Code:
    #### `LuaValue` world.callScriptedEntity(`EntityId` entityId, `String` functionName, [`LuaValue` args ...])
    
    Attempts to call the specified function name in the context of the specified scripted
    entity with the specified arguments and returns the result. This method is synchronous
    and thus can only be used on local master entities, i.e. scripts run on the server may
    only call scripted entities that are also server-side master and scripts run on the client
    may only call scripted entities that are client-side master on that client. For more
    featureful entity messaging, use world.sendEntityMessage.
    Besides that, there are several functions in word that can only be called serverside. They are listed starting at line 788 of word.md

    Code:
    --[=====[
        Copyright notice: The text on the comments and the lua code is mostly copied
        or derived from the wiki or vanilla unpacked assets, and are conveyed under the
        same license as them.
    --]=====]
    
    --[=====[
        Called (once) after the object is added to the world, and when the world
        containing that object is loaded.
    --]=====]
    function init()
    end
    
    --[=====[
        Update loop handler. Called once every scriptDelta (defined in *.object) ticks
        dt is the time in seconds (may be a fraction) since the last time update was called.
    --]=====]
    function update(dt)
    end
    
    --[=====[
        Called when the object is about to be removed from the world.
    --]=====]
    function die()
    end
    
    --[=====[
        Called when the container contents have been changed or interacted with in
        a GUI Pane.
    --]=====]
    function containerCallback()
    end
    
    --[=====[
    function onInteraction(args)
        Called when the object is interacted with.<br />
    
        'args' - Map of interaction event arguments:
            {
             sourceId = <Entity id of the entity interacting with this object>
             sourcePosition = <The {x,y} position of the interacting entity>
            }
    
        Returns:
         nil (no interaction response)
         or a table -- see sbvnobject and challengedoor examples in
                    -- the link for code examples.
            {
             <string "interactAction", --see above
             <map {<interactData>}>
            }
    
       If interactData is malformed in the returned value, the object will not be
       interactable in game, even with '''object.setInteractive(true)'''. The same
       applies if onInteracion() throws a lua exception at runtime.
    
       Objects that only need to rely on a GUI Pane doesn't need to specify a "scripts"
       variable in their '''.object''' file, just provide values for "interactAction" and
       "interactData"/"uiConfig".
    
        Available interaction responses ("interactActions") are:
            "OpenCraftingInterface"               --used 36 times in vanilla
            "OpenTeleportDialog"                  --used 29 times in vanilla
            "ScriptPane" - For widget-based panes --used 26 times in vanilla
            "OpenMerchantInterface"               --used 14 times in vanilla
            "ShowPopup"                           --used  6 times in vanilla
    
            "OpenCockpitInterface"                --not used in vanilla
            "SitDown"                             --not used in vanilla
            "OpenCookingInterface"                --not used in vanilla
            "OpenTechInterface"                   --not used in vanilla
            "Teleport"                            --not used in vanilla
            "OpenSongbookInterface"               --not used in vanilla
            "OpenNpcCraftingInterface"            --not used in vanilla
            "OpenTech3DPrinterDialog"             --not used in vanilla
            "ScriptConsole" - For ScriptCanvases  --not used in vanilla
        --]=====]
    function onInteraction(args)
        -- return {interactActions, interactData}
    end
    
    --[=====[
        Called when the level of an inbound connected node changes
        'args' - Map of:
          {
            node = <(int) index of the node that is changing>
            level = <new level of the node>
          }
    --]=====]
    function onInputNodeChange(args)
    end
    
    --[=====[
        Called when a node is connected or disconnected
    --]=====]
    function onNodeConnectionChange(args)
    end
    
    --[=====[
        Called when the object is interacted with, by an NPC.
    --]=====]
    function onNpcPlay(npcId)
    end
    Code:
    [=====[
        Copyright notice: The text on the comments and the lua code is mostly copied
        or derived from the wiki or vanilla unpacked assets, and are conveyed under the
        same license as them.
    --]=====]
    
    function init()
    end
    
    function uninit()
    end
    
    function update(dt)
    end
    
    function questOffer()
    end
    
    function questDecline()
    end
    
    function questStart()
    end
    
    function questComplete()
    end
    
    function conditionsMet()
    end
    Code:
    /*
        This code has been around the community for some time, and it's so simple
        that I believe it falls under the common programming patterns category,
        rather than actual software protectable by copyright.
    */
    [
        [
            { "op": "test", "path": "/scripts", "inverse" : true },
            { "op": "add", "path": "/scripts", "value": [ ] }
        ],
        [
            { "op": "add", "path": "/scripts/-", "value": "/scripts/mything/mything.lua"  }
        ]
    ]
    Code:
    /*
        Copyright notice: Written by @IHart and released under MIT License.
        Copyright © 2017 IHart
    */
    [
        [
            { "op": "test", "path": "/learnBlueprintsOnPickup"                                         },
            { "op": "test", "path": "/learnBlueprintsOnPickup/0"              , "inverse": true        },
            { "op": "test", "path": "/learnBlueprintsOnPickup"  , "value" : [], "inverse": true        },
    
            { "op": "move", "from": "/learnBlueprintsOnPickup"  , "path": "/hold"                      },
    
            { "op": "add" , "path": "/learnBlueprintsOnPickup"  , "value": []                          },
            { "op": "move", "from": "/hold",                    , "path": "/learnBlueprintsOnPickup/-" }
        ],
        [
            { "op": "test", "path": "/learnBlueprintsOnPickup"                , "inverse": true        },
            { "op": "add" , "path": "/learnBlueprintsOnPickup"                , "value": []            }
        ],
        [
            { "op": "add", "path": "/learnBlueprintsOnPickup/-" , "value": "your_item"                 }
        ]
    ]
    
    Code:
    ==================================================
    "renderLayer"
    ==================================================
    Used to make an object image render on a specific layer. 
    Values are between -10 and +10, with 0 being the default layer.
    
    "renderLayer" : "Object+10",
    "renderLayer" : "Player+10",
    
    Possible application of this function would be for making objects render in
    front of the player. What this means is, the player will walk BEHIND the object
    if you use a posative value. So you could make things like  bushes and railings
    that the player actually walks behind, giving the illusion of depth. This
    functions basically like the "zlevel" bit from block configs, but for objects.
    
    I am not 100% sure on the difference between “Object” and “Player”.
    
    Example: http://imgur.com/Ke3nWNi
    
    
    ==================================================
    "actionOnReap"
    ==================================================
    Used to perform actions, such as spawning projectiles, calling up configs, or
    even spawning items. Most commonly used with weapons, especially in projectile
    configs for spawning multiple projectiles.
    
    Options for "action":
    - spawnitem
    - projectile
    - config
    
    Examples:
    
        "actionOnReap" : [
            {
                "action" : "spawnitem",
                "item" : "YourItem",
                "count": 1
            }
        ]
       
        Source - rocketTrooper.monstertype
        ----------------------------------
        "actionOnReap" : [
            {
                "action" : "config",
                "file" : "/projectiles/explosions/regularexplosion2/regularexplosionknockback.config"
            }
        ]
       
        Source - aegisaltbow.activeitem
        -------------------------------
        "actionOnReap" : [
            {
                "action" : "projectile",
                "type" : "chargedenergyarrow",
                "angleAdjust" : -50,
                "inheritDamageFactor" : 0.2,
                "inheritSpeedFactor" : 1
            }
        ]
    
    
    ==================================================
    "requiresBlueprint"
    ==================================================
    This setting can be used on crafting table configs to make players not require
    blueprints for whatever the table can make. AKA: No need to patch the
    player.config for every new item you want to add.
    
    Example:
        "requiresBlueprint" : false,
       
    By default, this setting is not in the configs for crafting tables, and it is
    assumed "true".
    
    
    ==================================================
    Adding glow to materials
    ==================================================
    How to add a glow to materials, like the Erchias Crystals and whatnot.
    
    Example:
        "renderParameters" : {
            "texture" : "supermatter.png",
            "variants" : 5,
            "lightTransparent" : false,
            "multiColored" : true,
            "occludesBelow" : true,
            "zLevel" : 3930,
            "radiantLight" : [128, 0, 128] // <-- This line
        }
    
       
    ==================================================
    Teleport Object
    ==================================================
    This can be added to an object to make a teleportation dialogue open. Beyond
    just teleporting to your ship or another bookmark, this paticular setup will
    allow you to define the X and Y coordinates specifically. Meaning you can use
    this to make hard coded teleportation points on a specific planet. I found this
    code being used for fake "door" objects to make people teleport below the
    surface to what was supposed to be the "interior" of the building.
    
    Example:
        "interactAction" : "OpenTeleportDialog"
        "interactData" : {
            "canBookmark": false, // Prevents bookmarking of this teleporter object
            "destinations": [
                {
                    "icon": "return",
                    "name": "^white;Enter Building",
                    "planetName": "",
                    "warpAction": "Nowhere=x.y" // The X and Y values can be found with /debug
                }
            ],
            "includePartyMembers": false, // Don't show party members in the dialogue
            "includePlayerBookmarks": false // Don't show bookmarks in the dialogue
        }
    
    
    ==================================================
    Collision Samples
    ==================================================
    These are quite common on most objects, but I wanted to document how they are
    used.
    
    This one is quite simple. There are pre-defined sets of collision types.
        "collision" : "platform",
        "collision" : "solid",
        "collision" : [
            [-0.25, 0],
            [0.25, 0],
            [0.25, 12],
            [-0.25, 12]
        ],
    
    platform - An objects collision will act like platforms. the collision will only
    be at the "top" of the object, as calculated by the "spaceScan" setting.
    
    solid - As the name implies, the collision of the object will be completely
    solid, as calculated by the "spaceScan" setting.
    
    [ ] - This is an array. You can use coordinates to define a "collision area",
         which is useful for more complex objects.
    
        Example:
        "collision" : [
            [-1, 0],
            [1, 0],
            [1, 1],
            [-1, 1]
        ],
    
    - Values are based on the number of block spaces, so a calue of [-1,0] means one
      block to the left, and zero blocks up.
    - This will make a 2x2 (block) collision box, centered on the object. Note, that
      offsetting an object image will cause the "center" to shift as well. [0,0] is
      basically wherever the object is on the curser when attempting to place it.
    - You can use as many coordinates as you wish to make more complex collision
      shapes.
    
    
    This setting is used to define specific spaces as having collision. Basically "I
    want collision exactly here". This is used in conjunction with the abolve
    "collision" setting to define the type of collision; "solid" or "platform".
    
        "collisionSpaces" : [ [-5,6], [-4,6], [3,6], [-2,6] ]
    
    
    ==================================================
    Image Modifiers
    ==================================================
    Additional parameters you can add to the "image" setting for various effects.
    
    ?fade=<color>=<fraction_amount>
    ?brightness=<amt_percent>
    ?saturation=<amt_percent>
    ?hueshift=<amt_degrees>
    ?replace;<color1>=<color2>;
    ?addmask=<file>
    ?submask=<file>
    ?multiply=<color>
    ?border=<size>;<color1>;<color2>
    ?scalenearest
    ?scalebilinear
    ?scale=<value>                <-- can't be a variable
    ?scalebicubic
    ?crop=<left>;<top>;<right>;<bottom>
    ?setcolor=<color>
    ?flip
    
    Example:
        "image" : "<partImage>:default.<frame>?scale=2"
       
    http://community.playstarbound.com/threads/s2f-basic-lua-and-advancing-animation.57871/#post-1859602



    * Edited on July, 14, 2017:
    • Better info on objecttemplate.lua, including connection nodes callbacks.
    • Included questtemplate.lua.
    • Included testaddtemplate.patch
    • Included testaddtemplate2.patch -- author: @IHart
    * Edited on July, 15, 2017:
    • Added copyright notice to @IHart contributed code
    • Added information on client/server sidedness
    * Edited on July, 17, 2017:
    • Added assorted json bits contributed by @lazarus78.


    • document questtemplate.lua
    • add other templates
     
    Last edited: Jul 17, 2017
    Cyel and IHart like this.
  2. bk3k

    bk3k Oxygen Tank

    I think that page lists them all the built in ones, but keep in mind that you can call functions with scripted entity calls - for example in
    \objects\wired\door\door.lua
    You have hasCapability() and doorOccupiesSpace() which exist for the sole reason of being called this way. And of course the messaging added callbacks also present in this script as well.
     
  3. cpeosphoros

    cpeosphoros Orbital Explorer

    Yes, I'm aware of world.callScriptedEntity and world.sendEntityMessage and how they work. Actually, I could grasp that way sooner, just by reading some mods' code. My problem wast to find when the lifecycle methods were called, since I needed them in order to do some tricks I have in mind, but since they are called from the game engine and not from inside script code, even in the vanila folder tree, I couldn't get that without some documentation.
     
    Last edited: Jul 11, 2017
    IHart likes this.
  4. IHart

    IHart Scruffy Nerf-Herder

    +1 for tricks.
     
    cpeosphoros likes this.
  5. cpeosphoros

    cpeosphoros Orbital Explorer

    Edited the op.
     
  6. IHart

    IHart Scruffy Nerf-Herder

    Challenge mode!
    "learnBlueprintsOnPickup" accepts strings or arrays.
    Preserve the string if present.
    My best solution so far:
    Code:
    [[
      {
      "op": "test",
      "path": "/learnBlueprintsOnPickup"
      },
      {
      "op": "test",
      "path": "/learnBlueprintsOnPickup/0",
      "inverse": true
      },
      {
      "op": "test",
      "path": "/learnBlueprintsOnPickup",
      "value" : [],
      "inverse": true
      },
      {
      "op": "move",
      "from": "/learnBlueprintsOnPickup",
      "path": "/hold"
      },
      {
      "op": "add",
      "path": "/learnBlueprintsOnPickup",
      "value": []
      },
      {
      "op": "move",
      "from": "/hold",
      "path": "/learnBlueprintsOnPickup/-"
      }
    ],[
      {
      "op": "test",
      "path": "/learnBlueprintsOnPickup",
      "inverse": true
      },
      {
      "op": "add",
      "path": "/learnBlueprintsOnPickup",
      "value": []
      }
    ],[
      {
      "op": "add",
      "path": "/learnBlueprintsOnPickup/-",
      "value": "your_item"
      }
    ]]
     
  7. cpeosphoros

    cpeosphoros Orbital Explorer

    Seems very good. I can't think of a better solution. Including it at the OP.
     
    IHart likes this.
  8. lazarus78

    lazarus78 The Waste of Time

    Ive accumulated a list of my own for some "useful" bits of information... Not specifically scripting, but JSON. Would this be an appropriate place to post it, or maybe its own thread would be better?
     
  9. cpeosphoros

    cpeosphoros Orbital Explorer

    The intention of this thread is exactly that, @lazarus78. Of course I reserve the rights to curate and edit the original post, but anyone who wants to contribute can share their material here.
     
  10. lazarus78

    lazarus78 The Waste of Time

    Cool beans. Ill need to make it reader friendly, but will post it up later.
     
    cpeosphoros likes this.
  11. lazarus78

    lazarus78 The Waste of Time

  12. cpeosphoros

    cpeosphoros Orbital Explorer

    Done. Included it as is. Later I'll move each part to the information on the respective assets.
     
    lazarus78 likes this.

Share This Page