diff --git a/package.json b/package.json
index bd08bb2f..d5e9b240 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,11 @@
"fmt": "next lint --fix",
"analyse": "ANALYSE=true next build",
"test": "jest",
- "cdn-docker": "yarn build-docker-scraper && docker run --rm -it -v $(pwd)/cdn:/srv/cdn -v $(pwd)/src/lib/EquipmentAliases.ts:/srv/src/lib/EquipmentAliases.ts weirdgloop/osrs-dps-calc:scraper",
+ "cdn-docker-base": "docker run --rm -it -v $(pwd)/scripts:/srv/scripts -v $(pwd)/cdn:/srv/cdn -v $(pwd)/src/lib/EquipmentAliases.ts:/srv/src/lib/EquipmentAliases.ts weirdgloop/osrs-dps-calc:scraper",
+ "cdn-docker": "yarn build-docker-scraper && yarn cdn-docker-base",
+ "cdn-docker-aliases": "yarn cdn-docker-base python generateEquipmentAliases.py",
+ "cdn-docker-equipment": "yarn cdn-docker-base python generateEquipment.py",
+ "cdn-docker-monsters": "yarn cdn-docker-base python generateMonsters.py",
"cdn-dispatch": "gh workflow run regenerate.yml"
},
"dependencies": {
diff --git a/src/app/home.tsx b/src/app/home.tsx
index 6f153faf..b66ed6d0 100644
--- a/src/app/home.tsx
+++ b/src/app/home.tsx
@@ -3,7 +3,7 @@
import type { NextPage } from 'next';
import MonsterContainer from '@/app/components/monster/MonsterContainer';
import { Tooltip } from 'react-tooltip';
-import React, { Suspense, useEffect } from 'react';
+import React, { Suspense, useEffect, useMemo } from 'react';
import { observer } from 'mobx-react-lite';
import { useStore } from '@/state';
import { ToastContainer } from 'react-toastify';
@@ -18,6 +18,11 @@ import DebugPanels from '@/app/components/results/DebugPanels';
import { IconAlertTriangle } from '@tabler/icons-react';
import NPCVersusPlayerResultsContainer from '@/app/components/results/NPCVersusPlayerResultsContainer';
import { CalcProvider, useCalc } from '@/worker/CalcWorker';
+import UserIssueType from '@/enums/UserIssueType';
+
+const GLOBAL_ISSUE_TYPES: UserIssueType[] = [
+ UserIssueType.IMPORT_MISSING_DATA,
+];
const Home: NextPage = observer(() => {
const calc = useCalc();
@@ -91,6 +96,23 @@ const Home: NextPage = observer(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ const globalIssues = useMemo(() => {
+ const issues = store.userIssues.filter((is) => GLOBAL_ISSUE_TYPES.includes(is.type));
+ return (
+ <>
+ {issues.map((is) => (
+
{store.prefs.manualMode && (
@@ -103,6 +125,7 @@ const Home: NextPage = observer(() => {
Manual mode is enabled! Some things may not function correctly. Click here to disable it.
)}
+ {globalIssues}
diff --git a/src/enums/UserIssueType.ts b/src/enums/UserIssueType.ts
index c7e65cf5..34da3741 100644
--- a/src/enums/UserIssueType.ts
+++ b/src/enums/UserIssueType.ts
@@ -1,10 +1,12 @@
enum UserIssueType {
EQUIPMENT_MISSING_AMMO = 'equipment_slot_ammo_missing',
EQUIPMENT_WRONG_AMMO = 'equipment_slot_ammo_wrong',
+ EQUIPMENT_WEAPON_EMPTY = 'equipment_slot_weapon_empty',
EQUIPMENT_SET_EFFECT_UNSUPPORTED = 'equipment_slot_body_unsupported_set_effect',
SPELL_WRONG_WEAPON = 'spell_wrong_weapon',
SPELL_WRONG_MONSTER = 'spell_wrong_monster',
MONSTER_UNIQUE_EFFECTS = 'monster_overall_unique_effects',
+ IMPORT_MISSING_DATA = 'runelite_import_missing_data',
}
export default UserIssueType;
diff --git a/src/lib/BaseCalc.ts b/src/lib/BaseCalc.ts
index 3034ce1b..6aa52d8a 100644
--- a/src/lib/BaseCalc.ts
+++ b/src/lib/BaseCalc.ts
@@ -1,6 +1,11 @@
import { EquipmentPiece, Player } from '@/types/Player';
import { Monster } from '@/types/Monster';
-import { AmmoApplicability, ammoApplicability, getCanonicalEquipment } from '@/lib/Equipment';
+import {
+ AmmoApplicability,
+ ammoApplicability,
+ EMPTY_BLOWPIPE,
+ getCanonicalEquipment,
+} from '@/lib/Equipment';
import UserIssueType from '@/enums/UserIssueType';
import { MonsterAttribute } from '@/enums/MonsterAttribute';
import { CAST_STANCES } from '@/lib/constants';
@@ -58,6 +63,8 @@ export default class BaseCalc {
userIssues: UserIssue[] = [];
+ protected fatal: boolean = false;
+
constructor(player: Player, monster: Monster, opts: CalcOpts = {}) {
this.opts = {
...DEFAULT_OPTS,
@@ -532,8 +539,14 @@ export default class BaseCalc {
]);
}
- protected addIssue(type: UserIssueType, message: string) {
- this.userIssues.push({ type, message, loadout: this.opts.loadoutName });
+ protected addIssue(type: UserIssueType, message: string, severity: UserIssue['severity'] = 'warn') {
+ this.userIssues.push({
+ type,
+ severity,
+ message,
+ loadout: this.opts.loadoutName,
+ });
+ this.fatal ||= severity === 'fatal';
}
private sanitizeInputs() {
@@ -558,11 +571,12 @@ export default class BaseCalc {
};
}
- if (this.player.style.stance !== 'Manual Cast' && ammoApplicability(eq.weapon?.id, eq.ammo?.id) === AmmoApplicability.INVALID) {
+ if (ammoApplicability(eq.weapon?.id, eq.ammo?.id) === AmmoApplicability.INVALID) {
+ const severity = this.player.style.stance === 'Manual Cast' ? 'warn' : 'fatal';
if (eq.ammo?.name) {
- this.addIssue(UserIssueType.EQUIPMENT_WRONG_AMMO, 'This ammo does not work with your current weapon.');
+ this.addIssue(UserIssueType.EQUIPMENT_WRONG_AMMO, 'This ammo does not work with your current weapon.', severity);
} else {
- this.addIssue(UserIssueType.EQUIPMENT_MISSING_AMMO, 'Your weapon requires ammo to use.');
+ this.addIssue(UserIssueType.EQUIPMENT_MISSING_AMMO, 'Your weapon requires ammo to use.', severity);
}
}
@@ -579,7 +593,7 @@ export default class BaseCalc {
...this.player,
spell: null,
};
- this.addIssue(UserIssueType.SPELL_WRONG_WEAPON, 'This spell needs a specific weapon equipped to cast.');
+ this.addIssue(UserIssueType.SPELL_WRONG_WEAPON, 'This spell needs a specific weapon equipped to cast.', 'fatal');
}
// Certain spells can only be cast on specific monsters
@@ -591,7 +605,7 @@ export default class BaseCalc {
...this.player,
spell: null,
};
- this.addIssue(UserIssueType.SPELL_WRONG_MONSTER, 'This spell cannot be cast on the selected monster.');
+ this.addIssue(UserIssueType.SPELL_WRONG_MONSTER, 'This spell cannot be cast on the selected monster.', 'fatal');
}
// Some set effects are currently not accounted for
@@ -601,5 +615,14 @@ export default class BaseCalc {
) {
this.addIssue(UserIssueType.EQUIPMENT_SET_EFFECT_UNSUPPORTED, 'The calculator currently does not account for your equipment set effect.');
}
+
+ if (this.wearing(['Toxic blowpipe', 'Blazing blowpipe'])) {
+ const severity = this.player.style.stance === 'Manual Cast' ? 'warn' : 'fatal';
+ if (eq.weapon?.version === 'Empty') {
+ this.addIssue(UserIssueType.EQUIPMENT_WEAPON_EMPTY, 'An empty weapon cannot be used.', severity);
+ } else if (eq.weapon?.bonuses?.ranged_str === EMPTY_BLOWPIPE?.bonuses?.ranged_str) {
+ this.addIssue(UserIssueType.IMPORT_MISSING_DATA, 'The import data did not specify darts for the blowpipe equipped', severity);
+ }
+ }
}
}
diff --git a/src/lib/Equipment.ts b/src/lib/Equipment.ts
index 6b2e22ee..ea500bea 100644
--- a/src/lib/Equipment.ts
+++ b/src/lib/Equipment.ts
@@ -12,6 +12,9 @@ export type EquipmentBonuses = Pick
e.name === 'Toxic blowpipe' && e.version === 'Charged',
+);
export const noStatExceptions = [
'Castle wars bracelet',
diff --git a/src/lib/PlayerVsNPCCalc.ts b/src/lib/PlayerVsNPCCalc.ts
index d8d7f554..d5272915 100644
--- a/src/lib/PlayerVsNPCCalc.ts
+++ b/src/lib/PlayerVsNPCCalc.ts
@@ -707,12 +707,8 @@ export default class PlayerVsNPCCalc extends BaseCalc {
* Get the max hit for this loadout, which is based on the player's current combat style
*/
private getMaxHit(): MinMax {
- if (this.player.style.stance !== 'Manual Cast') {
- const weaponId = this.player.equipment.weapon?.id;
- const ammoId = this.player.equipment.ammo?.id;
- if (ammoApplicability(weaponId, ammoId) === AmmoApplicability.INVALID) {
- return [0, 0];
- }
+ if (this.fatal) {
+ return [0, 0];
}
const style = this.player.style.type;
@@ -818,6 +814,10 @@ export default class PlayerVsNPCCalc extends BaseCalc {
}
private getDistributionImpl(): AttackDistribution {
+ if (this.fatal) {
+ return new AttackDistribution([new HitDistribution([new WeightedHit(1.0, [new Hitsplat(0, false)])])]);
+ }
+
const mattrs = this.monster.attributes;
const acc = this.getHitChance();
const [min, max] = this.getMaxHit();
diff --git a/src/types/State.ts b/src/types/State.ts
index b1aac6d8..278d7dec 100644
--- a/src/types/State.ts
+++ b/src/types/State.ts
@@ -6,6 +6,7 @@ import { DetailEntry } from '@/lib/CalcDetails';
export interface UserIssue {
type: UserIssueType;
+ severity: 'warn' | 'fatal';
message: string;
loadout?: string;
}