diff --git a/state/assets/character_state_machine.json b/state/assets/character_state_machine.json index 2d80e47..d9362eb 100644 --- a/state/assets/character_state_machine.json +++ b/state/assets/character_state_machine.json @@ -201,7 +201,8 @@ "ignore": [ "RechargeBattery", "GoToBattery" - ] + ], + "restore_transfer_variables": true }, "conditions": [ { diff --git a/state/scenes/state/SMCharacter.gd b/state/scenes/state/SMCharacter.gd index b233ecb..9d10b36 100644 --- a/state/scenes/state/SMCharacter.gd +++ b/state/scenes/state/SMCharacter.gd @@ -14,8 +14,10 @@ const ROTATION_SPEED: float = 5.0 # adians per second signal waste_detected(waste) # var battery_charge: float = 100.0 -const battery_charge_drain_per_second: float = 4.0 -const battery_charge_recharge_per_second: float = 27.0 +const battery_charge_drain_per_second: float = 5.0 +const battery_charge_recharge_per_second: float = 30.0 +# carry item +var carry_item: Node2D = null func _ready() -> void: @@ -43,7 +45,11 @@ func _draw() -> void: func _process(delta: float) -> void: + battery_charge -= battery_charge_drain_per_second * delta %StateMachineInfoPanel.values["battery_charge"] = battery_charge + if carry_item: + # put the carry_item on the character + carry_item.position = position func _physics_process(delta: float) -> void: @@ -51,8 +57,6 @@ func _physics_process(delta: float) -> void: detect_waste() queue_redraw() - battery_charge -= battery_charge_drain_per_second * delta - func detect_waste() -> void: var overlapping_bodies: Array[Node2D] = detection_area.get_overlapping_bodies() diff --git a/state/scenes/state/State.gd b/state/scenes/state/State.gd index 22c8eca..141afae 100644 --- a/state/scenes/state/State.gd +++ b/state/scenes/state/State.gd @@ -17,7 +17,7 @@ func state_process(delta: float) -> void: pass -func state_exit() -> void: +func state_exit(new_state: State) -> void: pass diff --git a/state/scenes/state/StateMachine.gd b/state/scenes/state/StateMachine.gd index 1039c72..b9e9246 100644 --- a/state/scenes/state/StateMachine.gd +++ b/state/scenes/state/StateMachine.gd @@ -10,14 +10,13 @@ var state_machine_data: Dictionary = {} var received_signals: Array[Dictionary] = [] # state change parameters ("transfer") var state_transfer_variables: Dictionary = {} - var state_history: Array = [] var current_state: State: set(value): if current_state: - current_state.state_exit() - state_history.append(value) + current_state.state_exit(value) + state_history.append({"state": value, "transfer": state_transfer_variables}) current_state = value current_state.state_enter() @@ -95,16 +94,30 @@ func check_transitions() -> void: %StateMachineInfoPanel.values["transitioned to"] = transition["target"] # evaluate transfer parameters if any - state_transfer_variables = {} + if transition.has("copy_transfer_variables") and transition["copy_transfer_variables"]: + pass + else: + state_transfer_variables = {} + if "transfer" in transition and transition["transfer"]: for key in transition["transfer"]: state_transfer_variables[key] = transition_resolve_parameter(transition["transfer"][key]) - %StateMachineInfoPanel.values["transfer [" + key + "]"] = state_transfer_variables[key] # allow current state to contribute to transfer variables current_state.contribute_transfer_variables(state_transfer_variables) - print("All criteria were met for switching to state ", transition["target"], " with transfer variables ", state_transfer_variables) + print("All criteria were met for switching to state ", transition["target"]) + + if transition["target"] is Dictionary: + if transition["target"].has("restore_transfer_variables") and transition["target"]["restore_transfer_variables"]: + var transfer_variables: Dictionary = find_state_by_selector(transition["target"])["transfer"] + for key in transfer_variables: + state_transfer_variables[key] = transfer_variables[key] + + print("Transfer variables: ", state_transfer_variables) + for key in state_transfer_variables: + %StateMachineInfoPanel.values["transfer [" + str(key) + "]"] = state_transfer_variables[key] + current_state = find_state(transition["target"]) @@ -233,7 +246,7 @@ func find_detected_signal(signal_name: String) -> Dictionary: # UTILITY FUNCTIONS func find_state(name) -> State: if name is Dictionary: - return find_state_by_selector(name) + return find_state_by_selector(name)["state"] return find_state_by_name(name) @@ -242,10 +255,11 @@ func find_state_by_name(name: String) -> State: if child is State: if child.get_name() == name: return child - return null + # return the default state as a fallback + return self.get_child(state_machine_data["start"]["state"]) -func find_state_by_selector(name: Dictionary) -> State: +func find_state_by_selector(name: Dictionary) -> Dictionary: # { # "type": "history_first", # "ignore": [ @@ -256,10 +270,12 @@ func find_state_by_selector(name: Dictionary) -> State: if name["type"] == "history_first": var index: int = state_history.size() - 1 while index >= 0: - if not state_history[index].get_name() in name["ignore"]: + if not state_history[index]["state"].get_name() in name["ignore"]: return state_history[index] index -= 1 - return null + + # return the default state as a fallback + return {"state": self.get_child(state_machine_data["start"]["state"]), "transfer": {}} func was_signal_received(signal_name: String) -> bool: diff --git a/state/scenes/state/state_ThrowTrashAway.gd b/state/scenes/state/state_ThrowTrashAway.gd index 083ec67..a14d6d1 100644 --- a/state/scenes/state/state_ThrowTrashAway.gd +++ b/state/scenes/state/state_ThrowTrashAway.gd @@ -6,20 +6,23 @@ var carry_trash: Node2D = null func state_enter(): - carry_trash = state_machine.state_transfer_variables["waste"] - # disable it's collision to avoid it from colliding with the character - carry_trash.get_node("CollisionShape2D").disabled = true + if state_machine.state_transfer_variables.has("waste"): + carry_trash = state_machine.state_transfer_variables["waste"] + character.carry_item = carry_trash + # disable it's collision to avoid it from colliding with the character + carry_trash.get_node("CollisionShape2D").disabled = true + print("Carry ", carry_trash, " to ", trash_bin.position) func state_process(delta: float) -> void: - # put the trash on the character - carry_trash.position = character.position # carry the trash to the trash bin var target_position: Vector2 = trash_bin.position character.move_towards(target_position, delta) character.move_and_slide() -func state_exit() -> void: - carry_trash.queue_free() +func state_exit(new_state: State) -> void: + if new_state.get_name() != "GoToBattery": + character.carry_item = null + carry_trash.queue_free() diff --git a/state/scenes/state/visualization/tree_visualizer.gd b/state/scenes/state/visualization/tree_visualizer.gd index 8da84c8..d8a0a47 100644 --- a/state/scenes/state/visualization/tree_visualizer.gd +++ b/state/scenes/state/visualization/tree_visualizer.gd @@ -59,12 +59,16 @@ func build_tree(): # Set up labels (input and output) populate_node_labels(graph_node, state_name, state_data, all_node_names_to_nodes) + # Arrange nodes + var idx: int = 0 + for graph_node in all_nodes: @warning_ignore("integer_division") var y_pos: int = randi() % (y_spacing * 2) - y_spacing - var x_pos: int = x_spacing * i + var x_pos: int = x_spacing * idx y_pos += randi() % 200 - 100 graph_node.position_offset = Vector2(x_pos, y_pos) node_positions.append(graph_node) + idx += 1 func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void: @@ -82,7 +86,10 @@ func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dicti if not transition.has("target"): continue + if not transition['target'] is String: + if transition['target'] is Dictionary: + populate_node_labels_state_dict(node, state_name, node_names_to_nodes, transition, port_out) continue var target_state_name: String = transition['target'] @@ -100,3 +107,39 @@ func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dicti ) print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in]) + +func populate_node_labels_state_dict(node: DTreeNode, state_name: String, node_names_to_nodes: Dictionary, transition: Dictionary, port_out: Vector3i) -> void: + # { + # "type": "history_first", + # "ignore": [ + # "RechargeBattery", + # "GoToBattery" + # ], + # "restore_transfer_variables": true + # } + # display a new node for the target state with it's information + var target_state_name: String = node.get_name() + "_" + transition['target']['type'] + var target_node: DTreeNode = null + if node_names_to_nodes.has(target_state_name): + target_node = node_names_to_nodes[target_state_name] + else: + target_node = D_TREE_NODE.instantiate() + graph_edit.add_child(target_node) + target_node.title = transition['target']['type'] + all_nodes.append(target_node) + all_node_names_to_nodes[target_state_name] = target_node + + # add the "ignore" states as input ports + if transition['target'].has("ignore"): + target_node.add_label("ignore:", false, false) + for ignore_state in transition['target']['ignore']: + target_node.add_label("- %s" % [ignore_state], false, false) + + var port_in: Vector3i = target_node.add_label("%s" % [state_name], true, false) + + # connect the nodes + graph_edit.connect_node( + node.get_name(), port_out.z, + target_node.get_name(), port_in.y + ) + print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in]) \ No newline at end of file