From f74dcdba2c82b67c73b578c9039c4974b40db746 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:41:03 +0300 Subject: [PATCH] Added berry picking --- godot/prefabs/player/player.gd | 13 ++ godot/prefabs/player/player.gd.uid | 1 + godot/prefabs/player/player.tscn | 14 +- godot/prefabs/tiles/blueberry.tscn | 26 ++++ godot/prefabs/tiles/cowberry.tscn | 27 ++++ godot/prefabs/tiles/gatherable_animation.gd | 16 +++ .../prefabs/tiles/gatherable_animation.gd.uid | 1 + godot/scenes/overworld.tscn | 5 +- godot/sprites/sheet.aseprite | Bin 2172 -> 2438 bytes godot/sprites/sheet.png | Bin 3100 -> 3350 bytes godot/tilesets/foreground_tileset.tres | 18 +-- rust/src/gathering.rs | 124 +++++++++++++++++ rust/src/{overworld.rs => grid.rs} | 130 +++++++----------- rust/src/lib.rs | 3 +- rust/src/turn.rs | 13 +- rust/src/utils.rs | 54 ++++---- 16 files changed, 319 insertions(+), 126 deletions(-) create mode 100644 godot/prefabs/player/player.gd create mode 100644 godot/prefabs/player/player.gd.uid create mode 100644 godot/prefabs/tiles/blueberry.tscn create mode 100644 godot/prefabs/tiles/cowberry.tscn create mode 100644 godot/prefabs/tiles/gatherable_animation.gd create mode 100644 godot/prefabs/tiles/gatherable_animation.gd.uid create mode 100644 rust/src/gathering.rs rename rust/src/{overworld.rs => grid.rs} (67%) diff --git a/godot/prefabs/player/player.gd b/godot/prefabs/player/player.gd new file mode 100644 index 0000000..eea81d0 --- /dev/null +++ b/godot/prefabs/player/player.gd @@ -0,0 +1,13 @@ +class_name Player +extends GridPosition + +@export var gatherer: Gatherer + +static func is_player(node: Node) -> bool: + return node is Player || node.get_parent() is Player + +func _ready() -> void: + gatherer.gathered.connect(_on_gathered) + +func _on_gathered(item: String, count: int): + print("gathered: ", count, " x ", item) diff --git a/godot/prefabs/player/player.gd.uid b/godot/prefabs/player/player.gd.uid new file mode 100644 index 0000000..b19ffc9 --- /dev/null +++ b/godot/prefabs/player/player.gd.uid @@ -0,0 +1 @@ +uid://cvviym6gdlod8 diff --git a/godot/prefabs/player/player.tscn b/godot/prefabs/player/player.tscn index e38f81f..7d1abd8 100644 --- a/godot/prefabs/player/player.tscn +++ b/godot/prefabs/player/player.tscn @@ -1,29 +1,27 @@ -[gd_scene load_steps=3 format=3 uid="uid://drs6h7ks4r2ta"] +[gd_scene load_steps=4 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://cvviym6gdlod8" path="res://prefabs/player/player.gd" id="1_wv1mm"] [ext_resource type="Script" uid="uid://sxo578w2yds2" path="res://prefabs/player/player_input.gd" id="2_rdx4y"] -[node name="Player" type="GridPosition"] +[node name="Player" type="GridPosition" node_paths=PackedStringArray("gatherer")] +script = ExtResource("1_wv1mm") +gatherer = NodePath("Gatherer") [node name="Sprite2D" type="Sprite2D" parent="."] texture = ExtResource("1_72ieh") -centered = false hframes = 8 vframes = 8 frame = 1 [node name="Camera2D" type="Camera2D" parent="."] -position = Vector2(16, 16) zoom = Vector2(2, 2) -[node name="Gatherer" type="Gatherer" parent="." node_paths=PackedStringArray("grid", "actor")] -grid = NodePath("..") -actor = NodePath("../TurnActor") +[node name="Gatherer" type="Gatherer" parent="."] [node name="Mover" type="Mover" parent="."] [node name="TurnActor" type="TurnActor" parent="."] -position = Vector2(-1, 0) [node name="ActionDecider" type="Node2D" parent="." node_paths=PackedStringArray("grid", "mover", "actor")] script = ExtResource("2_rdx4y") diff --git a/godot/prefabs/tiles/blueberry.tscn b/godot/prefabs/tiles/blueberry.tscn new file mode 100644 index 0000000..bf60705 --- /dev/null +++ b/godot/prefabs/tiles/blueberry.tscn @@ -0,0 +1,26 @@ +[gd_scene load_steps=3 format=3 uid="uid://8xuvjmyjkpeo"] + +[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_gjkx7"] +[ext_resource type="Script" uid="uid://jd6ce3f7e1bh" path="res://prefabs/tiles/gatherable_animation.gd" id="1_jqgth"] + +[node name="Blueberry" type="GridPosition" node_paths=PackedStringArray("gatherable", "picked", "not_picked")] +script = ExtResource("1_jqgth") +gatherable = NodePath("Gatherable") +picked = NodePath("Picked") +not_picked = NodePath("Not Picked") + +[node name="Not Picked" type="Sprite2D" parent="."] +texture = ExtResource("1_gjkx7") +hframes = 8 +vframes = 8 +frame = 16 + +[node name="Picked" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("1_gjkx7") +hframes = 8 +vframes = 8 +frame = 17 + +[node name="Gatherable" type="Gatherable" parent="." node_paths=PackedStringArray("grid")] +grid = NodePath("..") diff --git a/godot/prefabs/tiles/cowberry.tscn b/godot/prefabs/tiles/cowberry.tscn new file mode 100644 index 0000000..b73fa94 --- /dev/null +++ b/godot/prefabs/tiles/cowberry.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=3 format=3 uid="uid://l72f05nek0y4"] + +[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_e3ld6"] +[ext_resource type="Script" uid="uid://jd6ce3f7e1bh" path="res://prefabs/tiles/gatherable_animation.gd" id="2_kod7n"] + +[node name="Blueberry" type="GridPosition" node_paths=PackedStringArray("gatherable", "picked", "not_picked")] +script = ExtResource("2_kod7n") +gatherable = NodePath("Gatherable") +picked = NodePath("Picked") +not_picked = NodePath("Not Picked") + +[node name="Not Picked" type="Sprite2D" parent="."] +texture = ExtResource("1_e3ld6") +hframes = 8 +vframes = 8 +frame = 20 + +[node name="Picked" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("1_e3ld6") +hframes = 8 +vframes = 8 +frame = 17 + +[node name="Gatherable" type="Gatherable" parent="." node_paths=PackedStringArray("grid")] +grid = NodePath("..") +item = "Cowberry" diff --git a/godot/prefabs/tiles/gatherable_animation.gd b/godot/prefabs/tiles/gatherable_animation.gd new file mode 100644 index 0000000..19e1048 --- /dev/null +++ b/godot/prefabs/tiles/gatherable_animation.gd @@ -0,0 +1,16 @@ +extends Node + +@export var gatherable: Gatherable +@export var picked: CanvasItem +@export var not_picked: CanvasItem + +func _ready() -> void: + update_visibility(gatherable.get_is_picked()) + gatherable.picked_state_changed.connect(_on_gatherable_state_changed) + +func _on_gatherable_state_changed(is_picked): + update_visibility(is_picked) + +func update_visibility(is_picked: bool): + picked.visible = is_picked + not_picked.visible = not is_picked diff --git a/godot/prefabs/tiles/gatherable_animation.gd.uid b/godot/prefabs/tiles/gatherable_animation.gd.uid new file mode 100644 index 0000000..0388cf2 --- /dev/null +++ b/godot/prefabs/tiles/gatherable_animation.gd.uid @@ -0,0 +1 @@ +uid://jd6ce3f7e1bh diff --git a/godot/scenes/overworld.tscn b/godot/scenes/overworld.tscn index d5d4333..4591630 100644 --- a/godot/scenes/overworld.tscn +++ b/godot/scenes/overworld.tscn @@ -15,9 +15,8 @@ tile_map_data = PackedByteArray("AAD8//z/AQAAAAQAAAD8//3/AQAAAAQAAAD8//7/AQAAAAQ tile_set = ExtResource("1_m1b5j") [node name="Foreground" type="TileMapLayer" parent="Root"] -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_map_data = PackedByteArray("AAD/////AQAAAAMAAAD8//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/wAAAQABAAMAAAD6/wQAAAAAAAAAAgD5/wQAAAAAAAAAAgD5/wMAAAAAAAAAAgAEAAEAAAAAAAAAAQADAAEAAAAAAAAAAQADAAIAAAAAAAAAAQADAAMAAAAAAAAAAQACAAMAAAAAAAAAAQA=") tile_set = ExtResource("2_u2ss0") [node name="Player" parent="Root" instance=ExtResource("3_u2ss0")] - -[node name="Camera2D" type="Camera2D" parent="Root"] +position = Vector2(16, 16) diff --git a/godot/sprites/sheet.aseprite b/godot/sprites/sheet.aseprite index e304de237bf3443cf258d19a1316e1dc7a21d610..0846edee51289500d860c5cccf041d13f8a118cd 100644 GIT binary patch delta 2147 zcmZ`)c~p{#7H7;trxY7YEw_0mm#is5+tiS>(#*_K(-xPRcpjTE12c00{YqSOOUk)7#|tv7mm9!7C$(h?(rVgUiWcofiic?tei{?)AnJ1x1_3M*{<|QHXg<84|k+? z%)YkgBe9*51{x0=Kj;qnJW{txD0`U+))<4aMPRM@UJp`GVBj7M&<1aR#shZugN1mN zVwmsISFr?UiQhOCfBa)%_?b^1yjBqU#V(4Q;4s!4%1(hHb^O*@S(b$2lmT2Xc9PV8 zXg+)MJu2>}>~Y3;g<~JJ&}rW%Gbqr)8c*1{olPx_S~f~&py0h3qlX0v2Tk8ij$`it z#EY7&06dK|jN>xTC;(?vbU?GZ#7VZD#8;BrQccBg7-&{6?I&pL2p|GGo` z77q1+i2gccbBU<+;l>7`)SX;+-+1N3wKDE+IwlSc76{<4egA2zRFO-UR_v1V;t}M` zy`SaxJFrJH$F0+S?712zivv%^hQL8w5Cmf4wc@kY8~8a(-tV1d$}wp4qruU;-Mc3! ze$!K!@!nNKINNhz_3?T+atEW;wNbeT0}@e}C_tq)WST?A!qV6jQc;#V!1ISaKyMxLF#dMMd&(9Yh zK}Hy!T5c~|nD&d7ZNNL5cMi~{L!(<yFxkYaH#oA76#s1fR z1K;xKtB<49;Zf+A(oEaa%G5b>1F1RK#w%GtkFo)3b521^A(J(+>QN(HmQ4vVM)}hB z71wa}&;*%?zw2xIL?vnUIRnu)ZIk*L*9*`OaS|h1>4@3@P7-V(2m_E=FTlQj^ztB` z(NTB8I0(S92?f3WB5#=m*Q&?-8tGR$t8ME@;2=cnAAJ6u*zL`QTO8%8(&YjobL};F<3DZ&896o zZkR+Q)RG>=*Srec6#(!PU#X9U=|p!VuCf=Ac$tOf-ZTkk*{gW!vels{oM6{%`n^%q zJE8XV8lVYa(Av5|Z)6rLIY<|n3j(&WCXKJaS7e&h77%@dlXO{yN^XQDYQJ%;z9XE; zZ|1w)>?>%<2wTBcR{^;bOjTYJM+Uo-Q8jeS{xL<<5)IrEMQ~6OVSRYDz4yFs91n|E^?QW z63HdvL)-BuJ|Fp=WP|i%t;pWGB40{G{mc|a9MND&WD?oyfF~#-f5kAuUZ$=V$%Q;N zkNTs`Ol)3;9f1o4?XJO=Z>XX~D6u)#*!y%P%|!>RwkEBFZXTreK_SSXDVy*?tl zo-Ha@IV3Zgg=xI7`Dxn$gCo|Bc~hDblEZ1K(!&v6i@_(5`69*s716&tb!djmdTr;v z1X&`z#thpocSR2$tNA7m^D|mQXlA+~+X4oyCk*3*y>8t2|$0xgeJ?^$1i*s)E&()?$!`#H3s15}ZRBGg{XmMn{UwsX1ut65V_nQl{DrZEi!|BX)O-OO$FWyEcO?pUq_c2PTM|tVb_qjqa2$ZyrE=px zfr8_gR?>a)ie;E&$bAmD)lv#4uL+>hHGukPliS=AbcVukHJk$YK=&w-716SR4C`M` z^Q44YKp1V@8{ylcF6q&WWxp0w=${`jhkcg^KHpGr_rZ=fsn*pZH@;IxG*8;N;dzvA zA9z21Yj2cAH}tftr51qSg5%7)v3atx$SxwPJ*int$h;f-`>LDyh3<0o<#z$9jteV` z6&!kxZk$MY-iYFSW(17r|HaK3t`fG2*}5u$>mZIpzfY;o)3=fBa+o(Ig`*LxxsL<(c$jR6X81!wA-Y^{mLiardbTZvP!>bT&A|%Ks zm*-0w&(AQe&Ak+@0(kx}>C}eqSpBV||Co6HDY5@Z{a@zl+Kbs|+flh0ugo#Kx*l4v SYh&gf>wt0wV4WMoY5xO{_=#u$ delta 1879 zcmZ`(Yfw`M7G?#DDEm0SXfEZ9z)JO|N6CMed(gkGY zQJ@u&B%l-#7n1U*5rmuiBDjq=WMF{=xZxqWz=d2Tka$TXEA32w?9P5a&YAPgcV^C< zZ|2K$G}*7?Y$#oqwa#Q+n)3u8TU%JH_po>mOAGV5wY~Yi#x4?UO-|e7%9;1d~ z?^Krig;lUUEi&gHF4G>LSdsp`^e3x7SRM5Bh%UIdCD>jx-W2kBnwpprS-61T=(kJ z>uW>NwZfOn{=x4qQv)QS{>y^f3sKd9DS4)kl>?(6G44L-pE8IBJvx4s(us^q@m+h> z#2;C6S+7j0*TTBHgMti{vU2z zWF#zK-v1zo9Thax;s z=Y&icE~S;gxx;i?pcA2o%p)bX6_Zj0(=O2Sf+Do9lg{slD-yw@mdnouu}FqdL+ABd z|2@^x40Fz!ga>J~Uw!?hCX-h?m|jL@!l-4%MQ zo^O6P#fX?;&a-Pbjl}gnGU^7QdeBC1E8WSdu_0qpx_%)UuHhYswHfEQg70EPnQfI3 zZ>e6%LOU|{6c&h$qff+vdcV(U?N@AghJ+<>%mU#aqF{;-$U}$bx7m>2O94CbkTnlI z$Ffy-W;1^~^3xB@*0;RxI*CBoYC1g(plqhAt6+xFW{akZo_0WjPb@fm20Tu1ljiaw_4?&n(`0gF}F;Pi^xDrQwcnc~|a|2JdYV?2uaw1KA^VU$o zEVVRfSU8IM^b&H;HDBgLWE43@|GZ~0j8UhcNcU58&5r-ZBfta{4B@upv~;mNcs*MG z%vr>fL@xvuU{y`GjCWeKO5C97J_AO+8pu4bi{(m8hJ69=FwjXnQOLkjv@NBWmLiF~ zydnyzap${R0PigypO2S(e`9tc@npZK1kPh`YXZZM0a#-Cu2ie7YM~B-8!EIxl7eEq zU}@>cTC5m$l6)-?J46eE@Wn@Ma=(9KmAW3d4$SO2bvscQ&N06g@0{VlO3)=E4tbr! zCV1kz$492#&1ii%OukLVQLrs(-gMU64&Mws4Dx&q=61!+E6C;xIp?IR9-AZa-ajnuRzR6 z#ZieU*ek_NCFy`pW4U-(a>`2|Ne1?0hnI|SVusfy7*V7iuq%4gXZph2bjP;4!?}%s z+*W^1j$6#{s&3($S2}KZl#Pii0@y&LvRF{a0^7GQdOt@;e_x6Y&GG#5^K-1U<(JUZ zHzxdRr&z?{ivA7(0UEPBhJ}Ma@EdYevAVg@G$&a(XKT6R)mmgc$dEI(eR=0chbvD_z6 z(~Vgl(WiVzbk4;nQM7hhRpw_YE`QUm*X3~gS^-OoY%&Sp1{7UqXLShRx8q~86JqtI zg6fi>n8gyIdBb?D`&taGMT;f0{5xbNo5}sDgZjU8=>OsWn@;%lOR{@@qxpIzgF2LT V;^H-GoLJ+0+PuYN=Ri?2{sUl79sB?Q diff --git a/godot/sprites/sheet.png b/godot/sprites/sheet.png index c0f4ce9fd4a2b5a546e967094855c01a22dc35e9..6373967f81370f7853eadee8300c204b1c5ff76d 100644 GIT binary patch delta 3103 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 pAZ8d1+Q^ENNytifeq&CuCCg;Z79uUE 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, +} + +#[godot_api] +impl INode for Gatherer {} + +#[godot_api] +impl Gatherer { + #[func] + pub fn gather(&mut self, item: ItemKind, count: i32) { + self.signals().gathered().emit(&item.to_godot(), count); + } + + #[signal] + fn gathered(item: GString, count: i32); +} + +#[derive(Debug, GodotClass)] +#[class(init, base=Node)] +pub struct Gatherable { + #[export] + grid: Option>, + #[export] + item: ItemKind, + #[export] + #[init(val = 1)] + count: i32, + #[export] + #[init(val = true)] + pick_automatically: bool, + + #[var(get = get_is_picked, set = set_is_picked)] + _is_picked: bool, + + base: Base, +} + +#[godot_api] +impl INode for Gatherable {} + +#[godot_api] +impl Gatherable { + #[func] + pub fn get_is_picked(&self) -> bool { + self._is_picked + } + + #[func] + pub fn set_is_picked(&mut self, value: bool) { + let old = self._is_picked; + self._is_picked = value; + if old != value { + self.signals().picked_state_changed().emit(value); + } + } + + #[func] + fn on_turn_end(&mut self, actor: Option>, _action: GString) { + if self.get_is_picked() { + return; + } + let actor = match actor { + Some(actor) => actor, + None => return, + }; + let actor_grid = match find_in_parents::(actor.clone()) { + Some(grid) => grid, + None => return, + }; + let gatherer = match find_by_type_in_children::(actor_grid.clone()) { + Some(gatherer) => gatherer, + None => return, + }; + let my_coords = self + .grid + .as_ref() + .expect("Getting Grid from EnterTrigger") + .bind() + .get_coords(); + let coords = actor_grid.bind().get_coords(); + if my_coords.distance_squared_to(coords) <= (gatherer.bind().get_radius() as i64).pow(2) { + self.pick_for_gatherer(gatherer); + } + } + + fn pick_for_gatherer(&mut self, mut gatherer: Gd) { + gatherer.bind_mut().gather(self.item, self.count); + let item = self.item.to_godot().clone(); + let count = self.count; + if self.pick_automatically { + self.set_is_picked(true); + } + self.signals().gathered().emit(&item, count); + } + + #[signal] + fn picked_state_changed(is_picked: bool); + + #[signal] + fn gathered(item: GString, count: i32); +} diff --git a/rust/src/overworld.rs b/rust/src/grid.rs similarity index 67% rename from rust/src/overworld.rs rename to rust/src/grid.rs index e102014..2cb00b6 100644 --- a/rust/src/overworld.rs +++ b/rust/src/grid.rs @@ -1,6 +1,6 @@ -use godot::{classes::*, prelude::*}; +use godot::{classes::*, obj::WithUserSignals, prelude::*}; -use crate::{turn::*, utils::find_in_parents}; +use crate::utils::find_in_parents; #[derive(Debug, GodotClass)] #[class(init, base=Node)] @@ -78,6 +78,10 @@ pub struct GridPosition { #[init(val = Vector2::splat(32.0))] grid_size: Vector2, + #[export] + #[init(val = Vector2::splat(0.5))] + grid_anchor: Vector2, + base: Base, } @@ -85,85 +89,29 @@ pub struct GridPosition { impl GridPosition { #[func] pub fn get_coords(&self) -> Vector2i { - (self.base().get_global_position() / self.grid_size) + self.world_to_coords(self.base().get_global_position()) + } + + #[func] + pub fn set_coords(&mut self, coords: Vector2i) { + let old = self.get_coords(); + let pos = self.coords_to_world(coords); + self.base_mut().set_global_position(pos); + self.signals().coords_set().emit(old, coords); + } + + pub fn coords_to_world(&self, coords: Vector2i) -> Vector2 { + (coords.cast_float() + self.grid_anchor) * self.grid_size + } + + pub fn world_to_coords(&self, pos: Vector2) -> Vector2i { + ((pos / self.grid_size) - self.grid_anchor) .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); - } -} - -#[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)] -#[godot(via = GString)] -pub enum ItemKind { - #[default] - Blueberry, -} - -#[derive(Debug, GodotClass)] -#[class(init, base=Node)] -pub struct Gatherer { - #[export] - grid: Option>, - #[export] - actor: Option>, - - #[export] - #[init(val = 0)] - radius: i32, - - level: Option>, - - base: Base, -} - -#[godot_api] -impl INode for Gatherer { - fn ready(&mut self) { - self.level = find_in_parents(self.to_gd()); - self.actor - .as_ref() - .expect("getting Actor") - .signals() - .turn_ended() - .connect_other(self, |this, action| this.on_turn_end(action)); - } -} - -#[godot_api] -impl Gatherer { - fn on_turn_end(&self, _action: GString) -> Option<()> { - let coords = self.grid.clone()?.bind().get_coords(); - if let Some((item, count)) = self.try_gather_tile(coords) { - godot_print!("Gathered {count} x {:?}", item); - } - Some(()) - } - - fn try_gather_tile(&self, coords: Vector2i) -> Option<(ItemKind, i32)> { - let mut fg = self.level.clone()?.bind().get_fg(); - // TODO: This is a horrible way to handle this - if get_custom_data_bool(&fg, coords, "is_berry") { - let source_id = fg.get_cell_source_id(coords); - let alternative_tile = fg.get_cell_alternative_tile(coords); - let new_atlas_coords = fg.get_cell_atlas_coords(coords) + Vector2i::RIGHT; - fg.set_cell_ex(coords) - .source_id(source_id) - .alternative_tile(alternative_tile) - .atlas_coords(new_atlas_coords) - .done(); - return Some((ItemKind::Blueberry, 1)); - } - None - } - #[signal] - fn gathered(item: ItemKind, count: i32); + fn coords_set(old: Vector2i, new: Vector2i); } #[derive(Debug, GodotClass)] @@ -190,6 +138,10 @@ impl INode for Mover { fn ready(&mut self) { self.grid = find_in_parents(self.to_gd()); self.target_coords = self.get_grid().bind().get_coords(); + self.get_grid() + .signals() + .coords_set() + .connect_other(self, |this, _old, new| this.on_grid_coords_set(new)); } fn process(&mut self, delta: f64) { @@ -216,12 +168,12 @@ impl Mover { #[func] pub fn get_grid(&self) -> Gd { - self.grid.clone().unwrap() + self.grid.clone().expect("Getting Grid for Mover") } #[func] pub fn get_target_pos(&self) -> Vector2 { - self.target_coords.cast_float() * self.get_grid().bind().grid_size + self.get_grid().bind().coords_to_world(self.target_coords) } #[func] @@ -240,7 +192,7 @@ impl Mover { #[func] pub fn try_move(&mut self, dir: Vector2i) -> bool { - let start_coords = self.target_coords; + let start_coords = self.get_grid().bind().get_coords(); let target_coords = start_coords + dir; if !self.can_move(dir) { @@ -253,6 +205,26 @@ impl Mover { true } + #[func] + pub fn try_move_instant(&mut self, dir: Vector2i) -> bool { + if !self.try_move(dir) { + return false; + } + let start_coords = self.get_grid().bind().get_coords(); + self.get_grid().bind_mut().set_coords(start_coords + dir); + true + } + + fn on_grid_coords_set(&mut self, coords: Vector2i) { + // Warning! + // Calling self.get_grid().set_coords() would cause infinite recursion. + if self.is_moving { + self.is_moving = false; + self.target_coords = coords; + self.signals().finished_moving().emit(coords); + } + } + #[signal] fn started_moving(from_coords: Vector2i, dir: Vector2i); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0f73da5..983f022 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2,7 +2,8 @@ use godot::prelude::*; pub struct VelhoExtension; -mod overworld; +mod gathering; +mod grid; mod turn; mod utils; diff --git a/rust/src/turn.rs b/rust/src/turn.rs index 94307ad..d39f8d0 100644 --- a/rust/src/turn.rs +++ b/rust/src/turn.rs @@ -54,8 +54,12 @@ impl TurnManager { fn start_round(&mut self) { self.unregister_deleted(); + self.current_actor = None; self.round_queue = self.new_round(); - self.start_next_turn(); + if !self.round_queue.is_empty() { + self.base_mut().propagate_call_ex("on_round_start").done(); + self.start_next_turn(); + } } fn start_next_turn(&mut self) { @@ -77,7 +81,12 @@ impl TurnManager { } } - fn on_actor_turn_end(&mut self, _action: GString) { + fn on_actor_turn_end(&mut self, action: GString) { + let actor = self.current_actor.clone(); + self.base_mut() + .propagate_call_ex("on_turn_end") + .args(&varray![actor, action]) + .done(); self.start_next_turn(); } } diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 48cc39c..8181570 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -15,29 +15,33 @@ where None } -// /// Try to find a child of type `T` with the name `T::class_name()` -// pub fn find_by_class_name_in_children(parent: Gd) -> Option> -// where -// T: Inherits, -// U: Inherits, -// { -// parent -// .upcast::() -// .try_get_node_as::(&T::class_name().to_string()) -// } +/// Try to find a child of type `T` with the name `T::class_name()` +pub fn find_by_class_name_in_children(parent: Gd) -> Option> +where + T: Inherits, + U: Inherits, +{ + parent + .upcast::() + .try_get_node_as::(&T::class_name().to_string()) +} -// /// Try to find a child of type `T` by iterating over children. -// /// -// /// Returns only the first result. -// pub fn find_by_type_in_children(parent: Gd) -> Option> -// where -// T: Inherits, -// U: Inherits, -// { -// for child in parent.upcast::().get_children().iter_shared() { -// if let Ok(result) = child.try_cast::() { -// return Some(result); -// } -// } -// None -// } +/// Try to find a child of type `T` by iterating over children. +/// It first checks for a child with the name of `T::class_name()` before iterating. +/// +/// Returns only the first result. +pub fn find_by_type_in_children(parent: Gd) -> Option> +where + T: Inherits, + U: Inherits, +{ + if let Some(found) = find_by_class_name_in_children::(parent.clone()) { + return Some(found); + } + for child in parent.upcast::().get_children().iter_shared() { + if let Ok(result) = child.try_cast::() { + return Some(result); + } + } + None +}