diff --git a/godot/nodes/creature/ai_wanderer.gd b/godot/nodes/creature/ai_wanderer.gd new file mode 100644 index 0000000..5aae48b --- /dev/null +++ b/godot/nodes/creature/ai_wanderer.gd @@ -0,0 +1,10 @@ +@icon("res://icons/turn_action.svg") +extends ActionDecider + +@export var mover: Mover + +func handle_decide_action(): + var directions = [Vector2i.UP, Vector2i.DOWN, Vector2i.LEFT, Vector2i.RIGHT] + if try_perform(MoveAction.new(mover, directions.pick_random())): + return + try_perform(SkipAction.new()) diff --git a/godot/nodes/creature/ai_wanderer.gd.uid b/godot/nodes/creature/ai_wanderer.gd.uid new file mode 100644 index 0000000..6491b2b --- /dev/null +++ b/godot/nodes/creature/ai_wanderer.gd.uid @@ -0,0 +1 @@ +uid://hbrne8wt101n diff --git a/godot/prefabs/creatures/goblonoid.tscn b/godot/prefabs/creatures/goblonoid.tscn new file mode 100644 index 0000000..caf6767 --- /dev/null +++ b/godot/prefabs/creatures/goblonoid.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=4 format=3 uid="uid://dgiy7a67o1c6i"] + +[ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_4ufx4"] +[ext_resource type="Script" uid="uid://swwas6oe75sc" path="res://nodes/mover_sprite_flipper.gd" id="2_d3j4p"] +[ext_resource type="Script" uid="uid://hbrne8wt101n" path="res://nodes/creature/ai_wanderer.gd" id="3_4ufx4"] + +[node name="Goblonoid" type="GridPosition"] + +[node name="Sprite2D" type="Sprite2D" parent="." node_paths=PackedStringArray("mover")] +texture = ExtResource("1_4ufx4") +hframes = 8 +vframes = 8 +frame = 8 +script = ExtResource("2_d3j4p") +mover = NodePath("../Mover") + +[node name="TurnActor" type="TurnActor" parent="."] + +[node name="ActionDecider" type="Node" parent="." node_paths=PackedStringArray("mover", "actor")] +script = ExtResource("3_4ufx4") +mover = NodePath("../Mover") +actor = NodePath("../TurnActor") +metadata/_custom_type_script = "uid://okxdlbfuvb1b" + +[node name="Mover" type="Mover" parent="."] +movement_speed = 100.0 + +[node name="Gatherer" type="Gatherer" parent="."] diff --git a/godot/prefabs/player/player.tscn b/godot/prefabs/player/player.tscn index d96c810..47a777b 100644 --- a/godot/prefabs/player/player.tscn +++ b/godot/prefabs/player/player.tscn @@ -28,9 +28,8 @@ zoom = Vector2(2, 2) [node name="TurnActor" type="TurnActor" parent="."] -[node name="ActionDecider" type="Node2D" parent="." node_paths=PackedStringArray("grid", "mover", "actor")] +[node name="ActionDecider" type="Node2D" parent="." node_paths=PackedStringArray("mover", "actor")] script = ExtResource("2_rdx4y") -grid = NodePath("..") mover = NodePath("../Mover") actor = NodePath("../TurnActor") metadata/_custom_type_script = "uid://okxdlbfuvb1b" diff --git a/godot/prefabs/player/player_input.gd b/godot/prefabs/player/player_input.gd index 26ffb16..ad8527c 100644 --- a/godot/prefabs/player/player_input.gd +++ b/godot/prefabs/player/player_input.gd @@ -1,7 +1,6 @@ @icon("res://icons/turn_action.svg") extends ActionDecider -@export var grid: GridPosition @export var mover: Mover func poll_movement_dir(): diff --git a/godot/prefabs/ui/ui.gd b/godot/prefabs/ui/ui.gd index d2fba2c..4fcea5c 100644 --- a/godot/prefabs/ui/ui.gd +++ b/godot/prefabs/ui/ui.gd @@ -10,7 +10,7 @@ var manager: GameManager func _ready(): manager = GameManager.from_node(self) - manager.time().time_advanced.connect(_on_time_advance) + manager.time().time_of_day_changed.connect(_on_time_of_day_changed) player.entity_inventory.inventory.changed.connect(_on_player_inventory_changed) update_inventory_ui() update_clock_hand(manager.time().day_progress()) @@ -21,11 +21,8 @@ func _on_player_inventory_changed(): func update_inventory_ui(): inventory_ui.update(player.get_inventory()) -func _on_time_advance(_now: int, _advanced_by: int): +func _on_time_of_day_changed(_time_of_day: int): update_clock_hand(manager.time().day_progress()) func update_clock_hand(progress: float): clock_hand.rotation = progress * PI * 2.0 - -func on_day_end(): - print("Day over!") diff --git a/godot/scenes/overworld.tscn b/godot/scenes/overworld.tscn index b43808e..aa834cf 100644 --- a/godot/scenes/overworld.tscn +++ b/godot/scenes/overworld.tscn @@ -22,7 +22,7 @@ tile_map_data = PackedByteArray("AAD8//z/AQAAAAQAAAD8//3/AQAAAAQAAAD8//7/AQAAAAQ tile_set = ExtResource("1_m1b5j") [node name="Foreground" type="TileMapLayer" parent="World"] -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_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/wMAAAAAAAAAAgAEAAEAAAAAAAAAAQADAAEAAAAAAAAAAQADAAIAAAAAAAAAAQADAAMAAAAAAAAAAQACAAMAAAAAAAAAAQAEAAMAAAAAAAAAAQAFAAMAAAAAAAAAAQAFAAIAAAAAAAAAAQAFAAEAAAAAAAAAAQAEAAIAAAAAAAAAAwD7//7/AAAAAAAAAwA=") tile_set = ExtResource("2_u2ss0") [node name="Player" parent="World" instance=ExtResource("3_u2ss0")] diff --git a/godot/sprites/sheet.aseprite b/godot/sprites/sheet.aseprite index 5ebacb0..9ec94e7 100644 Binary files a/godot/sprites/sheet.aseprite and b/godot/sprites/sheet.aseprite differ diff --git a/godot/sprites/sheet.png b/godot/sprites/sheet.png index 3b2504d..baae2e4 100644 Binary files a/godot/sprites/sheet.png and b/godot/sprites/sheet.png differ diff --git a/godot/tilesets/foreground_tileset.tres b/godot/tilesets/foreground_tileset.tres index bc8f7c7..b7a9bc4 100644 --- a/godot/tilesets/foreground_tileset.tres +++ b/godot/tilesets/foreground_tileset.tres @@ -1,13 +1,15 @@ -[gd_resource type="TileSet" load_steps=6 format=3 uid="uid://bxwohuw2p43k1"] +[gd_resource type="TileSet" load_steps=7 format=3 uid="uid://bxwohuw2p43k1"] [ext_resource type="Texture2D" uid="uid://doscvutq8uqmd" path="res://sprites/sheet.png" id="1_oe62d"] [ext_resource type="PackedScene" uid="uid://8xuvjmyjkpeo" path="res://prefabs/tiles/blueberry.tscn" id="1_rc83b"] [ext_resource type="PackedScene" uid="uid://l72f05nek0y4" path="res://prefabs/tiles/cowberry.tscn" id="2_w3v7n"] +[ext_resource type="PackedScene" uid="uid://dgiy7a67o1c6i" path="res://prefabs/creatures/goblonoid.tscn" id="3_nsg88"] [sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_w3v7n"] resource_name = "Entities" scenes/1/scene = ExtResource("1_rc83b") scenes/2/scene = ExtResource("2_w3v7n") +scenes/3/scene = ExtResource("3_nsg88") [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_rc83b"] texture = ExtResource("1_oe62d") diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index 35a9a4f..f300dce 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -53,6 +53,10 @@ pub struct TimeManager { #[init(val = 40)] day_length: i32, + #[var(get, set = set_time_of_day)] + #[init(val = -1)] + day: i32, + #[var(get)] time_of_day: i32, @@ -65,21 +69,37 @@ impl INode for TimeManager {} #[godot_api] impl TimeManager { #[func] - pub fn reset_time(&mut self) { - self.time_of_day = 0; - self.signals().time_resetted().emit(); + pub fn set_time_of_day(&mut self, value: i32) { + self.time_of_day = value; + self.signals().time_of_day_changed().emit(value); + } + + #[func] + pub fn set_day(&mut self, value: i32) { + self.day = value; + self.signals().day_changed().emit(value); + } + + #[func] + pub fn start_new_day(&mut self) { + self.set_time_of_day(0); + self.set_day(self.day + 1); + GameManager::get(self.to_gd()).propagate_call(calls::ON_DAY_START); } #[func] pub fn advance_time(&mut self, amount: i32) { - let new_time = self.time_of_day + amount; - self.time_of_day = new_time; - self.signals().time_advanced().emit(new_time, amount); - if self.time_of_day >= self.day_length { + self.set_time_of_day(self.time_of_day + amount); + if self.is_day_over() { GameManager::get(self.to_gd()).propagate_call(calls::ON_DAY_END); } } + #[func] + pub fn is_day_over(&self) -> bool { + self.time_of_day >= self.day_length + } + #[func] pub fn day_progress(&self) -> f32 { self.time_of_day as f32 / self.day_length as f32 @@ -91,8 +111,8 @@ impl TimeManager { } #[signal] - fn time_resetted(); + fn day_changed(day: i32); #[signal] - fn time_advanced(now: i32, advanced_by: i32); + fn time_of_day_changed(time_of_day: i32); } diff --git a/rust/src/gathering.rs b/rust/src/gathering.rs index aed23d5..8d096c0 100644 --- a/rust/src/gathering.rs +++ b/rust/src/gathering.rs @@ -1,4 +1,4 @@ -use godot::{classes::*, prelude::*}; +use godot::{classes::*, obj::WithBaseField, prelude::*}; use crate::prelude::*; @@ -46,6 +46,20 @@ pub struct Gatherable { base: Base, } +#[godot_api] +impl INode for Gatherable { + fn ready(&mut self) { + GameManager::get(self.to_gd()) + .bind() + .turn() + .signals() + .action_taken() + .connect_other(self, |this, actor, action| { + this.on_action_taken(actor, action) + }); + } +} + #[godot_api] impl Gatherable { #[func] @@ -63,14 +77,10 @@ impl Gatherable { } #[func] - fn on_turn_end(&mut self, actor: Option>, _action: GString) { + fn on_action_taken(&mut self, actor: Gd, _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, diff --git a/rust/src/turn.rs b/rust/src/turn.rs index 02765bd..a1eb0c7 100644 --- a/rust/src/turn.rs +++ b/rust/src/turn.rs @@ -70,9 +70,15 @@ impl TurnManager { .signals() .turn_ended() .builder() - .flags(ConnectFlags::ONE_SHOT) + // `ConnectFlags::Deferred` prevents double-borrow errors. + // The double-borrow occurs when there are 2 actors, and the first one + // immediately skips their turn. + // + // This should be investigated further. + .flags(ConnectFlags::ONE_SHOT | ConnectFlags::DEFERRED) .connect_other_mut(self, Self::on_actor_turn_end); actor.bind_mut().start_turn(); + GameManager::get(self.to_gd()).propagate_call(calls::ON_TURN_START); } else { self.unregister(&actor); self.start_next_turn(); @@ -83,12 +89,14 @@ impl TurnManager { } fn on_actor_turn_end(&mut self, action: GString) { - GameManager::get(self.to_gd()) - .propagate_call_ex(calls::ON_TURN_END) - .args(&varray![self.current_actor, action]) - .done(); + GameManager::get(self.to_gd()).propagate_call(calls::ON_TURN_END); + let actor = self.current_actor.clone().unwrap(); + self.signals().action_taken().emit(&actor, &action); self.start_next_turn(); } + + #[signal] + pub fn action_taken(actor: Gd, action: GString); } #[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)] diff --git a/rust/src/utils.rs b/rust/src/utils.rs index eff4093..c7e21f0 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -1,10 +1,17 @@ use godot::prelude::*; +/// Contains hard-coded method names for `propagate_call`. +/// +/// This project uses the convention, that `propagate_call` should never pass arguments, and if +/// arguments are needed, signals should be used instead. `propagate_call` should be called from +/// the `GameManager` node. pub mod calls { + pub const ON_DAY_START: &str = "on_day_start"; + pub const ON_DAY_END: &str = "on_day_end"; pub const ON_ROUND_START: &str = "on_round_start"; pub const ON_ROUND_END: &str = "on_round_end"; + pub const ON_TURN_START: &str = "on_turn_start"; pub const ON_TURN_END: &str = "on_turn_end"; - pub const ON_DAY_END: &str = "on_day_end"; } pub fn find_in_parents(from: Gd) -> Option>