Skip to main content

rusterix/map/
linedef.rs

1use crate::{BBox, Map, ValueContainer};
2use theframework::prelude::*;
3
4#[derive(Serialize, Deserialize, Clone, Debug)]
5pub struct Linedef {
6    pub id: u32,
7
8    // For editors
9    pub creator_id: Uuid,
10
11    pub name: String,
12    pub start_vertex: u32,
13    pub end_vertex: u32,
14
15    // List of sector IDs this linedef belongs to
16    #[serde(default)]
17    pub sector_ids: Vec<u32>,
18
19    pub properties: ValueContainer,
20}
21
22impl Linedef {
23    pub fn new(id: u32, start_vertex: u32, end_vertex: u32) -> Self {
24        let properties = ValueContainer::default();
25        Self {
26            id,
27            creator_id: Uuid::new_v4(),
28            name: String::new(),
29            start_vertex,
30            end_vertex,
31            sector_ids: Vec::new(),
32
33            properties,
34        }
35    }
36
37    /// Signed distance from a point to this linedef.
38    /// Negative if the point is on the "front" (normal-facing) side.
39    pub fn signed_distance(&self, map: &Map, point: Vec2<f32>) -> Option<f32> {
40        let v0 = map.get_vertex(self.start_vertex)?;
41        let v1 = map.get_vertex(self.end_vertex)?;
42
43        let edge = v1 - v0;
44        let to_point = point - v0;
45
46        let t = to_point.dot(edge) / edge.dot(edge);
47        let t_clamped = t.clamp(0.0, 1.0);
48        let closest = v0 + edge * t_clamped;
49
50        let dist = (point - closest).magnitude();
51
52        // Use perpendicular normal to define the "front" side
53        let normal = Vec2::new(-edge.y, edge.x).normalized();
54        let side = (point - closest).dot(normal);
55
56        Some(if side < 0.0 { -dist } else { dist })
57    }
58
59    /// Calculate the length of the linedef, applying animation states
60    pub fn length(&self, map: &Map) -> Option<f32> {
61        let start = map.get_vertex(self.start_vertex)?;
62        let end = map.get_vertex(self.end_vertex)?;
63
64        Some((end - start).magnitude())
65    }
66
67    /// Generate a bounding box for the linedef
68    pub fn bounding_box(&self, map: &Map) -> BBox {
69        let start = map
70            .get_vertex(self.start_vertex)
71            .unwrap_or(Vec2::broadcast(0.0));
72        let end = map
73            .get_vertex(self.end_vertex)
74            .unwrap_or(Vec2::broadcast(0.0));
75
76        let min = Vec2::new(start.x.min(end.x), start.y.min(end.y));
77        let max = Vec2::new(start.x.max(end.x), start.y.max(end.y));
78
79        BBox::new(min, max)
80    }
81
82    /// Returns the vertical span (min_y, max_y) of this linedef in world space (Y-up).
83    pub fn y_span_world(&self, map: &Map) -> Option<(f32, f32)> {
84        let a = map.get_vertex_3d(self.start_vertex)?;
85        let b = map.get_vertex_3d(self.end_vertex)?;
86        let min_y = a.y.min(b.y);
87        let max_y = a.y.max(b.y);
88        Some((min_y, max_y))
89    }
90
91    /// Checks whether this linedef intersects a vertical slice centered at `slice_y` with thickness `thickness`.
92    pub fn intersects_vertical_slice(&self, map: &Map, slice_y: f32, thickness: f32) -> bool {
93        if thickness <= 0.0 {
94            return false;
95        }
96        if let Some((min_y, max_y)) = self.y_span_world(map) {
97            let half = thickness * 0.5;
98            let y0 = slice_y - half;
99            let y1 = slice_y + half;
100            max_y >= y0 && min_y <= y1
101        } else {
102            false
103        }
104    }
105}
106
107impl PartialEq for Linedef {
108    fn eq(&self, other: &Self) -> bool {
109        (self.start_vertex == other.start_vertex && self.end_vertex == other.end_vertex)
110            || (self.start_vertex == other.end_vertex && self.end_vertex == other.start_vertex)
111    }
112}
113impl Eq for Linedef {}
114
115/// A "compiled" version which is used in MapMini for lighting, navigation etc
116#[derive(Clone)]
117pub struct CompiledLinedef {
118    pub start: Vec2<f32>,
119    pub end: Vec2<f32>,
120
121    pub wall_width: f32,
122    pub wall_height: f32,
123}
124
125impl CompiledLinedef {
126    pub fn new(start: Vec2<f32>, end: Vec2<f32>, wall_width: f32, wall_height: f32) -> Self {
127        Self {
128            start,
129            end,
130            wall_width,
131            wall_height,
132        }
133    }
134}