sevenx_engine 0.2.11

Engine de jogos 2D/3D completa com suporte Android, física, áudio, partículas, tilemap, UI, eventos e sistema 3D avançado com PBR.
Documentation
// Sistema de Pathfinding A*
use std::collections::{BinaryHeap, HashMap};
use std::cmp::Ordering;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GridPos {
    pub x: i32,
    pub y: i32,
}

impl GridPos {
    pub fn new(x: i32, y: i32) -> Self {
        Self { x, y }
    }

    pub fn distance(&self, other: &GridPos) -> f32 {
        let dx = (self.x - other.x) as f32;
        let dy = (self.y - other.y) as f32;
        (dx * dx + dy * dy).sqrt()
    }

    pub fn manhattan_distance(&self, other: &GridPos) -> i32 {
        (self.x - other.x).abs() + (self.y - other.y).abs()
    }
}

#[derive(Debug, Clone)]
struct Node {
    pos: GridPos,
    g_cost: f32,
    h_cost: f32,
    parent: Option<GridPos>,
}

impl Node {
    fn f_cost(&self) -> f32 {
        self.g_cost + self.h_cost
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.f_cost() == other.f_cost()
    }
}

impl Eq for Node {}

impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        other.f_cost().partial_cmp(&self.f_cost())
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap_or(Ordering::Equal)
    }
}

pub struct Pathfinder {
    pub grid_width: i32,
    pub grid_height: i32,
    pub obstacles: Vec<GridPos>,
    pub allow_diagonal: bool,
}

impl Pathfinder {
    pub fn new(width: i32, height: i32) -> Self {
        Self {
            grid_width: width,
            grid_height: height,
            obstacles: Vec::new(),
            allow_diagonal: true,
        }
    }

    pub fn add_obstacle(&mut self, x: i32, y: i32) {
        self.obstacles.push(GridPos::new(x, y));
    }

    pub fn remove_obstacle(&mut self, x: i32, y: i32) {
        self.obstacles.retain(|pos| pos.x != x || pos.y != y);
    }

    pub fn is_walkable(&self, pos: &GridPos) -> bool {
        pos.x >= 0
            && pos.x < self.grid_width
            && pos.y >= 0
            && pos.y < self.grid_height
            && !self.obstacles.contains(pos)
    }

    fn get_neighbors(&self, pos: &GridPos) -> Vec<GridPos> {
        let mut neighbors = Vec::new();
        let directions = if self.allow_diagonal {
            vec![
                (-1, 0), (1, 0), (0, -1), (0, 1),
                (-1, -1), (-1, 1), (1, -1), (1, 1),
            ]
        } else {
            vec![(-1, 0), (1, 0), (0, -1), (0, 1)]
        };

        for (dx, dy) in directions {
            let new_pos = GridPos::new(pos.x + dx, pos.y + dy);
            if self.is_walkable(&new_pos) {
                neighbors.push(new_pos);
            }
        }

        neighbors
    }

    // Algoritmo A*
    pub fn find_path(&self, start: GridPos, goal: GridPos) -> Option<Vec<GridPos>> {
        if !self.is_walkable(&start) || !self.is_walkable(&goal) {
            return None;
        }

        let mut open_set = BinaryHeap::new();
        let mut came_from: HashMap<GridPos, GridPos> = HashMap::new();
        let mut g_score: HashMap<GridPos, f32> = HashMap::new();

        g_score.insert(start, 0.0);
        open_set.push(Node {
            pos: start,
            g_cost: 0.0,
            h_cost: start.distance(&goal),
            parent: None,
        });

        while let Some(current) = open_set.pop() {
            if current.pos == goal {
                return Some(self.reconstruct_path(&came_from, current.pos));
            }

            for neighbor in self.get_neighbors(&current.pos) {
                let tentative_g = g_score.get(&current.pos).unwrap_or(&f32::MAX)
                    + current.pos.distance(&neighbor);

                if tentative_g < *g_score.get(&neighbor).unwrap_or(&f32::MAX) {
                    came_from.insert(neighbor, current.pos);
                    g_score.insert(neighbor, tentative_g);
                    open_set.push(Node {
                        pos: neighbor,
                        g_cost: tentative_g,
                        h_cost: neighbor.distance(&goal),
                        parent: Some(current.pos),
                    });
                }
            }
        }

        None
    }

    fn reconstruct_path(&self, came_from: &HashMap<GridPos, GridPos>, mut current: GridPos) -> Vec<GridPos> {
        let mut path = vec![current];
        while let Some(&prev) = came_from.get(&current) {
            path.push(prev);
            current = prev;
        }
        path.reverse();
        path
    }
}