Init project.

master
hheik 2025-09-08 01:06:22 +03:00
commit 8c451c28e7
35 changed files with 1116 additions and 0 deletions

4
godot/.editorconfig Normal file
View File

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
godot/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

3
godot/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

1
godot/icon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 994 B

37
godot/icon.svg.import Normal file
View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bgacydp2nwaus"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16.643429mm"
height="16.735657mm"
viewBox="0 0 16.643429 16.735657"
version="1.1"
id="svg1"
sodipodi:docname="turn_action.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">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="7.2173129"
inkscape:cx="10.668791"
inkscape:cy="23.208083"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<defs
id="defs1" />
<g
inkscape:label="Rendered Gears"
transform="translate(-47.844886,-1.0381718)"
id="g37">
<path
id="path36"
style="fill:#75848d;fill-opacity:1;stroke:#000000;stroke-width:0.264583"
d="m 54.883733,1.2119253 -2.495972,0.8108033 0.709001,1.3854451 -1.685686,1.2252482 -1.098641,-1.102775 -1.542541,2.1233846 1.388029,0.703833 -0.643888,1.9823079 -1.536858,-0.2464966 v 2.6246462 l 1.536858,-0.246497 0.643888,1.982308 -1.388029,0.703833 1.542541,2.123385 1.098641,-1.102775 1.685686,1.225248 -0.709001,1.385445 2.495972,0.810803 0.240812,-1.53789 h 2.08411 l 0.240812,1.53789 2.495972,-0.810803 -0.709517,-1.385445 1.686202,-1.225248 1.098641,1.102775 1.542541,-2.123385 -1.388029,-0.703833 0.643889,-1.982308 1.536857,0.246497 V 8.0936758 L 62.819166,8.3401724 62.175277,6.3578645 63.563306,5.6540315 62.020765,3.5306469 60.922124,4.6334219 59.235922,3.4081737 59.945439,2.0227286 57.449467,1.2119253 57.208655,2.7492992 h -2.08411 z m 1.282609,5.5939738 A 2.6002014,2.6002014 0 0 1 58.7667,9.4057404 2.6002014,2.6002014 0 0 1 56.166342,12.006098 2.6002014,2.6002014 0 0 1 53.5665,9.4057404 2.6002014,2.6002014 0 0 1 56.166342,6.8058991 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6op0q4kbyhwv"
path="res://.godot/imported/turn_action.svg-ba28e62e6dfc65acd8d0077863718b65.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/turn_action.svg"
dest_files=["res://.godot/imported/turn_action.svg-ba28e62e6dfc65acd8d0077863718b65.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

25
godot/nodes/action.gd Normal file
View File

@ -0,0 +1,25 @@
@icon("res://icons/turn_action.svg")
class_name Action
extends Node
# Base class for actions
# Optional predicate for if the action is allowed
func predicate() -> bool:
return true
# Called when the action starts
func action_ready():
pass
# Called every frame, like _process
func action_process(_delta: float):
pass
# Must be emitted when an action is successful
@warning_ignore("unused_signal")
signal done()
# Must be emitted if something went wrong and the action can't be performed
# This could be called for example when an action was checked to be valid, but
# failed during execution
@warning_ignore("unused_signal")
signal abort()

View File

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

View File

@ -0,0 +1,82 @@
@icon("res://icons/turn_action.svg")
class_name ActionDecider
extends Node
var actor: TurnActor
var current_action: Action
func get_actor():
var parent = get_parent()
if parent is TurnActor:
return parent
else:
return null
func connect_to_actor():
actor.connect("turn_started", handle_turn_start)
actor.connect("deciding_action", handle_decide_action)
actor.connect("performing_action", handle_perform_action)
actor.connect("turn_ended", handle_turn_end)
func handle_turn_start():
#print("turn start!")
pass
func handle_decide_action():
#print("deciding action...")
pass
func handle_perform_action():
#print("performing action: ", actor.get_current_action())
pass
func handle_turn_end():
#print("turn end!")
pass
func is_deciding() -> bool:
return actor.is_deciding()
func try_perform(action_node: Action) -> bool:
if current_action:
push_error("Tried to start an action while another one is performing! Current action: ", current_action.name)
return false
add_child(action_node)
if action_node.predicate():
inner_perform(action_node)
return true
else:
action_node.queue_free()
return false
func inner_perform(action_node: Action):
current_action = action_node
actor.perform_action(action_node.name)
current_action.done.connect(_on_action_done, CONNECT_ONE_SHOT)
current_action.abort.connect(_on_action_abort, CONNECT_ONE_SHOT)
current_action.action_ready()
func _on_action_done():
current_action.abort.disconnect(_on_action_abort)
inner_action_cleanup()
func _on_action_abort():
push_error("Action aborted: ", current_action.get_path())
current_action.done.disconnect(_on_action_done)
inner_action_cleanup()
func inner_action_cleanup():
current_action.queue_free()
current_action = null
actor.end_turn()
func _ready():
actor = get_actor()
if not actor is TurnActor:
push_error("Couldn't get TurnActor from TurnAction")
else:
connect_to_actor()
func _process(delta: float):
if current_action:
current_action.action_process(delta)

View File

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

View File

@ -0,0 +1,22 @@
@icon("res://icons/turn_action.svg")
class_name MoveAction
extends Action
var mover: GridPosition
var dir: Vector2i
func _init(new_mover: GridPosition, new_dir: Vector2i) -> void:
mover = new_mover
dir = new_dir
func predicate():
return mover.can_move(dir)
func action_ready():
mover.finished_moving.connect(_on_mover_done, CONNECT_ONE_SHOT)
if not mover.try_move(dir):
mover.finished_moving.disconnect(_on_mover_done)
abort.emit()
func _on_mover_done(_dir: Vector2i):
done.emit()

View File

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

View File

@ -0,0 +1,6 @@
@icon("res://icons/turn_action.svg")
class_name SkipAction
extends Action
func action_ready():
done.emit()

View File

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

View File

@ -0,0 +1,25 @@
[gd_scene load_steps=3 format=3 uid="uid://drs6h7ks4r2ta"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_72ieh"]
[ext_resource type="Script" uid="uid://sxo578w2yds2" path="res://prefabs/player/player_input.gd" id="2_rdx4y"]
[node name="Player" type="TurnActor"]
[node name="Input" type="Node2D" parent="."]
script = ExtResource("2_rdx4y")
metadata/_custom_type_script = "uid://okxdlbfuvb1b"
[node name="GridPosition" type="GridPosition" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="GridPosition"]
texture = ExtResource("1_72ieh")
centered = false
hframes = 8
vframes = 8
frame = 1
[node name="Camera2D" type="Camera2D" parent="GridPosition"]
position = Vector2(16, 16)
zoom = Vector2(2, 2)
[node name="Gatherer" type="Node" parent="GridPosition"]

View File

@ -0,0 +1,47 @@
@icon("res://icons/turn_action.svg")
extends ActionDecider
@onready var grid_position: GridPosition = $"../GridPosition"
func poll_movement_dir():
var movement_dir = Vector2i.ZERO
if Input.is_action_pressed("move_up"):
movement_dir = Vector2i.UP
elif Input.is_action_pressed("move_down"):
movement_dir = Vector2i.DOWN
elif Input.is_action_pressed("move_left"):
movement_dir = Vector2i.LEFT
elif Input.is_action_pressed("move_right"):
movement_dir = Vector2i.RIGHT
return movement_dir
func _input(event: InputEvent) -> void:
if not is_deciding():
return
if event is InputEventKey:
if event.is_pressed():
match event.keycode:
KEY_SPACE:
print("skip turn!")
if try_perform(SkipAction.new()):
return
#KEY_UP:
#if try_perform(MoveAction.new(grid_position, Vector2i.UP)):
#return
#KEY_DOWN:
#if try_perform(MoveAction.new(grid_position, Vector2i.DOWN)):
#return
#KEY_LEFT:
#if try_perform(MoveAction.new(grid_position, Vector2i.LEFT)):
#return
#KEY_RIGHT:
#if try_perform(MoveAction.new(grid_position, Vector2i.RIGHT)):
#return
func _process(_delta: float) -> void:
if not is_deciding():
return
var movement_dir = poll_movement_dir()
if movement_dir != Vector2i.ZERO:
if try_perform(MoveAction.new(grid_position, movement_dir)):
return

View File

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

62
godot/project.godot Normal file
View File

@ -0,0 +1,62 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Velho Peli"
run/main_scene="uid://b3odri2wtvke4"
config/features=PackedStringArray("4.4", "Forward Plus")
config/icon="res://icon.svg"
[display]
window/size/viewport_width=1216
window/size/viewport_height=704
window/vsync/vsync_mode=0
[dotnet]
project/assembly_name="Velho Peli"
[input]
move_up={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_down={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
skip_turn={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
[rendering]
textures/canvas_textures/default_texture_filter=0

View File

@ -0,0 +1,21 @@
[gd_scene load_steps=4 format=4 uid="uid://b3odri2wtvke4"]
[ext_resource type="TileSet" uid="uid://b1ps1ww0rtkop" path="res://tilesets/background_tileset.tres" id="1_m1b5j"]
[ext_resource type="TileSet" uid="uid://bxwohuw2p43k1" path="res://tilesets/foreground_tileset.tres" id="2_u2ss0"]
[ext_resource type="PackedScene" uid="uid://drs6h7ks4r2ta" path="res://prefabs/player/player.tscn" id="3_u2ss0"]
[node name="Root" type="Level" node_paths=PackedStringArray("background", "foreground")]
background = NodePath("Background")
foreground = NodePath("Foreground")
[node name="Background" type="TileMapLayer" parent="."]
tile_map_data = PackedByteArray("AAD8//z/AQAAAAQAAAD8//3/AQAAAAQAAAD8//7/AQAAAAQAAAD8////AQAAAAQAAAD9//z/AQAAAAQAAAD9//3/AQAAAAQAAAD9//7/AQAAAAQAAAD9////AQAAAAQAAAD+//z/AQAAAAQAAAD+//3/AQAAAAQAAAD+//7/AQAAAAQAAAD+////AQAAAAQAAAD///z/AQAAAAQAAAD///3/AQAAAAQAAAD///7/AQAAAAQAAAD/////AQAAAAQAAAD8/wAAAQAAAAQAAAD8/wEAAQAAAAQAAAD8/wIAAQAAAAQAAAD8/wMAAQAAAAQAAAD9/wAAAQAAAAQAAAD9/wEAAQAAAAQAAAD9/wIAAQAAAAQAAAD9/wMAAQAAAAQAAAD+/wAAAQAAAAQAAAD+/wEAAQAAAAQAAAD+/wIAAQAAAAQAAAD+/wMAAQAAAAQAAAD//wAAAQAAAAQAAAD//wEAAQAAAAQAAAD//wIAAQAAAAQAAAD//wMAAQAAAAQAAAAAAPz/AQAAAAQAAAAAAP3/AQAAAAQAAAAAAP7/AQAAAAQAAAAAAP//AQAAAAQAAAAAAAAAAQAAAAQAAAAAAAEAAQAAAAQAAAAAAAIAAQAAAAQAAAAAAAMAAQAAAAQAAAABAPz/AQAAAAQAAAABAP3/AQAAAAQAAAABAP7/AQAAAAQAAAABAP//AQAAAAQAAAABAAAAAQAAAAQAAAABAAEAAQAAAAQAAAABAAIAAQAAAAQAAAABAAMAAQAAAAQAAAACAPz/AQAAAAQAAAACAP3/AQAAAAQAAAACAP7/AQAAAAQAAAACAP//AQAAAAQAAAACAAAAAQAAAAQAAAACAAEAAQAAAAQAAAACAAIAAQAAAAQAAAACAAMAAQAAAAQAAAADAPz/AQAAAAQAAAADAP3/AQAAAAQAAAADAP7/AQAAAAQAAAADAP//AQAAAAQAAAADAAAAAQAAAAQAAAADAAEAAQAAAAQAAAADAAIAAQAAAAQAAAADAAMAAQAAAAQAAAD6//v/AQAAAAQAAAD6//z/AQAAAAQAAAD6//3/AQAAAAQAAAD6//7/AQAAAAQAAAD6////AQAAAAQAAAD6/wAAAQAAAAQAAAD6/wEAAQAAAAQAAAD6/wIAAQAAAAQAAAD6/wMAAQAAAAQAAAD6/wQAAQAAAAQAAAD6/wUAAQAAAAQAAAD7//v/AQAAAAQAAAD7//z/AQAAAAQAAAD7//3/AQAAAAQAAAD7//7/AQAAAAQAAAD7////AQAAAAQAAAD7/wAAAQAAAAQAAAD7/wEAAQAAAAQAAAD7/wIAAQAAAAQAAAD7/wMAAQAAAAQAAAD7/wQAAQAAAAQAAAD7/wUAAQAAAAQAAAD8//v/AQAAAAQAAAD8/wQAAQAAAAQAAAD8/wUAAQAAAAQAAAD9//v/AQAAAAQAAAD9/wQAAQAAAAQAAAD9/wUAAQAAAAQAAAD+//v/AQAAAAQAAAD+/wQAAQAAAAQAAAD+/wUAAQAAAAQAAAD///v/AQAAAAQAAAD//wQAAQAAAAQAAAD//wUAAQAAAAQAAAAAAPv/AQAAAAQAAAAAAAQAAQAAAAQAAAAAAAUAAQAAAAQAAAABAPv/AQAAAAQAAAABAAQAAQAAAAQAAAABAAUAAQAAAAQAAAACAPv/AQAAAAQAAAACAAQAAQAAAAQAAAACAAUAAQAAAAQAAAADAPr/AQABAAQAAAADAPv/AQAAAAQAAAADAAQAAQAAAAQAAAADAAUAAQAAAAQAAAAEAPr/AQABAAQAAAAEAPv/AQABAAQAAAAEAPz/AQABAAQAAAAEAP3/AQABAAQAAAAEAP7/AQAAAAQAAAAEAP//AQAAAAQAAAAEAAAAAQAAAAQAAAAEAAEAAQAAAAQAAAAEAAIAAQAAAAQAAAAEAAMAAQAAAAQAAAAEAAQAAQAAAAQAAAAEAAUAAQAAAAQAAAAFAPr/AQABAAQAAAAFAPv/AQABAAQAAAAFAPz/AQABAAQAAAAFAP3/AQABAAQAAAAFAP7/AQAAAAQAAAAFAP//AQAAAAQAAAAFAAAAAQAAAAQAAAAFAAEAAQAAAAQAAAAFAAIAAQAAAAQAAAAFAAMAAQAAAAQAAAAFAAQAAQAAAAQAAAAFAAUAAQAAAAQAAAAGAPj/AQABAAQAAAAGAPn/AQABAAQAAAAGAPr/AQABAAQAAAAGAPv/AQABAAQAAAAGAPz/AQABAAQAAAAHAPj/AQABAAQAAAAHAPn/AQABAAQAAAAHAPr/AQABAAQAAAAHAPv/AQABAAQAAAAHAPz/AQABAAQAAAAIAPj/AQABAAQAAAAIAPn/AQABAAQAAAAIAPr/AQABAAQAAAAIAPv/AQABAAQAAAAIAPz/AQABAAQAAAAJAPj/AQABAAQAAAAJAPn/AQABAAQAAAAJAPr/AQABAAQAAAAJAPv/AQABAAQAAAAJAPz/AQABAAQAAAAKAPj/AQABAAQAAAAKAPn/AQABAAQAAAAKAPr/AQABAAQAAAAKAPv/AQABAAQAAAAKAPz/AQABAAQAAAAEAPn/AQABAAQAAAAGAP3/AQABAAQAAAAHAP3/AQABAAQAAAAIAP3/AQABAAQAAAAIAP7/AQABAAQAAAAHAP7/AQABAAQAAAAEAPj/AQABAAQAAAAEAPf/AQABAAQAAAAFAPf/AQABAAQAAAAFAPj/AQABAAQAAAAFAPn/AQABAAQAAAAHAPf/AQABAAQAAAAGAPf/AQABAAQAAAAIAPf/AQABAAQAAAAJAPf/AQABAAQAAAAKAPf/AQABAAQAAAALAPn/AQABAAQAAAALAPr/AQABAAQAAAALAPv/AQABAAQAAAAKAP3/AQABAAQAAAAJAP3/AQABAAQAAAD3//v/AQAAAAQAAAD3//z/AQAAAAQAAAD3//3/AQAAAAQAAAD3//7/AQAAAAQAAAD3////AQAAAAQAAAD3/wAAAQAAAAQAAAD3/wEAAQAAAAQAAAD3/wIAAQAAAAQAAAD3/wMAAQAAAAQAAAD3/wQAAQAAAAQAAAD3/wUAAQAAAAQAAAD4//v/AQAAAAQAAAD4//z/AQAAAAQAAAD4//3/AQAAAAQAAAD4//7/AQAAAAQAAAD4////AQAAAAQAAAD4/wAAAQAAAAQAAAD4/wEAAQAAAAQAAAD4/wIAAQAAAAQAAAD4/wMAAQAAAAQAAAD4/wQAAQAAAAQAAAD4/wUAAQAAAAQAAAD5//v/AQAAAAQAAAD5//z/AQAAAAQAAAD5//3/AQAAAAQAAAD5//7/AQAAAAQAAAD5////AQAAAAQAAAD5/wAAAQAAAAQAAAD5/wEAAQAAAAQAAAD5/wIAAQAAAAQAAAD5/wMAAQAAAAQAAAD5/wQAAQAAAAQAAAD5/wUAAQAAAAQAAAAGAP//AQAAAAQAAAAGAAAAAQAAAAQAAAAGAAEAAQAAAAQAAAAGAAIAAQAAAAQAAAAGAAMAAQAAAAQAAAAGAAQAAQAAAAQAAAAGAAUAAQAAAAQAAAAHAP//AQAAAAQAAAAHAAAAAQAAAAQAAAAHAAEAAQAAAAQAAAAHAAIAAQAAAAQAAAAHAAMAAQAAAAQAAAAHAAQAAQAAAAQAAAAHAAUAAQAAAAQAAAAIAP//AQAAAAQAAAAIAAAAAQAAAAQAAAAIAAEAAQAAAAQAAAAIAAIAAQAAAAQAAAAIAAMAAQAAAAQAAAAIAAQAAQAAAAQAAAAIAAUAAQAAAAQAAAAJAP//AQAAAAQAAAAJAAAAAQAAAAQAAAAJAAEAAQAAAAQAAAAJAAIAAQAAAAQAAAAJAAMAAQAAAAQAAAAJAAQAAQAAAAQAAAAJAAUAAQAAAAQAAAAJAP7/AQABAAQAAAAGAP7/AQABAAQAAAA=")
tile_set = ExtResource("1_m1b5j")
[node name="Foreground" type="TileMapLayer" parent="."]
tile_map_data = PackedByteArray("AAD/////AQAAAAMAAAAEAAEAAQAAAAIAAAADAAEAAQAAAAIAAAADAAIAAQAAAAIAAAADAAMAAQAAAAIAAAACAAMAAQAAAAIAAAAFAAMAAQABAAIAAAAGAAMAAQABAAIAAAD8//3/AQABAAMAAAD9//3/AQABAAMAAAD9//7/AQABAAMAAAD9/wAAAQABAAMAAAD9/wEAAQABAAMAAAD8/wEAAQABAAMAAAAEAPv/AQACAAMAAAAGAP3/AQACAAMAAAAHAP7/AQACAAMAAAAIAP7/AQACAAMAAAAJAP7/AQACAAMAAAAGAPn/AQACAAMAAAAGAPr/AQACAAMAAAAGAPv/AQACAAMAAAAHAPv/AQACAAMAAAAIAPv/AQACAAMAAAAIAPz/AQACAAMAAAAJAPz/AQACAAMAAAAKAPz/AQACAAMAAAAKAPv/AQACAAMAAAAJAPv/AQACAAMAAAAIAPr/AQACAAMAAAAHAPr/AQACAAMAAAAHAPn/AQACAAMAAAD3//v/AQABAAMAAAD3//z/AQABAAMAAAD3//3/AQABAAMAAAD3//7/AQABAAMAAAD3////AQABAAMAAAD3/wAAAQABAAMAAAD3/wEAAQABAAMAAAD3/wIAAQABAAMAAAD4//v/AQABAAMAAAD4//z/AQABAAMAAAD4//3/AQABAAMAAAD4//7/AQABAAMAAAD4////AQABAAMAAAD4/wAAAQABAAMAAAD4/wEAAQABAAMAAAD4/wIAAQABAAMAAAD3/wMAAQABAAMAAAD3/wQAAQABAAMAAAD3/wUAAQABAAMAAAD4/wMAAQABAAMAAAD4/wQAAQABAAMAAAD4/wUAAQABAAMAAAD5/wUAAQABAAMAAAD6/wUAAQABAAMAAAD7/wUAAQABAAMAAAD8/wUAAQABAAMAAAD9/wUAAQABAAMAAAD+/wUAAQABAAMAAAD//wUAAQABAAMAAAAAAAUAAQABAAMAAAABAAUAAQABAAMAAAACAAUAAQABAAMAAAADAAUAAQABAAMAAAAEAAUAAQABAAMAAAAFAAUAAQABAAMAAAAGAAUAAQABAAMAAAAHAAUAAQABAAMAAAAIAAUAAQABAAMAAAAJAAUAAQABAAMAAAD5//v/AQABAAMAAAD6//v/AQABAAMAAAD7//v/AQABAAMAAAD8//v/AQABAAMAAAD9//v/AQABAAMAAAD+//v/AQABAAMAAAD///v/AQABAAMAAAAAAPv/AQABAAMAAAABAPv/AQABAAMAAAACAPv/AQABAAMAAAADAPv/AQABAAMAAAAJAP//AQABAAMAAAAJAAAAAQABAAMAAAAJAAEAAQABAAMAAAAJAAIAAQABAAMAAAAJAAMAAQABAAMAAAAJAAQAAQABAAMAAAD7/wEAAQABAAMAAAD7//3/AQABAAMAAAD6//3/AQABAAMAAAD6/wEAAQABAAMAAAD6//7/AQABAAMAAAD6/wAAAQABAAMAAAA=")
tile_set = ExtResource("2_u2ss0")
[node name="Player" parent="." instance=ExtResource("3_u2ss0")]
[node name="TurnManager" type="TurnManager" parent="."]

Binary file not shown.

BIN
godot/sprites/sheet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://doscvutq8uqmd"
path="res://.godot/imported/sheet.png-611d8b977306b2c8b91271f64fa10ad9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sprites/sheet.png"
dest_files=["res://.godot/imported/sheet.png-611d8b977306b2c8b91271f64fa10ad9.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

View File

@ -0,0 +1,13 @@
[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://b1ps1ww0rtkop"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_ge1ul"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_5boi2"]
texture = ExtResource("1_ge1ul")
texture_region_size = Vector2i(32, 32)
0:4/0 = 0
1:4/0 = 0
[resource]
tile_size = Vector2i(32, 32)
sources/1 = SubResource("TileSetAtlasSource_5boi2")

View File

@ -0,0 +1,13 @@
[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://7lfy0ix4n65n"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_1tjj6"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_yenu7"]
texture = ExtResource("1_1tjj6")
texture_region_size = Vector2i(32, 32)
0:0/0 = 0
1:0/0 = 0
[resource]
tile_size = Vector2i(32, 32)
sources/0 = SubResource("TileSetAtlasSource_yenu7")

View File

@ -0,0 +1,24 @@
[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://bxwohuw2p43k1"]
[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_oe62d"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_rc83b"]
texture = ExtResource("1_oe62d")
texture_region_size = Vector2i(32, 32)
0:3/0 = 0
0:3/0/custom_data_0 = true
0:2/0 = 0
0:2/0/custom_data_1 = true
1:2/0 = 0
1:3/0 = 0
1:3/0/custom_data_0 = true
2:3/0 = 0
2:3/0/custom_data_0 = true
[resource]
tile_size = Vector2i(32, 32)
custom_data_layer_0/name = "has_collision"
custom_data_layer_0/type = 1
custom_data_layer_1/name = "is_berry"
custom_data_layer_1/type = 1
sources/1 = SubResource("TileSetAtlasSource_rc83b")

View File

@ -0,0 +1,14 @@
[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.1
reloadable = true
[libraries]
linux.debug.x86_64 = "res://../rust/target/debug/libvelho.so"
linux.release.x86_64 = "res://../rust/target/release/libvelho.so"
windows.debug.x86_64 = "res://../rust/target/debug/velho.dll"
windows.release.x86_64 = "res://../rust/target/release/velho.dll"
macos.debug = "res://../rust/target/debug/libvelho.dylib"
macos.release = "res://../rust/target/release/libvelho.dylib"
macos.debug.arm64 = "res://../rust/target/debug/libvelho.dylib"
macos.release.arm64 = "res://../rust/target/release/libvelho.dylib"

View File

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

1
rust/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

203
rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,203 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "gdextension-api"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ec0a03c8f9c91e3d8eb7ca56dea81c7248c03826dd3f545f33cd22ef275d4d1"
[[package]]
name = "glam"
version = "0.30.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85"
[[package]]
name = "godot"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab462a365a4b8bcfcdaf93afe767a189a183edfb1d1e697bde9fb26c3f9bfbe9"
dependencies = [
"godot-core",
"godot-macros",
]
[[package]]
name = "godot-bindings"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597469df0a890b2c7f102b094942e6b184e7ee573adf6ed35fad0421ee86b8fa"
dependencies = [
"gdextension-api",
]
[[package]]
name = "godot-cell"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e48bf49cf5d6ef0b64b2a58be980e4854f57e75b822889d639f96c3c098a5ec"
[[package]]
name = "godot-codegen"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282e80108e93950e66ed3a40f7dcb6f61978ecb0dd2d19ad7028799d6a5537cb"
dependencies = [
"godot-bindings",
"heck",
"nanoserde",
"proc-macro2",
"quote",
"regex",
]
[[package]]
name = "godot-core"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2593ce360312bdec4c45994402dc624c43c3dd8741c8e6391958626dcf635b40"
dependencies = [
"glam",
"godot-bindings",
"godot-cell",
"godot-codegen",
"godot-ffi",
]
[[package]]
name = "godot-ffi"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce5ae6a7ae29a753f2f0af3d2a2f3194fa482d9900fc167f35a0738fe7d54d2"
dependencies = [
"godot-bindings",
"godot-codegen",
"godot-macros",
"libc",
]
[[package]]
name = "godot-macros"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0f3ebf0fe09733267c4631ae0af2d6b1008b322d032832ddcfb4dfc54607ad"
dependencies = [
"godot-bindings",
"proc-macro2",
"quote",
"venial",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "nanoserde"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a36fb3a748a4c9736ed7aeb5f2dfc99665247f1ce306abbddb2bf0ba2ac530a4"
dependencies = [
"nanoserde-derive",
]
[[package]]
name = "nanoserde-derive"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a846cbc04412cf509efcd8f3694b114fc700a035fb5a37f21517f9fb019f1ebc"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "velho"
version = "0.1.0"
dependencies = [
"godot",
]
[[package]]
name = "venial"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a42528baceab6c7784446df2a10f4185078c39bf73dc614f154353f1a6b1229"
dependencies = [
"proc-macro2",
"quote",
]

10
rust/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "velho"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
godot = "0.3.5"

136
rust/src/common.rs Normal file
View File

@ -0,0 +1,136 @@
use godot::{classes::*, prelude::*};
#[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)]
#[godot(via = GString)]
pub enum TurnActorState {
/// The default value in Godot.
/// Turn manager should call `start_turn` to advance.
#[default]
WaitingForTurn,
/// Currently just automatically and immediately advances to `DecidingAction` state.
/// This is reserved for animations and such in the future.
TurnStarted,
/// An action decision script should call `take_control` to advance from this state.
DecidingAction,
/// An action script should call `end_turn` to advance from this state.
PerformingAction,
}
/// Turn breakdown:
///
/// Actor can be in 4 different states:
/// 1. Waiting for the turn to start. `TurnActor`s start in this state. (`turn_ended` emitted)
/// 2. Turn has started, waiting for enabled controls (`turn_started` emitted)
/// 3. Deciding what to do (`deciding_action` emitted)
/// 4. Performing the action (`performing_action` emitted)
/// ```
#[derive(Debug, GodotClass)]
#[class(init, base=Node2D)]
pub struct TurnActor {
state: TurnActorState,
current_action: Option<GString>,
base: Base<Node2D>,
}
#[godot_api]
impl INode2D for TurnActor {
fn ready(&mut self) {}
}
#[godot_api]
impl TurnActor {
#[func]
pub fn get_state(&self) -> TurnActorState {
self.state
}
#[func]
pub fn get_current_action(&self) -> Variant {
match self.current_action.clone() {
Some(action) => action.to_variant(),
None => Variant::nil(),
}
}
#[func]
pub fn is_deciding(&self) -> bool {
self.state == TurnActorState::DecidingAction
}
#[func]
pub fn is_my_turn(&self) -> bool {
match self.state {
TurnActorState::TurnStarted
| TurnActorState::DecidingAction
| TurnActorState::PerformingAction => true,
TurnActorState::WaitingForTurn => false,
}
}
/// Should be called by a turn manager
#[func]
pub fn start_turn(&mut self) {
if self.state != TurnActorState::WaitingForTurn {
godot_error!(
"TurnActor: incorrect state transfer. Called 'start_turn' while the actor is in state {:?} (expected WaitingForTurn)",
self.state,
);
}
self.state = TurnActorState::TurnStarted;
self.signals().turn_started().emit();
self.start_deciding();
}
/// Currently called automatically after the turn is started
#[func]
pub fn start_deciding(&mut self) {
if self.state != TurnActorState::TurnStarted {
godot_error!(
"TurnActor: incorrect state transfer. Called 'start_deciding' while the actor is in state {:?} (expected TurnStarted)",
self.state,
);
}
self.state = TurnActorState::DecidingAction;
self.signals().deciding_action().emit();
}
/// Should be called by an action script.
#[func]
pub fn perform_action(&mut self, action_name: GString) {
if self.state != TurnActorState::DecidingAction {
godot_error!(
"TurnActor: incorrect state transfer. Called 'take_control(\"{action_name}\")' while the actor is in state {:?} (expected DecidingAction)",
self.state,
);
}
self.state = TurnActorState::PerformingAction;
self.current_action = Some(action_name);
self.signals().performing_action().emit();
}
/// Should be called by an action script after it's done.
#[func]
pub fn end_turn(&mut self) {
if self.state != TurnActorState::PerformingAction {
godot_error!(
"TurnActor: incorrect state transfer. Called 'end_turn' while the actor is in state {:?} (expected PerformingAction)",
self.state,
);
}
self.state = TurnActorState::WaitingForTurn;
self.current_action = None;
self.signals().turn_ended().emit();
}
#[signal]
pub fn turn_started();
#[signal]
pub fn deciding_action();
#[signal]
pub fn performing_action();
#[signal]
pub fn turn_ended();
}

174
rust/src/level.rs Normal file
View File

@ -0,0 +1,174 @@
use godot::{classes::*, prelude::*};
#[derive(Debug, GodotClass)]
#[class(init, base=Node)]
pub struct Level {
#[export]
background: Option<Gd<TileMapLayer>>,
#[export]
foreground: Option<Gd<TileMapLayer>>,
base: Base<Node>,
}
#[godot_api]
impl INode for Level {
fn ready(&mut self) {
if self.background.is_none() {
self.background = self.base().try_get_node_as::<TileMapLayer>("./Background");
}
if self.foreground.is_none() {
self.foreground = self.base().try_get_node_as::<TileMapLayer>("./Foreground");
}
}
}
#[godot_api]
impl Level {
const HAS_COLLISION: &str = "has_collision";
#[func]
pub fn find_from_node(node: Gd<Node>) -> Option<Gd<Self>> {
let mut current = node.clone();
while let Some(parent) = current.get_parent() {
match parent.try_cast::<Self>() {
Ok(level) => return Some(level),
Err(other) => current = other,
}
}
None
}
#[func]
pub fn tile_has_collision(&self, coords: Vector2i) -> bool {
self.foreground
.as_ref()
.is_some_and(|layer| get_custom_data_bool(layer, coords, Self::HAS_COLLISION))
|| self
.background
.as_ref()
.is_some_and(|layer| get_custom_data_bool(layer, coords, Self::HAS_COLLISION))
}
#[func]
pub fn get_fg(&self) -> Gd<TileMapLayer> {
self.foreground.clone().unwrap()
}
}
fn get_custom_data_bool(layer: &Gd<TileMapLayer>, coords: Vector2i, custom_data: &str) -> bool {
layer.get_cell_tile_data(coords).is_some_and(|tile| {
tile.has_custom_data(custom_data) && tile.get_custom_data(custom_data).booleanize()
})
}
#[derive(Debug, GodotClass)]
#[class(init, base=Node2D)]
pub struct GridPosition {
#[export]
#[init(val = Vector2::splat(32.0))]
grid_size: Vector2,
#[export]
#[init(val = 30.0)]
movement_speed: f32,
#[export_group(name = "Flags")]
#[export]
#[init(val = false)]
ignore_collisions: bool,
is_moving: bool,
target_coords: Vector2i,
base: Base<Node2D>,
}
#[godot_api]
impl INode2D for GridPosition {
fn ready(&mut self) {
self.target_coords = self.get_coords();
}
fn process(&mut self, delta: f64) {
if self.is_moving {
let start = self.base().get_global_position();
let target_coords = self.target_coords;
let movement_speed = self.movement_speed;
let end = self.get_target_pos();
if start.distance_squared_to(end) <= Self::TILE_SNAP_DIST_SQR {
self.base_mut().set_global_position(end);
self.is_moving = false;
self.signals().finished_moving().emit(target_coords);
} else {
// self.base_mut()
// .set_global_position(start.move_toward(end, movement_speed * delta));
self.base_mut()
.set_global_position(start.lerp(end, movement_speed * delta as f32));
}
}
}
}
#[godot_api]
impl GridPosition {
const TILE_SNAP_DIST_SQR: f32 = 1.0;
fn level(&self) -> Option<Gd<Level>> {
Level::find_from_node(self.to_gd().upcast::<Node>())
}
#[func]
pub fn get_target_pos(&self) -> Vector2 {
self.target_coords.cast_float() * self.grid_size
}
#[func]
pub fn get_coords(&self) -> Vector2i {
(self.base().get_global_position() / self.grid_size)
.round()
.cast_int()
}
#[func]
pub fn set_coords(&mut self, pos: Vector2i) {
let grid_size = self.grid_size;
self.base_mut()
.set_global_position(pos.cast_float() * grid_size);
}
#[func]
pub fn can_move(&self, dir: Vector2i) -> bool {
if self.is_moving {
return false;
}
if self.ignore_collisions {
return true;
}
match self.level() {
Some(level) => !level.bind().tile_has_collision(self.get_coords() + dir),
None => true,
}
}
#[func]
pub fn try_move(&mut self, dir: Vector2i) -> bool {
let start_coords = self.target_coords;
let target_coords = start_coords + dir;
if !self.can_move(dir) {
return false;
}
self.is_moving = true;
self.target_coords = target_coords;
self.signals().started_moving().emit(start_coords, dir);
true
}
#[signal]
fn started_moving(from_coords: Vector2i, dir: Vector2i);
#[signal]
fn finished_moving(coords: Vector2i);
}

10
rust/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
use godot::prelude::*;
pub struct VelhoExtension;
mod common;
mod level;
mod turn_manager;
#[gdextension]
unsafe impl ExtensionLibrary for VelhoExtension {}

58
rust/src/turn_manager.rs Normal file
View File

@ -0,0 +1,58 @@
use godot::{
classes::{object::ConnectFlags, *},
prelude::*,
};
use crate::common::TurnActor;
#[derive(Debug, GodotClass)]
#[class(init, base=Node)]
pub struct TurnManager {
round_queue: Array<Gd<TurnActor>>,
current_actor: Option<Gd<TurnActor>>,
base: Base<Node>,
}
#[godot_api]
impl INode for TurnManager {
fn process(&mut self, _delta: f64) {
if self.current_actor.is_none() && self.round_queue.is_empty() {
self.start_round();
}
}
}
#[godot_api]
impl TurnManager {
fn start_round(&mut self) {
self.round_queue = self.find_sibling_actors();
godot_print!("New round: {:?}", self.round_queue);
self.start_next_turn();
}
fn start_next_turn(&mut self) {
self.current_actor = self.round_queue.pop();
if let Some(mut actor) = self.current_actor.clone() {
actor
.signals()
.turn_ended()
.builder()
.flags(ConnectFlags::ONE_SHOT)
.connect_other_mut(self, |this| this.start_next_turn());
actor.bind_mut().start_turn();
}
}
fn find_sibling_actors(&self) -> Array<Gd<TurnActor>> {
let mut actors: Array<Gd<TurnActor>> = Array::new();
self.base()
.get_parent()
.unwrap()
.get_children()
.iter_shared()
.filter_map(|node| node.try_cast::<TurnActor>().ok())
.for_each(|actor| actors.push(&actor));
actors
}
}