enefel 2.8.0 → 2.9.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.
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { CAREER_ID, RACE } from "../career";
3
+ import { getRaceFromCareer, getRaceSpecialSkills } from "../race";
4
+ import { isSpecialSkill, SKILL_NAMES } from "../skill";
5
+
6
+ describe("getRaceSpecialSkills", () => {
7
+ it("devrait retourner les compétences spéciales communes à une race", () => {
8
+ const imperialSkills = getRaceSpecialSkills(RACE.IMPERIAL);
9
+
10
+ // Imperial devrait avoir 'specialist' car 3/5 carrières l'ont
11
+ expect(imperialSkills).toContain("specialist");
12
+ expect(imperialSkills.length).toBeGreaterThan(0);
13
+
14
+ // Vérifier que toutes les compétences retournées sont bien spéciales
15
+ imperialSkills.forEach((skill) => {
16
+ expect(isSpecialSkill(skill as SKILL_NAMES)).toBe(true);
17
+ });
18
+ });
19
+
20
+ it("devrait retourner 'star' pour Vampire", () => {
21
+ const vampireSkills = getRaceSpecialSkills(RACE.VAMPIRE);
22
+ expect(vampireSkills).toContain("star");
23
+ });
24
+
25
+ it("devrait retourner 'specialist' pour Amazon", () => {
26
+ const amazonSkills = getRaceSpecialSkills(RACE.AMAZON);
27
+ expect(amazonSkills).toContain("specialist");
28
+ });
29
+
30
+ it("devrait retourner un tableau vide pour une race sans compétences spéciales communes", () => {
31
+ const humanSkills = getRaceSpecialSkills(RACE.HUMAN);
32
+ // Human n'a pas de compétences spéciales communes à toutes les carrières
33
+ expect(Array.isArray(humanSkills)).toBe(true);
34
+ });
35
+
36
+ it("devrait fonctionner avec un RACE obtenu depuis un CAREER_ID", () => {
37
+ const careerId = CAREER_ID.IMPERIAL_BODYGUARD;
38
+ const race = getRaceFromCareer(careerId);
39
+
40
+ expect(race).toBe(RACE.IMPERIAL);
41
+
42
+ const skills = getRaceSpecialSkills(race!);
43
+ expect(skills.length).toBeGreaterThan(0);
44
+ expect(skills).toContain("specialist");
45
+ });
46
+
47
+ it("devrait retourner les compétences qui apparaissent dans au moins 50% des carrières", () => {
48
+ const imperialSkills = getRaceSpecialSkills(RACE.IMPERIAL);
49
+
50
+ // Imperial a 5 carrières, donc le seuil est 3 (50% de 5 = 2.5, arrondi à 3)
51
+ // 'specialist' apparaît dans 3 carrières (thrower, blitzer, bodyguard)
52
+ // Donc 'specialist' devrait être retourné
53
+ expect(imperialSkills).toContain("specialist");
54
+
55
+ // 'big-guy' apparaît seulement dans 1 carrière (ogre), donc ne devrait pas être retourné
56
+ expect(imperialSkills).not.toContain("big-guy");
57
+ });
58
+ });
package/career.ts CHANGED
@@ -180,7 +180,7 @@ enum CAREER_VERSION {
180
180
  V2020 = "v2020",
181
181
  }
182
182
 
183
- type Career = {
183
+ export type Career = {
184
184
  MA: number;
185
185
  ST: number;
186
186
  AG: number;
@@ -2162,12 +2162,4 @@ const getCareers = (): Record<CAREER_ID, Career> => {
2162
2162
  return CAREER;
2163
2163
  };
2164
2164
 
2165
- export {
2166
- AVATAR,
2167
- AVATAR_RARITY,
2168
- CAREER,
2169
- CAREER_ID,
2170
- CAREER_VERSION,
2171
- getCareers,
2172
- RACE,
2173
- };
2165
+ export { AVATAR, AVATAR_RARITY, CAREER_ID, CAREER_VERSION, getCareers, RACE };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const career_1 = require("../career");
5
+ const race_1 = require("../race");
6
+ const skill_1 = require("../skill");
7
+ (0, globals_1.describe)("getRaceSpecialSkills", () => {
8
+ (0, globals_1.it)("devrait retourner les compétences spéciales communes à une race", () => {
9
+ const imperialSkills = (0, race_1.getRaceSpecialSkills)(career_1.RACE.IMPERIAL);
10
+ // Imperial devrait avoir 'specialist' car 3/5 carrières l'ont
11
+ (0, globals_1.expect)(imperialSkills).toContain("specialist");
12
+ (0, globals_1.expect)(imperialSkills.length).toBeGreaterThan(0);
13
+ // Vérifier que toutes les compétences retournées sont bien spéciales
14
+ imperialSkills.forEach((skill) => {
15
+ (0, globals_1.expect)((0, skill_1.isSpecialSkill)(skill)).toBe(true);
16
+ });
17
+ });
18
+ (0, globals_1.it)("devrait retourner 'star' pour Vampire", () => {
19
+ const vampireSkills = (0, race_1.getRaceSpecialSkills)(career_1.RACE.VAMPIRE);
20
+ (0, globals_1.expect)(vampireSkills).toContain("star");
21
+ });
22
+ (0, globals_1.it)("devrait retourner 'specialist' pour Amazon", () => {
23
+ const amazonSkills = (0, race_1.getRaceSpecialSkills)(career_1.RACE.AMAZON);
24
+ (0, globals_1.expect)(amazonSkills).toContain("specialist");
25
+ });
26
+ (0, globals_1.it)("devrait retourner un tableau vide pour une race sans compétences spéciales communes", () => {
27
+ const humanSkills = (0, race_1.getRaceSpecialSkills)(career_1.RACE.HUMAN);
28
+ // Human n'a pas de compétences spéciales communes à toutes les carrières
29
+ (0, globals_1.expect)(Array.isArray(humanSkills)).toBe(true);
30
+ });
31
+ (0, globals_1.it)("devrait fonctionner avec un RACE obtenu depuis un CAREER_ID", () => {
32
+ const careerId = career_1.CAREER_ID.IMPERIAL_BODYGUARD;
33
+ const race = (0, race_1.getRaceFromCareer)(careerId);
34
+ (0, globals_1.expect)(race).toBe(career_1.RACE.IMPERIAL);
35
+ const skills = (0, race_1.getRaceSpecialSkills)(race);
36
+ (0, globals_1.expect)(skills.length).toBeGreaterThan(0);
37
+ (0, globals_1.expect)(skills).toContain("specialist");
38
+ });
39
+ (0, globals_1.it)("devrait retourner les compétences qui apparaissent dans au moins 50% des carrières", () => {
40
+ const imperialSkills = (0, race_1.getRaceSpecialSkills)(career_1.RACE.IMPERIAL);
41
+ // Imperial a 5 carrières, donc le seuil est 3 (50% de 5 = 2.5, arrondi à 3)
42
+ // 'specialist' apparaît dans 3 carrières (thrower, blitzer, bodyguard)
43
+ // Donc 'specialist' devrait être retourné
44
+ (0, globals_1.expect)(imperialSkills).toContain("specialist");
45
+ // 'big-guy' apparaît seulement dans 1 carrière (ogre), donc ne devrait pas être retourné
46
+ (0, globals_1.expect)(imperialSkills).not.toContain("big-guy");
47
+ });
48
+ });
package/dist/career.d.ts CHANGED
@@ -159,7 +159,7 @@ declare enum CAREER_VERSION {
159
159
  V6 = "v6",
160
160
  V2020 = "v2020"
161
161
  }
162
- type Career = {
162
+ export type Career = {
163
163
  MA: number;
164
164
  ST: number;
165
165
  AG: number;
@@ -177,6 +177,5 @@ type Career = {
177
177
  hasSprite?: boolean;
178
178
  version: CAREER_VERSION;
179
179
  };
180
- declare const CAREER: Record<CAREER_ID, Career>;
181
180
  declare const getCareers: () => Record<CAREER_ID, Career>;
182
- export { AVATAR, AVATAR_RARITY, CAREER, CAREER_ID, CAREER_VERSION, getCareers, RACE, };
181
+ export { AVATAR, AVATAR_RARITY, CAREER_ID, CAREER_VERSION, getCareers, RACE };
package/dist/career.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // AG: Agility
5
5
  // AV: Armour Value
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.RACE = exports.getCareers = exports.CAREER_VERSION = exports.CAREER_ID = exports.CAREER = exports.AVATAR_RARITY = exports.AVATAR = void 0;
7
+ exports.RACE = exports.getCareers = exports.CAREER_VERSION = exports.CAREER_ID = exports.AVATAR_RARITY = exports.AVATAR = void 0;
8
8
  const skill_1 = require("./skill");
9
9
  var RACE;
10
10
  (function (RACE) {
@@ -2119,7 +2119,6 @@ const CAREER = {
2119
2119
  version: CAREER_VERSION.V6,
2120
2120
  },
2121
2121
  };
2122
- exports.CAREER = CAREER;
2123
2122
  const getCareers = () => {
2124
2123
  return CAREER;
2125
2124
  };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "enefel",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "enefel",
9
- "version": "2.8.0",
9
+ "version": "2.9.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "seedrandom": "3.0.5",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enefel",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "description": "Blood Bowl 3 game engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/dist/race.d.ts CHANGED
@@ -4,61 +4,10 @@ declare const ANIMOSITY_NUMBER = 2;
4
4
  declare const isPrivateRace: (race: RACE) => boolean;
5
5
  declare const hasUnlockRace: (user: User, race: RACE) => boolean;
6
6
  declare const hasUnlockCareer: (user: User, careerId: string) => boolean;
7
- declare const getCareerFromKey: (key: CAREER_ID) => {
8
- MA: number;
9
- ST: number;
10
- AG: number;
11
- PA: number;
12
- AV: number;
13
- normal: string;
14
- double: string;
15
- normalCoach: string;
16
- icons: string[];
17
- skills: (import("./skill").SKILL_NAMES | [import("./skill").SKILL_NAMES, number])[];
18
- avatars: AVATAR[];
19
- badge: RACE;
20
- range: number;
21
- cost: number;
22
- hasSprite?: boolean;
23
- version: import("./career").CAREER_VERSION;
24
- };
7
+ declare const getCareerFromKey: (key: CAREER_ID) => import("./career").Career;
25
8
  declare const getCareerKeyFromPlayer: (player: Player) => CAREER_ID;
26
- declare const getCareerFromPlayer: (player: Player) => {
27
- MA: number;
28
- ST: number;
29
- AG: number;
30
- PA: number;
31
- AV: number;
32
- normal: string;
33
- double: string;
34
- normalCoach: string;
35
- icons: string[];
36
- skills: (import("./skill").SKILL_NAMES | [import("./skill").SKILL_NAMES, number])[];
37
- avatars: AVATAR[];
38
- badge: RACE;
39
- range: number;
40
- cost: number;
41
- hasSprite?: boolean;
42
- version: import("./career").CAREER_VERSION;
43
- };
44
- declare const getCareerFromIcon: (nameIcon: string) => {
45
- MA: number;
46
- ST: number;
47
- AG: number;
48
- PA: number;
49
- AV: number;
50
- normal: string;
51
- double: string;
52
- normalCoach: string;
53
- icons: string[];
54
- skills: (import("./skill").SKILL_NAMES | [import("./skill").SKILL_NAMES, number])[];
55
- avatars: AVATAR[];
56
- badge: RACE;
57
- range: number;
58
- cost: number;
59
- hasSprite?: boolean;
60
- version: import("./career").CAREER_VERSION;
61
- } | null;
9
+ declare const getCareerFromPlayer: (player: Player) => import("./career").Career;
10
+ declare const getCareerFromIcon: (nameIcon: string) => import("./career").Career | null;
62
11
  declare const getPlayerAvatarDefault: (player: Player) => string | null;
63
12
  declare const getPlayerAvatar: (player: Player) => string | null;
64
13
  declare const getPlayerAvatarPath: (player: Player) => "tile/shop/avatar" | "tile/shop/avatar-default" | null;
@@ -96,4 +45,5 @@ declare const getPlayersByState: (players: Player[]) => {
96
45
  inactives: Player[];
97
46
  lastRetired: Player[];
98
47
  };
99
- export { ANIMOSITY_NUMBER, COMPATIBILITY, GAIN_CAREER_POINT, GAIN_CAREER_POINT_FRIENDLY, getAvatarTypeFromPlayer, getCareerFromIcon, getCareerFromKey, getCareerFromPlayer, getCareerKeyFromPlayer, getCareerRanges, getCareersByRace, getPlayerAvatar, getPlayerAvatarDefault, getPlayerAvatarPath, getPlayerCompatibility, getPlayerGroup, getPlayersAvailableForPromo, getPlayersByState, getPriceCareer, getPromoCareer, getRaceCompatibility, getRaceFromCareer, getRaceFromPlayer, getRaceGroup, getTeamGroups, getTeamIncompatibleGroups, GROUP_NAME, hasUnlockCareer, hasUnlockRace, isNeutralGroup, isPlayerNeutralGroup, isPrivateRace, };
48
+ declare const getRaceSpecialSkills: (race: RACE) => string[];
49
+ export { ANIMOSITY_NUMBER, COMPATIBILITY, GAIN_CAREER_POINT, GAIN_CAREER_POINT_FRIENDLY, getAvatarTypeFromPlayer, getCareerFromIcon, getCareerFromKey, getCareerFromPlayer, getCareerKeyFromPlayer, getCareerRanges, getCareersByRace, getPlayerAvatar, getPlayerAvatarDefault, getPlayerAvatarPath, getPlayerCompatibility, getPlayerGroup, getPlayersAvailableForPromo, getPlayersByState, getPriceCareer, getPromoCareer, getRaceCompatibility, getRaceFromCareer, getRaceFromPlayer, getRaceGroup, getRaceSpecialSkills, getTeamGroups, getTeamIncompatibleGroups, GROUP_NAME, hasUnlockCareer, hasUnlockRace, isNeutralGroup, isPlayerNeutralGroup, isPrivateRace, };
package/dist/race.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isPrivateRace = exports.hasUnlockRace = exports.hasUnlockCareer = exports.GROUP_NAME = exports.getRaceGroup = exports.getRaceFromPlayer = exports.getRaceFromCareer = exports.getRaceCompatibility = exports.getPlayersByState = exports.getPlayerGroup = exports.getPlayerCompatibility = exports.getPlayerAvatarPath = exports.getPlayerAvatarDefault = exports.getPlayerAvatar = exports.getCareersByRace = exports.getCareerKeyFromPlayer = exports.getCareerFromPlayer = exports.getCareerFromKey = exports.getCareerFromIcon = exports.getAvatarTypeFromPlayer = exports.GAIN_CAREER_POINT_FRIENDLY = exports.GAIN_CAREER_POINT = exports.COMPATIBILITY = exports.ANIMOSITY_NUMBER = void 0;
6
+ exports.isPrivateRace = exports.hasUnlockRace = exports.hasUnlockCareer = exports.GROUP_NAME = exports.getRaceSpecialSkills = exports.getRaceGroup = exports.getRaceFromPlayer = exports.getRaceFromCareer = exports.getRaceCompatibility = exports.getPlayersByState = exports.getPlayerGroup = exports.getPlayerCompatibility = exports.getPlayerAvatarPath = exports.getPlayerAvatarDefault = exports.getPlayerAvatar = exports.getCareersByRace = exports.getCareerKeyFromPlayer = exports.getCareerFromPlayer = exports.getCareerFromKey = exports.getCareerFromIcon = exports.getAvatarTypeFromPlayer = exports.GAIN_CAREER_POINT_FRIENDLY = exports.GAIN_CAREER_POINT = exports.COMPATIBILITY = exports.ANIMOSITY_NUMBER = void 0;
7
7
  exports.getCareerRanges = getCareerRanges;
8
8
  exports.getPlayersAvailableForPromo = getPlayersAvailableForPromo;
9
9
  exports.getPriceCareer = getPriceCareer;
@@ -18,6 +18,7 @@ const avatarsDefault_1 = require("./avatarsDefault");
18
18
  const badge_1 = require("./badge");
19
19
  const career_1 = require("./career");
20
20
  const random_1 = require("./random");
21
+ const skill_1 = require("./skill");
21
22
  const state_1 = require("./state");
22
23
  const ANIMOSITY_NUMBER = 2;
23
24
  exports.ANIMOSITY_NUMBER = ANIMOSITY_NUMBER;
@@ -353,3 +354,32 @@ const getPlayersByState = (players) => {
353
354
  };
354
355
  };
355
356
  exports.getPlayersByState = getPlayersByState;
357
+ const getRaceSpecialSkills = (race) => {
358
+ const careers = (0, career_1.getCareers)();
359
+ const raceCareerIds = Object.keys(careers).filter((careerId) => careers[careerId].badge === race);
360
+ if (raceCareerIds.length === 0) {
361
+ return [];
362
+ }
363
+ // Compter la fréquence des compétences spéciales dans la race
364
+ const skillCount = {};
365
+ raceCareerIds.forEach((careerId) => {
366
+ const careerSkills = careers[careerId].skills;
367
+ careerSkills.forEach((skill) => {
368
+ const skillName = Array.isArray(skill) ? skill[0] : skill;
369
+ // Ne compter que les compétences spéciales
370
+ if ((0, skill_1.isSpecialSkill)(skillName)) {
371
+ skillCount[skillName] = (skillCount[skillName] || 0) + 1;
372
+ }
373
+ });
374
+ });
375
+ // Retourner les compétences spéciales qui apparaissent dans au moins 50% des carrières
376
+ // Exclure les compétences de joueur individuel (specialist, big-guy) qui ne sont pas des compétences de race
377
+ const threshold = Math.ceil(raceCareerIds.length * 0.5);
378
+ const representativeSkills = Object.entries(skillCount)
379
+ .filter(([_, count]) => count >= threshold)
380
+ .map(([skillName, _]) => skillName)
381
+ .filter((skillName) => skillName !== skill_1.SKILL_NAMES.SPECIALIST &&
382
+ skillName !== skill_1.SKILL_NAMES.BIG_GUY);
383
+ return representativeSkills;
384
+ };
385
+ exports.getRaceSpecialSkills = getRaceSpecialSkills;
@@ -8,3 +8,9 @@ module.exports = {
8
8
  },
9
9
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
10
10
  };
11
+
12
+
13
+
14
+
15
+
16
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enefel",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "description": "Blood Bowl 3 game engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/race.ts CHANGED
@@ -4,6 +4,7 @@ import { AVATAR_DEFAULT_LIST } from "./avatarsDefault";
4
4
  import { BADGE_NAMES, hasBadge } from "./badge";
5
5
  import { AVATAR, CAREER_ID, getCareers, RACE } from "./career";
6
6
  import { getRandomNumber } from "./random";
7
+ import { isSpecialSkill, SKILL_NAMES } from "./skill";
7
8
  import { canBeSelectedByUser } from "./state";
8
9
  import { Player, Team, User } from "./types/models";
9
10
 
@@ -373,6 +374,45 @@ const getPlayersByState = (players: Player[]) => {
373
374
  ),
374
375
  };
375
376
  };
377
+ const getRaceSpecialSkills = (race: RACE) => {
378
+ const careers = getCareers();
379
+ const raceCareerIds = Object.keys(careers).filter(
380
+ (careerId) => careers[careerId as CAREER_ID].badge === race
381
+ );
382
+
383
+ if (raceCareerIds.length === 0) {
384
+ return [];
385
+ }
386
+
387
+ // Compter la fréquence des compétences spéciales dans la race
388
+ const skillCount: Record<string, number> = {};
389
+
390
+ raceCareerIds.forEach((careerId) => {
391
+ const careerSkills = careers[careerId as CAREER_ID].skills;
392
+ careerSkills.forEach((skill) => {
393
+ const skillName = Array.isArray(skill) ? skill[0] : skill;
394
+ // Ne compter que les compétences spéciales
395
+ if (isSpecialSkill(skillName)) {
396
+ skillCount[skillName] = (skillCount[skillName] || 0) + 1;
397
+ }
398
+ });
399
+ });
400
+
401
+ // Retourner les compétences spéciales qui apparaissent dans au moins 50% des carrières
402
+ // Exclure les compétences de joueur individuel (specialist, big-guy) qui ne sont pas des compétences de race
403
+ const threshold = Math.ceil(raceCareerIds.length * 0.5);
404
+ const representativeSkills = Object.entries(skillCount)
405
+ .filter(([_, count]) => count >= threshold)
406
+ .map(([skillName, _]) => skillName)
407
+ .filter(
408
+ (skillName) =>
409
+ skillName !== SKILL_NAMES.SPECIALIST &&
410
+ skillName !== SKILL_NAMES.BIG_GUY
411
+ );
412
+
413
+ return representativeSkills;
414
+ };
415
+
376
416
  export {
377
417
  ANIMOSITY_NUMBER,
378
418
  COMPATIBILITY,
@@ -398,6 +438,7 @@ export {
398
438
  getRaceFromCareer,
399
439
  getRaceFromPlayer,
400
440
  getRaceGroup,
441
+ getRaceSpecialSkills,
401
442
  getTeamGroups,
402
443
  getTeamIncompatibleGroups,
403
444
  GROUP_NAME,