Skip to content

Commit 2f873bf

Browse files
committed
[Update] Add magical creatures button to Monster UI
1 parent f0d27d6 commit 2f873bf

File tree

8 files changed

+115
-67
lines changed

8 files changed

+115
-67
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bpsr-meter",
3-
"version": "0.3.8",
3+
"version": "0.3.9",
44
"description": "BPSR Meter",
55
"author": "Denoder",
66
"type": "module",

src/renderer/src/monsters/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default function MonstersApp(): React.JSX.Element {
3131
const [sortKey, setSortKey] = useState<"id" | "name" | "hp" | "distance">("distance");
3232
const [sortDesc, setSortDesc] = useState<boolean>(false);
3333
const [translatedNames, setTranslatedNames] = useState<Record<number, string>>({});
34-
const [bossOnlyMode, setBossOnlyMode] = useState<boolean>(false);
34+
const [filterMode, setFilterMode] = useState<"all" | "bosses" | "magical">("all");
3535
const [currentTime, setCurrentTime] = useState<Date>(new Date());
3636
const [activeTab, setActiveTab] = useState<"monsters" | "schedule">("monsters");
3737
const previousMonstersRef = useRef<Record<string, MonsterEntry>>({});
@@ -244,8 +244,8 @@ export default function MonstersApp(): React.JSX.Element {
244244
) : activeTab === "monsters" ? (
245245
<MonsterList
246246
monsters={monsters}
247-
bossOnlyMode={bossOnlyMode}
248-
setBossOnlyMode={setBossOnlyMode}
247+
filterMode={filterMode}
248+
setFilterMode={setFilterMode}
249249
sortKey={sortKey}
250250
setSortKey={setSortKey}
251251
sortDesc={sortDesc}

src/renderer/src/monsters/MonsterList.tsx

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import SortDropdown from "./SortDropdown";
3-
import { TRACKED_MONSTER_IDS } from "../shared/constants";
3+
import { BOSS_MONSTER_IDS, MAGICAL_CREATURE_IDS } from "../shared/constants";
44
import { formatStat } from "../shared/utils/formatters";
55
import { getNextSpawnTime, formatTimeUntilSpawn } from "./bossSpawnTimes";
66
import { monsterRespawnTracker } from "./monsterRespawnTracker";
@@ -18,8 +18,8 @@ interface MonsterEntry {
1818

1919
interface MonsterListProps {
2020
monsters: Record<string, MonsterEntry>;
21-
bossOnlyMode: boolean;
22-
setBossOnlyMode: (value: boolean | ((prev: boolean) => boolean)) => void;
21+
filterMode: "all" | "bosses" | "magical";
22+
setFilterMode: (value: "all" | "bosses" | "magical") => void;
2323
sortKey: "id" | "name" | "hp" | "distance";
2424
setSortKey: (value: "id" | "name" | "hp" | "distance") => void;
2525
sortDesc: boolean;
@@ -29,35 +29,69 @@ interface MonsterListProps {
2929

3030
export default function MonsterList({
3131
monsters,
32-
bossOnlyMode,
33-
setBossOnlyMode,
32+
filterMode,
33+
setFilterMode,
3434
sortKey,
3535
setSortKey,
3636
sortDesc,
3737
setSortDesc,
3838
t
3939
}: MonsterListProps): React.JSX.Element {
40+
const getFilteredCount = () => {
41+
if (filterMode === "all") return Object.keys(monsters).length;
42+
if (filterMode === "bosses") {
43+
return Object.values(monsters).filter(m => m.monster_id && BOSS_MONSTER_IDS.has(String(m.monster_id))).length;
44+
}
45+
if (filterMode === "magical") {
46+
return Object.values(monsters).filter(m => m.monster_id && MAGICAL_CREATURE_IDS.has(String(m.monster_id))).length;
47+
}
48+
return 0;
49+
};
50+
51+
const MERGED_MONSTER_IDS = new Set([
52+
...BOSS_MONSTER_IDS,
53+
...MAGICAL_CREATURE_IDS
54+
]);
55+
4056
return (
4157
<div className="monsters-container">
4258
<div className="flex justify-between items-center mb-2 gap-2">
4359
<div className="flex items-center gap-2">
4460
<div className="text-sm font-semibold">
45-
{t("ui.messages.monsters")} ({
46-
bossOnlyMode
47-
? Object.values(monsters).filter(m => m.monster_id && TRACKED_MONSTER_IDS.has(String(m.monster_id))).length
48-
: Object.keys(monsters).length
49-
})
61+
{t("ui.messages.monsters")} ({getFilteredCount()})
62+
</div>
63+
<div className="flex gap-1">
64+
<button
65+
onClick={() => setFilterMode("all")}
66+
className="text-xs px-2 py-1 rounded"
67+
style={{
68+
background: filterMode === "all" ? "#3498db" : "rgba(255,255,255,0.1)",
69+
color: filterMode === "all" ? "#fff" : "rgba(255,255,255,0.7)"
70+
}}
71+
>
72+
{t("ui.buttons.allMonsters")}
73+
</button>
74+
<button
75+
onClick={() => setFilterMode("bosses")}
76+
className="text-xs px-2 py-1 rounded"
77+
style={{
78+
background: filterMode === "bosses" ? "#e74c3c" : "rgba(255,255,255,0.1)",
79+
color: filterMode === "bosses" ? "#fff" : "rgba(255,255,255,0.7)"
80+
}}
81+
>
82+
{t("ui.buttons.bossesOnly")}
83+
</button>
84+
<button
85+
onClick={() => setFilterMode("magical")}
86+
className="text-xs px-2 py-1 rounded"
87+
style={{
88+
background: filterMode === "magical" ? "#9b59b6" : "rgba(255,255,255,0.1)",
89+
color: filterMode === "magical" ? "#fff" : "rgba(255,255,255,0.7)"
90+
}}
91+
>
92+
{t("ui.buttons.magicalOnly", "Magical")}
93+
</button>
5094
</div>
51-
<button
52-
onClick={() => setBossOnlyMode((prev) => !prev)}
53-
className="text-xs px-2 py-1 rounded"
54-
style={{
55-
background: bossOnlyMode ? "#3498db" : "rgba(255,255,255,0.1)",
56-
color: bossOnlyMode ? "#fff" : "rgba(255,255,255,0.7)"
57-
}}
58-
>
59-
{bossOnlyMode ? t("ui.buttons.bossesOnly") : t("ui.buttons.allMonsters")}
60-
</button>
6195
</div>
6296
<div className="flex items-center gap-2">
6397
<label className="text-xs">{t("ui.buttons.sort")}:</label>
@@ -80,19 +114,25 @@ export default function MonsterList({
80114
<table className="monsters-table w-full border-collapse">
81115
<thead>
82116
<tr>
83-
<th className="text-left p-1">{t("ui.messages.name")}</th>
84-
<th className="text-center p-1">{t("ui.messages.hp")}</th>
85-
<th className="text-center p-1">{t("ui.messages.direction")}</th>
86-
<th className="text-right p-1 pl-5">{t("ui.messages.distance")}</th>
87-
<th className="text-center p-1 pl-5">{t("ui.messages.nextSpawn")}</th>
117+
<th className="text-sm text-left p-1">{t("ui.messages.name")}</th>
118+
<th className="text-sm text-center p-1">{t("ui.messages.hp")}</th>
119+
<th className="text-sm text-center p-1">{t("ui.messages.direction")}</th>
120+
<th className="text-sm text-right p-1 pl-5">{t("ui.messages.distance")}</th>
121+
<th className="text-sm text-center p-1 pl-5">{t("ui.messages.nextSpawn")}</th>
88122
</tr>
89123
</thead>
90124
<tbody>
91125
{Object.entries(monsters)
92126
.map(([id, m]) => ({ id, ...m }))
93127
.filter((m) => {
94-
if (!bossOnlyMode) return true;
95-
return m.monster_id && TRACKED_MONSTER_IDS.has(String(m.monster_id));
128+
if (filterMode === "all") return true;
129+
if (filterMode === "bosses") {
130+
return m.monster_id && BOSS_MONSTER_IDS.has(String(m.monster_id));
131+
}
132+
if (filterMode === "magical") {
133+
return m.monster_id && MAGICAL_CREATURE_IDS.has(String(m.monster_id));
134+
}
135+
return true;
96136
})
97137
.sort((a, b) => {
98138
if (sortKey === "hp") {
@@ -122,7 +162,7 @@ export default function MonsterList({
122162
: "-";
123163
const direction = m.direction ?? "-";
124164

125-
let spawnInfo = m.monster_id && TRACKED_MONSTER_IDS.has(String(m.monster_id))
165+
let spawnInfo = m.monster_id && MERGED_MONSTER_IDS.has(String(m.monster_id))
126166
? getNextSpawnTime(String(m.monster_id))
127167
: null;
128168

src/renderer/src/shared/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export {
88
roleColors,
99
themeColors,
1010
} from "./colors";
11+
12+
export { BOSS_MONSTER_IDS, MAGICAL_CREATURE_IDS } from "./monsters";
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1-
export const TRACKED_MONSTER_IDS = new Set([
1+
export const MAGICAL_CREATURE_IDS = new Set([
2+
// Golden Nappo
3+
'10900',
4+
// Silver Nappo
5+
'10901',
6+
// Lovely Boarlet
7+
'10902',
8+
// Breezy Boarlet
9+
'10903',
10+
// Loyal Boarlet
11+
'10904',
12+
]);
13+
14+
export const BOSS_MONSTER_IDS = new Set([
215
// Golden Juggernaut
316
'80006', '10032',
417
// Frost Ogre
5-
'108', '10009', '20100', '20127', '80002', '2000129', '2000140', '2004172', '3000006', '3000019',
18+
'108', '20100', '20127', '80002', '2000129', '2000140', '2004172', '3000006', '3000019',
619
// Inferno Ogre
720
'80004', '10018',
821
// Phantom Arachnocrab
922
'80008', '10069',
1023
// Brigand Leader
1124
'10056',
25+
// Lizardman King
26+
'10085',
1227
// Venobzzar Incubator
1328
'80009', '10077',
1429
// Muku Chief
@@ -23,16 +38,6 @@ export const TRACKED_MONSTER_IDS = new Set([
2338
'10010', '20024', '20072', '20092', '80003', '2000131', '2000137',
2439
// Celestial Flier
2540
'10084',
26-
// Earth Ogre
27-
'80005', '10025',
28-
// Lovely Boarlet
29-
'10902',
30-
// Breezy Boarlet
31-
'10903',
32-
// Loyal Boarlet
33-
'10904',
34-
// Golden Nappo
35-
'10900',
36-
// Silver Nappo
37-
'10901'
38-
]);
41+
// Muku King
42+
'10029', '20027', '20071', '20108', '80005', '81000',
43+
]);

src/server/sniffer.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const Cap = cap.Cap;
1515
async function checkNpcap(logger: Logger) {
1616
try {
1717
const devices = Cap.deviceList();
18-
if ( !devices || devices.length === 0 || devices.every((d) => d.name.includes("Loopback")) ) {
18+
if (!devices || devices.length === 0 || devices.every((d) => d.name.includes("Loopback"))) {
1919
throw new Error("Npcap not detected or not functional.");
2020
}
2121
logger.info("Npcap detected and functional.");
@@ -209,8 +209,7 @@ class Sniffer {
209209

210210
const srcport = tcpPacket.info.srcport;
211211
const dstport = tcpPacket.info.dstport;
212-
const src_server =
213-
srcaddr + ":" + srcport + " -> " + dstaddr + ":" + dstport;
212+
const src_server = srcaddr + ":" + srcport + " -> " + dstaddr + ":" + dstport;
214213

215214
await this.tcp_lock.acquire();
216215
try {
@@ -252,24 +251,24 @@ class Sniffer {
252251
this.userDataManager.users.size !==
253252
0
254253
) {
255-
const lp = this.globalSettings.lastPausedAt || 0;
256-
const lr = this.globalSettings.lastResumedAt || 0;
257-
const wasPausedThenResumed = lp > 0 && lr > lp;
258-
if (!wasPausedThenResumed) {
259-
this.userDataManager.clearAll(true);
260-
console.log(
261-
"Server changed, statistics cleared!",
262-
);
263-
} else {
264-
console.log(
265-
"Server changed detected but skip clear because pause->resume was observed.",
266-
);
267-
} try {
254+
const lp = this.globalSettings.lastPausedAt || 0;
255+
const lr = this.globalSettings.lastResumedAt || 0;
256+
const wasPausedThenResumed = lp > 0 && lr > lp;
257+
if (!wasPausedThenResumed) {
258+
this.userDataManager.clearAll(true);
259+
this.logger.info(
260+
"Server changed, statistics cleared!",
261+
);
262+
} else {
263+
this.logger.info(
264+
"Server changed detected but skip clear because pause->resume was observed.",
265+
);
266+
} try {
268267
this.globalSettings.lastPausedAt = null;
269268
this.globalSettings.lastResumedAt = null;
270269
} catch (e) { }
271270
}
272-
console.log(
271+
this.logger.info(
273272
"Game server detected. Measuring DPS...",
274273
);
275274
}
@@ -310,11 +309,11 @@ class Sniffer {
310309
const wasPausedThenResumed = lp > 0 && lr > lp;
311310
if (!wasPausedThenResumed) {
312311
this.userDataManager.clearAll(true);
313-
console.log(
312+
this.logger.info(
314313
"Server changed, statistics cleared!",
315314
);
316315
} else {
317-
console.log(
316+
this.logger.info(
318317
"Server changed detected but skip clear because pause->resume was observed.",
319318
);
320319
}
@@ -323,7 +322,7 @@ class Sniffer {
323322
this.globalSettings.lastResumedAt = null;
324323
} catch (e) { }
325324
}
326-
console.log(
325+
this.logger.info(
327326
"Game server detected by login packet. Measuring DPS...",
328327
);
329328
}

translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"save": "Save",
8888
"delete": "Delete",
8989
"bossesOnly": "Bosses Only",
90+
"magicalOnly": "Magical",
9091
"allMonsters": "All Monsters",
9192
"sort": "Sort",
9293
"asc": "Asc ↑",

translations/zh.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"save": "保存",
8888
"delete": "删除",
8989
"bossesOnly": "仅显示Boss",
90+
"magicalOnly": "魔法生物",
9091
"allMonsters": "所有怪物",
9192
"sort": "排序",
9293
"asc": "升序 ↑",

0 commit comments

Comments
 (0)