lib_curveball/map/
qmap.rs1use crate::map::geometry::Brush;
7use core::fmt;
8use std::fmt::{Display, Formatter};
9
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub struct QEntity {
15 pub parameters: HashMap<String, String>,
16 pub brushes: Vec<Brush>,
17}
18
19impl QEntity {
20 pub(crate) fn bake(&self) -> impl Display + use<'_> {
21 struct QEntityDisp<'a>(&'a QEntity);
22 impl Display for QEntityDisp<'_> {
23 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
24 writeln!(f, "{{",)?;
25 for (key, value) in self.0.parameters.iter() {
26 writeln!(f, "\"{key}\" \"{value}\"")?;
27 }
28 for (i, brush) in self.0.brushes.iter().enumerate() {
29 writeln!(f, "// brush {i}")?;
30 write!(f, "{}", brush.bake())?;
31 }
32 writeln!(f, "}}")?;
33 Ok(())
34 }
35 }
36 QEntityDisp(self)
37 }
38}
39
40#[derive(Debug, Clone)]
45pub struct QMap {
46 pub entities: Vec<QEntity>,
47 pub metadata: Vec<String>,
48}
49
50impl QMap {
51 pub fn new(entities: Vec<QEntity>) -> Self {
52 Self {
53 entities,
54 metadata: Vec::new(),
55 }
56 }
57
58 pub fn with_metadata(mut self, metadata: String) -> Self {
59 self.metadata.push(metadata);
60 self
61 }
62
63 pub fn with_tb_neverball_metadata(self) -> Self {
64 self.with_metadata("Game: Neverball".to_string())
65 .with_metadata("Format: Quake3".to_string())
66 }
67
68 pub fn bake(&self) -> impl Display + use<'_> {
69 struct QMapDisp<'a>(&'a QMap);
70 impl Display for QMapDisp<'_> {
71 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
72 for metadata_line in self.0.metadata.iter().flat_map(|meta| meta.lines()) {
73 writeln!(f, "// {}", metadata_line)?;
74 }
75 for (i, entity) in self.0.entities.iter().enumerate() {
76 writeln!(f, "// entity {i}")?;
77 write!(f, "{}", entity.bake())?;
78 }
79 Ok(())
80 }
81 }
82 QMapDisp(self)
83 }
84}
85
86impl Display for QMap {
87 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88 write!(f, "{}", self.bake())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::map::entity::SimpleWorldspawn;
96 use glam::DVec3;
97
98 #[test]
99 fn compile_map() {
100 let vertices = vec![
101 DVec3::from([0.0, 0.0, 0.0]),
102 DVec3::from([0.0, 0.0, 1.0]),
103 DVec3::from([0.0, 1.0, 0.0]),
104 DVec3::from([1.0, 0.0, 0.0]),
105 DVec3::from([0.3, 0.3, 0.3]),
106 ];
107
108 let brush1 = Brush::try_from_vertices(&vertices, Some(1000)).unwrap();
109 let brush2 = Brush::try_from_vertices(&vertices, Some(1000)).unwrap();
110
111 let worldspawn = SimpleWorldspawn::new(vec![brush1, brush2]);
112 let entity: QEntity = worldspawn.into();
113 let map: QMap = QMap::new(vec![entity]).with_tb_neverball_metadata();
114
115 println!("{}", map.to_string());
116
117 let should_eq_str = r#"// Game: Neverball
118// Format: Quake3
119// entity 0
120{
121"classname" "worldspawn"
122// brush 0
123{
124( 0.000000 0.000000 0.000000 ) ( 0.000000 1.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
125( 0.000000 1.000000 0.000000 ) ( 1.000000 0.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
126( 1.000000 0.000000 0.000000 ) ( 0.000000 0.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
127( 0.000000 0.000000 0.000000 ) ( 1.000000 0.000000 0.000000 ) ( 0.000000 1.000000 0.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
128}
129// brush 1
130{
131( 0.000000 0.000000 0.000000 ) ( 0.000000 1.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
132( 0.000000 1.000000 0.000000 ) ( 1.000000 0.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
133( 1.000000 0.000000 0.000000 ) ( 0.000000 0.000000 0.000000 ) ( 0.000000 0.000000 1.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
134( 0.000000 0.000000 0.000000 ) ( 1.000000 0.000000 0.000000 ) ( 0.000000 1.000000 0.000000 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0
135}
136}
137"#;
138 println!("{}", should_eq_str);
139 assert_eq!(format!("{}", map.to_string()), should_eq_str);
140 }
141}