144 lines
6.4 KiB
HTML
144 lines
6.4 KiB
HTML
// Helper function to create location shape based on type
|
|
function createLocationShape(locationId, loc) {
|
|
const coords = loc.coordinates;
|
|
const centerLatLng = [coords.x, coords.z]; // 7D2D coordinates (x, z)
|
|
const dims = loc.dimensions || {};
|
|
const shape = loc.shape || 'circle';
|
|
const isEnabled = loc.is_enabled;
|
|
const is3D = (shape === 'box' || shape === 'spherical');
|
|
|
|
// Color scheme
|
|
const fillColor = isEnabled ? '#ff9900' : '#666666';
|
|
const strokeColor = isEnabled ? '#ffcc00' : '#999999';
|
|
const fillOpacity = isEnabled ? 0.3 : 0.15;
|
|
|
|
let leafletShape;
|
|
|
|
if (shape === 'circle') {
|
|
const radius = parseFloat(dims.radius || 10);
|
|
leafletShape = L.circle(centerLatLng, {
|
|
radius: radius,
|
|
fillColor: fillColor,
|
|
color: strokeColor,
|
|
weight: 2,
|
|
opacity: 0.8,
|
|
fillOpacity: fillOpacity
|
|
});
|
|
} else if (shape === 'spherical') {
|
|
const radius = parseFloat(dims.radius || 10);
|
|
leafletShape = L.circle(centerLatLng, {
|
|
radius: radius,
|
|
fillColor: fillColor,
|
|
color: strokeColor,
|
|
weight: 2,
|
|
opacity: 0.8,
|
|
fillOpacity: fillOpacity,
|
|
dashArray: '5, 5' // Dashed to indicate 3D
|
|
});
|
|
} else if (shape === 'rectangular') {
|
|
const width = parseFloat(dims.width || 10);
|
|
const length = parseFloat(dims.length || 10);
|
|
// Rectangle bounds: from center, extend width/length in both directions
|
|
const bounds = [
|
|
[coords.x - width, coords.z - length],
|
|
[coords.x + width, coords.z + length]
|
|
];
|
|
leafletShape = L.rectangle(bounds, {
|
|
fillColor: fillColor,
|
|
color: strokeColor,
|
|
weight: 2,
|
|
opacity: 0.8,
|
|
fillOpacity: fillOpacity
|
|
});
|
|
} else if (shape === 'box') {
|
|
const width = parseFloat(dims.width || 10);
|
|
const length = parseFloat(dims.length || 10);
|
|
const bounds = [
|
|
[coords.x - width, coords.z - length],
|
|
[coords.x + width, coords.z + length]
|
|
];
|
|
leafletShape = L.rectangle(bounds, {
|
|
fillColor: fillColor,
|
|
color: strokeColor,
|
|
weight: 2,
|
|
opacity: 0.8,
|
|
fillOpacity: fillOpacity,
|
|
dashArray: '5, 5' // Dashed to indicate 3D
|
|
});
|
|
} else {
|
|
// Fallback to circle
|
|
leafletShape = L.circle(centerLatLng, {
|
|
radius: 10,
|
|
fillColor: fillColor,
|
|
color: strokeColor,
|
|
weight: 2,
|
|
opacity: 0.8,
|
|
fillOpacity: fillOpacity
|
|
});
|
|
}
|
|
|
|
// Build popup content
|
|
const dimensionText = shape === 'circle' || shape === 'spherical'
|
|
? `Radius: ${dims.radius || 'N/A'}`
|
|
: `Width: ${dims.width || 'N/A'}, Length: ${dims.length || 'N/A'}${shape === 'box' ? ', Height: ' + (dims.height || 'N/A') : ''}`;
|
|
|
|
// Parse locationId to extract components
|
|
// Format: {dataset}_{owner}_{identifier}
|
|
const locationIdParts = locationId.split('_');
|
|
const dataset = locationIdParts.slice(0, -2).join('_'); // Handle datasets with underscores
|
|
const owner = locationIdParts[locationIdParts.length - 2];
|
|
const identifier = locationIdParts[locationIdParts.length - 1];
|
|
|
|
const teleportEntry = loc.teleport_entry || {};
|
|
const hasTeleport = teleportEntry.x !== undefined && teleportEntry.y !== undefined && teleportEntry.z !== undefined;
|
|
const teleportText = hasTeleport
|
|
? `TP: ${parseFloat(teleportEntry.x || 0).toFixed(0)}, ${parseFloat(teleportEntry.y || 0).toFixed(0)}, ${parseFloat(teleportEntry.z || 0).toFixed(0)}`
|
|
: 'TP: Not set';
|
|
|
|
// Use template literal for clean HTML
|
|
const popupContent = `
|
|
<div style="min-width: 250px; font-family: monospace;">
|
|
<b style="font-size: 1.1em;">${loc.name}</b>
|
|
<br><span style="font-size: 0.9em; color: #888;">${is3D ? '🎲 3D' : '⬜ 2D'} - ${shape}</span>
|
|
<br><hr style="margin: 5px 0; border-color: #333;">
|
|
<b>Type:</b> ${loc.type && loc.type.length > 0 ? loc.type.join(', ') : 'None'}
|
|
<br><b>Owner:</b> ${loc.owner}
|
|
<br><b>Status:</b> ${isEnabled ? '✅ Enabled' : '❌ Disabled'}
|
|
<br><b>Position:</b> ${coords.x.toFixed(0)}, ${coords.y.toFixed(0)}, ${coords.z.toFixed(0)}
|
|
<br><b>Dimensions:</b> ${dimensionText}
|
|
<br><b>${teleportText}</b>
|
|
<br><hr style="margin: 8px 0; border-color: #333;">
|
|
<div style="display: flex; gap: 5px; justify-content: space-between; align-items: center; margin-bottom: 5px;">
|
|
<button onclick="editLocationFromMap('${dataset}', '${owner}', '${identifier}')"
|
|
style="flex: 1; padding: 6px 12px; background: var(--lcars-hopbush); color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
|
✏️ Edit</button>
|
|
<label style="display: flex; align-items: center; gap: 5px; cursor: pointer; white-space: nowrap;">
|
|
<input type="checkbox"
|
|
id="enable_${locationId}"
|
|
${isEnabled ? 'checked' : ''}
|
|
onchange="toggleLocationEnabled('${dataset}', '${owner}', '${identifier}', this.checked)"
|
|
style="cursor: pointer; width: 18px; height: 18px;" />
|
|
<span style="font-weight: bold;">Enabled</span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; gap: 5px;">
|
|
<button onclick="moveLocationFromMap('${locationId}')"
|
|
style="flex: 1; padding: 6px 10px; background: var(--lcars-anakiwa); color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9em;">
|
|
📍 Move</button>
|
|
<button onclick="setTeleportFromMap('${locationId}')"
|
|
style="flex: 1; padding: 6px 10px; background: var(--lcars-golden-tanoi); color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9em;">
|
|
🎯 Set TP</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
leafletShape.bindPopup(popupContent);
|
|
leafletShape.addTo(map);
|
|
|
|
return leafletShape;
|
|
}
|
|
|
|
// Location shapes are now loaded dynamically via Socket.IO
|
|
// Initial loading is handled by location_update events
|
|
// See location_update_handler.html for shape creation logic
|