@axiapps/gw2-data 0.1.0 → 0.1.2

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 (43) hide show
  1. package/README.md +156 -0
  2. package/data/overrides.json +24 -0
  3. package/package.json +3 -1
  4. package/src/engine/attributes.js +102 -23
  5. package/src/engine/boons.js +19 -1
  6. package/src/engine/constants.js +27 -2
  7. package/src/engine/index.js +4 -4
  8. package/src/engine/modifiers.js +57 -21
  9. package/src/wiki/parser.js +17 -9
  10. package/scripts/generate-fixtures.js +0 -242
  11. package/tests/api-client.test.js +0 -138
  12. package/tests/cache.test.js +0 -108
  13. package/tests/engine/attributes.test.js +0 -252
  14. package/tests/engine/boons.test.js +0 -129
  15. package/tests/engine/combos.test.js +0 -76
  16. package/tests/engine/constants.test.js +0 -576
  17. package/tests/engine/fixtures/berserker-thief.json +0 -61
  18. package/tests/engine/fixtures/berserker-warrior.json +0 -113
  19. package/tests/engine/fixtures/celestial-firebrand-wvw.json +0 -94
  20. package/tests/engine/fixtures/harrier-druid.json +0 -119
  21. package/tests/engine/fixtures/viper-mirage.json +0 -104
  22. package/tests/engine/graph.test.js +0 -30
  23. package/tests/engine/integration.test.js +0 -111
  24. package/tests/engine/modifiers.test.js +0 -473
  25. package/tests/engine/overrides.test.js +0 -70
  26. package/tests/engine/snapshot.test.js +0 -53
  27. package/tests/engine/test-utils.js +0 -20
  28. package/tests/engine/tooltips.test.js +0 -62
  29. package/tests/fixtures/capture.js +0 -160
  30. package/tests/fixtures/fixtures.json +0 -839
  31. package/tests/integration.test.js +0 -100
  32. package/tests/match.test.js +0 -176
  33. package/tests/merge.test.js +0 -128
  34. package/tests/normalize.test.js +0 -78
  35. package/tests/parser.test.js +0 -506
  36. package/tests/real-data.test.js +0 -296
  37. package/tests/relations.test.js +0 -80
  38. package/tests/resolver.test.js +0 -721
  39. package/tests/validate-live.js +0 -191
  40. package/tests/wiki-client.test.js +0 -468
  41. package/tests/wiki-integration.test.js +0 -177
  42. package/tests/wiki-live-validation.test.js +0 -61
  43. package/tests/wiki-snapshots.test.js +0 -166
@@ -1,113 +0,0 @@
1
- {
2
- "name": "Berserker Warrior",
3
- "description": "Heavy armor, 3-stat, full ascended, signets, Might+Fury assumed",
4
- "ctx": {
5
- "profession": "Warrior",
6
- "specializations": [
7
- {
8
- "id": 4,
9
- "majorChoices": {
10
- "1": 1444,
11
- "2": 1449,
12
- "3": 1437
13
- }
14
- }
15
- ],
16
- "equipment": {
17
- "slots": {
18
- "head": "Berserker's",
19
- "shoulders": "Berserker's",
20
- "chest": "Berserker's",
21
- "gloves": "Berserker's",
22
- "legs": "Berserker's",
23
- "boots": "Berserker's",
24
- "mainhand1": "Berserker's",
25
- "offhand1": "Berserker's",
26
- "back": "Berserker's",
27
- "accessory1": "Berserker's",
28
- "accessory2": "Berserker's",
29
- "amulet": "Berserker's",
30
- "ring1": "Berserker's",
31
- "ring2": "Berserker's"
32
- },
33
- "weapons": {
34
- "mainhand1": "greatsword"
35
- },
36
- "runes": {},
37
- "infusions": {},
38
- "enrichment": null,
39
- "food": null,
40
- "utility": null
41
- },
42
- "gameMode": "pve",
43
- "underwaterMode": false,
44
- "activeWeaponSet": 1,
45
- "skills": {
46
- "healId": null,
47
- "utilityIds": [
48
- 9093
49
- ],
50
- "eliteId": null
51
- },
52
- "assumedBoons": {
53
- "might": 25,
54
- "fury": true
55
- },
56
- "sigilStacks": null
57
- },
58
- "catalogs": {
59
- "traits": [
60
- {
61
- "id": 1444,
62
- "facts": [
63
- {
64
- "type": "AttributeAdjust",
65
- "target": "Power",
66
- "value": 120
67
- }
68
- ]
69
- },
70
- {
71
- "id": 1449,
72
- "facts": []
73
- },
74
- {
75
- "id": 1437,
76
- "facts": []
77
- }
78
- ],
79
- "specializations": [
80
- {
81
- "id": 4,
82
- "minorTraits": []
83
- }
84
- ],
85
- "skills": [],
86
- "runes": [],
87
- "foods": [],
88
- "utilities": [],
89
- "infusions": [],
90
- "enrichments": []
91
- },
92
- "expected": {
93
- "total": {
94
- "Power": 3463,
95
- "Precision": 1982,
96
- "Toughness": 1000,
97
- "Vitality": 1000,
98
- "Ferocity": 982,
99
- "ConditionDamage": 750,
100
- "HealingPower": 0,
101
- "Expertise": 0,
102
- "Concentration": 0
103
- },
104
- "derived": {
105
- "health": 19212,
106
- "critChance": 81.76190476190476,
107
- "critDamage": 215.46666666666667,
108
- "conditionDuration": 0,
109
- "boonDuration": 0,
110
- "armor": 2271
111
- }
112
- }
113
- }
@@ -1,94 +0,0 @@
1
- {
2
- "name": "Celestial Firebrand WvW",
3
- "description": "Heavy armor, 9-stat, WvW Celestial exclusion, rune bonuses",
4
- "ctx": {
5
- "profession": "Guardian",
6
- "specializations": [],
7
- "equipment": {
8
- "slots": {
9
- "head": "Celestial",
10
- "shoulders": "Celestial",
11
- "chest": "Celestial",
12
- "gloves": "Celestial",
13
- "legs": "Celestial",
14
- "boots": "Celestial",
15
- "mainhand1": "Celestial",
16
- "back": "Celestial",
17
- "accessory1": "Celestial",
18
- "accessory2": "Celestial",
19
- "amulet": "Celestial",
20
- "ring1": "Celestial",
21
- "ring2": "Celestial"
22
- },
23
- "weapons": {
24
- "mainhand1": "axe"
25
- },
26
- "runes": {
27
- "head": 24836,
28
- "shoulders": 24836,
29
- "chest": 24836,
30
- "gloves": 24836,
31
- "legs": 24836,
32
- "boots": 24836
33
- },
34
- "infusions": {},
35
- "enrichment": null,
36
- "food": null,
37
- "utility": null
38
- },
39
- "gameMode": "wvw",
40
- "underwaterMode": false,
41
- "activeWeaponSet": 1,
42
- "skills": {
43
- "healId": null,
44
- "utilityIds": [],
45
- "eliteId": null
46
- },
47
- "assumedBoons": null,
48
- "sigilStacks": null
49
- },
50
- "catalogs": {
51
- "traits": [],
52
- "specializations": [],
53
- "skills": [],
54
- "runes": [
55
- {
56
- "id": 24836,
57
- "name": "Superior Rune of the Scholar",
58
- "bonuses": [
59
- "+25 Power",
60
- "+35 Ferocity",
61
- "+50 Power",
62
- "+65 Ferocity",
63
- "+100 Power",
64
- "+125 Ferocity"
65
- ]
66
- }
67
- ],
68
- "foods": [],
69
- "utilities": [],
70
- "infusions": [],
71
- "enrichments": []
72
- },
73
- "expected": {
74
- "total": {
75
- "Power": 1710,
76
- "Precision": 1535,
77
- "Toughness": 1535,
78
- "Vitality": 1535,
79
- "Ferocity": 760,
80
- "ConditionDamage": 535,
81
- "HealingPower": 535,
82
- "Expertise": 0,
83
- "Concentration": 0
84
- },
85
- "derived": {
86
- "health": 16995,
87
- "critChance": 35.476190476190474,
88
- "critDamage": 200.66666666666666,
89
- "conditionDuration": 0,
90
- "boonDuration": 0,
91
- "armor": 2806
92
- }
93
- }
94
- }
@@ -1,119 +0,0 @@
1
- {
2
- "name": "Harrier Druid",
3
- "description": "Medium armor, 3-stat healing, enrichment, infusions",
4
- "ctx": {
5
- "profession": "Ranger",
6
- "specializations": [],
7
- "equipment": {
8
- "slots": {
9
- "head": "Harrier's",
10
- "shoulders": "Harrier's",
11
- "chest": "Harrier's",
12
- "gloves": "Harrier's",
13
- "legs": "Harrier's",
14
- "boots": "Harrier's",
15
- "mainhand1": "Harrier's",
16
- "back": "Harrier's",
17
- "accessory1": "Harrier's",
18
- "accessory2": "Harrier's",
19
- "amulet": "Harrier's",
20
- "ring1": "Harrier's",
21
- "ring2": "Harrier's"
22
- },
23
- "weapons": {
24
- "mainhand1": "staff"
25
- },
26
- "runes": {},
27
- "infusions": {
28
- "head": [
29
- 49432
30
- ],
31
- "shoulders": [
32
- 49432
33
- ],
34
- "chest": [
35
- 49432
36
- ],
37
- "gloves": [
38
- 49432
39
- ],
40
- "legs": [
41
- 49432
42
- ],
43
- "boots": [
44
- 49432
45
- ]
46
- },
47
- "enrichment": 78061,
48
- "food": null,
49
- "utility": null
50
- },
51
- "gameMode": "pve",
52
- "underwaterMode": false,
53
- "activeWeaponSet": 1,
54
- "skills": {
55
- "healId": null,
56
- "utilityIds": [],
57
- "eliteId": null
58
- },
59
- "assumedBoons": null,
60
- "sigilStacks": null
61
- },
62
- "catalogs": {
63
- "traits": [],
64
- "specializations": [],
65
- "skills": [],
66
- "runes": [],
67
- "foods": [],
68
- "utilities": [],
69
- "infusions": [
70
- {
71
- "id": 49432,
72
- "name": "+5 Healing Power Infusion",
73
- "infixUpgrade": {
74
- "attributes": [
75
- {
76
- "attribute": "Healing",
77
- "modifier": 5
78
- }
79
- ]
80
- }
81
- }
82
- ],
83
- "enrichments": [
84
- {
85
- "id": 78061,
86
- "name": "+10 Concentration Enrichment",
87
- "infixUpgrade": {
88
- "attributes": [
89
- {
90
- "attribute": "BoonDuration",
91
- "modifier": 10
92
- }
93
- ]
94
- }
95
- }
96
- ]
97
- },
98
- "expected": {
99
- "total": {
100
- "Power": 2288,
101
- "Precision": 1000,
102
- "Toughness": 1000,
103
- "Vitality": 1000,
104
- "Ferocity": 0,
105
- "ConditionDamage": 0,
106
- "HealingPower": 922,
107
- "Expertise": 0,
108
- "Concentration": 902
109
- },
110
- "derived": {
111
- "health": 15922,
112
- "critChance": 10,
113
- "critDamage": 150,
114
- "conditionDuration": 0,
115
- "boonDuration": 60.13333333333333,
116
- "armor": 2118
117
- }
118
- }
119
- }
@@ -1,104 +0,0 @@
1
- {
2
- "name": "Viper Mirage",
3
- "description": "Medium armor, 4-stat, trait conversions, food + utility",
4
- "ctx": {
5
- "profession": "Mesmer",
6
- "specializations": [
7
- {
8
- "id": 24,
9
- "majorChoices": {
10
- "1": 700
11
- }
12
- }
13
- ],
14
- "equipment": {
15
- "slots": {
16
- "head": "Viper's",
17
- "shoulders": "Viper's",
18
- "chest": "Viper's",
19
- "gloves": "Viper's",
20
- "legs": "Viper's",
21
- "boots": "Viper's",
22
- "mainhand1": "Viper's",
23
- "back": "Viper's",
24
- "accessory1": "Viper's",
25
- "accessory2": "Viper's",
26
- "amulet": "Viper's",
27
- "ring1": "Viper's",
28
- "ring2": "Viper's"
29
- },
30
- "weapons": {
31
- "mainhand1": "axe"
32
- },
33
- "runes": {},
34
- "infusions": {},
35
- "enrichment": null,
36
- "food": 91805,
37
- "utility": null
38
- },
39
- "gameMode": "pve",
40
- "underwaterMode": false,
41
- "activeWeaponSet": 1,
42
- "skills": {
43
- "healId": null,
44
- "utilityIds": [],
45
- "eliteId": null
46
- },
47
- "assumedBoons": null,
48
- "sigilStacks": null
49
- },
50
- "catalogs": {
51
- "traits": [
52
- {
53
- "id": 700,
54
- "facts": [
55
- {
56
- "type": "BuffConversion",
57
- "source": "Vitality",
58
- "target": "ConditionDamage",
59
- "percent": 10
60
- }
61
- ]
62
- }
63
- ],
64
- "specializations": [
65
- {
66
- "id": 24,
67
- "minorTraits": []
68
- }
69
- ],
70
- "skills": [],
71
- "runes": [],
72
- "foods": [
73
- {
74
- "id": 91805,
75
- "name": "Plate of Beef Rendang",
76
- "buff": "+100 Expertise\n+70 Condition Damage"
77
- }
78
- ],
79
- "utilities": [],
80
- "infusions": [],
81
- "enrichments": []
82
- },
83
- "expected": {
84
- "total": {
85
- "Power": 1980,
86
- "Precision": 1529,
87
- "Toughness": 1000,
88
- "Vitality": 1000,
89
- "Ferocity": 0,
90
- "ConditionDamage": 1150,
91
- "HealingPower": 0,
92
- "Expertise": 629,
93
- "Concentration": 0
94
- },
95
- "derived": {
96
- "health": 15922,
97
- "critChance": 35.19047619047619,
98
- "critDamage": 150,
99
- "conditionDuration": 41.93333333333333,
100
- "boonDuration": 0,
101
- "armor": 1967
102
- }
103
- }
104
- }
@@ -1,30 +0,0 @@
1
- "use strict";
2
-
3
- const { buildInteractionGraph } = require("../../src/engine/graph");
4
-
5
- describe("buildInteractionGraph", () => {
6
- test("builds graph from relations data", () => {
7
- const relations = new Map([
8
- [1444, { skills: [5489, 5507], traits: [] }],
9
- [1449, { skills: [], traits: [1444] }],
10
- ]);
11
- const graph = buildInteractionGraph(new Set([1444, 1449]), relations);
12
- expect(graph.get(1444).relatedSkills).toEqual(new Set([5489, 5507]));
13
- expect(graph.get(1449).relatedTraits).toEqual(new Set([1444]));
14
- });
15
-
16
- test("returns empty sets for traits with no relations", () => {
17
- const relations = new Map();
18
- const graph = buildInteractionGraph(new Set([100]), relations);
19
- expect(graph.get(100).relatedSkills.size).toBe(0);
20
- expect(graph.get(100).relatedTraits.size).toBe(0);
21
- });
22
-
23
- test("ignores traits not in activeTraitIds", () => {
24
- const relations = new Map([
25
- [999, { skills: [100], traits: [] }],
26
- ]);
27
- const graph = buildInteractionGraph(new Set([1444]), relations);
28
- expect(graph.has(999)).toBe(false);
29
- });
30
- });
@@ -1,111 +0,0 @@
1
- "use strict";
2
-
3
- const { StatEngine } = require("../../src/engine");
4
-
5
- function makeCatalogs() {
6
- return {
7
- traitById: new Map(),
8
- skillById: new Map(),
9
- specializationById: new Map(),
10
- runeById: new Map(),
11
- foodById: new Map(),
12
- utilityById: new Map(),
13
- infusionById: new Map(),
14
- enrichmentById: new Map(),
15
- };
16
- }
17
-
18
- function makeCtx(overrides = {}) {
19
- return {
20
- profession: "Warrior",
21
- specializations: [],
22
- equipment: { slots: {}, weapons: {}, runes: {}, infusions: {} },
23
- gameMode: "pve",
24
- underwaterMode: false,
25
- activeWeaponSet: 1,
26
- skills: {},
27
- assumedBoons: null,
28
- sigilStacks: null,
29
- ...overrides,
30
- };
31
- }
32
-
33
- describe("StatEngine", () => {
34
- test("constructs with catalogs", () => {
35
- const engine = new StatEngine(makeCatalogs());
36
- expect(engine).toBeDefined();
37
- });
38
-
39
- test("computeAttributes returns full breakdown", () => {
40
- const engine = new StatEngine(makeCatalogs());
41
- const result = engine.computeAttributes(makeCtx());
42
- expect(result.base.Power).toBe(1000);
43
- expect(result.total.Power).toBe(1000);
44
- expect(result.derived.health).toBe(19212);
45
- });
46
-
47
- test("collectModifiers returns array", () => {
48
- const engine = new StatEngine(makeCatalogs());
49
- const mods = engine.collectModifiers(makeCtx());
50
- expect(Array.isArray(mods)).toBe(true);
51
- });
52
-
53
- test("computeTooltip returns damage for valid skill", () => {
54
- const catalogs = makeCatalogs();
55
- const engine = new StatEngine(catalogs);
56
- const skill = { id: 1, name: "Slash", facts: [{ type: "Damage", dmg_multiplier: 0.8, hit_count: 1 }] };
57
- const ctx = makeCtx({
58
- equipment: { slots: {}, weapons: { mainhand1: "sword" }, runes: {}, infusions: {} },
59
- });
60
- const result = engine.computeTooltip(ctx, skill, "sword");
61
- expect(result).toBeDefined();
62
- expect(result.damage).toBeGreaterThan(0);
63
- });
64
-
65
- test("analyzeBoons returns boons and conditions arrays", () => {
66
- const engine = new StatEngine(makeCatalogs());
67
- const skills = [{
68
- name: "Shout", description: "Grant might to allies.", icon: "",
69
- facts: [{ type: "Buff", status: "Might", apply_count: 3, duration: 8 }],
70
- }];
71
- const result = engine.analyzeBoons(skills, []);
72
- expect(result.boons).toHaveLength(1);
73
- expect(result.boons[0].name).toBe("Might");
74
- });
75
-
76
- test("analyzeCombos returns fields and finishers arrays", () => {
77
- const engine = new StatEngine(makeCatalogs());
78
- const skills = [{
79
- name: "Flame Wall", icon: "", description: "",
80
- facts: [{ type: "ComboField", field_type: "Fire" }],
81
- }];
82
- const result = engine.analyzeCombos(skills, []);
83
- expect(result.fields).toHaveLength(1);
84
- });
85
-
86
- test("full pipeline: equip gear, compute attributes, get tooltip", () => {
87
- const catalogs = makeCatalogs();
88
- catalogs.specializationById.set(4, { id: 4, minorTraits: [] });
89
- catalogs.traitById.set(1444, {
90
- id: 1444, facts: [{ type: "AttributeAdjust", target: "Power", value: 150 }],
91
- });
92
-
93
- const engine = new StatEngine(catalogs);
94
- const ctx = makeCtx({
95
- specializations: [{ id: 4, majorChoices: { 1: 1444 } }],
96
- equipment: {
97
- slots: { chest: "Berserker's", legs: "Berserker's" },
98
- weapons: { mainhand1: "greatsword" },
99
- runes: {}, infusions: {},
100
- },
101
- });
102
-
103
- const attrs = engine.computeAttributes(ctx);
104
- expect(attrs.total.Power).toBeGreaterThan(1000);
105
- expect(attrs.traits.Power).toBe(150);
106
-
107
- const skill = { id: 5489, name: "Fireball", facts: [{ type: "Damage", dmg_multiplier: 0.75, hit_count: 1 }] };
108
- const tooltip = engine.computeTooltip(ctx, skill, "greatsword");
109
- expect(tooltip.damage).toBeGreaterThan(0);
110
- });
111
- });