mirror of
https://github.com/tonytins/citylimits
synced 2025-06-26 01:44:42 -04:00
Major clean up and reorganization
- Upgraded to Godot 4 - Just remembered the basic principles are based on a tile editor, and dramatically simplified from there. Derp. - New state machine and license display add-ons. - Re-licensed under the GPL because Micropolis' assets aren't under a separate one.
This commit is contained in:
parent
55ed76c914
commit
c980445340
337 changed files with 5129 additions and 7661 deletions
21
addons/simple-state/LICENSE.md
Normal file
21
addons/simple-state/LICENSE.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Addons By Aura
|
||||
|
||||
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.
|
47
addons/simple-state/classes/animation_state.gd
Normal file
47
addons/simple-state/classes/animation_state.gd
Normal file
|
@ -0,0 +1,47 @@
|
|||
@icon("../icons/animation_state.png")
|
||||
class_name AnimationState
|
||||
extends State
|
||||
|
||||
## Plays an animation from the linked [member animation_player].
|
||||
## The name of the animation to be played comes from the name of the node.
|
||||
|
||||
## Emitted when the animation started by this state has finished playing.
|
||||
signal animation_finished
|
||||
|
||||
|
||||
@export_range(0, 20, 1, "or_greater")
|
||||
## How many times to play before emitting [signal State.choose_new_substate_requested].
|
||||
## [b]If set to zero, it will go forever.[/b]
|
||||
var loops := 0
|
||||
|
||||
|
||||
var _loops_left := 0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_meta(&"description", "Plays the named animation from the linked AnimationPlayer.")
|
||||
|
||||
|
||||
func _on_animation_finished(animation_name: StringName) -> void:
|
||||
if animation_name != name: return
|
||||
if loops == 0:
|
||||
animation_player.play(name)
|
||||
elif _loops_left <= 0:
|
||||
choose_new_substate_requested.emit()
|
||||
else:
|
||||
_loops_left -= 1
|
||||
animation_player.play(name)
|
||||
|
||||
|
||||
func enter(set_target: Node, set_animation_player: AnimationPlayer, set_debug_mode := false) -> void:
|
||||
super(set_target, set_animation_player, set_debug_mode)
|
||||
assert(animation_player != null, "AnimationPlayer must be set, either directly or by an ancestor.")
|
||||
animation_player.animation_finished.connect(_on_animation_finished)
|
||||
_loops_left = loops - 1
|
||||
animation_player.play(name)
|
||||
|
||||
|
||||
func exit() -> void:
|
||||
super()
|
||||
animation_player.animation_finished.disconnect(_on_animation_finished)
|
||||
animation_player.stop()
|
139
addons/simple-state/classes/debugger.gd
Normal file
139
addons/simple-state/classes/debugger.gd
Normal file
|
@ -0,0 +1,139 @@
|
|||
@icon("../icons/state_machine_debugger.png")
|
||||
class_name StateMachineDebugger
|
||||
extends Tree
|
||||
|
||||
|
||||
## Displays an interactive state tree.
|
||||
# This source code is a mess, I'm trying to make it less so.
|
||||
|
||||
|
||||
@export
|
||||
## Root state machine to reference.
|
||||
var state_machine_root : State:
|
||||
set(value):
|
||||
state_machine_root = value
|
||||
_setup_tree()
|
||||
|
||||
@export
|
||||
## What color to make the item when a state is active.
|
||||
var active_color := Color.FOREST_GREEN
|
||||
|
||||
@export
|
||||
## Forcefully switch states by double-clicking them.
|
||||
## Due to its nature, it has the potential to be destructive
|
||||
## and/ or not behave completely how one might expect.
|
||||
var allow_state_switching := false:
|
||||
set(value):
|
||||
allow_state_switching = value
|
||||
if allow_state_switching:
|
||||
item_activated.connect(_on_item_activated)
|
||||
else:
|
||||
item_activated.disconnect(_on_item_activated)
|
||||
|
||||
|
||||
@export_group("Signals", "signal_")
|
||||
@export
|
||||
## Show when a state emits a relevant signal.
|
||||
var signal_show := false:
|
||||
set(value):
|
||||
signal_show = value
|
||||
|
||||
if state_machine_root == null:
|
||||
return
|
||||
|
||||
if signal_show:
|
||||
connect_signals()
|
||||
else:
|
||||
disconnect_signals()
|
||||
|
||||
@export
|
||||
## Which signals to connect to on each state, as long as they exist.
|
||||
var signal_connections : Array[StringName] = [
|
||||
&"entered",
|
||||
&"exited",
|
||||
&"choose_new_substate_requested",
|
||||
&"animation_finished",
|
||||
]
|
||||
|
||||
@export
|
||||
## Delay before hiding signal.
|
||||
var signal_hide_delay := 1.0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
columns = 2
|
||||
|
||||
|
||||
func change_state_by_path(path: NodePath) -> void:
|
||||
if not state_machine_root.has_node(path):
|
||||
return
|
||||
var state := state_machine_root
|
||||
for i in path.get_name_count():
|
||||
var part := path.get_name(i)
|
||||
state = await state.change_state_name(part)
|
||||
|
||||
|
||||
func connect_signals(state := state_machine_root) -> void:
|
||||
if not state.has_meta(&"tree_item"):
|
||||
return
|
||||
for signal_name in signal_connections:
|
||||
if state.has_signal(signal_name) and not \
|
||||
state.is_connected(signal_name, _on_state_signal):
|
||||
state.connect(signal_name, _on_state_signal.bind(signal_name, state.get_meta(&"tree_item")))
|
||||
for child in(state.get_children() as Array[State]):
|
||||
connect_signals(child)
|
||||
|
||||
|
||||
func disconnect_signals(state := state_machine_root) -> void:
|
||||
for signal_name in signal_connections:
|
||||
if state.has_signal(signal_name) and \
|
||||
state.is_connected(signal_name, _on_state_signal):
|
||||
state.disconnect(signal_name, _on_state_signal)
|
||||
for child in (state.get_children() as Array[State]):
|
||||
disconnect_signals(child)
|
||||
|
||||
|
||||
func _setup_tree(state := state_machine_root, parent_item: TreeItem = null) -> void:
|
||||
if state == state_machine_root:
|
||||
if get_root() != null:
|
||||
disconnect_signals()
|
||||
clear()
|
||||
if state_machine_root == null:
|
||||
return
|
||||
# state.print_tree_pretty()
|
||||
|
||||
# TODO: add icons
|
||||
var item := create_item(parent_item)
|
||||
item.set_text(0, state.name)
|
||||
item.set_metadata(0, state)
|
||||
state.set_meta(&"tree_item", item)
|
||||
connect_signals(state)
|
||||
|
||||
for child in (state.get_children() as Array[State]):
|
||||
_setup_tree(child, item)
|
||||
|
||||
|
||||
func _on_item_activated() -> void:
|
||||
change_state_by_path(state_machine_root.get_path_to(
|
||||
get_selected().get_metadata(0) as State))
|
||||
|
||||
|
||||
func _on_state_signal(signal_name: StringName, state_item: TreeItem) -> void:
|
||||
match signal_name:
|
||||
&"entered":
|
||||
for i in columns:
|
||||
state_item.set_custom_color(i, active_color)
|
||||
&"exited":
|
||||
for i in columns:
|
||||
state_item.clear_custom_color(i)
|
||||
|
||||
if not signal_show:
|
||||
return
|
||||
|
||||
state_item.set_text(1, signal_name)
|
||||
var timer := state_item.get_metadata(1) as SceneTreeTimer
|
||||
if timer != null:
|
||||
timer.timeout.disconnect(state_item.set_text)
|
||||
timer = get_tree().create_timer(signal_hide_delay)
|
||||
timer.timeout.connect(state_item.set_text.bind(1, ""))
|
||||
state_item.set_metadata(1, timer)
|
34
addons/simple-state/classes/random_state.gd
Normal file
34
addons/simple-state/classes/random_state.gd
Normal file
|
@ -0,0 +1,34 @@
|
|||
@icon("../icons/random_state.png")
|
||||
class_name RandomState
|
||||
extends State
|
||||
|
||||
## Activates a random one of its substates.
|
||||
## Useful in conjuction with [AnimationState] for random idles.
|
||||
|
||||
|
||||
@export
|
||||
## When one of its children asks for a state change,
|
||||
## instead of picking another one itself, it defers that choice to its parent.
|
||||
## Allows for nested random states for finer control over flow and probability.
|
||||
var defer_choice := false
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_meta(&"description", "Pseudo-randomly picks a state to start.")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
randomize()
|
||||
super()
|
||||
|
||||
|
||||
# You can define which state is picked automatically (like on enter).
|
||||
# If you would like to call it yourself, use the public version (choose_substate).
|
||||
func _choose_substate() -> State:
|
||||
if get_child_count() == 0:
|
||||
return null
|
||||
if defer_choice and _active_substate != null:
|
||||
choose_new_substate_requested.emit()
|
||||
return null
|
||||
return get_child(randi() % get_child_count()) as State
|
||||
|
43
addons/simple-state/classes/sequence_state.gd
Normal file
43
addons/simple-state/classes/sequence_state.gd
Normal file
|
@ -0,0 +1,43 @@
|
|||
@icon("../icons/sequence_state.png")
|
||||
class_name SequenceState
|
||||
extends State
|
||||
|
||||
|
||||
## Executes its children in order, one after the other. Like an [Array] in [State] form!
|
||||
|
||||
|
||||
@export_range(0, 20, 1, "or_greater")
|
||||
## How many times the sequence should be looped through before emitting [signal State.choose_new_substate_requested].
|
||||
## [b]If set to zero, it will go forever.[/b]
|
||||
var loops := 1
|
||||
|
||||
|
||||
var _loops_left := 0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_meta(&"description", "Starts its children one after the other in order, \
|
||||
waiting for each one to be done before starting the next.")
|
||||
|
||||
|
||||
# You can define which state is picked automatically (like on [method enter]).
|
||||
# If you would like to call it yourself, use the public version ([method choose_substate]).
|
||||
func _choose_substate() -> State:
|
||||
if _active_substate == null:
|
||||
return get_child(0) as State if get_child_count() > 0 else null
|
||||
|
||||
if _active_substate.get_index() == get_child_count() - 1:
|
||||
if loops == 0:
|
||||
return get_child(0) as State
|
||||
elif _loops_left == 0:
|
||||
choose_new_substate_requested.emit()
|
||||
return null
|
||||
else:
|
||||
_loops_left -= 1
|
||||
return get_child(0) as State
|
||||
return get_child(_active_substate.get_index() + 1) as State
|
||||
|
||||
|
||||
func enter(set_target: Node, set_animation_player: AnimationPlayer, set_debug := false) -> void:
|
||||
super(set_target, set_animation_player, set_debug)
|
||||
_loops_left = loops - 1
|
291
addons/simple-state/classes/state.gd
Normal file
291
addons/simple-state/classes/state.gd
Normal file
|
@ -0,0 +1,291 @@
|
|||
@icon("../icons/state.png")
|
||||
class_name State
|
||||
extends Node
|
||||
|
||||
## The bare, basic state. Use it if you want total control over the state-flow.
|
||||
##
|
||||
## Properties marked as [b](inherited)[/b] are passed to substates,
|
||||
## meaning you don't have to set it on each individual state, only the root.
|
||||
## You can override it of course, and that will be passed to all of [i]its[/i] children.
|
||||
|
||||
|
||||
## Emitted between [method _enter] and [method _after_enter].
|
||||
signal entered
|
||||
|
||||
## Emitted after [method _exit].
|
||||
signal exited
|
||||
|
||||
## Emitted between [method _update] and [method _after_update]
|
||||
signal updated
|
||||
|
||||
## Switched active substates.
|
||||
signal active_substate_changed(new: State, old: State)
|
||||
|
||||
## A request for the parent to pick a new substate to activate.
|
||||
## Mainly used by children of [RandomState], such as an [AnimationState].
|
||||
signal choose_new_substate_requested
|
||||
|
||||
|
||||
## Active or not.
|
||||
enum Status {
|
||||
INACTIVE, ## Inactive
|
||||
ACTIVE, ## Active
|
||||
}
|
||||
|
||||
|
||||
@export
|
||||
## The node that the states will act upon. [b](inherited)[/b]
|
||||
## Doesn't actually get used in the addon scripts, it's just
|
||||
## included for your convenience when scripting your own behaviour.
|
||||
var target: Node:
|
||||
set(value):
|
||||
target = value
|
||||
if _active_substate != null:
|
||||
_active_substate.target = target
|
||||
|
||||
@export
|
||||
## Where to play animations from. [b](inherited)[/b]
|
||||
var animation_player: AnimationPlayer
|
||||
|
||||
@export_range(0, 120, 1, "or_greater")
|
||||
## How many seconds the state should be active before emitting [signal choose_new_substate_requested].
|
||||
## [b]If set to zero, it will go forever.[/b]
|
||||
var timer := 0.0
|
||||
|
||||
@export
|
||||
## Whether to force-restart the chosen substate in the callback for [signal choose_new_substate_requested] if it was already active.
|
||||
var force := true
|
||||
|
||||
@export
|
||||
## The state will not be activated under any circumstances.
|
||||
var disabled := false:
|
||||
set(value):
|
||||
disabled = value
|
||||
var root := is_root()
|
||||
if root and not disabled:
|
||||
enter(target, animation_player, debug_mode)
|
||||
elif status == Status.ACTIVE:
|
||||
exit()
|
||||
|
||||
@export
|
||||
## Print a message avery time there is a state change. [b](inherited)[/b]
|
||||
var debug_mode := false:
|
||||
set(value):
|
||||
debug_mode = value
|
||||
if _active_substate != null:
|
||||
_active_substate.debug_mode = debug_mode
|
||||
|
||||
## The status of this state, ie. whether it's running or not.
|
||||
var status := Status.INACTIVE
|
||||
|
||||
|
||||
# The substate that is currently active, if any.
|
||||
var _active_substate: State:
|
||||
set(value):
|
||||
if _active_substate != null:
|
||||
_active_substate.choose_new_substate_requested.disconnect(_on_choose_new_substate_requested)
|
||||
active_substate_changed.emit(value, _active_substate)
|
||||
_active_substate = value
|
||||
if _active_substate != null:
|
||||
_active_substate.choose_new_substate_requested.connect(_on_choose_new_substate_requested)
|
||||
|
||||
# If a timer is set, the object will be stored here.
|
||||
var _timer_object: SceneTreeTimer
|
||||
|
||||
|
||||
#########################
|
||||
### VIRTUAL METHODS ###
|
||||
#########################
|
||||
func _init() -> void:
|
||||
set_physics_process(false)
|
||||
set_meta(&"description", "A bare, basic state - will only ever automatically start its first child.")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
for child in get_children():
|
||||
assert(child is State, "A State should not have any children that are not other States.")
|
||||
if is_root() and not disabled:
|
||||
enter(target, animation_player, debug_mode)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if status == Status.INACTIVE:
|
||||
set_physics_process(false)
|
||||
return
|
||||
update(delta)
|
||||
|
||||
|
||||
## [b][parents, then children][/b] Called when the state is activated.
|
||||
func _enter() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## [b][children, then parents][/b] Called after the state is activated.
|
||||
func _after_enter() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## [b][parents, then children][/b] Called every physics frame (only when the state is active, of course).
|
||||
func _update(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## [b][children, then parents][/b] Called at the end of every physics frame.
|
||||
func _after_update(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## [b][parents, then children][/b] Called before the state is deactivated.
|
||||
func _before_exit() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## [b][children, then parents][/b] Called when the state is deactivated.
|
||||
func _exit() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## You can define which state is picked automatically (like on [method enter]).
|
||||
## Return `null` to not change substate at all.
|
||||
## If you would like to call it yourself, use the public version ([method choose_substate]).
|
||||
func _choose_substate() -> State:
|
||||
return get_child(0) as State if get_child_count() > 0 else null
|
||||
|
||||
|
||||
########################
|
||||
### PUBLIC METHODS ###
|
||||
########################
|
||||
|
||||
## Switch to the specified substate by name. It is just a shortcut to [method change_state_node].
|
||||
func change_state_name(name: String, force := false) -> State:
|
||||
return await change_state_node(get_node_or_null(name) as State, force)
|
||||
|
||||
|
||||
## Switch to the specified substate by node. If it is not a direct child, nothing will happen.
|
||||
## If `force`, it will start a state again even if it's already running.
|
||||
## It waits for the next [signal updated] to make sure it's not
|
||||
## switching all over the place in one tick.
|
||||
func change_state_node(node: State, force := false) -> State:
|
||||
await updated
|
||||
if (
|
||||
node == null
|
||||
or node.disabled
|
||||
or (node.status != Status.INACTIVE and not force)
|
||||
or node.get_parent() != self
|
||||
):
|
||||
return node
|
||||
|
||||
var old := _active_substate
|
||||
_active_substate = node
|
||||
if old != null:
|
||||
old.exit()
|
||||
_active_substate.enter(target, animation_player, debug_mode)
|
||||
|
||||
if debug_mode:
|
||||
print(
|
||||
("FORCE " if force else "") +
|
||||
"STATE: " +
|
||||
str(get_root().get_parent().get_path_to(_active_substate))
|
||||
)
|
||||
return _active_substate
|
||||
|
||||
|
||||
## Return the currently active substate, if any.
|
||||
func get_active_substate() -> State:
|
||||
return _active_substate
|
||||
|
||||
|
||||
## Public [method _choose_substate].
|
||||
func choose_substate() -> State:
|
||||
return _choose_substate()
|
||||
|
||||
|
||||
## Shortcut for `change_state_node(choose_substate())`.
|
||||
func change_to_next_substate(force := false) -> void:
|
||||
await change_state_node(choose_substate(), force)
|
||||
|
||||
|
||||
## Whether this state is the root of the state tree,
|
||||
## ie. it is the common ancestor of all the others.
|
||||
func is_root() -> bool:
|
||||
# If your parent is not a state, then you are the root.
|
||||
return not get_parent() is State
|
||||
|
||||
|
||||
## Get the root state.
|
||||
func get_root() -> State:
|
||||
var node: State = self
|
||||
while not node.is_root():
|
||||
node = node.get_parent() as State
|
||||
return node
|
||||
|
||||
|
||||
## Runs [method _enter] and [method _after_enter],
|
||||
## not a good idea to call it yourself unless you really know what you're doing.
|
||||
func enter(set_target: Node, set_animation_player: AnimationPlayer, set_debug_mode: bool) -> void:
|
||||
for child in get_children():
|
||||
assert(child is State, "A State should not have any children that are not other States.")
|
||||
|
||||
_enter()
|
||||
entered.emit()
|
||||
status = Status.ACTIVE
|
||||
if timer != 0:
|
||||
_timer_object = get_tree().create_timer(timer)
|
||||
_timer_object.timeout.connect(_on_timer_timeout)
|
||||
set_physics_process(is_root())
|
||||
|
||||
# Only set them if they're not being overridden
|
||||
if target == null:
|
||||
target = set_target
|
||||
if animation_player == null:
|
||||
animation_player = set_animation_player
|
||||
if debug_mode == false:
|
||||
debug_mode = set_debug_mode
|
||||
|
||||
change_to_next_substate()
|
||||
_after_enter()
|
||||
|
||||
|
||||
## Runs [method _update] and [method _after_update],
|
||||
## not a good idea to call it yourself unless you really know what you're doing.
|
||||
func update(delta: float) -> void:
|
||||
_update(delta)
|
||||
updated.emit()
|
||||
if _active_substate != null:
|
||||
_active_substate.update(delta)
|
||||
_after_update(delta)
|
||||
|
||||
|
||||
## Runs [method _exit] and [method _before_exit],
|
||||
## not a good idea to call it yourself unless you really know what you're doing.
|
||||
func exit() -> void:
|
||||
_before_exit()
|
||||
status = Status.INACTIVE
|
||||
if _active_substate != null:
|
||||
_active_substate.exit()
|
||||
_active_substate = null
|
||||
|
||||
if is_instance_valid(_timer_object):
|
||||
_timer_object.timeout.disconnect(_on_timer_timeout)
|
||||
_timer_object = null
|
||||
|
||||
_exit()
|
||||
exited.emit()
|
||||
set_physics_process(false)
|
||||
|
||||
|
||||
#########################
|
||||
### PRIVATE METHODS ###
|
||||
#########################
|
||||
|
||||
|
||||
#################
|
||||
### CALLBACKS ###
|
||||
#################
|
||||
|
||||
func _on_choose_new_substate_requested() -> void:
|
||||
change_to_next_substate(force)
|
||||
|
||||
|
||||
func _on_timer_timeout() -> void:
|
||||
choose_new_substate_requested.emit()
|
14
addons/simple-state/demo/demo.gd
Normal file
14
addons/simple-state/demo/demo.gd
Normal file
|
@ -0,0 +1,14 @@
|
|||
extends State
|
||||
|
||||
|
||||
const NEXT_STATE_ACTION = "demo_next_state"
|
||||
|
||||
|
||||
func _enter() -> void:
|
||||
if InputMap.has_action(NEXT_STATE_ACTION):
|
||||
return
|
||||
var input_event := InputEventKey.new()
|
||||
input_event.keycode = KEY_TAB
|
||||
|
||||
InputMap.add_action(NEXT_STATE_ACTION)
|
||||
InputMap.action_add_event(NEXT_STATE_ACTION, input_event)
|
218
addons/simple-state/demo/demo.tscn
Normal file
218
addons/simple-state/demo/demo.tscn
Normal file
|
@ -0,0 +1,218 @@
|
|||
[gd_scene load_steps=17 format=3 uid="uid://clnliyc6fmqy6"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/simple-state/classes/debugger.gd" id="1_rqf1w"]
|
||||
[ext_resource type="Script" path="res://addons/simple-state/demo/description_box.gd" id="2_gktik"]
|
||||
[ext_resource type="Script" path="res://addons/simple-state/classes/sequence_state.gd" id="3_4afa7"]
|
||||
[ext_resource type="Script" path="res://addons/simple-state/demo/demo.gd" id="3_x0hcs"]
|
||||
[ext_resource type="Script" path="res://addons/simple-state/classes/random_state.gd" id="4_wxjoe"]
|
||||
[ext_resource type="Script" path="res://addons/simple-state/demo/emit_next.gd" id="6_kxcgl"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ysqm7"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wxupu"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7gdgn"]
|
||||
draw_center = false
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
|
||||
[sub_resource type="Animation" id="Animation_5pgem"]
|
||||
resource_name = "RESET"
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_mwhj1"]
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(0, 0, 0, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_yyph0"]
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(0, 0, 1, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_xmkhy"]
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(0, 1, 0, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_4pwkk"]
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 0, 0, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_gt7hj"]
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("ColorRect:color")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 0, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_01hf1"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_5pgem"),
|
||||
"black": SubResource("Animation_mwhj1"),
|
||||
"blue": SubResource("Animation_yyph0"),
|
||||
"green": SubResource("Animation_xmkhy"),
|
||||
"red": SubResource("Animation_4pwkk"),
|
||||
"yellow": SubResource("Animation_gt7hj")
|
||||
}
|
||||
|
||||
[node name="SimpleStateDemo" type="Panel"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="StateMachineDebugger" type="Tree" parent="MarginContainer/HBoxContainer" node_paths=PackedStringArray("state_machine_root")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/relationship_line_width = 2
|
||||
theme_override_constants/draw_guides = 0
|
||||
theme_override_styles/panel = SubResource("StyleBoxEmpty_ysqm7")
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_wxupu")
|
||||
theme_override_styles/selected = SubResource("StyleBoxFlat_7gdgn")
|
||||
theme_override_styles/selected_focus = SubResource("StyleBoxFlat_7gdgn")
|
||||
select_mode = 1
|
||||
script = ExtResource("1_rqf1w")
|
||||
state_machine_root = NodePath("../../Root")
|
||||
allow_state_switching = true
|
||||
signal_show = true
|
||||
|
||||
[node name="DescriptionBox" type="Label" parent="MarginContainer/HBoxContainer" node_paths=PackedStringArray("tree")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
autowrap_mode = 3
|
||||
script = ExtResource("2_gktik")
|
||||
tree = NodePath("../StateMachineDebugger")
|
||||
show_descriptions = 2
|
||||
|
||||
[node name="InputInfo" type="Label" parent="MarginContainer/HBoxContainer/DescriptionBox"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -349.0
|
||||
offset_top = -78.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
text = "MOUSE CLICK on state: select
|
||||
DOUBLE MOUSE CLICK on state: start
|
||||
TAB: next substate (only has effect on leaves)"
|
||||
horizontal_alignment = 2
|
||||
vertical_alignment = 2
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="MarginContainer"]
|
||||
custom_minimum_size = Vector2(50, 50)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 8
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="MarginContainer"]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_01hf1")
|
||||
}
|
||||
|
||||
[node name="Root" type="Node" parent="MarginContainer" node_paths=PackedStringArray("target", "animation_player")]
|
||||
script = ExtResource("3_x0hcs")
|
||||
target = NodePath("")
|
||||
animation_player = NodePath("../AnimationPlayer")
|
||||
|
||||
[node name="SequenceState" type="Node" parent="MarginContainer/Root"]
|
||||
script = ExtResource("3_4afa7")
|
||||
|
||||
[node name="RandomState" type="Node" parent="MarginContainer/Root/SequenceState"]
|
||||
script = ExtResource("4_wxjoe")
|
||||
defer_choice = true
|
||||
|
||||
[node name="red" type="Node" parent="MarginContainer/Root/SequenceState/RandomState"]
|
||||
script = ExtResource("6_kxcgl")
|
||||
|
||||
[node name="yellow" type="Node" parent="MarginContainer/Root/SequenceState/RandomState"]
|
||||
script = ExtResource("6_kxcgl")
|
||||
|
||||
[node name="green" type="Node" parent="MarginContainer/Root/SequenceState"]
|
||||
script = ExtResource("6_kxcgl")
|
||||
|
||||
[node name="blue" type="Node" parent="MarginContainer/Root/SequenceState"]
|
||||
script = ExtResource("6_kxcgl")
|
||||
|
||||
[node name="black" type="Node" parent="MarginContainer/Root"]
|
||||
script = ExtResource("6_kxcgl")
|
||||
timer = 1.0
|
||||
metadata/description = "Will never start automatically, due to its parent being only a normal state."
|
98
addons/simple-state/demo/description_box.gd
Normal file
98
addons/simple-state/demo/description_box.gd
Normal file
|
@ -0,0 +1,98 @@
|
|||
extends Label
|
||||
|
||||
|
||||
## Mode of description box rendering.
|
||||
enum DisplayModes {
|
||||
NONE, ## Description box completely hidden.
|
||||
ACTIVE, ## Show descriptions of all active states.
|
||||
SELECTION, ## Show description of last selected state (also includes manual switches).
|
||||
}
|
||||
|
||||
|
||||
@export
|
||||
## [StateMachineDebugger] to reference.
|
||||
var tree : StateMachineDebugger:
|
||||
set(value):
|
||||
tree = value
|
||||
if show_descriptions == DisplayModes.SELECTION and \
|
||||
not tree.item_selected.is_connected(_on_tree_item_selected):
|
||||
tree.item_selected.connect(_on_tree_item_selected)
|
||||
|
||||
|
||||
@export
|
||||
## Show a description of a state.
|
||||
## Looks for a string metadata value by the name of [code]description[/code] on each state.
|
||||
var show_descriptions := DisplayModes.NONE:
|
||||
set(value):
|
||||
show_descriptions = value
|
||||
|
||||
match show_descriptions:
|
||||
DisplayModes.NONE:
|
||||
visible = false
|
||||
DisplayModes.ACTIVE:
|
||||
visible = true
|
||||
if not is_instance_valid(tree):
|
||||
return
|
||||
if tree.item_selected.is_connected(_on_tree_item_selected):
|
||||
tree.item_selected.disconnect(_on_tree_item_selected)
|
||||
DisplayModes.SELECTION:
|
||||
visible = true
|
||||
if not is_instance_valid(tree):
|
||||
return
|
||||
if not tree.item_selected.is_connected(_on_tree_item_selected):
|
||||
tree.item_selected.connect(_on_tree_item_selected)
|
||||
|
||||
|
||||
var _active_states : Array[State] = []
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
visible = show_descriptions != DisplayModes.NONE
|
||||
connect_signals()
|
||||
|
||||
|
||||
func connect_signals(state := tree.state_machine_root) -> void:
|
||||
if not state.has_meta(&"tree_item"):
|
||||
return
|
||||
for signal_name in tree.signal_connections:
|
||||
if state.has_signal(signal_name) and not \
|
||||
state.is_connected(signal_name, _on_state_signal):
|
||||
state.connect(signal_name, _on_state_signal.bind(signal_name, state))
|
||||
for child in(state.get_children() as Array[State]):
|
||||
connect_signals(child)
|
||||
|
||||
|
||||
func disconnect_signals(state := tree.state_machine_root) -> void:
|
||||
for signal_name in tree.signal_connections:
|
||||
if state.has_signal(signal_name) and \
|
||||
state.is_connected(signal_name, _on_state_signal):
|
||||
state.disconnect(signal_name, _on_state_signal)
|
||||
for child in (state.get_children() as Array[State]):
|
||||
disconnect_signals(child)
|
||||
|
||||
|
||||
func _set_description_from_active_states() -> void:
|
||||
text = ""
|
||||
for state in _active_states:
|
||||
text += ("" if state.is_root() else "\n\n") + \
|
||||
state.name as String + \
|
||||
": " + \
|
||||
state.get_meta(&"description", "") as String
|
||||
|
||||
|
||||
func _on_tree_item_selected() -> void:
|
||||
text = tree.get_selected() \
|
||||
.get_metadata(0).get_meta(&"description", "")
|
||||
|
||||
|
||||
func _on_state_signal(signal_name: StringName, state: State) -> void:
|
||||
match signal_name:
|
||||
&"entered":
|
||||
if show_descriptions == DisplayModes.ACTIVE:
|
||||
_active_states.push_back(state)
|
||||
_set_description_from_active_states()
|
||||
&"exited":
|
||||
if show_descriptions == DisplayModes.ACTIVE:
|
||||
_active_states.pop_back()
|
||||
_set_description_from_active_states()
|
6
addons/simple-state/demo/emit_next.gd
Normal file
6
addons/simple-state/demo/emit_next.gd
Normal file
|
@ -0,0 +1,6 @@
|
|||
extends AnimationState
|
||||
|
||||
|
||||
func _update(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed(get_root().NEXT_STATE_ACTION):
|
||||
choose_new_substate_requested.emit()
|
BIN
addons/simple-state/icons/animation_state.png
Normal file
BIN
addons/simple-state/icons/animation_state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
34
addons/simple-state/icons/animation_state.png.import
Normal file
34
addons/simple-state/icons/animation_state.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cna1s8hi8xc58"
|
||||
path="res://.godot/imported/animation_state.png-5f10255295e45d80e555d2885de83e22.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/animation_state.png"
|
||||
dest_files=["res://.godot/imported/animation_state.png-5f10255295e45d80e555d2885de83e22.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
2
addons/simple-state/icons/licenses/Visit Kenney.url
Normal file
2
addons/simple-state/icons/licenses/Visit Kenney.url
Normal file
|
@ -0,0 +1,2 @@
|
|||
[InternetShortcut]
|
||||
URL=http://www.kenney.nl/
|
2
addons/simple-state/icons/licenses/Visit Patreon.url
Normal file
2
addons/simple-state/icons/licenses/Visit Patreon.url
Normal file
|
@ -0,0 +1,2 @@
|
|||
[InternetShortcut]
|
||||
URL=https://www.patreon.com/kenney/
|
22
addons/simple-state/icons/licenses/board_game_icons.txt
Normal file
22
addons/simple-state/icons/licenses/board_game_icons.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
|
||||
Board Game Icons (1.0)
|
||||
|
||||
Created/distributed by Kenney (www.kenney.nl)
|
||||
Creation date: 10-01-2022
|
||||
|
||||
------------------------------
|
||||
|
||||
License: (Creative Commons Zero, CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
This content is free to use in personal, educational and commercial projects.
|
||||
Support us by crediting Kenney or www.kenney.nl (this is not mandatory)
|
||||
|
||||
------------------------------
|
||||
|
||||
Donate: http://support.kenney.nl
|
||||
Patreon: http://patreon.com/kenney/
|
||||
|
||||
Follow on Twitter for updates:
|
||||
http://twitter.com/KenneyNL
|
14
addons/simple-state/icons/licenses/game_icons+expansion.txt
Normal file
14
addons/simple-state/icons/licenses/game_icons+expansion.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
###############################################################################
|
||||
|
||||
Game icon pack by Kenney Vleugels (www.kenney.nl)
|
||||
|
||||
------------------------------
|
||||
|
||||
License (CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
You may use these graphics in personal and commercial projects.
|
||||
Credit (Kenney or www.kenney.nl) would be nice but is not mandatory.
|
||||
|
||||
###############################################################################
|
20
addons/simple-state/icons/licenses/pictogrammers.txt
Normal file
20
addons/simple-state/icons/licenses/pictogrammers.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
Pictogrammers Free License
|
||||
--------------------------
|
||||
|
||||
This icon collection is released as free, open source, and GPL friendly by
|
||||
the [Pictogrammers](http://pictogrammers.com/). You may use it
|
||||
for commercial projects, open source projects, or anything really.
|
||||
|
||||
# Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||
Some of the icons are redistributed under the Apache 2.0 license. All other
|
||||
icons are either redistributed under their respective licenses or are
|
||||
distributed under the Apache 2.0 license.
|
||||
|
||||
# Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||
All web and desktop fonts are distributed under the Apache 2.0 license. Web
|
||||
and desktop fonts contain some icons that are redistributed under the Apache
|
||||
2.0 license. All other icons are either redistributed under their respective
|
||||
licenses or are distributed under the Apache 2.0 license.
|
||||
|
||||
# Code: MIT (https://opensource.org/licenses/MIT)
|
||||
The MIT license applies to all non-font and non-icon files.
|
BIN
addons/simple-state/icons/random_state.png
Normal file
BIN
addons/simple-state/icons/random_state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 551 B |
34
addons/simple-state/icons/random_state.png.import
Normal file
34
addons/simple-state/icons/random_state.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cgoc214akn2d6"
|
||||
path="res://.godot/imported/random_state.png-0878745fbdc123f3f0d51012c73a7024.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/random_state.png"
|
||||
dest_files=["res://.godot/imported/random_state.png-0878745fbdc123f3f0d51012c73a7024.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
addons/simple-state/icons/sequence_state.png
Normal file
BIN
addons/simple-state/icons/sequence_state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 424 B |
34
addons/simple-state/icons/sequence_state.png.import
Normal file
34
addons/simple-state/icons/sequence_state.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bcviu3dccvvm2"
|
||||
path="res://.godot/imported/sequence_state.png-4718f2c926301319d99a0ccd15ff62fb.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/sequence_state.png"
|
||||
dest_files=["res://.godot/imported/sequence_state.png-4718f2c926301319d99a0ccd15ff62fb.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
12
addons/simple-state/icons/sources.txt
Normal file
12
addons/simple-state/icons/sources.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
Icon Sources
|
||||
-----------
|
||||
[0] Board Game Icons: https://kenney.nl/assets/board-game-icons
|
||||
- state.png
|
||||
- random_state.png
|
||||
- sequence_state.png
|
||||
|
||||
[1] Game Icons: https://kenney.nl/assets/game-icons
|
||||
- animation_state.png
|
||||
|
||||
[2] Pictogrammers: <https://pictogrammers.com/library/mdi/>
|
||||
- state_machine_debugger.png (tinted)
|
BIN
addons/simple-state/icons/state.png
Normal file
BIN
addons/simple-state/icons/state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 572 B |
34
addons/simple-state/icons/state.png.import
Normal file
34
addons/simple-state/icons/state.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://msu07hn5ewo5"
|
||||
path="res://.godot/imported/state.png-fa3dd722682a28f890f138dcc6e162af.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/state.png"
|
||||
dest_files=["res://.godot/imported/state.png-fa3dd722682a28f890f138dcc6e162af.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
addons/simple-state/icons/state_fullsize.png
Normal file
BIN
addons/simple-state/icons/state_fullsize.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 767 B |
34
addons/simple-state/icons/state_fullsize.png.import
Normal file
34
addons/simple-state/icons/state_fullsize.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b32u1sa3robj0"
|
||||
path="res://.godot/imported/state_fullsize.png-41a4827bc36d7745ba55c17c0bc213e9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/state_fullsize.png"
|
||||
dest_files=["res://.godot/imported/state_fullsize.png-41a4827bc36d7745ba55c17c0bc213e9.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
addons/simple-state/icons/state_machine_debugger.png
Normal file
BIN
addons/simple-state/icons/state_machine_debugger.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
34
addons/simple-state/icons/state_machine_debugger.png.import
Normal file
34
addons/simple-state/icons/state_machine_debugger.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dsglk01amsawf"
|
||||
path="res://.godot/imported/state_machine_debugger.png-4af22f2c577f0756aa0b1d50ce679700.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/simple-state/icons/state_machine_debugger.png"
|
||||
dest_files=["res://.godot/imported/state_machine_debugger.png-4af22f2c577f0756aa0b1d50ce679700.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
7
addons/simple-state/plugin.cfg
Normal file
7
addons/simple-state/plugin.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="SimpleState"
|
||||
description="A super-simple state machine. Lightweight and (hopefully) reliable."
|
||||
author="AuraTheEnby"
|
||||
version="1.3.2"
|
||||
script="plugin.gd"
|
25
addons/simple-state/plugin.gd
Normal file
25
addons/simple-state/plugin.gd
Normal file
|
@ -0,0 +1,25 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
## It uses the icons provided by the scripts anyway, so
|
||||
## we don't really need to specify the real ones here.
|
||||
## Plus, it might help with enabling it before the project
|
||||
## has been reloaded for the first time.
|
||||
var placeholder_texture := PlaceholderTexture2D.new()
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
add_custom_type("State", "Node", State, placeholder_texture)
|
||||
add_custom_type("RandomState", "Node", RandomState, placeholder_texture)
|
||||
add_custom_type("AnimationState", "Node", AnimationState, placeholder_texture)
|
||||
add_custom_type("SequenceState", "Node", SequenceState, placeholder_texture)
|
||||
add_custom_type("StateMachineDebugger", "Tree", StateMachineDebugger, placeholder_texture)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_custom_type("State")
|
||||
remove_custom_type("RandomState")
|
||||
remove_custom_type("AnimationState")
|
||||
remove_custom_type("SequenceState")
|
||||
remove_custom_type("StateMachineDebugger")
|
33
addons/simple-state/templates/State/empty_state.gd
Normal file
33
addons/simple-state/templates/State/empty_state.gd
Normal file
|
@ -0,0 +1,33 @@
|
|||
# meta-default: true
|
||||
extends _BASE_
|
||||
|
||||
|
||||
# Called when the state is activated. (parents, then children)
|
||||
func _enter() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called after the state is activated. (children, then parents)
|
||||
func _after_enter() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called every physics frame (only when the state is active, of course). (parents, then children)
|
||||
func _update(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called at the end of every physics frame. (children, then parents)
|
||||
func _after_update(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called before the state is deactivated. (parents, then children)
|
||||
func _before_exit() -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Called when the state is deactivated. (children, then parents)
|
||||
func _exit() -> void:
|
||||
pass
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue