osrs-tools 2.12.0 → 2.13.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.
@@ -1,7 +1,7 @@
1
1
  import { Mortifier } from './Mortifier';
2
2
  import { SlayerMaster } from './SlayerMaster';
3
3
  import { Task } from './Task';
4
- import { SlayerMasterConfig } from './types';
4
+ import { SlayerAssignmentContext, SlayerMasterConfig } from './types';
5
5
  /**
6
6
  * Configuration for Mortimer, extending the standard SlayerMaster config with the
7
7
  * quirks that make him behave differently to every other Slayer Master.
@@ -64,8 +64,12 @@ declare class MortimerSlayerMaster extends SlayerMaster {
64
64
  * Get a set of distinct, weighted-random task choices for a Mortimer visit.
65
65
  * The number of choices scales with completedTasks (see getTaskChoiceCount()).
66
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.
67
+ * @param {number} [completedTasks] - Number of tasks completed with Mortimer so far.
68
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
69
+ * quests, and unlocked rewards; tasks the player doesn't qualify for are excluded (see
70
+ * {@link SlayerMaster.getAvailableTasks}).
71
+ * @returns {Task[]} The offered tasks. May be shorter than requested if there aren't enough distinct eligible tasks.
68
72
  */
69
- getTaskChoices(completedTasks?: number): Task[];
73
+ getTaskChoices(completedTasks?: number, context?: SlayerAssignmentContext): Task[];
70
74
  }
71
75
  export { MortimerSlayerMaster };
@@ -67,11 +67,15 @@ class MortimerSlayerMaster extends SlayerMaster {
67
67
  * Get a set of distinct, weighted-random task choices for a Mortimer visit.
68
68
  * The number of choices scales with completedTasks (see getTaskChoiceCount()).
69
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.
70
+ * @param {number} [completedTasks] - Number of tasks completed with Mortimer so far.
71
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
72
+ * quests, and unlocked rewards; tasks the player doesn't qualify for are excluded (see
73
+ * {@link SlayerMaster.getAvailableTasks}).
74
+ * @returns {Task[]} The offered tasks. May be shorter than requested if there aren't enough distinct eligible tasks.
71
75
  */
72
- getTaskChoices(completedTasks = 0) {
76
+ getTaskChoices(completedTasks = 0, context) {
73
77
  const choiceCount = this.getTaskChoiceCount(completedTasks);
74
- const remainingTasks = this.getAvailableTasks().slice();
78
+ const remainingTasks = this.getAvailableTasks(context).slice();
75
79
  const choices = [];
76
80
  while (choices.length < choiceCount && remainingTasks.length > 0) {
77
81
  const totalWeight = remainingTasks.reduce((total, task) => total + (task.weight || 0), 0);
@@ -1,5 +1,5 @@
1
1
  import { Task } from './Task';
2
- import { SlayerMasterConfig } from './types';
2
+ import { SlayerAssignmentContext, SlayerMasterConfig } from './types';
3
3
  /**
4
4
  * SlayerMaster class represents a Slayer Master in the game.
5
5
  * It encapsulates the properties and methods related to a Slayer Master,
@@ -51,9 +51,32 @@ declare class SlayerMaster {
51
51
  /**
52
52
  * Get a random task based on the weighting system of the tasks.
53
53
  * Tasks with higher weights have a higher chance of being selected.
54
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
55
+ * quests, and unlocked rewards. When provided, tasks the player doesn't qualify for are
56
+ * excluded from both the candidate pool and the weight sum, per the task weighting formula
57
+ * (see https://oldschool.runescape.wiki/w/Slayer#Task_weighting). When omitted, only blocked
58
+ * tasks are excluded.
54
59
  * @returns {Task | null} A randomly selected task or null if no tasks are available.
55
60
  */
56
- getRandomTask(): Task | null;
61
+ getRandomTask(context?: SlayerAssignmentContext): Task | null;
62
+ /**
63
+ * Get the percentage chance of being assigned each currently available task:
64
+ * (w / S) * 100, where w is a task's weight and S is the sum of weights of every task
65
+ * the player currently qualifies for (blocked, toggled-off, and requirement-locked tasks
66
+ * excluded from S). Reference: https://oldschool.runescape.wiki/w/Slayer#Task_weighting
67
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
68
+ * quests, and unlocked rewards. When omitted, only blocked tasks are excluded from S.
69
+ * @returns {Map<string, number>} Task name to percentage chance (0-100), for eligible tasks only.
70
+ */
71
+ getTaskChances(context?: SlayerAssignmentContext): Map<string, number>;
72
+ /**
73
+ * Get the percentage chance of being assigned a specific task, by name (case insensitive).
74
+ * @param {string} taskName - Name of the task to check.
75
+ * @param {SlayerAssignmentContext} [context] - See {@link getTaskChances}.
76
+ * @returns {number} The percentage chance (0-100) of being assigned this task, or 0 if the
77
+ * task doesn't exist or isn't currently available to the player.
78
+ */
79
+ getTaskChance(taskName: string, context?: SlayerAssignmentContext): number;
57
80
  /**
58
81
  * Block a task so it will no longer be returned by getRandomTask().
59
82
  * Mirrors the in-game block list: only tasks this master actually assigns can be blocked.
@@ -76,9 +99,14 @@ declare class SlayerMaster {
76
99
  */
77
100
  getBlockedTasks(): string[];
78
101
  /**
79
- * Get the tasks this master can currently assign, excluding blocked ones.
102
+ * Get the tasks this master can currently assign: excludes blocked tasks, and - when a
103
+ * player context is given - also excludes tasks whose Slayer level, combat level, skill,
104
+ * quest, or unlock requirements the player doesn't yet meet. A boosted Slayer level never
105
+ * unlocks a task; pass the player's base level.
106
+ * @param {SlayerAssignmentContext} [context] - The requesting player's stats/unlocks. When
107
+ * omitted, requirement eligibility isn't checked (only the block list is applied).
80
108
  */
81
- getAvailableTasks(): Task[];
109
+ getAvailableTasks(context?: SlayerAssignmentContext): Task[];
82
110
  /**
83
111
  * Get the total weight of tasks
84
112
  */
@@ -1,3 +1,4 @@
1
+ import { meetsRequirements } from './eligibility';
1
2
  /**
2
3
  * SlayerMaster class represents a Slayer Master in the game.
3
4
  * It encapsulates the properties and methods related to a Slayer Master,
@@ -95,12 +96,17 @@ class SlayerMaster {
95
96
  /**
96
97
  * Get a random task based on the weighting system of the tasks.
97
98
  * Tasks with higher weights have a higher chance of being selected.
99
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
100
+ * quests, and unlocked rewards. When provided, tasks the player doesn't qualify for are
101
+ * excluded from both the candidate pool and the weight sum, per the task weighting formula
102
+ * (see https://oldschool.runescape.wiki/w/Slayer#Task_weighting). When omitted, only blocked
103
+ * tasks are excluded.
98
104
  * @returns {Task | null} A randomly selected task or null if no tasks are available.
99
105
  */
100
- getRandomTask() {
101
- const availableTasks = this.getAvailableTasks();
106
+ getRandomTask(context) {
107
+ const availableTasks = this.getAvailableTasks(context);
102
108
  if (availableTasks.length === 0)
103
- return null; // Return null if there are no unblocked tasks
109
+ return null; // Return null if there are no eligible tasks
104
110
  const availableWeight = availableTasks.reduce((total, task) => total + (task.weight || 0), 0);
105
111
  if (availableWeight <= 0)
106
112
  return null;
@@ -114,6 +120,41 @@ class SlayerMaster {
114
120
  }
115
121
  return null; // Fallback in case no task is selected (shouldn't happen if weights are correct)
116
122
  }
123
+ /**
124
+ * Get the percentage chance of being assigned each currently available task:
125
+ * (w / S) * 100, where w is a task's weight and S is the sum of weights of every task
126
+ * the player currently qualifies for (blocked, toggled-off, and requirement-locked tasks
127
+ * excluded from S). Reference: https://oldschool.runescape.wiki/w/Slayer#Task_weighting
128
+ * @param {SlayerAssignmentContext} [context] - The requesting player's Slayer level, completed
129
+ * quests, and unlocked rewards. When omitted, only blocked tasks are excluded from S.
130
+ * @returns {Map<string, number>} Task name to percentage chance (0-100), for eligible tasks only.
131
+ */
132
+ getTaskChances(context) {
133
+ const availableTasks = this.getAvailableTasks(context);
134
+ const availableWeight = availableTasks.reduce((total, task) => total + (task.weight || 0), 0);
135
+ const chances = new Map();
136
+ if (availableWeight <= 0)
137
+ return chances;
138
+ for (const task of availableTasks) {
139
+ chances.set(task.getName(), ((task.weight || 0) / availableWeight) * 100);
140
+ }
141
+ return chances;
142
+ }
143
+ /**
144
+ * Get the percentage chance of being assigned a specific task, by name (case insensitive).
145
+ * @param {string} taskName - Name of the task to check.
146
+ * @param {SlayerAssignmentContext} [context] - See {@link getTaskChances}.
147
+ * @returns {number} The percentage chance (0-100) of being assigned this task, or 0 if the
148
+ * task doesn't exist or isn't currently available to the player.
149
+ */
150
+ getTaskChance(taskName, context) {
151
+ const normalized = taskName.toLowerCase();
152
+ for (const [name, chance] of this.getTaskChances(context)) {
153
+ if (name.toLowerCase() === normalized)
154
+ return chance;
155
+ }
156
+ return 0;
157
+ }
117
158
  /**
118
159
  * Block a task so it will no longer be returned by getRandomTask().
119
160
  * Mirrors the in-game block list: only tasks this master actually assigns can be blocked.
@@ -149,10 +190,21 @@ class SlayerMaster {
149
190
  return Array.from(this.blockedTasks);
150
191
  }
151
192
  /**
152
- * Get the tasks this master can currently assign, excluding blocked ones.
153
- */
154
- getAvailableTasks() {
155
- return this.tasks.filter(task => !this.isTaskBlocked(task.getName()));
193
+ * Get the tasks this master can currently assign: excludes blocked tasks, and - when a
194
+ * player context is given - also excludes tasks whose Slayer level, combat level, skill,
195
+ * quest, or unlock requirements the player doesn't yet meet. A boosted Slayer level never
196
+ * unlocks a task; pass the player's base level.
197
+ * @param {SlayerAssignmentContext} [context] - The requesting player's stats/unlocks. When
198
+ * omitted, requirement eligibility isn't checked (only the block list is applied).
199
+ */
200
+ getAvailableTasks(context) {
201
+ return this.tasks.filter(task => {
202
+ if (this.isTaskBlocked(task.getName()))
203
+ return false;
204
+ if (!context)
205
+ return true;
206
+ return meetsRequirements(task.getRequirements(), context);
207
+ });
156
208
  }
157
209
  /**
158
210
  * Get the total weight of tasks
@@ -0,0 +1,7 @@
1
+ import { Requirement } from '../../Requirement';
2
+ import { SlayerAssignmentContext } from './types';
3
+ /**
4
+ * Checks whether every requirement in the list is satisfied by the given player context.
5
+ * A task with no requirements is always eligible.
6
+ */
7
+ export declare function meetsRequirements(requirements: Requirement[], context: SlayerAssignmentContext): boolean;
@@ -0,0 +1,41 @@
1
+ import { RequirementType, } from '../../Requirement';
2
+ function toLowerCaseSet(values) {
3
+ const result = new Set();
4
+ if (!values)
5
+ return result;
6
+ for (const value of values)
7
+ result.add(value.toLowerCase());
8
+ return result;
9
+ }
10
+ /**
11
+ * Checks whether a single requirement is satisfied by the given player context.
12
+ * Requirement types that don't gate task assignment (e.g. items to bring) are
13
+ * always treated as satisfied - only Slayer level, combat level, skill level,
14
+ * quest, and Slayer unlock requirements can block an assignment.
15
+ */
16
+ function meetsRequirement(requirement, context) {
17
+ switch (requirement.type) {
18
+ case RequirementType.SlayerLevel:
19
+ return (context.slayerLevel ?? 0) >= requirement.level;
20
+ case RequirementType.CombatLevel:
21
+ return (context.combatLevel ?? 0) >= requirement.level;
22
+ case RequirementType.Level: {
23
+ const levelRequirement = requirement;
24
+ const playerLevel = context.skillLevels?.[levelRequirement.skillName] ?? 0;
25
+ return playerLevel >= levelRequirement.level;
26
+ }
27
+ case RequirementType.Quest:
28
+ return toLowerCaseSet(context.completedQuests).has(requirement.questName.toLowerCase());
29
+ case RequirementType.SlayerUnlock:
30
+ return toLowerCaseSet(context.unlockedRewards).has(requirement.name.toLowerCase());
31
+ default:
32
+ return true;
33
+ }
34
+ }
35
+ /**
36
+ * Checks whether every requirement in the list is satisfied by the given player context.
37
+ * A task with no requirements is always eligible.
38
+ */
39
+ export function meetsRequirements(requirements, context) {
40
+ return requirements.every(requirement => meetsRequirement(requirement, context));
41
+ }
@@ -10,5 +10,5 @@ export { MortimerSlayerMaster } from './MortimerSlayerMaster';
10
10
  export { SlayerMaster } from './SlayerMaster';
11
11
  export type { TaskJson } from './Task';
12
12
  export { Task } from './Task';
13
- export type { SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity } from './types';
13
+ export type { SlayerAssignmentContext, SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity, } from './types';
14
14
  export { POINTS_TABLE, SlayerBoss, SlayerLocation, TASK_DEFAULTS } from './types';
@@ -94,6 +94,24 @@ export interface TaskDefinition {
94
94
  * Builder options for creating tasks (alias for TaskDefinition)
95
95
  */
96
96
  export type TaskOptions = TaskDefinition;
97
+ /**
98
+ * Describes what a specific player currently has access to, for the purposes of
99
+ * filtering which Slayer tasks a master is allowed to assign them.
100
+ * All fields are optional; an omitted field is treated as "player has none of these"
101
+ * (e.g. no completed quests), which is the conservative/safe default for eligibility checks.
102
+ */
103
+ export interface SlayerAssignmentContext {
104
+ /** The player's base (unboosted) Slayer level. Boosts never unlock higher-tier tasks. */
105
+ slayerLevel?: number;
106
+ /** The player's combat level. */
107
+ combatLevel?: number;
108
+ /** Names of quests the player has completed (case-insensitive). */
109
+ completedQuests?: Iterable<string>;
110
+ /** Names of Slayer unlocks the player has purchased with reward points (case-insensitive). */
111
+ unlockedRewards?: Iterable<string>;
112
+ /** Base skill levels keyed by skill name, for tasks with non-Slayer skill requirements. */
113
+ skillLevels?: Partial<Record<string, number>>;
114
+ }
97
115
  /**
98
116
  * Slayer Master configuration interface
99
117
  */
@@ -3,7 +3,7 @@
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 { MortifierConfig, MortimerSlayerMasterConfig, SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity, } from './core';
6
+ export type { MortifierConfig, MortimerSlayerMasterConfig, SlayerAssignmentContext, SlayerMasterConfig, TaskDefinition, TaskOptions, TaskQuantity, } from './core';
7
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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osrs-tools",
3
- "version": "2.12.0",
3
+ "version": "2.13.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,