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
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
- RUNNING_PASS = "running-pass",
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
- SPECIAL = "SP",
148
+ DEVIOUS = "D",
149
+ SPECIAL = "SP", // SPECIALIST
114
150
  }
115
151
 
116
- // TODO: add text translation when add a skill
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
- [SKILL_NAMES.ACCURATE]: SKILL_TYPE.PASSING,
119
- [SKILL_NAMES.ALWAYS_HUNGRY]: SKILL_TYPE.EXTRAORDINARY,
120
- [SKILL_NAMES.ANIMAL_SAVAGERY]: SKILL_TYPE.EXTRAORDINARY,
121
- [SKILL_NAMES.ANIMOSITY]: SKILL_TYPE.SPECIAL,
122
- [SKILL_NAMES.BIG_GUY]: SKILL_TYPE.SPECIAL,
123
- [SKILL_NAMES.BIG_HAND]: SKILL_TYPE.MUTATION,
124
- [SKILL_NAMES.BLOCK]: SKILL_TYPE.GENERAL,
125
- [SKILL_NAMES.BONE_HEAD]: SKILL_TYPE.EXTRAORDINARY,
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 table zone
25
- return player.status === PLAYER_STATUS.STANDING;
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;
@@ -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");