diff --git a/godot/nodes/action.gd b/godot/nodes/action.gd index 170ddd4..7dc5924 100644 --- a/godot/nodes/action.gd +++ b/godot/nodes/action.gd @@ -7,6 +7,10 @@ extends Node func predicate() -> bool: return true +func get_action_name() -> String: + push_error("get_action_name is unimplemented!") + return "UNIMPLEMENTED" + # Called when the action starts func action_ready(): pass diff --git a/godot/nodes/action_decider.gd b/godot/nodes/action_decider.gd index a20b103..cc7efd9 100644 --- a/godot/nodes/action_decider.gd +++ b/godot/nodes/action_decider.gd @@ -26,11 +26,11 @@ func handle_decide_action(): #print("deciding action...") pass -func handle_perform_action(): +func handle_perform_action(_action: String): #print("performing action: ", actor.get_current_action()) pass -func handle_turn_end(): +func handle_turn_end(_action: String): #print("turn end!") pass @@ -51,7 +51,7 @@ func try_perform(action_node: Action) -> bool: func inner_perform(action_node: Action): current_action = action_node - actor.perform_action(action_node.name) + actor.perform_action(action_node.get_action_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() diff --git a/godot/nodes/actions/move_action.gd b/godot/nodes/actions/move_action.gd index 9c6300a..b090c77 100644 --- a/godot/nodes/actions/move_action.gd +++ b/godot/nodes/actions/move_action.gd @@ -9,6 +9,9 @@ func _init(new_mover: GridPosition, new_dir: Vector2i) -> void: mover = new_mover dir = new_dir +func get_action_name() -> String: + return "move" + func predicate(): return mover.can_move(dir) diff --git a/godot/nodes/actions/skip_action.gd b/godot/nodes/actions/skip_action.gd index 9814a02..69f9b63 100644 --- a/godot/nodes/actions/skip_action.gd +++ b/godot/nodes/actions/skip_action.gd @@ -2,5 +2,8 @@ class_name SkipAction extends Action +func get_action_name() -> String: + return "skip" + func action_ready(): done.emit() diff --git a/godot/prefabs/player/player.tscn b/godot/prefabs/player/player.tscn index e598c04..642fa0b 100644 --- a/godot/prefabs/player/player.tscn +++ b/godot/prefabs/player/player.tscn @@ -22,4 +22,4 @@ frame = 1 position = Vector2(16, 16) zoom = Vector2(2, 2) -[node name="Gatherer" type="Node" parent="GridPosition"] +[node name="Gatherer" type="Gatherer" parent="GridPosition"] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 494cbd2..0f73da5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2,9 +2,9 @@ use godot::prelude::*; pub struct VelhoExtension; -mod common; -mod level; -mod turn_manager; +mod overworld; +mod turn; +mod utils; #[gdextension] unsafe impl ExtensionLibrary for VelhoExtension {} diff --git a/rust/src/level.rs b/rust/src/overworld.rs similarity index 62% rename from rust/src/level.rs rename to rust/src/overworld.rs index e642594..d57a7e7 100644 --- a/rust/src/level.rs +++ b/rust/src/overworld.rs @@ -1,4 +1,6 @@ -use godot::{classes::*, prelude::*}; +use godot::{classes::*, obj::WithBaseField, prelude::*}; + +use crate::{turn::TurnActor, utils}; #[derive(Debug, GodotClass)] #[class(init, base=Node)] @@ -20,6 +22,8 @@ impl INode for Level { if self.foreground.is_none() { self.foreground = self.base().try_get_node_as::("./Foreground"); } + assert_ne!(self.background, None); + assert_ne!(self.foreground, None); } } @@ -50,6 +54,11 @@ impl Level { .is_some_and(|layer| get_custom_data_bool(layer, coords, Self::HAS_COLLISION)) } + #[func] + pub fn get_bg(&self) -> Gd { + self.background.clone().unwrap() + } + #[func] pub fn get_fg(&self) -> Gd { self.foreground.clone().unwrap() @@ -101,8 +110,6 @@ impl INode2D for GridPosition { 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)); } @@ -114,10 +121,22 @@ impl INode2D for GridPosition { impl GridPosition { const TILE_SNAP_DIST_SQR: f32 = 1.0; - fn level(&self) -> Option> { + pub fn level(&self) -> Option> { Level::find_from_node(self.to_gd().upcast::()) } + // #[func] + // pub fn find_from_node(node: Gd) -> Option> { + // let mut current = node.clone(); + // while let Some(parent) = current.get_parent() { + // match parent.try_cast::() { + // Ok(level) => return Some(level), + // Err(other) => current = other, + // } + // } + // None + // } + #[func] pub fn get_target_pos(&self) -> Vector2 { self.target_coords.cast_float() * self.grid_size @@ -172,3 +191,68 @@ impl GridPosition { #[signal] fn finished_moving(coords: Vector2i); } + +#[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] + #[init(val = 0)] + radius: i32, + + level: Option>, + grid_position: Option>, + + base: Base, +} + +#[godot_api] +impl INode for Gatherer { + fn ready(&mut self) { + self.level = utils::find_in_parents(self.to_gd()); + self.grid_position = utils::find_in_parents(self.to_gd()); + let actor = TurnActor::find_from_node(self.to_gd().upcast::()) + .expect("Getting Actor in some parent"); + 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_position.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); +} diff --git a/rust/src/common.rs b/rust/src/turn.rs similarity index 64% rename from rust/src/common.rs rename to rust/src/turn.rs index be314c1..334c1f0 100644 --- a/rust/src/common.rs +++ b/rust/src/turn.rs @@ -1,4 +1,59 @@ -use godot::{classes::*, prelude::*}; +use godot::{ + classes::{object::ConnectFlags, *}, + prelude::*, +}; + +#[derive(Debug, GodotClass)] +#[class(init, base=Node)] +pub struct TurnManager { + round_queue: Array>, + current_actor: Option>, + + base: Base, +} + +#[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, _action| this.start_next_turn()); + actor.bind_mut().start_turn(); + } + } + + fn find_sibling_actors(&self) -> Array> { + let mut actors: Array> = Array::new(); + self.base() + .get_parent() + .unwrap() + .get_children() + .iter_shared() + .filter_map(|node| node.try_cast::().ok()) + .for_each(|actor| actors.push(&actor)); + actors + } +} #[derive(GodotConvert, Var, Export, Clone, Copy, Default, Debug, PartialEq, Eq)] #[godot(via = GString)] @@ -44,6 +99,18 @@ impl TurnActor { self.state } + #[func] + pub fn find_from_node(node: Gd) -> Option> { + let mut current = node.clone(); + while let Some(parent) = current.get_parent() { + match parent.try_cast::() { + Ok(level) => return Some(level), + Err(other) => current = other, + } + } + None + } + #[func] pub fn get_current_action(&self) -> Variant { match self.current_action.clone() { @@ -104,8 +171,8 @@ impl TurnActor { ); } self.state = TurnActorState::PerformingAction; - self.current_action = Some(action_name); - self.signals().performing_action().emit(); + self.current_action = Some(action_name.clone()); + self.signals().performing_action().emit(&action_name); } /// Should be called by an action script after it's done. @@ -118,8 +185,9 @@ impl TurnActor { ); } self.state = TurnActorState::WaitingForTurn; + let action = self.current_action.clone().unwrap_or_default(); self.current_action = None; - self.signals().turn_ended().emit(); + self.signals().turn_ended().emit(&action); } #[signal] @@ -129,8 +197,8 @@ impl TurnActor { pub fn deciding_action(); #[signal] - pub fn performing_action(); + pub fn performing_action(action_name: GString); #[signal] - pub fn turn_ended(); + pub fn turn_ended(action_name: GString); } diff --git a/rust/src/turn_manager.rs b/rust/src/turn_manager.rs deleted file mode 100644 index 5ae4eeb..0000000 --- a/rust/src/turn_manager.rs +++ /dev/null @@ -1,58 +0,0 @@ -use godot::{ - classes::{object::ConnectFlags, *}, - prelude::*, -}; - -use crate::common::TurnActor; - -#[derive(Debug, GodotClass)] -#[class(init, base=Node)] -pub struct TurnManager { - round_queue: Array>, - current_actor: Option>, - - base: Base, -} - -#[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> { - let mut actors: Array> = Array::new(); - self.base() - .get_parent() - .unwrap() - .get_children() - .iter_shared() - .filter_map(|node| node.try_cast::().ok()) - .for_each(|actor| actors.push(&actor)); - actors - } -} diff --git a/rust/src/utils.rs b/rust/src/utils.rs new file mode 100644 index 0000000..133886e --- /dev/null +++ b/rust/src/utils.rs @@ -0,0 +1,16 @@ +use godot::prelude::*; + +pub fn find_in_parents(from: Gd) -> Option> +where + T: Inherits, + U: Inherits, +{ + let mut current = from.clone().upcast::(); + while let Some(parent) = current.get_parent() { + match parent.try_cast::() { + Ok(level) => return Some(level), + Err(other) => current = other, + } + } + None +}