added inventory, updated DB
This commit is contained in:
parent
f218350959
commit
10c51f6825
16528 changed files with 147743 additions and 79 deletions
405
static/script.js
405
static/script.js
|
|
@ -460,11 +460,182 @@ function showInventoryWindow(name) {
|
|||
header.appendChild(title);
|
||||
header.appendChild(closeBtn);
|
||||
win.appendChild(header);
|
||||
// Loading message
|
||||
const loading = document.createElement('div');
|
||||
loading.className = 'inventory-loading';
|
||||
loading.textContent = 'Loading inventory...';
|
||||
win.appendChild(loading);
|
||||
|
||||
// Content container
|
||||
const content = document.createElement('div');
|
||||
content.className = 'inventory-content';
|
||||
content.innerHTML = '<div class="inventory-placeholder">Inventory feature coming soon...</div>';
|
||||
content.style.display = 'none';
|
||||
win.appendChild(content);
|
||||
|
||||
// Fetch inventory data from main app (which will proxy to inventory service)
|
||||
fetch(`${API_BASE}/inventory/${encodeURIComponent(name)}?limit=1000`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
loading.style.display = 'none';
|
||||
content.style.display = 'block';
|
||||
|
||||
// Create inventory grid
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'inventory-grid';
|
||||
|
||||
// Render each item
|
||||
data.items.forEach(item => {
|
||||
|
||||
const slot = document.createElement('div');
|
||||
slot.className = 'inventory-slot';
|
||||
|
||||
// Create layered icon container
|
||||
const iconContainer = document.createElement('div');
|
||||
iconContainer.className = 'item-icon-composite';
|
||||
|
||||
// Get base icon ID with portal.dat offset
|
||||
const baseIconId = (item.icon + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
|
||||
// Check for overlay and underlay from enhanced format or legacy format
|
||||
let overlayIconId = null;
|
||||
let underlayIconId = null;
|
||||
|
||||
// Enhanced format (inventory service) - check for proper icon overlay/underlay properties
|
||||
if (item.icon_overlay_id && item.icon_overlay_id > 0) {
|
||||
overlayIconId = (item.icon_overlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
|
||||
if (item.icon_underlay_id && item.icon_underlay_id > 0) {
|
||||
underlayIconId = (item.icon_underlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
|
||||
// Fallback: Enhanced format (inventory service) - check spells object for decal info
|
||||
if (!overlayIconId && !underlayIconId && item.spells && typeof item.spells === 'object') {
|
||||
// Icon overlay (using the actual property names from the data)
|
||||
// Only use valid icon IDs (must be > 100 to avoid invalid small IDs)
|
||||
if (item.spells.spell_decal_218103838 && item.spells.spell_decal_218103838 > 100) {
|
||||
overlayIconId = (item.spells.spell_decal_218103838 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
|
||||
// Icon underlay
|
||||
if (item.spells.spell_decal_218103848 && item.spells.spell_decal_218103848 > 100) {
|
||||
underlayIconId = (item.spells.spell_decal_218103848 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
} else if (item.item_data) {
|
||||
// Legacy format - parse item_data
|
||||
try {
|
||||
const itemData = typeof item.item_data === 'string' ? JSON.parse(item.item_data) : item.item_data;
|
||||
|
||||
if (itemData.IntValues) {
|
||||
// Icon overlay (ID 218103849) - only use valid icon IDs
|
||||
if (itemData.IntValues['218103849'] && itemData.IntValues['218103849'] > 100) {
|
||||
overlayIconId = (itemData.IntValues['218103849'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
|
||||
// Icon underlay (ID 218103850) - only use valid icon IDs
|
||||
if (itemData.IntValues['218103850'] && itemData.IntValues['218103850'] > 100) {
|
||||
underlayIconId = (itemData.IntValues['218103850'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse item data for', item.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Create underlay (bottom layer)
|
||||
if (underlayIconId) {
|
||||
const underlayImg = document.createElement('img');
|
||||
underlayImg.className = 'icon-underlay';
|
||||
underlayImg.src = `/icons/${underlayIconId}.png`;
|
||||
underlayImg.alt = 'underlay';
|
||||
underlayImg.onerror = function() { this.style.display = 'none'; };
|
||||
iconContainer.appendChild(underlayImg);
|
||||
}
|
||||
|
||||
// Create base icon (middle layer)
|
||||
const baseImg = document.createElement('img');
|
||||
baseImg.className = 'icon-base';
|
||||
baseImg.src = `/icons/${baseIconId}.png`;
|
||||
baseImg.alt = item.name || 'Unknown Item';
|
||||
baseImg.onerror = function() {
|
||||
// Final fallback
|
||||
this.src = '/icons/06000133.png';
|
||||
};
|
||||
iconContainer.appendChild(baseImg);
|
||||
|
||||
// Create overlay (top layer)
|
||||
if (overlayIconId) {
|
||||
const overlayImg = document.createElement('img');
|
||||
overlayImg.className = 'icon-overlay';
|
||||
overlayImg.src = `/icons/${overlayIconId}.png`;
|
||||
overlayImg.alt = 'overlay';
|
||||
overlayImg.onerror = function() { this.style.display = 'none'; };
|
||||
iconContainer.appendChild(overlayImg);
|
||||
}
|
||||
|
||||
// Create tooltip data
|
||||
slot.dataset.name = item.name || 'Unknown Item';
|
||||
slot.dataset.value = item.value || 0;
|
||||
slot.dataset.burden = item.burden || 0;
|
||||
|
||||
// Store enhanced data for tooltips
|
||||
// All data now comes from inventory service (no more local fallback)
|
||||
if (item.max_damage !== undefined || item.object_class_name !== undefined || item.spells !== undefined) {
|
||||
// Inventory service provides clean, structured data with translations
|
||||
// Only include properties that actually exist on the item
|
||||
const enhancedData = {};
|
||||
|
||||
// Check all possible enhanced properties from inventory service
|
||||
const possibleProps = [
|
||||
'max_damage', 'armor_level', 'damage_bonus', 'attack_bonus',
|
||||
'wield_level', 'skill_level', 'lore_requirement', 'equip_skill', 'equip_skill_name',
|
||||
'material', 'material_name', 'material_id', 'imbue', 'item_set', 'tinks',
|
||||
'workmanship', 'workmanship_text', 'damage_rating', 'crit_rating',
|
||||
'heal_boost_rating', 'has_id_data', 'object_class_name', 'spells',
|
||||
'enhanced_properties', 'damage_range', 'damage_type', 'min_damage',
|
||||
'speed_text', 'speed_value', 'mana_display', 'spellcraft', 'current_mana', 'max_mana',
|
||||
'melee_defense_bonus', 'magic_defense_bonus', 'missile_defense_bonus',
|
||||
'elemental_damage_vs_monsters', 'mana_conversion_bonus', 'icon_overlay_id', 'icon_underlay_id'
|
||||
];
|
||||
|
||||
// Only add properties that exist and have meaningful values
|
||||
possibleProps.forEach(prop => {
|
||||
if (item.hasOwnProperty(prop) && item[prop] !== undefined && item[prop] !== null) {
|
||||
enhancedData[prop] = item[prop];
|
||||
}
|
||||
});
|
||||
|
||||
slot.dataset.enhancedData = JSON.stringify(enhancedData);
|
||||
} else {
|
||||
// No enhanced data available
|
||||
slot.dataset.enhancedData = JSON.stringify({});
|
||||
}
|
||||
|
||||
// Add tooltip on hover
|
||||
slot.addEventListener('mouseenter', e => showInventoryTooltip(e, slot));
|
||||
slot.addEventListener('mousemove', e => showInventoryTooltip(e, slot));
|
||||
slot.addEventListener('mouseleave', hideInventoryTooltip);
|
||||
|
||||
slot.appendChild(iconContainer);
|
||||
grid.appendChild(slot);
|
||||
});
|
||||
|
||||
content.appendChild(grid);
|
||||
|
||||
// Add item count
|
||||
const count = document.createElement('div');
|
||||
count.className = 'inventory-count';
|
||||
count.textContent = `${data.item_count} items`;
|
||||
content.appendChild(count);
|
||||
})
|
||||
.catch(err => {
|
||||
loading.textContent = `Failed to load inventory: ${err.message}`;
|
||||
console.error('Inventory fetch failed:', err);
|
||||
});
|
||||
|
||||
document.body.appendChild(win);
|
||||
inventoryWindows[name] = win;
|
||||
|
||||
|
|
@ -472,6 +643,238 @@ function showInventoryWindow(name) {
|
|||
makeDraggable(win, header);
|
||||
}
|
||||
|
||||
// Inventory tooltip functions
|
||||
let inventoryTooltip = null;
|
||||
|
||||
function showInventoryTooltip(e, slot) {
|
||||
if (!inventoryTooltip) {
|
||||
inventoryTooltip = document.createElement('div');
|
||||
inventoryTooltip.className = 'inventory-tooltip';
|
||||
document.body.appendChild(inventoryTooltip);
|
||||
}
|
||||
|
||||
const name = slot.dataset.name;
|
||||
const value = parseInt(slot.dataset.value) || 0;
|
||||
const burden = parseInt(slot.dataset.burden) || 0;
|
||||
|
||||
// Build enhanced tooltip
|
||||
let tooltipHTML = `<div class="tooltip-name">${name}</div>`;
|
||||
|
||||
// Basic stats
|
||||
tooltipHTML += `<div class="tooltip-section">`;
|
||||
tooltipHTML += `<div class="tooltip-value">Value: ${value.toLocaleString()}</div>`;
|
||||
tooltipHTML += `<div class="tooltip-burden">Burden: ${burden}</div>`;
|
||||
|
||||
// Add workmanship right after basic stats for weapons/items
|
||||
if (slot.dataset.enhancedData) {
|
||||
try {
|
||||
const enhanced = JSON.parse(slot.dataset.enhancedData);
|
||||
if (enhanced.workmanship_text) {
|
||||
tooltipHTML += `<div class="tooltip-workmanship">Workmanship: ${enhanced.workmanship_text}</div>`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore parsing errors for this section
|
||||
}
|
||||
}
|
||||
|
||||
tooltipHTML += `</div>`;
|
||||
|
||||
// Enhanced data from inventory service
|
||||
if (slot.dataset.enhancedData) {
|
||||
try {
|
||||
const enhanced = JSON.parse(slot.dataset.enhancedData);
|
||||
|
||||
// Only show enhanced sections if we have enhanced data
|
||||
if (Object.keys(enhanced).length > 0) {
|
||||
|
||||
// Helper function to check for valid values
|
||||
const isValid = (val) => val !== undefined && val !== null && val !== -1 && val !== -1.0;
|
||||
|
||||
// Weapon-specific stats section (for weapons)
|
||||
if (enhanced.damage_range || enhanced.speed_text || enhanced.equip_skill_name) {
|
||||
tooltipHTML += `<div class="tooltip-section">`;
|
||||
|
||||
// Skill requirement
|
||||
if (enhanced.equip_skill_name) {
|
||||
tooltipHTML += `<div class="tooltip-property">Skill: ${enhanced.equip_skill_name}</div>`;
|
||||
}
|
||||
|
||||
// Damage with type
|
||||
if (enhanced.damage_range && enhanced.damage_type) {
|
||||
const damageText = enhanced.damage_type !== 'Physical' ?
|
||||
`${enhanced.damage_range}, ${enhanced.damage_type}` :
|
||||
enhanced.damage_range;
|
||||
tooltipHTML += `<div class="tooltip-property">Damage: ${damageText}</div>`;
|
||||
}
|
||||
|
||||
// Speed
|
||||
if (enhanced.speed_text) {
|
||||
tooltipHTML += `<div class="tooltip-property">Speed: ${enhanced.speed_text}</div>`;
|
||||
}
|
||||
|
||||
// Attack and defense bonuses (as percentages)
|
||||
if (isValid(enhanced.attack_bonus)) {
|
||||
const attackPercent = ((enhanced.attack_bonus - 1) * 100).toFixed(1);
|
||||
if (attackPercent !== "0.0") {
|
||||
tooltipHTML += `<div class="tooltip-property">Bonus to Attack Skill: ${attackPercent > 0 ? '+' : ''}${attackPercent}%</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Defense bonuses
|
||||
if (enhanced.melee_defense_bonus && isValid(enhanced.melee_defense_bonus)) {
|
||||
const defensePercent = ((enhanced.melee_defense_bonus - 1) * 100).toFixed(1);
|
||||
if (defensePercent !== "0.0") {
|
||||
tooltipHTML += `<div class="tooltip-property">Bonus to Melee Defense: ${defensePercent > 0 ? '+' : ''}${defensePercent}%</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Magic defense bonus
|
||||
if (enhanced.magic_defense_bonus && isValid(enhanced.magic_defense_bonus)) {
|
||||
const magicDefensePercent = ((enhanced.magic_defense_bonus - 1) * 100).toFixed(1);
|
||||
if (magicDefensePercent !== "0.0") {
|
||||
tooltipHTML += `<div class="tooltip-property">Bonus to Magic Defense: ${magicDefensePercent > 0 ? '+' : ''}${magicDefensePercent}%</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Elemental damage vs monsters
|
||||
if (enhanced.elemental_damage_vs_monsters && isValid(enhanced.elemental_damage_vs_monsters)) {
|
||||
const elementalPercent = ((enhanced.elemental_damage_vs_monsters - 1) * 100).toFixed(1);
|
||||
if (elementalPercent !== "0.0") {
|
||||
tooltipHTML += `<div class="tooltip-property">Elemental Damage vs Monsters: ${elementalPercent > 0 ? '+' : ''}${elementalPercent}%</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Traditional combat stats section (for non-weapons or additional stats)
|
||||
const combatProps = [];
|
||||
if (isValid(enhanced.armor_level)) combatProps.push(`Armor Level: ${enhanced.armor_level}`);
|
||||
if (!enhanced.damage_range && isValid(enhanced.max_damage)) combatProps.push(`Max Damage: ${enhanced.max_damage}`);
|
||||
if (!enhanced.attack_bonus && isValid(enhanced.damage_bonus)) combatProps.push(`Damage Bonus: ${enhanced.damage_bonus.toFixed(1)}`);
|
||||
|
||||
if (combatProps.length > 0) {
|
||||
tooltipHTML += `<div class="tooltip-section"><div class="tooltip-section-title">Combat Stats</div>`;
|
||||
combatProps.forEach(prop => {
|
||||
tooltipHTML += `<div class="tooltip-property">${prop}</div>`;
|
||||
});
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Requirements section
|
||||
const reqProps = [];
|
||||
if (isValid(enhanced.wield_level)) reqProps.push(`Level Required: ${enhanced.wield_level}`);
|
||||
if (isValid(enhanced.skill_level)) reqProps.push(`Skill Level: ${enhanced.skill_level}`);
|
||||
if (enhanced.equip_skill_name) reqProps.push(`Skill: ${enhanced.equip_skill_name}`);
|
||||
if (isValid(enhanced.lore_requirement)) reqProps.push(`Lore: ${enhanced.lore_requirement}`);
|
||||
|
||||
if (reqProps.length > 0) {
|
||||
tooltipHTML += `<div class="tooltip-section"><div class="tooltip-section-title">Requirements</div>`;
|
||||
reqProps.forEach(prop => {
|
||||
tooltipHTML += `<div class="tooltip-property">${prop}</div>`;
|
||||
});
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Enhancement section
|
||||
const enhanceProps = [];
|
||||
if (enhanced.material_name) enhanceProps.push(`Material: ${enhanced.material_name}`);
|
||||
if (enhanced.imbue) enhanceProps.push(`Imbue: ${enhanced.imbue}`);
|
||||
if (enhanced.item_set) enhanceProps.push(`Set: ${enhanced.item_set}`);
|
||||
if (isValid(enhanced.tinks)) enhanceProps.push(`Tinks: ${enhanced.tinks}`);
|
||||
// Use workmanship_text if available, otherwise numeric value
|
||||
if (enhanced.workmanship_text) {
|
||||
enhanceProps.push(`Workmanship: ${enhanced.workmanship_text}`);
|
||||
} else if (isValid(enhanced.workmanship)) {
|
||||
enhanceProps.push(`Workmanship: ${enhanced.workmanship.toFixed(1)}`);
|
||||
}
|
||||
|
||||
if (enhanceProps.length > 0) {
|
||||
tooltipHTML += `<div class="tooltip-section"><div class="tooltip-section-title">Enhancements</div>`;
|
||||
enhanceProps.forEach(prop => {
|
||||
tooltipHTML += `<div class="tooltip-property">${prop}</div>`;
|
||||
});
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Ratings section
|
||||
const ratingProps = [];
|
||||
if (isValid(enhanced.damage_rating)) ratingProps.push(`Damage Rating: ${enhanced.damage_rating}`);
|
||||
if (isValid(enhanced.crit_rating)) ratingProps.push(`Crit Rating: ${enhanced.crit_rating}`);
|
||||
if (isValid(enhanced.heal_boost_rating)) ratingProps.push(`Heal Boost: ${enhanced.heal_boost_rating}`);
|
||||
|
||||
if (ratingProps.length > 0) {
|
||||
tooltipHTML += `<div class="tooltip-section"><div class="tooltip-section-title">Ratings</div>`;
|
||||
ratingProps.forEach(prop => {
|
||||
tooltipHTML += `<div class="tooltip-property">${prop}</div>`;
|
||||
});
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Spells section (condensed list)
|
||||
if (enhanced.spells && enhanced.spells.spells && enhanced.spells.spells.length > 0) {
|
||||
const spellNames = enhanced.spells.spells.map(spell => spell.name).join(', ');
|
||||
tooltipHTML += `<div class="tooltip-section">`;
|
||||
tooltipHTML += `<div class="tooltip-property">Spells: ${spellNames}</div>`;
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Mana and Spellcraft section
|
||||
if (enhanced.mana_display || enhanced.spellcraft) {
|
||||
tooltipHTML += `<div class="tooltip-section">`;
|
||||
if (enhanced.spellcraft) {
|
||||
tooltipHTML += `<div class="tooltip-property">Spellcraft: ${enhanced.spellcraft}</div>`;
|
||||
}
|
||||
if (enhanced.mana_display) {
|
||||
tooltipHTML += `<div class="tooltip-property">Mana: ${enhanced.mana_display}</div>`;
|
||||
}
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Detailed Spell Descriptions section
|
||||
if (enhanced.spells && enhanced.spells.spells && enhanced.spells.spells.length > 0) {
|
||||
tooltipHTML += `<div class="tooltip-section"><div class="tooltip-section-title">Spell Descriptions</div>`;
|
||||
enhanced.spells.spells.forEach(spell => {
|
||||
tooltipHTML += `<div class="tooltip-spell">`;
|
||||
tooltipHTML += `<div class="spell-name">${spell.name}</div>`;
|
||||
if (spell.description) {
|
||||
tooltipHTML += `<div class="spell-description">${spell.description}</div>`;
|
||||
}
|
||||
tooltipHTML += `</div>`;
|
||||
});
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
// Object class info
|
||||
if (enhanced.object_class_name) {
|
||||
tooltipHTML += `<div class="tooltip-section">`;
|
||||
tooltipHTML += `<div class="tooltip-info">Type: ${enhanced.object_class_name}</div>`;
|
||||
tooltipHTML += `</div>`;
|
||||
}
|
||||
|
||||
} // End of enhanced data check
|
||||
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse enhanced tooltip data', e);
|
||||
}
|
||||
}
|
||||
|
||||
inventoryTooltip.innerHTML = tooltipHTML;
|
||||
|
||||
// Position tooltip near cursor
|
||||
const x = e.clientX + 10;
|
||||
const y = e.clientY + 10;
|
||||
inventoryTooltip.style.left = `${x}px`;
|
||||
inventoryTooltip.style.top = `${y}px`;
|
||||
inventoryTooltip.style.display = 'block';
|
||||
}
|
||||
|
||||
function hideInventoryTooltip() {
|
||||
if (inventoryTooltip) {
|
||||
inventoryTooltip.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const applyTransform = () =>
|
||||
group.style.transform = `translate(${offX}px,${offY}px) scale(${scale})`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue