velho/rust/src/turn.rs

254 lines
7.9 KiB
Rust

use std::collections::HashSet;
use godot::{
classes::{object::ConnectFlags, *},
prelude::*,
};
use crate::prelude::*;
#[derive(Debug, GodotClass)]
#[class(init, base=Node)]
pub struct TurnManager {
#[export]
time_manager: Option<Gd<TimeManager>>,
registered_actors: HashSet<Gd<TurnActor>>,
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 let Some(current) = self.current_actor.as_ref() {
if !current.is_instance_valid() {
self.start_next_turn();
}
}
if self.can_start_round() && self.current_actor.is_none() && self.round_queue.is_empty() {
self.start_round();
}
}
}
#[godot_api]
impl TurnManager {
pub fn register(&mut self, actor: Gd<TurnActor>) {
self.registered_actors.insert(actor);
}
fn unregister(&mut self, actor: &Gd<TurnActor>) {
self.registered_actors.remove(actor);
}
fn unregister_deleted(&mut self) {
self.registered_actors
.retain(|actor| actor.is_instance_valid() && !actor.is_queued_for_deletion());
}
fn can_start_round(&self) -> bool {
!self
.time_manager
.clone()
.expect("Getting TimeManager for TurnManager")
.bind()
.is_day_over()
}
fn new_round(&self) -> Array<Gd<TurnActor>> {
let mut actors: Array<Gd<TurnActor>> = self.registered_actors.iter().cloned().collect();
actors.sort_unstable_by(|a, b| a.instance_id().cmp(&b.instance_id()));
actors
}
fn start_round(&mut self) {
self.unregister_deleted();
self.current_actor = None;
self.round_queue = self.new_round();
if !self.round_queue.is_empty() {
GameManager::get(self.to_gd()).propagate_call(calls::ON_ROUND_START);
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() {
if actor.is_instance_valid() && !actor.is_queued_for_deletion() {
actor
.signals()
.turn_ended()
.builder()
// `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();
}
} else {
GameManager::get(self.to_gd()).propagate_call(calls::ON_ROUND_END);
}
}
fn on_actor_turn_end(&mut self, action: GString) {
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<TurnActor>, action: GString);
}
#[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) {}
fn enter_tree(&mut self) {
let managers = GameManager::get(self.to_gd());
managers.bind().turn().bind_mut().register(self.to_gd())
}
}
#[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.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);
}