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.
- package/.claude/settings.local.json +7 -0
- package/2025/newSkills.ts +769 -0
- package/2025/raceMigration.ts +66 -0
- package/2025/rawTeamsData.ts +1726 -0
- package/2025/skillMigration.ts +155 -0
- package/avatarsSeason.ts +61 -0
- package/career.ts +64 -8
- package/career2025.ts +2752 -0
- package/dice.ts +40 -8
- package/dist/2025/newSkills.d.ts +10 -0
- package/dist/2025/newSkills.js +663 -0
- package/dist/2025/raceMigration.d.ts +10 -0
- package/dist/2025/raceMigration.js +53 -0
- package/dist/2025/rawTeamsData.d.ts +12 -0
- package/dist/2025/rawTeamsData.js +1699 -0
- package/dist/2025/skillMigration.d.ts +31 -0
- package/dist/2025/skillMigration.js +95 -0
- package/dist/avatarsSeason.d.ts +22 -0
- package/dist/avatarsSeason.js +54 -0
- package/dist/career.d.ts +13 -4
- package/dist/career.js +36 -7
- package/dist/career2025.d.ts +192 -0
- package/dist/career2025.js +2611 -0
- package/dist/dice.d.ts +10 -2
- package/dist/dice.js +31 -8
- package/dist/index.d.ts +17 -2
- package/dist/index.js +44 -5
- package/dist/move.d.ts +6 -2
- package/dist/move.js +35 -0
- package/dist/package-lock.json +913 -7
- package/dist/package.json +6 -2
- package/dist/pitch.d.ts +10 -1
- package/dist/pitch.js +16 -0
- package/dist/player.d.ts +8 -4
- package/dist/player.js +104 -20
- package/dist/position.d.ts +4 -0
- package/dist/position.js +17 -1
- package/dist/race.d.ts +23 -12
- package/dist/race.js +210 -2
- package/dist/skill.d.ts +26 -3
- package/dist/skill.js +199 -81
- package/dist/skills/hitAndRun.d.ts +10 -0
- package/dist/skills/hitAndRun.js +12 -0
- package/dist/skills/safePairOfHands.d.ts +28 -0
- package/dist/skills/safePairOfHands.js +18 -0
- package/dist/skills/sidestep.d.ts +21 -0
- package/dist/skills/sidestep.js +20 -0
- package/dist/status.d.ts +4 -1
- package/dist/status.js +14 -4
- package/dist/store.d.ts +3 -0
- package/dist/store.js +3 -0
- package/dist/teams/career_v2025.d.ts +246 -0
- package/dist/teams/career_v2025.js +3512 -0
- package/dist/teams/convert-csv-to-career.d.ts +1 -0
- package/dist/teams/convert-csv-to-career.js +1085 -0
- package/dist/types/models.d.ts +4 -0
- package/index.ts +57 -4
- package/jest.config.js +3 -0
- package/move.ts +50 -2
- package/npm-login.sh +0 -0
- package/package.json +6 -2
- package/pitch.ts +22 -0
- package/player.ts +105 -22
- package/position.ts +16 -0
- package/race.ts +227 -13
- package/skill.ts +217 -83
- package/skills/hitAndRun.ts +14 -0
- package/skills/safePairOfHands.ts +33 -0
- package/skills/sidestep.ts +25 -0
- package/status.ts +15 -3
- package/store.ts +3 -0
- package/teams/README.md +53 -0
- package/teams/clean-stats.js +54 -0
- package/teams/convert-csv-to-career.ts +1209 -0
- package/teams/players_clean.csv +107 -0
- package/teams/players_next.csv +21 -0
- package/types/models.ts +4 -0
package/skill.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Player, PlayerSkill, PlayerWithSkill } from "types/models";
|
|
2
|
+
import { newSkills } from "./2025/newSkills";
|
|
2
3
|
import { distanceSquares } from "./calcul";
|
|
3
4
|
import { hasStatusOnField } from "./status";
|
|
4
5
|
import { getTime, TYPE_TIMER } from "./time";
|
|
@@ -24,17 +25,22 @@ const SKILL_DURATION = {
|
|
|
24
25
|
|
|
25
26
|
// Missing for races
|
|
26
27
|
enum SKILL_NAMES {
|
|
27
|
-
// SIDE_STEP: "side-step",
|
|
28
28
|
ACCURATE = "accurate",
|
|
29
29
|
ALWAYS_HUNGRY = "always-hungry",
|
|
30
30
|
ANIMAL_SAVAGERY = "animal-salvagery",
|
|
31
31
|
ANIMOSITY = "animosity",
|
|
32
|
+
ARM_BAR = "arm-bar",
|
|
33
|
+
// BALL_AND_CHAIN = "ball-and-chain",
|
|
32
34
|
BIG_GUY = "big-guy",
|
|
33
35
|
BIG_HAND = "big-hand",
|
|
34
36
|
BLOCK = "block",
|
|
37
|
+
BLOODLUST = "bloodlust",
|
|
35
38
|
BONE_HEAD = "bone-head",
|
|
39
|
+
// BOMBARDIER = "bombardier",
|
|
36
40
|
BRAWLER = "brawler",
|
|
41
|
+
BREATHE_FIRE = "breathe-fire",
|
|
37
42
|
BREAK_TACKLE = "break-tackle",
|
|
43
|
+
// BULLSEYE = "bullseye",
|
|
38
44
|
CANNONEER = "cannoneer",
|
|
39
45
|
CATCH = "catch",
|
|
40
46
|
CHAINSAW = "chainsaw",
|
|
@@ -49,152 +55,264 @@ enum SKILL_NAMES {
|
|
|
49
55
|
DODGE = "dodge",
|
|
50
56
|
DRUNKARD = "drunkard",
|
|
51
57
|
DUMP_OFF = "dump-off",
|
|
58
|
+
EYE_GOUGE = "eye-gouge",
|
|
52
59
|
EXTRA_ARMS = "extra-arms",
|
|
53
60
|
FEND = "fend",
|
|
54
61
|
FOUL_APPEARANCE = "foul-appearance",
|
|
55
62
|
FRENZY = "frenzy",
|
|
56
63
|
FUMBLEROOSKIE = "fumblerooskie",
|
|
64
|
+
// GRAB = "grab",
|
|
65
|
+
HAIL_MARY_PASS = "hail-mary-pass",
|
|
66
|
+
// HATRED = "hatred",
|
|
57
67
|
GUARD = "guard",
|
|
68
|
+
HIT_AND_RUN = "hit-and-run",
|
|
58
69
|
HORNS = "horns",
|
|
59
70
|
HYPNOTIC_GAZE = "hypnotic-gaze",
|
|
71
|
+
// INSIGNIFICANT = "insignificant",
|
|
60
72
|
IRON_HARD_SKIN = "iron-hard-skin",
|
|
61
73
|
JUGGERNAUT = "juggernaut",
|
|
62
74
|
JUMP_UP = "jump-up",
|
|
75
|
+
// KICK = "kick",
|
|
76
|
+
// KICK_TEAMMATE = "kick-teammate",
|
|
77
|
+
// LETHAL_FLIGHT = "lethal-flight",
|
|
78
|
+
// LEADER = "leader",
|
|
63
79
|
LEAP = "leap",
|
|
80
|
+
// LONE_FOULER = "lone-fouler",
|
|
64
81
|
LONER = "loner",
|
|
65
82
|
MIGHTY_BLOW = "mighty-blow",
|
|
66
83
|
MONSTROUS_MOUTH = "monstrous-mouth",
|
|
84
|
+
MY_BALL = "my-ball",
|
|
85
|
+
// MULTIPLE_BLOCK = "multiple-block",
|
|
67
86
|
NERVES_OF_STEEL = "nerves-of-steel",
|
|
68
87
|
NO_HANDS = "no-hand",
|
|
69
88
|
ON_THE_BALL = "on-the-ball",
|
|
70
89
|
PASS = "pass",
|
|
71
90
|
PILE_DRIVER = "pile-driver",
|
|
91
|
+
PICK_ME_UP = "pick-me-up",
|
|
72
92
|
PLAGUE_RIDDEN = "plague-ridden",
|
|
93
|
+
PUNT = "punt",
|
|
94
|
+
PRO = "pro",
|
|
95
|
+
PUT_THE_BOOT_IN = "put-the-boot-in",
|
|
73
96
|
POGO_STICK = "pogo-stick",
|
|
74
97
|
PREHENSILE_TAIL = "prehensile-tail",
|
|
98
|
+
QUICK_FOUL = "quick-foul",
|
|
75
99
|
PROJECTILE_VOMIT = "projectile-vomit",
|
|
76
100
|
REALLY_STUPID = "really-stupid",
|
|
77
101
|
REGENERATION = "regeneration",
|
|
78
102
|
RIGHT_STUFF = "right-stuff",
|
|
79
|
-
|
|
103
|
+
GIVE_AND_GO = "running-pass", // Keep "running-pass" for DB compatibility
|
|
104
|
+
SAFE_PAIR_OF_HANDS = "safe-pair-of-hands",
|
|
105
|
+
// SABOTEUR = "saboteur",
|
|
80
106
|
SAFE_PASS = "safe-pass",
|
|
81
107
|
SECRET_WEAPON = "secret-weapon",
|
|
82
108
|
SHADOWING = "shadowing",
|
|
109
|
+
STAB = "stab",
|
|
110
|
+
SIDESTEP = "sidestep",
|
|
83
111
|
SNEAKY_GIT = "sneaky-git",
|
|
84
112
|
SPECIALIST = "specialist",
|
|
85
113
|
SPRINT = "sprint",
|
|
86
114
|
STAND_FIRM = "stand-firm",
|
|
115
|
+
STEADY_FOOTING = "steady-footing",
|
|
87
116
|
STAR = "star",
|
|
88
117
|
STRIP_BALL = "strip-ball",
|
|
89
118
|
STRONG_ARM = "strong-arm",
|
|
90
119
|
STUNTY = "stunty",
|
|
120
|
+
SWOOP = "swoop",
|
|
91
121
|
SURE_FEET = "sure-feet",
|
|
92
122
|
SURE_HANDS = "sure-hands",
|
|
93
123
|
TACKLE = "tackle",
|
|
124
|
+
TAUNT = "taunt",
|
|
94
125
|
TAKE_ROOT = "take-root",
|
|
95
126
|
TENTACLES = "tentacles",
|
|
96
127
|
THICK_SKULL = "thick-skull",
|
|
97
128
|
THROW_TEAM_MATE = "throw-team-mate",
|
|
129
|
+
TIMMM_BER = "timmm-ber",
|
|
98
130
|
TITCHY = "titchy",
|
|
131
|
+
// TRICKSTER = "trickster",
|
|
99
132
|
TWO_HEADS = "two-heads",
|
|
100
133
|
UNCHANNELLED_FURY = "unchannelled-fury",
|
|
134
|
+
UNSTEADY = "unsteady",
|
|
101
135
|
VERY_LONG_LEGS = "very-long-legs",
|
|
136
|
+
// VIOLENT_INNOVATOR = "violent-innovator",
|
|
102
137
|
WRESTLE = "wrestle",
|
|
103
138
|
DIVING_CATCH = "diving-catch",
|
|
104
139
|
}
|
|
105
140
|
|
|
106
141
|
enum SKILL_TYPE {
|
|
107
142
|
AGILITY = "A",
|
|
108
|
-
EXTRAORDINARY = "E",
|
|
143
|
+
EXTRAORDINARY = "E", // TRAIT
|
|
109
144
|
GENERAL = "G",
|
|
110
145
|
MUTATION = "M",
|
|
111
146
|
PASSING = "P",
|
|
112
147
|
STRENGTH = "S",
|
|
113
|
-
|
|
148
|
+
DEVIOUS = "D",
|
|
149
|
+
SPECIAL = "SP", // SPECIALIST
|
|
114
150
|
}
|
|
115
151
|
|
|
116
|
-
|
|
152
|
+
const SKILL_AGILITY: SKILL_NAMES[] = [
|
|
153
|
+
SKILL_NAMES.CATCH,
|
|
154
|
+
SKILL_NAMES.DEFENSIVE,
|
|
155
|
+
SKILL_NAMES.DIVING_CATCH,
|
|
156
|
+
SKILL_NAMES.DIVING_TACKLE,
|
|
157
|
+
SKILL_NAMES.DODGE,
|
|
158
|
+
SKILL_NAMES.HIT_AND_RUN,
|
|
159
|
+
SKILL_NAMES.JUMP_UP,
|
|
160
|
+
SKILL_NAMES.LEAP,
|
|
161
|
+
SKILL_NAMES.SAFE_PAIR_OF_HANDS,
|
|
162
|
+
SKILL_NAMES.SIDESTEP,
|
|
163
|
+
SKILL_NAMES.SPRINT,
|
|
164
|
+
SKILL_NAMES.SURE_FEET,
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const SKILL_EXTRAORDINARY: SKILL_NAMES[] = [
|
|
168
|
+
SKILL_NAMES.ALWAYS_HUNGRY,
|
|
169
|
+
SKILL_NAMES.ANIMAL_SAVAGERY,
|
|
170
|
+
SKILL_NAMES.ANIMOSITY,
|
|
171
|
+
// SKILL_NAMES.BALL_AND_CHAIN,
|
|
172
|
+
SKILL_NAMES.BONE_HEAD,
|
|
173
|
+
// SKILL_NAMES.BOMBARDIER,
|
|
174
|
+
SKILL_NAMES.BLOODLUST,
|
|
175
|
+
SKILL_NAMES.BREATHE_FIRE,
|
|
176
|
+
SKILL_NAMES.CHAINSAW,
|
|
177
|
+
SKILL_NAMES.DECAY,
|
|
178
|
+
SKILL_NAMES.DRUNKARD,
|
|
179
|
+
// SKILL_NAMES.HATRED,
|
|
180
|
+
SKILL_NAMES.HYPNOTIC_GAZE,
|
|
181
|
+
// SKILL_NAMES.INSIGNIFICANT,
|
|
182
|
+
// SKILL_NAMES.KICK_TEAMMATE,
|
|
183
|
+
SKILL_NAMES.LONER,
|
|
184
|
+
SKILL_NAMES.MY_BALL,
|
|
185
|
+
SKILL_NAMES.NO_HANDS,
|
|
186
|
+
SKILL_NAMES.PICK_ME_UP,
|
|
187
|
+
SKILL_NAMES.PLAGUE_RIDDEN,
|
|
188
|
+
SKILL_NAMES.POGO_STICK,
|
|
189
|
+
SKILL_NAMES.PROJECTILE_VOMIT,
|
|
190
|
+
SKILL_NAMES.REALLY_STUPID,
|
|
191
|
+
SKILL_NAMES.REGENERATION,
|
|
192
|
+
SKILL_NAMES.RIGHT_STUFF,
|
|
193
|
+
SKILL_NAMES.SECRET_WEAPON,
|
|
194
|
+
SKILL_NAMES.STAB,
|
|
195
|
+
SKILL_NAMES.STUNTY,
|
|
196
|
+
SKILL_NAMES.SWOOP,
|
|
197
|
+
SKILL_NAMES.TAKE_ROOT,
|
|
198
|
+
SKILL_NAMES.THROW_TEAM_MATE,
|
|
199
|
+
SKILL_NAMES.TIMMM_BER,
|
|
200
|
+
SKILL_NAMES.TITCHY,
|
|
201
|
+
// SKILL_NAMES.TRICKSTER,
|
|
202
|
+
SKILL_NAMES.UNCHANNELLED_FURY,
|
|
203
|
+
SKILL_NAMES.UNSTEADY,
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
const SKILL_GENERAL: SKILL_NAMES[] = [
|
|
207
|
+
SKILL_NAMES.BLOCK,
|
|
208
|
+
SKILL_NAMES.DAUNTLESS,
|
|
209
|
+
SKILL_NAMES.FEND,
|
|
210
|
+
SKILL_NAMES.FRENZY,
|
|
211
|
+
// SKILL_NAMES.KICK,
|
|
212
|
+
SKILL_NAMES.PRO,
|
|
213
|
+
SKILL_NAMES.STEADY_FOOTING,
|
|
214
|
+
SKILL_NAMES.STRIP_BALL,
|
|
215
|
+
SKILL_NAMES.SURE_HANDS,
|
|
216
|
+
SKILL_NAMES.TACKLE,
|
|
217
|
+
SKILL_NAMES.TAUNT,
|
|
218
|
+
SKILL_NAMES.WRESTLE,
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
const SKILL_MUTATION: SKILL_NAMES[] = [
|
|
222
|
+
SKILL_NAMES.BIG_HAND,
|
|
223
|
+
SKILL_NAMES.CLAW,
|
|
224
|
+
SKILL_NAMES.DISTURBING_PRESENCE,
|
|
225
|
+
SKILL_NAMES.EXTRA_ARMS,
|
|
226
|
+
SKILL_NAMES.FOUL_APPEARANCE,
|
|
227
|
+
SKILL_NAMES.HORNS,
|
|
228
|
+
SKILL_NAMES.IRON_HARD_SKIN,
|
|
229
|
+
SKILL_NAMES.MONSTROUS_MOUTH,
|
|
230
|
+
SKILL_NAMES.PREHENSILE_TAIL,
|
|
231
|
+
SKILL_NAMES.TENTACLES,
|
|
232
|
+
SKILL_NAMES.TWO_HEADS,
|
|
233
|
+
SKILL_NAMES.VERY_LONG_LEGS,
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
const SKILL_PASSING: SKILL_NAMES[] = [
|
|
237
|
+
SKILL_NAMES.ACCURATE,
|
|
238
|
+
SKILL_NAMES.CANNONEER,
|
|
239
|
+
SKILL_NAMES.CLOUD_BURSTER,
|
|
240
|
+
SKILL_NAMES.DUMP_OFF,
|
|
241
|
+
SKILL_NAMES.GIVE_AND_GO,
|
|
242
|
+
SKILL_NAMES.HAIL_MARY_PASS,
|
|
243
|
+
// SKILL_NAMES.LEADER,
|
|
244
|
+
SKILL_NAMES.NERVES_OF_STEEL,
|
|
245
|
+
SKILL_NAMES.ON_THE_BALL,
|
|
246
|
+
SKILL_NAMES.PASS,
|
|
247
|
+
SKILL_NAMES.PUNT,
|
|
248
|
+
SKILL_NAMES.SAFE_PASS,
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
const SKILL_STRENGTH: SKILL_NAMES[] = [
|
|
252
|
+
SKILL_NAMES.ARM_BAR,
|
|
253
|
+
SKILL_NAMES.BRAWLER,
|
|
254
|
+
SKILL_NAMES.BREAK_TACKLE,
|
|
255
|
+
// SKILL_NAMES.BULLSEYE,
|
|
256
|
+
// SKILL_NAMES.GRAB,
|
|
257
|
+
SKILL_NAMES.GUARD,
|
|
258
|
+
SKILL_NAMES.JUGGERNAUT,
|
|
259
|
+
SKILL_NAMES.MIGHTY_BLOW,
|
|
260
|
+
// SKILL_NAMES.MULTIPLE_BLOCK,
|
|
261
|
+
SKILL_NAMES.STAND_FIRM,
|
|
262
|
+
SKILL_NAMES.STRONG_ARM,
|
|
263
|
+
SKILL_NAMES.THICK_SKULL,
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
const SKILL_DEVIOUS: SKILL_NAMES[] = [
|
|
267
|
+
SKILL_NAMES.DIRTY_PLAYER,
|
|
268
|
+
SKILL_NAMES.EYE_GOUGE,
|
|
269
|
+
SKILL_NAMES.FUMBLEROOSKIE,
|
|
270
|
+
// SKILL_NAMES.LETHAL_FLIGHT,
|
|
271
|
+
// SKILL_NAMES.LONE_FOULER,
|
|
272
|
+
SKILL_NAMES.PILE_DRIVER,
|
|
273
|
+
SKILL_NAMES.PUT_THE_BOOT_IN,
|
|
274
|
+
SKILL_NAMES.QUICK_FOUL,
|
|
275
|
+
// SKILL_NAMES.SABOTEUR,
|
|
276
|
+
SKILL_NAMES.SHADOWING,
|
|
277
|
+
SKILL_NAMES.SNEAKY_GIT,
|
|
278
|
+
// SKILL_NAMES.VIOLENT_INNOVATOR,
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
const SKILL_SPECIAL: SKILL_NAMES[] = [
|
|
282
|
+
SKILL_NAMES.BIG_GUY,
|
|
283
|
+
SKILL_NAMES.SPECIALIST,
|
|
284
|
+
SKILL_NAMES.STAR,
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
const buildSkillsByType = (
|
|
288
|
+
skills: SKILL_NAMES[],
|
|
289
|
+
type: SKILL_TYPE
|
|
290
|
+
): Record<SKILL_NAMES, SKILL_TYPE> => {
|
|
291
|
+
return skills.reduce((acc, skill) => {
|
|
292
|
+
acc[skill] = type;
|
|
293
|
+
return acc;
|
|
294
|
+
}, {} as Record<SKILL_NAMES, SKILL_TYPE>);
|
|
295
|
+
};
|
|
296
|
+
|
|
117
297
|
const SKILLS: Record<SKILL_NAMES, SKILL_TYPE> = {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
[SKILL_NAMES.BRAWLER]: SKILL_TYPE.STRENGTH,
|
|
127
|
-
[SKILL_NAMES.BREAK_TACKLE]: SKILL_TYPE.STRENGTH,
|
|
128
|
-
[SKILL_NAMES.CANNONEER]: SKILL_TYPE.PASSING,
|
|
129
|
-
[SKILL_NAMES.CATCH]: SKILL_TYPE.AGILITY,
|
|
130
|
-
[SKILL_NAMES.CHAINSAW]: SKILL_TYPE.EXTRAORDINARY,
|
|
131
|
-
[SKILL_NAMES.CLAW]: SKILL_TYPE.MUTATION,
|
|
132
|
-
[SKILL_NAMES.CLOUD_BURSTER]: SKILL_TYPE.PASSING,
|
|
133
|
-
[SKILL_NAMES.DAUNTLESS]: SKILL_TYPE.GENERAL,
|
|
134
|
-
[SKILL_NAMES.DECAY]: SKILL_TYPE.EXTRAORDINARY,
|
|
135
|
-
[SKILL_NAMES.DEFENSIVE]: SKILL_TYPE.AGILITY,
|
|
136
|
-
[SKILL_NAMES.DIRTY_PLAYER]: SKILL_TYPE.GENERAL,
|
|
137
|
-
[SKILL_NAMES.DISTURBING_PRESENCE]: SKILL_TYPE.MUTATION,
|
|
138
|
-
[SKILL_NAMES.DIVING_TACKLE]: SKILL_TYPE.AGILITY,
|
|
139
|
-
[SKILL_NAMES.DODGE]: SKILL_TYPE.AGILITY,
|
|
140
|
-
[SKILL_NAMES.DRUNKARD]: SKILL_TYPE.EXTRAORDINARY,
|
|
141
|
-
[SKILL_NAMES.DUMP_OFF]: SKILL_TYPE.PASSING,
|
|
142
|
-
[SKILL_NAMES.EXTRA_ARMS]: SKILL_TYPE.MUTATION,
|
|
143
|
-
[SKILL_NAMES.FEND]: SKILL_TYPE.GENERAL,
|
|
144
|
-
[SKILL_NAMES.FOUL_APPEARANCE]: SKILL_TYPE.MUTATION,
|
|
145
|
-
[SKILL_NAMES.FRENZY]: SKILL_TYPE.GENERAL,
|
|
146
|
-
[SKILL_NAMES.FUMBLEROOSKIE]: SKILL_TYPE.PASSING,
|
|
147
|
-
[SKILL_NAMES.GUARD]: SKILL_TYPE.STRENGTH,
|
|
148
|
-
[SKILL_NAMES.HORNS]: SKILL_TYPE.MUTATION,
|
|
149
|
-
[SKILL_NAMES.HYPNOTIC_GAZE]: SKILL_TYPE.EXTRAORDINARY,
|
|
150
|
-
[SKILL_NAMES.IRON_HARD_SKIN]: SKILL_TYPE.MUTATION,
|
|
151
|
-
[SKILL_NAMES.JUGGERNAUT]: SKILL_TYPE.STRENGTH,
|
|
152
|
-
[SKILL_NAMES.JUMP_UP]: SKILL_TYPE.AGILITY,
|
|
153
|
-
[SKILL_NAMES.LEAP]: SKILL_TYPE.AGILITY,
|
|
154
|
-
[SKILL_NAMES.LONER]: SKILL_TYPE.EXTRAORDINARY,
|
|
155
|
-
[SKILL_NAMES.MIGHTY_BLOW]: SKILL_TYPE.STRENGTH,
|
|
156
|
-
[SKILL_NAMES.MONSTROUS_MOUTH]: SKILL_TYPE.MUTATION,
|
|
157
|
-
[SKILL_NAMES.NERVES_OF_STEEL]: SKILL_TYPE.PASSING,
|
|
158
|
-
[SKILL_NAMES.NO_HANDS]: SKILL_TYPE.EXTRAORDINARY,
|
|
159
|
-
[SKILL_NAMES.ON_THE_BALL]: SKILL_TYPE.PASSING,
|
|
160
|
-
[SKILL_NAMES.PASS]: SKILL_TYPE.PASSING,
|
|
161
|
-
[SKILL_NAMES.PILE_DRIVER]: SKILL_TYPE.STRENGTH,
|
|
162
|
-
[SKILL_NAMES.PLAGUE_RIDDEN]: SKILL_TYPE.EXTRAORDINARY,
|
|
163
|
-
[SKILL_NAMES.POGO_STICK]: SKILL_TYPE.EXTRAORDINARY,
|
|
164
|
-
[SKILL_NAMES.PREHENSILE_TAIL]: SKILL_TYPE.MUTATION,
|
|
165
|
-
[SKILL_NAMES.PROJECTILE_VOMIT]: SKILL_TYPE.EXTRAORDINARY,
|
|
166
|
-
[SKILL_NAMES.REALLY_STUPID]: SKILL_TYPE.EXTRAORDINARY,
|
|
167
|
-
[SKILL_NAMES.REGENERATION]: SKILL_TYPE.EXTRAORDINARY,
|
|
168
|
-
[SKILL_NAMES.RIGHT_STUFF]: SKILL_TYPE.EXTRAORDINARY,
|
|
169
|
-
[SKILL_NAMES.RUNNING_PASS]: SKILL_TYPE.PASSING,
|
|
170
|
-
[SKILL_NAMES.SAFE_PASS]: SKILL_TYPE.PASSING,
|
|
171
|
-
[SKILL_NAMES.SECRET_WEAPON]: SKILL_TYPE.EXTRAORDINARY,
|
|
172
|
-
[SKILL_NAMES.SHADOWING]: SKILL_TYPE.GENERAL,
|
|
173
|
-
[SKILL_NAMES.SNEAKY_GIT]: SKILL_TYPE.AGILITY,
|
|
174
|
-
[SKILL_NAMES.SPECIALIST]: SKILL_TYPE.SPECIAL,
|
|
175
|
-
[SKILL_NAMES.SPRINT]: SKILL_TYPE.AGILITY,
|
|
176
|
-
[SKILL_NAMES.STAND_FIRM]: SKILL_TYPE.STRENGTH,
|
|
177
|
-
[SKILL_NAMES.STAR]: SKILL_TYPE.SPECIAL,
|
|
178
|
-
[SKILL_NAMES.STRIP_BALL]: SKILL_TYPE.GENERAL,
|
|
179
|
-
[SKILL_NAMES.STRONG_ARM]: SKILL_TYPE.STRENGTH,
|
|
180
|
-
[SKILL_NAMES.STUNTY]: SKILL_TYPE.EXTRAORDINARY,
|
|
181
|
-
[SKILL_NAMES.SURE_FEET]: SKILL_TYPE.AGILITY,
|
|
182
|
-
[SKILL_NAMES.SURE_HANDS]: SKILL_TYPE.GENERAL,
|
|
183
|
-
[SKILL_NAMES.TACKLE]: SKILL_TYPE.GENERAL,
|
|
184
|
-
[SKILL_NAMES.TAKE_ROOT]: SKILL_TYPE.EXTRAORDINARY,
|
|
185
|
-
[SKILL_NAMES.TENTACLES]: SKILL_TYPE.MUTATION,
|
|
186
|
-
[SKILL_NAMES.THICK_SKULL]: SKILL_TYPE.STRENGTH,
|
|
187
|
-
[SKILL_NAMES.THROW_TEAM_MATE]: SKILL_TYPE.EXTRAORDINARY,
|
|
188
|
-
[SKILL_NAMES.TITCHY]: SKILL_TYPE.EXTRAORDINARY,
|
|
189
|
-
[SKILL_NAMES.TWO_HEADS]: SKILL_TYPE.MUTATION,
|
|
190
|
-
[SKILL_NAMES.UNCHANNELLED_FURY]: SKILL_TYPE.EXTRAORDINARY,
|
|
191
|
-
[SKILL_NAMES.VERY_LONG_LEGS]: SKILL_TYPE.MUTATION,
|
|
192
|
-
[SKILL_NAMES.WRESTLE]: SKILL_TYPE.GENERAL,
|
|
193
|
-
[SKILL_NAMES.DIVING_CATCH]: SKILL_TYPE.AGILITY,
|
|
194
|
-
|
|
195
|
-
// [SKILL_NAMES.SIDE_STEP]: SKILL_TYPE.AGILITY,
|
|
298
|
+
...buildSkillsByType(SKILL_AGILITY, SKILL_TYPE.AGILITY),
|
|
299
|
+
...buildSkillsByType(SKILL_EXTRAORDINARY, SKILL_TYPE.EXTRAORDINARY),
|
|
300
|
+
...buildSkillsByType(SKILL_GENERAL, SKILL_TYPE.GENERAL),
|
|
301
|
+
...buildSkillsByType(SKILL_MUTATION, SKILL_TYPE.MUTATION),
|
|
302
|
+
...buildSkillsByType(SKILL_PASSING, SKILL_TYPE.PASSING),
|
|
303
|
+
...buildSkillsByType(SKILL_STRENGTH, SKILL_TYPE.STRENGTH),
|
|
304
|
+
...buildSkillsByType(SKILL_DEVIOUS, SKILL_TYPE.DEVIOUS),
|
|
305
|
+
...buildSkillsByType(SKILL_SPECIAL, SKILL_TYPE.SPECIAL),
|
|
196
306
|
};
|
|
197
307
|
|
|
308
|
+
const SKILL_IS_2025: Record<SKILL_NAMES, boolean> = Object.values(
|
|
309
|
+
SKILL_NAMES
|
|
310
|
+
).reduce((acc, id) => {
|
|
311
|
+
acc[id as SKILL_NAMES] = false;
|
|
312
|
+
return acc;
|
|
313
|
+
}, {} as Record<SKILL_NAMES, boolean>);
|
|
314
|
+
(SKILL_IS_2025 as Record<SKILL_NAMES, boolean>)[SKILL_NAMES.BLOCK] = true;
|
|
315
|
+
|
|
198
316
|
// Strip Ball: When a player with this skill pushes an opposing player during a Block, they will cause the opposing player to drop the ball in square they are pushed back to, even if the opposing player is not Knocked Down.
|
|
199
317
|
// Sure Hands: A player with Sure Hands is allowed to re-roll the result if they fail to pick up the ball. In addition, the Strip Ball skill does not work against a player with this skill.
|
|
200
318
|
function useSkill(
|
|
@@ -291,6 +409,7 @@ const getPlayersWithSkill = (
|
|
|
291
409
|
|
|
292
410
|
const hasSkillDisplayableInfo = (skillId: SKILL_NAMES) => {
|
|
293
411
|
const activables = [
|
|
412
|
+
SKILL_NAMES.BLOODLUST,
|
|
294
413
|
SKILL_NAMES.LONER,
|
|
295
414
|
SKILL_NAMES.ANIMOSITY,
|
|
296
415
|
SKILL_NAMES.CHAINSAW,
|
|
@@ -349,6 +468,18 @@ function getSpecialistLimit(teamPlayers: PlayerWithSkill[]) {
|
|
|
349
468
|
return SPECIALIST_LIMIT + extra;
|
|
350
469
|
}
|
|
351
470
|
|
|
471
|
+
// Migration 2025: indique si une compétence legacy existe dans la nouvelle liste 2025
|
|
472
|
+
const SKILL_MIGRATION_2025: Record<SKILL_NAMES, boolean> = (() => {
|
|
473
|
+
const normalize = (value: string) =>
|
|
474
|
+
value.toLowerCase().replace(/[^a-z0-9]/gi, "");
|
|
475
|
+
const migrated = new Set<string>(newSkills.map((s) => normalize(s.name)));
|
|
476
|
+
const map = {} as Record<SKILL_NAMES, boolean>;
|
|
477
|
+
(Object.values(SKILL_NAMES) as SKILL_NAMES[]).forEach((id) => {
|
|
478
|
+
map[id] = migrated.has(normalize(id));
|
|
479
|
+
});
|
|
480
|
+
return map;
|
|
481
|
+
})();
|
|
482
|
+
|
|
352
483
|
function getSkillLimitSizeTeam(
|
|
353
484
|
player: PlayerWithSkill,
|
|
354
485
|
teamPlayers: PlayerWithSkill[]
|
|
@@ -388,6 +519,7 @@ function getNoTurnoverSkill() {
|
|
|
388
519
|
SKILL_NAMES.ANIMOSITY,
|
|
389
520
|
SKILL_NAMES.TAKE_ROOT,
|
|
390
521
|
SKILL_NAMES.UNCHANNELLED_FURY,
|
|
522
|
+
SKILL_NAMES.BLOODLUST,
|
|
391
523
|
SKILL_NAMES.DUMP_OFF,
|
|
392
524
|
SKILL_NAMES.HYPNOTIC_GAZE,
|
|
393
525
|
SKILL_NAMES.FOUL_APPEARANCE,
|
|
@@ -480,6 +612,8 @@ export {
|
|
|
480
612
|
SKILL_DURATION,
|
|
481
613
|
SKILL_INFO_ACTIVE,
|
|
482
614
|
SKILL_INFO_INACTIVE,
|
|
615
|
+
SKILL_IS_2025,
|
|
616
|
+
SKILL_MIGRATION_2025,
|
|
483
617
|
SKILL_NAMES,
|
|
484
618
|
SKILL_TYPE,
|
|
485
619
|
SKILLS,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Hit and Run reuses the same strategies as Sidestep
|
|
2
|
+
// The UI is a toggle + select in the block action dialog
|
|
3
|
+
export { SidestepStrategy as HitAndRunStrategy, CustomPriorities } from "./sidestep";
|
|
4
|
+
|
|
5
|
+
export enum HitAndRunMode {
|
|
6
|
+
DISABLED = "DISABLED",
|
|
7
|
+
ENABLED = "ENABLED",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface HitAndRunPreference {
|
|
11
|
+
mode: HitAndRunMode;
|
|
12
|
+
strategy: import("./sidestep").SidestepStrategy;
|
|
13
|
+
customPriorities?: import("./safePairOfHands").CustomPriorities; // Priorités personnalisées pour stratégie CUSTOM
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Interface commune pour les priorités personnalisées
|
|
2
|
+
// Permet de définir une priorité de 1 (meilleur) à 8 (pire) pour chaque case adjacente
|
|
3
|
+
export interface CustomPriorities {
|
|
4
|
+
topLeft: number; // (-1,-1) - Case en haut à gauche
|
|
5
|
+
top: number; // (0,-1) - Case en haut
|
|
6
|
+
topRight: number; // (1,-1) - Case en haut à droite
|
|
7
|
+
left: number; // (-1,0) - Case à gauche
|
|
8
|
+
right: number; // (1,0) - Case à droite
|
|
9
|
+
bottomLeft: number; // (-1,1) - Case en bas à gauche
|
|
10
|
+
bottom: number; // (0,1) - Case en bas
|
|
11
|
+
bottomRight: number; // (1,1) - Case en bas à droite
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum SafePairOfHandsMode {
|
|
15
|
+
DISABLED = "DISABLED",
|
|
16
|
+
AUTO = "AUTO",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum SafePairOfHandsStrategy {
|
|
20
|
+
TOWARD_TEAMMATE = "TOWARD_TEAMMATE",
|
|
21
|
+
AWAY_FROM_THREAT = "AWAY_FROM_THREAT",
|
|
22
|
+
TOWARD_ENDZONE = "TOWARD_ENDZONE",
|
|
23
|
+
AWAY_FROM_SIDELINE = "AWAY_FROM_SIDELINE",
|
|
24
|
+
SAFE_SQUARE = "SAFE_SQUARE",
|
|
25
|
+
RANDOM = "RANDOM",
|
|
26
|
+
CUSTOM = "CUSTOM", // Stratégie avec priorités personnalisées
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SafePairOfHandsPreference {
|
|
30
|
+
mode: SafePairOfHandsMode;
|
|
31
|
+
strategy: SafePairOfHandsStrategy;
|
|
32
|
+
customPriorities?: CustomPriorities; // Priorités personnalisées pour stratégie CUSTOM
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Réutilisation de l'interface commune pour les priorités personnalisées
|
|
2
|
+
export { CustomPriorities } from "./safePairOfHands";
|
|
3
|
+
|
|
4
|
+
export enum SidestepMode {
|
|
5
|
+
DISABLED = "DISABLED",
|
|
6
|
+
AUTO = "AUTO",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export enum SidestepStrategy {
|
|
10
|
+
AWAY_FROM_BALL = "AWAY_FROM_BALL",
|
|
11
|
+
TOWARD_BALL = "TOWARD_BALL",
|
|
12
|
+
AWAY_FROM_THREAT = "AWAY_FROM_THREAT",
|
|
13
|
+
MARK = "MARK",
|
|
14
|
+
TOWARD_SIDELINE = "TOWARD_SIDELINE",
|
|
15
|
+
AWAY_FROM_SIDELINE = "AWAY_FROM_SIDELINE",
|
|
16
|
+
TOWARD_ENDZONE = "TOWARD_ENDZONE",
|
|
17
|
+
RANDOM = "RANDOM",
|
|
18
|
+
CUSTOM = "CUSTOM", // Stratégie avec priorités personnalisées
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SidestepPreference {
|
|
22
|
+
mode: SidestepMode;
|
|
23
|
+
strategy: SidestepStrategy;
|
|
24
|
+
customPriorities?: import("./safePairOfHands").CustomPriorities; // Priorités personnalisées pour stratégie CUSTOM
|
|
25
|
+
}
|
package/status.ts
CHANGED
|
@@ -10,6 +10,8 @@ export enum PLAYER_STATUS {
|
|
|
10
10
|
// For other casuality (death), update all playerTo.status === PLAYER_STATUS.CASUALTY
|
|
11
11
|
SENDOFF = 6, // Expulsé
|
|
12
12
|
BONE_HEAD = 7, // Plus de zdt
|
|
13
|
+
EYE_GOUGED = 8, // Ne peut pas fournir de soutiens
|
|
14
|
+
BLOODLUST = 9, // Soif de sang - ne peut pas passer/transmettre/marquer
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export const hasStatusCanFaceup = (player: Player) => {
|
|
@@ -21,17 +23,27 @@ export const hasStatusCanStandup = (player: Player) => {
|
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
export const hasStatusTacleZone = (player: Player) => {
|
|
24
|
-
// BONE_HEAD doesn't have
|
|
25
|
-
return
|
|
26
|
+
// BONE_HEAD doesn't have tackle zone, EYE_GOUGED keeps tackle zone
|
|
27
|
+
return (
|
|
28
|
+
player.status === PLAYER_STATUS.STANDING ||
|
|
29
|
+
player.status === PLAYER_STATUS.EYE_GOUGED ||
|
|
30
|
+
player.status === PLAYER_STATUS.BLOODLUST
|
|
31
|
+
);
|
|
26
32
|
};
|
|
27
33
|
|
|
28
34
|
export const hasStatusStanding = (player: Player) => {
|
|
29
35
|
return (
|
|
30
36
|
player.status === PLAYER_STATUS.STANDING ||
|
|
31
|
-
player.status === PLAYER_STATUS.BONE_HEAD
|
|
37
|
+
player.status === PLAYER_STATUS.BONE_HEAD ||
|
|
38
|
+
player.status === PLAYER_STATUS.EYE_GOUGED ||
|
|
39
|
+
player.status === PLAYER_STATUS.BLOODLUST
|
|
32
40
|
);
|
|
33
41
|
};
|
|
34
42
|
|
|
43
|
+
export const hasStatusCanAssist = (player: Player) => {
|
|
44
|
+
return player.status !== PLAYER_STATUS.EYE_GOUGED;
|
|
45
|
+
};
|
|
46
|
+
|
|
35
47
|
export const hasStatusInjury = (player: Player) => {
|
|
36
48
|
return (
|
|
37
49
|
player.status === PLAYER_STATUS.KO ||
|
package/store.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { AVATAR_RARITY } from "./career";
|
|
|
4
4
|
const STORE_ITEM_TYPE = {
|
|
5
5
|
AVATAR: "a",
|
|
6
6
|
BADGE: "b",
|
|
7
|
+
DICE: "d",
|
|
8
|
+
SEASON_AVATAR: "sa",
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
const PRODUCTS = {
|
|
@@ -69,6 +71,7 @@ const AVATAR_PRICE = {
|
|
|
69
71
|
[AVATAR_RARITY.COMMUN]: 100,
|
|
70
72
|
[AVATAR_RARITY.RARE]: 250,
|
|
71
73
|
[AVATAR_RARITY.UNIQUE]: 390,
|
|
74
|
+
[AVATAR_RARITY.SEASONAL]: 100,
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
const STORE_BADGE_PRICE = 180;
|
package/teams/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Scripts de conversion des équipes
|
|
2
|
+
|
|
3
|
+
## convert-csv-to-career.ts
|
|
4
|
+
|
|
5
|
+
Ce script convertit le fichier CSV `players_clean.csv` en un fichier TypeScript `career_v2025.ts` similaire à `career.ts` mais avec les nouvelles équipes et races du Blood Bowl 2025.
|
|
6
|
+
|
|
7
|
+
### Utilisation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd enefel
|
|
11
|
+
yarn convert:career
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
ou
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd enefel
|
|
18
|
+
npx ts-node teams/convert-csv-to-career.ts
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Fichiers
|
|
22
|
+
|
|
23
|
+
- **Input**: `teams/players_clean.csv` - Fichier CSV contenant les données des équipes
|
|
24
|
+
- **Output**: `teams/career_v2025.ts` - Fichier TypeScript généré avec les carrières
|
|
25
|
+
|
|
26
|
+
### Format du CSV
|
|
27
|
+
|
|
28
|
+
Le CSV doit contenir les colonnes suivantes:
|
|
29
|
+
- `Team`: Nom de l'équipe
|
|
30
|
+
- `Quantity`: Quantité de joueurs (ex: "0-16", "0-2")
|
|
31
|
+
- `Position`: Position du joueur
|
|
32
|
+
- `Keywords`: Mots-clés séparés par des virgules
|
|
33
|
+
- `Cost`: Coût en k (ex: "50k")
|
|
34
|
+
- `Skills`: Compétences séparées par des virgules
|
|
35
|
+
- `Primary`: Types de compétences primaires (ex: "G", "AG")
|
|
36
|
+
- `Secondary`: Types de compétences secondaires (ex: "AS", "PS")
|
|
37
|
+
- `MA`, `ST`, `AG`, `PA`, `AR`: Statistiques (ex: "6", "3+", "4+")
|
|
38
|
+
|
|
39
|
+
### Mapping des compétences
|
|
40
|
+
|
|
41
|
+
Le script mappe automatiquement les noms de compétences du CSV vers les valeurs de l'enum `SKILL_NAMES`. Certaines compétences avec valeurs sont gérées spécialement:
|
|
42
|
+
- `Loner (4+)` → `[SKILL_NAMES.LONER, 4]`
|
|
43
|
+
- `Mighty Blow (+1)` → `[SKILL_NAMES.MIGHTY_BLOW, 1]`
|
|
44
|
+
- `Dirty Player (2)` → `[SKILL_NAMES.DIRTY_PLAYER, 2]`
|
|
45
|
+
- `Bloodlust (2+)` → not implemented (skill commented out)
|
|
46
|
+
|
|
47
|
+
### Notes
|
|
48
|
+
|
|
49
|
+
- Les compétences non mappées afficheront un avertissement dans la console
|
|
50
|
+
- Les avatars sont déterminés automatiquement à partir des keywords et de la position
|
|
51
|
+
- Le script génère automatiquement les enums `RACE` et `CAREER_ID` à partir des données du CSV
|
|
52
|
+
|
|
53
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { parse } from "csv-parse/sync";
|
|
2
|
+
import { stringify } from "csv-stringify/sync";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
const inputPath = "./teams/raw.csv";
|
|
6
|
+
const outputPath = "./teams/players_clean.csv";
|
|
7
|
+
|
|
8
|
+
const raw = fs.readFileSync(inputPath, "utf8");
|
|
9
|
+
|
|
10
|
+
// parse CSV
|
|
11
|
+
const rows = parse(raw, {
|
|
12
|
+
columns: true,
|
|
13
|
+
skip_empty_lines: true,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// regex format: 633+4+8+ → 6 | 3 | 3+ | 4+ | 8+
|
|
17
|
+
const STAT_REGEX = /^(\d)(\d)(\d\+)(\d\+)(\d{1,2}\+)$/;
|
|
18
|
+
|
|
19
|
+
const cleanRows = rows.map((row) => {
|
|
20
|
+
const stats = row["Stats"].replace(/\s+/g, "");
|
|
21
|
+
|
|
22
|
+
const match = stats.match(STAT_REGEX);
|
|
23
|
+
|
|
24
|
+
if (!match) {
|
|
25
|
+
row.MA = "";
|
|
26
|
+
row.ST = "";
|
|
27
|
+
row.AG = "";
|
|
28
|
+
row.PA = "";
|
|
29
|
+
row.AR = "";
|
|
30
|
+
return row;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [, MA, ST, AG, PA, AR] = match;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
...row,
|
|
37
|
+
MA,
|
|
38
|
+
ST,
|
|
39
|
+
AG,
|
|
40
|
+
PA,
|
|
41
|
+
AR,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// remove old column
|
|
46
|
+
cleanRows.forEach((r) => delete r["Stats"]);
|
|
47
|
+
|
|
48
|
+
// stringify
|
|
49
|
+
const csv = stringify(cleanRows, { header: true });
|
|
50
|
+
|
|
51
|
+
// write output
|
|
52
|
+
fs.writeFileSync(outputPath, csv);
|
|
53
|
+
|
|
54
|
+
console.log("players_clean.csv generated");
|