Files
cursebreaker-parser-rust/cursebreaker-map/static/resources.js
2026-01-16 09:33:30 +00:00

262 lines
7.9 KiB
JavaScript

// Resource management for Cursebreaker map
let resourceLayerGroups = {}; // Map: resource name -> L.layerGroup
let resourceIcons = {}; // Map: resource name -> L.icon
let resourceData = {}; // Map: resource name -> resource metadata (skill, level, etc.)
let filterState = {}; // Map: resource name -> boolean (visible)
// Load resources from API
async function loadResources() {
try {
console.log('Loading resources from API...');
const response = await fetch('/api/resources');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`Received ${data.resources.length} resource types`);
// Create icons and layer groups for each resource
for (const group of data.resources) {
createResourceGroup(group);
}
// Initialize filter UI
initializeFilterUI();
// Restore filter state from localStorage
restoreFilterState();
console.log(`Loaded ${data.resources.length} resource types successfully`);
} catch (error) {
console.error('Error loading resources:', error);
const container = document.getElementById('resource-filters');
if (container) {
container.innerHTML = '<p style="color: #ff6b6b;">Failed to load resources. Check console for details.</p>';
}
}
}
// Create resource group with icon and markers
function createResourceGroup(group) {
const config = window.MapConfig;
// Create icon definition (cached per resource type)
const iconUrl = `data:image/webp;base64,${group.icon_base64}`;
const icon = L.icon({
iconUrl: iconUrl,
iconSize: [config.resourceIconSize, config.resourceIconSize],
iconAnchor: [config.resourceIconSize / 2, config.resourceIconSize / 2],
popupAnchor: [0, -(config.resourceIconSize / 2)],
});
resourceIcons[group.name] = icon;
// Store metadata
resourceData[group.name] = {
item_id: group.item_id,
skill: group.skill,
level: group.level,
};
// Create layer group for this resource type
const layerGroup = L.layerGroup();
// Add markers for all positions
for (const pos of group.positions) {
const marker = L.marker([pos.y, pos.x], {
icon: icon,
title: group.name,
});
// Add popup with resource details
marker.bindPopup(
`<strong>${group.name}</strong><br/>Position: (${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`
);
marker.addTo(layerGroup);
}
// Add to map (initially visible)
layerGroup.addTo(map);
resourceLayerGroups[group.name] = layerGroup;
filterState[group.name] = true; // Initially visible
}
// Initialize filter UI with skill grouping
function initializeFilterUI() {
const container = document.getElementById('resource-filters');
if (!container) {
console.error('resource-filters container not found');
return;
}
container.innerHTML = ''; // Clear loading text
// Group resources by skill
const skillGroups = {};
for (const name in resourceLayerGroups) {
const metadata = resourceData[name];
if (!skillGroups[metadata.skill]) {
skillGroups[metadata.skill] = [];
}
skillGroups[metadata.skill].push({
name: name,
level: metadata.level,
});
}
// Sort skills alphabetically
const sortedSkills = Object.keys(skillGroups).sort();
// Create UI for each skill group
for (const skill of sortedSkills) {
const skillDiv = document.createElement('div');
skillDiv.className = 'skill-group';
const header = document.createElement('div');
header.className = 'skill-header';
// Capitalize first letter of skill
header.textContent = skill.charAt(0).toUpperCase() + skill.slice(1);
skillDiv.appendChild(header);
// Resources are already sorted by level in backend, but sort again to be sure
skillGroups[skill].sort((a, b) => a.level - b.level);
// Create checkbox for each resource
for (const resource of skillGroups[skill]) {
const label = createFilterLabel(resource.name);
skillDiv.appendChild(label);
}
container.appendChild(skillDiv);
}
// Attach bulk filter handlers
const selectAllBtn = document.getElementById('select-all-resources');
const deselectAllBtn = document.getElementById('deselect-all-resources');
if (selectAllBtn) {
selectAllBtn.addEventListener('click', () => {
setAllFilters(true);
});
}
if (deselectAllBtn) {
deselectAllBtn.addEventListener('click', () => {
setAllFilters(false);
});
}
}
// Create filter label with checkbox and icon
function createFilterLabel(resourceName) {
const label = document.createElement('label');
label.className = 'filter-label';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = filterState[resourceName];
checkbox.dataset.resource = resourceName;
checkbox.addEventListener('change', handleFilterChange);
const icon = document.createElement('img');
icon.src = resourceIcons[resourceName].options.iconUrl;
icon.className = 'filter-icon';
icon.alt = resourceName;
const text = document.createElement('span');
text.textContent = resourceName;
label.appendChild(checkbox);
label.appendChild(icon);
label.appendChild(text);
return label;
}
// Handle filter checkbox change
function handleFilterChange(event) {
const resourceName = event.target.dataset.resource;
const isVisible = event.target.checked;
filterState[resourceName] = isVisible;
// Show/hide layer group
const layerGroup = resourceLayerGroups[resourceName];
if (isVisible) {
layerGroup.addTo(map);
} else {
map.removeLayer(layerGroup);
}
// Persist state
saveFilterState();
}
// Set all filters to visible or hidden
function setAllFilters(visible) {
for (const name in filterState) {
filterState[name] = visible;
const layerGroup = resourceLayerGroups[name];
if (visible) {
layerGroup.addTo(map);
} else {
map.removeLayer(layerGroup);
}
}
// Update checkboxes
document.querySelectorAll('#resource-filters input[type="checkbox"]').forEach((cb) => {
cb.checked = visible;
});
saveFilterState();
}
// Save filter state to localStorage
function saveFilterState() {
try {
localStorage.setItem('cursebreaker_resource_filters', JSON.stringify(filterState));
} catch (error) {
console.warn('Failed to save filter state to localStorage:', error);
}
}
// Restore filter state from localStorage
function restoreFilterState() {
const saved = localStorage.getItem('cursebreaker_resource_filters');
if (!saved) return;
try {
const savedState = JSON.parse(saved);
for (const name in savedState) {
if (resourceLayerGroups[name]) {
filterState[name] = savedState[name];
const layerGroup = resourceLayerGroups[name];
if (!savedState[name]) {
map.removeLayer(layerGroup);
}
}
}
// Update checkboxes after UI is created
setTimeout(() => {
document.querySelectorAll('#resource-filters input[type="checkbox"]').forEach((cb) => {
const name = cb.dataset.resource;
if (filterState[name] !== undefined) {
cb.checked = filterState[name];
}
});
}, 100);
console.log('Restored filter state from localStorage');
} catch (error) {
console.warn('Failed to restore filter state:', error);
}
}