mirror of
https://github.com/louis-e/arnis.git
synced 2026-02-15 09:02:20 -05:00
Merge pull request #742 from louis-e/buildings-improvement
Buildings improvement
This commit is contained in:
@@ -129,6 +129,81 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
)],
|
||||
),
|
||||
|
||||
// Dark oak log with axis
|
||||
"dark_oak_log" => BedrockBlock::with_states(
|
||||
"dark_oak_log",
|
||||
vec![(
|
||||
"pillar_axis",
|
||||
BedrockBlockStateValue::String("y".to_string()),
|
||||
)],
|
||||
),
|
||||
|
||||
// Jungle log with axis
|
||||
"jungle_log" => BedrockBlock::with_states(
|
||||
"jungle_log",
|
||||
vec![(
|
||||
"pillar_axis",
|
||||
BedrockBlockStateValue::String("y".to_string()),
|
||||
)],
|
||||
),
|
||||
|
||||
// Acacia log with axis
|
||||
"acacia_log" => BedrockBlock::with_states(
|
||||
"acacia_log",
|
||||
vec![(
|
||||
"pillar_axis",
|
||||
BedrockBlockStateValue::String("y".to_string()),
|
||||
)],
|
||||
),
|
||||
|
||||
// Spruce leaves with persistence
|
||||
"spruce_leaves" => BedrockBlock::with_states(
|
||||
"leaves",
|
||||
vec![
|
||||
(
|
||||
"old_leaf_type",
|
||||
BedrockBlockStateValue::String("spruce".to_string()),
|
||||
),
|
||||
("persistent_bit", BedrockBlockStateValue::Bool(true)),
|
||||
],
|
||||
),
|
||||
|
||||
// Dark oak leaves with persistence
|
||||
"dark_oak_leaves" => BedrockBlock::with_states(
|
||||
"leaves2",
|
||||
vec![
|
||||
(
|
||||
"new_leaf_type",
|
||||
BedrockBlockStateValue::String("dark_oak".to_string()),
|
||||
),
|
||||
("persistent_bit", BedrockBlockStateValue::Bool(true)),
|
||||
],
|
||||
),
|
||||
|
||||
// Jungle leaves with persistence
|
||||
"jungle_leaves" => BedrockBlock::with_states(
|
||||
"leaves",
|
||||
vec![
|
||||
(
|
||||
"old_leaf_type",
|
||||
BedrockBlockStateValue::String("jungle".to_string()),
|
||||
),
|
||||
("persistent_bit", BedrockBlockStateValue::Bool(true)),
|
||||
],
|
||||
),
|
||||
|
||||
// Acacia leaves with persistence
|
||||
"acacia_leaves" => BedrockBlock::with_states(
|
||||
"leaves2",
|
||||
vec![
|
||||
(
|
||||
"new_leaf_type",
|
||||
BedrockBlockStateValue::String("acacia".to_string()),
|
||||
),
|
||||
("persistent_bit", BedrockBlockStateValue::Bool(true)),
|
||||
],
|
||||
),
|
||||
|
||||
// Stone slab (bottom half by default)
|
||||
"stone_slab" => BedrockBlock::with_states(
|
||||
"stone_block_slab",
|
||||
@@ -215,6 +290,13 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
BedrockBlockStateValue::String("stone_brick".to_string()),
|
||||
)],
|
||||
),
|
||||
"brick_wall" => BedrockBlock::with_states(
|
||||
"cobblestone_wall",
|
||||
vec![(
|
||||
"wall_block_type",
|
||||
BedrockBlockStateValue::String("brick".to_string()),
|
||||
)],
|
||||
),
|
||||
|
||||
// Flowers - poppy is just "red_flower" in Bedrock
|
||||
"poppy" => BedrockBlock::with_states(
|
||||
@@ -321,6 +403,10 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
"concrete",
|
||||
vec![("color", BedrockBlockStateValue::String("brown".to_string()))],
|
||||
),
|
||||
"green_concrete" => BedrockBlock::with_states(
|
||||
"concrete",
|
||||
vec![("color", BedrockBlockStateValue::String("green".to_string()))],
|
||||
),
|
||||
|
||||
// Terracotta colors
|
||||
"white_terracotta" => BedrockBlock::with_states(
|
||||
@@ -372,6 +458,13 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
"stained_hardened_clay",
|
||||
vec![("color", BedrockBlockStateValue::String("black".to_string()))],
|
||||
),
|
||||
"light_gray_terracotta" => BedrockBlock::with_states(
|
||||
"stained_hardened_clay",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("silver".to_string()),
|
||||
)],
|
||||
),
|
||||
// Plain terracotta
|
||||
"terracotta" => BedrockBlock::simple("hardened_clay"),
|
||||
|
||||
@@ -403,6 +496,17 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
BedrockBlockStateValue::String("yellow".to_string()),
|
||||
)],
|
||||
),
|
||||
"orange_wool" => BedrockBlock::with_states(
|
||||
"wool",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("orange".to_string()),
|
||||
)],
|
||||
),
|
||||
"blue_wool" => BedrockBlock::with_states(
|
||||
"wool",
|
||||
vec![("color", BedrockBlockStateValue::String("blue".to_string()))],
|
||||
),
|
||||
|
||||
// Carpets
|
||||
"white_carpet" => BedrockBlock::with_states(
|
||||
@@ -434,6 +538,54 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
"stained_glass",
|
||||
vec![("color", BedrockBlockStateValue::String("brown".to_string()))],
|
||||
),
|
||||
"cyan_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![("color", BedrockBlockStateValue::String("cyan".to_string()))],
|
||||
),
|
||||
"blue_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![("color", BedrockBlockStateValue::String("blue".to_string()))],
|
||||
),
|
||||
"light_blue_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("light_blue".to_string()),
|
||||
)],
|
||||
),
|
||||
"red_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![("color", BedrockBlockStateValue::String("red".to_string()))],
|
||||
),
|
||||
"yellow_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("yellow".to_string()),
|
||||
)],
|
||||
),
|
||||
"purple_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("purple".to_string()),
|
||||
)],
|
||||
),
|
||||
"orange_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("orange".to_string()),
|
||||
)],
|
||||
),
|
||||
"magenta_stained_glass" => BedrockBlock::with_states(
|
||||
"stained_glass",
|
||||
vec![(
|
||||
"color",
|
||||
BedrockBlockStateValue::String("magenta".to_string()),
|
||||
)],
|
||||
),
|
||||
"daylight_detector" => BedrockBlock::simple("daylight_detector"),
|
||||
|
||||
// Planks - Bedrock uses single "planks" block with wood_type state
|
||||
"oak_planks" => BedrockBlock::with_states(
|
||||
@@ -539,8 +691,34 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock {
|
||||
// Oak items mapped to dark_oak in Bedrock (or generic equivalents)
|
||||
"oak_pressure_plate" => BedrockBlock::simple("wooden_pressure_plate"),
|
||||
"oak_door" => BedrockBlock::simple("wooden_door"),
|
||||
"spruce_door" => BedrockBlock::simple("spruce_door"),
|
||||
"dark_oak_door" => BedrockBlock::simple("dark_oak_door"),
|
||||
"oak_trapdoor" => BedrockBlock::simple("trapdoor"),
|
||||
|
||||
// Vegetation with different Bedrock names
|
||||
"fern" => BedrockBlock::with_states(
|
||||
"tallgrass",
|
||||
vec![(
|
||||
"tall_grass_type",
|
||||
BedrockBlockStateValue::String("fern".to_string()),
|
||||
)],
|
||||
),
|
||||
"large_fern" => BedrockBlock::with_states(
|
||||
"double_plant",
|
||||
vec![(
|
||||
"double_plant_type",
|
||||
BedrockBlockStateValue::String("fern".to_string()),
|
||||
)],
|
||||
),
|
||||
"cobweb" => BedrockBlock::simple("web"),
|
||||
|
||||
// Potted plants (Bedrock uses "flower_pot" for all variants;
|
||||
// the contained plant is a block entity, not a block state)
|
||||
"potted_poppy" => BedrockBlock::simple("flower_pot"),
|
||||
"potted_red_tulip" => BedrockBlock::simple("flower_pot"),
|
||||
"potted_dandelion" => BedrockBlock::simple("flower_pot"),
|
||||
"potted_blue_orchid" => BedrockBlock::simple("flower_pot"),
|
||||
|
||||
// Bed (Bedrock uses single "bed" block with color state)
|
||||
"red_bed" => BedrockBlock::with_states(
|
||||
"bed",
|
||||
@@ -564,8 +742,14 @@ pub fn to_bedrock_block_with_properties(
|
||||
) -> BedrockBlock {
|
||||
let java_name = block.name();
|
||||
|
||||
// If no stored properties were passed, fall back to block.properties()
|
||||
// so that blocks placed via set_block_absolute (e.g. doors with half=upper/lower)
|
||||
// still get their default properties forwarded to the Bedrock converter.
|
||||
let fallback_props = block.properties();
|
||||
let effective_properties = java_properties.or(fallback_props.as_ref());
|
||||
|
||||
// Extract Java properties as a map if present
|
||||
let props_map = java_properties.and_then(|v| {
|
||||
let props_map = effective_properties.and_then(|v| {
|
||||
if let fastnbt::Value::Compound(map) = v {
|
||||
Some(map)
|
||||
} else {
|
||||
@@ -593,6 +777,16 @@ pub fn to_bedrock_block_with_properties(
|
||||
return convert_log(java_name, props_map);
|
||||
}
|
||||
|
||||
// Handle doors with half property (upper/lower → upper_block_bit)
|
||||
if java_name.ends_with("_door") && java_name != "iron_door" {
|
||||
return convert_door(java_name, props_map);
|
||||
}
|
||||
|
||||
// Handle trapdoors with facing/open/half properties
|
||||
if java_name.ends_with("_trapdoor") {
|
||||
return convert_trapdoor(java_name, props_map);
|
||||
}
|
||||
|
||||
// Fall back to basic conversion without properties
|
||||
to_bedrock_block(block)
|
||||
}
|
||||
@@ -795,6 +989,152 @@ fn convert_log(
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Java door block to Bedrock format with upper_block_bit.
|
||||
///
|
||||
/// Java doors use `half=upper/lower`, Bedrock uses `upper_block_bit` (bool).
|
||||
/// Also maps door names: `oak_door` → `wooden_door`, others keep their names.
|
||||
fn convert_door(
|
||||
java_name: &str,
|
||||
props: Option<&std::collections::HashMap<String, fastnbt::Value>>,
|
||||
) -> BedrockBlock {
|
||||
let bedrock_name = match java_name {
|
||||
"oak_door" => "wooden_door",
|
||||
_ => java_name, // spruce_door, dark_oak_door, etc. keep their name
|
||||
};
|
||||
|
||||
let mut states = HashMap::new();
|
||||
|
||||
if let Some(props) = props {
|
||||
// Convert half: Java "upper"/"lower" → Bedrock upper_block_bit true/false
|
||||
if let Some(fastnbt::Value::String(half)) = props.get("half") {
|
||||
let is_upper = half == "upper";
|
||||
states.insert(
|
||||
"upper_block_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(is_upper),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert facing if present
|
||||
if let Some(fastnbt::Value::String(facing)) = props.get("facing") {
|
||||
let direction = match facing.as_str() {
|
||||
"east" => 0,
|
||||
"south" => 1,
|
||||
"west" => 2,
|
||||
"north" => 3,
|
||||
_ => 0,
|
||||
};
|
||||
states.insert(
|
||||
"direction".to_string(),
|
||||
BedrockBlockStateValue::Int(direction),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert hinge if present
|
||||
if let Some(fastnbt::Value::String(hinge)) = props.get("hinge") {
|
||||
let door_hinge = hinge == "right";
|
||||
states.insert(
|
||||
"door_hinge_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(door_hinge),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert open if present
|
||||
if let Some(fastnbt::Value::String(open)) = props.get("open") {
|
||||
let is_open = open == "true";
|
||||
states.insert(
|
||||
"open_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(is_open),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Defaults if no properties were set
|
||||
if !states.contains_key("upper_block_bit") {
|
||||
states.insert(
|
||||
"upper_block_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(false),
|
||||
);
|
||||
}
|
||||
if !states.contains_key("direction") {
|
||||
states.insert("direction".to_string(), BedrockBlockStateValue::Int(0));
|
||||
}
|
||||
|
||||
BedrockBlock {
|
||||
name: format!("minecraft:{bedrock_name}"),
|
||||
states,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Java trapdoor block to Bedrock format with facing/open/half states.
|
||||
fn convert_trapdoor(
|
||||
java_name: &str,
|
||||
props: Option<&std::collections::HashMap<String, fastnbt::Value>>,
|
||||
) -> BedrockBlock {
|
||||
// Map Java trapdoor names to Bedrock equivalents
|
||||
let bedrock_name = match java_name {
|
||||
"oak_trapdoor" => "trapdoor",
|
||||
"iron_trapdoor" => "iron_trapdoor",
|
||||
_ => java_name, // spruce_trapdoor, dark_oak_trapdoor, birch_trapdoor, etc.
|
||||
};
|
||||
|
||||
let mut states = HashMap::new();
|
||||
|
||||
if let Some(props) = props {
|
||||
// Convert facing: Java "north/south/east/west" → Bedrock "direction" (0-3)
|
||||
// Bedrock trapdoor: 0=south, 1=north, 2=east, 3=west
|
||||
if let Some(fastnbt::Value::String(facing)) = props.get("facing") {
|
||||
let direction = match facing.as_str() {
|
||||
"south" => 0,
|
||||
"north" => 1,
|
||||
"east" => 2,
|
||||
"west" => 3,
|
||||
_ => 0,
|
||||
};
|
||||
states.insert(
|
||||
"direction".to_string(),
|
||||
BedrockBlockStateValue::Int(direction),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert open: Java "true"/"false" → Bedrock open_bit
|
||||
if let Some(fastnbt::Value::String(open)) = props.get("open") {
|
||||
let is_open = open == "true";
|
||||
states.insert(
|
||||
"open_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(is_open),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert half: Java "top"/"bottom" → Bedrock upside_down_bit
|
||||
if let Some(fastnbt::Value::String(half)) = props.get("half") {
|
||||
let upside_down = half == "top";
|
||||
states.insert(
|
||||
"upside_down_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(upside_down),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Defaults if no properties were set
|
||||
if !states.contains_key("direction") {
|
||||
states.insert("direction".to_string(), BedrockBlockStateValue::Int(0));
|
||||
}
|
||||
if !states.contains_key("open_bit") {
|
||||
states.insert("open_bit".to_string(), BedrockBlockStateValue::Bool(false));
|
||||
}
|
||||
if !states.contains_key("upside_down_bit") {
|
||||
states.insert(
|
||||
"upside_down_bit".to_string(),
|
||||
BedrockBlockStateValue::Bool(false),
|
||||
);
|
||||
}
|
||||
|
||||
BedrockBlock {
|
||||
name: format!("minecraft:{bedrock_name}"),
|
||||
states,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -290,6 +290,43 @@ impl Block {
|
||||
209 => "redstone_block",
|
||||
210 => "chain",
|
||||
211 => "chain",
|
||||
212 => "spruce_door",
|
||||
213 => "spruce_door",
|
||||
214 => "smooth_stone_slab",
|
||||
215 => "glass_pane",
|
||||
216 => "light_gray_terracotta",
|
||||
217 => "oak_slab",
|
||||
218 => "oak_door",
|
||||
219 => "dark_oak_log",
|
||||
220 => "dark_oak_leaves",
|
||||
221 => "jungle_log",
|
||||
222 => "jungle_leaves",
|
||||
223 => "acacia_log",
|
||||
224 => "acacia_leaves",
|
||||
225 => "spruce_leaves",
|
||||
226 => "cyan_stained_glass",
|
||||
227 => "blue_stained_glass",
|
||||
228 => "light_blue_stained_glass",
|
||||
229 => "daylight_detector",
|
||||
230 => "red_stained_glass",
|
||||
231 => "yellow_stained_glass",
|
||||
232 => "purple_stained_glass",
|
||||
233 => "orange_stained_glass",
|
||||
234 => "magenta_stained_glass",
|
||||
235 => "potted_poppy",
|
||||
236 => "oak_trapdoor",
|
||||
237 => "oak_trapdoor",
|
||||
238 => "oak_trapdoor",
|
||||
239 => "oak_trapdoor",
|
||||
240 => "quartz_slab",
|
||||
241 => "dark_oak_trapdoor",
|
||||
242 => "spruce_trapdoor",
|
||||
243 => "birch_trapdoor",
|
||||
244 => "mud_brick_slab",
|
||||
245 => "brick_slab",
|
||||
246 => "potted_red_tulip",
|
||||
247 => "potted_dandelion",
|
||||
248 => "potted_blue_orchid",
|
||||
_ => panic!("Invalid id"),
|
||||
}
|
||||
}
|
||||
@@ -348,6 +385,13 @@ impl Block {
|
||||
map
|
||||
})),
|
||||
|
||||
// Oak door lower
|
||||
159 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("half".to_string(), Value::String("lower".to_string()));
|
||||
map
|
||||
})),
|
||||
|
||||
116 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
@@ -529,6 +573,98 @@ impl Block {
|
||||
map.insert("axis".to_string(), Value::String("z".to_string()));
|
||||
map
|
||||
})),
|
||||
// Spruce door lower
|
||||
212 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("half".to_string(), Value::String("lower".to_string()));
|
||||
map
|
||||
})),
|
||||
// Spruce door upper
|
||||
213 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("half".to_string(), Value::String("upper".to_string()));
|
||||
map
|
||||
})),
|
||||
// Smooth stone slab (bottom by default)
|
||||
214 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("type".to_string(), Value::String("bottom".to_string()));
|
||||
map
|
||||
})),
|
||||
// Oak slab top
|
||||
217 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("type".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
// Oak door upper
|
||||
218 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("half".to_string(), Value::String("upper".to_string()));
|
||||
map
|
||||
})),
|
||||
// Dark oak leaves
|
||||
220 => Some(Value::Compound({
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
map.insert("persistent".to_string(), Value::String("true".to_string()));
|
||||
map
|
||||
})),
|
||||
// Jungle leaves
|
||||
222 => Some(Value::Compound({
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
map.insert("persistent".to_string(), Value::String("true".to_string()));
|
||||
map
|
||||
})),
|
||||
// Acacia leaves
|
||||
224 => Some(Value::Compound({
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
map.insert("persistent".to_string(), Value::String("true".to_string()));
|
||||
map
|
||||
})),
|
||||
// Spruce leaves
|
||||
225 => Some(Value::Compound({
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
map.insert("persistent".to_string(), Value::String("true".to_string()));
|
||||
map
|
||||
})),
|
||||
// Quartz slab (top half) used as window sill
|
||||
240 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("type".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
// Open oak trapdoor facing north (hangs flat against wall, looks like shutter)
|
||||
236 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("north".to_string()));
|
||||
map.insert("open".to_string(), Value::String("true".to_string()));
|
||||
map.insert("half".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
// Open oak trapdoor facing south
|
||||
237 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("south".to_string()));
|
||||
map.insert("open".to_string(), Value::String("true".to_string()));
|
||||
map.insert("half".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
// Open oak trapdoor facing east
|
||||
238 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("east".to_string()));
|
||||
map.insert("open".to_string(), Value::String("true".to_string()));
|
||||
map.insert("half".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
// Open oak trapdoor facing west
|
||||
239 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("west".to_string()));
|
||||
map.insert("open".to_string(), Value::String("true".to_string()));
|
||||
map.insert("half".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -789,6 +925,43 @@ pub const BRICK_WALL: Block = Block::new(208);
|
||||
pub const REDSTONE_BLOCK: Block = Block::new(209);
|
||||
pub const CHAIN_X: Block = Block::new(210);
|
||||
pub const CHAIN_Z: Block = Block::new(211);
|
||||
pub const SPRUCE_DOOR_LOWER: Block = Block::new(212);
|
||||
pub const SPRUCE_DOOR_UPPER: Block = Block::new(213);
|
||||
pub const SMOOTH_STONE_SLAB: Block = Block::new(214);
|
||||
pub const GLASS_PANE: Block = Block::new(215);
|
||||
pub const LIGHT_GRAY_TERRACOTTA: Block = Block::new(216);
|
||||
pub const OAK_SLAB_TOP: Block = Block::new(217);
|
||||
pub const OAK_DOOR_UPPER: Block = Block::new(218);
|
||||
pub const DARK_OAK_LOG: Block = Block::new(219);
|
||||
pub const DARK_OAK_LEAVES: Block = Block::new(220);
|
||||
pub const JUNGLE_LOG: Block = Block::new(221);
|
||||
pub const JUNGLE_LEAVES: Block = Block::new(222);
|
||||
pub const ACACIA_LOG: Block = Block::new(223);
|
||||
pub const ACACIA_LEAVES: Block = Block::new(224);
|
||||
pub const SPRUCE_LEAVES: Block = Block::new(225);
|
||||
pub const CYAN_STAINED_GLASS: Block = Block::new(226);
|
||||
pub const BLUE_STAINED_GLASS: Block = Block::new(227);
|
||||
pub const LIGHT_BLUE_STAINED_GLASS: Block = Block::new(228);
|
||||
pub const DAYLIGHT_DETECTOR: Block = Block::new(229);
|
||||
pub const RED_STAINED_GLASS: Block = Block::new(230);
|
||||
pub const YELLOW_STAINED_GLASS: Block = Block::new(231);
|
||||
pub const PURPLE_STAINED_GLASS: Block = Block::new(232);
|
||||
pub const ORANGE_STAINED_GLASS: Block = Block::new(233);
|
||||
pub const MAGENTA_STAINED_GLASS: Block = Block::new(234);
|
||||
pub const FLOWER_POT: Block = Block::new(235);
|
||||
pub const OAK_TRAPDOOR_OPEN_NORTH: Block = Block::new(236);
|
||||
pub const OAK_TRAPDOOR_OPEN_SOUTH: Block = Block::new(237);
|
||||
pub const OAK_TRAPDOOR_OPEN_EAST: Block = Block::new(238);
|
||||
pub const OAK_TRAPDOOR_OPEN_WEST: Block = Block::new(239);
|
||||
pub const QUARTZ_SLAB_TOP: Block = Block::new(240);
|
||||
pub const DARK_OAK_TRAPDOOR: Block = Block::new(241);
|
||||
pub const SPRUCE_TRAPDOOR: Block = Block::new(242);
|
||||
pub const BIRCH_TRAPDOOR: Block = Block::new(243);
|
||||
pub const MUD_BRICK_SLAB: Block = Block::new(244);
|
||||
pub const BRICK_SLAB: Block = Block::new(245);
|
||||
pub const POTTED_RED_TULIP: Block = Block::new(246);
|
||||
pub const POTTED_DANDELION: Block = Block::new(247);
|
||||
pub const POTTED_BLUE_ORCHID: Block = Block::new(248);
|
||||
|
||||
/// Maps a block to its corresponding stair variant
|
||||
#[inline]
|
||||
@@ -840,58 +1013,80 @@ pub static WINDOW_VARIATIONS: [Block; 7] = [
|
||||
TINTED_GLASS,
|
||||
];
|
||||
|
||||
// Window types for different building styles
|
||||
// Residential window options
|
||||
pub static RESIDENTIAL_WINDOW_OPTIONS: [Block; 4] = [
|
||||
GLASS,
|
||||
WHITE_STAINED_GLASS,
|
||||
LIGHT_GRAY_STAINED_GLASS,
|
||||
BROWN_STAINED_GLASS,
|
||||
];
|
||||
|
||||
// Institutional window options (hospital, school, etc.)
|
||||
pub static INSTITUTIONAL_WINDOW_OPTIONS: [Block; 3] =
|
||||
[GLASS, WHITE_STAINED_GLASS, LIGHT_GRAY_STAINED_GLASS];
|
||||
|
||||
// Hospitality window options (hotel, restaurant)
|
||||
pub static HOSPITALITY_WINDOW_OPTIONS: [Block; 2] = [GLASS, WHITE_STAINED_GLASS];
|
||||
|
||||
// Industrial window options
|
||||
pub static INDUSTRIAL_WINDOW_OPTIONS: [Block; 4] = [
|
||||
GLASS,
|
||||
GRAY_STAINED_GLASS,
|
||||
LIGHT_GRAY_STAINED_GLASS,
|
||||
BROWN_STAINED_GLASS,
|
||||
];
|
||||
|
||||
// Window types for different building styles (non-deterministic, for backwards compatibility)
|
||||
pub fn get_window_block_for_building_type(building_type: &str) -> Block {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
get_window_block_for_building_type_with_rng(building_type, &mut rng)
|
||||
}
|
||||
|
||||
/// Deterministic window block selection using provided RNG
|
||||
pub fn get_window_block_for_building_type_with_rng(
|
||||
building_type: &str,
|
||||
rng: &mut impl rand::Rng,
|
||||
) -> Block {
|
||||
match building_type {
|
||||
"residential" | "house" | "apartment" => {
|
||||
let residential_windows = [
|
||||
GLASS,
|
||||
WHITE_STAINED_GLASS,
|
||||
LIGHT_GRAY_STAINED_GLASS,
|
||||
BROWN_STAINED_GLASS,
|
||||
];
|
||||
residential_windows[rng.gen_range(0..residential_windows.len())]
|
||||
"residential" | "house" | "apartment" | "apartments" => {
|
||||
RESIDENTIAL_WINDOW_OPTIONS[rng.gen_range(0..RESIDENTIAL_WINDOW_OPTIONS.len())]
|
||||
}
|
||||
"hospital" | "school" | "university" => {
|
||||
let institutional_windows = [GLASS, WHITE_STAINED_GLASS, LIGHT_GRAY_STAINED_GLASS];
|
||||
institutional_windows[rng.gen_range(0..institutional_windows.len())]
|
||||
INSTITUTIONAL_WINDOW_OPTIONS[rng.gen_range(0..INSTITUTIONAL_WINDOW_OPTIONS.len())]
|
||||
}
|
||||
"hotel" | "restaurant" => {
|
||||
let hospitality_windows = [GLASS, WHITE_STAINED_GLASS];
|
||||
hospitality_windows[rng.gen_range(0..hospitality_windows.len())]
|
||||
HOSPITALITY_WINDOW_OPTIONS[rng.gen_range(0..HOSPITALITY_WINDOW_OPTIONS.len())]
|
||||
}
|
||||
"industrial" | "warehouse" => {
|
||||
let industrial_windows = [
|
||||
GLASS,
|
||||
GRAY_STAINED_GLASS,
|
||||
LIGHT_GRAY_STAINED_GLASS,
|
||||
BROWN_STAINED_GLASS,
|
||||
];
|
||||
industrial_windows[rng.gen_range(0..industrial_windows.len())]
|
||||
INDUSTRIAL_WINDOW_OPTIONS[rng.gen_range(0..INDUSTRIAL_WINDOW_OPTIONS.len())]
|
||||
}
|
||||
_ => WINDOW_VARIATIONS[rng.gen_range(0..WINDOW_VARIATIONS.len())],
|
||||
}
|
||||
}
|
||||
|
||||
// Random floor block selection
|
||||
// Floor block options for buildings
|
||||
pub static FLOOR_BLOCK_OPTIONS: [Block; 8] = [
|
||||
WHITE_CONCRETE,
|
||||
GRAY_CONCRETE,
|
||||
LIGHT_GRAY_CONCRETE,
|
||||
POLISHED_ANDESITE,
|
||||
SMOOTH_STONE,
|
||||
STONE_BRICKS,
|
||||
MUD_BRICKS,
|
||||
OAK_PLANKS,
|
||||
];
|
||||
|
||||
// Random floor block selection (non-deterministic, for backwards compatibility)
|
||||
pub fn get_random_floor_block() -> Block {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
FLOOR_BLOCK_OPTIONS[rng.gen_range(0..FLOOR_BLOCK_OPTIONS.len())]
|
||||
}
|
||||
|
||||
let floor_options = [
|
||||
WHITE_CONCRETE,
|
||||
GRAY_CONCRETE,
|
||||
LIGHT_GRAY_CONCRETE,
|
||||
POLISHED_ANDESITE,
|
||||
SMOOTH_STONE,
|
||||
STONE_BRICKS,
|
||||
MUD_BRICKS,
|
||||
OAK_PLANKS,
|
||||
];
|
||||
floor_options[rng.gen_range(0..floor_options.len())]
|
||||
/// Deterministic floor block selection using provided RNG
|
||||
pub fn get_floor_block_with_rng(rng: &mut impl rand::Rng) -> Block {
|
||||
FLOOR_BLOCK_OPTIONS[rng.gen_range(0..FLOOR_BLOCK_OPTIONS.len())]
|
||||
}
|
||||
|
||||
// Define all predefined colors with their blocks
|
||||
@@ -1073,7 +1268,6 @@ pub fn get_fallback_building_block() -> Block {
|
||||
STONE_BRICKS,
|
||||
WHITE_CONCRETE,
|
||||
WHITE_TERRACOTTA,
|
||||
OAK_PLANKS,
|
||||
];
|
||||
fallback_options[rng.gen_range(0..fallback_options.len())]
|
||||
}
|
||||
|
||||
@@ -187,6 +187,8 @@ pub fn generate_world_with_options(
|
||||
man_made::generate_man_made(&mut editor, &element, args);
|
||||
} else if way.tags.contains_key("power") {
|
||||
power::generate_power(&mut editor, &element);
|
||||
} else if way.tags.contains_key("place") {
|
||||
landuse::generate_place(&mut editor, way, args, &flood_fill_cache);
|
||||
}
|
||||
// Release flood fill cache entry for this way
|
||||
flood_fill_cache.remove_way(way.id);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -417,3 +417,30 @@ pub fn generate_landuse_from_relation(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates ground blocks for place=* areas (squares, neighbourhoods, etc.)
|
||||
pub fn generate_place(
|
||||
editor: &mut WorldEditor,
|
||||
element: &ProcessedWay,
|
||||
args: &Args,
|
||||
flood_fill_cache: &FloodFillCache,
|
||||
) {
|
||||
let binding = String::new();
|
||||
let place_tag = element.tags.get("place").unwrap_or(&binding);
|
||||
|
||||
// Determine block type based on place tag
|
||||
let block_type = match place_tag.as_str() {
|
||||
"square" => STONE_BRICKS,
|
||||
"neighbourhood" | "city_block" | "quarter" | "suburb" => SMOOTH_STONE,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Get the area using flood fill cache
|
||||
let floor_area: Vec<(i32, i32)> =
|
||||
flood_fill_cache.get_or_compute(element, args.timeout.as_ref());
|
||||
|
||||
// Place ground blocks
|
||||
for (x, z) in floor_area {
|
||||
editor.set_block(block_type, x, 0, z, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,33 @@ const BIRCH_LEAVES_FILL: [(Coord, Coord); 5] = [
|
||||
((0, 7, 0), (0, 8, 0)),
|
||||
];
|
||||
|
||||
/// Dark oak: short but wide canopy, leaves start at y=3 up to y=6 with a cap
|
||||
const DARK_OAK_LEAVES_FILL: [(Coord, Coord); 5] = [
|
||||
((-1, 3, 0), (-1, 6, 0)),
|
||||
((1, 3, 0), (1, 6, 0)),
|
||||
((0, 3, -1), (0, 6, -1)),
|
||||
((0, 3, 1), (0, 6, 1)),
|
||||
((0, 6, 0), (0, 7, 0)),
|
||||
];
|
||||
|
||||
/// Jungle: tall tree with canopy only near the top, leaves from y=7 to y=11
|
||||
const JUNGLE_LEAVES_FILL: [(Coord, Coord); 5] = [
|
||||
((-1, 7, 0), (-1, 11, 0)),
|
||||
((1, 7, 0), (1, 11, 0)),
|
||||
((0, 7, -1), (0, 11, -1)),
|
||||
((0, 7, 1), (0, 11, 1)),
|
||||
((0, 11, 0), (0, 12, 0)),
|
||||
];
|
||||
|
||||
/// Acacia: umbrella-shaped canopy with a gentle dome, leaves from y=5 to y=8
|
||||
const ACACIA_LEAVES_FILL: [(Coord, Coord); 5] = [
|
||||
((-1, 5, 0), (-1, 8, 0)),
|
||||
((1, 5, 0), (1, 8, 0)),
|
||||
((0, 5, -1), (0, 8, -1)),
|
||||
((0, 5, 1), (0, 8, 1)),
|
||||
((0, 8, 0), (0, 9, 0)),
|
||||
];
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/// Helper function to set blocks in various patterns.
|
||||
@@ -97,6 +124,9 @@ pub enum TreeType {
|
||||
Oak,
|
||||
Spruce,
|
||||
Birch,
|
||||
DarkOak,
|
||||
Jungle,
|
||||
Acacia,
|
||||
}
|
||||
|
||||
// TODO what should be moved in, and what should be referenced?
|
||||
@@ -126,10 +156,13 @@ impl Tree<'_> {
|
||||
// The element_id of 0 is used as a salt for tree-specific randomness
|
||||
let mut rng = coord_rng(x, z, 0);
|
||||
|
||||
let tree_type = match rng.gen_range(1..=3) {
|
||||
1 => TreeType::Oak,
|
||||
2 => TreeType::Spruce,
|
||||
3 => TreeType::Birch,
|
||||
let tree_type = match rng.gen_range(1..=10) {
|
||||
1..=3 => TreeType::Oak,
|
||||
4..=5 => TreeType::Spruce,
|
||||
6..=7 => TreeType::Birch,
|
||||
8 => TreeType::DarkOak,
|
||||
9 => TreeType::Jungle,
|
||||
10 => TreeType::Acacia,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -214,9 +247,9 @@ impl Tree<'_> {
|
||||
// kind,
|
||||
log_block: SPRUCE_LOG,
|
||||
log_height: 9,
|
||||
leaves_block: BIRCH_LEAVES, // TODO Is this correct?
|
||||
leaves_block: SPRUCE_LEAVES,
|
||||
leaves_fill: &SPRUCE_LEAVES_FILL,
|
||||
// TODO can I omit the third empty vec? May cause issues with iter zip
|
||||
// Conical shape: wide at bottom, narrow at top
|
||||
round_ranges: [vec![9, 7, 6, 4, 3], vec![6, 3], vec![]],
|
||||
},
|
||||
|
||||
@@ -228,6 +261,44 @@ impl Tree<'_> {
|
||||
leaves_fill: &BIRCH_LEAVES_FILL,
|
||||
round_ranges: [(2..=6).rev().collect(), (2..=4).collect(), vec![]],
|
||||
},
|
||||
|
||||
TreeType::DarkOak => Self {
|
||||
// Short trunk with a very wide, bushy canopy
|
||||
log_block: DARK_OAK_LOG,
|
||||
log_height: 5,
|
||||
leaves_block: DARK_OAK_LEAVES,
|
||||
leaves_fill: &DARK_OAK_LEAVES_FILL,
|
||||
// All 3 round patterns used for maximum width
|
||||
round_ranges: [
|
||||
(3..=6).rev().collect(),
|
||||
(3..=5).rev().collect(),
|
||||
(4..=5).rev().collect(),
|
||||
],
|
||||
},
|
||||
|
||||
TreeType::Jungle => Self {
|
||||
// Tall trunk, canopy clustered at the top
|
||||
log_block: JUNGLE_LOG,
|
||||
log_height: 10,
|
||||
leaves_block: JUNGLE_LEAVES,
|
||||
leaves_fill: &JUNGLE_LEAVES_FILL,
|
||||
// Canopy only near the top of the tree
|
||||
round_ranges: [(7..=11).rev().collect(), (8..=10).rev().collect(), vec![]],
|
||||
},
|
||||
|
||||
TreeType::Acacia => Self {
|
||||
// Medium trunk with umbrella-shaped canopy, domed center
|
||||
log_block: ACACIA_LOG,
|
||||
log_height: 6,
|
||||
leaves_block: ACACIA_LEAVES,
|
||||
leaves_fill: &ACACIA_LEAVES_FILL,
|
||||
// Inner rounds reach higher → gentle dome, outer stays low → wide brim
|
||||
round_ranges: [
|
||||
(5..=8).rev().collect(),
|
||||
(5..=7).rev().collect(),
|
||||
(6..=7).rev().collect(),
|
||||
],
|
||||
},
|
||||
} // match
|
||||
} // fn get_tree
|
||||
|
||||
@@ -363,6 +434,9 @@ impl Tree<'_> {
|
||||
GRAY_STAINED_GLASS,
|
||||
LIGHT_GRAY_STAINED_GLASS,
|
||||
BROWN_STAINED_GLASS,
|
||||
CYAN_STAINED_GLASS,
|
||||
BLUE_STAINED_GLASS,
|
||||
LIGHT_BLUE_STAINED_GLASS,
|
||||
TINTED_GLASS,
|
||||
// Carpets
|
||||
WHITE_CARPET,
|
||||
|
||||
@@ -146,6 +146,7 @@ pub fn fetch_data_from_overpass(
|
||||
nwr["advertising"];
|
||||
nwr["man_made"];
|
||||
nwr["aeroway"];
|
||||
way["place"];
|
||||
way;
|
||||
)->.relsinbbox;
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user