mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-03 18:19:38 -06:00
Feature/state machine (#1705)
* Fix script error in old state machine example * Add a module with a more complex state machine, that can be created and controlled through the Lua API. Useful for interactive installations * Add an example asset for the new state machine and rename the old linear "state machine" to luastatemachine Co-authored-by: Malin Ejdbo <malin.ejdbo@gmail.com>
This commit is contained in:
47
data/assets/examples/luastatemachine.asset
Normal file
47
data/assets/examples/luastatemachine.asset
Normal file
@@ -0,0 +1,47 @@
|
||||
local stateMachineHelper = asset.require('util/lua_state_machine_helper')
|
||||
|
||||
local states = {
|
||||
{
|
||||
Title = "Highlight EarthTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
end
|
||||
},
|
||||
{
|
||||
Title = "Highlight MarsTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local stateMachine
|
||||
|
||||
function next()
|
||||
stateMachine.goToNextState()
|
||||
end
|
||||
|
||||
function previous()
|
||||
stateMachine.goToPreviousState()
|
||||
end
|
||||
|
||||
asset.onInitialize(function ()
|
||||
stateMachine = stateMachineHelper.createStateMachine(states)
|
||||
openspace.bindKey('RIGHT', 'next()')
|
||||
openspace.bindKey('LEFT', 'previous()')
|
||||
end)
|
||||
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
stateMachine = nil
|
||||
openspace.clearKey('RIGHT')
|
||||
openspace.clearKey('LEFT')
|
||||
end)
|
||||
@@ -1,47 +1,75 @@
|
||||
local stateMachineHelper = asset.require('util/state_machine_helper')
|
||||
-- Create a state machine with a few different states. The state machine can be controlled through
|
||||
-- the scripting commands from the state machine module.
|
||||
|
||||
states = {
|
||||
local targetNode = function(nodeIdentifier)
|
||||
return [[
|
||||
openspace.setPropertyValueSingle("NavigationHandler.OrbitalNavigator.RetargetAnchor", nil)
|
||||
openspace.setPropertyValueSingle(
|
||||
"NavigationHandler.OrbitalNavigator.Anchor",
|
||||
']] .. nodeIdentifier .. [['
|
||||
)
|
||||
openspace.setPropertyValueSingle("NavigationHandler.OrbitalNavigator.Aim", '')
|
||||
]]
|
||||
end
|
||||
|
||||
local states = {
|
||||
{
|
||||
Title = "Highlight EarthTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 2, 1)
|
||||
end
|
||||
},
|
||||
Identifier = "Constellations",
|
||||
Enter = [[
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 1.0, 1.0)
|
||||
]],
|
||||
Exit = [[
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 0.0, 1.0)
|
||||
]]
|
||||
},
|
||||
{
|
||||
Title = "Highlight MarsTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 10, 1)
|
||||
end
|
||||
Identifier = "Earth",
|
||||
Enter = "openspace.setPropertyValueSingle('Scene.EarthLabel.Renderable.Enabled', true)",
|
||||
Exit = "openspace.setPropertyValueSingle('Scene.EarthLabel.Renderable.Enabled', false)"
|
||||
},
|
||||
{
|
||||
Identifier = "Moon",
|
||||
Enter = "",
|
||||
Exit = ""
|
||||
}
|
||||
}
|
||||
|
||||
local stateMachine
|
||||
local transitions = {
|
||||
{
|
||||
From = "Earth",
|
||||
To = "Moon",
|
||||
Action = targetNode("Moon")
|
||||
},
|
||||
{
|
||||
From = "Moon",
|
||||
To = "Earth",
|
||||
Action = targetNode("Earth")
|
||||
},
|
||||
{
|
||||
From = "Earth",
|
||||
To = "Constellations",
|
||||
-- action is optional
|
||||
},
|
||||
{
|
||||
From = "Constellations",
|
||||
To = "Earth"
|
||||
},
|
||||
{
|
||||
From = "Moon",
|
||||
To = "Constellations",
|
||||
Action = targetNode("Earth")
|
||||
},
|
||||
{
|
||||
From = "Constellations",
|
||||
To = "Moon",
|
||||
Action = targetNode("Moon")
|
||||
}
|
||||
}
|
||||
|
||||
function next()
|
||||
stateMachine.goToNextState()
|
||||
end
|
||||
asset.onInitialize(function()
|
||||
-- Setup
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Enabled', true)
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 0.0)
|
||||
|
||||
function previous()
|
||||
stateMachine.goToPreviousState()
|
||||
end
|
||||
|
||||
asset.onInitialize(function ()
|
||||
stateMachine = stateMachineHelper.createStateMachine(states)
|
||||
openspace.bindKey('RIGHT', 'next()')
|
||||
openspace.bindKey('LEFT', 'previous()')
|
||||
end)
|
||||
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
stateMachine = nil
|
||||
openspace.clearKey('RIGHT')
|
||||
openspace.clearKey('LEFT')
|
||||
openspace.statemachine.createStateMachine(states, transitions, "Earth")
|
||||
end)
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
-- Contains the required functions to create a simple Lua state machine, that can step
|
||||
-- forwards and backwards through a list of states.
|
||||
--
|
||||
-- A state is given as a table with a Title string, and two functions: Play and Rewind
|
||||
-- (see example asset)
|
||||
|
||||
local goToNextStateFunction = function (machine)
|
||||
if (machine.currentStateIndex >= #machine.states) then
|
||||
Submodule ext/ghoul updated: 1febcfdee2...bce78a781c
47
modules/statemachine/CMakeLists.txt
Normal file
47
modules/statemachine/CMakeLists.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
##########################################################################################
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2021 #
|
||||
# #
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
|
||||
# software and associated documentation files (the "Software"), to deal in the Software #
|
||||
# without restriction, including without limitation the rights to use, copy, modify, #
|
||||
# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to #
|
||||
# permit persons to whom the Software is furnished to do so, subject to the following #
|
||||
# conditions: #
|
||||
# #
|
||||
# The above copyright notice and this permission notice shall be included in all copies #
|
||||
# or substantial portions of the Software. #
|
||||
# #
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A #
|
||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF #
|
||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE #
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
|
||||
##########################################################################################
|
||||
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/state.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/transition.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/statemachine.h
|
||||
)
|
||||
source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/statemachinemodule_lua.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/state.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/transition.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/statemachine.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
create_new_module(
|
||||
"StateMachine"
|
||||
statemachine_module
|
||||
STATIC
|
||||
${HEADER_FILES} ${SOURCE_FILES}
|
||||
)
|
||||
1
modules/statemachine/include.cmake
Normal file
1
modules/statemachine/include.cmake
Normal file
@@ -0,0 +1 @@
|
||||
set(DEFAULT_MODULE ON)
|
||||
55
modules/statemachine/include/state.h
Normal file
55
modules/statemachine/include/state.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_STATEMACHINE___STATE___H__
|
||||
#define __OPENSPACE_MODULE_STATEMACHINE___STATE___H__
|
||||
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class State {
|
||||
public:
|
||||
explicit State(const ghoul::Dictionary& dictionary);
|
||||
~State() = default;
|
||||
|
||||
void enter() const;
|
||||
void exit() const;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::string _enter;
|
||||
std::string _exit;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif __OPENSPACE_MODULE_STATEMACHINE___STATE___H__
|
||||
64
modules/statemachine/include/statemachine.h
Normal file
64
modules/statemachine/include/statemachine.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINE___H__
|
||||
#define __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINE___H__
|
||||
|
||||
#include <modules/statemachine/include/state.h>
|
||||
#include <modules/statemachine/include/transition.h>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class StateMachine {
|
||||
public:
|
||||
explicit StateMachine(const ghoul::Dictionary& dictionary);
|
||||
~StateMachine() = default;
|
||||
|
||||
void setInitialState(const std::string initialState);
|
||||
const State* currentState() const;
|
||||
void transitionTo(const std::string& newState);
|
||||
bool canTransitionTo(const std::string& state) const;
|
||||
|
||||
/*
|
||||
* Return the identifiers of all possible transitions from the current state
|
||||
*/
|
||||
std::vector<std::string> possibleTransitions() const;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
int findTransitionTo(const std::string& state) const;
|
||||
int findState(const std::string& state) const;
|
||||
|
||||
int _currentStateIndex = -1;
|
||||
std::vector<State> _states;
|
||||
std::vector<Transition> _transitions;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINE___H__
|
||||
54
modules/statemachine/include/transition.h
Normal file
54
modules/statemachine/include/transition.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_STATEMACHINE___TRANSITION___H__
|
||||
#define __OPENSPACE_MODULE_STATEMACHINE___TRANSITION___H__
|
||||
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class Transition {
|
||||
public:
|
||||
explicit Transition(const ghoul::Dictionary& dictionary);
|
||||
~Transition() = default;
|
||||
|
||||
const std::string& from() const;
|
||||
const std::string& to() const;
|
||||
void performAction() const;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
std::string _from;
|
||||
std::string _to;
|
||||
std::string _action;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif __OPENSPACE_MODULE_STATEMACHINE___TRANSITION___H__
|
||||
80
modules/statemachine/src/state.cpp
Normal file
80
modules/statemachine/src/state.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/statemachine/include/state.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
|
||||
namespace {
|
||||
struct [[codegen::Dictionary(State)]] Parameters {
|
||||
// A string that will be used to identify the state. Cannot be the same as
|
||||
// any other state in the machine
|
||||
std::string identifier;
|
||||
|
||||
// A string containing a Lua script that will be executed when the state
|
||||
// is entered, i.e on a transition from another state
|
||||
std::string enter;
|
||||
|
||||
// A string containing a Lua script that will be executed when the state
|
||||
// is exited, i.e on a transition to another state
|
||||
std::string exit;
|
||||
};
|
||||
#include "state_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation State::Documentation() {
|
||||
return codegen::doc<Parameters>("statemachine_state");
|
||||
}
|
||||
|
||||
State::State(const ghoul::Dictionary& dictionary) {
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_name = p.identifier;
|
||||
_enter = p.enter;
|
||||
_exit = p.exit;
|
||||
}
|
||||
|
||||
void State::enter() const {
|
||||
global::scriptEngine->queueScript(
|
||||
_enter,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
|
||||
void State::exit() const {
|
||||
global::scriptEngine->queueScript(
|
||||
_exit,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
|
||||
std::string State::name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
198
modules/statemachine/src/statemachine.cpp
Normal file
198
modules/statemachine/src/statemachine.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/statemachine/include/statemachine.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <optional>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "StateMachine";
|
||||
|
||||
struct [[codegen::Dictionary(StateMachine)]] Parameters {
|
||||
// A list of states
|
||||
std::vector<ghoul::Dictionary> states
|
||||
[[codegen::reference("statemachine_state")]];
|
||||
|
||||
// A list of transitions between the different states
|
||||
std::vector<ghoul::Dictionary> transitions
|
||||
[[codegen::reference("statemachine_transition")]];
|
||||
|
||||
// The initial state of the state machine. Defaults to the first in the list
|
||||
std::optional<std::string> startState;
|
||||
};
|
||||
#include "statemachine_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation StateMachine::Documentation() {
|
||||
return codegen::doc<Parameters>("statemachine_statemachine");
|
||||
}
|
||||
|
||||
StateMachine::StateMachine(const ghoul::Dictionary& dictionary) {
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_states.reserve(p.states.size());
|
||||
for (const ghoul::Dictionary& s : p.states) {
|
||||
_states.push_back(State(s));
|
||||
}
|
||||
|
||||
_transitions.reserve(p.transitions.size());
|
||||
for (const ghoul::Dictionary& t : p.transitions) {
|
||||
const Transition trans = Transition(t);
|
||||
|
||||
// Check so transition has valid identifiers
|
||||
bool foundFrom = findState(trans.from()) != -1;
|
||||
bool foundTo = findState(trans.to()) != -1;
|
||||
|
||||
if (foundFrom && foundTo) {
|
||||
_transitions.push_back(trans);
|
||||
}
|
||||
else {
|
||||
LERROR(fmt::format(
|
||||
"Invalid transition from '{}' to '{}'. One or both of the states do not "
|
||||
"exist in the state machine", trans.from(), trans.to()
|
||||
));
|
||||
}
|
||||
}
|
||||
_transitions.shrink_to_fit();
|
||||
|
||||
if (_transitions.empty()) {
|
||||
LWARNING("Created state machine without transitions");
|
||||
}
|
||||
|
||||
if (_states.empty()) {
|
||||
LERROR("Created state machine with no states");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string startState = p.startState.value_or(_states.front().name());
|
||||
setInitialState(startState);
|
||||
}
|
||||
|
||||
void StateMachine::setInitialState(const std::string initialState) {
|
||||
int stateIndex = findState(initialState);
|
||||
|
||||
if (stateIndex == -1) {
|
||||
LWARNING(fmt::format(
|
||||
"Attempting to initialize with undefined state '{}'", initialState
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
_currentStateIndex = stateIndex;
|
||||
currentState()->enter();
|
||||
}
|
||||
|
||||
const State* StateMachine::currentState() const {
|
||||
if (_currentStateIndex == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_states[_currentStateIndex];
|
||||
}
|
||||
|
||||
void StateMachine::transitionTo(const std::string& newState) {
|
||||
if (!currentState()) {
|
||||
LERROR(
|
||||
"Cannot perform transition as the machine is in no current state. "
|
||||
"First set an initial state."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int stateIndex = findState(newState);
|
||||
if (stateIndex == -1) {
|
||||
LWARNING(fmt::format(
|
||||
"Attempting to transition to undefined state '{}'", newState
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
int transitionIndex = findTransitionTo(newState);
|
||||
if (transitionIndex == -1) {
|
||||
LWARNING(fmt::format(
|
||||
"Transition from '{}' to '{}' is undefined",
|
||||
currentState()->name(), newState
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
currentState()->exit();
|
||||
_transitions[transitionIndex].performAction();
|
||||
_currentStateIndex = stateIndex;
|
||||
currentState()->enter();
|
||||
}
|
||||
|
||||
bool StateMachine::canTransitionTo(const std::string& state) const {
|
||||
const int transitionIndex = findTransitionTo(state);
|
||||
return transitionIndex != -1;
|
||||
}
|
||||
|
||||
// Search if the transition from _currentState to newState exists.
|
||||
// If yes then return the index to the transition, otherwise return -1
|
||||
int StateMachine::findTransitionTo(const std::string& state) const {
|
||||
if (!currentState()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _transitions.size(); ++i) {
|
||||
if (_transitions[i].from() == currentState()->name() &&
|
||||
_transitions[i].to() == state)
|
||||
{
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Search if the state exist.
|
||||
// If yes then return the index to the state, otherwise return -1
|
||||
int StateMachine::findState(const std::string& state) const {
|
||||
for (size_t i = 0; i < _states.size(); ++i) {
|
||||
if (_states[i].name() == state) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<std::string> StateMachine::possibleTransitions() const {
|
||||
std::vector<std::string> res;
|
||||
|
||||
if (!currentState()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res.reserve(_transitions.size());
|
||||
for (size_t i = 0; i < _transitions.size(); ++i) {
|
||||
if (_transitions[i].from() == currentState()->name()) {
|
||||
res.push_back(_transitions[i].to());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
78
modules/statemachine/src/transition.cpp
Normal file
78
modules/statemachine/src/transition.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/statemachine/include/transition.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
|
||||
namespace {
|
||||
struct [[codegen::Dictionary(Transition)]] Parameters {
|
||||
// The identifier of the state that can trigger the transition
|
||||
std::string from;
|
||||
|
||||
// The identifier of the state that the state machine will move to after the
|
||||
// transition
|
||||
std::string to;
|
||||
|
||||
// A string containing a Lua script that will be executed when the transition
|
||||
// is triggered
|
||||
std::optional<std::string> action;
|
||||
};
|
||||
#include "transition_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation Transition::Documentation() {
|
||||
return codegen::doc<Parameters>("statemachine_transition");
|
||||
}
|
||||
|
||||
Transition::Transition(const ghoul::Dictionary& dictionary) {
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_from = p.from;
|
||||
_to = p.to;
|
||||
_action = p.action.value_or("");
|
||||
}
|
||||
|
||||
const std::string& Transition::from() const {
|
||||
return _from;
|
||||
}
|
||||
|
||||
const std::string& Transition::to() const {
|
||||
return _to;
|
||||
}
|
||||
|
||||
void Transition::performAction() const {
|
||||
if (_action.empty()) {
|
||||
return;
|
||||
}
|
||||
global::scriptEngine->queueScript(
|
||||
_action,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
193
modules/statemachine/statemachinemodule.cpp
Normal file
193
modules/statemachine/statemachinemodule.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/statemachine/statemachinemodule.h>
|
||||
|
||||
#include <modules/statemachine/include/state.h>
|
||||
#include <modules/statemachine/include/statemachine.h>
|
||||
#include <modules/statemachine/include/transition.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/scripting/lualibrary.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
#include "statemachinemodule_lua.inl"
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "StateMachine";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
StateMachineModule::StateMachineModule()
|
||||
: OpenSpaceModule(Name)
|
||||
{ }
|
||||
|
||||
void StateMachineModule::initializeStateMachine(const ghoul::Dictionary& states,
|
||||
const ghoul::Dictionary& transitions,
|
||||
const std::optional<std::string> startState)
|
||||
{
|
||||
ghoul::Dictionary dictionary;
|
||||
dictionary.setValue("States", states);
|
||||
dictionary.setValue("Transitions", transitions);
|
||||
|
||||
if (startState.has_value()) {
|
||||
dictionary.setValue("StartState", *startState);
|
||||
}
|
||||
|
||||
try {
|
||||
_machine = std::make_unique<StateMachine>(dictionary);
|
||||
LINFO(fmt::format(
|
||||
"State machine was created with start state: {}", currentState()
|
||||
));
|
||||
}
|
||||
catch (const documentation::SpecificationError& e) {
|
||||
LERROR(ghoul::to_string(e.result));
|
||||
LERROR(fmt::format("Error loading state machine: {}", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
bool StateMachineModule::hasStateMachine() const {
|
||||
return _machine != nullptr;
|
||||
}
|
||||
|
||||
void StateMachineModule::setInitialState(const std::string initialState) {
|
||||
if (!_machine) {
|
||||
LWARNING("Attempting to use uninitialized state machine");
|
||||
return;
|
||||
}
|
||||
|
||||
_machine->setInitialState(initialState);
|
||||
}
|
||||
|
||||
std::string StateMachineModule::currentState() const {
|
||||
if (!_machine || !_machine->currentState()) {
|
||||
LWARNING("Attempting to use uninitialized state machine");
|
||||
return "";
|
||||
}
|
||||
|
||||
return _machine->currentState()->name();
|
||||
}
|
||||
|
||||
std::vector<std::string> StateMachineModule::possibleTransitions() const {
|
||||
if (!_machine) {
|
||||
LWARNING("Attempting to use uninitialized state machine");
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
return _machine->possibleTransitions();
|
||||
}
|
||||
|
||||
void StateMachineModule::transitionTo(const std::string& newState) {
|
||||
if (!_machine) {
|
||||
LWARNING("Attempting to use uninitialized state machine");
|
||||
return;
|
||||
}
|
||||
|
||||
_machine->transitionTo(newState);
|
||||
}
|
||||
|
||||
bool StateMachineModule::canGoToState(const std::string& state) const {
|
||||
if (!_machine) {
|
||||
LWARNING("Attempting to use uninitialized state machine");
|
||||
return false;
|
||||
}
|
||||
|
||||
return _machine->canTransitionTo(state);
|
||||
}
|
||||
|
||||
scripting::LuaLibrary StateMachineModule::luaLibrary() const {
|
||||
scripting::LuaLibrary res;
|
||||
res.name = "statemachine";
|
||||
res.functions = {
|
||||
{
|
||||
"createStateMachine",
|
||||
&luascriptfunctions::createStateMachine,
|
||||
{},
|
||||
"table, table, [string]",
|
||||
"Creates a state machine from a list of states and transitions. See State "
|
||||
"and Transition documentation for details. The optional thrid argument is "
|
||||
"the identifier of the desired initial state. If left out, the first state "
|
||||
"in the list will be used."
|
||||
},
|
||||
{
|
||||
"goToState",
|
||||
&luascriptfunctions::goToState,
|
||||
{},
|
||||
"string",
|
||||
"Triggers a transition from the current state to the state with the given "
|
||||
"identifier. Requires that the specified string corresponds to an existing "
|
||||
"state, and that a transition between the two states exists."
|
||||
},
|
||||
{
|
||||
"setInitialState",
|
||||
&luascriptfunctions::setInitialState,
|
||||
{},
|
||||
"string",
|
||||
"Immediately sets the current state to the state with the given name, if "
|
||||
"it exists. This is done without doing a transition and completely ignores "
|
||||
"the previous state."
|
||||
},
|
||||
{
|
||||
"currentState",
|
||||
&luascriptfunctions::currentState,
|
||||
{},
|
||||
"",
|
||||
"Returns the string name of the current state that the statemachine is in."
|
||||
},
|
||||
{
|
||||
"possibleTransitions",
|
||||
&luascriptfunctions::possibleTransitions,
|
||||
{},
|
||||
"",
|
||||
"Returns a list with the identifiers of all the states that can be "
|
||||
"transitioned to from the current state."
|
||||
},
|
||||
{
|
||||
"canGoToState",
|
||||
&luascriptfunctions::canGoToState,
|
||||
{},
|
||||
"string",
|
||||
"Returns true if there is a defined transition between the current state and "
|
||||
"the given string name of a state, otherwise false"
|
||||
},
|
||||
{
|
||||
"printCurrentStateInfo",
|
||||
&luascriptfunctions::printCurrentStateInfo,
|
||||
{},
|
||||
"",
|
||||
"Prints information about the current state and possible transitions to the log."
|
||||
}
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<documentation::Documentation> StateMachineModule::documentations() const {
|
||||
return {
|
||||
State::Documentation(),
|
||||
StateMachine::Documentation(),
|
||||
Transition::Documentation()
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
65
modules/statemachine/statemachinemodule.h
Normal file
65
modules/statemachine/statemachinemodule.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINEMODULE___H__
|
||||
#define __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINEMODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
|
||||
#include <modules/statemachine/include/statemachine.h>
|
||||
#include <optional>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class StateMachineModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "StateMachine";
|
||||
|
||||
StateMachineModule();
|
||||
~StateMachineModule() = default;
|
||||
|
||||
void initializeStateMachine(const ghoul::Dictionary& states,
|
||||
const ghoul::Dictionary& transitions,
|
||||
const std::optional<std::string> startState = std::nullopt);
|
||||
|
||||
bool hasStateMachine() const;
|
||||
|
||||
// initializeStateMachine must have been called before
|
||||
void setInitialState(const std::string initialState);
|
||||
std::string currentState() const;
|
||||
std::vector<std::string> possibleTransitions() const;
|
||||
void transitionTo(const std::string& newState);
|
||||
bool canGoToState(const std::string& state) const;
|
||||
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
std::vector<documentation::Documentation> documentations() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<StateMachine> _machine = nullptr;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif __OPENSPACE_MODULE_STATEMACHINE___STATEMACHINEMODULE___H__
|
||||
195
modules/statemachine/statemachinemodule_lua.inl
Normal file
195
modules/statemachine/statemachinemodule_lua.inl
Normal file
@@ -0,0 +1,195 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/statemachine/statemachinemodule.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/misc.h>
|
||||
#include <optional>
|
||||
|
||||
namespace openspace::luascriptfunctions {
|
||||
|
||||
int createStateMachine(lua_State* L) {
|
||||
const int nArguments = ghoul::lua::checkArgumentsAndThrow(
|
||||
L,
|
||||
{ 2, 3 },
|
||||
"lua::createStateMachine"
|
||||
);
|
||||
|
||||
// If three arguments, a start state was included
|
||||
std::optional<std::string> startState = std::nullopt;
|
||||
if (nArguments > 2) {
|
||||
startState = ghoul::lua::value<std::string>(L, 3, ghoul::lua::PopValue::Yes);
|
||||
}
|
||||
|
||||
// Last dictionary is on top of the stack
|
||||
ghoul::Dictionary transitions;
|
||||
try {
|
||||
ghoul::lua::luaDictionaryFromState(L, transitions);
|
||||
}
|
||||
catch (const ghoul::lua::LuaFormatException& e) {
|
||||
LERRORC("createStateMachine", e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pop, so that first dictionary is on top and can be read
|
||||
lua_pop(L, 1);
|
||||
ghoul::Dictionary states;
|
||||
try {
|
||||
ghoul::lua::luaDictionaryFromState(L, states);
|
||||
}
|
||||
catch (const ghoul::lua::LuaFormatException& e) {
|
||||
LERRORC("createStateMachine", e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
|
||||
module->initializeStateMachine(states, transitions, startState);
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int goToState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::goToState");
|
||||
const bool isString = (lua_isstring(L, 1) != 0);
|
||||
|
||||
if (!isString) {
|
||||
lua_settop(L, 0);
|
||||
const char* msg = lua_pushfstring(
|
||||
L,
|
||||
"%s expected, got %s",
|
||||
lua_typename(L, LUA_TSTRING),
|
||||
luaL_typename(L, 0)
|
||||
);
|
||||
return luaL_error(L, "bad argument #%d (%s)", 1, msg);
|
||||
}
|
||||
|
||||
const std::string newState = lua_tostring(L, 1);
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
module->transitionTo(newState);
|
||||
LINFOC("StateMachine", "Transitioning to " + newState);
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setInitialState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::setStartState");
|
||||
const bool isString = (lua_isstring(L, 1) != 0);
|
||||
|
||||
if (!isString) {
|
||||
lua_settop(L, 0);
|
||||
const char* msg = lua_pushfstring(
|
||||
L,
|
||||
"%s expected, got %s",
|
||||
lua_typename(L, LUA_TSTRING),
|
||||
luaL_typename(L, 0)
|
||||
);
|
||||
return luaL_error(L, "bad argument #%d (%s)", 1, msg);
|
||||
}
|
||||
|
||||
const std::string startState = lua_tostring(L, 1);
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
module->setInitialState(startState);
|
||||
LINFOC("StateMachine", "Initial state set to: " + startState);
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int currentState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::currentState");
|
||||
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
std::string currentState = module->currentState();
|
||||
|
||||
lua_pushstring(L, currentState.c_str());
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int possibleTransitions(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::possibleTransitions");
|
||||
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
std::vector<std::string> transitions = module->possibleTransitions();
|
||||
|
||||
ghoul::lua::push(L, transitions);
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int canGoToState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::canGoToState");
|
||||
const bool isString = (lua_isstring(L, 1) != 0);
|
||||
|
||||
if (!isString) {
|
||||
lua_settop(L, 0);
|
||||
const char* msg = lua_pushfstring(
|
||||
L,
|
||||
"%s expected, got %s",
|
||||
lua_typename(L, LUA_TSTRING),
|
||||
luaL_typename(L, 0)
|
||||
);
|
||||
return luaL_error(L, "bad argument #%d (%s)", 1, msg);
|
||||
}
|
||||
|
||||
const std::string state = lua_tostring(L, 1);
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
ghoul::lua::push(L, module->canGoToState(state));
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int printCurrentStateInfo(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::printCurrentStateInfo");
|
||||
|
||||
StateMachineModule* module = global::moduleEngine->module<StateMachineModule>();
|
||||
|
||||
if (module->hasStateMachine()) {
|
||||
std::string currentState = module->currentState();
|
||||
std::vector<std::string> transitions = module->possibleTransitions();
|
||||
LINFOC("StateMachine", fmt::format(
|
||||
"Currently in state: '{}'. Can transition to states: [ {} ]",
|
||||
currentState,
|
||||
ghoul::join(transitions, ",")
|
||||
));
|
||||
}
|
||||
else {
|
||||
LINFOC("StateMachine", "No state machine has been created");
|
||||
}
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
} //namespace openspace::luascriptfunctions
|
||||
Reference in New Issue
Block a user