Dragon-Knight/assets/scripts/spell-search.js

141 lines
3.5 KiB
JavaScript

// All available spells data (loaded from API)
let allSpells = [];
const searchInput = document.getElementById('spell-search');
const resultsDiv = document.getElementById('spell-results');
const selectedDiv = document.getElementById('selected-spell');
const spellIdInput = document.getElementById('spell-id');
let currentResults = [];
// Load spells data from API
async function loadSpells() {
try {
const response = await fetch('/admin/api/spells');
if (response.ok) {
allSpells = await response.json();
} else {
console.error('Failed to load spells');
}
} catch (error) {
console.error('Error loading spells:', error);
}
}
// Initialize data when DOM is loaded
document.addEventListener('DOMContentLoaded', loadSpells);
// Simple fuzzy search function
function fuzzyMatch(search, target) {
search = search.toLowerCase();
target = target.toLowerCase();
// Exact match gets highest score
if (target.includes(search)) {
return 1000 - target.indexOf(search);
}
// Character-by-character fuzzy matching
let score = 0;
let searchIndex = 0;
for (let i = 0; i < target.length && searchIndex < search.length; i++) {
if (target[i] === search[searchIndex]) {
score += 10;
searchIndex++;
}
}
// Only return score if we matched all search characters
return searchIndex === search.length ? score : 0;
}
function getSpellTypeName(type) {
switch (type) {
case 0:
return 'Heal';
case 1:
return 'Damage';
case 2:
return 'Sleep';
case 3:
return 'Uber Attack';
case 4:
return 'Uber Defense';
default:
return 'Unknown';
}
}
function searchSpells() {
const query = searchInput.value.trim();
if (query.length < 2) {
resultsDiv.style.display = 'none';
return;
}
// Search and score results
const scored = allSpells.map(spell => ({
...spell,
score: fuzzyMatch(query, spell.Name)
})).filter(spell => spell.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, 8); // Limit to 8 results
currentResults = scored;
if (scored.length === 0) {
resultsDiv.innerHTML = '<div style="padding: 0.5rem; color: #666;">No spells found</div>';
} else {
resultsDiv.innerHTML = scored.map((spell, index) =>
`<div class="spell-result" data-index="${index}" style="padding: 0.5rem; cursor: pointer; border-bottom: 1px solid #eee;">
<strong>${spell.Name}</strong> - ${getSpellTypeName(spell.Type)}
<span style="color: #666;">(${spell.MP} MP, ${spell.Power} Power)</span>
</div>`
).join('');
}
resultsDiv.style.display = 'block';
}
function selectSpell(spell) {
selectedDiv.innerHTML = `
<strong>${spell.Name}</strong> - ${getSpellTypeName(spell.Type)}<br>
<small>MP Cost: ${spell.MP} | Power: ${spell.Power}</small>
${spell.Lore ? `<br><em>${spell.Lore}</em>` : ''}
`;
spellIdInput.value = spell.ID;
searchInput.value = spell.Name;
resultsDiv.style.display = 'none';
}
// Event listeners
searchInput.addEventListener('input', searchSpells);
searchInput.addEventListener('focus', () => {
if (currentResults.length > 0) {
resultsDiv.style.display = 'block';
}
});
document.addEventListener('click', (e) => {
if (!searchInput.contains(e.target) && !resultsDiv.contains(e.target)) {
resultsDiv.style.display = 'none';
}
});
resultsDiv.addEventListener('click', (e) => {
const resultEl = e.target.closest('.spell-result');
if (resultEl) {
const index = parseInt(resultEl.dataset.index);
selectSpell(currentResults[index]);
}
});
// Keyboard navigation
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
resultsDiv.style.display = 'none';
}
});