velho/rust/src/turn.rs

205 lines
6.1 KiB
Rust

use godot::{
classes::{object::ConnectFlags, *},
prelude::*,
};
#[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, _action| 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
}
}
#[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 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 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.clone());
self.signals().performing_action().emit(&action_name);
}
/// 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;
let action = self.current_action.clone().unwrap_or_default();
self.current_action = None;
self.signals().turn_ended().emit(&action);
}
#[signal]
pub fn turn_started();
#[signal]
pub fn deciding_action();
#[signal]
pub fn performing_action(action_name: GString);
#[signal]
pub fn turn_ended(action_name: GString);
}