Skip to main content

minedmap_resource/
lib.rs

1#![doc = env!("CARGO_PKG_DESCRIPTION")]
2#![warn(missing_docs)]
3#![warn(clippy::missing_docs_in_private_items)]
4
5mod biomes;
6mod block_color;
7mod block_types;
8mod legacy_biomes;
9mod legacy_block_types;
10
11use std::collections::HashMap;
12
13use enumflags2::{BitFlags, bitflags};
14use serde::{Deserialize, Serialize};
15
16/// Flags describing special properties of [BlockType]s
17#[bitflags]
18#[repr(u8)]
19#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
20pub enum BlockFlag {
21	/// The block type is opaque
22	Opaque,
23	/// The block type is colored using biome grass colors
24	Grass,
25	/// The block type is colored using biome foliage colors
26	Foliage,
27	/// The block type is birch foliage
28	Birch,
29	/// The block type is spruce foliage
30	Spruce,
31	/// The block type is colored using biome water colors
32	Water,
33	/// The block type is a wall sign
34	///
35	/// The WallSign flag is used to distinguish wall signs from
36	/// freestanding or -hanging signs.
37	WallSign,
38}
39
40/// An RGB color with u8 components
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
42pub struct Color(pub [u8; 3]);
43
44/// An RGB color with f32 components
45pub type Colorf = glam::Vec3;
46
47/// A block type specification
48#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
49pub struct BlockColor {
50	/// Bit set of [BlockFlag]s describing special properties of the block type
51	pub flags: BitFlags<BlockFlag>,
52	/// Base color of the block type
53	pub color: Color,
54}
55
56impl BlockColor {
57	/// Checks whether a block color has a given [BlockFlag] set
58	#[inline]
59	pub fn is(&self, flag: BlockFlag) -> bool {
60		self.flags.contains(flag)
61	}
62}
63
64/// A block type specification (for use in constants)
65#[derive(Debug, Clone)]
66struct ConstBlockType {
67	/// Determines the rendered color of the block type
68	pub block_color: BlockColor,
69	/// Material of a sign block
70	pub sign_material: Option<&'static str>,
71}
72
73/// A block type specification
74#[derive(Debug, Clone)]
75pub struct BlockType {
76	/// Determines the rendered color of the block type
77	pub block_color: BlockColor,
78	/// Material of a sign block
79	pub sign_material: Option<String>,
80}
81
82impl From<&ConstBlockType> for BlockType {
83	fn from(value: &ConstBlockType) -> Self {
84		BlockType {
85			block_color: value.block_color,
86			sign_material: value.sign_material.map(String::from),
87		}
88	}
89}
90
91/// Used to look up standard Minecraft block types
92#[derive(Debug)]
93pub struct BlockTypes {
94	/// Map of string IDs to block types
95	block_type_map: HashMap<String, BlockType>,
96	/// Array used to look up old numeric block type and subtype values
97	legacy_block_types: Box<[[BlockType; 16]; 256]>,
98}
99
100impl Default for BlockTypes {
101	fn default() -> Self {
102		let block_type_map: HashMap<_, _> = block_types::BLOCK_TYPES
103			.iter()
104			.map(|(k, v)| (String::from(*k), BlockType::from(v)))
105			.collect();
106		let legacy_block_types = Box::new(legacy_block_types::LEGACY_BLOCK_TYPES.map(|inner| {
107			inner.map(|id| {
108				block_type_map
109					.get(id)
110					.expect("Unknown legacy block type")
111					.clone()
112			})
113		}));
114
115		BlockTypes {
116			block_type_map,
117			legacy_block_types,
118		}
119	}
120}
121
122impl BlockTypes {
123	/// Resolves a Minecraft 1.13+ string block type ID
124	#[inline]
125	pub fn get(&self, id: &str) -> Option<&BlockType> {
126		let suffix = id.strip_prefix("minecraft:").unwrap_or(id);
127		self.block_type_map.get(suffix)
128	}
129
130	/// Resolves a Minecraft pre-1.13 numeric block type ID
131	#[inline]
132	pub fn get_legacy(&self, id: u8, data: u8) -> Option<&BlockType> {
133		Some(&self.legacy_block_types[id as usize][data as usize])
134	}
135}
136
137pub use block_color::{block_color, needs_biome};
138
139/// Grass color modifier used by a biome
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
141pub enum BiomeGrassColorModifier {
142	/// Grass color modifier used by the dark forest biome
143	DarkForest,
144	/// Grass color modifier used by swamp biomes
145	Swamp,
146}
147
148/// A biome specification
149///
150/// A Biome contains all information about a biome necessary to compute a block
151/// color given a block type and depth
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
153pub struct Biome {
154	/// Temperature value
155	///
156	/// For more efficient storage, the temperature is stored as an integer
157	/// after mutiplying the raw value by 20
158	pub temp: i8,
159	/// Downfall value
160	///
161	/// For more efficient storage, the downfall is stored as an integer
162	/// after mutiplying the raw value by 20
163	pub downfall: i8,
164	/// Water color override
165	pub water_color: Option<Color>,
166	/// Foliage color override
167	pub foliage_color: Option<Color>,
168	/// Grass color override
169	pub grass_color: Option<Color>,
170	/// Grass color modifier
171	pub grass_color_modifier: Option<BiomeGrassColorModifier>,
172}
173
174impl Biome {
175	/// Constructs a new Biome
176	const fn new(temp: i16, downfall: i16) -> Biome {
177		/// Helper to encode temperature and downfall values
178		///
179		/// Converts temperatue and downfall from the input format
180		/// (mutiplied by 100) to i8 range for more efficient storage.
181		const fn encode(v: i16) -> i8 {
182			(v / 5) as i8
183		}
184		Biome {
185			temp: encode(temp),
186			downfall: encode(downfall),
187			grass_color_modifier: None,
188			water_color: None,
189			foliage_color: None,
190			grass_color: None,
191		}
192	}
193
194	/// Builder function to override the biome water color
195	const fn water(self, water_color: [u8; 3]) -> Biome {
196		Biome {
197			water_color: Some(Color(water_color)),
198			..self
199		}
200	}
201
202	/// Builder function to override the biome foliage color
203	const fn foliage(self, foliage_color: [u8; 3]) -> Biome {
204		Biome {
205			foliage_color: Some(Color(foliage_color)),
206			..self
207		}
208	}
209
210	/// Builder function to override the biome grass color
211	const fn grass(self, grass_color: [u8; 3]) -> Biome {
212		Biome {
213			grass_color: Some(Color(grass_color)),
214			..self
215		}
216	}
217
218	/// Builder function to set a grass color modifier
219	const fn modify(self, grass_color_modifier: BiomeGrassColorModifier) -> Biome {
220		Biome {
221			grass_color_modifier: Some(grass_color_modifier),
222			..self
223		}
224	}
225
226	/// Decodes a temperature or downfall value from the storage format to
227	/// f32 for further calculation
228	fn decode(val: i8) -> f32 {
229		f32::from(val) / 20.0
230	}
231
232	/// Returns the biome's temperature decoded to its original float value
233	pub fn temp(&self) -> f32 {
234		Self::decode(self.temp)
235	}
236
237	/// Returns the biome's downfall decoded to its original float value
238	pub fn downfall(&self) -> f32 {
239		Self::decode(self.downfall)
240	}
241}
242
243/// Used to look up standard Minecraft biome types
244#[derive(Debug)]
245pub struct BiomeTypes {
246	/// Map of string IDs to biome types
247	biome_map: HashMap<String, &'static Biome>,
248	/// Array used to look up old numeric biome IDs
249	legacy_biomes: Box<[&'static Biome; 256]>,
250	/// Fallback for unknown (new/modded) biomes
251	fallback_biome: &'static Biome,
252}
253
254impl Default for BiomeTypes {
255	fn default() -> Self {
256		let mut biome_map: HashMap<_, _> = biomes::BIOMES
257			.iter()
258			.map(|(k, v)| (String::from(*k), v))
259			.collect();
260
261		for &(old, new) in legacy_biomes::BIOME_ALIASES.iter().rev() {
262			let biome = biome_map
263				.get(new)
264				.copied()
265				.expect("Biome alias for unknown biome");
266			assert!(biome_map.insert(String::from(old), biome).is_none());
267		}
268
269		let legacy_biomes = (0..=255)
270			.map(|index| {
271				let id = legacy_biomes::legacy_biome(index);
272				*biome_map.get(id).expect("Unknown legacy biome")
273			})
274			.collect::<Box<[_]>>()
275			.try_into()
276			.unwrap();
277
278		let fallback_biome = *biome_map.get("plains").expect("Plains biome undefined");
279
280		Self {
281			biome_map,
282			legacy_biomes,
283			fallback_biome,
284		}
285	}
286}
287
288impl BiomeTypes {
289	/// Resolves a Minecraft 1.18+ string biome type ID
290	#[inline]
291	pub fn get(&self, id: &str) -> Option<&Biome> {
292		let suffix = id.strip_prefix("minecraft:").unwrap_or(id);
293		self.biome_map.get(suffix).copied()
294	}
295
296	/// Resolves a Minecraft pre-1.18 numeric biome type ID
297	#[inline]
298	pub fn get_legacy(&self, id: u8) -> Option<&Biome> {
299		Some(self.legacy_biomes[id as usize])
300	}
301
302	/// Returns the fallback for unknown (new/modded) biomes
303	#[inline]
304	pub fn get_fallback(&self) -> &Biome {
305		self.fallback_biome
306	}
307}