aboutsummaryrefslogtreecommitdiff
path: root/games/rstnode/rst-core/src/map.rs
//! Implements a map graph and world logic

use crate::{
    data::{Link, Node, NodeId},
    wire::Response,
};
use async_std::sync::Arc;
use quadtree_rs::{area::AreaBuilder, point::Point, Quadtree};
use std::collections::BTreeMap;

pub struct MapNode {
    pub pos: (f64, f64),
    pub inner: Node,
}

/// A map that people fight on
///
/// A map is defined by it's graph relationships, but also where on
/// the map nodes are placed, how much spacing there is, etc.  All
/// this information is encoded in the same structs because it's
/// static, and just more convenient.
pub struct Map {
    /// Node IDs mapped to coordinates
    nodes: BTreeMap<NodeId, (i64, i64)>,
    /// Link IDs mapped to link objects
    _links: BTreeMap<u16, Arc<Link>>,
    /// A coordinate map for the network
    coord: Quadtree<i64, Arc<MapNode>>,
}

impl Map {
    pub fn new() -> Self {
        Self {
            nodes: BTreeMap::new(),
            _links: BTreeMap::new(),
            coord: Quadtree::new(2),
        }
    }

    /// Get the position of a node by its ID
    pub fn node_position(&self, id: NodeId) -> Option<(i64, i64)> {
        self.nodes.get(&id).cloned()
    }

    /// Get all objects that can be selected by a single point
    pub fn get_by_point(&self, x: i64, y: i64) -> Option<Vec<NodeId>> {
        self.coord
            .query(
                AreaBuilder::default()
                    .anchor(Point::from((x, y)))
                    .dimensions((1, 1))
                    .build()
                    .ok()?,
            )
            .map(|entry| Some(entry.value_ref().inner.id))
            .collect()
    }

    /// Get all objects that can be selected by a 2d area
    pub fn get_by_area(&self, x: i64, y: i64, w: i64, h: i64) -> Option<Vec<NodeId>> {
        self.coord
            .query(
                AreaBuilder::default()
                    .anchor(Point::from((x, y)))
                    .dimensions((w, h))
                    .build()
                    .ok()?,
            )
            .map(|entry| Some(entry.value_ref().inner.id))
            .collect()
    }
}