Skip to content
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 Machines - Add configurable number of ticks per frame #1359

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions addons/statemachine/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ PREP(createFromConfig);
PREP(delete);
PREP(getCurrentState);
PREP(manualTransition);
PREP(tick);
PREP(toString);
PREP(updateList);

Expand Down
86 changes: 2 additions & 84 deletions addons/statemachine/fnc_clockwork.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -17,89 +17,7 @@ Author:
SCRIPT(clockwork);

{
#ifdef STATEMACHINE_PERFORMANCE_COUNTERS
private _perfStartTime = diag_tickTime;
#endif
private _stateMachine = _x;
private _list = _stateMachine getVariable QGVAR(list);
private _skipNull = _stateMachine getVariable QGVAR(skipNull);
private _id = _stateMachine getVariable QGVAR(ID);
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;
};
for "_i" from 1 to (_x getVariable [QGVAR(ticksPerFrame), 1]) do {
[_x] call FUNC(tick);
};

// When the list was iterated through, jump back to start and update it
if (_tick >= count _list) then {
private _updateCode = _stateMachine getVariable QGVAR(updateCode);
_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 !(_list isEqualTo []) 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));
};

#ifdef STATEMACHINE_PERFORMANCE_COUNTERS
private _perfRunTime = diag_tickTime - _perfStartTime;
(GVAR(performanceCounters) select _id) pushBack _perfRunTime;
#endif

} forEach GVAR(stateMachines);
18 changes: 10 additions & 8 deletions addons/statemachine/fnc_create.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Author:
SCRIPT(create);
params [
["_list", [], [[], {}]],
["_skipNull", false, [true]]
["_skipNull", false, [true]],
["_ticksPerFrame", 1, [0]]
];

if (isNil QGVAR(stateMachines)) then {
Expand All @@ -52,13 +53,14 @@ if (_list isEqualType {}) then {
};

private _stateMachine = call CBA_fnc_createNamespace;
_stateMachine setVariable [QGVAR(nextUniqueStateID), 0]; // Unique ID for autogenerated state names
_stateMachine setVariable [QGVAR(tick), 0]; // List index ticker
_stateMachine setVariable [QGVAR(states), []]; // State machine states
_stateMachine setVariable [QGVAR(list), _list]; // List state machine iterates over
_stateMachine setVariable [QGVAR(skipNull), _skipNull]; // Skip items that are null
_stateMachine setVariable [QGVAR(updateCode), _updateCode]; // List update code
_stateMachine setVariable [QGVAR(ID), GVAR(nextUniqueID)]; // Unique state machine ID
_stateMachine setVariable [QGVAR(nextUniqueStateID), 0]; // Unique ID for autogenerated state names
_stateMachine setVariable [QGVAR(tick), 0]; // List index ticker
_stateMachine setVariable [QGVAR(states), []]; // State machine states
_stateMachine setVariable [QGVAR(list), _list]; // List state machine iterates over
_stateMachine setVariable [QGVAR(skipNull), _skipNull]; // Skip items that are null
_stateMachine setVariable [QGVAR(ticksPerFrame), _ticksPerFrame]; // How many ticks to run on each frame
_stateMachine setVariable [QGVAR(updateCode), _updateCode]; // List update code
_stateMachine setVariable [QGVAR(ID), GVAR(nextUniqueID)]; // Unique state machine ID
INC(GVAR(nextUniqueID));

if (isNil QGVAR(efID)) then {
Expand Down
4 changes: 3 additions & 1 deletion addons/statemachine/fnc_createFromConfig.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ if (isNull _config) exitWith {};

private _list = compile getText (_config >> "list");
private _skipNull = (getNumber (_config >> "skipNull")) > 0;
private _stateMachine = [_list, _skipNull] call FUNC(create);
private _ticksPerFrame = getNumber (_config >> "ticksPerFrame");
_ticksPerFrame = [_ticksPerFrame, 1] select (_ticksPerFrame == 0);
Fusselwurm marked this conversation as resolved.
Show resolved Hide resolved
private _stateMachine = [_list, _skipNull, _ticksPerFrame] call FUNC(create);

{
private _state = configName _x;
Expand Down
106 changes: 106 additions & 0 deletions addons/statemachine/fnc_tick.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "script_component.hpp"
/* ----------------------------------------------------------------------------
Function: CBA_statemachine_fnc_tick

Description:
Execute a single tick on a given statemachine

Parameters:
_stateMachine - statemachine created by CBA_statemachine_fnc_create

Returns:
Nothing

Author:
BaerMitUmlaut
---------------------------------------------------------------------------- */
SCRIPT(tick);

params [
["_stateMachine", objNull, [objNull, locationNull]]
];

#ifdef STATEMACHINE_PERFORMANCE_COUNTERS
private _perfStartTime = diag_tickTime;
#endif

private _list = _stateMachine getVariable QGVAR(list);
private _skipNull = _stateMachine getVariable QGVAR(skipNull);
private _id = _stateMachine getVariable QGVAR(ID);
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 {
private _updateCode = _stateMachine getVariable QGVAR(updateCode);
_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 !(_list isEqualTo []) 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));
};

#ifdef STATEMACHINE_PERFORMANCE_COUNTERS
private _perfRunTime = diag_tickTime - _perfStartTime;
(GVAR(performanceCounters) select _id) pushBack _perfRunTime;
#endif