/*
Hurry Curry! - a game about cooking
Copyright (C) 2025 Hurry Curry! Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use super::{Entity, EntityContext};
use anyhow::Result;
use hurrycurry_bot::{BotAlgo, DynBotAlgo, PacketSink};
use hurrycurry_locale::TrError;
use hurrycurry_protocol::{Character, PacketS, PlayerClass, PlayerID};
use log::debug;
use std::any::Any;
pub type DynBotDriver = BotDriver;
pub enum BotDriver {
Joining {
name: String,
character: Character,
class: PlayerClass,
constructor: Option T + Send + Sync>>,
},
Running {
state: T,
id: PlayerID,
},
Left,
}
impl BotDriver {
pub fn new(
name: String,
character: Character,
class: PlayerClass,
constructor: impl FnOnce(PlayerID) -> T + 'static + Send + Sync,
) -> Self {
Self::Joining {
character,
class,
constructor: Some(Box::new(constructor)),
name,
}
}
}
impl Entity for BotDriver {
fn finished(&self) -> bool {
matches!(self, Self::Left)
}
fn tick(&mut self, c: EntityContext<'_>) -> Result<(), TrError> {
match self {
BotDriver::Joining {
name,
character,
class,
constructor,
} => {
let id = c.game.get_unused_player_id(); // TODO clashes when multiple bots join in the same tick
debug!("enter {id}");
c.packet_in.push_back(PacketS::Join {
name: name.to_string(),
character: *character,
id: Some(id),
class: *class,
position: None,
});
*self = BotDriver::Running {
state: constructor.take().unwrap()(id),
id,
};
Ok(())
}
BotDriver::Running { state, id } => {
state.tick(PacketSink::new(c.packet_in), c.game, c.dt);
if state.is_finished() {
debug!("leave {id}");
c.packet_in.push_back(PacketS::Leave { player: *id });
*self = BotDriver::Left
}
Ok(())
}
BotDriver::Left => Ok(()),
}
}
fn destructor(&mut self, c: EntityContext<'_>) {
if let Self::Running { id, .. } = self {
c.packet_in.push_back(PacketS::Leave { player: *id })
}
}
}