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

Add timeout conditions #41

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
tool
extends HBoxContainer

onready var timeout_edit = $Timeout
onready var remove = $Remove

var undo_redo

var condition setget set_condition
var _old_timeout = 0.0

func _ready():
timeout_edit.connect("value_changed", self, "_on_timeout_value_changed")
timeout_edit.connect("focus_entered", self, "_on_timeout_focus_entered")
timeout_edit.connect("focus_exited", self, "_on_timeout_focus_exited")
set_process_input(false)

func _input(event):
if event is InputEventMouseButton:
if event.pressed:
if get_focus_owner() == timeout_edit:
var local_event = timeout_edit.make_input_local(event)
if not timeout_edit.get_rect().has_point(local_event.position):
timeout_edit.release_focus()

func set_timeout(v):
if condition.timeout != v:
condition.timeout = v
timeout_edit.value = v

func change_timeout_action(from, to):
if from == to:
return
undo_redo.create_action("Change Condition Timeout")
undo_redo.add_do_method(self, "set_timeout", to)
undo_redo.add_undo_method(self, "set_timeout", from)
undo_redo.commit_action()

func _on_timeout_value_changed(timeout):
change_timeout_action(_old_timeout, timeout)
timeout_edit.release_focus()

func _on_timeout_focus_entered():
set_process_input(true)
_old_timeout = timeout_edit.value

func _on_timeout_focus_exited():
set_process_input(false)
change_timeout_action(_old_timeout, timeout_edit.value)

func _on_condition_changed(new_condition):
if new_condition:
timeout_edit.value = new_condition.timeout

func set_condition(c):
if condition != c:
condition = c
_on_condition_changed(c)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[gd_scene load_steps=3 format=2]

[ext_resource path="res://addons/imjp94.yafsm/scenes/condition_editors/TimeoutConditionEditor.gd" type="Script" id=1]
[ext_resource path="res://addons/imjp94.yafsm/assets/icons/close-white-18dp.svg" type="Texture" id=2]

[node name="TimeoutConditionEditor" type="HBoxContainer"]
margin_right = 58.0
margin_bottom = 24.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}

[node name="Label" type="Label" parent="."]
margin_top = 5.0
margin_right = 53.0
margin_bottom = 19.0
text = "Timeout"

[node name="Timeout" type="SpinBox" parent="."]
margin_left = 57.0
margin_right = 131.0
margin_bottom = 24.0
size_flags_horizontal = 3
step = 0.01
allow_greater = true
align = 2
suffix = "s"
__meta__ = {
"_edit_use_anchors_": false
}

[node name="Remove" type="Button" parent="."]
margin_left = 135.0
margin_right = 165.0
margin_bottom = 24.0
size_flags_horizontal = 9
icon = ExtResource( 2 )
flat = true
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ const BooleanCondition = preload("../../src/conditions/BooleanCondition.gd")
const IntegerCondition = preload("../../src/conditions/IntegerCondition.gd")
const FloatCondition = preload("../../src/conditions/FloatCondition.gd")
const StringCondition = preload("../../src/conditions/StringCondition.gd")
const TimeoutCondition = preload("../../src/conditions/TimeoutCondition.gd")
const ConditionEditor = preload("../condition_editors/ConditionEditor.tscn")
const BoolConditionEditor = preload("../condition_editors/BoolConditionEditor.tscn")
const IntegerConditionEditor = preload("../condition_editors/IntegerConditionEditor.tscn")
const FloatConditionEditor = preload("../condition_editors/FloatConditionEditor.tscn")
const StringConditionEditor = preload("../condition_editors/StringConditionEditor.tscn")
const TimeoutConditionEditor = preload("../condition_editors/TimeoutConditionEditor.tscn")

onready var header = $HeaderContainer/Header
onready var title = $HeaderContainer/Header/Title
Expand Down Expand Up @@ -72,6 +74,8 @@ func _on_add_popup_menu_index_pressed(index):
condition = FloatCondition.new()
4: # String
condition = StringCondition.new()
5: # String
condition = TimeoutCondition.new()
_:
push_error("Unexpected index(%d) from PopupMenu" % index)
var editor = create_condition_editor(condition)
Expand Down Expand Up @@ -148,6 +152,8 @@ func create_condition_editor(condition):
editor = FloatConditionEditor.instance()
elif condition is StringCondition:
editor = StringConditionEditor.instance()
elif condition is TimeoutCondition:
editor = TimeoutConditionEditor.instance()
else:
editor = ConditionEditor.instance()
return editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ flat = true
[node name="PopupMenu" type="PopupMenu" parent="HeaderContainer/Header/HBoxContainer/Add"]
margin_right = 20.0
margin_bottom = 20.0
items = [ "Trigger", null, 0, false, false, 0, 0, null, "", false, "Boolean", null, 0, false, false, 1, 0, null, "", false, "Integer", null, 0, false, false, 2, 0, null, "", false, "Float", null, 0, false, false, 3, 0, null, "", false, "String", null, 0, false, false, 4, 0, null, "", false ]
items = [ "Trigger", null, 0, false, false, 0, 0, null, "", false, "Boolean", null, 0, false, false, 1, 0, null, "", false, "Integer", null, 0, false, false, 2, 0, null, "", false, "Float", null, 0, false, false, 3, 0, null, "", false, "String", null, 0, false, false, 4, 0, null, "", false, "Timeout", null, 0, false, false, 5, 0, null, "", false ]
__meta__ = {
"_edit_use_anchors_": false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func update_label():
var override_template_var = _template_var.get(condition.name)
if override_template_var:
label.text = label.text.format(override_template_var)
elif "timeout" in condition:
label.text = condition.display_string()
else:
label.text = condition.name
update()
Expand Down
5 changes: 4 additions & 1 deletion addons/imjp94.yafsm/src/StateMachinePlayer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var _local_parameters
var _is_update_locked = true
var _was_transited = false # If last transition was successful
var _is_param_edited = false
var _time_in_state = 0.0


func _init():
Expand Down Expand Up @@ -83,7 +84,7 @@ func _transit():

var from = get_current()
var local_params = _local_parameters.get(path_backward(from), {})
var next_state = state_machine.transit(get_current(), _parameters, local_params)
var next_state = state_machine.transit(get_current(), _parameters, local_params, _time_in_state)
if next_state:
if stack.has(next_state):
reset(stack.find(next_state))
Expand Down Expand Up @@ -116,6 +117,7 @@ func _on_state_changed(from, to):
clear_param(state, false) # Clearing params internally, do not update
emit_signal("exited", state)

_time_in_state = 0.0
emit_signal("transited", from, to)

# Called internally if process_mode is PHYSICS/IDLE to unlock update()
Expand Down Expand Up @@ -194,6 +196,7 @@ func update(delta=get_physics_process_delta_time()):
if process_mode != ProcessMode.MANUAL:
assert(not _is_update_locked, "Attempting to update manually with ProcessMode.%s" % ProcessMode.keys()[process_mode])

_time_in_state += delta
_transit()
var current_state = get_current()
_on_updated(current_state, delta)
Expand Down
18 changes: 18 additions & 0 deletions addons/imjp94.yafsm/src/conditions/TimeoutCondition.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tool
extends "Condition.gd"

signal timeout_changed(new_timeout)

export(float) var timeout setget set_timeout

func set_timeout(t):
if not is_equal_approx(timeout, t):
timeout = t
emit_signal("timeout_changed", t)
emit_signal("display_string_changed", display_string())

func display_string():
return "Timeout: %.2fs" % timeout

func has_timed_out(t):
return t >= timeout
6 changes: 3 additions & 3 deletions addons/imjp94.yafsm/src/states/StateMachine.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func _init(p_name="", p_transitions={}, p_states={}):
states = p_states

# Attempt to transit with global/local parameters, where local_params override params
func transit(current_state, params={}, local_params={}):
func transit(current_state, params={}, local_params={}, time_in_state=0.0):
var nested_states = current_state.split("/")
var is_nested = nested_states.size() > 1
var end_state_machine = self
Expand All @@ -40,7 +40,7 @@ func transit(current_state, params={}, local_params={}):
end_state_machine_parent_path = join_path(end_state_machine_parent_path, [nested_states[i]])
var end_state_machine_parent = get_state(end_state_machine_parent_path)
var normalized_current_state = end_state_machine.name
var next_state = end_state_machine_parent.transit(normalized_current_state, params)
var next_state = end_state_machine_parent.transit(normalized_current_state, params, time_in_state)
if next_state:
# Construct next state into absolute path
next_state = join_path(end_state_machine_parent_path, [next_state])
Expand All @@ -53,7 +53,7 @@ func transit(current_state, params={}, local_params={}):
from_transitions_array.sort_custom(Transition, "sort")

for transition in from_transitions_array:
var next_state = transition.transit(params, local_params)
var next_state = transition.transit(params, local_params, time_in_state)
if next_state:
if "states" in end_state_machine.states[next_state]:
# Next state is a StateMachine, return entry state of the state machine in absolute path
Expand Down
27 changes: 15 additions & 12 deletions addons/imjp94.yafsm/src/transitions/Transition.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,24 @@ func _init(p_from="", p_to="", p_conditions={}):
conditions = p_conditions

# Attempt to transit with parameters given, return name of next state if succeeded else null
func transit(params={}, local_params={}):
func transit(params={}, local_params={}, time_in_state=0.0):
var can_transit = conditions.size() > 0
for condition in conditions.values():
var has_param = params.has(condition.name)
var has_local_param = local_params.has(condition.name)
if has_param or has_local_param:
# local_params > params
var value = local_params.get(condition.name) if has_local_param else params.get(condition.name)
if value == null: # null value is treated as trigger
can_transit = can_transit and true
else:
if "value" in condition:
can_transit = can_transit and condition.compare(value)
if condition.has_method("has_timed_out"):
can_transit = can_transit and condition.has_timed_out(time_in_state)
else:
can_transit = false
var has_param = params.has(condition.name)
var has_local_param = local_params.has(condition.name)
if has_param or has_local_param:
# local_params > params
var value = local_params.get(condition.name) if has_local_param else params.get(condition.name)
if value == null: # null value is treated as trigger
can_transit = can_transit and true
else:
if "value" in condition:
can_transit = can_transit and condition.compare(value)
else:
can_transit = false
if can_transit or conditions.size() == 0:
return to
return null
Expand Down