-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
State machine system #389
Merged
Merged
State machine system #389
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
509b0cf
Added state machine component
BaerMitUmlaut aae332c
Fixed toString function header
BaerMitUmlaut b63898f
Added onStateEntered and onStateLeaving functions
BaerMitUmlaut f45489d
Removed diag_logs, used isEqualType, add support for inherition, use …
BaerMitUmlaut ba3d396
Added skipNull functionality
BaerMitUmlaut 50d17ec
Added Linux compatibility
BaerMitUmlaut File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
x\cba\addons\statemachine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class Extended_PreStart_EventHandlers { | ||
class ADDON { | ||
init = QUOTE(if (!IS_LINUX) then {call COMPILE_FILE(XEH_preStart)}); | ||
}; | ||
}; | ||
|
||
class Extended_PreInit_EventHandlers { | ||
class ADDON { | ||
init = QUOTE(if (!IS_LINUX) then {call COMPILE_FILE(XEH_preInit)}); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
PREP(addState); | ||
PREP(addTransition); | ||
PREP(clockwork); | ||
PREP(create); | ||
PREP(createFromConfig); | ||
PREP(delete); | ||
PREP(toString); | ||
PREP(updateList); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#include "script_component.hpp" | ||
|
||
ADDON = false; | ||
|
||
#include "XEH_PREP.hpp" | ||
|
||
ADDON = true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#include "script_component.hpp" | ||
|
||
#include "XEH_PREP.hpp" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#include "script_component.hpp" | ||
|
||
class CfgPatches { | ||
class ADDON { | ||
author = "$STR_CBA_Author"; | ||
name = CSTRING(component); | ||
url = "$STR_CBA_URL"; | ||
units[] = {}; | ||
requiredVersion = REQUIRED_VERSION; | ||
requiredAddons[] = {"CBA_common"}; | ||
version = VERSION; | ||
authors[] = {"BaerMitUmlaut"}; | ||
}; | ||
}; | ||
|
||
#include "CfgEventHandlers.hpp" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
This is an example for a CBA state machine config that can be read by the | ||
CBA_statemachine_fnc_createFromConfig function. | ||
This example would result in the same state machine as the one from | ||
example.sqf. | ||
*/ | ||
|
||
class MyAddon_Statemachine { | ||
// Class properties have the same name as the corresponding function parameters | ||
// and code goes into strings. | ||
list = "allGroups select {!isPlayer leader _x}"; | ||
skipNull = 1; | ||
|
||
// States are just subclasses of the state machine | ||
class Initial { | ||
onState = ""; | ||
onStateEntered = ""; | ||
onStateLeaving = ""; | ||
|
||
// Transitions are also just subclasses of states | ||
class InCombat { | ||
targetState = "Alert"; | ||
condition = "combatMode _this == 'YELLOW'"; | ||
onTransition = "{ \ | ||
_x setSkill ['spotDistance', ((_x skill 'spotDistance') * 1.5) min 1]; \ | ||
_x setSkill ['spotTime', ((_x skill 'spotTime') * 1.5) min 1]; \ | ||
} forEach (units _this);"; | ||
}; | ||
}; | ||
|
||
// Empty classes will also work if the state contains no transitions or onState code. | ||
class Alert {}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
This is an example for a script that uses the CBA state machine system. | ||
In this case, we're "sharpening the senses" of AI units that got attacked before. | ||
Once they had contact, they stay alert. | ||
|
||
This would simulate units that did not expect an attack, but are now aware that | ||
they might come under fire again. | ||
|
||
You can test this example by executing the following code in the debug console: | ||
[] call compile preprocessFileLineNumbers "\x\cba\addons\statemachine\example.sqf" | ||
*/ | ||
|
||
private _stateMachine = [{allGroups select {!isPlayer leader _x}}, true] call CBA_statemachine_fnc_create; | ||
|
||
[_stateMachine, {}, {}, {}, "Initial"] call CBA_statemachine_fnc_addState; | ||
[_stateMachine, {}, {}, {}, "Alert"] call CBA_statemachine_fnc_addState; | ||
|
||
[_stateMachine, "Initial", "Alert", {combatMode _this == "YELLOW"}, { | ||
// Set skill once on transition | ||
// This could also be done in the onStateEntered function | ||
{ | ||
_x setSkill ["spotDistance", ((_x skill "spotDistance") * 1.5) min 1]; | ||
_x setSkill ["spotTime", ((_x skill "spotTime") * 1.5) min 1]; | ||
} forEach (units _this); | ||
}, "InCombat"] call CBA_statemachine_fnc_addTransition; | ||
|
||
// This makes sure you can execute this through the debug console | ||
_stateMachine spawn { | ||
sleep 0.1; | ||
private _output = [_this, true, true] call CBA_statemachine_fnc_toString; | ||
copyToClipboard _output; | ||
hintC _output; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* ---------------------------------------------------------------------------- | ||
Function: CBA_statemachine_fnc_addState | ||
|
||
Description: | ||
Adds a state to a state machine. | ||
|
||
Parameters: | ||
_stateMachine - a state machine <LOCATION> | ||
_onState - code that is executed when state is active (frequency | ||
depends on amount of objects active in state machine) | ||
<CODE> | ||
(Default: {}) | ||
_onStateEntered - code that is executed once when state was entered, after | ||
onTransition (also once for the intial state) <CODE> | ||
(Default: {}) | ||
_onStateLeaving - code that is executed once when exiting state, before | ||
onTransition <CODE> | ||
(Default: {}) | ||
_name - unique state name <STRING> | ||
(Default: "stateX" with X being a unique number) | ||
|
||
Returns: | ||
_name - unique state name or empty string on error <STRING> | ||
|
||
Examples: | ||
(begin example) | ||
_name = [_stateMachine, {}] call CBA_statemachine_fnc_addState; | ||
(end) | ||
|
||
Author: | ||
BaerMitUmlaut | ||
---------------------------------------------------------------------------- */ | ||
#include "script_component.hpp" | ||
SCRIPT(addState); | ||
params [ | ||
["_stateMachine", locationNull, [locationNull]], | ||
["_onState", {}, [{}]], | ||
["_onStateEntered", {}, [{}]], | ||
["_onStateLeaving", {}, [{}]], | ||
["_name", "", [""]] | ||
]; | ||
|
||
private _states = _stateMachine getVariable QGVAR(states); | ||
|
||
if (isNull _stateMachine || {_name in _states}) exitWith {""}; | ||
|
||
// Autogenerate unique name | ||
if (_name == "") then { | ||
private _nextUniqueID = _stateMachine getVariable QGVAR(nextUniqueStateID); | ||
_name = "state" + str _nextUniqueID; | ||
_stateMachine setVariable [QGVAR(nextUniqueStateID), _nextUniqueID + 1]; | ||
}; | ||
|
||
_states pushBack _name; | ||
_stateMachine setVariable [QGVAR(states), _states]; | ||
_stateMachine setVariable [ONSTATE(_name), _onState]; | ||
_stateMachine setVariable [ONSTATEENTERED(_name), _onStateEntered]; | ||
_stateMachine setVariable [ONSTATELEAVING(_name), _onStateLeaving]; | ||
_stateMachine setVariable [TRANSITIONS(_name), []]; | ||
|
||
// First state added is always the intial state | ||
if (isNil {_stateMachine getVariable QGVAR(initialState)}) then { | ||
_stateMachine setVariable [QGVAR(initialState), _name]; | ||
}; | ||
|
||
_name |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* ---------------------------------------------------------------------------- | ||
Function: CBA_statemachine_fnc_addTransition | ||
|
||
Description: | ||
Creates a transition between two states. | ||
|
||
Parameters: | ||
_stateMachine - a state machine <LOCATION> | ||
_originalState - state the transition origins from <STRING> | ||
_targetState - state the transition goes to <STRING> | ||
_condition - condition under which the transition will happen <CODE> | ||
_onTransition - code that gets executed once transition happens <CODE> | ||
(Default: {}) | ||
_name - name for this specific transition <STRING> | ||
(Default: "NONAME") | ||
|
||
Returns: | ||
_wasCreated - check if the transition was created <BOOL> | ||
|
||
Examples: | ||
(begin example) | ||
[_stateMachine, "initial", "end", {true}, { | ||
systemChat format [ | ||
"%1 transitioned from %2 to %3 via %4.", | ||
_this, _thisOrigin, _thisTarget, _thisTransition | ||
]; | ||
}, "dummyTransition"] call CBA_statemachine_fnc_addTransition; | ||
(end) | ||
|
||
Author: | ||
BaerMitUmlaut | ||
---------------------------------------------------------------------------- */ | ||
#include "script_component.hpp" | ||
SCRIPT(addTransition); | ||
params [ | ||
["_stateMachine", locationNull, [locationNull]], | ||
["_originalState", "", [""]], | ||
["_targetState", "", [""]], | ||
["_condition", {}, [{}]], | ||
["_onTransition", {}, [{}]], | ||
["_name", "NONAME", [""]] | ||
]; | ||
|
||
private _states = _stateMachine getVariable QGVAR(states); | ||
|
||
if (isNull _stateMachine | ||
|| {!(_originalState in _states)} | ||
|| {!(_targetState in _states)} | ||
|| {_condition isEqualTo {}} | ||
) exitWith {false}; | ||
|
||
private _transitions = _stateMachine getVariable TRANSITIONS(_originalState); | ||
_transitions pushBack [_name, _condition, _targetState, _onTransition]; | ||
_stateMachine setVariable [TRANSITIONS(_originalState), _transitions]; | ||
|
||
true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* ---------------------------------------------------------------------------- | ||
Function: CBA_statemachine_fnc_clockwork | ||
|
||
Description: | ||
Clockwork which runs all state machines. | ||
|
||
Parameters: | ||
None | ||
|
||
Returns: | ||
Nothing | ||
|
||
Author: | ||
BaerMitUmlaut | ||
---------------------------------------------------------------------------- */ | ||
#include "script_component.hpp" | ||
SCRIPT(clockwork); | ||
|
||
{ | ||
private _stateMachine = _x; | ||
private _list = _stateMachine getVariable QGVAR(list); | ||
private _skipNull = _stateMachine getVariable QGVAR(skipNull); | ||
private _updateCode = _stateMachine getVariable QGVAR(updateCode); | ||
private _id = _stateMachine getVariable QGVAR(ID); | ||
|
||
// Skip state machine when it has no states yet | ||
if !(isNil {_stateMachine getVariable QGVAR(initialState)}) then { | ||
private _tick = _stateMachine getVariable QGVAR(tick); | ||
|
||
// Skip to next non-null element or end of list | ||
if (_skipNull) then { | ||
while {(_tick < count _list) && {isNull (_list select _tick)}} do { | ||
_tick = _tick + 1; | ||
}; | ||
}; | ||
|
||
// When the list was iterated through, jump back to start and update it | ||
if (_tick >= count _list) then { | ||
_tick = 0; | ||
if !(_updateCode isEqualTo {}) then { | ||
_list = [] call _updateCode; | ||
|
||
// Make sure list contains no null elements in case the code doesn't filter them | ||
// Else they wouldn't be skipped at this point which could cause errors | ||
if (_skipNull) then { | ||
_list = _list select {!isNull _x}; | ||
}; | ||
|
||
_stateMachine setVariable [QGVAR(list), _list]; | ||
}; | ||
}; | ||
|
||
// If the list has no items, we can stop checking this state machine | ||
// No need to set the tick when it will get reset next frame anyways | ||
if (count _list > 0) then { | ||
_stateMachine setVariable [QGVAR(tick), _tick + 1]; | ||
|
||
private _current = _list select _tick; | ||
private _thisState = _current getVariable (QGVAR(state) + str _id); | ||
|
||
if (isNil "_thisState") then { | ||
// Item is new and gets set to the intial state, onStateEntered | ||
// function of initial state gets executed as well. | ||
_thisState = _stateMachine getVariable QGVAR(initialState); | ||
_current setVariable [QGVAR(state) + str _id, _thisState]; | ||
_current call (_stateMachine getVariable ONSTATEENTERED(_thisState)); | ||
}; | ||
|
||
// onState functions can use: | ||
// _stateMachine - the state machine | ||
// _this - the current list item | ||
// _thisState - the current state | ||
_current call (_stateMachine getVariable ONSTATE(_thisState)); | ||
|
||
private _thisOrigin = _thisState; | ||
{ | ||
_x params ["_thisTransition", "_condition", "_thisTarget", "_onTransition"]; | ||
// Transition conditions, onTransition, onStateLeaving and | ||
// onStateEntered functions can use: | ||
// _stateMachine - the state machine | ||
// _this - the current list item | ||
// _thisTransition - the current transition we're in | ||
// _thisOrigin - the state we're coming from | ||
// _thisState - same as _thisOrigin | ||
// _thisTarget - the state we're transitioning to | ||
// Note: onTransition and onStateLeaving functions can change | ||
// the transition target by overwriting the passed | ||
// _thisTarget variable. | ||
// Note: onStateEntered functions of initial states won't have | ||
// some of these variables defined. | ||
if (_current call _condition) exitWith { | ||
_current call (_stateMachine getVariable ONSTATELEAVING(_thisOrigin)); | ||
_current call _onTransition; | ||
_current setVariable [QGVAR(state) + str _id, _thisTarget]; | ||
_current call (_stateMachine getVariable ONSTATEENTERED(_thisTarget)); | ||
}; | ||
} forEach (_stateMachine getVariable TRANSITIONS(_thisState)); | ||
}; | ||
}; | ||
|
||
false | ||
} count GVAR(stateMachines); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Entered" vs "Leaving". I think you should probably conyugate those two consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's on purpose, it follows an event naming convention from Microsoft. The point is that it tells you already exactly when this happens:
More info: https://msdn.microsoft.com/de-de/library/h0eyck3s(v=vs.71).aspx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very Microsoft