Initial commit

This commit is contained in:
Haze Weathers 2025-08-06 10:26:28 -06:00
commit 3b96451047
71 changed files with 2302 additions and 0 deletions

View file

@ -0,0 +1 @@
../../LICENSE.txt

View file

@ -0,0 +1,22 @@
@tool
class_name WBBasicInteractive
extends StaticBody2D
## Collision object that emits a signal when interacted with by a player character.
## Emitted when a player character interacts with this object.
signal interacted()
func _init() -> void:
for child in get_children():
if child is CollisionShape2D or child is CollisionPolygon2D:
return
var col_shape := CollisionShape2D.new()
col_shape.shape = RectangleShape2D.new()
col_shape.shape.size = Vector2(8.0, 8.0)
add_child(col_shape)
func interact() -> void:
interacted.emit()

View file

@ -0,0 +1 @@
uid://dkj06ucsoypda

View file

@ -0,0 +1,174 @@
@tool
@icon("character.svg")
class_name WBCharacter
extends CharacterBody2D
## A character that can be controlled by events and behaviors and moves on a grid.
## Emitted when the character begins moving.
signal move_started()
## Emitted when the character reaches its target position.
signal move_finished()
enum Dir {LEFT, RIGHT, UP, DOWN}
const DIR_VECTORS: Dictionary[Dir, Vector2] = {
Dir.LEFT: Vector2.LEFT,
Dir.RIGHT: Vector2.RIGHT,
Dir.UP: Vector2.UP,
Dir.DOWN: Vector2.DOWN,
}
const DIR_ANIM_SUFFIXES: Dictionary[Dir, StringName] = {
Dir.LEFT: &"_left",
Dir.RIGHT: &"_right",
Dir.UP: &"_up",
Dir.DOWN: &"_down",
}
## Size of the grid the character is restricted to.
@export var tile_size: float = 16.0
## Speed the character walks at.
@export var walk_speed: float = 4.0
## Speed the character runs at.
@export var run_speed: float = 8.0
## Direction the character is facing.
@export var facing: Dir = Dir.DOWN
## Animation library for the character. [br]
## At a minimum, the [code]idle_*[/code] animations are required. [br]
## The following animations are used by default behavior: [br]
## [code]idle_[left,right,up,down][/code] [br]
## [code]walk_[left,right,up,down][/code] [br]
## [code]run_[left,right,up,down][/code] [br]
## [code]run_*[/code] will fallback to [code]walk_*[/code],
## which will fallback to [code]idle_*[/code]. [br]
## Addition custom animations may be provided to play on demand.
@export var animations: SpriteFrames:
set(value):
animations = value
sprite.sprite_frames = animations
## Texture drawing offset of the animated sprite.
@export var sprite_offset: Vector2 = Vector2.ZERO:
set(value):
sprite_offset = value
sprite.offset = sprite_offset
## True when the character is moving.
var moving: bool = false
## Whether the character is running.
var running: bool = false
## Tile position of the character on the grid.
var tile_position: Vector2i:
get():
return pos_to_tile(global_position)
var sprite: AnimatedSprite2D
var _next_pos: Vector2
var _playing_custom_animation: bool = false
func _init() -> void:
sprite = AnimatedSprite2D.new()
sprite.sprite_frames = animations
add_child(sprite)
func _ready() -> void:
global_position = closest_tile_center(global_position)
for child in get_children():
if child is CollisionShape2D or child is CollisionPolygon2D:
return
var col_shape := CollisionShape2D.new()
col_shape.shape = RectangleShape2D.new()
col_shape.shape.size = Vector2(tile_size - 2.0, tile_size - 2.0)
add_child(col_shape)
func _physics_process(delta: float) -> void:
if moving:
var move_delta := (run_speed if running else walk_speed) * tile_size * delta
global_position = global_position.move_toward(_next_pos, move_delta)
if global_position == _next_pos:
moving = false
move_finished.emit()
func _process(delta: float) -> void:
if moving:
_playing_custom_animation = false
var anims: Array[StringName] = [
&"walk" + DIR_ANIM_SUFFIXES[facing],
&"idle" + DIR_ANIM_SUFFIXES[facing]
]
if running:
anims.push_front(&"run" + DIR_ANIM_SUFFIXES[facing])
_try_animations(anims)
elif not _playing_custom_animation:
_try_animations([&"idle" + DIR_ANIM_SUFFIXES[facing]])
## Makes the character move one tile in the given direction. [br]
## If [param ignore_collision] is true, the character will not perform collision checks.
func start_move(dir: Dir, ignore_collision: bool = false) -> bool:
if moving:
return false
facing = dir
_next_pos = global_position + DIR_VECTORS[dir] * tile_size
var col := move_and_collide(_next_pos - global_position, true)
if col and not ignore_collision:
return false
moving = true
move_started.emit()
return true
## Plays a given custom animation from the animation set. [br]
## If [param reset_after] is [constant true], the animation will return to
## the default idle animation after it finishes.
func play_custom_animation(anim: StringName, reset_after: bool = false) -> void:
_try_animations([anim])
_playing_custom_animation = true
if reset_after and not animations.get_animation_loop(anim):
await sprite.animation_finished
_playing_custom_animation = false
## Stops playing custom animation if one is currently playing.
func end_custom_animation() -> void:
_playing_custom_animation = false
## Returns the closest tile center position to a given position in global coordinates.
func closest_tile_center(pos: Vector2) -> Vector2:
var tile := pos - Vector2(tile_size, tile_size) * 0.5
tile = tile.snappedf(tile_size)
tile += Vector2(tile_size, tile_size) * 0.5
return tile
## Returns the tile coordinates of a given position in global coordinates.
func pos_to_tile(pos: Vector2) -> Vector2i:
return Vector2i((global_position / Vector2(tile_size, tile_size)).floor())
## Returns the center position of a given tile in global coordinates.
func tile_center_pos(tile: Vector2i) -> Vector2:
return (Vector2(tile) * Vector2(tile_size, tile_size)) + (Vector2(tile_size, tile_size) * 0.5)
func _try_animations(anims: Array[StringName]) -> void:
for anim in anims:
if animations.has_animation(anim):
sprite.play(anim)
return

View file

@ -0,0 +1 @@
uid://dx8lxwwkyrjin

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="character.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="14.854978"
inkscape:cx="0.67317502"
inkscape:cy="10.434213"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="#8da5f3"
d="m 6.4922281,1 a 1,1 0 0 0 -1,1 v 3 a 1,1 0 0 0 1,1 h 1 v 0.99 a 1,1 0 0 0 -0.316,0.062 l -2.051,0.684 -0.684,-2.051 a 1.0000706,1.0000706 0 0 0 -1.898,0.631 l 1,3 a 1,1 0 0 0 1.265,0.633 l 1.684,-0.56 v 0.61 c 0,0.041 0.019,0.076 0.024,0.116 l -4.579,3.052 a 1.0001245,1.0001245 0 1 0 1.11,1.664 l 5.056,-3.37 1.495,2.986 a 1,1 0 0 0 1.2100009,0.502 l 3,-1 a 1,1 0 1 0 -0.632,-1.897 l -2.178,0.725 -0.975001,-1.951 A 0.981,0.981 0 0 0 10.492229,9.999 V 9 h 1.382999 l 0.722,1.448 a 1.0006409,1.0006409 0 1 0 1.790001,-0.895 l -1,-2 A 1,1 0 0 0 12.492229,7 H 9.4922281 V 6 h 1.0000009 a 1,1 0 0 0 1,-1 V 2 a 1,1 0 0 0 -1,-1 z m 0,2 h 1 v 2 h -1 z"
id="path1"
style="fill:#20c997" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ufpt8ejikj1j"
path="res://.godot/imported/character.svg-492c7305b4930c6092d7995fed4891a3.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/characters/character.svg"
dest_files=["res://.godot/imported/character.svg-492c7305b4930c6092d7995fed4891a3.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,44 @@
@tool
@icon("character_behavior.svg")
class_name WBCharacterBehavior
extends Node
## Controls/puppets a parent character.
## Whether the behavior is currently active.
@export var active: bool:
set(value):
var last_value := active
active = value
if active:
if not is_node_ready():
await ready
for child in get_parent().get_children():
if child != self and child is WBCharacterBehavior:
child.active = false
if active:
_activate()
else:
_deactivate()
## The character being controlled.
var character: WBCharacter:
get():
return get_parent() as WBCharacter
## Enables the behavior.
func enable() -> void:
active = true
## Disables the behavior.
func disable() -> void:
active = false
func _activate() -> void:
pass
func _deactivate() -> void:
pass

View file

@ -0,0 +1 @@
uid://cgunv5ngogsky

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="character_behavior.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="14.854978"
inkscape:cx="0.67317502"
inkscape:cy="10.434213"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#20c997;stroke-width:2;stroke-dasharray:none" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://lib4e4qnml8a"
path="res://.godot/imported/character_behavior.svg-d7dbdf997daf0ba87cdd58e5db5e61e1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/characters/character_behavior.svg"
dest_files=["res://.godot/imported/character_behavior.svg-d7dbdf997daf0ba87cdd58e5db5e61e1.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,81 @@
@tool
@icon("path_behavior.svg")
class_name WBPathBehavior
extends WBCharacterBehavior
## A behavior that makes a character walk along a specified path,
## looping when they reach the end.
## List of points to walk to.
@export var points: Array[Vector2i]
## Whether the character should be running.
@export var run: bool = false
var _current_point: int
func _init() -> void:
if Engine.is_editor_hint():
add_child(DebugView.new(self))
func _activate() -> void:
_do_move()
func _do_move() -> void:
if not active:
return
if character.tile_position == points[_current_point]:
_current_point = posmod(_current_point + 1, points.size())
var point := points[_current_point]
if character.tile_position.x < point.x:
character.start_move(WBCharacter.Dir.RIGHT)
elif character.tile_position.x > point.x:
character.start_move(WBCharacter.Dir.LEFT)
elif character.tile_position.y < point.y:
character.start_move(WBCharacter.Dir.DOWN)
elif character.tile_position.y > point.y:
character.start_move(WBCharacter.Dir.UP)
if character.moving:
await character.move_finished
_do_move()
class DebugView extends Node2D:
var behavior: WBPathBehavior
func _init(p_behavior: WBPathBehavior) -> void:
behavior = p_behavior
func _process(delta: float) -> void:
if Engine.get_frames_drawn() % 60 == 0:
queue_redraw()
func _draw() -> void:
if not behavior.character:
return
var color := Color.MEDIUM_PURPLE
var width := -1.0
var selection := EditorInterface.get_selection()
if behavior in selection.get_selected_nodes():
color = Color.PURPLE
width = 2.0
var last_point := to_local(behavior.character.closest_tile_center(behavior.character.global_position))
for point in behavior.points + [behavior.points[0]]:
var current_point := to_local(behavior.character.tile_center_pos(point))
draw_line(last_point, Vector2(current_point.x, last_point.y), color, width)
draw_line(Vector2(current_point.x, last_point.y), current_point, color, width)
var arrow_dir := current_point.direction_to(Vector2(current_point.x, last_point.y))
if arrow_dir.is_zero_approx():
arrow_dir = current_point.direction_to(last_point)
draw_line(current_point, current_point + arrow_dir.rotated(deg_to_rad(45.0)) * 8.0, color, width)
draw_line(current_point, current_point + arrow_dir.rotated(deg_to_rad(-45.0)) * 8.0, color, width)
last_point = current_point

View file

@ -0,0 +1 @@
uid://deanlc8wbefgm

View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="path_behavior.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="14.854978"
inkscape:cx="0.67317502"
inkscape:cy="10.434213"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#20c997;stroke-width:2;stroke-dasharray:none" />
<path
style="fill:none;fill-opacity:1;stroke:#20c997;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 4.2410027,11.511293 4.1736852,4.3083202 8.5493228,4.7122252 8.2127353,7.7415128 11.915198,8.2127353 11.242023,12.11715 Z"
id="path8" />
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3los038dn424"
path="res://.godot/imported/path_behavior.svg-92b1066b81b0693c0689fa68366f00d0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/characters/path_behavior.svg"
dest_files=["res://.godot/imported/path_behavior.svg-92b1066b81b0693c0689fa68366f00d0.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,98 @@
@tool
@icon("player_character.svg")
class_name WBPlayerCharacter
extends WBCharacter
## A character controlled directly by the player.
## Whether the character will react to player input.
@export var controllable: bool = true
## Time to wait after turning before the character will start moving.
@export var turn_walk_time: float = 0.25
## Collision mask for interactive objects.
## If a collision is detected, [method interact] will be called on the target object if it exists.
@export_flags_2d_physics var interact_mask: int:
set(value):
interact_mask = value
_interact_raycast.collision_mask = interact_mask
@export_group("Input Actions", "input_")
@export var input_left: StringName = &"ui_left"
@export var input_right: StringName = &"ui_right"
@export var input_up: StringName = &"ui_up"
@export var input_down: StringName = &"ui_down"
@export var input_interact: StringName = &"ui_accept"
@export var input_run: StringName = &"ui_cancel"
var _interact_raycast: RayCast2D
var _turn_cooldown: float = 0.0
func _init() -> void:
super._init()
move_finished.connect(_check_move_input)
_interact_raycast = RayCast2D.new()
_interact_raycast.enabled = false
_interact_raycast.collide_with_areas = true
_interact_raycast.hit_from_inside = true
_interact_raycast.add_exception(self)
add_child(_interact_raycast)
func _physics_process(delta: float) -> void:
if Engine.is_editor_hint():
return
_turn_cooldown -= delta
running = Input.is_action_pressed(input_run)
if not moving:
_check_move_input(true)
super._physics_process(delta)
## Enables player control of the character.
func enable_control() -> void:
controllable = true
## Disables player control of the character.
func disable_control() -> void:
controllable = false
func _check_move_input(check_turn_cooldown: bool = false) -> void:
if not controllable:
return
if Input.is_action_pressed(input_left):
_try_move(Dir.LEFT, check_turn_cooldown)
elif Input.is_action_pressed(input_right):
_try_move(Dir.RIGHT, check_turn_cooldown)
elif Input.is_action_pressed(input_up):
_try_move(Dir.UP, check_turn_cooldown)
elif Input.is_action_pressed(input_down):
_try_move(Dir.DOWN, check_turn_cooldown)
elif Input.is_action_just_pressed(input_interact):
_try_interact()
func _try_move(dir: Dir, check_turn_cooldown: bool) -> void:
if facing != dir:
_turn_cooldown = turn_walk_time
if running:
_turn_cooldown *= 0.5
facing = dir
if check_turn_cooldown and _turn_cooldown >= 0.0:
return
start_move(dir)
func _try_interact() -> void:
_interact_raycast.target_position = DIR_VECTORS[facing] * tile_size
_interact_raycast.force_raycast_update()
if _interact_raycast.is_colliding():
var interactive := _interact_raycast.get_collider()
if interactive.has_method(&"interact"):
interactive.interact()

View file

@ -0,0 +1 @@
uid://cbc7nngunyym7

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="player_character.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.753231"
inkscape:cx="4.5042892"
inkscape:cy="6.6787737"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="#8da5f3"
d="m 7.4922281,6.99 c -0.1079911,0.00346 -0.2147078,0.024398 -0.316,0.062 l -2.051,0.684 -0.684,-2.051 C 4.0205616,4.4196672 2.1225616,5.0506672 2.5432281,6.316 l 1,3 c 0.1744187,0.5242077 0.7408721,0.807658 1.265,0.633 l 1.684,-0.56 v 0.61 c 0,0.041 0.019,0.076 0.024,0.116 l -4.579,3.052 c -1.10956307,0.739954 4.369e-4,2.403954 1.11,1.664 l 5.056,-3.37 1.495,2.986 c 0.2223325,0.444998 0.7379489,0.658915 1.2100009,0.502 l 3,-1 c 1.302584,-0.402102 0.651473,-2.356463 -0.632,-1.897 l -2.178,0.725 -0.975001,-1.951 c 0.288394,-0.176434 0.465617,-0.488934 0.469001,-0.827 V 9 h 1.382999 l 0.722,1.448 c 0.596513,1.193792 2.386514,0.298792 1.790001,-0.895 l -1,-2 C 13.217804,7.2139484 12.871255,6.9998234 12.492229,7 H 9.4922281 Z"
id="path1"
style="fill:#20c997"
sodipodi:nodetypes="ccccccccsccccccccccccccccccc" />
<path
style="fill:#20c997;fill-opacity:1;stroke:none;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 8.7367679,7.0282444 7.843676,4.89259 5.1644006,4.0383283 7.843676,2.951086 9.1250687,0.46596095 9.78518,3.1064064 12.969247,3.8830079 9.6686898,4.8537599 9.7463498,3.1452364 7.882506,3.0287462 l 0.03883,1.7861836 1.7861837,0.07766 z"
id="path8" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cdwrfpa2xn5cf"
path="res://.godot/imported/player_character.svg-b26ca4ad6a1901b6ebacace37c911113.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/characters/player_character.svg"
dest_files=["res://.godot/imported/player_character.svg-b26ca4ad6a1901b6ebacace37c911113.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,78 @@
@tool
@icon("wander_behavior.svg")
class_name WBWanderBehavior
extends WBCharacterBehavior
## A behavior that makes a character randomly wander around within a specified area.
## Rectangle enclosing the tiles the character is allowed to move to.
@export var territory: Rect2i
## Minimum distance to travel in one "burst".
@export var min_distance: int
## Maximum distance to travel in one "burst".
@export var max_distance: int
## Minimum time to idle after finishing a "burst".
@export_custom(0, "suffix:s") var min_idle: float
## Maximum time to idle after finishing a "burst".
@export_custom(0, "suffix:s") var max_idle: float
## Whether the character should run.
@export var run: bool = false
func _init() -> void:
if Engine.is_editor_hint():
add_child(DebugView.new(self))
func _activate() -> void:
if character:
_do_move()
func _do_move() -> void:
if not active:
return
var remaining_distance: int = randi_range(min_distance, max_distance)
var dir: WBCharacter.Dir = WBCharacter.Dir.values().pick_random()
while remaining_distance > 0:
while not territory.has_point(character.tile_position + Vector2i(WBCharacter.DIR_VECTORS[dir])):
dir = WBCharacter.Dir.values().pick_random()
character.running = run
character.start_move(dir)
if character.moving:
await character.move_finished
remaining_distance -= 1
await create_tween().tween_interval(randf_range(min_idle, max_idle)).finished
_do_move()
class DebugView extends Node2D:
var behavior: WBWanderBehavior
func _init(p_behavior: WBWanderBehavior) -> void:
behavior = p_behavior
func _process(delta: float) -> void:
if Engine.get_frames_drawn() % 60 == 0:
queue_redraw()
func _draw() -> void:
if not behavior.character:
return
var position := to_local(Vector2(behavior.territory.position) * behavior.character.tile_size)
var size := Vector2(behavior.territory.size) * behavior.character.tile_size
var color := Color.MEDIUM_PURPLE
var width := -1.0
var selection := EditorInterface.get_selection()
if behavior in selection.get_selected_nodes():
color = Color.PURPLE
width = 2.0
draw_rect(
Rect2(position, size),
color, false, width
)

View file

@ -0,0 +1 @@
uid://xcfljtjwi1vt

View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="wander_behavior.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="14.854978"
inkscape:cx="0.67317502"
inkscape:cy="10.434213"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#20c997;stroke-width:2;stroke-dasharray:none" />
<path
style="fill:#20c997;fill-opacity:1;stroke:#20c997;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 3.9276284,7.9330316 5.599787,7.0386211 5.5608995,8.8663295 Z"
id="path5"
sodipodi:nodetypes="cccc" />
<path
style="fill:#20c997;fill-opacity:1;stroke:#20c997;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 11.501205,7.9790821 9.8290464,8.8734926 9.8679344,7.0457842 Z"
id="path6"
sodipodi:nodetypes="cccc" />
<path
style="fill:#20c997;fill-opacity:1;stroke:#20c997;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 7.7568856,4.1887121 8.6512961,5.8608707 6.8235877,5.8219827 Z"
id="path7"
sodipodi:nodetypes="cccc" />
<path
style="fill:#20c997;fill-opacity:1;stroke:#20c997;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
d="M 7.5941729,11.762289 6.6997624,10.09013 8.5274708,10.129018 Z"
id="path8"
sodipodi:nodetypes="cccc" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dcb3i7xdowdte"
path="res://.godot/imported/wander_behavior.svg-cec028e106c64cd15d62b2fb10cf549b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/characters/wander_behavior.svg"
dest_files=["res://.godot/imported/wander_behavior.svg-cec028e106c64cd15d62b2fb10cf549b.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,31 @@
@icon("character_animation_event.svg")
class_name WBCharacterAnimationEvent
extends WBEvent
## Event that makes a character play a specific animation.
## Character to animate.
@export var character: WBCharacter
## Animation to play.
@export var animation: StringName
## Number of times to let animation loop if the animation loops.
@export var loops: int = 1
func _perform() -> void:
if not character:
push_error("Target character does not exist.")
return
if not character.animations.has_animation(animation):
push_error("The specified animation does not exist in this character.")
return
character.play_custom_animation(animation)
if character.animations.get_animation_loop(animation):
var remaining_loops := loops
while remaining_loops > 0:
await character.sprite.animation_looped
remaining_loops -= 1
else:
await character.sprite.animation_finished
character.end_custom_animation()

View file

@ -0,0 +1 @@
uid://c4bjkninxu305

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="character_animation_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="a"
x2="0"
y1="2"
y2="14"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4)">
<stop
offset="0"
stop-color="#ff8dbc"
id="stop1" />
<stop
offset=".4"
stop-color="#7260ff"
id="stop2" />
<stop
offset=".6"
stop-color="#7260ff"
id="stop3" />
<stop
offset="1"
stop-color="#74c9ff"
id="stop4" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
fill="url(#a)"
d="M 12.816902,3.0999201 H 3.0167421 V 3.7999316 H 4.4167649 V 5.1999544 H 3.0167421 v 5.6000916 h 1.4000228 v 1.400023 H 3.0167421 V 12.90008 H 12.816902 V 12.200069 H 11.41688 v -1.400023 h 1.400022 V 5.1999544 H 11.41688 V 3.7999316 h 1.400022 z M 10.016857,3.7999316 V 5.1999544 H 8.6168334 V 3.7999316 Z m 0,7.0001144 v 1.400023 H 8.6168334 V 10.800046 Z M 7.2168106,3.7999316 V 5.1999544 H 5.8167878 V 3.7999316 Z m 0,7.0001144 v 1.400023 H 5.8167878 V 10.800046 Z M 6.5167991,6.6748784 a 0.52500856,0.52500856 0 0 1 0.787513,-0.4459073 l 2.1000343,1.3181214 a 0.52500856,0.52500856 0 0 1 0,0.9051148 L 7.3043121,9.7703293 A 0.52500856,0.52500856 0 0 1 6.5167991,9.324422 Z"
id="path4"
style="fill:#8da5f3;fill-opacity:1;stroke-width:0.700012" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ddeaoeqtcg5x2"
path="res://.godot/imported/character_animation_event.svg-d0fd25cb2ad4401cf873b25840865b53.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/character_animation_event.svg"
dest_files=["res://.godot/imported/character_animation_event.svg-d0fd25cb2ad4401cf873b25840865b53.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,12 @@
@icon("delay_event.svg")
class_name WBDelayEvent
extends WBEvent
## Event that waits for a given duration.
## Time to wait for.
@export_custom(0, "suffix:s") var delay: float
func _perform() -> void:
await create_tween().tween_interval(delay).finished

View file

@ -0,0 +1 @@
uid://bqapvnlskkprc

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="delay_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
fill="#e0e0e0"
d="m 4.8110802,3.5943837 a 0.62779194,0.62779194 0 0 0 -0.6277919,0.627792 c 0,0.9416879 0.6070748,1.6479538 1.1758543,2.2977185 C 5.8362645,7.0660732 6.248096,7.5513563 6.5362525,7.9889273 6.248096,8.4264983 5.8368922,8.913037 5.3591426,9.4592163 4.7903631,10.10898 4.1832883,10.813991 4.1832883,11.755679 a 0.62779194,0.62779194 0 0 0 0.6277919,0.627792 H 11.089 a 0.62779194,0.62779194 0 0 0 0.627792,-0.627792 c 0,-0.941688 -0.607075,-1.646699 -1.175855,-2.2964627 C 10.063815,8.913037 9.6519839,8.4264983 9.3638269,7.9889273 9.6519839,7.5513563 10.063188,7.0660732 10.540937,6.5198942 11.109717,5.8701295 11.716792,5.1638636 11.716792,4.2221757 A 0.62779194,0.62779194 0 0 0 11.089,3.5943837 Z M 10.092066,4.8499676 C 9.9426519,5.1261961 9.9005899,5.3459232 9.5967379,5.6924644 9.0668819,6.2976558 8.4051891,6.9304701 8.0159581,7.7076765 A 0.62779194,0.62779194 0 0 0 7.9500399,7.9889273 0.62779194,0.62779194 0 0 0 7.883494,7.7083043 C 7.4948907,6.9304701 6.833198,6.2976558 6.3033417,5.6924644 5.9994904,5.3452955 5.9574283,5.1268239 5.807386,4.8499676 Z"
id="path1-2"
style="stroke-width:0.627792" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://codl7c70plmp0"
path="res://.godot/imported/delay_event.svg-51be5be8e4173986beaba93125ff14e2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/delay_event.svg"
dest_files=["res://.godot/imported/delay_event.svg-51be5be8e4173986beaba93125ff14e2.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,33 @@
@icon("event.svg")
class_name WBEvent
extends Node
## Basic event that does nothing and emits its signals.
## Emitted when the event starts performing.
signal event_started()
## Emitted when the event has finished performing.
signal event_finished()
## [constant true] when the event is currently active.
## It is an error to try to perform an event that is already running.
var running: bool = false
## Starts the event.
func perform() -> void:
if running:
push_error("Event may not be performed if it is already running.")
running = true
event_started.emit()
await _perform()
running = false
event_finished.emit()
func _perform() -> void:
pass

View file

@ -0,0 +1 @@
uid://drkkeoynpxpgd

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.31301"
id="rect4"
width="1.1482214"
height="1.1907481"
x="7.3398166"
y="10.89865" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.76085"
id="rect5"
width="0.64153773"
height="3.8329279"
x="7.5902767"
y="4.1139956" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://df7egmuwd6mtt"
path="res://.godot/imported/event.svg-94666083a457e4b1b83e2b5cf3cf906d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/event.svg"
dest_files=["res://.godot/imported/event.svg-94666083a457e4b1b83e2b5cf3cf906d.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,37 @@
@icon("move_character_event.svg")
class_name WBMoveCharacterEvent
extends WBEvent
## Event that moves a character to the specified tile.
## Charcter to move.
@export var character: WBCharacter
## Tile position to move the target to.
@export var target_tile: Vector2i
## Whether the character should be running.
@export var run: bool = false
## Whether the character should move vertically or horizontally first.
@export var prefer_vertical: bool = false
func _perform() -> void:
while character.tile_position != target_tile:
if prefer_vertical:
if character.tile_position.y < target_tile.y:
character.start_move(WBCharacter.Dir.DOWN, true)
elif character.tile_position.y > target_tile.y:
character.start_move(WBCharacter.Dir.UP, true)
elif character.tile_position.x < target_tile.x:
character.start_move(WBCharacter.Dir.RIGHT, true)
elif character.tile_position.x > target_tile.x:
character.start_move(WBCharacter.Dir.LEFT, true)
else:
if character.tile_position.x < target_tile.x:
character.start_move(WBCharacter.Dir.RIGHT, true)
elif character.tile_position.x > target_tile.x:
character.start_move(WBCharacter.Dir.LEFT, true)
elif character.tile_position.y < target_tile.y:
character.start_move(WBCharacter.Dir.DOWN, true)
elif character.tile_position.y > target_tile.y:
character.start_move(WBCharacter.Dir.UP, true)
await character.move_finished

View file

@ -0,0 +1 @@
uid://tajefl5yoipy

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="move_character_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
style="fill:#8da5f3;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
d="m 3.694304,12.560634 0.1555495,-8.1663567 6.6108595,0.077775 0.07777,-1.1666223 2.022146,2.5665691 -2.099921,2.0610326 0,-1.5943838 -5.1331379,-0.077775 -0.038887,6.2219851 z"
id="path5"
sodipodi:nodetypes="cccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ckyw0hvg7jhij"
path="res://.godot/imported/move_character_event.svg-9eb1716c5e768c9b0d5289ef761908df.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/move_character_event.svg"
dest_files=["res://.godot/imported/move_character_event.svg-9eb1716c5e768c9b0d5289ef761908df.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,26 @@
@icon("parallel_event.svg")
class_name WBParallelEvent
extends WBEvent
## Event that starts all child events at the same time, then finishes once all have completed.
signal _children_finished()
var _children_running: int = 0
func _perform() -> void:
for child in get_children():
if child is WBEvent:
_start_child(child)
if _children_running > 0:
await _children_finished
func _start_child(event: WBEvent) -> void:
_children_running += 1
await event.perform()
_children_running -= 1
if _children_running <= 0:
_children_finished.emit()

View file

@ -0,0 +1 @@
uid://dcwk6e3ufl2rs

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="parallel_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:2.02785"
id="rect3"
width="0.54810518"
height="5.949976"
x="5.4226375"
y="4.9062467" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.9332"
id="rect5"
width="0.48720151"
height="6.083509"
x="10.030569"
y="4.7445955" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ckrac6a5h1sd4"
path="res://.godot/imported/parallel_event.svg-c258bd2568944bcb205b6d0f605db926.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/parallel_event.svg"
dest_files=["res://.godot/imported/parallel_event.svg-c258bd2568944bcb205b6d0f605db926.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,12 @@
@icon("sequence_event.svg")
class_name WBSequenceEvent
extends WBEvent
## Event that performs each child event in sequence, one after another.
func _perform() -> void:
event_started.emit()
for child in get_children():
if child is WBEvent:
await child.perform()
event_finished.emit()

View file

@ -0,0 +1 @@
uid://cghsjdjghxhn6

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="sequence_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.28162"
id="rect3"
width="1.1207687"
height="1.1622787"
x="3.9217882"
y="7.9282513" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.31301"
id="rect4"
width="1.1482214"
height="1.1907481"
x="7.4175916"
y="7.9043198" />
<rect
style="fill:#dd5498;stroke:#dd5498;stroke-width:1.28162"
id="rect5"
width="1.1207687"
height="1.1622787"
x="11.044965"
y="7.9186711" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bakm6fp22sxac"
path="res://.godot/imported/sequence_event.svg-1b2ebbf7d19a80d4622c19258498b900.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/sequence_event.svg"
dest_files=["res://.godot/imported/sequence_event.svg-1b2ebbf7d19a80d4622c19258498b900.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,17 @@
@icon("teleport_character_event.svg")
class_name WBTeleportCharacterEvent
extends WBEvent
## Event that teleports a character to the specified tile position.
## Character to teleport.
@export var character: WBCharacter
## Tile position to teleport the character to.
@export var target_tile: Vector2i
func _perform() -> void:
if not character:
push_error("Target character does not exist.")
return
character.global_position = character.tile_center_pos(target_tile)

View file

@ -0,0 +1 @@
uid://dco2ay8p6o615

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="teleport_character_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
sodipodi:type="star"
style="fill:none;fill-opacity:1;stroke:#8da5f3;stroke-width:1.34623;stroke-dasharray:none;stroke-opacity:1"
id="path5"
inkscape:flatsided="false"
sodipodi:sides="5"
sodipodi:cx="4.8220387"
sodipodi:cy="5.0942507"
sodipodi:r1="5.4158311"
sodipodi:r2="2.7079153"
sodipodi:arg1="0.61190243"
sodipodi:arg2="1.240221"
inkscape:rounded="0"
inkscape:randomized="0"
d="M 9.2552034,8.2052435 5.7009935,7.655548 3.2332319,10.271791 2.6577122,6.7216708 -0.5930627,5.1831535 2.6054565,3.5387543 3.0641287,-0.02834426 5.616442,2.505481 9.1506919,1.8394101 7.5295891,5.0497994 Z"
inkscape:transform-center-x="0.37156418"
inkscape:transform-center-y="0.020029869"
transform="matrix(0.75679887,0,0,0.72909077,4.6005899,4.4238451)" />
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cpqqmbh3d5nev"
path="res://.godot/imported/teleport_character_event.svg-984335146dbb260f6d1052999d295a22.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/teleport_character_event.svg"
dest_files=["res://.godot/imported/teleport_character_event.svg-984335146dbb260f6d1052999d295a22.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,21 @@
@icon("wait_signal_event.svg")
class_name WBWaitSignalEvent
extends WBEvent
## Event that waits for a specified signal on a target node to be emitted.
## Node to await a signal of.
@export var target: Node
## Signal to await.
@export var signal_to_await: StringName = &""
func _perform() -> void:
if not target:
push_error("Target object is not specified.")
return
if not target.has_signal(signal_to_await):
push_error("Target does not have the specified signal.")
return
await Signal(target, signal_to_await)

View file

@ -0,0 +1 @@
uid://cqodqaf46ktes

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="wait_signal_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
fill="#ff5f5f"
d="m 3.5788288,4.711046 v 6.577908 H 4.8944104 7.5255736 V 9.9733724 H 4.8944104 V 6.0266276 H 7.5255736 V 4.711046 H 4.8944104 Z M 9.4989464,5.3688368 V 7.3422092 H 6.209992 V 8.6577908 H 9.4989464 V 10.631163 L 11.143423,9.3155816 12.7879,8 11.143423,6.6844184 Z"
id="path1-5"
style="stroke-width:0.657791" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ccury2hlmsf45"
path="res://.godot/imported/wait_signal_event.svg-d1b2d658af4493719cdf3a0f1a267c69.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/wait_signal_event.svg"
dest_files=["res://.godot/imported/wait_signal_event.svg-d1b2d658af4493719cdf3a0f1a267c69.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,27 @@
@icon("walk_character_event.svg")
class_name WBWalkCharacterEvent
extends WBEvent
## Event that makes a character walk a certain distance in the given direction.
## Character to move.
@export var character: WBCharacter
## Direction for the character to walk.
@export var direction: WBCharacter.Dir
## Number of tiles to move the character.
@export_custom(0, "suffix:tiles") var distance: int
## Whether the character should be running.
@export var run: bool = false
func _perform() -> void:
if not character:
push_error("Target character does not exist.")
return
character.running = run
var remaining_distance := distance
while remaining_distance > 0:
character.start_move(direction, true)
await character.move_finished
remaining_distance -= 1

View file

@ -0,0 +1 @@
uid://csxb817wahr5q

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="move_character_event.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="25.715264"
inkscape:cx="5.1720255"
inkscape:cy="9.488528"
inkscape:window-width="1346"
inkscape:window-height="727"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" />
<path
fill="none"
stroke="#8da5f3"
stroke-linejoin="round"
stroke-width="2.28571"
d="M 14.857145,14.857148 H 1.1428572 V 1.1428572 H 14.857145 Z"
id="path1"
style="stroke:#dd5498;stroke-width:2;stroke-dasharray:none" />
<path
style="fill:#8da5f3;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none"
d="m 3.6554165,9.6440776 -0.038887,-2.761006 5.1720255,-1e-7 0.038887,-2.0610327 3.577642,3.4998669 -3.4998673,3.4998663 -0.077775,-2.138807 z"
id="path5"
sodipodi:nodetypes="cccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dc8q0sv4w2lxc"
path="res://.godot/imported/walk_character_event.svg-dcf6bff1559ceda5cbf4918ee7e487a2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/walkabout/events/walk_character_event.svg"
dest_files=["res://.godot/imported/walk_character_event.svg-dcf6bff1559ceda5cbf4918ee7e487a2.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,7 @@
[plugin]
name="WalkAbout"
description="Overworld character movement and cutscenes system."
author="fogwaves"
version="0.1"
script="walkabout_plugin.gd"

View file

@ -0,0 +1,12 @@
@tool
extends EditorPlugin
func _enter_tree() -> void:
# Initialization of the plugin goes here.
pass
func _exit_tree() -> void:
# Clean-up of the plugin goes here.
pass

View file

@ -0,0 +1 @@
uid://bgk2xef0qnnhl