Compare commits

...

10 Commits

Author SHA1 Message Date
louis-e
7c808ec352 Address code review feedback 2026-01-10 18:36:43 +01:00
louis-e
b757c5acf4 Better icons and map overlay fix 2026-01-10 17:56:37 +01:00
louis-e
ced5fc274e More landuse variations 2026-01-10 17:27:10 +01:00
Louis Erbkamm
295ca415d7 Merge pull request #660 from louis-e/copilot/fix-spawn-point-y-coordinate
Fix spawn point Y coordinate update for longitude values outside ±90°
2026-01-10 13:20:46 +01:00
Louis Erbkamm
e2b4ca8bdb Merge branch 'main' into copilot/fix-spawn-point-y-coordinate 2026-01-10 13:15:04 +01:00
Louis Erbkamm
07105f0208 Merge pull request #703 from louis-e/no-internet-warning
Send proper error for no internet
2026-01-08 23:36:36 +01:00
copilot-swe-agent[bot]
0a51b302ee Simplify bbox format comment
Co-authored-by: louis-e <44675238+louis-e@users.noreply.github.com>
2025-12-07 14:22:50 +00:00
copilot-swe-agent[bot]
93dc9f446c Improve comment explaining bbox format conversion
Co-authored-by: louis-e <44675238+louis-e@users.noreply.github.com>
2025-12-07 14:17:56 +00:00
copilot-swe-agent[bot]
e6430f2a04 Fix spawn point Y coordinate bbox format
Co-authored-by: louis-e <44675238+louis-e@users.noreply.github.com>
2025-12-07 14:11:36 +00:00
copilot-swe-agent[bot]
5962decf44 Initial plan 2025-12-07 13:58:19 +00:00
8 changed files with 114 additions and 21 deletions

View File

@@ -357,12 +357,14 @@ pub fn generate_world_with_options(
if world_format == WorldFormat::JavaAnvil {
if let Some(spawn_coords) = &args.spawn_point {
use crate::gui::update_player_spawn_y_after_generation;
// Reconstruct bbox string to match the format that GUI originally provided.
// This ensures LLBBox::from_str() can parse it correctly.
let bbox_string = format!(
"{},{},{},{}",
args.bbox.min().lng(),
args.bbox.min().lat(),
args.bbox.max().lng(),
args.bbox.max().lat()
args.bbox.min().lng(),
args.bbox.max().lat(),
args.bbox.max().lng()
);
if let Err(e) = update_player_spawn_y_after_generation(

View File

@@ -17,6 +17,9 @@ pub fn generate_landuse(
let binding: String = "".to_string();
let landuse_tag: &String = element.tags.get("landuse").unwrap_or(&binding);
// Use deterministic RNG seeded by element ID for consistent results across region boundaries
let mut rng = element_rng(element.id);
let block_type = match landuse_tag.as_str() {
"greenfield" | "meadow" | "grass" | "orchard" | "forest" => GRASS_BLOCK,
"farmland" => FARMLAND,
@@ -28,10 +31,10 @@ pub fn generate_landuse(
if residential_tag == "rural" {
GRASS_BLOCK
} else {
STONE_BRICKS
STONE_BRICKS // Placeholder, will be randomized per-block
}
}
"commercial" => SMOOTH_STONE,
"commercial" => SMOOTH_STONE, // Placeholder, will be randomized per-block
"education" => POLISHED_ANDESITE,
"religious" => POLISHED_ANDESITE,
"industrial" => COBBLESTONE,
@@ -54,16 +57,42 @@ pub fn generate_landuse(
let floor_area: Vec<(i32, i32)> =
flood_fill_cache.get_or_compute(element, args.timeout.as_ref());
// Use deterministic RNG seeded by element ID for consistent results across region boundaries
let mut rng = element_rng(element.id);
for (x, z) in floor_area {
if landuse_tag == "traffic_island" {
editor.set_block(block_type, x, 1, z, None, None);
} else if landuse_tag == "construction" || landuse_tag == "railway" {
editor.set_block(block_type, x, 0, z, None, Some(&[SPONGE]));
// Apply per-block randomness for certain landuse types
let actual_block = if landuse_tag == "residential" && block_type == STONE_BRICKS {
// Urban residential: mix of stone bricks, cracked stone bricks, stone, cobblestone
let random_value = rng.gen_range(0..100);
if random_value < 72 {
STONE_BRICKS
} else if random_value < 87 {
CRACKED_STONE_BRICKS
} else if random_value < 92 {
STONE
} else {
COBBLESTONE
}
} else if landuse_tag == "commercial" {
// Commercial: mix of smooth stone, stone, cobblestone, stone bricks
let random_value = rng.gen_range(0..100);
if random_value < 40 {
SMOOTH_STONE
} else if random_value < 70 {
STONE_BRICKS
} else if random_value < 90 {
STONE
} else {
COBBLESTONE
}
} else {
editor.set_block(block_type, x, 0, z, None, None);
block_type
};
if landuse_tag == "traffic_island" {
editor.set_block(actual_block, x, 1, z, None, None);
} else if landuse_tag == "construction" || landuse_tag == "railway" {
editor.set_block(actual_block, x, 0, z, None, Some(&[SPONGE]));
} else {
editor.set_block(actual_block, x, 0, z, None, None);
}
// Add specific features for different landuse types

View File

@@ -351,7 +351,8 @@ body,
background-position: -31px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-preview.disabled {
.leaflet-draw-toolbar .leaflet-draw-edit-preview.disabled,
.leaflet-draw-toolbar .leaflet-draw-edit-preview.editing-mode {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;

View File

@@ -287,7 +287,7 @@ button:hover {
/* Customization Settings */
.modal {
position: fixed;
z-index: 1000;
z-index: 20001;
left: 0;
top: 0;
width: 100%;
@@ -606,9 +606,12 @@ button:hover {
transition: background-color 0.3s, border-color 0.3s;
}
.settings-button .gear-icon::before {
content: "⚙️";
font-size: 18px;
.settings-button svg {
stroke: white;
width: 22px;
height: 22px;
min-width: 22px;
min-height: 22px;
}
/* Logo Animation */

4
src/gui/index.html vendored
View File

@@ -61,8 +61,8 @@
<div class="button-container">
<button type="button" id="start-button" class="start-button" onclick="startGeneration()" data-localize="start_generation">Start Generation</button>
<button type="button" class="settings-button" onclick="openSettings()">
<i class="gear-icon"></i>
<button type="button" class="settings-button" onclick="openSettings()" aria-label="Settings">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path><circle cx="12" cy="12" r="3"></circle></svg>
</button>
</div>
<br><br>

44
src/gui/js/bbox.js vendored
View File

@@ -564,6 +564,7 @@ $(document).ready(function () {
var worldOverlayEnabled = false;
var worldPreviewAvailable = false;
var sliderControl = null;
var worldOverlayHiddenForEdit = false; // Track if we hid the overlay for edit/delete mode
// Create the opacity slider as a proper Leaflet control
var SliderControl = L.Control.extend({
@@ -722,6 +723,32 @@ $(document).ready(function () {
}
}
// Temporarily hide the overlay (for edit/delete mode)
function hideWorldOverlayTemporarily() {
if (worldOverlay && worldOverlayEnabled) {
worldOverlayHiddenForEdit = true;
map.removeLayer(worldOverlay);
}
// Also visually disable the preview button during edit/delete mode
var btn = document.getElementById('world-preview-btn');
if (btn) {
btn.classList.add('editing-mode');
}
}
// Restore the overlay after edit/delete mode ends
function restoreWorldOverlay() {
if (worldOverlayHiddenForEdit && worldOverlay && worldOverlayEnabled) {
worldOverlay.addTo(map);
worldOverlayHiddenForEdit = false;
}
// Re-enable the preview button
var btn = document.getElementById('world-preview-btn');
if (btn) {
btn.classList.remove('editing-mode');
}
}
// Listen for messages from parent window
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'changeTileTheme') {
@@ -999,6 +1026,23 @@ $(document).ready(function () {
map.fitBounds(bounds.getBounds());
});
// Hide world preview overlay when entering edit or delete mode
map.on('draw:editstart', function() {
hideWorldOverlayTemporarily();
});
map.on('draw:deletestart', function() {
hideWorldOverlayTemporarily();
});
// Restore world preview overlay when exiting edit or delete mode
map.on('draw:editstop', function() {
restoreWorldOverlay();
});
map.on('draw:deletestop', function() {
restoreWorldOverlay();
});
function display() {
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));

14
src/gui/js/main.js vendored
View File

@@ -250,6 +250,20 @@ function initSettings() {
settingsModal.style.display = "none";
}
// Close settings and license modals on escape key
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
if (settingsModal.style.display === "flex") {
closeSettings();
}
const licenseModal = document.getElementById("license-modal");
if (licenseModal && licenseModal.style.display === "flex") {
closeLicense();
}
}
});
window.openSettings = openSettings;
window.closeSettings = closeSettings;

2
src/gui/maps.html vendored
View File

@@ -26,7 +26,7 @@
<div id="search-container">
<div id="search-box">
<input type="text" id="city-search" placeholder="Search for a city..." autocomplete="off" />
<button id="search-btn">🔍</button>
<button id="search-btn" aria-label="Search"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.3-4.3"></path></svg></button>
</div>
<div id="search-results"></div>
</div>