Merge pull request #742 from louis-e/buildings-improvement

Buildings improvement
This commit is contained in:
Louis Erbkamm
2026-02-08 19:43:17 +01:00
committed by GitHub
7 changed files with 4227 additions and 1335 deletions

View File

@@ -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::*;

View File

@@ -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())]
}

View File

@@ -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);

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -146,6 +146,7 @@ pub fn fetch_data_from_overpass(
nwr["advertising"];
nwr["man_made"];
nwr["aeroway"];
way["place"];
way;
)->.relsinbbox;
(