use std::string::ToString; use std::sync::OnceLock; use indexmap::IndexMap; use wicked_waifus_protocol::{DFsm, DFsmBlackBoard, EntityComponentPb, EntityFsmComponentPb, FsmCustomBlackboardDatas}; use wicked_waifus_protocol::entity_component_pb::ComponentPb; use wicked_waifus_data::{ai_base_data, ai_state_machine_config_data, AiStateMachineConfigData, StateMachineJson, StateMachineNode, StateMachineNodeCommon}; use crate::logic::ecs::component::Component; static COMMON_FSM: OnceLock = OnceLock::new(); #[derive(Default)] pub struct Fsm { pub hash_code: i32, pub common_hash_code: i32, pub state_list: Vec, pub node_list: IndexMap, } impl Component for Fsm { fn set_pb_data(&self, pb: &mut wicked_waifus_protocol::EntityPb) { pb.component_pbs.push(EntityComponentPb { component_pb: Some(ComponentPb::EntityFsmComponentPb(EntityFsmComponentPb { fsms: self.get_initial_fsm(), hash_code: self.hash_code, common_hash_code: self.common_hash_code, black_board: self.get_black_board(), fsm_custom_blackboard_datas: self.get_fsm_custom_blackboard_datas(), })), }) } } impl Fsm { pub fn from_ai_id(ai_id: i32) -> Self { let ai_base = ai_base_data::get(&ai_id); let Some(base) = ai_base else { tracing::error!("Ai Base not found for AI ID: {}", ai_id); return Self::default(); }; let common_state_machine: &StateMachineJson = &get_common_fsm().state_machine_json; // Should always be defined since it comes from bindata let Some(state_machine_config_data) = ai_state_machine_config_data::get(&base.state_machine) else { tracing::error!("State machine config not found for AI ID: {}", ai_id); return Self::default(); }; let state_machine_config: &StateMachineJson = &state_machine_config_data.state_machine_json; let mut fsm_tree: IndexMap = IndexMap::with_capacity(state_machine_config.nodes.len()); for state_machine in &state_machine_config.state_machines { for node in &state_machine_config.nodes { match node { StateMachineNode::Reference(_node) => { // TODO: // common_state_machine.nodes.iter() // .filter_map(|state_machine_node| match state_machine_node { // StateMachineNode::Reference(_) => None, // StateMachineNode::Override(_) => None, // StateMachineNode::Custom(custom) => Some(custom) // }) // .find() } StateMachineNode::Override(node) => { // TODO: tracing::warn!( "FSM: {state_machine} with override node {} for {} unimplemented", node.common.uuid, node.override_common_uuid ); } StateMachineNode::Custom(node) => { fsm_tree.insert(node.common.uuid, node.common.clone()); } } } } Self { hash_code: state_machine_config.version as i32, common_hash_code: common_state_machine.version as i32, state_list: state_machine_config.state_machines.clone(), node_list: fsm_tree, } } fn get_initial_fsm(&self) -> Vec { self.node_list.iter() .filter_map(|(&id, node)| { self.state_list.contains(&id).then(|| DFsm { fsm_id: id, current_state: node.children.as_ref() .and_then(|c| c.get(0).cloned()) .unwrap_or_default(), flag: 0, // TODO: k_ts: 0, // TODO: }) }).collect::>() } fn get_black_board(&self) -> Vec { vec![] } fn get_fsm_custom_blackboard_datas(&self) -> Option { None } } fn get_common_fsm() -> &'static AiStateMachineConfigData { COMMON_FSM.get_or_init(|| { let name = "SM_Common".to_string(); // Common shall be always defined ai_state_machine_config_data::get(&name).cloned().unwrap() }) }