var DropView = (function() {
var _cache = {};
var _timers = {};
var _seq = 0;
var DEBOUNCE_MS = 200;
function fetchTable(id) {
if (!id) return Promise.resolve(null);
if (id in _cache) return Promise.resolve(_cache[id]);
return API.get('/api/drops/' + encodeURIComponent(id)).then(function(data) {
_cache[id] = data;
return data;
}).catch(function() {
_cache[id] = null;
return null;
});
}
function invalidate(id) {
if (id !== undefined && id !== null) {
delete _cache[id];
} else {
_cache = {};
}
}
function entryWeight(e) {
var w = parseInt(e.weight) || 0;
if (w <= 0) return 0;
if (!e.item_id && !e.table) return 0;
return w;
}
function mapAdd(map, key, prob) {
map.set(key, (map.get(key) || 0) + prob);
}
function computeNestedProbMap(tableID, visited) {
return fetchTable(tableID).then(function(data) {
var map = new Map();
if (!data || !Array.isArray(data.drops) || data.drops.length === 0) return map;
if (visited.has(tableID)) return map;
if (visited.size >= 32) return map;
visited.add(tableID);
return _computeEntriesProbMap(data.drops, visited).then(function(result) {
visited.delete(tableID);
return result;
}).catch(function() {
visited.delete(tableID);
return new Map();
});
}).catch(function() {
return new Map();
});
}
function _computeEntriesProbMap(entries, visited) {
var total = 0;
for (var i = 0; i < entries.length; i++) {
total += entryWeight(entries[i]);
}
if (total === 0) return Promise.resolve(new Map());
var map = new Map();
var innerCache = {};
var promises = [];
for (var i = 0; i < entries.length; i++) {
(function(e) {
var w = entryWeight(e);
if (w === 0) return;
var pEntry = w / total;
if (e.table) {
var rolls = parseInt(e.quantity) || 1;
if (!innerCache[e.table]) {
innerCache[e.table] = computeNestedProbMap(e.table, new Set(visited));
}
promises.push(
innerCache[e.table].then(function(innerMap) {
innerMap.forEach(function(innerP, key) {
var atLeastOnce = 1 - Math.pow(1 - innerP, rolls);
mapAdd(map, key, pEntry * atLeastOnce);
});
})
);
} else if (e.item_id) {
var qty = parseInt(e.quantity) || 1;
var key = e.item_id + '\x00' + qty;
mapAdd(map, key, pEntry);
}
})(entries[i]);
}
return Promise.all(promises).then(function() {
return map;
});
}
function computeProbMap(getEntries) {
var entries = getEntries();
if (!entries || !entries.length) return Promise.resolve(new Map());
var visited = new Set();
return _computeEntriesProbMap(entries, visited);
}
function snapUnitFraction(p) {
if (p <= 0) return '0';
if (p <= 0.0000001) return 'Very rare';
if (p >= 0.9999) return '1';
var n = Math.round(1 / p);
if (n > 10000000) return 'Very rare';
if (n <= 1) return '1';
return '1/' + n;
}
function renderCompact(probMap) {
if (probMap.size === 0) return '
No drops
';
var items = [];
probMap.forEach(function(p, key) {
var parts = key.split('\x00');
items.push({ itemID: parts[0], qty: parseInt(parts[1]) || 1, prob: p });
});
items.sort(function(a, b) {
if (b.prob !== a.prob) return b.prob - a.prob;
var nameCmp = a.itemID.localeCompare(b.itemID);
if (nameCmp !== 0) return nameCmp;
return a.qty - b.qty;
});
var h = '';
for (var i = 0; i < items.length; i++) {
var snap = snapUnitFraction(items[i].prob);
var qtyStr = items[i].qty > 1 ? items[i].qty + '\u00d7 ' : '';
h += '
';
h += '' + esc(qtyStr + items[i].itemID) + '';
h += '' + esc(snap) + '';
h += '
';
}
h += '
';
return h;
}
function refreshViewEl(view, getEntries) {
if (!document.contains(view)) return;
var body = view.querySelector('.obj-drop-view-body');
if (!body) return;
body.innerHTML = 'Computing...';
computeProbMap(getEntries).then(function(probMap) {
if (!document.contains(view)) return;
body.innerHTML = renderCompact(probMap);
}).catch(function(e) {
if (!document.contains(view)) return;
body.innerHTML = 'Error: ' + esc(e.message) + '';
});
}
function scheduleRefresh(view, getEntries) {
var key = view._dvKey || (view._dvKey = ++_seq);
if (_timers[key]) clearTimeout(_timers[key]);
_timers[key] = setTimeout(function() {
delete _timers[key];
refreshViewEl(view, getEntries);
}, DEBOUNCE_MS);
}
function mount(container, getEntries) {
if (typeof container === 'string') container = document.querySelector(container);
if (!container) return null;
var viewEl = container.querySelector('.obj-drop-view');
if (!viewEl) {
viewEl = document.createElement('div');
viewEl.className = 'obj-drop-view';
container.appendChild(viewEl);
}
if (!viewEl.querySelector('.obj-drop-view-refresh')) {
viewEl.innerHTML =
'' +
'Computing...
';
}
viewEl._getEntries = getEntries;
viewEl.querySelector('.obj-drop-view-refresh').onclick = function(e) {
e.stopPropagation();
invalidate();
scheduleRefresh(viewEl, getEntries);
};
if (!container._dvSetup) {
container._dvSetup = true;
container.addEventListener('input', function(e) {
if (e.target.tagName === 'INPUT') {
scheduleRefresh(viewEl, getEntries);
}
});
container.addEventListener('change', function(e) {
scheduleRefresh(viewEl, getEntries);
});
}
scheduleRefresh(viewEl, getEntries);
return viewEl;
}
function refreshView(btn) {
var view = (btn && btn.closest) ? btn.closest('.obj-drop-view') : null;
if (!view || !view._getEntries) return;
invalidate();
scheduleRefresh(view, view._getEntries);
}
function setupAllInter() {
document.querySelectorAll('.ie-act-kv[data-ie-act-key="drop_table"]').forEach(function(kv) {
if (kv.querySelector('.obj-drop-view')) {
mount(kv, function() {
var dt = kv.querySelector('.ie-drop-table');
if (!dt) return [];
var collected = ie_collectDropTableBlock(dt);
if (!collected) return [];
return collected.map(function(e) {
return { item_id: e.item_id || '', table: e.table || '', weight: e.weight || 0, quantity: e.quantity || 0 };
});
});
}
});
}
return {
mount: mount,
invalidate: invalidate,
refreshView: refreshView,
computeProbMap: computeProbMap,
snapUnitFraction: snapUnitFraction,
setupAllInter: setupAllInter
};
})();