UNPKG

osrs-tools

Version:

A comprehensive TypeScript library for Old School RuneScape (OSRS) data and utilities, including quest data, skill requirements, and game item information

365 lines (364 loc) 12.3 kB
/** * NPC Drop Implementation Utilities * Helpers for efficiently implementing drops across all NPCs * * Usage: * - Use templates for quick NPC generation * - Leverage shared drop definitions * - Validate drops before committing * - Batch update similar NPCs */ import { NpcDrop } from "../NpcDrop"; import { CompleteDropTable, WeightedDropTable } from "../ComplexDropSystems"; /** * ============================================================================ * SECTION 1: Common Drop Definitions (DRY Principle) * ============================================================================ */ /** * Standard drops used by many NPCs * Avoids duplication and keeps data consistent */ export const STANDARD_DROPS = { // Bones by type bones: new NpcDrop("Bones", 1, "Always"), bigBones: new NpcDrop("Big Bones", 1, "Always"), babyBones: new NpcDrop("Baby Dragon Bones", 1, "Always"), // Common currency coinsSmall: () => new NpcDrop("Coins", [1, 50], "Always"), coinsMedium: () => new NpcDrop("Coins", [50, 200], "Always"), coinsLarge: () => new NpcDrop("Coins", [200, 500], "Always"), // Common tertiary drops bones_tertiary: new NpcDrop("Bones", 1, "1/32"), ashesGrimy: new NpcDrop("Ashes", [1, 2], "1/32"), clueScrollEasy: new NpcDrop("Clue Scroll (Easy)", 1, "1/64"), clueScrollMedium: new NpcDrop("Clue Scroll (Medium)", 1, "1/128"), }; /** * ============================================================================ * SECTION 2: Drop Probability Helpers * ============================================================================ */ /** * Common drop rate constants for easy reference * Makes probabilities more readable in code */ export const DROP_RATES = { // Always ALWAYS: "Always", // Common (player farming areas) COMMON_1_2: "1/2", // 50% COMMON_1_4: "1/4", // 25% COMMON_1_8: "1/8", // 12.5% // Frequent (Slayer creatures) FREQUENT_1_16: "1/16", // 6.25% FREQUENT_1_32: "1/32", // 3.125% FREQUENT_1_64: "1/64", // 1.56% // Uncommon (Tertiary drops) UNCOMMON_1_128: "1/128", // 0.78% UNCOMMON_1_256: "1/256", // 0.39% // Rare (Boss unique) RARE_1_512: "1/512", // 0.195% RARE_1_1024: "1/1024", // 0.0976% // Very rare VERY_RARE_1_5000: "1/5000", }; /** * Create a simple NPC drop list from template */ export function createSimpleNPCDrops(template) { const drops = []; if (template.guaranteed) { drops.push(...template.guaranteed); } if (template.tertiary) { drops.push(...template.tertiary); } if (template.questOnly) { drops.push(...template.questOnly); } return drops; } /** * Create a boss drop table from template */ export function createBossNPCDrops(template) { const table = new CompleteDropTable("Boss"); // Primary roll if (template.primaryDrops?.length ?? 0 > 0) { table.addSimpleRoll("Primary", template.primaryDrops); } // Secondary roll if ((template.secondaryDrops?.length ?? 0) > 0) { table.addSimpleRoll("Secondary", template.secondaryDrops); } // Tertiary roll if ((template.tertiaryDrops?.length ?? 0) > 0) { table.addSimpleRoll("Tertiary", template.tertiaryDrops); } // Rare/Unique drops if (template.rareDrops && template.rareDrops.length > 0) { const rareTable = new WeightedDropTable(); for (const rare of template.rareDrops) { rareTable.addDrop(rare.item, rare.weight); } table.addWeightedRoll("Unique", rareTable, template.uniqueRate || "1/512"); } return table; } /** * ============================================================================ * SECTION 4: NPC Category Builders * ============================================================================ */ /** * Build drops for dragon types * Dragons share similar drop patterns with variations */ export function createDragonDrops(options) { const drops = []; // Tier determines bone type and drop quantities const boneTypes = { baby: "Baby Dragon Bones", chromatic: "Dragon Bones", metallic: "Dragon Bones", elder: "Elder Dragon Bones", }; // All dragons drop bones drops.push(new NpcDrop(boneTypes[options.tier], 1, "Always")); // Coins const coinRange = { baby: [50, 150], chromatic: [100, 250], metallic: [150, 400], elder: [500, 1000], }; drops.push(new NpcDrop("Coins", coinRange[options.tier], "Always")); // Tertiary drops drops.push(new NpcDrop("Dragon Scale", [1, 3], DROP_RATES.FREQUENT_1_32), new NpcDrop("Dragon Tooth", [1, 2], DROP_RATES.UNCOMMON_1_256)); // Unique drops for certain dragons if (options.hasUniqueDrops) { drops.push(new NpcDrop("Dragon Dagger", 1, DROP_RATES.RARE_1_512)); } return drops; } /** * Build drops for demon types * Most demons have similar drops with rarity variations */ export function createDemonDrops(options) { const drops = []; // All demons drop bones drops.push(STANDARD_DROPS.bones); // Coins based on tier const coinAmounts = { lesser: [0, 50], greater: [25, 75], black: [50, 150], abyssal: [100, 250], }; drops.push(new NpcDrop("Coins", coinAmounts[options.tier], "Always")); // Tertiary drops - all demons share these drops.push(new NpcDrop("Ashes", [1, 2], DROP_RATES.FREQUENT_1_32), new NpcDrop("Clue Scroll (Medium)", 1, DROP_RATES.UNCOMMON_1_128), new NpcDrop("Ancient Shard", 1, DROP_RATES.RARE_1_512)); return drops; } /** * Build drops for various rats * Rats have consistent patterns but vary in drop value */ export function createRatDrops(options) { const drops = []; // Rats drop bones drops.push(STANDARD_DROPS.bones); // Size determines coin drops const coinDrops = { small: [1, 10], giant: [20, 50], behemoth: [50, 100], }; drops.push(new NpcDrop("Coins", coinDrops[options.size], "Always")); // All rats can drop cheese and clue scrolls drops.push(new NpcDrop("Cheese", [0, 1], DROP_RATES.FREQUENT_1_64), new NpcDrop("Clue Scroll (Easy)", 1, DROP_RATES.UNCOMMON_1_128)); // Crypt rats have special drops if (options.area === "crypt") { drops.push(new NpcDrop("Ashes", [1, 2], DROP_RATES.FREQUENT_1_32)); } return drops; } /** * ============================================================================ * SECTION 5: Shared Drop Table Registry * ============================================================================ */ /** * Centralized location for shared drop tables * Prevents duplication across similar NPCs */ export const SharedDropTables = { // GWD Bosses share rare tables gwd: { rareTable: null, initialize: function () { if (this.rareTable) return this.rareTable; this.rareTable = new WeightedDropTable(); this.rareTable.addDrop(new NpcDrop("Bandos Godsword", 1, "Always"), 1); this.rareTable.addDrop(new NpcDrop("Other GWD Unique", 1, "Always"), 1); return this.rareTable; }, }, // Boss unique table bossUniqueSmallTable: null, // Initialize shared tables on first use initializeAll: function () { this.gwd.initialize(); // Add more as needed }, }; /** * Validate a complete drop table */ export function validateNPCDrops(npcName, drops, options) { const errors = []; const warnings = []; // Check for empty drops if (drops.length === 0) { warnings.push(`No drops defined for ${npcName}`); } // Check for duplicates if (!options?.allowDuplicates) { const itemIds = drops.map((d) => d.itemId); const duplicates = itemIds.filter((id, index) => itemIds.indexOf(id) !== index); if (duplicates.length > 0) { errors.push(`Duplicate items found: ${[...new Set(duplicates)].join(", ")}`); } } // Check that at least one guaranteed drop exists for combat NPCs const hasGuaranteed = drops.some((d) => d.chance.isGuaranteed); if (!hasGuaranteed && npcName !== "Passive") { warnings.push(`No guaranteed drops for ${npcName} (combat NPC should have at least one)`); } // Basic sanity check on drop rates for (const drop of drops) { if (drop.chance.fraction > 1 || drop.chance.fraction < 0) { errors.push(`Invalid drop rate for ${drop.itemId}: ${drop.chance.asRatioString}`); } } return { valid: errors.length === 0, errors, warnings, }; } /** * ============================================================================ * SECTION 7: Implementation Helpers * ============================================================================ */ /** * Get all NPCs that should have drops by category */ export function getNPCsByCategory(category) { // This would be populated from actual NPC data // Just a template for the concept const categories = { boss: ["Zulrah", "General Graardor", "K'ril Tsutsaroth", "Zilyana", "Kree'arra", "Kraken", "Cerberus", "Kalphite Queen", "King Black Dragon", "Vorkath"], combat: ["Demon", "Dragon", "Giant", "Vampire", "Zombie", "Skeleton"], slayer: ["Aberrant Spectre", "Abyssal Demon", "Blue Dragon", "Cave Kraken"], animal: ["Chicken", "Duck", "Cow", "Sheep", "Penguin"], other: ["Any specialized creatures"], }; return categories[category] || []; } /** * Create implementation checklist for a batch of NPCs */ export function createImplementationChecklist(npcs) { let checklist = "# Implementation Checklist\n\n"; for (const npc of npcs) { checklist += `- [ ] ${npc}\n`; checklist += ` - [ ] Research wiki\n`; checklist += ` - [ ] Define drops\n`; checklist += ` - [ ] Validate\n`; checklist += ` - [ ] Tests pass\n`; } return checklist; } /** * ============================================================================ * SECTION 8: Quick Start Examples * ============================================================================ */ /** * Example 1: Quick implementation of a simple combat NPC */ export const EXAMPLE_SimpleCombat = { name: "Demon", drops: createSimpleNPCDrops({ guaranteed: [STANDARD_DROPS.bones, new NpcDrop("Coins", [50, 100], "Always")], tertiary: [new NpcDrop("Ashes", [1, 2], DROP_RATES.FREQUENT_1_32)], }), }; /** * Example 2: Quick implementation of a dragon type */ export const EXAMPLE_Dragon = { name: "Green Dragon", drops: createDragonDrops({ tier: "chromatic", hasUniqueDrops: false, }), }; /** * Example 3: Quick implementation of a boss */ export function EXAMPLE_CreateBoss() { return createBossNPCDrops({ primaryDrops: [new NpcDrop("Boss Scale", [20, 40], "Always"), new NpcDrop("Boss Hide", [10, 20], "Always")], secondaryDrops: [new NpcDrop("Herb", [2, 5], "Always")], rareDrops: [ { item: new NpcDrop("Boss Unique 1", 1, "Always"), weight: 1, }, { item: new NpcDrop("Boss Unique 2", 1, "Always"), weight: 1, }, ], uniqueRate: DROP_RATES.RARE_1_512, }); } /** * ============================================================================ * USAGE GUIDE * ============================================================================ * * For Simple NPCs: * ================ * const drops = createSimpleNPCDrops({ * guaranteed: [STANDARD_DROPS.bones, coinDrop], * tertiary: [clueScroll], * }); * * For Dragons: * ============ * const drops = createDragonDrops({ tier: 'baby', hasUniqueDrops: false }); * * For Demons: * =========== * const drops = createDemonDrops({ tier: 'lesser' }); * * For Bosses: * =========== * const table = createBossNPCDrops({ * primaryDrops: [...], * rareDrops: [...], * uniqueRate: DROP_RATES.RARE_1_512, * }); * * For Validation: * ================ * const result = validateNPCDrops('Boss Name', drops); * if (!result.valid) console.error(result.errors); */