osrs-tools
Version:
A comprehensive TypeScript library for Old School RuneScape (OSRS) data and utilities, including quest data, skill requirements, and game item information
125 lines (124 loc) • 5.73 kB
JavaScript
import { COLLECTION_LOG_CATEGORIES, CollectionLogCategory, } from "./CollectionLog.model";
const COLLECTION_LOG_SOURCE = "https://oldschool.runescape.wiki/w/Collection_log";
const item = (id, name) => ({
id,
name,
wikiUrl: COLLECTION_LOG_SOURCE,
});
const entry = (id, name, category, activity, items) => ({
id,
name,
category,
activity,
items,
});
export const COLLECTION_LOG_DATASET = {
source: COLLECTION_LOG_SOURCE,
entries: [
entry("cl-zulrah", "Zulrah", CollectionLogCategory.Bosses, "Zulrah", [
item("cl-zulrah-magic-fang", "Magic fang"),
item("cl-zulrah-serp-visage", "Serpentine visage"),
item("cl-zulrah-tanz-fang", "Tanzanite fang"),
item("cl-zulrah-pet", "Pet snakeling"),
]),
entry("cl-vorkath", "Vorkath", CollectionLogCategory.Bosses, "Vorkath", [
item("cl-vorkath-draconic-visage", "Draconic visage"),
item("cl-vorkath-skeletal-visage", "Skeletal visage"),
item("cl-vorkath-dragonbone-necklace", "Dragonbone necklace"),
item("cl-vorkath-head", "Vorkath's head"),
item("cl-vorkath-pet", "Vorki"),
]),
entry("cl-barrows", "Barrows Chests", CollectionLogCategory.Bosses, "Barrows", [
item("cl-barrows-dharok-helm", "Dharok's helm"),
item("cl-barrows-karil-top", "Karil's leathertop"),
item("cl-barrows-ahrim-top", "Ahrim's robetop"),
item("cl-barrows-guthan-spear", "Guthan's warspear"),
]),
entry("cl-cox", "Chambers of Xeric", CollectionLogCategory.Raids, "Chambers of Xeric", [
item("cl-cox-twisted-bow", "Twisted bow"),
item("cl-cox-ancestral-top", "Ancestral robe top"),
item("cl-cox-dex-scroll", "Dexterous prayer scroll"),
item("cl-cox-kodai-insignia", "Kodai insignia"),
item("cl-cox-olmlet", "Olmlet"),
]),
entry("cl-tob", "Theatre of Blood", CollectionLogCategory.Raids, "Theatre of Blood", [
item("cl-tob-scythe", "Scythe of vitur"),
item("cl-tob-rapier", "Ghrazi rapier"),
item("cl-tob-sanguinesti", "Sanguinesti staff"),
item("cl-tob-lil-zik", "Lil' zik"),
]),
entry("cl-toa", "Tombs of Amascut", CollectionLogCategory.Raids, "Tombs of Amascut", [
item("cl-toa-shadow", "Tumeken's shadow"),
item("cl-toa-fang", "Osmumten's fang"),
item("cl-toa-ward", "Elidinis' ward"),
item("cl-toa-ics", "Icthlarin's shroud"),
item("cl-toa-pet", "Tumeken's guardian"),
]),
],
};
function normalizeIdSet(itemIds) {
return new Set(itemIds.map((itemId) => itemId.trim().toLowerCase()));
}
function percent(obtained, total) {
if (total === 0)
return 0;
return Number(((obtained / total) * 100).toFixed(2));
}
export function getCollectionLogEntries(dataset = COLLECTION_LOG_DATASET) {
return [...dataset.entries];
}
export function getCollectionLogEntryById(entryId, dataset = COLLECTION_LOG_DATASET) {
const normalizedEntryId = entryId.trim().toLowerCase();
return dataset.entries.find((logEntry) => logEntry.id.toLowerCase() === normalizedEntryId);
}
export function getCollectionLogEntriesByCategory(category, dataset = COLLECTION_LOG_DATASET) {
return dataset.entries.filter((logEntry) => logEntry.category === category);
}
export function getMissingCollectionLogItems(obtainedItemIds, dataset = COLLECTION_LOG_DATASET) {
const obtainedIds = normalizeIdSet(obtainedItemIds);
return dataset.entries
.flatMap((logEntry) => logEntry.items)
.filter((logItem) => !obtainedIds.has(logItem.id.toLowerCase()));
}
function entryProgress(logEntry, obtainedIds) {
const obtained = logEntry.items.filter((logItem) => obtainedIds.has(logItem.id.toLowerCase())).length;
const missingItemIds = logEntry.items
.filter((logItem) => !obtainedIds.has(logItem.id.toLowerCase()))
.map((logItem) => logItem.id);
return {
entryId: logEntry.id,
entryName: logEntry.name,
category: logEntry.category,
total: logEntry.items.length,
obtained,
completionPercent: percent(obtained, logEntry.items.length),
missingItemIds,
};
}
function categoryProgress(category, entrySummaries) {
const categoryEntries = entrySummaries.filter((entrySummary) => entrySummary.category === category);
const total = categoryEntries.reduce((runningTotal, entrySummary) => runningTotal + entrySummary.total, 0);
const obtained = categoryEntries.reduce((runningTotal, entrySummary) => runningTotal + entrySummary.obtained, 0);
return {
category,
total,
obtained,
completionPercent: percent(obtained, total),
missingItemIds: categoryEntries.flatMap((entrySummary) => entrySummary.missingItemIds),
};
}
export function calculateCollectionLogProgress(obtainedItemIds, dataset = COLLECTION_LOG_DATASET) {
const obtainedIds = normalizeIdSet(obtainedItemIds);
const byEntry = dataset.entries.map((logEntry) => entryProgress(logEntry, obtainedIds));
const byCategory = COLLECTION_LOG_CATEGORIES.map((category) => categoryProgress(category, byEntry));
const total = byEntry.reduce((runningTotal, entrySummary) => runningTotal + entrySummary.total, 0);
const obtained = byEntry.reduce((runningTotal, entrySummary) => runningTotal + entrySummary.obtained, 0);
return {
total,
obtained,
completionPercent: percent(obtained, total),
byEntry,
byCategory,
missingItemIds: byEntry.flatMap((entrySummary) => entrySummary.missingItemIds),
};
}