Skip to content

Commit 327547d

Browse files
committed
feat: Add the dt command
1 parent 4e8cb7d commit 327547d

File tree

7 files changed

+259
-1
lines changed

7 files changed

+259
-1
lines changed

src/cache/pokemonGo.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import { fsPath } from '@/utils/fsPath';
77

88
// eslint-disable-next-line @typescript-eslint/no-namespace -- Looks better with dot notation
99
export namespace PokemonGO {
10+
export type Stats = {
11+
atk: number;
12+
def: number;
13+
sta: number;
14+
};
15+
1016
export type Pokemon = {
1117
_id: string;
1218
name: string;
1319
num: number;
1420
types: string[];
15-
baseStats: { atk: number; def: number; sta: number };
21+
baseStats: Stats;
1622
heightm: number;
1723
weightkg: number;
1824
prevo?: string;

src/globals/augment.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Extends React/JSX types with PS-specific ones
2+
3+
import type { HTMLProps } from 'react';
4+
5+
type _PSIconProps = HTMLProps<HTMLSpanElement> & { pokemon?: string; type?: string; item?: string };
6+
7+
declare module 'react' {
8+
export type PSIconProps = _PSIconProps;
9+
}
10+
11+
declare global {
12+
// eslint-disable-next-line @typescript-eslint/no-namespace -- Used to augment IntrinsicElements
13+
namespace JSX {
14+
export interface IntrinsicElements {
15+
psicon: _PSIconProps;
16+
font: HTMLProps<HTMLSpanElement>;
17+
}
18+
}
19+
}

src/i18n/english.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default {
66
},
77

88
ACCESS_DENIED: 'Access denied.',
9+
SCREW_YOU: ['Yeah no', 'Buzz off!', 'Screw you.'],
910
CANCELLED: 'This action was cancelled.',
1011
CMD_NOT_FOUND: 'Command not found.',
1112
CONFIRM: "Are you sure? Type 'confirm' to confirm.",
@@ -19,6 +20,7 @@ export default {
1920
USER_NOT_FOUND: 'User not found!',
2021
WRONG_ROOM: 'Wrong room.',
2122
INVALID_ARGUMENTS: 'Invalid number of arguments.',
23+
ENTRY_NOT_FOUND: 'Entry not found.',
2224

2325
GAME: {
2426
ALREADY_JOINED: 'You have already joined this game.',

src/ps/commands/dt.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { GOData } from '@/cache/pokemonGo';
2+
import { toId } from '@/tools';
3+
import { ChatError } from '@/utils/chatError';
4+
import { PSIcon } from '@/utils/components/ps/psicon';
5+
import { getCP } from '@/utils/pokemonGo';
6+
7+
import type { PSCommand } from '@/types/chat';
8+
9+
function getPokemonGen(num: number): number {
10+
return [0, 152, 252, 387, 495, 650, 722, 810, 906, 1011].findIndex(firstOfGen => num < firstOfGen);
11+
}
12+
13+
export const command: PSCommand[] = [
14+
{
15+
name: 'dt',
16+
help: 'Shows the data for a Pokémon.',
17+
syntax: 'CMD [mon/move]',
18+
// TODO: Keep this GO-only
19+
async run({ broadcastHTML, arg, $T }) {
20+
const query = toId(arg);
21+
if (query === 'constructor') throw new ChatError($T('SCREW_YOU'));
22+
if (query in GOData.pokedex) {
23+
const mon = GOData.pokedex[query];
24+
const stats = mon.baseStats;
25+
26+
return broadcastHTML(
27+
<>
28+
<div className="message">
29+
<ul className="utilichart">
30+
<li className="result">
31+
<span className="col numcol">{mon.unreleased ? 'UR' : 'GO'}</span>{' '}
32+
<span className="col iconcol">
33+
<PSIcon pokemon={toId(mon.name)} />
34+
</span>{' '}
35+
<span className="col pokemonnamecol" style={{ whiteSpace: 'nowrap' }}>
36+
<a href={`https://dex.pokemonshowdown.com/pokemon/${toId(mon.name)}`} target="_blank">
37+
{mon.name}
38+
</a>
39+
</span>{' '}
40+
<span className="col typecol">
41+
{mon.types.map(type => (
42+
<img src={`https://play.pokemonshowdown.com/sprites/types/${type}.png`} alt={type} height="14" width="32" />
43+
))}
44+
</span>{' '}
45+
<span style={{ float: 'left', minHeight: 26 }}>
46+
<span className="col statcol">
47+
<em>Atk</em>
48+
<br />
49+
{stats.atk}
50+
</span>{' '}
51+
<span className="col statcol">
52+
<em>Def</em>
53+
<br />
54+
{stats.def}
55+
</span>{' '}
56+
<span className="col statcol">
57+
<em>Sta</em>
58+
<br />
59+
{stats.sta}
60+
</span>{' '}
61+
<span className="col bstcol" style={{ marginLeft: 10 }}>
62+
<em>40</em>
63+
<br />
64+
{getCP(stats, 40)}
65+
</span>{' '}
66+
<span className="col bstcol" style={{ marginLeft: 10 }}>
67+
<em>50</em>
68+
<br />
69+
{getCP(stats, 50)}
70+
</span>{' '}
71+
<span className="col bstcol" style={{ marginLeft: 10 }}>
72+
<em>MCP</em>
73+
<br />
74+
{getCP(stats, 51)}
75+
</span>{' '}
76+
</span>
77+
</li>
78+
<li style={{ clear: 'both' }}></li>
79+
</ul>
80+
</div>
81+
<font size={1}>
82+
<font color="#686868">Dex#:</font> {mon.num}&nbsp;|&#8287;<font color="#686868">Gen:</font> {getPokemonGen(mon.num)}
83+
&nbsp;|&#8287;
84+
<font color="#686868">Height:</font> {mon.heightm} m&nbsp;|&#8287;<font color="#686868">Weight:</font> {mon.weightkg} kg
85+
{mon.shiny ? <>&nbsp;|&#8287; ✓ Can be shiny</> : null}
86+
{mon.shinyLocked ? <>&nbsp;|&#8287;Shiny-locked</> : null}&nbsp;|&#8287;<font color="#686868">Evolution:</font>{' '}
87+
{mon.evos?.join(', ') || 'None'}
88+
</font>
89+
<br />
90+
<hr />
91+
<details style={{ marginBottom: -10 }}>
92+
<summary title={mon.unreleased ? 'Moves are for an unreleased Pokémon and may not be accurate' : undefined}>
93+
Moves{mon.unreleased ? '*' : ''}
94+
</summary>
95+
Fast: {[...mon.moves.fast, ...mon.moves.fast_elite.map(move => `${move}*`)].sort().join(', ')}
96+
<br />
97+
Charged: {[...mon.moves.charged, ...mon.moves.charged_elite.map(move => `${move}*`)].sort().join(', ')}
98+
</details>
99+
</>
100+
);
101+
}
102+
103+
throw new ChatError($T('ENTRY_NOT_FOUND'));
104+
},
105+
},
106+
];

src/static/go/cpScaling.json

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"1": 0.094,
3+
"1.5": 0.1351374318,
4+
"2": 0.16639787,
5+
"2.5": 0.192650919,
6+
"3": 0.21573247,
7+
"3.5": 0.2365726613,
8+
"4": 0.25572005,
9+
"4.5": 0.2735303812,
10+
"5": 0.29024988,
11+
"5.5": 0.3060573775,
12+
"6": 0.3210876,
13+
"6.5": 0.3354450362,
14+
"7": 0.34921268,
15+
"7.5": 0.3624577511,
16+
"8": 0.3752356,
17+
"8.5": 0.387592416,
18+
"9": 0.39956728,
19+
"9.5": 0.4111935514,
20+
"10": 0.4225,
21+
"10.5": 0.4329264091,
22+
"11": 0.44310755,
23+
"11.5": 0.4530599591,
24+
"12": 0.4627984,
25+
"12.5": 0.472336093,
26+
"13": 0.48168495,
27+
"13.5": 0.4908558003,
28+
"14": 0.49985844,
29+
"14.5": 0.508701765,
30+
"15": 0.51739395,
31+
"15.5": 0.5259425113,
32+
"16": 0.5343543,
33+
"16.5": 0.5426357375,
34+
"17": 0.5507927,
35+
"17.5": 0.5588305862,
36+
"18": 0.5667545,
37+
"18.5": 0.5745691333,
38+
"19": 0.5822789,
39+
"19.5": 0.5898879072,
40+
"20": 0.5974,
41+
"20.5": 0.6048236651,
42+
"21": 0.6121573,
43+
"21.5": 0.6194041216,
44+
"22": 0.6265671,
45+
"22.5": 0.6336491432,
46+
"23": 0.64065295,
47+
"23.5": 0.6475809666,
48+
"24": 0.65443563,
49+
"24.5": 0.6612192524,
50+
"25": 0.667934,
51+
"25.5": 0.6745818959,
52+
"26": 0.6811649,
53+
"26.5": 0.6876849038,
54+
"27": 0.69414365,
55+
"27.5": 0.70054287,
56+
"28": 0.7068842,
57+
"28.5": 0.7131691091,
58+
"29": 0.7193991,
59+
"29.5": 0.7255756136,
60+
"30": 0.7317,
61+
"30.5": 0.7347410093,
62+
"31": 0.7377695,
63+
"31.5": 0.7407855938,
64+
"32": 0.74378943,
65+
"32.5": 0.7467812109,
66+
"33": 0.74976104,
67+
"33.5": 0.7527290867,
68+
"34": 0.7556855,
69+
"34.5": 0.7586303683,
70+
"35": 0.76156384,
71+
"35.5": 0.7644860647,
72+
"36": 0.76739717,
73+
"36.5": 0.7702972656,
74+
"37": 0.7731865,
75+
"37.5": 0.7760649616,
76+
"38": 0.77893275,
77+
"38.5": 0.7817900548,
78+
"39": 0.784637,
79+
"39.5": 0.7874736075,
80+
"40": 0.7903,
81+
"40.5": 0.792803968,
82+
"41": 0.79530001,
83+
"41.5": 0.797800015,
84+
"42": 0.8003,
85+
"42.5": 0.802799995,
86+
"43": 0.8053,
87+
"43.5": 0.8078,
88+
"44": 0.81029999,
89+
"44.5": 0.812799985,
90+
"45": 0.81529999,
91+
"45.5": 0.81779999,
92+
"46": 0.82029999,
93+
"46.5": 0.82279999,
94+
"47": 0.82529999,
95+
"47.5": 0.82779999,
96+
"48": 0.83029999,
97+
"48.5": 0.83279999,
98+
"49": 0.83529999,
99+
"49.5": 0.83779999,
100+
"50": 0.84029999,
101+
"50.5": 0.84279999,
102+
"51": 0.84529999
103+
}

src/utils/components/ps/psicon.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { PSIconProps, ReactElement } from 'react';
2+
3+
export function PSIcon(props: PSIconProps): ReactElement {
4+
return <psicon {...props} />;
5+
}

src/utils/pokemonGo.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import CP_SCALING from '@/static/go/cpScaling.json';
2+
3+
import type { PokemonGO } from '@/cache/pokemonGo';
4+
5+
export function getCP(stats: PokemonGO.Stats, level?: number, ivs?: PokemonGO.Stats): number {
6+
if (!level) level = 40;
7+
if (!ivs) ivs = { atk: 15, def: 15, sta: 15 };
8+
if (Array.isArray(ivs)) ivs = { atk: ivs[0], def: ivs[1], sta: ivs[2] };
9+
10+
const atk = stats.atk + ivs.atk,
11+
def = stats.def + ivs.def,
12+
sta = stats.sta + ivs.sta;
13+
14+
// Formula used in-game
15+
const CP = Math.floor((atk * def ** 0.5 * sta ** 0.5 * ((CP_SCALING as Record<number, number>)[level] || 0.7903) ** 2) / 10);
16+
return Math.max(CP, 10);
17+
}

0 commit comments

Comments
 (0)