osrs-tools 2.9.4 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,88 +1,25 @@
1
1
  # OSRS Tools
2
2
 
3
- [![NPM Version](https://img.shields.io/npm/v/osrs-tools.svg?style=for-the-badge)](https://www.npmjs.com/package/osrs-tools)
4
- [![Downloads](https://img.shields.io/npm/dm/osrs-tools.svg?style=for-the-badge)](https://www.npmjs.com/package/osrs-tools)
5
- [![CI](https://img.shields.io/github/actions/workflow/status/jamescer/osrs-tools/ci.yml?branch=master&style=for-the-badge&label=CI)](https://github.com/jamescer/osrs-tools/actions/workflows/ci.yml)
6
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg?style=for-the-badge)](https://www.typescriptlang.org)
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
8
- [![Node Version](https://img.shields.io/node/v/osrs-tools.svg?style=for-the-badge)](https://nodejs.org)
9
- [![Test Coverage](https://img.shields.io/codecov/c/github/jamescer/osrs-tools.svg?style=for-the-badge)](https://codecov.io/gh/jamescer/osrs-tools)
3
+ [![NPM Version](https://img.shields.io/npm/v/osrs-tools.svg?style=flat-square)](https://www.npmjs.com/package/osrs-tools)
4
+ [![Downloads](https://img.shields.io/npm/dm/osrs-tools.svg?style=flat-square)](https://www.npmjs.com/package/osrs-tools)
5
+ [![CI](https://img.shields.io/github/actions/workflow/status/jamescer/osrs-tools/ci.yml?branch=master&style=flat-square&label=CI)](https://github.com/jamescer/osrs-tools/actions/workflows/ci.yml)
6
+ [![Test Coverage](https://img.shields.io/codecov/c/github/jamescer/osrs-tools.svg?style=flat-square)](https://codecov.io/gh/jamescer/osrs-tools)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](./license)
10
8
 
11
- > A comprehensive TypeScript library for Old School RuneScape (OSRS) data, tools, and game modeling.
9
+ A fully-typed TypeScript library of Old School RuneScape game data and logic — quests, accounts, diaries, combat achievements, slayer, clues, items, and more.
12
10
 
13
- **Type-safe**   **Zero runtime dependencies**   **CJS + ESM**   **Tree-shakeable**   **Well-tested**
11
+ Zero runtime dependencies, dual CJS/ESM output, tree-shakeable, Node ≥ 18.
14
12
 
15
- ## Table of Contents
16
-
17
- - [Overview](#overview)
18
- - [Installation](#installation)
19
- - [Features](#features)
20
- - [Usage](#usage)
21
- - [API Reference](#api-reference)
22
- - [Development](#development)
23
- - [Contributing](#contributing)
24
- - [Support](#support)
25
- - [License](#license)
26
-
27
- ## Overview
28
-
29
- `osrs-tools` gives OSRS developers a fully-typed model of the game, covering:
30
-
31
- - full quest and requirement system with 189+ quests and miniquests
32
- - account data modeling for skills, bosses, clues, and progression
33
- - achievement diary lookups for all 10 regions, with a `DiaryTool` for account-based progress checks
34
- - Combat Achievements: all 637 tasks across six tiers, with a `CombatAchievementTool` for points and reward-tier progress
35
- - clue scroll reward simulation with wiki-accurate odds
36
- - item data model with combat stats and clue/quest item registries
37
- - slayer masters, tasks, reward unlocks/extends/buys, and point tables
38
- - hunter guild, rumours, and master progression
39
- - skill metadata, experience tables, and combat helpers
40
- - league support (Demonic Pacts, Raging Echoes)
41
- - custom `ValidationError` for consistent error handling
42
-
43
- This library is built for analytics, web apps, and toolchains that need OSRS game logic and data. It ships as dual CommonJS/ESM with bundled type definitions, has a single runtime dependency (`tslib`), and is marked side-effect free for tree-shaking.
44
-
45
- ## Installation
46
-
47
- ### Prerequisites
48
-
49
- - Node.js 18.x or higher
50
-
51
- TypeScript is not required to consume this package — it ships compiled JavaScript with bundled `.d.ts` type definitions — but is recommended to get full type-checking in your own project.
52
-
53
- ### Install
13
+ ## Install
54
14
 
55
15
  ```bash
56
16
  npm install osrs-tools
57
17
  ```
58
18
 
59
- ## Features
60
-
61
- - Quest system with all OSRS quests and recursive requirement checking
62
- - `OsrsAccount` skill, score, and combat level lookups
63
- - Achievement diaries with full task and requirement breakdowns via `DiaryTool`
64
- - Combat Achievements: all 637 tasks, reward-tier unlocks, and points calculation via `CombatAchievementTool`
65
- - Clue scroll casket simulation with tier-specific tables and mimic handling
66
- - Item model with combat stats, alch values, and quest/clue item flags
67
- - Slayer masters (Turael through Krystilia), task weights, point tables, reward utilities
68
- - Hunter guild rumours, masters, and locations
69
- - League modules for Demonic Pacts and Raging Echoes
70
- - Skill metadata helpers (24 skills including Sailing) and experience tables
71
- - Requirement classes covering levels, quests, items, combat, location, and more
72
- - `ValidationError` for uniform error handling across the library
73
-
74
- ## Usage
75
-
76
- ### Core Import
19
+ ## Quick Start
77
20
 
78
21
  ```typescript
79
- import {
80
- QuestTool,
81
- OsrsAccount,
82
- ClueScrollHelper,
83
- Item,
84
- ValidationError,
85
- } from "osrs-tools";
22
+ import { QuestTool, OsrsAccount } from "osrs-tools";
86
23
 
87
24
  const account = OsrsAccount.fromJson({
88
25
  name: "Player123",
@@ -95,191 +32,35 @@ const account = OsrsAccount.fromJson({
95
32
 
96
33
  const questTool = new QuestTool(account);
97
34
  const dragonSlayer = QuestTool.getQuestByName("Dragon Slayer I");
98
- const canComplete = questTool.canCompleteQuest(dragonSlayer);
99
35
 
100
- console.log("Requirements:", dragonSlayer?.requirements);
101
- console.log("Can complete?", canComplete);
36
+ console.log(questTool.canCompleteQuest(dragonSlayer));
102
37
  ```
103
38
 
104
- ### Subpath Imports
39
+ Import from the root package or from a subpath to keep bundles small:
105
40
 
106
41
  ```typescript
107
- import { QuestTool, DragonSlayerI } from "osrs-tools/quest";
108
42
  import { Duradel, getMasterByName } from "osrs-tools/slayer";
109
- import { OsrsAccount, Skill } from "osrs-tools/account";
110
- import diaries, { getDiaryByName, getAllDiaries, DiaryTool } from "osrs-tools/diary";
111
- import { CombatAchievementTool, ALL_COMBAT_ACHIEVEMENT_TASKS } from "osrs-tools/combat-achievements";
112
- import { getExperienceForLevel, getLevelForExperience } from "osrs-tools/tools";
113
- import { validateSkillLevel, getSkillsByCategory } from "osrs-tools/utils";
114
- ```
115
-
116
- ## API Reference
117
-
118
- ### Quest System (`osrs-tools/quest`)
119
-
120
- - `QuestTool` — account-based quest completion checks and quest registry
121
- - `QuestTool.getQuestByName(name)` — look up a quest by name
122
- - `QuestList`, `MiniQuestList` — full quest and miniquest registries
123
- - `QuestDifficulty`, `QuestLength`, `QuestStatus` — enums for quest metadata
124
- - 189+ individual quest classes (e.g., `DragonSlayerI`, `AKingdomDivided`)
125
- - Requirement classes:
126
- - `LevelRequirement` — skill level with optional boost support
127
- - `SlayerLevelRequirement` — slayer-specific level checks
128
- - `CombatLevelRequirement` — combat level gate
129
- - `QuestRequirement` — quest completion dependency
130
- - `ItemRequirement` — item with quantity, consumption, alternatives, and bankable flags
131
- - `LocationRequirement` — geographic access requirement
132
- - `QuestPointRequirement` — quest point gate
133
- - `SlayerUnlockRequirement` — slayer unlock dependency
134
-
135
- ### Account Modeling (`osrs-tools/account`)
136
-
137
- - `OsrsAccount.fromJson(json)` — parse an account from hiscores-style JSON
138
- - Skill access via `Skill` enum (24 skills: Attack, Defence, Strength, Hitpoints, Ranged, Prayer, Magic, Woodcutting, Fishing, Firemaking, Crafting, Smithing, Mining, Herblore, Agility, Thieving, Slayer, Farming, Runecraft, Hunter, Construction, Cooking, Fletching, Sailing)
139
- - `SKILL_METADATA` — per-skill metadata including category, maxLevel, and isBoostable
140
-
141
- ### Achievement Diaries (`osrs-tools/diary`)
142
-
143
- - `diaries` default export — all diaries
144
- - `getDiaryByName(name)` — look up a diary by region name
145
- - `getAllDiaries()` — array of all 10 regional diaries
146
- - Regions: Ardougne, Desert, Fremennik, Kandarin, Karamja, Kourend & Kebos, Lumbridge & Draynor, Morytania, Varrock, Wilderness
147
- - Each diary includes Easy/Medium/Hard/Elite tasks with full requirement breakdowns
148
- - `DiaryTool` — account-bound task checks:
149
- - `canCompleteTask(task)` / `canCompleteLevel(level)` — checks Level/Quest/CombatLevel/QuestPoint requirements
150
- - `getCompletableTasks(source)` / `getRemainingTasks(source)` — accepts a full `Diary` or a single `DiaryLevel`
151
- - `getDiaryProgress(diary)` — completable/remaining/total breakdown per tier
152
-
153
- ### Combat Achievements (`osrs-tools/combat-achievements`)
154
-
155
- - All 637 tasks across six tiers (Easy 41 / Medium 60 / Hard 85 / Elite 162 / Master 168 / Grandmaster 121), sourced from the OSRS Wiki
156
- - `ALL_COMBAT_ACHIEVEMENT_TASKS`, `EASY_COMBAT_ACHIEVEMENTS` ... `GRANDMASTER_COMBAT_ACHIEVEMENTS` — task lists per tier
157
- - `getCombatAchievementByName(name)`, `getCombatAchievementsByTier(tier)`, `getCombatAchievementsByMonster(monster)`
158
- - `COMBAT_ACHIEVEMENT_REWARD_TIERS` — the 6 cumulative point thresholds (41/161/416/1064/1904/2630) and their reward text
159
- - `CombatAchievementTool`:
160
- - `hasEngagedMonster(task)` — best-effort accessibility signal from the account's boss kill count; task completion itself isn't exposed via hiscores and can't be verified
161
- - `getAccessibleTasks(tasks)` / `getInaccessibleTasks(tasks)`
162
- - `calculatePoints(completedTaskNames)` — points for a caller-supplied list of completed task names
163
- - `getUnlockedRewardTiers(points)` / `getNextRewardTier(points)`
164
- - `getProgress(completedTaskNames)` — full per-tier breakdown plus unlocked/next reward tiers
165
-
166
- ```typescript
43
+ import { DiaryTool, getDiaryByName } from "osrs-tools/diary";
167
44
  import { CombatAchievementTool } from "osrs-tools/combat-achievements";
168
-
169
- const caTool = new CombatAchievementTool(account);
170
- const progress = caTool.getProgress(["Barrows Novice", "Noxious Foe"]);
171
- console.log(progress.totalPoints, progress.nextRewardTier?.tier);
172
45
  ```
173
46
 
174
- ### Clue Scroll Simulation
175
-
176
- Available from the main `osrs-tools` import:
177
-
178
- ```typescript
179
- import { ClueScrollHelper } from "osrs-tools";
180
-
181
- const result = ClueScrollHelper.openCasket("hard");
182
- console.log(result.items, result.mimicTriggered);
183
- ```
184
-
185
- - `ClueScrollHelper.openCasket(tier)` — simulate a casket opening with wiki-accurate odds
186
- - Tiers: `beginner`, `easy`, `medium`, `hard`, `elite`, `master`
187
- - `CasketReward` interface: `{ items, count, masterClue, mimicTriggered, mimicGuaranteed, mimicBonusItem }`
188
-
189
- ### Items
190
-
191
- Available from the main `osrs-tools` import:
192
-
193
- - `Item` — item data model with combat stats (attack/defence stab/slash/crush/magic/ranged, melee/ranged/magic bonuses, prayer), alch values, weight, tradeable/equipable/stackable flags
194
- - `ClueRewards` — item registries for clue scroll reward tables
195
- - `QuestItems` — item registry for quest-related items
47
+ ## What's included
196
48
 
197
- ### Slayer System (`osrs-tools/slayer`)
49
+ | Module | Subpath | Highlights |
50
+ | --- | --- | --- |
51
+ | Quests | `osrs-tools/quest` | `QuestTool`, 189+ quests, requirement classes (level, quest, item, combat, location…) |
52
+ | Accounts | `osrs-tools/account` | `OsrsAccount` (hiscores-compatible), `Skill` enum, skill metadata |
53
+ | Diaries | `osrs-tools/diary` | 10 regional achievement diaries, `DiaryTool` for progress checks |
54
+ | Combat Achievements | `osrs-tools/combat-achievements` | All 637 tasks across six tiers, `CombatAchievementTool` |
55
+ | Slayer | `osrs-tools/slayer` | 9 masters, tasks, points, unlocks/extends/buys |
56
+ | Experience | `osrs-tools/tools` | XP ⇄ level conversion, full XP table |
57
+ | Validation | `osrs-tools/utils` | Skill/level validation, `ValidationError` |
58
+ | Clues, items, guilds, leagues | root `osrs-tools` import | `ClueScrollHelper`, `Item`, `HunterGuild`, league data |
198
59
 
199
- #### Masters
200
-
201
- | Class | `getMasterByName` key |
202
- | --- | --- |
203
- | `Turael` | `"Turael"` |
204
- | `Spria` | `"Spria"` |
205
- | `Mazchna` | `"Mazchna"` |
206
- | `Vannaka` | `"Vannaka"` |
207
- | `Chaeldar` | `"Chaeldar"` |
208
- | `KonarQuoMaten` | `"Konar quo Maten"` |
209
- | `Nieve` | `"Nieve"` |
210
- | `Duradel` | `"Duradel"` |
211
- | `Krystilia` | `"Krystilia"` |
212
-
213
- #### Registry Utilities
214
-
215
- - `getMasterByName(name)` — look up a master by name
216
- - `getAllMasters()` — all slayer masters
217
- - `getMastersByMinimumLevel(level)` — masters accessible at a given combat level
218
- - `getMastersByProgression()` — masters ordered by progression
219
- - `getRandomMasterForLevel(level)` — random master appropriate for a level
220
-
221
- #### Rewards
222
-
223
- - `SlayerUnlock`, `SlayerExtend`, `SlayerBuy` — reward classes
224
- - `SLAYER_UNLOCKS`, `SLAYER_EXTENDS`, `SLAYER_BUYS` — full data sets
225
- - `getUnlockByName`, `getUnlocksByCostRange`, `calculateUnlocksCost`
226
- - `getExtendByName`, `getExtendsForTask`
227
- - `getBuyByItem`, `getBuysByCostRange`, `getBuysByRequirement`, `calculateBuysCost`
228
-
229
- #### Data & Constants
230
-
231
- - `POINTS_TABLE` — point rewards per master and streak
232
- - `SLAYER_EXP_RATES`, `STANDARD_TASK_WEIGHTS`, `TASK_QUANTITY_PRESETS`, `EXTENDED_QUANTITY_PRESETS`
233
- - `SlayerLocation`, `SlayerBoss`, `MonsterType` enums
234
-
235
- ### Experience Tools (`osrs-tools/tools`)
236
-
237
- - `getExperienceForLevel(level: number): number` — XP required to reach a level
238
- - `getLevelForExperience(xp: number): number` — level at a given XP total
239
- - `LEVEL_TO_EXPERIENCE: Record<number, number>` — full XP table (levels 1–99)
240
-
241
- ### Guilds
242
-
243
- Available from the main `osrs-tools` import:
244
-
245
- #### Hunter Guild
246
-
247
- - `HunterGuild` — guild class
248
- - `HunterGuildMaster` — master NPC with progression data
249
- - `HunterGuildMasterName` — enum of master names
250
- - `HunterRumour` — rumour entry with location and loot
251
- - `Rumours` — full rumour registry
252
- - `RumourLocation` — location data for rumours
253
-
254
- ### Leagues
255
-
256
- Available from the main `osrs-tools` import:
257
-
258
- - `League` interface — relics, reward tiers, and relic tiers
259
- - `Relic` — selectable relic with passive bonuses
260
- - `RelicTier` — tier with XP/drop multipliers
261
- - `RewardTier` — milestone reward unlock
262
- - `DemonicPactsLeagueAreas` — area data for Demonic Pacts
263
- - `RagingEchoesLeague` — region and relic data for Raging Echoes
264
- - `LeagueRegion`, `NotableDrop`, `GenericLeagueRegion` enums
265
-
266
- ### Validation & Skill Utilities (`osrs-tools/utils`)
267
-
268
- - `validateSkillLevel(level: number, skillName: string): void` — throws `ValidationError` for invalid levels
269
- - `validateQuestPoints(points: number): void` — throws `ValidationError` for invalid point totals
270
- - `getSkillMetadata(skill: Skill | string): SkillMetadata | undefined`
271
- - `isValidSkill(skillName: string): boolean`
272
- - `isValidLevel(skill: Skill | string, level: number): boolean`
273
- - `getSkillsByCategory(category: "combat" | "gathering" | "production" | "support"): Skill[]`
274
-
275
- ### Error Handling
276
-
277
- - `ValidationError` — extends `Error` with `fieldName: string`, `value: unknown`, and optional `context: Record<string, unknown>`
60
+ For the full API surface and source layout, see [summary.md](./summary.md).
278
61
 
279
62
  ## Development
280
63
 
281
- ### Local Setup
282
-
283
64
  ```bash
284
65
  git clone https://github.com/jamescer/osrs-tools.git
285
66
  cd osrs-tools
@@ -288,110 +69,30 @@ npm run build
288
69
  npm test
289
70
  ```
290
71
 
291
- ### Available Scripts
292
-
293
- | Command | Description |
294
- | ----------------------- | ------------------------------------- |
295
- | `build` | Compile TypeScript to `dist/` |
296
- | `build:npm` | Build with publish tsconfig |
297
- | `clean` | Remove `dist/` output directory |
298
- | `dev` | Watch mode compilation |
299
- | `test` | Run test suite |
300
- | `test:watch` | Run tests in watch mode |
301
- | `test:coverage` | Generate coverage report |
302
- | `coverage` | Run coverage and upload to Codecov |
303
- | `format` | Format source and tests with Prettier |
304
- | `lint` | Lint and auto-fix with ESLint |
305
- | `publish-test` | Dry-run `npm publish` |
306
-
307
- ### Project Structure
308
-
309
- ```text
310
- src/
311
- ├── index.ts # Main entry point
312
- └── runescape/
313
- ├── errors.ts # ValidationError
314
- ├── model/
315
- │ ├── Requirement.ts # All requirement classes and RequirementType enum
316
- │ ├── account/ # OsrsAccount, Skill enum, SKILL_METADATA
317
- │ │ └── skills/unlocks/ # AttackUnlocks, SkillUnlock
318
- │ ├── clue/ # ClueScrollHelper, ClueScrollRewards
319
- │ ├── diaries/ # 10 regional diary modules + DiaryTool
320
- │ ├── combat-achievements/ # 637 tasks (6 tiers), reward tiers, CombatAchievementTool
321
- │ ├── guilds/
322
- │ │ └── hunter/ # HunterGuild, HunterGuildMaster, Rumours, RumourLocation
323
- │ ├── items/ # Item, ClueRewards, QuestItems
324
- │ ├── leagues/ # DemonicPactsLeague, RagingEchoesLeague, League interfaces
325
- │ ├── quest/
326
- │ │ ├── QuestTool.ts
327
- │ │ ├── QuestList.ts
328
- │ │ ├── MiniQuestList.ts
329
- │ │ └── all/ # 189+ individual quest classes
330
- │ ├── skills/ # Skill interface, attack and hunter skill modules
331
- │ └── slayer/
332
- │ ├── core/ # SlayerMaster, Task, Assignment
333
- │ ├── masters/ # 9 master classes + registry
334
- │ ├── rewards/ # SlayerUnlock, SlayerExtend, SlayerBuy + data
335
- │ └── tasks/ # Task constants and weights
336
- ├── tools/
337
- │ └── experience.ts # getExperienceForLevel, getLevelForExperience, LEVEL_TO_EXPERIENCE
338
- └── utils/
339
- ├── validation.ts # validateSkillLevel, validateQuestPoints
340
- └── skillUtils.ts # getSkillMetadata, isValidSkill, getSkillsByCategory, isValidLevel
72
+ | Command | Description |
73
+ | --- | --- |
74
+ | `npm run build` | Compile TypeScript to `dist/` |
75
+ | `npm test` | Run the test suite |
76
+ | `npm run lint` | Lint and auto-fix with ESLint |
77
+ | `npm run format` | Format with Prettier |
78
+ | `npm run changeset` | Record a change for the next release |
341
79
 
342
- test/
343
- └── unit/
344
- ├── account/
345
- ├── diaries/
346
- ├── combat-achievements/
347
- ├── quest/
348
- ├── slayer/
349
- ├── model/
350
- ├── tools/
351
- └── utils/
352
-
353
- dist/ # Compiled output (generated by build)
354
- ```
80
+ See [dev_setup.md](./dev_setup.md) for the full setup and release workflow.
355
81
 
356
82
  ## Contributing
357
83
 
358
- We welcome contributions! We use [Conventional Commits](https://www.conventionalcommits.org/).
359
-
360
- ### Getting Started
361
-
362
- 1. Fork and clone the repository
363
- 2. Create a branch (`feat/amazing-feature`)
364
- 3. Make your changes and add tests
365
- 4. Run `npm test && npm run lint`
366
- 5. Commit with conventional format:
367
-
368
- ```text
369
- feat: add quest requirement validation
370
- fix: correct slayer task weights
371
- docs: update API documentation
372
- ```
84
+ Contributions are welcome. We use [Conventional Commits](https://www.conventionalcommits.org/) and [changesets](https://github.com/changesets/changesets) for releases.
373
85
 
374
- 6. Push and open a Pull Request
86
+ 1. Fork and clone the repo, create a branch
87
+ 2. Make your change and add tests
88
+ 3. `npm test && npm run lint`
89
+ 4. `npm run changeset` to record a changelog entry
90
+ 5. Open a pull request
375
91
 
376
- ### Guidelines
377
-
378
- - Add tests for new features
379
- - Update documentation for public API changes
380
- - Follow existing code style
381
- - Keep changes focused
382
-
383
- ## Support
384
-
385
- - [Report Issues](https://github.com/jamescer/osrs-tools/issues)
386
- - [Request Features](https://github.com/jamescer/osrs-tools/issues)
387
- - [Discussions](https://github.com/jamescer/osrs-tools/discussions)
92
+ Please also review our [Code of Conduct](./code-of-conduct.md) and [Security Policy](./SECURITY.md).
388
93
 
389
94
  ## License
390
95
 
391
- [MIT](./LICENSE) © James Cerniglia
392
-
393
- ## Credits
96
+ [MIT](./license) © [James Cerniglia](mailto:jamesmcerniglia@gmail.com)
394
97
 
395
- - **Author**: [James Cerniglia](mailto:jamesmcerniglia@gmail.com)
396
- - **Contributors**: [View all contributors](https://github.com/jamescer/osrs-tools/graphs/contributors)
397
- - **Data Source**: [Old School RuneScape Wiki](https://oldschool.runescape.wiki/)
98
+ Data sourced from the [Old School RuneScape Wiki](https://oldschool.runescape.wiki/).
package/dist/index.d.ts CHANGED
@@ -27,7 +27,7 @@ export * from './runescape/model/diaries';
27
27
  export * from './runescape/model/quest/QuestTool';
28
28
  export * from './runescape/model/items';
29
29
  export * from './runescape/model/leagues';
30
- export { Assignment, Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, KonarQuoMaten, Krystilia, Mazchna, Nieve, POINTS_TABLE, SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS, SlayerBoss, SlayerBuy, SlayerExtend, SlayerLocation, SlayerMaster, SlayerUnlock, Spria, Task, Turael, Vannaka, } from './runescape/model/slayer';
30
+ export { Assignment, Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, KonarQuoMaten, Krystilia, Mazchna, Mortifier, MORTIFIERS, Mortimer, MortimerSlayerMaster, Nieve, POINTS_TABLE, SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS, SlayerBoss, SlayerBuy, SlayerExtend, SlayerLocation, SlayerMaster, SlayerUnlock, Spria, Task, Turael, Vannaka, } from './runescape/model/slayer';
31
31
  export * from './runescape/model/guilds';
32
32
  export * from './runescape/model/skills';
33
33
  export * from './runescape/errors';
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ export * from './runescape/model/items';
31
31
  // Leagues
32
32
  export * from './runescape/model/leagues';
33
33
  // Slayer system
34
- export { Assignment, Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, KonarQuoMaten, Krystilia, Mazchna, Nieve, POINTS_TABLE, SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS, SlayerBoss, SlayerBuy, SlayerExtend, SlayerLocation, SlayerMaster, SlayerUnlock, Spria, Task, Turael, Vannaka, } from './runescape/model/slayer';
34
+ export { Assignment, Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, KonarQuoMaten, Krystilia, Mazchna, Mortifier, MORTIFIERS, Mortimer, MortimerSlayerMaster, Nieve, POINTS_TABLE, SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS, SlayerBoss, SlayerBuy, SlayerExtend, SlayerLocation, SlayerMaster, SlayerUnlock, Spria, Task, Turael, Vannaka, } from './runescape/model/slayer';
35
35
  // Guilds system
36
36
  export * from './runescape/model/guilds';
37
37
  // Skills system
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Mortifiers - Mortimer's unique task modifier system
3
+ * Reference: https://oldschool.runescape.wiki/w/Mortimer
4
+ *
5
+ * Unlike other Slayer Masters, Mortimer does not reward Slayer points as the primary
6
+ * incentive for completing tasks. Instead his tasks carry a "Mortifier" - a modifier
7
+ * that is unlocked permanently as a player completes more tasks with him, and which
8
+ * is then applied (with a variable magnitude, "X") to a given task offer.
9
+ *
10
+ * NOTE: Mortimer and Wyrmscraig were, at time of writing, planned but not yet
11
+ * released content (Player Designed Island Competition). Names, thresholds and
12
+ * effects may change before/if this is implemented in the live game.
13
+ */
14
+ export interface MortifierConfig {
15
+ id: number;
16
+ name: string;
17
+ /** Number of Slayer tasks completed with Mortimer required to unlock this Mortifier. */
18
+ unlockThreshold: number;
19
+ description: string;
20
+ }
21
+ export declare class Mortifier {
22
+ id: number;
23
+ name: string;
24
+ unlockThreshold: number;
25
+ description: string;
26
+ constructor(config: MortifierConfig);
27
+ /**
28
+ * Get the number of tasks that must be completed with Mortimer to unlock this Mortifier.
29
+ */
30
+ getUnlockThreshold(): number;
31
+ /**
32
+ * Whether this Mortifier is unlocked for a player with the given number of completed tasks.
33
+ */
34
+ isUnlockedAt(completedTasks: number): boolean;
35
+ getDescription(): string;
36
+ }
37
+ /**
38
+ * All Mortifiers, in unlock order.
39
+ */
40
+ export declare const MORTIFIERS: Mortifier[];
41
+ /**
42
+ * Get every Mortifier unlocked for a player with the given number of tasks completed with Mortimer.
43
+ */
44
+ export declare function getAvailableMortifiers(completedTasks: number): Mortifier[];
45
+ /**
46
+ * Get a single Mortifier by its name (case insensitive).
47
+ */
48
+ export declare function getMortifierByName(name: string): Mortifier | undefined;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Mortifiers - Mortimer's unique task modifier system
3
+ * Reference: https://oldschool.runescape.wiki/w/Mortimer
4
+ *
5
+ * Unlike other Slayer Masters, Mortimer does not reward Slayer points as the primary
6
+ * incentive for completing tasks. Instead his tasks carry a "Mortifier" - a modifier
7
+ * that is unlocked permanently as a player completes more tasks with him, and which
8
+ * is then applied (with a variable magnitude, "X") to a given task offer.
9
+ *
10
+ * NOTE: Mortimer and Wyrmscraig were, at time of writing, planned but not yet
11
+ * released content (Player Designed Island Competition). Names, thresholds and
12
+ * effects may change before/if this is implemented in the live game.
13
+ */
14
+ export class Mortifier {
15
+ id;
16
+ name;
17
+ unlockThreshold;
18
+ description;
19
+ constructor(config) {
20
+ this.id = config.id;
21
+ this.name = config.name;
22
+ this.unlockThreshold = config.unlockThreshold;
23
+ this.description = config.description;
24
+ }
25
+ /**
26
+ * Get the number of tasks that must be completed with Mortimer to unlock this Mortifier.
27
+ */
28
+ getUnlockThreshold() {
29
+ return this.unlockThreshold;
30
+ }
31
+ /**
32
+ * Whether this Mortifier is unlocked for a player with the given number of completed tasks.
33
+ */
34
+ isUnlockedAt(completedTasks) {
35
+ return completedTasks >= this.unlockThreshold;
36
+ }
37
+ getDescription() {
38
+ return this.description;
39
+ }
40
+ }
41
+ const BonusSlayerPoints = new Mortifier({
42
+ description: 'Task awards X additional Slayer Points on completion.',
43
+ id: 1,
44
+ name: 'Bonus Slayer Points',
45
+ unlockThreshold: 0,
46
+ });
47
+ const QuantityModifier = new Mortifier({
48
+ description: 'Task quantity modified by X%.',
49
+ id: 2,
50
+ name: 'Quantity Modifier',
51
+ unlockThreshold: 0,
52
+ });
53
+ const ClueScrollBoost = new Mortifier({
54
+ description: 'Clue scroll drop-rates are increased by X%.',
55
+ id: 3,
56
+ name: 'Clue Scroll Boost',
57
+ unlockThreshold: 15,
58
+ });
59
+ const SuperiorUniqueBoost = new Mortifier({
60
+ description: "Slayer creatures' superiors access the superior unique table X% more often.",
61
+ id: 4,
62
+ name: 'Superior Unique Boost',
63
+ unlockThreshold: 25,
64
+ });
65
+ const BonusSlayerExp = new Mortifier({
66
+ description: 'Slayer creatures award X% more Slayer XP.',
67
+ id: 5,
68
+ name: 'Bonus Slayer Exp',
69
+ unlockThreshold: 40,
70
+ });
71
+ const ThirdTaskChoice = new Mortifier({
72
+ description: 'Adds a third task choice to Mortimer task offers.',
73
+ id: 6,
74
+ name: 'Third Task Choice',
75
+ unlockThreshold: 50,
76
+ });
77
+ /**
78
+ * All Mortifiers, in unlock order.
79
+ */
80
+ export const MORTIFIERS = [
81
+ BonusSlayerPoints,
82
+ QuantityModifier,
83
+ ClueScrollBoost,
84
+ SuperiorUniqueBoost,
85
+ BonusSlayerExp,
86
+ ThirdTaskChoice,
87
+ ];
88
+ /**
89
+ * Get every Mortifier unlocked for a player with the given number of tasks completed with Mortimer.
90
+ */
91
+ export function getAvailableMortifiers(completedTasks) {
92
+ return MORTIFIERS.filter(mortifier => mortifier.isUnlockedAt(completedTasks));
93
+ }
94
+ /**
95
+ * Get a single Mortifier by its name (case insensitive).
96
+ */
97
+ export function getMortifierByName(name) {
98
+ const normalized = name.toLowerCase();
99
+ return MORTIFIERS.find(mortifier => mortifier.name.toLowerCase() === normalized);
100
+ }
@@ -0,0 +1,71 @@
1
+ import { Mortifier } from './Mortifier';
2
+ import { SlayerMaster } from './SlayerMaster';
3
+ import { Task } from './Task';
4
+ import { SlayerMasterConfig } from './types';
5
+ /**
6
+ * Configuration for Mortimer, extending the standard SlayerMaster config with the
7
+ * quirks that make him behave differently to every other Slayer Master.
8
+ * Reference: https://oldschool.runescape.wiki/w/Mortimer
9
+ */
10
+ export interface MortimerSlayerMasterConfig extends SlayerMasterConfig {
11
+ /** Slayer points cost to skip a task offer (Mortimer: 100, vs. the usual 30). */
12
+ skipCost?: number;
13
+ /** Number of block slots Mortimer offers (Mortimer: 2, vs. the usual 6/7). */
14
+ blockSlots?: number;
15
+ /** Slayer points cost per block slot (Mortimer: 120). */
16
+ blockSlotCost?: number;
17
+ /** Number of task choices offered before the "Third Task Choice" Mortifier is unlocked. */
18
+ baseTaskChoiceCount?: number;
19
+ /** Number of task choices offered once the "Third Task Choice" Mortifier is unlocked. */
20
+ extendedTaskChoiceCount?: number;
21
+ }
22
+ /**
23
+ * Mortimer - a planned Slayer Master for the upcoming Wyrmscraig island.
24
+ *
25
+ * Unlike other masters, Mortimer:
26
+ * - Offers multiple task choices per visit (2, or 3 after 50 completed tasks) rather than
27
+ * assigning a single task outright.
28
+ * - Only assigns monsters that have a superior variant.
29
+ * - Can assign Venators directly.
30
+ * - Uses the Mortifier system in place of standard Slayer point rewards as the main incentive.
31
+ * - Has a significantly higher skip cost and far fewer, more expensive block slots.
32
+ *
33
+ * NOTE: This content had not been released at time of writing (Player Designed Island
34
+ * Competition submission); values are subject to change.
35
+ */
36
+ declare class MortimerSlayerMaster extends SlayerMaster {
37
+ skipCost: number;
38
+ blockSlots: number;
39
+ blockSlotCost: number;
40
+ baseTaskChoiceCount: number;
41
+ extendedTaskChoiceCount: number;
42
+ constructor(config: MortimerSlayerMasterConfig);
43
+ /**
44
+ * All creatures Mortimer assigns have superior variants - this is his defining trait.
45
+ */
46
+ hasSuperiorVariants(): boolean;
47
+ /**
48
+ * Mortimer is the only Slayer Master who can assign Venators directly.
49
+ */
50
+ assignsVenatorsDirectly(): boolean;
51
+ getSkipCost(): number;
52
+ getBlockSlots(): number;
53
+ getBlockSlotCost(): number;
54
+ /**
55
+ * Get the Mortifiers currently unlocked for a player, based on tasks completed with Mortimer.
56
+ */
57
+ getAvailableMortifiers(completedTasks: number): Mortifier[];
58
+ /**
59
+ * Number of task choices offered on a given visit: 2 by default, 3 once the
60
+ * "Third Task Choice" Mortifier is unlocked at 50 completed tasks.
61
+ */
62
+ getTaskChoiceCount(completedTasks: number): number;
63
+ /**
64
+ * Get a set of distinct, weighted-random task choices for a Mortimer visit.
65
+ * The number of choices scales with completedTasks (see getTaskChoiceCount()).
66
+ * Mortimer never offers the same task twice in a single set of choices.
67
+ * @returns {Task[]} The offered tasks. May be shorter than requested if there aren't enough distinct unblocked tasks.
68
+ */
69
+ getTaskChoices(completedTasks?: number): Task[];
70
+ }
71
+ export { MortimerSlayerMaster };
@@ -0,0 +1,98 @@
1
+ import { getAvailableMortifiers } from './Mortifier';
2
+ import { SlayerMaster } from './SlayerMaster';
3
+ /**
4
+ * Mortimer - a planned Slayer Master for the upcoming Wyrmscraig island.
5
+ *
6
+ * Unlike other masters, Mortimer:
7
+ * - Offers multiple task choices per visit (2, or 3 after 50 completed tasks) rather than
8
+ * assigning a single task outright.
9
+ * - Only assigns monsters that have a superior variant.
10
+ * - Can assign Venators directly.
11
+ * - Uses the Mortifier system in place of standard Slayer point rewards as the main incentive.
12
+ * - Has a significantly higher skip cost and far fewer, more expensive block slots.
13
+ *
14
+ * NOTE: This content had not been released at time of writing (Player Designed Island
15
+ * Competition submission); values are subject to change.
16
+ */
17
+ class MortimerSlayerMaster extends SlayerMaster {
18
+ skipCost;
19
+ blockSlots;
20
+ blockSlotCost;
21
+ baseTaskChoiceCount;
22
+ extendedTaskChoiceCount;
23
+ constructor(config) {
24
+ super(config);
25
+ this.skipCost = config.skipCost ?? 100;
26
+ this.blockSlots = config.blockSlots ?? 2;
27
+ this.blockSlotCost = config.blockSlotCost ?? 120;
28
+ this.baseTaskChoiceCount = config.baseTaskChoiceCount ?? 2;
29
+ this.extendedTaskChoiceCount = config.extendedTaskChoiceCount ?? 3;
30
+ }
31
+ /**
32
+ * All creatures Mortimer assigns have superior variants - this is his defining trait.
33
+ */
34
+ hasSuperiorVariants() {
35
+ return true;
36
+ }
37
+ /**
38
+ * Mortimer is the only Slayer Master who can assign Venators directly.
39
+ */
40
+ assignsVenatorsDirectly() {
41
+ return true;
42
+ }
43
+ getSkipCost() {
44
+ return this.skipCost;
45
+ }
46
+ getBlockSlots() {
47
+ return this.blockSlots;
48
+ }
49
+ getBlockSlotCost() {
50
+ return this.blockSlotCost;
51
+ }
52
+ /**
53
+ * Get the Mortifiers currently unlocked for a player, based on tasks completed with Mortimer.
54
+ */
55
+ getAvailableMortifiers(completedTasks) {
56
+ return getAvailableMortifiers(completedTasks);
57
+ }
58
+ /**
59
+ * Number of task choices offered on a given visit: 2 by default, 3 once the
60
+ * "Third Task Choice" Mortifier is unlocked at 50 completed tasks.
61
+ */
62
+ getTaskChoiceCount(completedTasks) {
63
+ const thirdChoiceUnlocked = this.getAvailableMortifiers(completedTasks).some(mortifier => mortifier.name === 'Third Task Choice');
64
+ return thirdChoiceUnlocked ? this.extendedTaskChoiceCount : this.baseTaskChoiceCount;
65
+ }
66
+ /**
67
+ * Get a set of distinct, weighted-random task choices for a Mortimer visit.
68
+ * The number of choices scales with completedTasks (see getTaskChoiceCount()).
69
+ * Mortimer never offers the same task twice in a single set of choices.
70
+ * @returns {Task[]} The offered tasks. May be shorter than requested if there aren't enough distinct unblocked tasks.
71
+ */
72
+ getTaskChoices(completedTasks = 0) {
73
+ const choiceCount = this.getTaskChoiceCount(completedTasks);
74
+ const remainingTasks = this.getAvailableTasks().slice();
75
+ const choices = [];
76
+ while (choices.length < choiceCount && remainingTasks.length > 0) {
77
+ const totalWeight = remainingTasks.reduce((total, task) => total + (task.weight || 0), 0);
78
+ if (totalWeight <= 0)
79
+ break;
80
+ const randomWeight = Math.random() * totalWeight;
81
+ let cumulativeWeight = 0;
82
+ let selectedIndex = -1;
83
+ for (let i = 0; i < remainingTasks.length; i++) {
84
+ cumulativeWeight += remainingTasks[i].weight || 0;
85
+ if (randomWeight <= cumulativeWeight) {
86
+ selectedIndex = i;
87
+ break;
88
+ }
89
+ }
90
+ if (selectedIndex === -1)
91
+ break;
92
+ choices.push(remainingTasks[selectedIndex]);
93
+ remainingTasks.splice(selectedIndex, 1);
94
+ }
95
+ return choices;
96
+ }
97
+ }
98
+ export { MortimerSlayerMaster };
@@ -3,6 +3,10 @@
3
3
  * Exports the fundamental classes and types for the Slayer system
4
4
  */
5
5
  export { Assignment } from './Assignment';
6
+ export type { MortifierConfig } from './Mortifier';
7
+ export { getAvailableMortifiers, getMortifierByName, Mortifier, MORTIFIERS } from './Mortifier';
8
+ export type { MortimerSlayerMasterConfig } from './MortimerSlayerMaster';
9
+ export { MortimerSlayerMaster } from './MortimerSlayerMaster';
6
10
  export { SlayerMaster } from './SlayerMaster';
7
11
  export type { TaskJson } from './Task';
8
12
  export { Task } from './Task';
@@ -3,6 +3,8 @@
3
3
  * Exports the fundamental classes and types for the Slayer system
4
4
  */
5
5
  export { Assignment } from './Assignment';
6
+ export { getAvailableMortifiers, getMortifierByName, Mortifier, MORTIFIERS } from './Mortifier';
7
+ export { MortimerSlayerMaster } from './MortimerSlayerMaster';
6
8
  export { SlayerMaster } from './SlayerMaster';
7
9
  export { Task } from './Task';
8
10
  export { POINTS_TABLE, SlayerBoss, SlayerLocation, TASK_DEFAULTS } from './types';
@@ -3,11 +3,11 @@
3
3
  * Complete slayer system including masters, tasks, rewards, and utilities
4
4
  * Reference: https://oldschool.runescape.wiki/w/Slayer
5
5
  */
6
- export type { SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity } from './core';
7
- export { Assignment, SlayerMaster, Task } from './core';
6
+ export type { MortifierConfig, MortimerSlayerMasterConfig, SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity, } from './core';
7
+ export { Assignment, getAvailableMortifiers, getMortifierByName, Mortifier, MORTIFIERS, MortimerSlayerMaster, SlayerMaster, Task, } from './core';
8
8
  export { POINTS_TABLE, SlayerBoss, SlayerLocation, TASK_DEFAULTS } from './core';
9
9
  export type { SlayerMasterProgression } from './masters';
10
- export { Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, KonarQuoMaten, Krystilia, Mazchna, Nieve, Spria, Turael, Vannaka, } from './masters';
10
+ export { Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, KonarQuoMaten, Krystilia, Mazchna, Mortimer, Nieve, Spria, Turael, Vannaka, } from './masters';
11
11
  export { SlayerBuy, SlayerExtend, SlayerUnlock } from './rewards';
12
12
  export { SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS } from './rewards';
13
13
  export { calculateBuysCost, calculateUnlocksCost, getBuyByItem, getBuysByCostRange, getBuysByRequirement, getExtendByName, getExtendsForTask, getUnlockByName, getUnlocksByCostRange, } from './rewards';
@@ -3,9 +3,9 @@
3
3
  * Complete slayer system including masters, tasks, rewards, and utilities
4
4
  * Reference: https://oldschool.runescape.wiki/w/Slayer
5
5
  */
6
- export { Assignment, SlayerMaster, Task } from './core';
6
+ export { Assignment, getAvailableMortifiers, getMortifierByName, Mortifier, MORTIFIERS, MortimerSlayerMaster, SlayerMaster, Task, } from './core';
7
7
  export { POINTS_TABLE, SlayerBoss, SlayerLocation, TASK_DEFAULTS } from './core';
8
- export { Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, KonarQuoMaten, Krystilia, Mazchna, Nieve, Spria, Turael, Vannaka, } from './masters';
8
+ export { Chaeldar, Duradel, getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, KonarQuoMaten, Krystilia, Mazchna, Mortimer, Nieve, Spria, Turael, Vannaka, } from './masters';
9
9
  // ===== Rewards =====
10
10
  export { SlayerBuy, SlayerExtend, SlayerUnlock } from './rewards';
11
11
  export { SLAYER_BUYS, SLAYER_EXTENDS, SLAYER_UNLOCKS } from './rewards';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mortimer - Slayer Master
3
+ * Location: Wyrmscraig
4
+ * Wiki: https://oldschool.runescape.wiki/w/Mortimer
5
+ *
6
+ * NOTE: Mortimer is planned content for the upcoming Wyrmscraig island (Player Designed
7
+ * Island Competition, designed by ScreteMonge) and had not been released at time of writing.
8
+ * Values here reflect the published design and may change before/if this ships live.
9
+ */
10
+ import { MortimerSlayerMaster } from '../core/MortimerSlayerMaster';
11
+ /**
12
+ * Mortimer - Slayer Master of Wyrmscraig
13
+ *
14
+ * Rather than a standard Slayer point table, Mortimer's primary incentive is the Mortifier
15
+ * system (see MortimerSlayerMaster.getAvailableMortifiers()) - though one Mortifier
16
+ * ("Bonus Slayer Points") does award extra Slayer points on top of a task. Every monster he
17
+ * assigns has a superior variant, and he offers 2 task choices per visit (3 once the "Third
18
+ * Task Choice" Mortifier is unlocked at 50 completed tasks), instead of assigning a single task.
19
+ */
20
+ export declare const Mortimer: MortimerSlayerMaster;
21
+ export default Mortimer;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Mortimer - Slayer Master
3
+ * Location: Wyrmscraig
4
+ * Wiki: https://oldschool.runescape.wiki/w/Mortimer
5
+ *
6
+ * NOTE: Mortimer is planned content for the upcoming Wyrmscraig island (Player Designed
7
+ * Island Competition, designed by ScreteMonge) and had not been released at time of writing.
8
+ * Values here reflect the published design and may change before/if this ships live.
9
+ */
10
+ import { MortimerSlayerMaster } from '../core/MortimerSlayerMaster';
11
+ import { mortimerTasks } from '../task/MortimerTasks';
12
+ import { registerMaster } from './registry';
13
+ /**
14
+ * Mortimer - Slayer Master of Wyrmscraig
15
+ *
16
+ * Rather than a standard Slayer point table, Mortimer's primary incentive is the Mortifier
17
+ * system (see MortimerSlayerMaster.getAvailableMortifiers()) - though one Mortifier
18
+ * ("Bonus Slayer Points") does award extra Slayer points on top of a task. Every monster he
19
+ * assigns has a superior variant, and he offers 2 task choices per visit (3 once the "Third
20
+ * Task Choice" Mortifier is unlocked at 50 completed tasks), instead of assigning a single task.
21
+ */
22
+ export const Mortimer = new MortimerSlayerMaster({
23
+ blockSlotCost: 120,
24
+ blockSlots: 2,
25
+ location: 'Wyrmscraig',
26
+ minimumCombatLevel: 0,
27
+ name: 'Mortimer',
28
+ pointsTable: {},
29
+ skipCost: 100,
30
+ tasks: mortimerTasks,
31
+ wikiUrl: 'https://oldschool.runescape.wiki/w/Mortimer',
32
+ });
33
+ // Register in the masters registry
34
+ registerMaster(Mortimer);
35
+ export default Mortimer;
@@ -11,11 +11,13 @@ import './KonarQuoMaten';
11
11
  import './Nieve';
12
12
  import './Duradel';
13
13
  import './Krystilia';
14
+ import './Mortimer';
14
15
  export { Chaeldar } from './Chaeldar';
15
16
  export { Duradel } from './Duradel';
16
17
  export { KonarQuoMaten } from './KonarQuoMaten';
17
18
  export { Krystilia } from './Krystilia';
18
19
  export { Mazchna } from './Mazchna';
20
+ export { Mortimer } from './Mortimer';
19
21
  export { Nieve } from './Nieve';
20
22
  export { Spria } from './Spria';
21
23
  export { Turael } from './Turael';
@@ -13,6 +13,7 @@ import './KonarQuoMaten'; // Combat 75
13
13
  import './Nieve'; // Combat 85
14
14
  import './Duradel'; // Combat 50 (but elite rewards)
15
15
  import './Krystilia'; // Combat 1 (Wilderness)
16
+ import './Mortimer'; // Wyrmscraig (planned/unreleased)
16
17
  import { initializeMastersRegistry, } from './registry';
17
18
  // Export individual masters
18
19
  export { Chaeldar } from './Chaeldar';
@@ -20,6 +21,7 @@ export { Duradel } from './Duradel';
20
21
  export { KonarQuoMaten } from './KonarQuoMaten';
21
22
  export { Krystilia } from './Krystilia';
22
23
  export { Mazchna } from './Mazchna';
24
+ export { Mortimer } from './Mortimer';
23
25
  export { Nieve } from './Nieve';
24
26
  export { Spria } from './Spria';
25
27
  export { Turael } from './Turael';
@@ -45,5 +45,6 @@ export declare enum SlayerMasterProgression {
45
45
  KONAR = "Konar quo Maten",// Combat 75
46
46
  NIEVE = "Nieve",// Combat 85
47
47
  DURADEL = "Duradel",// Combat 50
48
- KRYSTILIA = "Krystilia"
48
+ KRYSTILIA = "Krystilia",// Combat 1 (Wilderness)
49
+ MORTIMER = "Mortimer"
49
50
  }
@@ -73,4 +73,5 @@ export var SlayerMasterProgression;
73
73
  SlayerMasterProgression["NIEVE"] = "Nieve";
74
74
  SlayerMasterProgression["DURADEL"] = "Duradel";
75
75
  SlayerMasterProgression["KRYSTILIA"] = "Krystilia";
76
+ SlayerMasterProgression["MORTIMER"] = "Mortimer";
76
77
  })(SlayerMasterProgression || (SlayerMasterProgression = {}));
@@ -2,6 +2,6 @@
2
2
  * Slayer Masters Module - Barrel Export
3
3
  * Re-exports all masters and utilities from the masters directory
4
4
  */
5
- export { Chaeldar, Duradel, KonarQuoMaten, Krystilia, Mazchna, Nieve, Spria, Turael, Vannaka } from './masters/index';
5
+ export { Chaeldar, Duradel, KonarQuoMaten, Krystilia, Mazchna, Mortimer, Nieve, Spria, Turael, Vannaka, } from './masters/index';
6
6
  export type { SlayerMasterProgression as SlayerMasterProgressionType } from './masters/registry';
7
7
  export { getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, initializeMastersRegistry, registerMaster, SlayerMasterProgression, } from './masters/registry';
@@ -2,5 +2,5 @@
2
2
  * Slayer Masters Module - Barrel Export
3
3
  * Re-exports all masters and utilities from the masters directory
4
4
  */
5
- export { Chaeldar, Duradel, KonarQuoMaten, Krystilia, Mazchna, Nieve, Spria, Turael, Vannaka } from './masters/index';
5
+ export { Chaeldar, Duradel, KonarQuoMaten, Krystilia, Mazchna, Mortimer, Nieve, Spria, Turael, Vannaka, } from './masters/index';
6
6
  export { getAllMasters, getMasterByName, getMastersByMinimumLevel, getMastersByProgression, getRandomMasterForLevel, initializeMastersRegistry, registerMaster, SlayerMasterProgression, } from './masters/registry';
@@ -0,0 +1,14 @@
1
+ import { Task } from '../core/Task';
2
+ /**
3
+ * Mortimer's task list.
4
+ * Reference: https://oldschool.runescape.wiki/w/Mortimer
5
+ *
6
+ * Every task Mortimer assigns has a superior variant (see MortimerSlayerMaster.hasSuperiorVariants()).
7
+ * Quantities, extended quantities and weights below are taken directly from the Wiki's task table.
8
+ *
9
+ * Gryphons, Custodian stalkers, Venators and Aquanites are Wyrmscraig-exclusive monsters with no
10
+ * established Slayer level requirement at time of writing, so their `requirements` are left empty.
11
+ * This content was planned but not yet released; details are subject to change.
12
+ */
13
+ declare const mortimerTasks: Task[];
14
+ export { mortimerTasks };
@@ -0,0 +1,227 @@
1
+ import { CombatLevelRequirement, QuestRequirement, SlayerLevelRequirement } from '../../Requirement';
2
+ import { Task } from '../core/Task';
3
+ /**
4
+ * Mortimer's task list.
5
+ * Reference: https://oldschool.runescape.wiki/w/Mortimer
6
+ *
7
+ * Every task Mortimer assigns has a superior variant (see MortimerSlayerMaster.hasSuperiorVariants()).
8
+ * Quantities, extended quantities and weights below are taken directly from the Wiki's task table.
9
+ *
10
+ * Gryphons, Custodian stalkers, Venators and Aquanites are Wyrmscraig-exclusive monsters with no
11
+ * established Slayer level requirement at time of writing, so their `requirements` are left empty.
12
+ * This content was planned but not yet released; details are subject to change.
13
+ */
14
+ const mortimerTasks = [
15
+ Task.create({
16
+ name: 'Crawling hands',
17
+ quantity: { max: 50, min: 35 },
18
+ requirements: [new SlayerLevelRequirement(5), new CombatLevelRequirement(20)],
19
+ weight: 10,
20
+ }),
21
+ Task.create({
22
+ name: 'Cave crawlers',
23
+ quantity: { max: 50, min: 35 },
24
+ requirements: [new SlayerLevelRequirement(10), new CombatLevelRequirement(10)],
25
+ weight: 10,
26
+ }),
27
+ Task.create({
28
+ name: 'Banshees',
29
+ quantity: { max: 50, min: 35 },
30
+ requirements: [
31
+ new SlayerLevelRequirement(15),
32
+ new CombatLevelRequirement(20),
33
+ new QuestRequirement('Priest in Peril'),
34
+ ],
35
+ weight: 10,
36
+ }),
37
+ Task.create({
38
+ name: 'Rockslugs',
39
+ quantity: { max: 50, min: 35 },
40
+ requirements: [new SlayerLevelRequirement(20)],
41
+ weight: 10,
42
+ }),
43
+ Task.create({
44
+ name: 'Cockatrice',
45
+ quantity: { max: 50, min: 35 },
46
+ requirements: [new SlayerLevelRequirement(25)],
47
+ weight: 10,
48
+ }),
49
+ Task.create({
50
+ name: 'Pyrefiends',
51
+ quantity: { max: 50, min: 35 },
52
+ requirements: [new SlayerLevelRequirement(30)],
53
+ weight: 10,
54
+ }),
55
+ Task.create({
56
+ name: 'Infernal mages',
57
+ quantity: { max: 50, min: 35 },
58
+ requirements: [
59
+ new SlayerLevelRequirement(45),
60
+ new CombatLevelRequirement(45),
61
+ new QuestRequirement('Priest in Peril'),
62
+ ],
63
+ weight: 10,
64
+ }),
65
+ Task.create({
66
+ extended: { max: 250, min: 200 },
67
+ name: 'Bloodvelds',
68
+ quantity: { max: 180, min: 120 },
69
+ requirements: [new SlayerLevelRequirement(50), new CombatLevelRequirement(50)],
70
+ weight: 8,
71
+ }),
72
+ Task.create({
73
+ extended: { max: 200, min: 160 },
74
+ name: 'Gryphons',
75
+ quantity: { max: 120, min: 80 },
76
+ requirements: [],
77
+ weight: 10,
78
+ }),
79
+ Task.create({
80
+ name: 'Jellies',
81
+ quantity: { max: 120, min: 80 },
82
+ requirements: [new SlayerLevelRequirement(52)],
83
+ weight: 10,
84
+ }),
85
+ Task.create({
86
+ extended: { max: 250, min: 200 },
87
+ name: 'Custodian stalkers',
88
+ quantity: { max: 120, min: 80 },
89
+ requirements: [],
90
+ weight: 8,
91
+ }),
92
+ Task.create({
93
+ name: 'Turoth',
94
+ quantity: { max: 120, min: 80 },
95
+ requirements: [new SlayerLevelRequirement(55)],
96
+ weight: 10,
97
+ }),
98
+ Task.create({
99
+ name: 'Warped creatures',
100
+ quantity: { max: 120, min: 80 },
101
+ requirements: [new SlayerLevelRequirement(56)],
102
+ weight: 10,
103
+ }),
104
+ Task.create({
105
+ extended: { max: 250, min: 200 },
106
+ name: 'Cave horrors',
107
+ quantity: { max: 120, min: 80 },
108
+ requirements: [new SlayerLevelRequirement(58), new CombatLevelRequirement(40), new QuestRequirement('Cabin Fever')],
109
+ weight: 10,
110
+ }),
111
+ Task.create({
112
+ alternatives: ['Deviant spectres'],
113
+ extended: { max: 250, min: 200 },
114
+ name: 'Aberrant spectres',
115
+ quantity: { max: 120, min: 80 },
116
+ requirements: [
117
+ new SlayerLevelRequirement(60),
118
+ new CombatLevelRequirement(65),
119
+ new QuestRequirement('Priest in Peril'),
120
+ ],
121
+ weight: 10,
122
+ }),
123
+ Task.create({
124
+ bosses: ['Basilisk Knight'],
125
+ extended: { max: 250, min: 200 },
126
+ name: 'Basilisks',
127
+ quantity: { max: 60, min: 40 },
128
+ requirements: [new SlayerLevelRequirement(40), new CombatLevelRequirement(40)],
129
+ weight: 10,
130
+ }),
131
+ Task.create({
132
+ extended: { max: 250, min: 200 },
133
+ name: 'Wyrms',
134
+ quantity: { max: 120, min: 80 },
135
+ requirements: [new SlayerLevelRequirement(62)],
136
+ weight: 10,
137
+ }),
138
+ Task.create({
139
+ extended: { max: 250, min: 200 },
140
+ name: 'Dust devils',
141
+ quantity: { max: 180, min: 120 },
142
+ requirements: [new SlayerLevelRequirement(65), new QuestRequirement('Desert Treasure')],
143
+ weight: 8,
144
+ }),
145
+ Task.create({
146
+ name: 'Kurask',
147
+ quantity: { max: 60, min: 40 },
148
+ requirements: [new SlayerLevelRequirement(70), new CombatLevelRequirement(80)],
149
+ weight: 10,
150
+ }),
151
+ Task.create({
152
+ name: 'Venators',
153
+ quantity: { max: 180, min: 120 },
154
+ requirements: [],
155
+ weight: 10,
156
+ }),
157
+ Task.create({
158
+ extended: { max: 250, min: 200 },
159
+ name: 'Gargoyles',
160
+ quantity: { max: 180, min: 120 },
161
+ requirements: [new SlayerLevelRequirement(75), new CombatLevelRequirement(80)],
162
+ weight: 10,
163
+ }),
164
+ Task.create({
165
+ extended: { max: 200, min: 150 },
166
+ name: 'Aquanites',
167
+ quantity: { max: 60, min: 40 },
168
+ requirements: [],
169
+ weight: 10,
170
+ }),
171
+ Task.create({
172
+ bosses: ['Nechryarch'],
173
+ extended: { max: 250, min: 200 },
174
+ name: 'Nechryael',
175
+ quantity: { max: 200, min: 150 },
176
+ requirements: [new SlayerLevelRequirement(80), new CombatLevelRequirement(80)],
177
+ weight: 8,
178
+ }),
179
+ Task.create({
180
+ name: 'Drakes',
181
+ quantity: { max: 60, min: 40 },
182
+ requirements: [new SlayerLevelRequirement(84)],
183
+ weight: 10,
184
+ }),
185
+ Task.create({
186
+ bosses: ['Abyssal Sire'],
187
+ extended: { max: 250, min: 200 },
188
+ name: 'Abyssal demons',
189
+ quantity: { max: 180, min: 120 },
190
+ requirements: [
191
+ new SlayerLevelRequirement(85),
192
+ new CombatLevelRequirement(85),
193
+ new QuestRequirement('Priest in Peril'),
194
+ new QuestRequirement('Fairytale II - Cure a Queen'),
195
+ ],
196
+ weight: 8,
197
+ }),
198
+ Task.create({
199
+ extended: { max: 135, min: 110 },
200
+ name: 'Dark beasts',
201
+ quantity: { max: 60, min: 40 },
202
+ requirements: [new SlayerLevelRequirement(90), new QuestRequirement("Mourning's End Part II")],
203
+ weight: 10,
204
+ }),
205
+ Task.create({
206
+ extended: { max: 250, min: 200 },
207
+ name: 'Araxytes',
208
+ quantity: { max: 180, min: 120 },
209
+ requirements: [new SlayerLevelRequirement(92), new QuestRequirement('Sins of the Father')],
210
+ weight: 8,
211
+ }),
212
+ Task.create({
213
+ bosses: ['Thermonuclear Smoke Devil'],
214
+ name: 'Smoke devils',
215
+ quantity: { max: 120, min: 80 },
216
+ requirements: [new SlayerLevelRequirement(93)],
217
+ weight: 8,
218
+ }),
219
+ Task.create({
220
+ bosses: ['Alchemical Hydra'],
221
+ name: 'Hydras',
222
+ quantity: { max: 200, min: 150 },
223
+ requirements: [new SlayerLevelRequirement(95), new QuestRequirement('Dragon Slayer II')],
224
+ weight: 10,
225
+ }),
226
+ ];
227
+ export { mortimerTasks };
@@ -7,6 +7,7 @@ export * from '../task/DuradelTasks';
7
7
  export * from '../task/KonarTasks';
8
8
  export * from '../task/KrystiliaTasks';
9
9
  export * from '../task/MazchnaTasks';
10
+ export * from '../task/MortimerTasks';
10
11
  export * from '../task/NieveTasks';
11
12
  export * from '../task/SpriaTasks';
12
13
  export * from '../task/TurealTasks';
@@ -8,6 +8,7 @@ export * from '../task/DuradelTasks';
8
8
  export * from '../task/KonarTasks';
9
9
  export * from '../task/KrystiliaTasks';
10
10
  export * from '../task/MazchnaTasks';
11
+ export * from '../task/MortimerTasks';
11
12
  export * from '../task/NieveTasks';
12
13
  export * from '../task/SpriaTasks';
13
14
  export * from '../task/TurealTasks';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osrs-tools",
3
- "version": "2.9.4",
3
+ "version": "2.11.0",
4
4
  "description": "A comprehensive TypeScript library for Old School RuneScape (OSRS) data and utilities, including quest data, skill requirements, and general game information",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,