Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Responsive Object for Starbound Mods
-- ===================================================================
-- RESPONSIVE OBJECT
-- ===================================================================
-- Version 1.0.0
--
-- little module/API for furniture and decorations to respond
-- if a player enters it proximity radius and trigger animations
--
-- Config Parameters:
-- see sample file
--
-- Animation State:
-- entity.animationState("responsiveState")
----------------------------------------------------------------------
-- Definition
----------------------------------------------------------------------
responsiveObject = {}
----------------------------------------------------------------------
-- init
----------------------------------------------------------------------
--
-- call it in your entities init() hook.
function responsiveObject.init()
self.anims = {}
-- indexed array
self.anims.idle = {}
-- indexed array
self.anims.active = {}
-- transitions are optional and you can only have one to idle and one to active.
self.anims.transIdle = {}
self.anims.transIdle.name = ""
self.anims.transIdle.time = -1
self.anims.transIdle.sounds = nil
self.anims.transActive = {}
self.anims.transActive.name = ""
self.anims.transActive.time = -1
self.anims.transActive.sounds = nil
responsiveObject.parse()
self.timer = -1
end
----------------------------------------------------------------------
-- start
----------------------------------------------------------------------
--
-- call it in your entities init() hook after
-- the responsive object initialization.
-- and optional registerAnimation calls.
function responsiveObject.start()
entity.setAnimationState(
"responsiveState",
self.anims.idle[1].name
)
self.timer = self.anims.idle[1].time
end
----------------------------------------------------------------------
-- update
----------------------------------------------------------------------
--
-- call it in your entities main() hook.
function responsiveObject.update()
-- first count down timer
if self.timer > 0 then
self.timer = self.timer - 1
elseif self.timer == 0 then
-- check if timer 0 and an animation switch is needed
self.timer = -1
-- transitions
if entity.animationState("responsiveState") == self.anims.transActive.name then
responsiveObject.nextAnimation("active")
elseif entity.animationState("responsiveState") == self.anims.transIdle.name then
responsiveObject.nextAnimation("idle")
else
if responsiveObject.isIdle() and #self.anims.idle > 1 then
responsiveObject.nextAnimation("idle")
elseif #self.anims.active > 1 then
responsiveObject.nextAnimation("active")
end
end
end
-- detect player
local entityIds = world.playerQuery(
entity.position(),
self.detectRadius,
{ inSightOf = entity.id() }
)
if responsiveObject.isIdle() then
if #entityIds > 0 then
responsiveObject.switchToActive()
end
elseif not responsiveObject.isIdle() then
if #entityIds == 0 then
responsiveObject.switchToIdle()
end
end
end
----------------------------------------------------------------------
-- die
----------------------------------------------------------------------
--
-- call it in your entities die() hook.
function responsiveObject.die()
self.anims = nil
end
----------------------------------------------------------------------
-- interact
----------------------------------------------------------------------
--
-- optional, only if your entity is interactive (mouseover + E)
-- call it in your entities onInteraction() hook.
--
-- triggers the first active animation.
function responsiveObject.interact()
if responsiveObject.isActive() then
return
end
entity.setAnimationState(
"responsiveState",
self.anims.active[1].name
)
self.timer = self.anims.active[1].time
if self.anims.active[1].sounds ~= nil then
entity.playSound(self.anims.active[1].sounds)
end
end
----------------------------------------------------------------------
-- isIdle
----------------------------------------------------------------------
--
-- the object is idle or off
--
-- returns true if one of the registered idle animation runs or
-- the idle transition runs.
function responsiveObject.isIdle()
for i = 1, #self.anims.idle do
if entity.animationState("responsiveState") == self.anims.idle[i].name then
return true
end
end
if entity.animationState("responsiveState") == self.anims.transIdle.name then
return true
end
return false
end
----------------------------------------------------------------------
-- isActive
----------------------------------------------------------------------
--
-- the object is active
--
-- returns true if one of the registered active animation runs or
-- the active transition runs.
function responsiveObject.isActive()
for i = 1, #self.anims.active do
if entity.animationState("responsiveState") == self.anims.active[i].name then
return true
end
end
if entity.animationState("responsiveState") == self.anims.transActive.name then
return true
end
return false
end
function responsiveObject.switchToActive()
if responsiveObject.isIdle() then
entity.setAnimationState(
"responsiveState",
self.anims.transActive.name
)
if not self.anims.transActive.sounds ~= nil then
entity.playSound(self.anims.transActive.sounds)
end
self.timer = self.anims.transActive.time
end
end
function responsiveObject.switchToIdle()
if not responsiveObject.isIdle() then
entity.setAnimationState(
"responsiveState",
self.anims.transIdle.name
)
if not self.anims.transIdle.sounds ~= nil then
entity.playSound(self.anims.transIdle.sounds)
end
self.timer = self.anims.transIdle.time
end
end
----------------------------------------------------------------------
-- nextAnimation
----------------------------------------------------------------------
--
-- switches to the next animation of that type
function responsiveObject.nextAnimation(type)
local anims = self.anims[type]
local index = 1
if self.randomize == true then
index = math.random(1, #anims)
end
entity.setAnimationState(
"responsiveState",
anims[index].name
)
self.timer = anims[index].time
if anims[index].sounds ~= nil then
entity.playSound(anims[index].sounds)
end
end
----------------------------------------------------------------------
-- registerAnimation
----------------------------------------------------------------------
--
-- registers an animation for the responsive object
-- the animation file of the object must have responsiveState state types defined.
--
-- @param name string name of the animation state
-- @param type string can be:
-- "idle" - for off or idle animations (this means no player around)
-- "active" - for off or idle animations (with at least one player in proximity)
-- "transIdle" - optional transition from active to idle anim
-- "transActive" - optional transition from idle to active anim
-- @param time number time how many update loops are waited until the next stage is entered
-- this means the duration is multiplied with your scriptDelta value
-- @param sounds array of strings
function responsiveObject.registerAnimation(
name,
type,
time,
sounds
)
if type == "transIdle" then
self.anims.transIdle.name = name;
self.anims.transIdle.time = time;
self.anims.transIdle.sounds = sounds;
elseif type == "transActive" then
self.anims.transActive.name = name;
self.anims.transActive.time = time;
self.anims.transActive.sounds = sounds;
else
local ani = {}
ani.name = name
ani.time = time
ani.sounds = sounds
self.anims[type][#self.anims[type]+1] = ani
end
end
----------------------------------------------------------------------
-- parse
----------------------------------------------------------------------
--
-- starts parsing the configParameters from our object file
--
function responsiveObject.parse()
-- can be nil since you can also call registerAnimation to setup the states
local responsiveConfig = entity.configParameter("responsiveObject")
if responsiveConfig == nil then
return
end
-- not nil parse params
-- "mostly fail saved"
self.detectRadius = responsiveConfig.detectRadius
if self.detectRadius == nil then
self.detectRadius = 1
end
self.detectLineOfSight = responsiveConfig.detectLineOfSight
if self.detectLineOfSight == nil then
self.detectLineOfSight = true
end
self.randomize = responsiveConfig.responsiveAnimations.randomize
if self.randomize == nil then
self.randomize = false
end
responsiveObject.parseAnimationArray("idle")
responsiveObject.parseAnimationArray("active")
responsiveObject.parseTransition("transActive")
responsiveObject.parseTransition("transIdle")
end
----------------------------------------------------------------------
-- parseAnimationArray
----------------------------------------------------------------------
--
-- for parsing idle and active
--
function responsiveObject.parseAnimationArray(state)
local responsiveConfig = entity.configParameter("responsiveObject")
local arrayanim = responsiveConfig.responsiveAnimations[state]
for i = 1, #arrayanim do
local time = arrayanim[i].time
local sounds = arrayanim[i].sounds
if time == nil then
time = -1
end
responsiveObject.registerAnimation(
arrayanim[i].name,
state,
time,
sounds
)
end
end
----------------------------------------------------------------------
-- parseTransition
----------------------------------------------------------------------
--
-- for parsing the optional transitions
--
function responsiveObject.parseTransition(state)
local responsiveConfig = entity.configParameter("responsiveObject")
local trans = responsiveConfig.responsiveAnimations[state]
-- since it is optional
if trans == nil then
return
end
local time = trans.time
local sounds = trans.sounds
if time == nil then
time = -1
end
responsiveObject.registerAnimation(
trans.name,
state,
time,
sounds
)
end
-- DO NOT INCLUDE THIS SCRIPT TO ANY OBJECT
-- THIS THIS JUST A SAMPLE FILE FOR RESPONSIVEOBJECT
-- REMOVE the lua comments inside these code spippets!
-- ===================================================================
-- include this with your values into your .object file (JSON Syntax):
-- ===================================================================
-- ===================================================================
-- starbound properties:
-- ===================================================================
"objectType" : "wire",
"scripts" : [
"/scripts/smee/responsiveobject.lua",
"yourobjectscript.lua"
],
"scriptDelta" : 5,
"animation" : "youranimationfile.animation",
"animationParts" : {
"yourpartid" : "youranimationimage.png"
},
-- ===================================================================
-- sound configuration:
-- ===================================================================
-- this is need since playSound needs a key instead of an array with
-- sound files
"mySounds" : [ "/sfx/somesoundfile.ogg" ],
-- ===================================================================
-- responsive object properties sample:
-- ===================================================================
"responsiveObject" : {
"detectRadius" : 5, // 5 is a good radius
"detectLineOfSight" : true, //optional default true
"responsiveAnimations" : {
"randomize" : true,
"idle" : [
{
"name" : "yourAnimName",
"time" : -1, // optional
"sounds" : "" // optional
}
-- and more
],
"active" : [
{
"name" : "yourAnimName",
"time" : -1, // optional
"sounds" : "" // optional
}
-- and more
],
-- transitions are optional remove them if not needed
"transActive" : {
"name" : "yourAnimName",
"time" : -1, // optional
"sounds" : "mySounds" // optional
},
"transIdle" : {
"name" : "yourAnimName",
"time" : -1, // optional
"sounds" : "mySounds" // optional
},
}
}
-- ===================================================================
-- sample for your .lua file (LUA Syntax):
-- ===================================================================
function init(virtual)
entity.setInteractive(true)
entity.setAllOutboundNodes(false)
if not virtual then
responsiveObject.init()
-- you can also add animation states here with:
-- responsiveObject.registerAnimation
responsiveObject.start()
end
-- TODO your custom code here
end
-- only need if entity.setInteractive(true)
function onInteraction(args)
-- optional if you don't need it remove this line
responsiveObject.interact()
return { "", {} }
end
function main()
responsiveObject.update()
-- TODO your custom code here
end
function die()
responsiveObject.die()
-- TODO your custom code here
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment