enefel 2.9.1 → 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.
Files changed (77) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/2025/newSkills.ts +769 -0
  3. package/2025/raceMigration.ts +66 -0
  4. package/2025/rawTeamsData.ts +1726 -0
  5. package/2025/skillMigration.ts +155 -0
  6. package/avatarsSeason.ts +61 -0
  7. package/career.ts +64 -8
  8. package/career2025.ts +2752 -0
  9. package/dice.ts +40 -8
  10. package/dist/2025/newSkills.d.ts +10 -0
  11. package/dist/2025/newSkills.js +663 -0
  12. package/dist/2025/raceMigration.d.ts +10 -0
  13. package/dist/2025/raceMigration.js +53 -0
  14. package/dist/2025/rawTeamsData.d.ts +12 -0
  15. package/dist/2025/rawTeamsData.js +1699 -0
  16. package/dist/2025/skillMigration.d.ts +31 -0
  17. package/dist/2025/skillMigration.js +95 -0
  18. package/dist/avatarsSeason.d.ts +22 -0
  19. package/dist/avatarsSeason.js +54 -0
  20. package/dist/career.d.ts +13 -4
  21. package/dist/career.js +36 -7
  22. package/dist/career2025.d.ts +192 -0
  23. package/dist/career2025.js +2611 -0
  24. package/dist/dice.d.ts +10 -2
  25. package/dist/dice.js +31 -8
  26. package/dist/index.d.ts +17 -2
  27. package/dist/index.js +44 -5
  28. package/dist/move.d.ts +6 -2
  29. package/dist/move.js +35 -0
  30. package/dist/package-lock.json +913 -7
  31. package/dist/package.json +6 -2
  32. package/dist/pitch.d.ts +10 -1
  33. package/dist/pitch.js +16 -0
  34. package/dist/player.d.ts +8 -4
  35. package/dist/player.js +104 -20
  36. package/dist/position.d.ts +4 -0
  37. package/dist/position.js +17 -1
  38. package/dist/race.d.ts +23 -12
  39. package/dist/race.js +210 -2
  40. package/dist/skill.d.ts +26 -3
  41. package/dist/skill.js +199 -81
  42. package/dist/skills/hitAndRun.d.ts +10 -0
  43. package/dist/skills/hitAndRun.js +12 -0
  44. package/dist/skills/safePairOfHands.d.ts +28 -0
  45. package/dist/skills/safePairOfHands.js +18 -0
  46. package/dist/skills/sidestep.d.ts +21 -0
  47. package/dist/skills/sidestep.js +20 -0
  48. package/dist/status.d.ts +4 -1
  49. package/dist/status.js +14 -4
  50. package/dist/store.d.ts +3 -0
  51. package/dist/store.js +3 -0
  52. package/dist/teams/career_v2025.d.ts +246 -0
  53. package/dist/teams/career_v2025.js +3512 -0
  54. package/dist/teams/convert-csv-to-career.d.ts +1 -0
  55. package/dist/teams/convert-csv-to-career.js +1085 -0
  56. package/dist/types/models.d.ts +4 -0
  57. package/index.ts +57 -4
  58. package/jest.config.js +3 -0
  59. package/move.ts +50 -2
  60. package/npm-login.sh +0 -0
  61. package/package.json +6 -2
  62. package/pitch.ts +22 -0
  63. package/player.ts +105 -22
  64. package/position.ts +16 -0
  65. package/race.ts +227 -13
  66. package/skill.ts +217 -83
  67. package/skills/hitAndRun.ts +14 -0
  68. package/skills/safePairOfHands.ts +33 -0
  69. package/skills/sidestep.ts +25 -0
  70. package/status.ts +15 -3
  71. package/store.ts +3 -0
  72. package/teams/README.md +53 -0
  73. package/teams/clean-stats.js +54 -0
  74. package/teams/convert-csv-to-career.ts +1209 -0
  75. package/teams/players_clean.csv +107 -0
  76. package/teams/players_next.csv +21 -0
  77. package/types/models.ts +4 -0
@@ -0,0 +1,155 @@
1
+ import { SKILL_TYPE } from "../skill";
2
+
3
+ type CareerLite = {
4
+ skills?: (string | [string, number])[];
5
+ };
6
+
7
+ type SkillBasic = {
8
+ id: string;
9
+ name?: string;
10
+ family?: string;
11
+ mode?: string;
12
+ elite?: boolean;
13
+ };
14
+
15
+ type SkillMigrationRow = {
16
+ key: string;
17
+ label: string;
18
+ intrinsic: boolean;
19
+ legacy?: {
20
+ name: string;
21
+ family: string;
22
+ };
23
+ next?: {
24
+ name: string;
25
+ family?: string;
26
+ mode?: string;
27
+ elite?: boolean;
28
+ };
29
+ };
30
+
31
+ const SKILL_DISPLAY_OVERRIDE: Record<string, string> = {
32
+ "animal-salvagery": "Animal Savagery",
33
+ "distrubing-presence": "Disturbing Presence",
34
+ claw: "Claws",
35
+ animosity: "Animosity (X)",
36
+ "running-pass": "Give and Go", // GIVE_AND_GO renamed to Give and Go
37
+ runningpass: "Give and Go", // Alias for GIVE_AND_GO
38
+ };
39
+
40
+ const SKILL_KEY_ALIAS: Record<string, string> = {
41
+ fumblerooskie: "fumblerooski",
42
+ giveandgo: "runningpass",
43
+ animalsalvagery: "animalsavagery",
44
+ distrubingpresence: "disturbingpresence",
45
+ claws: "claw",
46
+ animosityx: "animosity",
47
+ nohand: "noball",
48
+ lonerx: "loner",
49
+ pogostick: "pogo",
50
+ };
51
+
52
+ const normalizeSkillKey = (value: string) => {
53
+ const base = value.toLowerCase().replace(/[^a-z0-9]/gi, "");
54
+ return SKILL_KEY_ALIAS[base] ?? base;
55
+ };
56
+
57
+ const formatSkillName = (value: string) =>
58
+ value.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
59
+
60
+ const buildSkillMigrationRows = (
61
+ existingSkills: Record<string, SKILL_TYPE>,
62
+ nextSkills: SkillBasic[],
63
+ legacyCareers: Record<string, CareerLite>,
64
+ nextCareers: Record<string, CareerLite>
65
+ ): SkillMigrationRow[] => {
66
+ const labelByType: Record<SKILL_TYPE, string> = {
67
+ [SKILL_TYPE.AGILITY]: "Agility",
68
+ [SKILL_TYPE.EXTRAORDINARY]: "Extraordinary",
69
+ [SKILL_TYPE.GENERAL]: "General",
70
+ [SKILL_TYPE.MUTATION]: "Mutation",
71
+ [SKILL_TYPE.PASSING]: "Passing",
72
+ [SKILL_TYPE.STRENGTH]: "Strength",
73
+ [SKILL_TYPE.DEVIOUS]: "Devious",
74
+ [SKILL_TYPE.SPECIAL]: "Special",
75
+ };
76
+
77
+ const intrinsicSkills = new Set<string>();
78
+ const collectSkills = (careers: Record<string, CareerLite>) => {
79
+ Object.values(careers).forEach((career) => {
80
+ (career.skills ?? []).forEach((s) => {
81
+ const skillName = Array.isArray(s) ? s[0] : s;
82
+ intrinsicSkills.add(normalizeSkillKey(String(skillName)));
83
+ });
84
+ });
85
+ };
86
+ collectSkills(legacyCareers);
87
+ collectSkills(nextCareers);
88
+
89
+ const existingMap = new Map<
90
+ string,
91
+ {
92
+ name: string;
93
+ family: string;
94
+ }
95
+ >();
96
+ Object.entries(existingSkills).forEach(([id, type]) => {
97
+ const key = normalizeSkillKey(id);
98
+ const display = SKILL_DISPLAY_OVERRIDE[id] ?? formatSkillName(id);
99
+ existingMap.set(key, {
100
+ name: display,
101
+ family: labelByType[type] ?? String(type),
102
+ });
103
+ });
104
+
105
+ const nextMap = new Map<
106
+ string,
107
+ {
108
+ name: string;
109
+ family?: string;
110
+ mode?: string;
111
+ elite?: boolean;
112
+ }
113
+ >();
114
+ nextSkills.forEach((skill) => {
115
+ const key = normalizeSkillKey(skill.id);
116
+ nextMap.set(key, {
117
+ name: skill.name ?? formatSkillName(skill.id),
118
+ family: skill.family,
119
+ mode: skill.mode,
120
+ elite: skill.elite,
121
+ });
122
+ });
123
+
124
+ const keys: string[] = [];
125
+ existingMap.forEach((_v, key) => keys.push(key));
126
+ nextMap.forEach((_v, key) => {
127
+ if (!existingMap.has(key)) {
128
+ keys.push(key);
129
+ }
130
+ });
131
+
132
+ return keys
133
+ .map((key) => {
134
+ const legacy = existingMap.get(key);
135
+ const next = nextMap.get(key);
136
+ const label = next?.name ?? legacy?.name ?? key;
137
+ return {
138
+ key,
139
+ label,
140
+ legacy,
141
+ next,
142
+ intrinsic: intrinsicSkills.has(key),
143
+ };
144
+ })
145
+ .sort((a, b) => a.label.localeCompare(b.label));
146
+ };
147
+
148
+ export {
149
+ buildSkillMigrationRows,
150
+ formatSkillName,
151
+ normalizeSkillKey,
152
+ SKILL_DISPLAY_OVERRIDE,
153
+ SkillBasic,
154
+ SkillMigrationRow,
155
+ };
@@ -0,0 +1,61 @@
1
+ import { AVATAR_RARITY } from "./career";
2
+
3
+ export type SeasonAvatar = {
4
+ file: string;
5
+ season: number;
6
+ rarity: AVATAR_RARITY;
7
+ /** true for portrait images (ratio 2:3, e.g. 1024×1536) */
8
+ portrait?: boolean;
9
+ };
10
+
11
+ /** Display height multiplier for portrait avatars (2:3 ratio) */
12
+ export const PORTRAIT_RATIO = 1.5;
13
+
14
+ /**
15
+ * Avatars saisonniers — universels (aucune restriction de race).
16
+ * Pour ajouter des avatars : déposer l'image dans
17
+ * client/public/tile/shop/avatar-season/s{season}/
18
+ * et ajouter une entrée ici.
19
+ */
20
+ export const AVATAR_SEASON_LIST: SeasonAvatar[] = [
21
+ { file: "1.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
22
+ { file: "2.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
23
+ { file: "3.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
24
+ { file: "4.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
25
+ { file: "5.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
26
+ { file: "6.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
27
+ { file: "7.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
28
+ { file: "8.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
29
+ { file: "9.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
30
+ { file: "10.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
31
+ { file: "11.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
32
+ { file: "12.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
33
+ { file: "13.png", season: 17, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
34
+ { file: "1.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
35
+ { file: "2.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
36
+ { file: "3.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
37
+ { file: "4.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
38
+ { file: "5.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
39
+ { file: "6.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
40
+ { file: "7.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
41
+ { file: "8.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
42
+ { file: "9.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
43
+ { file: "10.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
44
+ { file: "11.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
45
+ { file: "12.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
46
+ { file: "13.png", season: 19, rarity: AVATAR_RARITY.SEASONAL, portrait: true },
47
+ ];
48
+
49
+ /** Chemin complet utilisé pour afficher l'avatar d'un joueur (ex: <img src>) */
50
+ export function getSeasonAvatarPath(season: number): string {
51
+ return `tile/shop/avatar-season/s${season}`;
52
+ }
53
+
54
+ /** Chemin relatif utilisé comme prop `type` dans le composant Item du shop */
55
+ export function getSeasonAvatarItemType(season: number): string {
56
+ return `avatar-season/s${season}`;
57
+ }
58
+
59
+ export function findSeasonAvatar(file: string): SeasonAvatar | undefined {
60
+ return AVATAR_SEASON_LIST.find((a) => a.file === file);
61
+ }
package/career.ts CHANGED
@@ -3,6 +3,11 @@
3
3
  // AG: Agility
4
4
  // AV: Armour Value
5
5
 
6
+ import {
7
+ CAREER_ID as CAREER_ID_2025,
8
+ Career as Career2025,
9
+ getCareers as getCareers2025,
10
+ } from "./career2025";
6
11
  import { SKILL_NAMES } from "./skill";
7
12
 
8
13
  enum RACE {
@@ -34,6 +39,7 @@ enum AVATAR_RARITY {
34
39
  COMMUN = "c",
35
40
  RARE = "r",
36
41
  UNIQUE = "u",
42
+ SEASONAL = "s",
37
43
  }
38
44
 
39
45
  enum AVATAR {
@@ -178,6 +184,7 @@ enum CAREER_ID {
178
184
  enum CAREER_VERSION {
179
185
  V6 = "v6",
180
186
  V2020 = "v2020",
187
+ V2025 = "v2025",
181
188
  }
182
189
 
183
190
  export type Career = {
@@ -199,6 +206,10 @@ export type Career = {
199
206
  version: CAREER_VERSION;
200
207
  };
201
208
 
209
+ type CAREER_ID_ALL = CAREER_ID | CAREER_ID_2025;
210
+ type CareerMap = Record<CAREER_ID_ALL, Career | Career2025>;
211
+ type AvailableCareerMap = Record<CAREER_ID_2025, Career2025>;
212
+
202
213
  const CAREER: Record<CAREER_ID, Career> = {
203
214
  // SLANN
204
215
  [CAREER_ID.SLANN_LINEMAN]: {
@@ -690,11 +701,7 @@ const CAREER: Record<CAREER_ID, Career> = {
690
701
  double: "AS",
691
702
  normalCoach: "G",
692
703
  icons: ["imperial-thrower1"],
693
- skills: [
694
- SKILL_NAMES.PASS,
695
- SKILL_NAMES.RUNNING_PASS,
696
- SKILL_NAMES.SPECIALIST,
697
- ],
704
+ skills: [SKILL_NAMES.PASS, SKILL_NAMES.GIVE_AND_GO, SKILL_NAMES.SPECIALIST],
698
705
  avatars: [AVATAR.HUMAN, AVATAR.HUMAN_WOMAN],
699
706
  badge: RACE.IMPERIAL,
700
707
  range: 1,
@@ -2158,8 +2165,57 @@ const CAREER: Record<CAREER_ID, Career> = {
2158
2165
  },
2159
2166
  };
2160
2167
 
2161
- const getCareers = (): Record<CAREER_ID, Career> => {
2162
- return CAREER;
2168
+ const getCareers = (): CareerMap => {
2169
+ const nextCareers = getCareers2025();
2170
+ return {
2171
+ ...(CAREER as CareerMap),
2172
+ ...(nextCareers as CareerMap),
2173
+ };
2174
+ };
2175
+
2176
+ const getAvailableCareers = (): AvailableCareerMap => {
2177
+ const nextCareers = getCareers2025();
2178
+ const allowedBadges = new Set<string>(Object.values(RACE));
2179
+
2180
+ return Object.entries(nextCareers).reduce<AvailableCareerMap>(
2181
+ (acc, [id, career]) => {
2182
+ if (allowedBadges.has(career.badge as unknown as string)) {
2183
+ acc[id as CAREER_ID_2025] = career as Career2025;
2184
+ }
2185
+ return acc;
2186
+ },
2187
+ {} as AvailableCareerMap
2188
+ );
2163
2189
  };
2164
2190
 
2165
- export { AVATAR, AVATAR_RARITY, CAREER_ID, CAREER_VERSION, getCareers, RACE };
2191
+ const CAREER_IS_2025: Record<CAREER_ID, boolean> = Object.values(
2192
+ CAREER_ID
2193
+ ).reduce((acc, id) => {
2194
+ acc[id as CAREER_ID] = new Set<string>(
2195
+ Object.values(CAREER_ID_2025) as string[]
2196
+ ).has(id as unknown as string);
2197
+ return acc;
2198
+ }, {} as Record<CAREER_ID, boolean>);
2199
+
2200
+ // Migration 2025 : indique si un career legacy existe en v2025
2201
+ const CAREER_MIGRATION_2025: Record<CAREER_ID, boolean> = (() => {
2202
+ const migrated = new Set<string>(Object.values(CAREER_ID_2025));
2203
+ const map = {} as Record<CAREER_ID, boolean>;
2204
+ (Object.values(CAREER_ID) as CAREER_ID[]).forEach((id) => {
2205
+ map[id] = migrated.has(id as string);
2206
+ });
2207
+ return map;
2208
+ })();
2209
+
2210
+ export {
2211
+ AVATAR,
2212
+ AVATAR_RARITY,
2213
+ CAREER_ID,
2214
+ CAREER_ID_ALL,
2215
+ CAREER_IS_2025,
2216
+ CAREER_MIGRATION_2025,
2217
+ CAREER_VERSION,
2218
+ RACE,
2219
+ getAvailableCareers,
2220
+ getCareers,
2221
+ };