States
There aren’t states in Slua.
The events state_entry() and state_exit() doesn’t exist.
The script executes the code that is at the top level, out of the functions.
The “New Script”
|
|
To keep the LSL structure, we can write an initialization function instead of state_entry(), for instance main() and call it from the top-level code.
Simulating LSL states.
We can use one table for each “state” to store its event handlers:
|
|
And a function to change from one “state” to another using LLEvents to remove the handlers in a “state” table and add the handlers in the other:
-- simulating states, function to change the "state" (SLua)
local _currentState = {} -- store the current "state" table
local function state(newState)
if newState ~= _currentState then -- the state is changing
if _currentState and _currentState.state_exit then -- there is a current state with state_exit()
_currentState.state_exit()
end
for _, eventName in LLEvents:eventNames() do -- all the events
for _, handler in LLEvents:listeners(eventName) do -- all the handlers of the event
LLEvents:off(eventName, handler) -- removing handler
end
end
for eventName, handler in newState do -- all the functions in the "state" table
if eventName ~= "state_entry" and eventName ~= "state_exit" then -- except "entry" and "exit"
LLEvents:on(eventName, handler) -- adding handler
end
end
_currentState = newState -- "state" changed
if _currentState.state_entry then -- there is state_entry()
_currentState.state_entry()
end
end
end
To change the state we use state(two) -- SLua instead of state two; // LSL
And after the “state” tables we add state(default) -- SLua to call state_entry() in the table “default”.
On state change
Aside of changing the events handlers when a state changes:
- All listens are released.
- When all the handlers to the even “listen” are removed, LLEvents releseases all the listens.
- The event queue is cleared.
- When all the handlers of an the even are removed, LLEvents remove the pending events from the event queue.
- Repeating sensors are released.
- When all the handlers to the even “sensor” are removed, LLEvents releases the repeating sensor.
- The timer event clock is not cleared.
- If the script uses the event “timer” and llcompat.SetTimerEvent() in the library llcompat:
- if the new state has a timer event, and the previous state has a timer set, the timer event in the new state will be triggered on the interval set in the previous state.
- If the script uses LLTimers:
- the timers will be triggered on the intervals set in the previous state.
- the event “timer” used by LLTimers is protected and can’t be removed by LLEvents:off(), we don’t need to check for it in the function “state”.
- LLTimers and llcompat.SetTimerEvent() are incompatible, scripts can’t use both.
- If the script uses the event “timer” and llcompat.SetTimerEvent() in the library llcompat:
States example
The LSL wiki example for states:
default
{
state_entry()
{
llSay(0,
"You either just saved the script after editing it"
+ "\nand/or the script (re)entered the default state.");
// white and opaque text
llSetText("Click to change states", <1.0, 1.0, 1.0>, (float)TRUE);
}
touch_end(integer num_detected)
{
// Note: NEVER do a state change from within a touch_start event -
// - that can lead to the next touch_start on return to this state to be missed.
// Here we do the state change safely, from within touch_end
state two;
}
state_exit()
{
llSay(0, "The script leaves the default state.");
}
}
state two
{
state_entry()
{
llSay(0, "The script entered state 'two'");
state default;
}
state_exit()
{
llSay(0, "The script leaves state 'two'");
}
}
Becomes:
-- simulating LSL states, LSL wiki example for states
local _currentState = {}
local function state(newState)
if newState ~= _currentState then
if _currentState and _currentState.state_exit then
_currentState.state_exit()
end
for _, eventName in LLEvents:eventNames() do
for _, handler in LLEvents:listeners(eventName) do
LLEvents:off(eventName, handler)
end
end
for eventName, handler in newState do
if eventName ~= "state_entry" and eventName ~= "state_exit" then
LLEvents:on(eventName, handler)
end
end
_currentState = newState
if _currentState.state_entry then
_currentState.state_entry()
end
end
end
local default, two = {}, {}
function default.state_entry()
ll.Say(0, "You either just saved the script after editing it" .. "\nand/or the script (re)entered the default state.")
-- white and opaque text
ll.SetText("Click to change states", vector(1.0, 1.0, 1.0), 1)
end
function default.touch_end(events)
local num_detected = #events
-- Note: NEVER do a state change from within a touch_start event -
-- - that can lead to the next touch_start on return to this state to be missed.
-- Here we do the state change safely, from within touch_end
state(two)
end
function default.state_exit()
ll.Say(0, "The script leaves the default state.")
end
function two.state_entry()
ll.Say(0, "The script entered state 'two'")
state(default)
end
function two.state_exit()
ll.Say(0, "The script leaves state 'two'")
end
state(default)