vintage_schematics/formats/
internal.rs1use serde::{Deserialize, Serialize};
5
6use crate::{
7 Map,
8 convert::{
9 block::{SIMPLE_CACHE, VS_FLOWER_POT_CODE, convert_block},
10 entities::Sign,
11 },
12 entity::{ItemClass, ItemStack, Value},
13 formats::{
14 Settings,
15 common::{MinecraftBlockEntity, Xyz},
16 },
17};
18
19pub type PropertyMap = Map<String, String>;
21
22pub trait PropertyMapExt {
23 fn get_facing_or_north(&self) -> &str;
24
25 fn get_u8(&self, key: &str) -> Option<u8>;
26
27 fn is_true(&self, key: &str) -> bool;
28}
29
30impl PropertyMapExt for PropertyMap {
31 fn get_facing_or_north(&self) -> &str { self.get("facing").map_or("north", String::as_str) }
32
33 fn get_u8(&self, key: &str) -> Option<u8> { self.get(key).and_then(|v| v.parse().ok()) }
34
35 fn is_true(&self, key: &str) -> bool { self.get(key).is_some_and(|v| v == "true") }
36}
37
38pub type EntityMap = Map<String, Value>;
40
41#[derive(Debug, Clone, Serialize)]
45pub struct Internal {
46 pub block_codes: BlockCodes,
48
49 pub blocks: Vec<Block>,
51
52 pub tile_entities: Vec<MinecraftBlockEntity>,
54
55 pub size: Xyz,
57}
58
59#[derive(Debug, Clone, Serialize)]
61pub enum BlockCodes {
62 VintageStory {
63 codes: Vec<VintageStoryBlockCode>,
64 properties: Vec<EntityMap>,
65 },
66 Minecraft(Vec<MinecraftBlockCode>),
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
70#[serde(rename_all = "PascalCase")]
71pub struct MinecraftBlockCode {
72 pub name: String,
73 #[serde(default)]
74 pub properties: PropertyMap,
75}
76
77#[derive(Debug, Clone, Serialize)]
78pub struct VintageStoryBlockCode {
79 pub name: String,
83
84 pub properties: Vec<usize>,
86}
87
88#[derive(Debug, Clone, Serialize)]
89pub struct Block {
90 pub id: usize,
91 pub position: Xyz,
92}
93
94impl Internal {
95 pub fn convert_to_vintage_story(&mut self, settings: &Settings) {
97 let (mut new_blocks, mut new_properties) = if let BlockCodes::Minecraft(block_codes) = &self.block_codes {
98 let mut new_blocks = Vec::with_capacity(block_codes.len());
99 let mut new_properties = Vec::new();
100 let mut extra_blocks = Vec::new();
101
102 if let Ok(mut lock) = SIMPLE_CACHE.lock() {
103 lock.clear();
104 }
105
106 for block in block_codes {
107 let (code, properties) = match convert_block(block, settings) {
108 Some(result) => result,
109 None if settings.replace_missing => (String::from("game:leaves-placed-birch"), None),
110 None => (String::from("game:air"), None), };
112
113 #[allow(clippy::option_if_let_else)]
114 let properties = if let Some(mut properties) = properties {
115 let existing = new_properties.iter().position(|p| *p == properties);
117
118 Some(
119 if let Some(existing) = existing
120 && !properties.contains_key("unique!")
121 {
122 existing
124 } else {
125 if code == VS_FLOWER_POT_CODE
126 && let Some(Value::String(flower)) = properties.remove("flower")
127 {
128 Self::convert_potted_flower(block_codes, &new_blocks, &mut extra_blocks, &mut properties, flower);
129 }
130 new_properties.push(properties);
132 new_properties.len() - 1
133 },
134 )
135 } else {
136 None
137 };
138
139 let properties = properties.map(|p| vec![p]).unwrap_or_default();
140 let block = VintageStoryBlockCode { name: code, properties };
141 new_blocks.push(block);
142 }
143
144 new_blocks.extend(extra_blocks.into_iter().map(|name| VintageStoryBlockCode {
145 name,
146 properties: Vec::new(),
147 }));
148 (new_blocks, new_properties)
149 } else {
150 return;
152 };
153
154 self.convert_tile_entities(&mut new_blocks, &mut new_properties);
155
156 self.block_codes = BlockCodes::VintageStory {
157 codes: new_blocks,
158 properties: new_properties,
159 };
160 }
161
162 fn convert_potted_flower(
163 block_codes: &[MinecraftBlockCode],
164 new_blocks: &[VintageStoryBlockCode],
165 extra_blocks: &mut Vec<String>,
166 properties: &mut EntityMap,
167 flower: String,
168 ) {
169 #[allow(clippy::option_if_let_else)]
175 let flower_id = if let Some(id) = new_blocks.iter().position(|b: &VintageStoryBlockCode| b.name == flower) {
176 id
177 } else if let Some(id) = extra_blocks.iter().position(|e| e == &flower) {
178 block_codes.len() + id
183 } else {
184 extra_blocks.push(flower);
187 block_codes.len() + extra_blocks.len() - 1
188 };
189
190 let item_stack = ItemStack {
192 class: ItemClass::Block,
193 id: i32::try_from(flower_id).unwrap_or_default(),
194 stack_size: 1,
195 stack_attributes: Map::new(),
196 };
197
198 properties.insert(String::from("meshAngle"), Value::Float(0.0));
199 properties.insert(
200 String::from("inventory"),
201 Value::Tree(Map::from([
202 (String::from("qslots"), Value::Integer(1)),
203 (
204 String::from("slots"),
205 Value::Tree(Map::from([(String::from("0"), Value::ItemStack(Some(item_stack)))])),
206 ),
207 ])),
208 );
209 }
210
211 fn convert_tile_entities(&self, new_blocks: &mut [VintageStoryBlockCode], new_properties: &mut Vec<EntityMap>) {
212 for tile_entity in &self.tile_entities {
214 if let Ok(sign) = Sign::try_from(tile_entity)
216 && let Some(block) = self.blocks.iter().find(|block| block.position == tile_entity.position())
217 && let Some(block) = new_blocks.get_mut(block.id)
218 && let Some(properties) = block.properties.first()
219 && let Some(properties) = new_properties.get_mut(*properties)
220 {
221 let (r, g, b) = sign.colour();
222 let colour = i32::from_be_bytes([0xFF, r, g, b]);
226
227 let properties = if properties.contains_key("unique!") {
228 properties
229 } else {
230 let properties = properties.clone();
233 block.properties.push(new_properties.len());
234 new_properties.push(properties);
235 let Some(properties) = new_properties.last_mut() else {
236 continue; };
238 properties
239 };
240
241 properties.extend([
242 (String::from("color"), Value::Integer(colour)),
243 (String::from("fontSize"), Value::Float(16.0)),
244 (String::from("text"), Value::String(sign.text())),
245 (String::from("posx"), Value::Integer(tile_entity.x)),
248 (String::from("posy"), Value::Integer(tile_entity.y)),
249 (String::from("posz"), Value::Integer(tile_entity.z)),
250 ]);
251 properties.remove("unique!");
252 }
253 }
254 }
255}