q 0.1.1

Q is a Rust GDExtension crate for Godot, offering a Game Manager, Music Manager, Bevy ECS, and Tokio-powered multi-threading. It simplifies and enhances game development with efficient tools and scalable performance.
Documentation
use crate::data::abstract_data_map::AbstractDataMap;
use crate::data::npc_data::{NPCData, NPCState};
use crate::debug_print;
use godot::classes::Sprite2D;
use godot::prelude::*;

#[derive(GodotClass)]
#[class(base = Node)]
pub struct NPCEntity {
    base: Base<Node>,
    #[export]
    speed: f32,
    pub data: NPCData,
    #[export]
    sprite: Option<Gd<Sprite2D>>,
}

#[godot_api]
impl INode for NPCEntity {
    fn init(base: Base<Node>) -> Self {
        NPCEntity {
            base,
            speed: 100.0,
            data: NPCData::new(
                "default_npc",
                "generic",
                Vector2::ZERO,
                100.0,
                NPCState::IDLE,
            ),
            sprite: None,
        }
    }

    fn ready(&mut self) {
        debug_print!("[NPCEntity] Initializing...");

        if let Some(sprite) = self.base().try_get_node_as::<Sprite2D>("Sprite2D") {
            self.sprite = Some(sprite);
        } else {
            godot_warn!("[NPCEntity] Base could not be cast to Node.");
        }

        if self.sprite.is_none() {
            godot_warn!("[NPCEntity] Sprite2D not found.");
        }
    }

    fn process(&mut self, delta: f64) {
        self.update_behavior();
        self.move_and_update(delta);
    }
}

#[godot_api]
impl NPCEntity {
    fn update_behavior(&mut self) {
        let state = self.data.get_state();
        if state.contains(NPCState::ESCAPING) {
            self.handle_escape();
        } else if state.contains(NPCState::ATTACKING) {
            self.handle_attack();
        } else if state.contains(NPCState::DEFENDING) {
            self.handle_defend();
        } else if state.contains(NPCState::PATROLLING) {
            self.handle_patrol();
        } else if state.contains(NPCState::MOVING) {
            self.handle_movement();
        } else {
            self.handle_idle();
        }
    }

    fn handle_idle(&mut self) {
        self.data.set_velocity(Vector2::ZERO);
    }

    fn handle_movement(&mut self) {
        let direction = Vector2::new(1.0, 0.0);
        self.data.set_velocity(direction * self.speed);
    }

    fn handle_attack(&mut self) {
        debug_print!("[NPCEntity] Attacking...");
        self.data.set_velocity(Vector2::ZERO);
    }

    fn handle_defend(&mut self) {
        debug_print!("[NPCEntity] Defending...");
        self.data.set_velocity(Vector2::ZERO);
    }

    fn handle_patrol(&mut self) {
        let patrol_direction = Vector2::new(0.5, 0.0);
        self.data.set_velocity(patrol_direction * self.speed);
    }

    fn handle_escape(&mut self) {
        let escape_direction = Vector2::new(-1.0, 0.0);
        self.data.set_velocity(escape_direction * self.speed * 1.5);
    }

    fn move_and_update(&mut self, delta: f64) {
        let velocity = self.data.get_velocity();
        if velocity != Vector2::ZERO {
            if let Some(sprite) = self.sprite.as_mut() {
                let new_position = sprite.get_position() + velocity * (delta as f32);
                sprite.set_position(new_position);
                self.data.set_position(new_position);
            } else {
                godot_error!("[NPCEntity] Sprite2D not linked. Cannot update position.");
            }
        }
    }

    pub fn set_npc_data(&mut self, data: NPCData) {
        self.data = data;
    }

    pub fn get_npc_data(&self) -> &NPCData {
        &self.data
    }

    #[func]
    fn save_npc_data(&self, file_path: GString) -> bool {
        debug_print!("Saving NPC data to {}", file_path);
        self.data.to_save_gfile_json(&file_path.to_string())
    }

    #[func]
    fn load_npc_data(&mut self, file_path: GString) -> bool {
        debug_print!("Loading NPC data from {}", file_path);
        if let Some(loaded_data) = NPCData::from_load_gfile_json(&file_path.to_string()) {
            self.data = loaded_data;
            true
        } else {
            false
        }
    }
}