UNPKG

runescape

Version:

A library to interact with the non-existent RuneScape API.

1,227 lines (1,212 loc) 50.6 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // source/utility/error.ts var ErrorCode = { // Profile. /** * The generic error when fetching a player's profile data as viewed on RuneMetrics. */ ProfileError: "ProfileError", /** * This seems to be returned on banned players. */ ProfileNotAMember: "ProfileNotAMember", /** * The RuneMetrics profile of this player is not public. */ ProfilePrivate: "ProfilePrivate", /** * It is supposable this player does not exist. */ ProfileNone: "ProfileNone" }; var Messages = { /** * The generic error when fetching a player's profile data as viewed on RuneMetrics. */ [ErrorCode.ProfileError]: "Failed to fetch the RuneMetrics profile of this player.", /** * This seems to be returned on banned players. */ [ErrorCode.ProfileNotAMember]: "This player is banned.", /** * The RuneMetrics profile of this player is not public. */ [ErrorCode.ProfilePrivate]: "This player's RuneMetrics profile is not public.", /** * It is supposable this player does not exist. */ [ErrorCode.ProfileNone]: "Unknown player." }; var RuneScapeAPIError = class extends Error { static { __name(this, "RuneScapeAPIError"); } /** * The status code of the error. */ statusCode; /** * The fully quallified URL of the request. */ url; /** * Constructs an error for the API. * * @param name - The name of this error * @param statusCode - The status code of the error * @param url - The fully quallified URL of the request */ constructor(name, statusCode, url) { super(name); this.statusCode = statusCode; this.url = url; } }; var RuneScapeError = class extends Error { static { __name(this, "RuneScapeError"); } /** * The defined error code. */ code; /** * The raw error that yielded this error. */ rawCode; /** * The fully quallified URL of the request. */ url; /** * Constructs a defined error from the library. * * @param code - The defined error code * @param rawCode - The raw error that yielded this error * @param url - The fully quallified URL of the request */ constructor(code, rawCode, url) { super(Messages[code]); this.code = code; this.rawCode = rawCode; this.url = url; } get name() { return `${super.name} [${this.rawCode}]`; } }; // source/clan.ts import { parse } from "csv-parse/sync"; // source/utility/make-request.ts function makeRequest(url, abortSignal) { const options = {}; if (abortSignal) { options.signal = abortSignal; } return fetch(url, options); } __name(makeRequest, "makeRequest"); // source/clan.ts var ClanRank = /* @__PURE__ */ ((ClanRank2) => { ClanRank2[ClanRank2["Owner"] = 0] = "Owner"; ClanRank2[ClanRank2["DeputyOwner"] = 1] = "DeputyOwner"; ClanRank2[ClanRank2["Overseer"] = 2] = "Overseer"; ClanRank2[ClanRank2["Coordinator"] = 3] = "Coordinator"; ClanRank2[ClanRank2["Organiser"] = 4] = "Organiser"; ClanRank2[ClanRank2["Administrator"] = 5] = "Administrator"; ClanRank2[ClanRank2["General"] = 6] = "General"; ClanRank2[ClanRank2["Captain"] = 7] = "Captain"; ClanRank2[ClanRank2["Lieutenant"] = 8] = "Lieutenant"; ClanRank2[ClanRank2["Sergeant"] = 9] = "Sergeant"; ClanRank2[ClanRank2["Corporal"] = 10] = "Corporal"; ClanRank2[ClanRank2["Recruit"] = 11] = "Recruit"; return ClanRank2; })(ClanRank || {}); var APIClanRankToClanRank = { Owner: 0 /* Owner */, "Deputy Owner": 1 /* DeputyOwner */, Overseer: 2 /* Overseer */, Coordinator: 3 /* Coordinator */, Organiser: 4 /* Organiser */, Administrator: 5 /* Administrator */, General: 6 /* General */, Captain: 7 /* Captain */, Lieutenant: 8 /* Lieutenant */, Sergeant: 9 /* Sergeant */, Corporal: 10 /* Corporal */, Recruit: 11 /* Recruit */ }; async function fetchClanMembers({ clanName, abortSignal }) { const url = `https://secure.runescape.com/m=clan-hiscores/members_lite.ws?clanName=${clanName}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching clan members.", response.status, url); } const text = await response.text(); return parse(text, { cast: /* @__PURE__ */ __name((value, { column }) => ( // Spaces in player names are "�". column === "clanmate" ? value.replace(/�/g, " ") : column === "clanRank" ? APIClanRankToClanRank[value] : column === "totalXP" || column === "kills" ? Number(value) : value ), "cast"), columns: /* @__PURE__ */ __name(() => [ { name: "clanmate" }, { name: "clanRank" }, { name: "totalXP" }, { name: "kills" } ], "columns"), trim: true }); } __name(fetchClanMembers, "fetchClanMembers"); // source/guthixian-cache.ts function guthixianCache(timestamp) { return new Date(timestamp).getUTCHours() % 3 === 0; } __name(guthixianCache, "guthixianCache"); // source/hi-scores.ts var Skill = /* @__PURE__ */ ((Skill2) => { Skill2["Agility"] = "Agility"; Skill2["Archaeology"] = "Archaeology"; Skill2["Attack"] = "Attack"; Skill2["Constitution"] = "Constitution"; Skill2["Construction"] = "Construction"; Skill2["Cooking"] = "Cooking"; Skill2["Crafting"] = "Crafting"; Skill2["Defence"] = "Defence"; Skill2["Divination"] = "Divination"; Skill2["Dungeoneering"] = "Dungeoneering"; Skill2["Farming"] = "Farming"; Skill2["Firemaking"] = "Firemaking"; Skill2["Fishing"] = "Fishing"; Skill2["Fletching"] = "Fletching"; Skill2["Herblore"] = "Herblore"; Skill2["Hunter"] = "Hunter"; Skill2["Invention"] = "Invention"; Skill2["Magic"] = "Magic"; Skill2["Mining"] = "Mining"; Skill2["Prayer"] = "Prayer"; Skill2["Ranged"] = "Ranged"; Skill2["Runecrafting"] = "Runecrafting"; Skill2["Slayer"] = "Slayer"; Skill2["Smithing"] = "Smithing"; Skill2["Strength"] = "Strength"; Skill2["Summoning"] = "Summoning"; Skill2["Thieving"] = "Thieving"; Skill2["Woodcutting"] = "Woodcutting"; return Skill2; })(Skill || {}); async function hiScore({ name, abortSignal }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("player", name); const url = `https://secure.runescape.com/m=hiscore/index_lite.ws?${urlSearchParams}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching HiScore data.", response.status, url); } const body = await response.text(); const dataLine = body.split("\n").map((line) => line.split(",")); return { total: { name: "Total", rank: Number(dataLine[0][0]), level: Number(dataLine[0][1]), totalXP: Number(dataLine[0][2]) }, attack: { name: "Attack" /* Attack */, rank: Number(dataLine[1][0]), level: Number(dataLine[1][1]), totalXP: Number(dataLine[1][2]) }, defence: { name: "Defence" /* Defence */, rank: Number(dataLine[2][0]), level: Number(dataLine[2][1]), totalXP: Number(dataLine[2][2]) }, strength: { name: "Strength" /* Strength */, rank: Number(dataLine[3][0]), level: Number(dataLine[3][1]), totalXP: Number(dataLine[3][2]) }, constitution: { name: "Constitution" /* Constitution */, rank: Number(dataLine[4][0]), level: Number(dataLine[4][1]), totalXP: Number(dataLine[4][2]) }, ranged: { name: "Ranged" /* Ranged */, rank: Number(dataLine[5][0]), level: Number(dataLine[5][1]), totalXP: Number(dataLine[5][2]) }, prayer: { name: "Prayer" /* Prayer */, rank: Number(dataLine[6][0]), level: Number(dataLine[6][1]), totalXP: Number(dataLine[6][2]) }, magic: { name: "Magic" /* Magic */, rank: Number(dataLine[7][0]), level: Number(dataLine[7][1]), totalXP: Number(dataLine[7][2]) }, cooking: { name: "Cooking" /* Cooking */, rank: Number(dataLine[8][0]), level: Number(dataLine[8][1]), totalXP: Number(dataLine[8][2]) }, woodcutting: { name: "Woodcutting" /* Woodcutting */, rank: Number(dataLine[9][0]), level: Number(dataLine[9][1]), totalXP: Number(dataLine[9][2]) }, fletching: { name: "Fletching" /* Fletching */, rank: Number(dataLine[10][0]), level: Number(dataLine[10][1]), totalXP: Number(dataLine[10][2]) }, fishing: { name: "Fishing" /* Fishing */, rank: Number(dataLine[11][0]), level: Number(dataLine[11][1]), totalXP: Number(dataLine[11][2]) }, firemaking: { name: "Firemaking" /* Firemaking */, rank: Number(dataLine[12][0]), level: Number(dataLine[12][1]), totalXP: Number(dataLine[12][2]) }, crafting: { name: "Crafting" /* Crafting */, rank: Number(dataLine[13][0]), level: Number(dataLine[13][1]), totalXP: Number(dataLine[13][2]) }, smithing: { name: "Smithing" /* Smithing */, rank: Number(dataLine[14][0]), level: Number(dataLine[14][1]), totalXP: Number(dataLine[14][2]) }, mining: { name: "Mining" /* Mining */, rank: Number(dataLine[15][0]), level: Number(dataLine[15][1]), totalXP: Number(dataLine[15][2]) }, herblore: { name: "Herblore" /* Herblore */, rank: Number(dataLine[16][0]), level: Number(dataLine[16][1]), totalXP: Number(dataLine[16][2]) }, agility: { name: "Agility" /* Agility */, rank: Number(dataLine[17][0]), level: Number(dataLine[17][1]), totalXP: Number(dataLine[17][2]) }, thieving: { name: "Thieving" /* Thieving */, rank: Number(dataLine[18][0]), level: Number(dataLine[18][1]), totalXP: Number(dataLine[18][2]) }, slayer: { name: "Slayer" /* Slayer */, rank: Number(dataLine[19][0]), level: Number(dataLine[19][1]), totalXP: Number(dataLine[19][2]) }, farming: { name: "Farming" /* Farming */, rank: Number(dataLine[20][0]), level: Number(dataLine[20][1]), totalXP: Number(dataLine[20][2]) }, runecrafting: { name: "Runecrafting" /* Runecrafting */, rank: Number(dataLine[21][0]), level: Number(dataLine[21][1]), totalXP: Number(dataLine[21][2]) }, hunter: { name: "Hunter" /* Hunter */, rank: Number(dataLine[22][0]), level: Number(dataLine[22][1]), totalXP: Number(dataLine[22][2]) }, construction: { name: "Construction" /* Construction */, rank: Number(dataLine[23][0]), level: Number(dataLine[23][1]), totalXP: Number(dataLine[23][2]) }, summoning: { name: "Summoning" /* Summoning */, rank: Number(dataLine[24][0]), level: Number(dataLine[24][1]), totalXP: Number(dataLine[24][2]) }, dungeoneering: { name: "Dungeoneering" /* Dungeoneering */, rank: Number(dataLine[25][0]), level: Number(dataLine[25][1]), totalXP: Number(dataLine[25][2]) }, divination: { name: "Divination" /* Divination */, rank: Number(dataLine[26][0]), level: Number(dataLine[26][1]), totalXP: Number(dataLine[26][2]) }, invention: { name: "Invention" /* Invention */, rank: Number(dataLine[27][0]), level: Number(dataLine[27][1]), totalXP: Number(dataLine[27][2]) }, archaeology: { name: "Archaeology" /* Archaeology */, rank: Number(dataLine[28][0]), level: Number(dataLine[28][1]), totalXP: Number(dataLine[28][2]) } }; } __name(hiScore, "hiScore"); async function groupIronman({ groupSize, size, page, isCompetitive, abortSignal }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("groupSize", String(groupSize)); if (size !== void 0) { urlSearchParams.set("size", String(size)); } if (page !== void 0) { urlSearchParams.set("page", String(page)); } if (isCompetitive !== void 0) { urlSearchParams.set("isCompetitive", String(isCompetitive)); } const url = `https://secure.runescape.com/m=runescape_gim_hiscores/v1/groupScores?${urlSearchParams}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching Group Ironman HiScore data.", response.status, url); } return response.json(); } __name(groupIronman, "groupIronman"); // source/utility/functions.ts var MULTI = 0x5deece66dn; var MASK = 281474976710656n; var c0 = 0xe66dn; var c1 = 0xdeecn; var c2 = 0x0005n; var chunk = 65536n; function runedate(timestamp) { return Math.floor((timestamp - Date.UTC(2002, 1, 27)) / 864e5 * 100) / 100; } __name(runedate, "runedate"); function multiplyAvoidLimit(seed) { const s0 = seed % chunk; const s1 = BigInt(Math.floor(Number(seed / chunk))) % chunk; const s2 = BigInt(Math.floor(Number(seed / chunk / chunk))); let carry = 11n; let r0 = s0 * c0 + carry; carry = BigInt(Math.floor(Number(r0 / chunk))); r0 %= chunk; let r1 = s1 * c0 + s0 * c1 + carry; carry = BigInt(Math.floor(Number(r1 / chunk))); r1 %= chunk; let r2 = s2 * c0 + s1 * c1 + s0 * c2 + carry; r2 %= chunk; return r2 * chunk * chunk + r1 * chunk + r0; } __name(multiplyAvoidLimit, "multiplyAvoidLimit"); function nextInt(seed, no, repeats = 1) { let computedSeed = seed ^ MULTI % MASK; for (let index = 0; index < repeats; index++) { computedSeed = multiplyAvoidLimit(computedSeed); } computedSeed >>= 17n; return computedSeed % no; } __name(nextInt, "nextInt"); function transformName(name, delimiter) { return name.replaceAll(" ", delimiter); } __name(transformName, "transformName"); // source/jewels.ts var Jewel = /* @__PURE__ */ ((Jewel2) => { Jewel2["ApmekenAmethyst"] = "Apmeken Amethyst"; Jewel2["ScabariteCrystal"] = "Scabarite Crystal"; return Jewel2; })(Jewel || {}); function jewel(timestamp) { const slot = nextInt(BigInt(Math.trunc(runedate(timestamp))) * 2n ** 32n, 5n); switch (slot) { case 0n: return "Scabarite Crystal" /* ScabariteCrystal */; case 2n: return "Apmeken Amethyst" /* ApmekenAmethyst */; default: return null; } } __name(jewel, "jewel"); // source/miscellaneous.ts function avatar({ name, width, height }) { const urlSearchParams = new URLSearchParams(); if (width !== void 0) { urlSearchParams.set("w", String(width)); } if (height !== void 0) { urlSearchParams.set("h", String(height)); } return `https://secure.runescape.com/m=avatar-rs/${transformName(name, "%20")}/chat.png${String(urlSearchParams) ? `?${urlSearchParams}` : ""}`; } __name(avatar, "avatar"); var ClanPage = /* @__PURE__ */ ((ClanPage2) => { ClanPage2["RuneInfo"] = "RuneInfo"; ClanPage2["RunePixels"] = "Runepixels"; ClanPage2["RuneScape"] = "RuneScape"; return ClanPage2; })(ClanPage || {}); function clanPage({ clan }) { return { ["RuneScape" /* RuneScape */]: `https://services.runescape.com/m=clan-home/clan/${transformName(clan, "%20")}`, ["RuneInfo" /* RuneInfo */]: `https://runeinfo.com/clan/${transformName(clan, "%20")}`, ["Runepixels" /* RunePixels */]: `https://runepixels.com/clans/${transformName(clan, "-")}` }; } __name(clanPage, "clanPage"); var PlayerPage = /* @__PURE__ */ ((PlayerPage2) => { PlayerPage2["RuneInfo"] = "RuneInfo"; PlayerPage2["RuneMetrics"] = "RuneMetrics"; PlayerPage2["RunePixels"] = "Runepixels"; PlayerPage2["RuneScape"] = "RuneScape"; PlayerPage2["RuneTracker"] = "RuneTracker"; return PlayerPage2; })(PlayerPage || {}); function playerPage({ name }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("user1", name); return { ["RuneScape" /* RuneScape */]: `https://secure.runescape.com/m=hiscore/compare?${urlSearchParams}`, ["RuneMetrics" /* RuneMetrics */]: `https://apps.runescape.com/runemetrics/app/overview/player/${transformName( name, "%20" )}`, ["RuneInfo" /* RuneInfo */]: `https://runeinfo.com/profile/${transformName(name, "%20")}`, ["Runepixels" /* RunePixels */]: `https://runepixels.com/players/${transformName(name, "-")}`, ["RuneTracker" /* RuneTracker */]: `https://runetracker.org/track-${transformName(name, "+")}` }; } __name(playerPage, "playerPage"); async function playerCount({ abortSignal } = {}) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("varname", "iPlayerCount"); urlSearchParams.set("callback", "jQuery000000000000000_0000000000"); const url = `https://runescape.com/player_count.js?${urlSearchParams}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching the player count.", response.status, url); } const body = await response.text(); return Number(body.slice(body.indexOf("(") + 1, body.indexOf(")"))); } __name(playerCount, "playerCount"); async function createdAccounts({ abortSignal } = {}) { const url = "https://secure.runescape.com/m=account-creation-reports/rsusertotal.ws"; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError( "Error fetching the amount of created accounts.", response.status, url ); } const body = await response.json(); return { accounts: body.accounts, accountsFormatted: body.accountsformatted }; } __name(createdAccounts, "createdAccounts"); // source/player-details.ts async function playerDetails({ names, abortSignal }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("names", JSON.stringify(names)); urlSearchParams.set("callback", "jQuery000000000000000_0000000000"); const response = await makeRequest( `https://secure.runescape.com/m=website-data/playerDetails.ws?${urlSearchParams}`, abortSignal ); const body = await response.text(); const json = JSON.parse(body.slice(body.indexOf("["), body.indexOf("]") + 1)); return json.map(({ isSuffix, recruiting, name, clan, title }) => ({ isSuffix, recruiting: recruiting ?? null, name, clan: clan ?? null, title: title === "" ? null : title })); } __name(playerDetails, "playerDetails"); // source/raven.ts var INITIAL_TIMESTAMP = Date.UTC(2014, 9, 4); function raven(timestamp) { return Math.floor((timestamp - INITIAL_TIMESTAMP) / 864e5) % 13 === 0; } __name(raven, "raven"); // source/rune-metrics.ts import dayjs from "dayjs"; import utc from "dayjs/plugin/utc.js"; var SkillId = /* @__PURE__ */ ((SkillId2) => { SkillId2[SkillId2["Attack"] = 0] = "Attack"; SkillId2[SkillId2["Defence"] = 1] = "Defence"; SkillId2[SkillId2["Strength"] = 2] = "Strength"; SkillId2[SkillId2["Constitution"] = 3] = "Constitution"; SkillId2[SkillId2["Ranged"] = 4] = "Ranged"; SkillId2[SkillId2["Prayer"] = 5] = "Prayer"; SkillId2[SkillId2["Magic"] = 6] = "Magic"; SkillId2[SkillId2["Cooking"] = 7] = "Cooking"; SkillId2[SkillId2["Woodcutting"] = 8] = "Woodcutting"; SkillId2[SkillId2["Fletching"] = 9] = "Fletching"; SkillId2[SkillId2["Fishing"] = 10] = "Fishing"; SkillId2[SkillId2["Firemaking"] = 11] = "Firemaking"; SkillId2[SkillId2["Crafting"] = 12] = "Crafting"; SkillId2[SkillId2["Smithing"] = 13] = "Smithing"; SkillId2[SkillId2["Mining"] = 14] = "Mining"; SkillId2[SkillId2["Herblore"] = 15] = "Herblore"; SkillId2[SkillId2["Agility"] = 16] = "Agility"; SkillId2[SkillId2["Thieving"] = 17] = "Thieving"; SkillId2[SkillId2["Slayer"] = 18] = "Slayer"; SkillId2[SkillId2["Farming"] = 19] = "Farming"; SkillId2[SkillId2["Runecrafting"] = 20] = "Runecrafting"; SkillId2[SkillId2["Hunter"] = 21] = "Hunter"; SkillId2[SkillId2["Construction"] = 22] = "Construction"; SkillId2[SkillId2["Summoning"] = 23] = "Summoning"; SkillId2[SkillId2["Dungeoneering"] = 24] = "Dungeoneering"; SkillId2[SkillId2["Divination"] = 25] = "Divination"; SkillId2[SkillId2["Invention"] = 26] = "Invention"; SkillId2[SkillId2["Archaeology"] = 27] = "Archaeology"; return SkillId2; })(SkillId || {}); dayjs.extend(utc); async function profile({ name, activities, abortSignal }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("user", name); if (activities !== void 0) { urlSearchParams.set("activities", String(activities)); } const url = `https://apps.runescape.com/runemetrics/profile?${urlSearchParams}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching RuneMetrics profile data.", response.status, url); } const body = await response.json(); if ("error" in body) { let code; switch (body.error) { case "NOT_A_MEMBER" /* NotAMember */: code = ErrorCode.ProfileNotAMember; break; case "PROFILE_PRIVATE" /* ProfilePrivate */: code = ErrorCode.ProfilePrivate; break; case "NO_PROFILE" /* NoProfile */: code = ErrorCode.ProfileNone; break; default: code = ErrorCode.ProfileError; } throw new RuneScapeError(code, body.error, url); } const { magic, questsstarted, totalskill, questscomplete, questsnotstarted, totalxp, ranged, activities: rawActivities, skillvalues, name: rawName, rank, melee, combatlevel, loggedIn } = body; return { magic, questsStarted: questsstarted, totalSkill: totalskill, questsComplete: questscomplete, questsNotStarted: questsnotstarted, totalXp: totalxp, ranged, activities: rawActivities.map(({ date, details, text }) => ({ timestamp: dayjs.utc(date, "Europe/London").valueOf(), details, text })), skillValues: skillvalues.map(({ level, xp, rank: rank2, id }) => ({ level, xp: xp / 10, rank: rank2, id })), name: rawName, rank, melee, combatLevel: combatlevel, loggedIn: loggedIn === "true" }; } __name(profile, "profile"); var QuestDifficulty = /* @__PURE__ */ ((QuestDifficulty2) => { QuestDifficulty2[QuestDifficulty2["Novice"] = 0] = "Novice"; QuestDifficulty2[QuestDifficulty2["Intermediate"] = 1] = "Intermediate"; QuestDifficulty2[QuestDifficulty2["Experienced"] = 2] = "Experienced"; QuestDifficulty2[QuestDifficulty2["Master"] = 3] = "Master"; QuestDifficulty2[QuestDifficulty2["Grandmaster"] = 4] = "Grandmaster"; QuestDifficulty2[QuestDifficulty2["Special"] = 250] = "Special"; return QuestDifficulty2; })(QuestDifficulty || {}); var QuestStatus = /* @__PURE__ */ ((QuestStatus2) => { QuestStatus2["Completed"] = "COMPLETED"; QuestStatus2["NotStarted"] = "NOT_STARTED"; QuestStatus2["Started"] = "STARTED"; return QuestStatus2; })(QuestStatus || {}); var QuestTitle = /* @__PURE__ */ ((QuestTitle2) => { QuestTitle2["AChristmasReunion"] = "A Christmas Reunion"; QuestTitle2["AClockworkSyringe"] = "A Clockwork Syringe"; QuestTitle2["AFairyTaleIGrowingPains"] = "A Fairy Tale I - Growing Pains"; QuestTitle2["AFairyTaleIICureaQueen"] = "A Fairy Tale II - Cure a Queen"; QuestTitle2["AFairyTaleIIIBattleatOrksRift"] = "A Fairy Tale III - Battle at Ork's Rift"; QuestTitle2["AGuildOfOurOwn"] = "A Guild of Our Own (miniquest)"; QuestTitle2["AShadowoverAshdale"] = "A Shadow over Ashdale"; QuestTitle2["ASoulsBane"] = "A Soul's Bane"; QuestTitle2["ATailOfTwoCats"] = "A Tail of Two Cats"; QuestTitle2["AVoidDance"] = "A Void Dance"; QuestTitle2["Aftermath"] = "Aftermath"; QuestTitle2["AllFiredUp"] = "All Fired Up"; QuestTitle2["AlphaVsOmega"] = "Alpha vs Omega"; QuestTitle2["AncientAwakening"] = "Ancient Awakening"; QuestTitle2["AnimalMagnetism"] = "Animal Magnetism"; QuestTitle2["AnotherSliceOfHAM"] = "Another Slice of H.A.M."; QuestTitle2["AsaFirstResort"] = "As a First Resort"; QuestTitle2["AzzanadrasQuest"] = "Azzanadra's Quest"; QuestTitle2["BackToMyRoots"] = "Back to my Roots"; QuestTitle2["BackToTheFreezer"] = "Back to the Freezer"; QuestTitle2["BarCrawl"] = "Bar Crawl (miniquest)"; QuestTitle2["BattleOfForinthry"] = "Battle of Forinthry"; QuestTitle2["BattleOfTheMonolith"] = "Battle of the Monolith"; QuestTitle2["BeneathCursedTides"] = "Beneath Cursed Tides"; QuestTitle2["BeneathScabarasSands"] = "Beneath Scabaras' Sands"; QuestTitle2["BenedictsWorldTour"] = "Benedict's World Tour (miniquest)"; QuestTitle2["BetweenARock"] = "Between a Rock..."; QuestTitle2["BigChompyBirdHunting"] = "Big Chompy Bird Hunting"; QuestTitle2["Biohazard"] = "Biohazard"; QuestTitle2["BirthrightOfTheDwarves"] = "Birthright of the Dwarves"; QuestTitle2["BloodRunsDeep"] = "Blood Runs Deep"; QuestTitle2["BoricsTaskI"] = "Boric's Task I (miniquest)"; QuestTitle2["BoricsTaskII"] = "Boric's Task II (miniquest)"; QuestTitle2["BoricsTaskIII"] = "Boric's Task III (miniquest)"; QuestTitle2["BringingHomeTheBacon"] = "Bringing Home the Bacon"; QuestTitle2["BrokenHome"] = "Broken Home"; QuestTitle2["BuyersAndCellars"] = "Buyers and Cellars"; QuestTitle2["CabinFever"] = "Cabin Fever"; QuestTitle2["CallOfTheAncestors"] = "Call of the Ancestors"; QuestTitle2["CarnilleanRising"] = "Carnillean Rising"; QuestTitle2["CatapultConstruction"] = "Catapult Construction"; QuestTitle2["ChefsAssistant"] = "Chef's Assistant"; QuestTitle2["ChildrenOfMah"] = "Children of Mah"; QuestTitle2["CityOfSenntisten"] = "City of Senntisten"; QuestTitle2["CivilWarI"] = "Civil War I (miniquest)"; QuestTitle2["CivilWarII"] = "Civil War II (miniquest)"; QuestTitle2["CivilWarIII"] = "Civil War III (miniquest)"; QuestTitle2["ClockTower"] = "Clock Tower"; QuestTitle2["ColdWar"] = "Cold War"; QuestTitle2["Contact"] = "Contact!"; QuestTitle2["CooksAssistant"] = "Cook's Assistant"; QuestTitle2["CreatureOfFenkenstrain"] = "Creature of Fenkenstrain"; QuestTitle2["CrocodileTears"] = "Crocodile Tears"; QuestTitle2["CurseOfTheBlackStone"] = "Curse of the Black Stone"; QuestTitle2["DamageControl"] = "Damage Control (miniquest)"; QuestTitle2["DaughterOfChaos"] = "Daughter of Chaos"; QuestTitle2["DeadAndBuried"] = "Dead and Buried"; QuestTitle2["DeadliestCatch"] = "Deadliest Catch"; QuestTitle2["DealingwithScabaras"] = "Dealing with Scabaras"; QuestTitle2["DeathPlateau"] = "Death Plateau"; QuestTitle2["DeathToTheDorgeshuun"] = "Death to the Dorgeshuun"; QuestTitle2["DefenderOfVarrock"] = "Defender of Varrock"; QuestTitle2["DemonSlayer"] = "Demon Slayer"; QuestTitle2["DesertSlayerDungeon"] = "Desert Slayer Dungeon (miniquest)"; QuestTitle2["DesertTreasure"] = "Desert Treasure"; QuestTitle2["DesperateCreatures"] = "Desperate Creatures"; QuestTitle2["DesperateMeasures"] = "Desperate Measures"; QuestTitle2["DesperateTimes"] = "Desperate Times"; QuestTitle2["DeviousMinds"] = "Devious Minds"; QuestTitle2["DiamondInTheRough"] = "Diamond in the Rough"; QuestTitle2["DimensionOfDisaster"] = "Dimension of Disaster"; QuestTitle2["DimensionOfDisasterCoinOfTheRealm"] = "Dimension of Disaster: Coin of the Realm"; QuestTitle2["DimensionOfDisasterCurseOfArrav"] = "Dimension of Disaster: Curse of Arrav"; QuestTitle2["DimensionOfDisasterDefenderOfVarrock"] = "Dimension of Disaster: Defender of Varrock"; QuestTitle2["DimensionOfDisasterDemonSlayer"] = "Dimension of Disaster: Demon Slayer"; QuestTitle2["DimensionOfDisasterShieldOfArrav"] = "Dimension of Disaster: Shield of Arrav"; QuestTitle2["DishonourAmongThieves"] = "Dishonour among Thieves"; QuestTitle2["DoNoEvil"] = "Do No Evil"; QuestTitle2["DoricsTaskI"] = "Doric's Task I (miniquest)"; QuestTitle2["DoricsTaskII"] = "Doric's Task II (miniquest)"; QuestTitle2["DoricsTaskIII"] = "Doric's Task III (miniquest)"; QuestTitle2["DoricsTaskIV"] = "Doric's Task IV (miniquest)"; QuestTitle2["DoricsTaskV"] = "Doric's Task V (miniquest)"; QuestTitle2["DoricsTaskVI"] = "Doric's Task VI (miniquest)"; QuestTitle2["DoricsTaskVII"] = "Doric's Task VII (miniquest)"; QuestTitle2["DoricsTaskVIII"] = "Doric's Task VIII (miniquest)"; QuestTitle2["DragonSlayer"] = "Dragon Slayer"; QuestTitle2["DreamMentor"] = "Dream Mentor"; QuestTitle2["DruidicRitual"] = "Druidic Ritual"; QuestTitle2["DuckQuest"] = "Duck Quest"; QuestTitle2["DwarfCannon"] = "Dwarf Cannon"; QuestTitle2["EadgarsRuse"] = "Eadgar's Ruse"; QuestTitle2["EaglesPeak"] = "Eagles' Peak"; QuestTitle2["EclipseOfTheHeart"] = "Eclipse of the Heart"; QuestTitle2["ElementalWorkshopI"] = "Elemental Workshop I"; QuestTitle2["ElementalWorkshopII"] = "Elemental Workshop II"; QuestTitle2["ElementalWorkshopIII"] = "Elemental Workshop III"; QuestTitle2["ElementalWorkshopIV"] = "Elemental Workshop IV"; QuestTitle2["EnakhrasLament"] = "Enakhra's Lament"; QuestTitle2["EnlightenedJourney"] = "Enlightened Journey"; QuestTitle2["EnterTheAbyss"] = "Enter the Abyss (miniquest)"; QuestTitle2["ErnestTheChicken"] = "Ernest the Chicken"; QuestTitle2["EvilDavesBigDayOut"] = "Evil Dave's Big Day Out"; QuestTitle2["Extinction"] = "Extinction"; QuestTitle2["EyeForAnEye"] = "Eye for an Eye (miniquest)"; QuestTitle2["EyeOfHetI"] = "Eye of Het I"; QuestTitle2["EyeOfHetII"] = "Eye of Het II"; QuestTitle2["FamilyCrest"] = "Family Crest"; QuestTitle2["FateOfTheGods"] = "Fate of the Gods"; QuestTitle2["FatherAndSon"] = "Father and Son"; QuestTitle2["FieldOfScreams"] = "Field of Screams"; QuestTitle2["FightArena"] = "Fight Arena"; QuestTitle2["FinalDestination"] = "Final Destination (miniquest)"; QuestTitle2["Finale"] = "Finale"; QuestTitle2["FishingContest"] = "Fishing Contest"; QuestTitle2["FlagFall"] = "Flag Fall (miniquest)"; QuestTitle2["Flashback"] = "Flashback"; QuestTitle2["Foreshadowing"] = "Foreshadowing"; QuestTitle2["ForgettableTaleOfADrunkenDwarf"] = "Forgettable Tale of a Drunken Dwarf"; QuestTitle2["ForgivenessOfAChaosDwarf"] = "Forgiveness of a Chaos Dwarf"; QuestTitle2["Fortunes"] = "Fortunes"; QuestTitle2["FromTinyAcorns"] = "From Tiny Acorns (miniquest)"; QuestTitle2["FurNSeek"] = "Fur 'n Seek"; QuestTitle2["GardenOfTranquillity"] = "Garden of Tranquillity"; QuestTitle2["GertrudesCat"] = "Gertrude's Cat"; QuestTitle2["GhostsAhoy"] = "Ghosts Ahoy"; QuestTitle2["GhostsfromThePast"] = "Ghosts from the Past (miniquest)"; QuestTitle2["GloriousMemories"] = "Glorious Memories"; QuestTitle2["GoblinDiplomacy"] = "Goblin Diplomacy"; QuestTitle2["GowerQuest"] = "Gower Quest"; QuestTitle2["GreatEggSpectations"] = "Great Egg-spectations"; QuestTitle2["GrimTales"] = "Grim Tales"; QuestTitle2["GunnarsGround"] = "Gunnar's Ground"; QuestTitle2["GuysAndDolls"] = "Guys and Dolls"; QuestTitle2["Harbinger"] = "Harbinger (miniquest)"; QuestTitle2["HauntedMine"] = "Haunted Mine"; QuestTitle2["HazeelCult"] = "Hazeel Cult"; QuestTitle2["HeadOfTheFamily"] = "Head of the Family (miniquest)"; QuestTitle2["HeartOfStone"] = "Heart of Stone"; QuestTitle2["Heartstealer"] = "Heartstealer"; QuestTitle2["HelpingLaniakea"] = "Helping Laniakea"; QuestTitle2["HeroesQuest"] = "Heroes' Quest"; QuestTitle2["HerosWelcome"] = "Hero's Welcome"; QuestTitle2["HolyGrail"] = "Holy Grail"; QuestTitle2["HopespearsWill"] = "Hopespear's Will (miniquest)"; QuestTitle2["HorrorfromTheDeep"] = "Horror from the Deep"; QuestTitle2["HousingOfParliament"] = "Housing of Parliament"; QuestTitle2["HuntforRedRaktuber"] = "Hunt for Red Raktuber"; QuestTitle2["IcthlarinsLittleHelper"] = "Icthlarin's Little Helper"; QuestTitle2["ImpCatcher"] = "Imp Catcher"; QuestTitle2["ImpressingTheLocals"] = "Impressing the Locals"; QuestTitle2["InAidOfTheMyreque"] = "In Aid of the Myreque"; QuestTitle2["InMemoryOfTheMyreque"] = "In Memory of the Myreque (miniquest)"; QuestTitle2["InPyreNeed"] = "In Pyre Need"; QuestTitle2["InSearchOfTheMyreque"] = "In Search of the Myreque"; QuestTitle2["ItsSnowBother"] = "It's Snow Bother"; QuestTitle2["JedHunter"] = "Jed Hunter (miniquest)"; QuestTitle2["JunglePotion"] = "Jungle Potion"; QuestTitle2["KennithsConcerns"] = "Kennith's Concerns"; QuestTitle2["KiliRow"] = "Kili Row"; QuestTitle2["KindredSpirits"] = "Kindred Spirits"; QuestTitle2["KingOfTheDwarves"] = "King of the Dwarves"; QuestTitle2["KingsRansom"] = "King's Ransom"; QuestTitle2["KoscheisTroubles"] = "Koschei's Troubles (miniquest)"; QuestTitle2["LairOfTarnRazorlor"] = "Lair of Tarn Razorlor (miniquest)"; QuestTitle2["LandOfTheGoblins"] = "Land of the Goblins"; QuestTitle2["LegacyOfSeergaze"] = "Legacy of Seergaze"; QuestTitle2["LegendsQuest"] = "Legends' Quest"; QuestTitle2["LetThemEatPie"] = "Let Them Eat Pie"; QuestTitle2["LostCity"] = "Lost City"; QuestTitle2["LostHerMarbles"] = "Lost Her Marbles (miniquest)"; QuestTitle2["LoveStory"] = "Love Story"; QuestTitle2["LunarDiplomacy"] = "Lunar Diplomacy"; QuestTitle2["MahjarratMemories"] = "Mahjarrat Memories (miniquest)"; QuestTitle2["MakingHistory"] = "Making History"; QuestTitle2["MeetingHistory"] = "Meeting History"; QuestTitle2["MerlinsCrystal"] = "Merlin's Crystal"; QuestTitle2["MissingMyMummy"] = "Missing My Mummy"; QuestTitle2["MissingPresumedDeath"] = "Missing, Presumed Death"; QuestTitle2["MonkeyMadness"] = "Monkey Madness"; QuestTitle2["MonksFriend"] = "Monk's Friend"; QuestTitle2["MountainDaughter"] = "Mountain Daughter"; QuestTitle2["MourningsEndPartI"] = "Mourning's End Part I"; QuestTitle2["MourningsEndPartII"] = "Mourning's End Part II"; QuestTitle2["MurderMystery"] = "Murder Mystery"; QuestTitle2["MurderOnTheBorder"] = "Murder on the Border"; QuestTitle2["MyArmsBigAdventure"] = "My Arm's Big Adventure"; QuestTitle2["MythsOfTheWhiteLands"] = "Myths of the White Lands"; QuestTitle2["Nadir"] = "Nadir (saga)"; QuestTitle2["NatureSpirit"] = "Nature Spirit"; QuestTitle2["Necromancy"] = "Necromancy!"; QuestTitle2["NewFoundations"] = "New Foundations"; QuestTitle2["NomadsElegy"] = "Nomad's Elegy"; QuestTitle2["NomadsRequiem"] = "Nomad's Requiem"; QuestTitle2["ObservatoryQuest"] = "Observatory Quest"; QuestTitle2["OdeofTheDevourer"] = "Ode of the Devourer"; QuestTitle2["OlafsQuest"] = "Olaf's Quest"; QuestTitle2["OnceUponaSlime"] = "Once Upon a Slime"; QuestTitle2["OnceUponaTimeInGielinor"] = "Once Upon a Time in Gielinor"; QuestTitle2["OneFootInTheGrave"] = "One Foot in the Grave (miniquest)"; QuestTitle2["OneOfAKind"] = "One of a Kind"; QuestTitle2["OnePiercingNote"] = "One Piercing Note"; QuestTitle2["OneSmallFavour"] = "One Small Favour"; QuestTitle2["OsseousRex"] = "Osseous Rex"; QuestTitle2["OurManInTheNorth"] = "Our Man in the North"; QuestTitle2["PerilsOfIceMountain"] = "Perils of Ice Mountain"; QuestTitle2["PharaohsFolly"] = "Pharaoh's Folly"; QuestTitle2["PhiteClub"] = "'Phite Club"; QuestTitle2["PiecesOfHate"] = "Pieces of Hate"; QuestTitle2["PiratesTreasure"] = "Pirate's Treasure"; QuestTitle2["PlagueCity"] = "Plague City"; QuestTitle2["PlaguesEnd"] = "Plague's End"; QuestTitle2["PriestInPeril"] = "Priest in Peril"; QuestTitle2["PurpleCat"] = "Purple Cat (miniquest)"; QuestTitle2["QuietBeforeTheSwarm"] = "Quiet Before the Swarm"; QuestTitle2["RagAndBoneMan"] = "Rag and Bone Man"; QuestTitle2["RakshaTheShadowColossus"] = "Raksha, the Shadow Colossus"; QuestTitle2["RatCatchers"] = "Rat Catchers"; QuestTitle2["RebuildingEdgeville"] = "Rebuilding Edgeville (miniquest)"; QuestTitle2["RecipeforDisaster"] = "Recipe for Disaster"; QuestTitle2["RecipeforDisasterAnotherCooksQuest"] = "Recipe for Disaster: Another Cook's Quest"; QuestTitle2["RecipeforDisasterDefeatingTheCulinaromancer"] = "Recipe for Disaster: Defeating the Culinaromancer"; QuestTitle2["RecipeforDisasterFreeingEvilDave"] = "Recipe for Disaster: Freeing Evil Dave"; QuestTitle2["RecipeforDisasterFreeingKingAwowogei"] = "Recipe for Disaster: Freeing King Awowogei"; QuestTitle2["RecipeforDisasterFreeingPiratePete"] = "Recipe for Disaster: Freeing Pirate Pete"; QuestTitle2["RecipeforDisasterFreeingSirAmikVarze"] = "Recipe for Disaster: Freeing Sir Amik Varze"; QuestTitle2["RecipeforDisasterFreeingSkrachUglogwee"] = "Recipe for Disaster: Freeing Skrach Uglogwee"; QuestTitle2["RecipeforDisasterFreeingTheGoblinGenerals"] = "Recipe for Disaster: Freeing the Goblin Generals"; QuestTitle2["RecipeforDisasterFreeingTheLumbridgeSage"] = "Recipe for Disaster: Freeing the Lumbridge Sage"; QuestTitle2["RecipeforDisasterFreeingTheMountainDwarf"] = "Recipe for Disaster: Freeing the Mountain Dwarf"; QuestTitle2["RecruitmentDrive"] = "Recruitment Drive"; QuestTitle2["Regicide"] = "Regicide"; QuestTitle2["RemainsOfTheNecrolord"] = "Remains of the Necrolord"; QuestTitle2["RequiemForADragon"] = "Requiem for a Dragon"; QuestTitle2["RitualOfTheMahjarrat"] = "Ritual of the Mahjarrat"; QuestTitle2["RiverOfBlood"] = "River of Blood"; QuestTitle2["RockingOut"] = "Rocking Out"; QuestTitle2["RovingElves"] = "Roving Elves"; QuestTitle2["RoyalTrouble"] = "Royal Trouble"; QuestTitle2["RumDeal"] = "Rum Deal"; QuestTitle2["RuneMechanics"] = "Rune Mechanics"; QuestTitle2["RuneMemories"] = "Rune Memories"; QuestTitle2["RuneMysteries"] = "Rune Mysteries"; QuestTitle2["RuneMythos"] = "Rune Mythos"; QuestTitle2["SaltInTheWound"] = "Salt in the Wound"; QuestTitle2["ScorpionCatcher"] = "Scorpion Catcher"; QuestTitle2["SeaSlug"] = "Sea Slug"; QuestTitle2["ShadesOfMortton"] = "Shades of Mort'ton"; QuestTitle2["ShadowOfTheStorm"] = "Shadow of the Storm"; QuestTitle2["SheepHerder"] = "Sheep Herder"; QuestTitle2["SheepShearer"] = "Sheep Shearer (miniquest)"; QuestTitle2["ShieldOfArrav"] = "Shield of Arrav"; QuestTitle2["ShiloVillage"] = "Shilo Village"; QuestTitle2["SinsOfTheFather"] = "Sins of the Father (miniquest)"; QuestTitle2["SliskesEndgame"] = "Sliske's Endgame"; QuestTitle2["SmokingKills"] = "Smoking Kills"; QuestTitle2["SomeLikeItCold"] = "Some Like It Cold"; QuestTitle2["SongfromTheDepths"] = "Song from the Depths"; QuestTitle2["SoulSearching"] = "Soul Searching"; QuestTitle2["SpiritOfSummer"] = "Spirit of Summer"; QuestTitle2["SpiritsOfTheElid"] = "Spirits of the Elid"; QuestTitle2["SpiritualEnlightenment"] = "Spiritual Enlightenment (miniquest)"; QuestTitle2["StellarBorn"] = "Stellar Born"; QuestTitle2["StolenHearts"] = "Stolen Hearts"; QuestTitle2["Succession"] = "Succession"; QuestTitle2["SummersEnd"] = "Summer's End"; QuestTitle2["SwanSong"] = "Swan Song"; QuestTitle2["SweptAway"] = "Swept Away"; QuestTitle2["TaiBwoWannaiTrio"] = "Tai Bwo Wannai Trio"; QuestTitle2["TalesOfNomad"] = "Tales of Nomad (miniquest)"; QuestTitle2["TalesOfTheGodWars"] = "Tales of the God Wars (miniquest)"; QuestTitle2["TearsOfGuthix"] = "Tears of Guthix"; QuestTitle2["TempleOfIkov"] = "Temple of Ikov"; QuestTitle2["ThatOldBlackMagic"] = "That Old Black Magic"; QuestTitle2["TheBloodPact"] = "The Blood Pact"; QuestTitle2["TheBranchesOfDarkmeyer"] = "The Branches of Darkmeyer"; QuestTitle2["TheBrinkOfExtinction"] = "The Brink of Extinction"; QuestTitle2["TheChosenCommander"] = "The Chosen Commander"; QuestTitle2["TheCurseOfArrav"] = "The Curse of Arrav"; QuestTitle2["TheCurseOfZaros"] = "The Curse of Zaros (miniquest)"; QuestTitle2["TheDarknessOfHallowvale"] = "The Darkness of Hallowvale"; QuestTitle2["TheDeathOfChivalry"] = "The Death of Chivalry"; QuestTitle2["TheDigSite"] = "The Dig Site"; QuestTitle2["TheElderKiln"] = "The Elder Kiln"; QuestTitle2["TheEyesOfGlouphrie"] = "The Eyes of Glouphrie"; QuestTitle2["TheFeud"] = "The Feud"; QuestTitle2["TheFiremakersCurse"] = "The Firemaker's Curse"; QuestTitle2["TheFremennikIsles"] = "The Fremennik Isles"; QuestTitle2["TheFremennikTrials"] = "The Fremennik Trials"; QuestTitle2["TheGeneralsShadow"] = "The General's Shadow (miniquest)"; QuestTitle2["TheGiantDwarf"] = "The Giant Dwarf"; QuestTitle2["TheGolem"] = "The Golem"; QuestTitle2["TheGrandTree"] = "The Grand Tree"; QuestTitle2["TheGreatBrainRobbery"] = "The Great Brain Robbery"; QuestTitle2["TheHandInTheSand"] = "The Hand in the Sand"; QuestTitle2["TheHuntforSurok"] = "The Hunt for Surok (miniquest)"; QuestTitle2["TheJackOfSpades"] = "The Jack of Spades"; QuestTitle2["TheKnightsSword"] = "The Knight's Sword"; QuestTitle2["TheLightWithin"] = "The Light Within"; QuestTitle2["TheLordOfVampyrium"] = "The Lord of Vampyrium"; QuestTitle2["TheLostToys"] = "The Lost Toys (miniquest)"; QuestTitle2["TheLostTribe"] = "The Lost Tribe"; QuestTitle2["TheMightyFall"] = "The Mighty Fall"; QuestTitle2["TheNeedleSkips"] = "The Needle Skips"; QuestTitle2["ThePathOfGlouphrie"] = "The Path of Glouphrie"; QuestTitle2["ThePrisonerOfGlouphrie"] = "The Prisoner of Glouphrie"; QuestTitle2["TheRestlessGhost"] = "The Restless Ghost"; QuestTitle2["TheSlugMenace"] = "The Slug Menace"; QuestTitle2["TheSpiritOfWar"] = "The Spirit of War"; QuestTitle2["TheTaleOfTheMuspah"] = "The Tale of the Muspah"; QuestTitle2["TheTempleatSenntisten"] = "The Temple at Senntisten"; QuestTitle2["TheTouristTrap"] = "The Tourist Trap"; QuestTitle2["TheVaultOfShadows"] = "The Vault of Shadows"; QuestTitle2["TheVoidStaresBack"] = "The Void Stares Back"; QuestTitle2["TheWorldWakes"] = "The World Wakes"; QuestTitle2["ThokItToEm"] = "Thok It To 'Em (saga)"; QuestTitle2["ThokYourBlockOff"] = "Thok Your Block Off (saga)"; QuestTitle2["ThreesCompany"] = "Three's Company (saga)"; QuestTitle2["ThroneOfMiscellania"] = "Throne of Miscellania"; QuestTitle2["TokTzKetDill"] = "TokTz-Ket-Dill"; QuestTitle2["TomesOfTheWarlock"] = "Tomes of the Warlock"; QuestTitle2["TortleCombat"] = "Tortle Combat (miniquest)"; QuestTitle2["TowerOfLife"] = "Tower of Life"; QuestTitle2["TreeGnomeVillage"] = "Tree Gnome Village"; QuestTitle2["TribalTotem"] = "Tribal Totem"; QuestTitle2["TrollRomance"] = "Troll Romance"; QuestTitle2["TrollStronghold"] = "Troll Stronghold"; QuestTitle2["TuaiLeitsOwn"] = "Tuai Leit's Own (miniquest)"; QuestTitle2["TwilightOfTheGods"] = "Twilight of the Gods"; QuestTitle2["UndergroundPass"] = "Underground Pass"; QuestTitle2["UnstableFoundations"] = "Unstable Foundations"; QuestTitle2["UnwelcomeGuests"] = "Unwelcome Guests"; QuestTitle2["VampyreSlayer"] = "Vampyre Slayer"; QuestTitle2["Vengeance"] = "Vengeance (saga)"; QuestTitle2["VesselOfTheHarbinger"] = "Vessel of the Harbinger"; QuestTitle2["VioletisBlue"] = "Violet is Blue"; QuestTitle2["VioletisBlueToo"] = "Violet is Blue Too"; QuestTitle2["WanderingGaal"] = "Wandering Ga'al (miniquest)"; QuestTitle2["Wanted"] = "Wanted!"; QuestTitle2["Watchtower"] = "Watchtower"; QuestTitle2["WaterfallQuest"] = "Waterfall Quest"; QuestTitle2["WhatLiesBelow"] = "What Lies Below"; QuestTitle2["WhatsMineisYours"] = "What's Mine is Yours"; QuestTitle2["WhileGuthixSleeps"] = "While Guthix Sleeps"; QuestTitle2["WitchsHouse"] = "Witch's House"; QuestTitle2["WitchsPotion"] = "Witch's Potion (miniquest)"; QuestTitle2["WithinTheLight"] = "Within the Light"; QuestTitle2["WolfWhistle"] = "Wolf Whistle"; QuestTitle2["YouAreIt"] = "You Are It"; QuestTitle2["ZogreFleshEaters"] = "Zogre Flesh Eaters"; return QuestTitle2; })(QuestTitle || {}); async function questDetails({ name, abortSignal }) { const urlSearchParams = new URLSearchParams(); urlSearchParams.set("user", name); const url = `https://apps.runescape.com/runemetrics/quests?${urlSearchParams}`; const response = await makeRequest(url, abortSignal); if (!response.ok) { throw new RuneScapeAPIError("Error fetching quest data.", response.status, url); } const body = await response.json(); const { quests, loggedIn } = body; return { quests, loggedIn: loggedIn === "true" }; } __name(questDetails, "questDetails"); // source/travelling-merchant.ts var Item = /* @__PURE__ */ ((Item2) => { Item2["AdvancedPulseCore"] = "Advanced pulse core"; Item2["AnimaCrystal"] = "Anima crystal"; Item2["BarrelOfBait"] = "Barrel of bait"; Item2["BrokenFishingRod"] = "Broken fishing rod"; Item2["CrystalTriskelion"] = "Crystal triskelion"; Item2["DDTokenDaily"] = "D&D token (daily)"; Item2["DDTokenMonthly"] = "D&D token (monthly)"; Item2["DDTokenWeekly"] = "D&D token (weekly)"; Item2["DeathtouchedDart"] = "Deathtouched dart"; Item2["DragonkinLamp"] = "Dragonkin Lamp"; Item2["DungeoneeringWildcard"] = "Dungeoneering Wildcard"; Item2["GiftForTheReaper"] = "Gift for the Reaper"; Item2["GoebieBurialCharm"] = "Goebie burial charm"; Item2["HarmonicDust"] = "Harmonic dust"; Item2["HornOfHonour"] = "Horn of honour"; Item2["LargeGoebieBurialCharm"] = "Large goebie burial charm"; Item2["LividPlant"] = "Livid plant"; Item2["MenaphiteGiftOfferingLarge"] = "Menaphite gift offering (large)"; Item2["MenaphiteGiftOfferingMedium"] = "Menaphite gift offering (medium)"; Item2["MenaphiteGiftOfferingSmall"] = "Menaphite gift offering (small)"; Item2["MessageInABottle"] = "Message in a bottle"; Item2["SacredClay"] = "Sacred clay"; Item2["ShatteredAnima"] = "Shattered anima"; Item2["SilverhawkDown"] = "Silverhawk down"; Item2["SlayerVIPCoupon"] = "Slayer VIP coupon"; Item2["SmallGoebieBurialCharm"] = "Small goebie burial charm"; Item2["StarvedAncientEffigy"] = "Starved ancient effigy"; Item2["Taijitu"] = "Taijitu"; Item2["TangledFishbowl"] = "Tangled fishbowl"; Item2["UnfocusedDamageEnhancer"] = "Unfocused damage enhancer"; Item2["UnfocusedRewardEnhancer"] = "Unfocused reward enhancer"; Item2["UnstableAirRune"] = "Unstable air rune"; return Item2; })(Item || {}); var SLOT_1_AND_2 = [ "Gift for the Reaper" /* GiftForTheReaper */, "Broken fishing rod" /* BrokenFishingRod */, "Barrel of bait" /* BarrelOfBait */, "Anima crystal" /* AnimaCrystal */, "Small goebie burial charm" /* SmallGoebieBurialCharm */, "Goebie burial charm" /* GoebieBurialCharm */, "Menaphite gift offering (small)" /* MenaphiteGiftOfferingSmall */, "Menaphite gift offering (medium)" /* MenaphiteGiftOfferingMedium */, "Shattered anima" /* ShatteredAnima */, "D&D token (daily)" /* DDTokenDaily */, "Sacred clay" /* SacredClay */, "Livid plant" /* LividPlant */, "Slayer VIP coupon" /* SlayerVIPCoupon */, "Silverhawk down" /* SilverhawkDown */, "Unstable air rune" /* UnstableAirRune */, "Advanced pulse core" /* AdvancedPulseCore */, "Tangled fishbowl" /* TangledFishbowl */, "Unfocused damage enhancer" /* UnfocusedDamageEnhancer */, "Horn of honour" /* HornOfHonour */ ]; var SLOTS = { 1: SLOT_1_AND_2, 2: SLOT_1_AND_2, 3: [ "Taijitu" /* Taijitu */, "Large goebie burial charm" /* LargeGoebieBurialCharm */, "Menaphite gift offering (large)" /* MenaphiteGiftOfferingLarge */, "D&D token (weekly)" /* DDTokenWeekly */, "D&D token (monthly)" /* DDTokenMonthly */, "Dungeoneering Wildcard" /* DungeoneeringWildcard */, "Message in a bottle" /* MessageInABottle */, "Crystal triskelion" /* CrystalTriskelion */, "Starved ancient effigy" /* StarvedAncientEffigy */, "Deathtouched dart" /* DeathtouchedDart */, "Dragonkin Lamp" /* DragonkinLamp */, "Harmonic dust" /* HarmonicDust */, "Unfocused reward enhancer" /* UnfocusedRewardEnhancer */ ] }; function getSlots(runedate2, n1, n2) { const seed = runedate2 * 2 ** 32 + runedate2 % n1; return nextInt(BigInt(Math.trunc(seed)), n2); } __name(getSlots, "getSlots"); function slot1(runedate2) { const index = Number(getSlots(runedate2, 3, 19n)); return SLOTS[1][index]; } __name(slot1, "slot1"); function slot2(runedate2) { const index = Number(getSlots(runedate2, 8, 19n)); return SLOTS[2][index]; } __name(slot2, "slot2"); function slot3(runedate2) { const index = Number(getSlots(runedate2, 5, 13n)); return SLOTS[3][index]; } __name(slot3, "slot3"); function stock(timestamp) { const date = new Date(timestamp); date.setUTCHours(0, 0, 0, 0); const currentRunedate = runedate(date.getTime()); return [slot1(currentRunedate), slot2(currentRunedate), slot3(currentRunedate)]; } __name(stock, "stock"); // source/wilderness-flash-event.ts var INITIAL_TIMESTAMP2 = Date.UTC(2024, 1, 5, 12); var WildernessFlashEvent = /* @__PURE__ */ ((WildernessFlashEvent2) => { WildernessFlashEvent2["KingBlackDragonRampage"] = "King Black Dragon Rampage"; WildernessFlashEvent2["ForgottenSoldiers"] = "Forgotten Soldiers"; WildernessFlashEvent2["SurprisingSeedlings"] = "Surprising Seedlings"; WildernessFlashEvent2["HellhoundPack"] = "Hellhound Pack"; WildernessFlashEvent2["InfernalStar"] = "Infernal Star"; WildernessFlashEvent2["LostSouls"] = "Lost Souls"; WildernessFlashEvent2["RamokeeIncursion"] = "Ramokee Incursion"; WildernessFlashEvent2["DisplacedEnergy"] = "Displaced Energy"; WildernessFlashEvent2["EvilBloodwoodTree"] = "Evil Bloodwood Tree"; WildernessFlashEvent2["SpiderSwarm"] = "Spider Swarm"; WildernessFlashEvent2["UnnaturalOutcrop"] = "Unnatural Outcrop"; WildernessFlashEvent2["StrykeTheWyrm"] = "Stryke the Wyrm"; WildernessFlashEvent2["DemonStragglers"] = "Demon Stragglers"; WildernessFlashEvent2["ButterflySwarm"] = "Butterfly Swarm"; return WildernessFlashEvent2; })(WildernessFlashEvent || {}); var WILDERNESS_FLASH_EVENTS = Object.values(WildernessFlashEvent); var WILDERNESS_FLASH_EVENTS_LENGTH = WILDERNESS_FLASH_EVENTS.length; function wildernessFlashEvent(timestamp) { const hoursElapsed = Math.floor((timestamp - INITIAL_TIMESTAMP2) / 1e3 / 60 / 60); return WILDERNESS_FLASH_EVENTS[hoursElapsed % WILDERNESS_FLASH_EVENTS_LENGTH]; } __name(wildernessFlashEvent, "wildernessFlashEvent"); // source/wilderness-warbands.ts function wildernessWarbands(timestamp) { const date = new Date(timestamp); const start = new Date(date); start.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 6) % 7); start.setUTCHours(2, 0, 0, 0)