diff --git a/src/index.ts b/src/index.ts index b7e7bc3e1..4cb6fcc1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,9 @@ +import "./structures/Items"; import { EItem } from "./EItem"; import { EMonster } from "./EMonster"; import * as constants from "./constants"; import { MonsterSlayerMaster } from "./meta/monsterData"; -import { MonsterKillOptions } from "./meta/types"; +import type { MonsterKillOptions } from "./meta/types"; import * as Misc from "./simulation/misc"; import Monsters from "./simulation/monsters/index"; import Openables from "./simulation/openables/index"; @@ -23,7 +24,7 @@ export { Items, LootTable, Misc, - MonsterKillOptions, + type MonsterKillOptions, Monsters, MonsterSlayerMaster, Openables, @@ -47,4 +48,4 @@ export * from "./simulation/openables"; export * from "./simulation/misc"; export * from "./simulation/openables/Implings"; export * from "./meta/monsterData"; -export * from "./simulation/subtables/index"; +export * from "./simulation/subtables/index"; \ No newline at end of file diff --git a/src/structures/Bank.ts b/src/structures/Bank.ts index 980b5252b..44f4ff2fe 100644 --- a/src/structures/Bank.ts +++ b/src/structures/Bank.ts @@ -105,8 +105,7 @@ export default class Bank { public addItem(item: number, quantity = 1): this { if (this.frozen) throw new Error(frozenErrorStr); if (quantity < 1) return this; - const current = this.map.get(item) ?? 0; - this.map.set(item, current + quantity); + this.map.set(item, (this.map.get(item) ?? 0) + quantity); return this; } diff --git a/src/structures/Items.ts b/src/structures/Items.ts index 2d77ab41d..e01ab033a 100644 --- a/src/structures/Items.ts +++ b/src/structures/Items.ts @@ -1,9 +1,8 @@ import deepMerge from "deepmerge"; -import _items from "../data/items/item_data.json"; +import _items from "../data/items/item_data.json" assert { type: "json" }; import type { Item, ItemID } from "../meta/types"; -import { cleanString } from "../util/cleanString"; -import Collection from "./Collection"; +import { Collection } from "./Collection"; // @ts-ignore asdf const items = _items as Record; @@ -50,7 +49,7 @@ export const USELESS_ITEMS = [ 23_814, 23_815, 23_816, 23_817, ]; -class Items extends Collection { +class Items extends Collection { public get(item: ItemResolvable): Item | undefined { const id = this.resolveID(item); if (typeof id === "undefined") return undefined; @@ -71,8 +70,7 @@ class Items extends Collection { } if (typeof input === "string") { - const cleanName = cleanString(input); - return itemNameMap.get(cleanName); + return itemNameMap.get(input); } return undefined; @@ -86,8 +84,9 @@ for (const [id, item] of Object.entries(items)) { if (USELESS_ITEMS.includes(numID)) continue; itemsExport.set(numID, item); - const cleanName = cleanString(item.name); - if (!itemNameMap.has(cleanName)) itemNameMap.set(cleanName, numID); + if (!itemNameMap.has(item.name)) { + itemNameMap.set(item.name, numID); + } } export default itemsExport; diff --git a/src/structures/LootTable.ts b/src/structures/LootTable.ts index a17dcd084..27d6f1891 100644 --- a/src/structures/LootTable.ts +++ b/src/structures/LootTable.ts @@ -1,3 +1,4 @@ +import { randArrItem } from "e"; import itemID from "../util/itemID"; import Bank from "./Bank"; import Items from "./Items"; @@ -48,6 +49,7 @@ export interface LootTableRollOptions { */ tertiaryItemPercentageChanges?: Map; targetBank?: Bank; + effectiveTertiaryItems?: OneInItems[]; } export default class LootTable { @@ -193,12 +195,7 @@ export default class LootTable { return this; } - roll(quantity?: number): Bank; - roll(quantity: number, options: { targetBank?: undefined } & LootTableRollOptions): Bank; - roll(quantity: number, options: { targetBank: Bank } & LootTableRollOptions): null; - public roll(quantity = 1, options: LootTableRollOptions = {}): Bank | null { - const loot = options.targetBank ?? new Bank(); - + public calculateTertiary(options: LootTableRollOptions = {}) { const effectiveTertiaryItems = options.tertiaryItemPercentageChanges ? this.tertiaryItems.map(i => { if (typeof i.item !== "number") return i; @@ -212,64 +209,79 @@ export default class LootTable { }) : this.tertiaryItems; + console.trace("effectiveTertiaryItems"); + return effectiveTertiaryItems; + } + + private cachedOptimizedTable: number[] | null = null; + roll(quantity?: number): Bank; + roll(quantity: number, options: { targetBank?: undefined } & LootTableRollOptions): Bank; + roll(quantity: number, options: { targetBank: Bank } & LootTableRollOptions): null; + public roll(quantity = 1, options: LootTableRollOptions = {}): Bank | null { + const loot = options.targetBank ?? new Bank(); + + const effectiveTertiaryItems = options.effectiveTertiaryItems ?? this.calculateTertiary(options); + + const limit = this.limit || this.totalWeight; + + if (this.table.every(i => Number.isInteger(i.weight)) && this.cachedOptimizedTable === null) { + this.cachedOptimizedTable = []; + for (const item of this.table) { + for (let j = 0; j < item.weight!; j++) { + this.cachedOptimizedTable.push(this.table.indexOf(item)); + } + } + } + outerLoop: for (let i = 0; i < quantity; i++) { - // The items that are rolled. - for (const item of this.everyItems) { - this.addResultToLoot(item, loot); + for (let j = 0; j < this.everyItems.length; j++) { + this.addResultToLoot(this.everyItems[j], loot, effectiveTertiaryItems); } - for (const { chance, item, quantity, options } of effectiveTertiaryItems) { - if (roll(chance)) { - this.addResultToLoot({ item, quantity, options }, loot); + for (let j = 0; j < effectiveTertiaryItems.length; j++) { + if (roll(effectiveTertiaryItems[j].chance)) { + this.addResultToLoot(effectiveTertiaryItems[j], loot, effectiveTertiaryItems); } } - for (const { chance, item, quantity, options } of this.oneInItems) { - if (roll(chance)) { - this.addResultToLoot({ item, quantity, options }, loot); + for (let j = 0; j < this.oneInItems.length; j++) { + if (roll(this.oneInItems[j].chance)) { + this.addResultToLoot(this.oneInItems[j], loot, effectiveTertiaryItems); continue outerLoop; } } - // Random float between 0 and the total weighting - const randomWeight = randFloat(0, this.limit || this.totalWeight); - - // The index of the item that will be used. - let result = -1; - let weight = 0; - - for (let i = 0; i < this.table.length; i++) { - const item = this.table[i]!; - weight += item.weight!; - if (randomWeight <= weight) { - result = i; - break; + if (this.cachedOptimizedTable) { + this.addResultToLoot(this.table[randArrItem(this.cachedOptimizedTable)], loot, effectiveTertiaryItems); + } else { + const randomWeight = randFloat(0, limit); + let weight = 0; + for (let i = 0; i < this.table.length; i++) { + weight += this.table[i].weight!; + if (randomWeight <= weight) { + this.addResultToLoot(this.table[i], loot, effectiveTertiaryItems); + break; + } } } - - const chosenItem = this.table[result]; - if (chosenItem) { - this.addResultToLoot(chosenItem, loot); - } } - if (options.targetBank) return null; - return loot; + if (!options.targetBank) { + return loot; + } + return null; } - private addResultToLoot(result: LootTableItem, loot: Bank): void { - if (!result) return; - const { item, quantity, options } = result; - - if (typeof item === "number") { - loot.add(item, this.determineQuantity(quantity)); + private addResultToLoot(result: LootTableItem, loot: Bank, effectiveTertiaryItems: OneInItems[]): void { + if (typeof result?.item === "number") { + loot.addItem(result.item, this.determineQuantity(result.quantity)); return; } - if (item instanceof LootTable) { - const qty = this.determineQuantity(quantity); - if (options?.multiply) loot.add(item.roll(1).multiply(qty)); - else item.roll(qty, { targetBank: loot }); + if (result?.item instanceof LootTable) { + const qty = this.determineQuantity(result.quantity); + if (result.options?.multiply) loot.add(result.item.roll(1, { effectiveTertiaryItems }).multiply(qty)); + else result.item.roll(qty, { targetBank: loot, effectiveTertiaryItems }); return; } }