From f142725dd8ec32f167408821074039b4034d169a Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 23 May 2026 08:21:09 -0400 Subject: [PATCH] Skill tree: subtrees, loadout slots, label fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skill trees now render the way the source does: each class has three named subtrees (e.g. Swordmaster: The Blade / The Will / The Way), each with its own 3-col or 5-col grid, sized in 72px cells. Extractor parses subtrees separately so the per-tree row/col coordinates are correct (previously all 22 nodes were stacked on one combined grid and overlapped). Connector edges are mapped per-subtree too. Loadout: new global 3-ability + 3-technique slot row at the bottom of the Skill Trees panel. The cap is global across all 5 classes (matches the source HTML which has `id=active-Ability-N` / `id=active-Technique-N` without per-tree scope). Click a slot to pick from any allocated Ability or Spice (for Ability slots) or any allocated Perk (for Technique slots); right-click clears. Slot backgrounds use the local ability.png / technique.png artwork copied into /icons. Label overlap fix: constrained the name label under each node to the node width (72px) and bumped the vertical gap from 44 to 60px so 2-3 line names have room without bleeding into the row below. Existing saved builds migrate cleanly — loadout normalizes to length-3 slot arrays if absent or malformed. Co-Authored-By: Claude Opus 4.7 (1M context) --- character-builder/data/index.json | 35 +- .../data/skills-benegesserit.json | 708 ++++++++++-------- character-builder/data/skills-mentat.json | 700 +++++++++-------- .../data/skills-planetologist.json | 600 ++++++++------- .../data/skills-swordmaster.json | 700 +++++++++-------- character-builder/data/skills-trooper.json | 700 +++++++++-------- .../frontend/public/icons/slot-ability.png | Bin 0 -> 3766 bytes .../frontend/public/icons/slot-technique.png | Bin 0 -> 3086 bytes character-builder/frontend/src/App.vue | 15 +- .../src/components/CharacterSummary.vue | 24 +- .../frontend/src/components/LoadoutSlots.vue | 424 +++++++++++ .../frontend/src/components/SkillTree.vue | 461 ++++++++---- character-builder/frontend/src/store.ts | 15 + character-builder/frontend/src/types.ts | 19 +- character-builder/scripts/extract.py | 236 +++--- 15 files changed, 2802 insertions(+), 1835 deletions(-) create mode 100644 character-builder/frontend/public/icons/slot-ability.png create mode 100644 character-builder/frontend/public/icons/slot-technique.png create mode 100644 character-builder/frontend/src/components/LoadoutSlots.vue diff --git a/character-builder/data/index.json b/character-builder/data/index.json index 0839683..a70199b 100644 --- a/character-builder/data/index.json +++ b/character-builder/data/index.json @@ -16,36 +16,61 @@ "id": "benegesserit", "name": "Bene Gesserit", "file": "skills-benegesserit.json", + "subtrees": [ + "Weirding Way", + "The Voice", + "Body Control" + ], "nodes": 22, - "edges": 23 + "edges": 29 }, { "id": "mentat", "name": "Mentat", "file": "skills-mentat.json", + "subtrees": [ + "Mental Calculus", + "Assassination", + "Tactician" + ], "nodes": 22, - "edges": 22 + "edges": 28 }, { "id": "planetologist", "name": "Planetologist", "file": "skills-planetologist.json", + "subtrees": [ + "Scientist", + "Explorer", + "Mechanic" + ], "nodes": 20, - "edges": 10 + "edges": 26 }, { "id": "swordmaster", "name": "Swordmaster", "file": "skills-swordmaster.json", + "subtrees": [ + "The Blade", + "The Will", + "The Way" + ], "nodes": 22, - "edges": 22 + "edges": 28 }, { "id": "trooper", "name": "Trooper", "file": "skills-trooper.json", + "subtrees": [ + "Gunnery", + "Suspensor Training", + "Tactical Tech" + ], "nodes": 22, - "edges": 22 + "edges": 28 } ], "icons": { diff --git a/character-builder/data/skills-benegesserit.json b/character-builder/data/skills-benegesserit.json index 819775d..57bc5bc 100644 --- a/character-builder/data/skills-benegesserit.json +++ b/character-builder/data/skills-benegesserit.json @@ -1,342 +1,388 @@ { "id": "benegesserit", "name": "Bene Gesserit", - "nodes": [ + "subtrees": [ { - "tag": "Skills.Spice.BinduDodge", - "id": "BinduDodge", - "name": "Bindu Dodge", - "kind": "Spice", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreebindudodge_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-bindudodge" + "name": "Weirding Way", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Spice.BinduDodge", + "id": "BinduDodge", + "name": "Bindu Dodge", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreebindudodge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-bindudodge" + }, + { + "tag": "Skills.Ability.BinduNerveStrike", + "id": "BinduNerveStrike", + "name": "Prana-Bindu Strikes", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilitybindunervestrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-bindunervestrike" + }, + { + "tag": "Skills.Ability.WeirdingStep", + "id": "WeirdingStep", + "name": "Weirding Step", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityweirdingstep_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-weirdingstep" + }, + { + "tag": "Skills.Attribute.WeirdingWay2", + "id": "WeirdingWay2", + "name": "Short Blade Damage", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway2" + }, + { + "tag": "Skills.Perk.Backstabber", + "id": "Backstabber", + "name": "Manipulate Instability", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbackstabber_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-backstabber" + }, + { + "tag": "Skills.Attribute.WeirdingWay1", + "id": "WeirdingWay1", + "name": "Blade Damage", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway1" + }, + { + "tag": "Skills.Ability.Hypersprint", + "id": "Hypersprint", + "name": "Bindu Sprint", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconabilitydash_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-hypersprint" + } + ], + "edges": [ + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Spice.BinduDodge" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Spice.BinduDodge" + }, + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.BinduNerveStrike", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.WeirdingStep", + "to": "Skills.Attribute.WeirdingWay1" + }, + { + "from": "Skills.Attribute.WeirdingWay2", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Attribute.WeirdingWay1", + "to": "Skills.Attribute.WeirdingWay2" + }, + { + "from": "Skills.Ability.Hypersprint", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Attribute.WeirdingWay1", + "to": "Skills.Perk.Backstabber" + }, + { + "from": "Skills.Ability.Hypersprint", + "to": "Skills.Attribute.WeirdingWay1" + } + ] }, { - "tag": "Skills.Ability.BinduNerveStrike", - "id": "BinduNerveStrike", - "name": "Prana-Bindu Strikes", - "kind": "Ability", - "row": 2, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconabilitybindunervestrike_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-bindunervestrike" + "name": "The Voice", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Spice.VoiceSplash", + "id": "VoiceSplash", + "name": "Screech", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreescreech_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-voicesplash" + }, + { + "tag": "Skills.Perk.VoiceAnalysis", + "id": "VoiceAnalysis", + "name": "Rapid Register", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreevoiceanalysis_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-voiceanalysis" + }, + { + "tag": "Skills.Ability.VoiceStop", + "id": "VoiceStop", + "name": "Stop", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityvoicestop_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-voicestop" + }, + { + "tag": "Skills.Ability.Blindspot", + "id": "Blindspot", + "name": "Ignore", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilityblindspot_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-blindspot" + }, + { + "tag": "Skills.Attribute.Manipulation1", + "id": "Manipulation1", + "name": "Voice Training", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreebenegesseritcooldown_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-manipulation1" + }, + { + "tag": "Skills.Ability.VoiceCompel", + "id": "VoiceCompel", + "name": "Compel", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitythevoicecompel_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-voicecompel" + } + ], + "edges": [ + { + "from": "Skills.Perk.VoiceAnalysis", + "to": "Skills.Spice.VoiceSplash" + }, + { + "from": "Skills.Ability.VoiceStop", + "to": "Skills.Spice.VoiceSplash" + }, + { + "from": "Skills.Ability.Blindspot", + "to": "Skills.Perk.VoiceAnalysis" + }, + { + "from": "Skills.Ability.VoiceStop", + "to": "Skills.Attribute.Manipulation1" + }, + { + "from": "Skills.Ability.Blindspot", + "to": "Skills.Ability.VoiceCompel" + }, + { + "from": "Skills.Ability.VoiceCompel", + "to": "Skills.Attribute.Manipulation1" + } + ] }, { - "tag": "Skills.Ability.WeirdingStep", - "id": "WeirdingStep", - "name": "Weirding Step", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilityweirdingstep_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-weirdingstep" - }, - { - "tag": "Skills.Attribute.WeirdingWay2", - "id": "WeirdingWay2", - "name": "Short Blade Damage", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillbrawler_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway2" - }, - { - "tag": "Skills.Perk.Backstabber", - "id": "Backstabber", - "name": "Manipulate Instability", - "kind": "Perk", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkbackstabber_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-backstabber" - }, - { - "tag": "Skills.Attribute.WeirdingWay1", - "id": "WeirdingWay1", - "name": "Blade Damage", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillbrawler_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weirdingway1" - }, - { - "tag": "Skills.Ability.Hypersprint", - "id": "Hypersprint", - "name": "Bindu Sprint", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconabilitydash_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-hypersprint" - }, - { - "tag": "Skills.Spice.VoiceSplash", - "id": "VoiceSplash", - "name": "Screech", - "kind": "Spice", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreescreech_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-voicesplash" - }, - { - "tag": "Skills.Perk.VoiceAnalysis", - "id": "VoiceAnalysis", - "name": "Rapid Register", - "kind": "Perk", - "row": 2, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconskilltreevoiceanalysis_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-voiceanalysis" - }, - { - "tag": "Skills.Ability.VoiceStop", - "id": "VoiceStop", - "name": "Stop", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilityvoicestop_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-voicestop" - }, - { - "tag": "Skills.Ability.Blindspot", - "id": "Blindspot", - "name": "Ignore", - "kind": "Ability", - "row": 4, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconabilityblindspot_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-blindspot" - }, - { - "tag": "Skills.Attribute.Manipulation1", - "id": "Manipulation1", - "name": "Voice Training", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreebenegesseritcooldown_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-manipulation1" - }, - { - "tag": "Skills.Ability.VoiceCompel", - "id": "VoiceCompel", - "name": "Compel", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconabilitythevoicecompel_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-voicecompel" - }, - { - "tag": "Skills.Ability.LitanyAgainstFear", - "id": "LitanyAgainstFear", - "name": "Litany Against Fear", - "kind": "Ability", - "row": 1, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconabilitylitanyagainstfear_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-litanyagainstfear" - }, - { - "tag": "Skills.Perk.BinduStability", - "id": "BinduStability", - "name": "Prana-Bindu Stability", - "kind": "Perk", - "row": 2, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreebindustability_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-bindustability" - }, - { - "tag": "Skills.Perk.MetabolizePoison", - "id": "MetabolizePoison", - "name": "Metabolize Poison", - "kind": "Perk", - "row": 2, - "col": 4, - "maxPoints": 1, - "icon": "t_ui_iconskilltreemetabolizeposion_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-metabolizepoison" - }, - { - "tag": "Skills.Attribute.SelfControl3", - "id": "SelfControl3", - "name": "Vitality", - "kind": "Attribute", - "row": 3, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributemaxhpbonus_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol3" - }, - { - "tag": "Skills.Attribute.SelfControl4", - "id": "SelfControl4", - "name": "Self-Healing", - "kind": "Attribute", - "row": 3, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol4" - }, - { - "tag": "Skills.Attribute.SelfControl5", - "id": "SelfControl5", - "name": "Poison Tolerance", - "kind": "Attribute", - "row": 3, - "col": 5, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributepoisondefense_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol5" - }, - { - "tag": "Skills.Perk.RegenCap", - "id": "RegenCap", - "name": "Trauma Recovery", - "kind": "Perk", - "row": 4, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkhealingfactor_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-regencap" - }, - { - "tag": "Skills.Attribute.SelfControl2", - "id": "SelfControl2", - "name": "Sun Tolerance", - "kind": "Attribute", - "row": 4, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributesundefense_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol2" - }, - { - "tag": "Skills.Attribute.SelfControl1", - "id": "SelfControl1", - "name": "Recovery", - "kind": "Attribute", - "row": 5, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol1" - } - ], - "edges": [ - { - "from": "Skills.Ability.BinduNerveStrike", - "to": "Skills.Spice.BinduDodge" - }, - { - "from": "Skills.Ability.WeirdingStep", - "to": "Skills.Spice.BinduDodge" - }, - { - "from": "Skills.Ability.BinduNerveStrike", - "to": "Skills.Attribute.WeirdingWay2" - }, - { - "from": "Skills.Ability.BinduNerveStrike", - "to": "Skills.Perk.Backstabber" - }, - { - "from": "Skills.Ability.WeirdingStep", - "to": "Skills.Attribute.WeirdingWay2" - }, - { - "from": "Skills.Ability.WeirdingStep", - "to": "Skills.Attribute.WeirdingWay1" - }, - { - "from": "Skills.Attribute.WeirdingWay2", - "to": "Skills.Perk.Backstabber" - }, - { - "from": "Skills.Attribute.WeirdingWay1", - "to": "Skills.Attribute.WeirdingWay2" - }, - { - "from": "Skills.Ability.Hypersprint", - "to": "Skills.Perk.Backstabber" - }, - { - "from": "Skills.Attribute.WeirdingWay1", - "to": "Skills.Perk.Backstabber" - }, - { - "from": "Skills.Ability.Hypersprint", - "to": "Skills.Attribute.WeirdingWay1" - }, - { - "from": "Skills.Ability.LitanyAgainstFear", - "to": "Skills.Perk.BinduStability" - }, - { - "from": "Skills.Ability.LitanyAgainstFear", - "to": "Skills.Perk.MetabolizePoison" - }, - { - "from": "Skills.Attribute.SelfControl3", - "to": "Skills.Perk.BinduStability" - }, - { - "from": "Skills.Attribute.SelfControl4", - "to": "Skills.Perk.BinduStability" - }, - { - "from": "Skills.Attribute.SelfControl4", - "to": "Skills.Perk.MetabolizePoison" - }, - { - "from": "Skills.Attribute.SelfControl5", - "to": "Skills.Perk.MetabolizePoison" - }, - { - "from": "Skills.Attribute.SelfControl3", - "to": "Skills.Perk.RegenCap" - }, - { - "from": "Skills.Attribute.SelfControl4", - "to": "Skills.Perk.RegenCap" - }, - { - "from": "Skills.Attribute.SelfControl2", - "to": "Skills.Attribute.SelfControl4" - }, - { - "from": "Skills.Attribute.SelfControl2", - "to": "Skills.Attribute.SelfControl5" - }, - { - "from": "Skills.Attribute.SelfControl1", - "to": "Skills.Perk.RegenCap" - }, - { - "from": "Skills.Attribute.SelfControl1", - "to": "Skills.Attribute.SelfControl2" + "name": "Body Control", + "cols": 5, + "nodes": [ + { + "tag": "Skills.Ability.LitanyAgainstFear", + "id": "LitanyAgainstFear", + "name": "Litany Against Fear", + "kind": "Ability", + "row": 1, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilitylitanyagainstfear_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-litanyagainstfear" + }, + { + "tag": "Skills.Perk.BinduStability", + "id": "BinduStability", + "name": "Prana-Bindu Stability", + "kind": "Perk", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreebindustability_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-bindustability" + }, + { + "tag": "Skills.Perk.MetabolizePoison", + "id": "MetabolizePoison", + "name": "Metabolize Poison", + "kind": "Perk", + "row": 2, + "col": 4, + "maxPoints": 1, + "icon": "t_ui_iconskilltreemetabolizeposion_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-metabolizepoison" + }, + { + "tag": "Skills.Attribute.SelfControl3", + "id": "SelfControl3", + "name": "Vitality", + "kind": "Attribute", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemaxhpbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol3" + }, + { + "tag": "Skills.Attribute.SelfControl4", + "id": "SelfControl4", + "name": "Self-Healing", + "kind": "Attribute", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol4" + }, + { + "tag": "Skills.Attribute.SelfControl5", + "id": "SelfControl5", + "name": "Poison Tolerance", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributepoisondefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol5" + }, + { + "tag": "Skills.Perk.RegenCap", + "id": "RegenCap", + "name": "Trauma Recovery", + "kind": "Perk", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkhealingfactor_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-regencap" + }, + { + "tag": "Skills.Attribute.SelfControl2", + "id": "SelfControl2", + "name": "Sun Tolerance", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributesundefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol2" + }, + { + "tag": "Skills.Attribute.SelfControl1", + "id": "SelfControl1", + "name": "Recovery", + "kind": "Attribute", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-selfcontrol1" + } + ], + "edges": [ + { + "from": "Skills.Ability.LitanyAgainstFear", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Ability.LitanyAgainstFear", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl3", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.BinduStability" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl5", + "to": "Skills.Perk.MetabolizePoison" + }, + { + "from": "Skills.Attribute.SelfControl3", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl4", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl2", + "to": "Skills.Attribute.SelfControl4" + }, + { + "from": "Skills.Attribute.SelfControl2", + "to": "Skills.Attribute.SelfControl5" + }, + { + "from": "Skills.Attribute.SelfControl1", + "to": "Skills.Perk.RegenCap" + }, + { + "from": "Skills.Attribute.SelfControl1", + "to": "Skills.Attribute.SelfControl2" + } + ] } ] } \ No newline at end of file diff --git a/character-builder/data/skills-mentat.json b/character-builder/data/skills-mentat.json index 74e1c26..32226e7 100644 --- a/character-builder/data/skills-mentat.json +++ b/character-builder/data/skills-mentat.json @@ -1,338 +1,384 @@ { "id": "mentat", "name": "Mentat", - "nodes": [ + "subtrees": [ { - "tag": "Skills.Perk.ShieldWeakpoint", - "id": "ShieldWeakpoint", - "name": "Shield Overcharge", - "kind": "Perk", - "row": 1, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeshieldovercharge_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-shieldweakpoint" + "name": "Mental Calculus", + "cols": 5, + "nodes": [ + { + "tag": "Skills.Perk.ShieldWeakpoint", + "id": "ShieldWeakpoint", + "name": "Shield Overcharge", + "kind": "Perk", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeshieldovercharge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-shieldweakpoint" + }, + { + "tag": "Skills.Perk.ExploitWeakness", + "id": "ExploitWeakness", + "name": "Exploit Weakness", + "kind": "Perk", + "row": 2, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreespiceeffectexploitweakness_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-exploitweakness" + }, + { + "tag": "Skills.Attribute.MentalCalculus5", + "id": "MentalCalculus5", + "name": "Rifle Damage", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusscattergun_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus5" + }, + { + "tag": "Skills.Attribute.MentalCalculus3", + "id": "MentalCalculus3", + "name": "Tailoring", + "kind": "Attribute", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus3" + }, + { + "tag": "Skills.Perk.HeadShots", + "id": "HeadShots", + "name": "Marksman", + "kind": "Perk", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkmarksman_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-headshots" + }, + { + "tag": "Skills.Attribute.MentalCalculus4", + "id": "MentalCalculus4", + "name": "Pistol Damage", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusgun_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus4" + }, + { + "tag": "Skills.Attribute.MentalCalculus1", + "id": "MentalCalculus1", + "name": "Garment Keeper", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepair_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus1" + }, + { + "tag": "Skills.Attribute.MentalCalculus2", + "id": "MentalCalculus2", + "name": "Ranged Damage", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus2" + }, + { + "tag": "Skills.Ability.TurretSeeker", + "id": "TurretSeeker", + "name": "The Sentinel", + "kind": "Ability", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilityturretseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-turretseeker" + } + ], + "edges": [ + { + "from": "Skills.Perk.ExploitWeakness", + "to": "Skills.Perk.ShieldWeakpoint" + }, + { + "from": "Skills.Attribute.MentalCalculus5", + "to": "Skills.Perk.ShieldWeakpoint" + }, + { + "from": "Skills.Attribute.MentalCalculus3", + "to": "Skills.Perk.ExploitWeakness" + }, + { + "from": "Skills.Perk.ExploitWeakness", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus5", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus4", + "to": "Skills.Attribute.MentalCalculus5" + }, + { + "from": "Skills.Attribute.MentalCalculus1", + "to": "Skills.Attribute.MentalCalculus3" + }, + { + "from": "Skills.Attribute.MentalCalculus1", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus2", + "to": "Skills.Perk.HeadShots" + }, + { + "from": "Skills.Attribute.MentalCalculus2", + "to": "Skills.Attribute.MentalCalculus4" + }, + { + "from": "Skills.Ability.TurretSeeker", + "to": "Skills.Attribute.MentalCalculus1" + }, + { + "from": "Skills.Ability.TurretSeeker", + "to": "Skills.Attribute.MentalCalculus2" + } + ] }, { - "tag": "Skills.Perk.ExploitWeakness", - "id": "ExploitWeakness", - "name": "Exploit Weakness", - "kind": "Perk", - "row": 2, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreespiceeffectexploitweakness_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-exploitweakness" + "name": "Assassination", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Ability.HunterSeeker", + "id": "HunterSeeker", + "name": "Hunter-Seeker", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_icongadgethunterseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-hunterseeker" + }, + { + "tag": "Skills.Perk.PoisonTooth", + "id": "PoisonTooth", + "name": "Poison Tooth", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreepoisontooth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-poisontooth" + }, + { + "tag": "Skills.Ability.StunDart", + "id": "StunDart", + "name": "Stunner", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitystunnerdart_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-stundart" + }, + { + "tag": "Skills.Attribute.Assassination2", + "id": "Assassination2", + "name": "Assassin's Shot", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-assassination2" + }, + { + "tag": "Skills.Ability.PoisonMine", + "id": "PoisonMine", + "name": "Poison Mine", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilitypoisonmine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-poisonmine" + }, + { + "tag": "Skills.Attribute.Assassination1", + "id": "Assassination1", + "name": "Headshot Damage", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributeheadshotbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-assassination1" + }, + { + "tag": "Skills.Ability.PoisonCapsuleLauncher", + "id": "PoisonCapsuleLauncher", + "name": "Poison Capsule", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_icongadgetpoisoncapsulelauncher_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-poisoncapsulelauncher" + } + ], + "edges": [ + { + "from": "Skills.Ability.HunterSeeker", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.HunterSeeker", + "to": "Skills.Ability.StunDart" + }, + { + "from": "Skills.Attribute.Assassination2", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.PoisonMine", + "to": "Skills.Perk.PoisonTooth" + }, + { + "from": "Skills.Ability.StunDart", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Ability.StunDart", + "to": "Skills.Attribute.Assassination1" + }, + { + "from": "Skills.Ability.PoisonMine", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Attribute.Assassination1", + "to": "Skills.Attribute.Assassination2" + }, + { + "from": "Skills.Ability.PoisonCapsuleLauncher", + "to": "Skills.Ability.PoisonMine" + }, + { + "from": "Skills.Ability.PoisonCapsuleLauncher", + "to": "Skills.Attribute.Assassination1" + } + ] }, { - "tag": "Skills.Attribute.MentalCalculus5", - "id": "MentalCalculus5", - "name": "Rifle Damage", - "kind": "Attribute", - "row": 2, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagebonusscattergun_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus5" - }, - { - "tag": "Skills.Attribute.MentalCalculus3", - "id": "MentalCalculus3", - "name": "Tailoring", - "kind": "Attribute", - "row": 3, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus3" - }, - { - "tag": "Skills.Perk.HeadShots", - "id": "HeadShots", - "name": "Marksman", - "kind": "Perk", - "row": 3, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkmarksman_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-headshots" - }, - { - "tag": "Skills.Attribute.MentalCalculus4", - "id": "MentalCalculus4", - "name": "Pistol Damage", - "kind": "Attribute", - "row": 3, - "col": 5, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagebonusgun_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus4" - }, - { - "tag": "Skills.Attribute.MentalCalculus1", - "id": "MentalCalculus1", - "name": "Garment Keeper", - "kind": "Attribute", - "row": 4, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributerepair_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus1" - }, - { - "tag": "Skills.Attribute.MentalCalculus2", - "id": "MentalCalculus2", - "name": "Ranged Damage", - "kind": "Attribute", - "row": 4, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamage_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-mentalcalculus2" - }, - { - "tag": "Skills.Ability.TurretSeeker", - "id": "TurretSeeker", - "name": "The Sentinel", - "kind": "Ability", - "row": 5, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconabilityturretseeker_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-turretseeker" - }, - { - "tag": "Skills.Ability.HunterSeeker", - "id": "HunterSeeker", - "name": "Hunter-Seeker", - "kind": "Ability", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_icongadgethunterseeker_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-hunterseeker" - }, - { - "tag": "Skills.Perk.PoisonTooth", - "id": "PoisonTooth", - "name": "Poison Tooth", - "kind": "Perk", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreepoisontooth_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-poisontooth" - }, - { - "tag": "Skills.Ability.StunDart", - "id": "StunDart", - "name": "Stunner", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilitystunnerdart_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-stundart" - }, - { - "tag": "Skills.Attribute.Assassination2", - "id": "Assassination2", - "name": "Assassin's Shot", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamage_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-assassination2" - }, - { - "tag": "Skills.Ability.PoisonMine", - "id": "PoisonMine", - "name": "Poison Mine", - "kind": "Ability", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconabilitypoisonmine_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-poisonmine" - }, - { - "tag": "Skills.Attribute.Assassination1", - "id": "Assassination1", - "name": "Headshot Damage", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributeheadshotbonus_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-assassination1" - }, - { - "tag": "Skills.Ability.PoisonCapsuleLauncher", - "id": "PoisonCapsuleLauncher", - "name": "Poison Capsule", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_icongadgetpoisoncapsulelauncher_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-poisoncapsulelauncher" - }, - { - "tag": "Skills.Ability.PortableGenerator", - "id": "PortableGenerator", - "name": "Source of Power", - "kind": "Ability", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconabilityportablegenerator_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-portablegenerator" - }, - { - "tag": "Skills.Ability.SuspensorMine_Reduction", - "id": "SuspensorMine_Reduction", - "name": "Anti-gravity Mine", - "kind": "Ability", - "row": 2, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_icongadgetreductionremotemine_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_reduction" - }, - { - "tag": "Skills.Perk.IronWill", - "id": "IronWill", - "name": "Iron Will", - "kind": "Perk", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeironwill_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-ironwill" - }, - { - "tag": "Skills.Ability.SuspensorMine_Amplification", - "id": "SuspensorMine_Amplification", - "name": "Gravity Mine", - "kind": "Ability", - "row": 4, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_icongadgetamplificationremotemine_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_amplification" - }, - { - "tag": "Skills.Ability.SolidoDecoy", - "id": "SolidoDecoy", - "name": "Solido Decoy", - "kind": "Ability", - "row": 4, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilitysolidodecoy_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-solidodecoy" - }, - { - "tag": "Skills.Ability.SuspensorWall", - "id": "SuspensorWall", - "name": "Shield Wall", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconabilitysuspensorwall_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensorwall" - } - ], - "edges": [ - { - "from": "Skills.Perk.ExploitWeakness", - "to": "Skills.Perk.ShieldWeakpoint" - }, - { - "from": "Skills.Attribute.MentalCalculus5", - "to": "Skills.Perk.ShieldWeakpoint" - }, - { - "from": "Skills.Attribute.MentalCalculus3", - "to": "Skills.Perk.ExploitWeakness" - }, - { - "from": "Skills.Perk.ExploitWeakness", - "to": "Skills.Perk.HeadShots" - }, - { - "from": "Skills.Attribute.MentalCalculus5", - "to": "Skills.Perk.HeadShots" - }, - { - "from": "Skills.Attribute.MentalCalculus4", - "to": "Skills.Attribute.MentalCalculus5" - }, - { - "from": "Skills.Attribute.MentalCalculus1", - "to": "Skills.Attribute.MentalCalculus3" - }, - { - "from": "Skills.Attribute.MentalCalculus1", - "to": "Skills.Perk.HeadShots" - }, - { - "from": "Skills.Attribute.MentalCalculus2", - "to": "Skills.Perk.HeadShots" - }, - { - "from": "Skills.Attribute.MentalCalculus2", - "to": "Skills.Attribute.MentalCalculus4" - }, - { - "from": "Skills.Ability.TurretSeeker", - "to": "Skills.Attribute.MentalCalculus1" - }, - { - "from": "Skills.Ability.TurretSeeker", - "to": "Skills.Attribute.MentalCalculus2" - }, - { - "from": "Skills.Ability.HunterSeeker", - "to": "Skills.Perk.PoisonTooth" - }, - { - "from": "Skills.Ability.HunterSeeker", - "to": "Skills.Ability.StunDart" - }, - { - "from": "Skills.Attribute.Assassination2", - "to": "Skills.Perk.PoisonTooth" - }, - { - "from": "Skills.Ability.PoisonMine", - "to": "Skills.Perk.PoisonTooth" - }, - { - "from": "Skills.Ability.StunDart", - "to": "Skills.Attribute.Assassination2" - }, - { - "from": "Skills.Ability.StunDart", - "to": "Skills.Attribute.Assassination1" - }, - { - "from": "Skills.Ability.PoisonMine", - "to": "Skills.Attribute.Assassination2" - }, - { - "from": "Skills.Attribute.Assassination1", - "to": "Skills.Attribute.Assassination2" - }, - { - "from": "Skills.Ability.PoisonCapsuleLauncher", - "to": "Skills.Ability.PoisonMine" - }, - { - "from": "Skills.Ability.PoisonCapsuleLauncher", - "to": "Skills.Attribute.Assassination1" + "name": "Tactician", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Ability.PortableGenerator", + "id": "PortableGenerator", + "name": "Source of Power", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilityportablegenerator_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-portablegenerator" + }, + { + "tag": "Skills.Ability.SuspensorMine_Reduction", + "id": "SuspensorMine_Reduction", + "name": "Anti-gravity Mine", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_icongadgetreductionremotemine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_reduction" + }, + { + "tag": "Skills.Perk.IronWill", + "id": "IronWill", + "name": "Iron Will", + "kind": "Perk", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeironwill_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-ironwill" + }, + { + "tag": "Skills.Ability.SuspensorMine_Amplification", + "id": "SuspensorMine_Amplification", + "name": "Gravity Mine", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_icongadgetamplificationremotemine_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensormine_amplification" + }, + { + "tag": "Skills.Ability.SolidoDecoy", + "id": "SolidoDecoy", + "name": "Solido Decoy", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitysolidodecoy_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-solidodecoy" + }, + { + "tag": "Skills.Ability.SuspensorWall", + "id": "SuspensorWall", + "name": "Shield Wall", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconabilitysuspensorwall_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorwall" + } + ], + "edges": [ + { + "from": "Skills.Ability.PortableGenerator", + "to": "Skills.Ability.SuspensorMine_Reduction" + }, + { + "from": "Skills.Ability.PortableGenerator", + "to": "Skills.Perk.IronWill" + }, + { + "from": "Skills.Ability.SuspensorMine_Amplification", + "to": "Skills.Ability.SuspensorMine_Reduction" + }, + { + "from": "Skills.Ability.SolidoDecoy", + "to": "Skills.Perk.IronWill" + }, + { + "from": "Skills.Ability.SuspensorMine_Amplification", + "to": "Skills.Ability.SuspensorWall" + }, + { + "from": "Skills.Ability.SolidoDecoy", + "to": "Skills.Ability.SuspensorWall" + } + ] } ] } \ No newline at end of file diff --git a/character-builder/data/skills-planetologist.json b/character-builder/data/skills-planetologist.json index 7ea223a..f58b356 100644 --- a/character-builder/data/skills-planetologist.json +++ b/character-builder/data/skills-planetologist.json @@ -1,268 +1,354 @@ { "id": "planetologist", "name": "Planetologist", - "nodes": [ + "subtrees": [ { - "tag": "Skills.Perk.BatteryExpert", - "id": "BatteryExpert", - "name": "Conservation of Energy", - "kind": "Perk", - "row": 1, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkbatteryexpert_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-batteryexpert" + "name": "Scientist", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Perk.BatteryExpert", + "id": "BatteryExpert", + "name": "Conservation of Energy", + "kind": "Perk", + "row": 1, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbatteryexpert_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-batteryexpert" + }, + { + "tag": "Skills.Attribute.Scientist5", + "id": "Scientist5", + "name": "Compaction", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist5" + }, + { + "tag": "Skills.Science.m_PowerMax", + "id": "m_PowerMax", + "name": "Overcharge", + "kind": "Science", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-science-m_powermax" + }, + { + "tag": "Skills.Attribute.Scientist4", + "id": "Scientist4", + "name": "Deep Analysis", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist4" + }, + { + "tag": "Skills.Attribute.Scientist2", + "id": "Scientist2", + "name": "Dew Gathering", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatheryield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist2" + }, + { + "tag": "Skills.Attribute.Scientist3", + "id": "Scientist3", + "name": "Rerouting", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist3" + }, + { + "tag": "Skills.Attribute.Scientist1", + "id": "Scientist1", + "name": "Cutteray Mining", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-scientist1" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Scientist5", + "to": "Skills.Perk.BatteryExpert" + }, + { + "from": "Skills.Perk.BatteryExpert", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist4", + "to": "Skills.Attribute.Scientist5" + }, + { + "from": "Skills.Attribute.Scientist2", + "to": "Skills.Attribute.Scientist5" + }, + { + "from": "Skills.Attribute.Scientist4", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist3", + "to": "Skills.Science.m_PowerMax" + }, + { + "from": "Skills.Attribute.Scientist2", + "to": "Skills.Attribute.Scientist4" + }, + { + "from": "Skills.Attribute.Scientist3", + "to": "Skills.Attribute.Scientist4" + }, + { + "from": "Skills.Attribute.Scientist1", + "to": "Skills.Attribute.Scientist2" + }, + { + "from": "Skills.Attribute.Scientist1", + "to": "Skills.Attribute.Scientist3" + } + ] }, { - "tag": "Skills.Attribute.Scientist5", - "id": "Scientist5", - "name": "Compaction", - "kind": "Attribute", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-scientist5" + "name": "Explorer", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Attribute.Explorer5", + "id": "Explorer5", + "name": "Spice Surveyor", + "kind": "Attribute", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeattributespice_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer5" + }, + { + "tag": "Skills.Attribute.Explorer3", + "id": "Explorer3", + "name": "Scanner Mastery", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer3" + }, + { + "tag": "Skills.Attribute.Explorer4", + "id": "Explorer4", + "name": "Stillsuit Seals", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhydration_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer4" + }, + { + "tag": "Skills.Attribute.Explorer1", + "id": "Explorer1", + "name": "Cartographer", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeskillobservation_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer1" + }, + { + "tag": "Skills.Attribute.Explorer2", + "id": "Explorer2", + "name": "Mountaineer", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillclimber_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-explorer2" + }, + { + "tag": "Skills.Ability.SuspensorPad", + "id": "SuspensorPad", + "name": "Suspensor Pad", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitysuspensorpad_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorpad" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Explorer3", + "to": "Skills.Attribute.Explorer5" + }, + { + "from": "Skills.Attribute.Explorer4", + "to": "Skills.Attribute.Explorer5" + }, + { + "from": "Skills.Attribute.Explorer1", + "to": "Skills.Attribute.Explorer3" + }, + { + "from": "Skills.Attribute.Explorer2", + "to": "Skills.Attribute.Explorer4" + }, + { + "from": "Skills.Ability.SuspensorPad", + "to": "Skills.Attribute.Explorer1" + }, + { + "from": "Skills.Ability.SuspensorPad", + "to": "Skills.Attribute.Explorer2" + } + ] }, { - "tag": "Skills.Science.m_PowerMax", - "id": "m_PowerMax", - "name": "Overcharge", - "kind": "Science", - "row": 2, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-science-m_powermax" - }, - { - "tag": "Skills.Attribute.Scientist4", - "id": "Scientist4", - "name": "Deep Analysis", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-scientist4" - }, - { - "tag": "Skills.Attribute.Scientist2", - "id": "Scientist2", - "name": "Dew Gathering", - "kind": "Attribute", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributewatheryield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-scientist2" - }, - { - "tag": "Skills.Attribute.Scientist3", - "id": "Scientist3", - "name": "Rerouting", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-scientist3" - }, - { - "tag": "Skills.Attribute.Scientist1", - "id": "Scientist1", - "name": "Cutteray Mining", - "kind": "Attribute", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-scientist1" - }, - { - "tag": "Skills.Attribute.Explorer5", - "id": "Explorer5", - "name": "Spice Surveyor", - "kind": "Attribute", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeattributespice_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-explorer5" - }, - { - "tag": "Skills.Attribute.Explorer3", - "id": "Explorer3", - "name": "Scanner Mastery", - "kind": "Attribute", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-explorer3" - }, - { - "tag": "Skills.Attribute.Explorer4", - "id": "Explorer4", - "name": "Stillsuit Seals", - "kind": "Attribute", - "row": 2, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillhydration_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-explorer4" - }, - { - "tag": "Skills.Attribute.Explorer1", - "id": "Explorer1", - "name": "Cartographer", - "kind": "Attribute", - "row": 4, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeskillobservation_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-explorer1" - }, - { - "tag": "Skills.Attribute.Explorer2", - "id": "Explorer2", - "name": "Mountaineer", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillclimber_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-explorer2" - }, - { - "tag": "Skills.Ability.SuspensorPad", - "id": "SuspensorPad", - "name": "Suspensor Pad", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconabilitysuspensorpad_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensorpad" - }, - { - "tag": "Skills.Spice.VehicleHeat", - "id": "VehicleHeat", - "name": "Heat Management", - "kind": "Spice", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeheatmanagement_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-vehicleheat" - }, - { - "tag": "Skills.Attribute.Driver5", - "id": "Driver5", - "name": "Fuel Efficient Pilot", - "kind": "Attribute", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributevehicle_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver5" - }, - { - "tag": "Skills.Attribute.Driver6", - "id": "Driver6", - "name": "Sandcrawler Yield", - "kind": "Attribute", - "row": 2, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver6" - }, - { - "tag": "Skills.Attribute.Driver4", - "id": "Driver4", - "name": "Vehicle Scanning", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver4" - }, - { - "tag": "Skills.Attribute.Driver2", - "id": "Driver2", - "name": "Fuel Efficient Driver", - "kind": "Attribute", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributevehicle_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver2" - }, - { - "tag": "Skills.Attribute.Driver3", - "id": "Driver3", - "name": "Vehicle Mining", - "kind": "Attribute", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver3" - }, - { - "tag": "Skills.Attribute.Driver1", - "id": "Driver1", - "name": "Vehicle Repair", - "kind": "Attribute", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-driver1" - } - ], - "edges": [ - { - "from": "Skills.Attribute.Scientist5", - "to": "Skills.Perk.BatteryExpert" - }, - { - "from": "Skills.Perk.BatteryExpert", - "to": "Skills.Science.m_PowerMax" - }, - { - "from": "Skills.Attribute.Scientist4", - "to": "Skills.Attribute.Scientist5" - }, - { - "from": "Skills.Attribute.Scientist2", - "to": "Skills.Attribute.Scientist5" - }, - { - "from": "Skills.Attribute.Scientist4", - "to": "Skills.Science.m_PowerMax" - }, - { - "from": "Skills.Attribute.Scientist3", - "to": "Skills.Science.m_PowerMax" - }, - { - "from": "Skills.Attribute.Scientist2", - "to": "Skills.Attribute.Scientist4" - }, - { - "from": "Skills.Attribute.Scientist3", - "to": "Skills.Attribute.Scientist4" - }, - { - "from": "Skills.Attribute.Scientist1", - "to": "Skills.Attribute.Scientist2" - }, - { - "from": "Skills.Attribute.Scientist1", - "to": "Skills.Attribute.Scientist3" + "name": "Mechanic", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Spice.VehicleHeat", + "id": "VehicleHeat", + "name": "Heat Management", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeheatmanagement_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-vehicleheat" + }, + { + "tag": "Skills.Attribute.Driver5", + "id": "Driver5", + "name": "Fuel Efficient Pilot", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributevehicle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver5" + }, + { + "tag": "Skills.Attribute.Driver6", + "id": "Driver6", + "name": "Sandcrawler Yield", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributespiceyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver6" + }, + { + "tag": "Skills.Attribute.Driver4", + "id": "Driver4", + "name": "Vehicle Scanning", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributescanningbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver4" + }, + { + "tag": "Skills.Attribute.Driver2", + "id": "Driver2", + "name": "Fuel Efficient Driver", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributevehicle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver2" + }, + { + "tag": "Skills.Attribute.Driver3", + "id": "Driver3", + "name": "Vehicle Mining", + "kind": "Attribute", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributemineralyield_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver3" + }, + { + "tag": "Skills.Attribute.Driver1", + "id": "Driver1", + "name": "Vehicle Repair", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-driver1" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Driver5", + "to": "Skills.Spice.VehicleHeat" + }, + { + "from": "Skills.Attribute.Driver6", + "to": "Skills.Spice.VehicleHeat" + }, + { + "from": "Skills.Attribute.Driver4", + "to": "Skills.Attribute.Driver5" + }, + { + "from": "Skills.Attribute.Driver2", + "to": "Skills.Attribute.Driver5" + }, + { + "from": "Skills.Attribute.Driver4", + "to": "Skills.Attribute.Driver6" + }, + { + "from": "Skills.Attribute.Driver3", + "to": "Skills.Attribute.Driver6" + }, + { + "from": "Skills.Attribute.Driver2", + "to": "Skills.Attribute.Driver4" + }, + { + "from": "Skills.Attribute.Driver3", + "to": "Skills.Attribute.Driver4" + }, + { + "from": "Skills.Attribute.Driver1", + "to": "Skills.Attribute.Driver2" + }, + { + "from": "Skills.Attribute.Driver1", + "to": "Skills.Attribute.Driver3" + } + ] } ] } \ No newline at end of file diff --git a/character-builder/data/skills-swordmaster.json b/character-builder/data/skills-swordmaster.json index 81def91..c3f1aee 100644 --- a/character-builder/data/skills-swordmaster.json +++ b/character-builder/data/skills-swordmaster.json @@ -1,338 +1,384 @@ { "id": "swordmaster", "name": "Swordmaster", - "nodes": [ + "subtrees": [ { - "tag": "Skills.Spice.ParryBoost", - "id": "ParryBoost", - "name": "Precise Parry", - "kind": "Spice", - "row": 1, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreepreciseparry_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-parryboost" + "name": "The Blade", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Spice.ParryBoost", + "id": "ParryBoost", + "name": "Precise Parry", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreepreciseparry_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-parryboost" + }, + { + "tag": "Skills.Ability.Whirlwind", + "id": "Whirlwind", + "name": "Eye of the Storm", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilitywhirlwind_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-whirlwind" + }, + { + "tag": "Skills.Ability.RiposteBreak", + "id": "RiposteBreak", + "name": "Foil", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitybreakingreposte_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-ripostebreak" + }, + { + "tag": "Skills.Attribute.Blade2", + "id": "Blade2", + "name": "Long Blade Damage", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-blade2" + }, + { + "tag": "Skills.Perk.MeleeChain", + "id": "MeleeChain", + "name": "Dance of Blades", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkbladechaining_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-meleechain" + }, + { + "tag": "Skills.Ability.RiposteInjure", + "id": "RiposteInjure", + "name": "Retaliate", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityinjuringreposte_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-riposteinjure" + }, + { + "tag": "Skills.Attribute.Blade1", + "id": "Blade1", + "name": "Blade Damage", + "kind": "Attribute", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillbrawler_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-blade1" + } + ], + "edges": [ + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Spice.ParryBoost" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Spice.ParryBoost" + }, + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Ability.Whirlwind", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Ability.RiposteBreak", + "to": "Skills.Ability.RiposteInjure" + }, + { + "from": "Skills.Attribute.Blade2", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteInjure", + "to": "Skills.Attribute.Blade2" + }, + { + "from": "Skills.Attribute.Blade1", + "to": "Skills.Perk.MeleeChain" + }, + { + "from": "Skills.Ability.RiposteInjure", + "to": "Skills.Attribute.Blade1" + } + ] }, { - "tag": "Skills.Ability.Whirlwind", - "id": "Whirlwind", - "name": "Eye of the Storm", - "kind": "Ability", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconabilitywhirlwind_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-whirlwind" + "name": "The Will", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Perk.ThriveOnDanger", + "id": "ThriveOnDanger", + "name": "Thrive on Danger", + "kind": "Perk", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreethriveondanger_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-thriveondanger" + }, + { + "tag": "Skills.Attribute.Resolve2", + "id": "Resolve2", + "name": "Solid Stance", + "kind": "Attribute", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributepoisedefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-resolve2" + }, + { + "tag": "Skills.Attribute.UnstoppableAttacks", + "id": "UnstoppableAttacks", + "name": "Confidence", + "kind": "Attribute", + "row": 2, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagemitigation_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-unstoppableattacks" + }, + { + "tag": "Skills.Attribute.Resolve1", + "id": "Resolve1", + "name": "Bleed Tolerance", + "kind": "Attribute", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-resolve1" + }, + { + "tag": "Skills.Perk.ToughLunge", + "id": "ToughLunge", + "name": "Reckless Lunge", + "kind": "Perk", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreetoughlunge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-toughlunge" + }, + { + "tag": "Skills.Ability.DeflectionSlow", + "id": "DeflectionSlow", + "name": "Deflection", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitydeflection_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-deflectionslow" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Resolve2", + "to": "Skills.Perk.ThriveOnDanger" + }, + { + "from": "Skills.Attribute.UnstoppableAttacks", + "to": "Skills.Perk.ThriveOnDanger" + }, + { + "from": "Skills.Attribute.Resolve1", + "to": "Skills.Attribute.Resolve2" + }, + { + "from": "Skills.Attribute.UnstoppableAttacks", + "to": "Skills.Perk.ToughLunge" + }, + { + "from": "Skills.Ability.DeflectionSlow", + "to": "Skills.Attribute.Resolve1" + }, + { + "from": "Skills.Ability.DeflectionSlow", + "to": "Skills.Perk.ToughLunge" + } + ] }, { - "tag": "Skills.Ability.RiposteBreak", - "id": "RiposteBreak", - "name": "Foil", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilitybreakingreposte_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-ripostebreak" - }, - { - "tag": "Skills.Attribute.Blade2", - "id": "Blade2", - "name": "Long Blade Damage", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillbrawler_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-blade2" - }, - { - "tag": "Skills.Perk.MeleeChain", - "id": "MeleeChain", - "name": "Dance of Blades", - "kind": "Perk", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkbladechaining_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-meleechain" - }, - { - "tag": "Skills.Ability.RiposteInjure", - "id": "RiposteInjure", - "name": "Retaliate", - "kind": "Ability", - "row": 4, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilityinjuringreposte_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-riposteinjure" - }, - { - "tag": "Skills.Attribute.Blade1", - "id": "Blade1", - "name": "Blade Damage", - "kind": "Attribute", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillbrawler_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-blade1" - }, - { - "tag": "Skills.Perk.ThriveOnDanger", - "id": "ThriveOnDanger", - "name": "Thrive on Danger", - "kind": "Perk", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreethriveondanger_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-thriveondanger" - }, - { - "tag": "Skills.Attribute.Resolve2", - "id": "Resolve2", - "name": "Solid Stance", - "kind": "Attribute", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributepoisedefense_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-resolve2" - }, - { - "tag": "Skills.Attribute.UnstoppableAttacks", - "id": "UnstoppableAttacks", - "name": "Confidence", - "kind": "Attribute", - "row": 2, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagemitigation_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-unstoppableattacks" - }, - { - "tag": "Skills.Attribute.Resolve1", - "id": "Resolve1", - "name": "Bleed Tolerance", - "kind": "Attribute", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillmaxhealth_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-resolve1" - }, - { - "tag": "Skills.Perk.ToughLunge", - "id": "ToughLunge", - "name": "Reckless Lunge", - "kind": "Perk", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreetoughlunge_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-toughlunge" - }, - { - "tag": "Skills.Ability.DeflectionSlow", - "id": "DeflectionSlow", - "name": "Deflection", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconabilitydeflection_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-deflectionslow" - }, - { - "tag": "Skills.Spice.ShadowStrike", - "id": "ShadowStrike", - "name": "Prescient Strike", - "kind": "Spice", - "row": 1, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconskilltreeprescientstrike_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-shadowstrike" - }, - { - "tag": "Skills.Attribute.Aggression3", - "id": "Aggression3", - "name": "General Conditioning", - "kind": "Attribute", - "row": 2, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributestamina_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-aggression3" - }, - { - "tag": "Skills.Attribute.Aggression4", - "id": "Aggression4", - "name": "Desert Conditioning", - "kind": "Attribute", - "row": 2, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributewatherdefense_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-aggression4" - }, - { - "tag": "Skills.Ability.CripplingStrike", - "id": "CripplingStrike", - "name": "Crippling Strike", - "kind": "Ability", - "row": 3, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconabilitycripplingstrike_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-cripplingstrike" - }, - { - "tag": "Skills.Perk.SprintStamina", - "id": "SprintStamina", - "name": "Disciplined Breathing", - "kind": "Perk", - "row": 3, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkrunner_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-sprintstamina" - }, - { - "tag": "Skills.Ability.BattleCry", - "id": "BattleCry", - "name": "Inspiration", - "kind": "Ability", - "row": 3, - "col": 5, - "maxPoints": 3, - "icon": "t_ui_iconabilitybattlecry_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-battlecry" - }, - { - "tag": "Skills.Attribute.Aggression1", - "id": "Aggression1", - "name": "Field Medicine", - "kind": "Attribute", - "row": 4, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-aggression1" - }, - { - "tag": "Skills.Attribute.Aggression2", - "id": "Aggression2", - "name": "Optimized Hydration", - "kind": "Attribute", - "row": 4, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributewatherbonus_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-aggression2" - }, - { - "tag": "Skills.Ability.KneeCharge", - "id": "KneeCharge", - "name": "Knee Charge", - "kind": "Ability", - "row": 5, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconabilitykneecharge_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-kneecharge" - } - ], - "edges": [ - { - "from": "Skills.Ability.Whirlwind", - "to": "Skills.Spice.ParryBoost" - }, - { - "from": "Skills.Ability.RiposteBreak", - "to": "Skills.Spice.ParryBoost" - }, - { - "from": "Skills.Ability.Whirlwind", - "to": "Skills.Attribute.Blade2" - }, - { - "from": "Skills.Ability.Whirlwind", - "to": "Skills.Perk.MeleeChain" - }, - { - "from": "Skills.Ability.RiposteBreak", - "to": "Skills.Attribute.Blade2" - }, - { - "from": "Skills.Ability.RiposteBreak", - "to": "Skills.Ability.RiposteInjure" - }, - { - "from": "Skills.Attribute.Blade2", - "to": "Skills.Perk.MeleeChain" - }, - { - "from": "Skills.Ability.RiposteInjure", - "to": "Skills.Attribute.Blade2" - }, - { - "from": "Skills.Attribute.Blade1", - "to": "Skills.Perk.MeleeChain" - }, - { - "from": "Skills.Ability.RiposteInjure", - "to": "Skills.Attribute.Blade1" - }, - { - "from": "Skills.Attribute.Aggression3", - "to": "Skills.Spice.ShadowStrike" - }, - { - "from": "Skills.Attribute.Aggression4", - "to": "Skills.Spice.ShadowStrike" - }, - { - "from": "Skills.Ability.CripplingStrike", - "to": "Skills.Attribute.Aggression3" - }, - { - "from": "Skills.Attribute.Aggression3", - "to": "Skills.Perk.SprintStamina" - }, - { - "from": "Skills.Attribute.Aggression4", - "to": "Skills.Perk.SprintStamina" - }, - { - "from": "Skills.Ability.BattleCry", - "to": "Skills.Attribute.Aggression4" - }, - { - "from": "Skills.Ability.CripplingStrike", - "to": "Skills.Attribute.Aggression1" - }, - { - "from": "Skills.Attribute.Aggression1", - "to": "Skills.Perk.SprintStamina" - }, - { - "from": "Skills.Attribute.Aggression2", - "to": "Skills.Perk.SprintStamina" - }, - { - "from": "Skills.Ability.BattleCry", - "to": "Skills.Attribute.Aggression2" - }, - { - "from": "Skills.Ability.KneeCharge", - "to": "Skills.Attribute.Aggression1" - }, - { - "from": "Skills.Ability.KneeCharge", - "to": "Skills.Attribute.Aggression2" + "name": "The Way", + "cols": 5, + "nodes": [ + { + "tag": "Skills.Spice.ShadowStrike", + "id": "ShadowStrike", + "name": "Prescient Strike", + "kind": "Spice", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconskilltreeprescientstrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-shadowstrike" + }, + { + "tag": "Skills.Attribute.Aggression3", + "id": "Aggression3", + "name": "General Conditioning", + "kind": "Attribute", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributestamina_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression3" + }, + { + "tag": "Skills.Attribute.Aggression4", + "id": "Aggression4", + "name": "Desert Conditioning", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatherdefense_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression4" + }, + { + "tag": "Skills.Ability.CripplingStrike", + "id": "CripplingStrike", + "name": "Crippling Strike", + "kind": "Ability", + "row": 3, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconabilitycripplingstrike_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-cripplingstrike" + }, + { + "tag": "Skills.Perk.SprintStamina", + "id": "SprintStamina", + "name": "Disciplined Breathing", + "kind": "Perk", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkrunner_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-sprintstamina" + }, + { + "tag": "Skills.Ability.BattleCry", + "id": "BattleCry", + "name": "Inspiration", + "kind": "Ability", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconabilitybattlecry_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-battlecry" + }, + { + "tag": "Skills.Attribute.Aggression1", + "id": "Aggression1", + "name": "Field Medicine", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillhealingmultiplier_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression1" + }, + { + "tag": "Skills.Attribute.Aggression2", + "id": "Aggression2", + "name": "Optimized Hydration", + "kind": "Attribute", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributewatherbonus_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-aggression2" + }, + { + "tag": "Skills.Ability.KneeCharge", + "id": "KneeCharge", + "name": "Knee Charge", + "kind": "Ability", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconabilitykneecharge_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-kneecharge" + } + ], + "edges": [ + { + "from": "Skills.Attribute.Aggression3", + "to": "Skills.Spice.ShadowStrike" + }, + { + "from": "Skills.Attribute.Aggression4", + "to": "Skills.Spice.ShadowStrike" + }, + { + "from": "Skills.Ability.CripplingStrike", + "to": "Skills.Attribute.Aggression3" + }, + { + "from": "Skills.Attribute.Aggression3", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Attribute.Aggression4", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Ability.BattleCry", + "to": "Skills.Attribute.Aggression4" + }, + { + "from": "Skills.Ability.CripplingStrike", + "to": "Skills.Attribute.Aggression1" + }, + { + "from": "Skills.Attribute.Aggression1", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Attribute.Aggression2", + "to": "Skills.Perk.SprintStamina" + }, + { + "from": "Skills.Ability.BattleCry", + "to": "Skills.Attribute.Aggression2" + }, + { + "from": "Skills.Ability.KneeCharge", + "to": "Skills.Attribute.Aggression1" + }, + { + "from": "Skills.Ability.KneeCharge", + "to": "Skills.Attribute.Aggression2" + } + ] } ] } \ No newline at end of file diff --git a/character-builder/data/skills-trooper.json b/character-builder/data/skills-trooper.json index 15f8828..6a2c3fd 100644 --- a/character-builder/data/skills-trooper.json +++ b/character-builder/data/skills-trooper.json @@ -1,338 +1,384 @@ { "id": "trooper", "name": "Trooper", - "nodes": [ + "subtrees": [ { - "tag": "Skills.Ability.EnergyCapsule", - "id": "EnergyCapsule", - "name": "Energy Capsule", - "kind": "Ability", - "row": 1, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilityenergycapsule_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-energycapsule" + "name": "Gunnery", + "cols": 5, + "nodes": [ + { + "tag": "Skills.Ability.EnergyCapsule", + "id": "EnergyCapsule", + "name": "Energy Capsule", + "kind": "Ability", + "row": 1, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilityenergycapsule_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-energycapsule" + }, + { + "tag": "Skills.Attribute.Weaponry5", + "id": "Weaponry5", + "name": "Heavy Weapon Damage", + "kind": "Attribute", + "row": 2, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusheavyweapon_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry5" + }, + { + "tag": "Skills.Attribute.Weaponry6", + "id": "Weaponry6", + "name": "Gunsmith", + "kind": "Attribute", + "row": 2, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry6" + }, + { + "tag": "Skills.Perk.HeavyWeaponNaib", + "id": "HeavyWeaponNaib", + "name": "Heavy Weapon Agility", + "kind": "Perk", + "row": 3, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkheavyweaponnaib_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-heavyweaponnaib" + }, + { + "tag": "Skills.Attribute.Weaponry3", + "id": "Weaponry3", + "name": "Scattergun Damage", + "kind": "Attribute", + "row": 3, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonusrifle_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry3" + }, + { + "tag": "Skills.Attribute.Weaponry4", + "id": "Weaponry4", + "name": "Field Maintenance", + "kind": "Attribute", + "row": 3, + "col": 5, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributerepair_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry4" + }, + { + "tag": "Skills.Attribute.Weaponry2", + "id": "Weaponry2", + "name": "Disruptor Damage", + "kind": "Attribute", + "row": 4, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamagebonussmg_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry2" + }, + { + "tag": "Skills.Perk.BodyShots", + "id": "BodyShots", + "name": "Center of Mass", + "kind": "Perk", + "row": 4, + "col": 4, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkcentralaim_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-bodyshots" + }, + { + "tag": "Skills.Attribute.Weaponry1", + "id": "Weaponry1", + "name": "Ranged Damage", + "kind": "Attribute", + "row": 5, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeattributedamage_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry1" + } + ], + "edges": [ + { + "from": "Skills.Ability.EnergyCapsule", + "to": "Skills.Attribute.Weaponry5" + }, + { + "from": "Skills.Ability.EnergyCapsule", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry5", + "to": "Skills.Perk.HeavyWeaponNaib" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Attribute.Weaponry5" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry4", + "to": "Skills.Attribute.Weaponry6" + }, + { + "from": "Skills.Attribute.Weaponry2", + "to": "Skills.Perk.HeavyWeaponNaib" + }, + { + "from": "Skills.Attribute.Weaponry2", + "to": "Skills.Attribute.Weaponry3" + }, + { + "from": "Skills.Attribute.Weaponry3", + "to": "Skills.Perk.BodyShots" + }, + { + "from": "Skills.Attribute.Weaponry4", + "to": "Skills.Perk.BodyShots" + }, + { + "from": "Skills.Attribute.Weaponry1", + "to": "Skills.Attribute.Weaponry2" + }, + { + "from": "Skills.Attribute.Weaponry1", + "to": "Skills.Perk.BodyShots" + } + ] }, { - "tag": "Skills.Attribute.Weaponry5", - "id": "Weaponry5", - "name": "Heavy Weapon Damage", - "kind": "Attribute", - "row": 2, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagebonusheavyweapon_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry5" + "name": "Suspensor Training", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Ability.SuspensorBlast", + "id": "SuspensorBlast", + "name": "Suspensor Blast", + "kind": "Ability", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconabilitysuspensorblast_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorblast" + }, + { + "tag": "Skills.Perk.DeathFromAbove", + "id": "DeathFromAbove", + "name": "Death from Above", + "kind": "Perk", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeperkdeathfromabove_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-deathfromabove" + }, + { + "tag": "Skills.Ability.CollapseGrenade", + "id": "CollapseGrenade", + "name": "Collapse Grenade", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_iconabilitycollapsegrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-collapsegrenade" + }, + { + "tag": "Skills.Attribute.SuspensorTech1", + "id": "SuspensorTech1", + "name": "Suspensor Efficiency", + "kind": "Attribute", + "row": 3, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", + "url": "https://dune.gaming.tools/skills/skills-attribute-suspensortech1" + }, + { + "tag": "Skills.Perk.SuspensorDash", + "id": "SuspensorDash", + "name": "Suspensor Dash", + "kind": "Perk", + "row": 4, + "col": 1, + "maxPoints": 1, + "icon": "t_ui_iconskilltreesuspensordash_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-suspensordash" + }, + { + "tag": "Skills.Ability.SuspensorGrenade_Amplification", + "id": "SuspensorGrenade_Amplification", + "name": "Gravity Field", + "kind": "Ability", + "row": 4, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_icongadgetamplificationgrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_amplification" + }, + { + "tag": "Skills.Ability.SuspensorGrenade_Reduction", + "id": "SuspensorGrenade_Reduction", + "name": "Anti-gravity Field", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_icongadgetreductionsuspensorgrenade_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_reduction" + } + ], + "edges": [ + { + "from": "Skills.Ability.SuspensorBlast", + "to": "Skills.Perk.DeathFromAbove" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Ability.SuspensorBlast" + }, + { + "from": "Skills.Attribute.SuspensorTech1", + "to": "Skills.Perk.DeathFromAbove" + }, + { + "from": "Skills.Perk.DeathFromAbove", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Attribute.SuspensorTech1" + }, + { + "from": "Skills.Ability.CollapseGrenade", + "to": "Skills.Ability.SuspensorGrenade_Amplification" + }, + { + "from": "Skills.Attribute.SuspensorTech1", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Amplification", + "to": "Skills.Attribute.SuspensorTech1" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Reduction", + "to": "Skills.Perk.SuspensorDash" + }, + { + "from": "Skills.Ability.SuspensorGrenade_Amplification", + "to": "Skills.Ability.SuspensorGrenade_Reduction" + } + ] }, { - "tag": "Skills.Attribute.Weaponry6", - "id": "Weaponry6", - "name": "Gunsmith", - "kind": "Attribute", - "row": 2, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributerepairefficiency_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry6" - }, - { - "tag": "Skills.Perk.HeavyWeaponNaib", - "id": "HeavyWeaponNaib", - "name": "Heavy Weapon Agility", - "kind": "Perk", - "row": 3, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkheavyweaponnaib_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-heavyweaponnaib" - }, - { - "tag": "Skills.Attribute.Weaponry3", - "id": "Weaponry3", - "name": "Scattergun Damage", - "kind": "Attribute", - "row": 3, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagebonusrifle_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry3" - }, - { - "tag": "Skills.Attribute.Weaponry4", - "id": "Weaponry4", - "name": "Field Maintenance", - "kind": "Attribute", - "row": 3, - "col": 5, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributerepair_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry4" - }, - { - "tag": "Skills.Attribute.Weaponry2", - "id": "Weaponry2", - "name": "Disruptor Damage", - "kind": "Attribute", - "row": 4, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamagebonussmg_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry2" - }, - { - "tag": "Skills.Perk.BodyShots", - "id": "BodyShots", - "name": "Center of Mass", - "kind": "Perk", - "row": 4, - "col": 4, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkcentralaim_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-bodyshots" - }, - { - "tag": "Skills.Attribute.Weaponry1", - "id": "Weaponry1", - "name": "Ranged Damage", - "kind": "Attribute", - "row": 5, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeattributedamage_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-weaponry1" - }, - { - "tag": "Skills.Ability.SuspensorBlast", - "id": "SuspensorBlast", - "name": "Suspensor Blast", - "kind": "Ability", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconabilitysuspensorblast_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensorblast" - }, - { - "tag": "Skills.Perk.DeathFromAbove", - "id": "DeathFromAbove", - "name": "Death from Above", - "kind": "Perk", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeperkdeathfromabove_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-deathfromabove" - }, - { - "tag": "Skills.Ability.CollapseGrenade", - "id": "CollapseGrenade", - "name": "Collapse Grenade", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_iconabilitycollapsegrenade_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-collapsegrenade" - }, - { - "tag": "Skills.Attribute.SuspensorTech1", - "id": "SuspensorTech1", - "name": "Suspensor Efficiency", - "kind": "Attribute", - "row": 3, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_iconskilltreeskillpowerefficiency_d.webp", - "url": "https://dune.gaming.tools/skills/skills-attribute-suspensortech1" - }, - { - "tag": "Skills.Perk.SuspensorDash", - "id": "SuspensorDash", - "name": "Suspensor Dash", - "kind": "Perk", - "row": 4, - "col": 1, - "maxPoints": 1, - "icon": "t_ui_iconskilltreesuspensordash_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-suspensordash" - }, - { - "tag": "Skills.Ability.SuspensorGrenade_Amplification", - "id": "SuspensorGrenade_Amplification", - "name": "Gravity Field", - "kind": "Ability", - "row": 4, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_icongadgetamplificationgrenade_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_amplification" - }, - { - "tag": "Skills.Ability.SuspensorGrenade_Reduction", - "id": "SuspensorGrenade_Reduction", - "name": "Anti-gravity Field", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_icongadgetreductionsuspensorgrenade_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-suspensorgrenade_reduction" - }, - { - "tag": "Skills.Spice.GadgetReload", - "id": "GadgetReload", - "name": "Reflexive Reload", - "kind": "Spice", - "row": 1, - "col": 2, - "maxPoints": 1, - "icon": "t_ui_iconskilltreereflexivereload_d.webp", - "url": "https://dune.gaming.tools/skills/skills-spice-gadgetreload" - }, - { - "tag": "Skills.Ability.AssaultSeeker", - "id": "AssaultSeeker", - "name": "Assault Seeker", - "kind": "Ability", - "row": 2, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_iconabilityassaultseeker_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-assaultseeker" - }, - { - "tag": "Skills.Ability.MagneticAttractor", - "id": "MagneticAttractor", - "name": "Attractor Field", - "kind": "Ability", - "row": 2, - "col": 3, - "maxPoints": 1, - "icon": "t_ui_icongadgetshigmultitoolmagneticattractor_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-magneticattractor" - }, - { - "tag": "Skills.Ability.FragGrenade", - "id": "FragGrenade", - "name": "Explosive Grenade", - "kind": "Ability", - "row": 4, - "col": 1, - "maxPoints": 3, - "icon": "t_ui_icongadgetfraggrenades_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-fraggrenade" - }, - { - "tag": "Skills.Perk.TrooperCooldowns", - "id": "TrooperCooldowns", - "name": "Battle Hardened", - "kind": "Perk", - "row": 4, - "col": 3, - "maxPoints": 3, - "icon": "t_ui_iconskilltreetroopercooldown_d.webp", - "url": "https://dune.gaming.tools/skills/skills-perk-troopercooldowns" - }, - { - "tag": "Skills.Ability.CablePull", - "id": "CablePull", - "name": "Shigawire Claw", - "kind": "Ability", - "row": 5, - "col": 2, - "maxPoints": 3, - "icon": "t_ui_icongadgetshigmultitoolsardaukarpull_d.webp", - "url": "https://dune.gaming.tools/skills/skills-ability-cablepull" - } - ], - "edges": [ - { - "from": "Skills.Ability.EnergyCapsule", - "to": "Skills.Attribute.Weaponry5" - }, - { - "from": "Skills.Ability.EnergyCapsule", - "to": "Skills.Attribute.Weaponry6" - }, - { - "from": "Skills.Attribute.Weaponry5", - "to": "Skills.Perk.HeavyWeaponNaib" - }, - { - "from": "Skills.Attribute.Weaponry3", - "to": "Skills.Attribute.Weaponry5" - }, - { - "from": "Skills.Attribute.Weaponry3", - "to": "Skills.Attribute.Weaponry6" - }, - { - "from": "Skills.Attribute.Weaponry4", - "to": "Skills.Attribute.Weaponry6" - }, - { - "from": "Skills.Attribute.Weaponry2", - "to": "Skills.Perk.HeavyWeaponNaib" - }, - { - "from": "Skills.Attribute.Weaponry2", - "to": "Skills.Attribute.Weaponry3" - }, - { - "from": "Skills.Attribute.Weaponry3", - "to": "Skills.Perk.BodyShots" - }, - { - "from": "Skills.Attribute.Weaponry4", - "to": "Skills.Perk.BodyShots" - }, - { - "from": "Skills.Attribute.Weaponry1", - "to": "Skills.Attribute.Weaponry2" - }, - { - "from": "Skills.Attribute.Weaponry1", - "to": "Skills.Perk.BodyShots" - }, - { - "from": "Skills.Ability.SuspensorBlast", - "to": "Skills.Perk.DeathFromAbove" - }, - { - "from": "Skills.Ability.CollapseGrenade", - "to": "Skills.Ability.SuspensorBlast" - }, - { - "from": "Skills.Attribute.SuspensorTech1", - "to": "Skills.Perk.DeathFromAbove" - }, - { - "from": "Skills.Perk.DeathFromAbove", - "to": "Skills.Perk.SuspensorDash" - }, - { - "from": "Skills.Ability.CollapseGrenade", - "to": "Skills.Attribute.SuspensorTech1" - }, - { - "from": "Skills.Ability.CollapseGrenade", - "to": "Skills.Ability.SuspensorGrenade_Amplification" - }, - { - "from": "Skills.Attribute.SuspensorTech1", - "to": "Skills.Perk.SuspensorDash" - }, - { - "from": "Skills.Ability.SuspensorGrenade_Amplification", - "to": "Skills.Attribute.SuspensorTech1" - }, - { - "from": "Skills.Ability.SuspensorGrenade_Reduction", - "to": "Skills.Perk.SuspensorDash" - }, - { - "from": "Skills.Ability.SuspensorGrenade_Amplification", - "to": "Skills.Ability.SuspensorGrenade_Reduction" + "name": "Tactical Tech", + "cols": 3, + "nodes": [ + { + "tag": "Skills.Spice.GadgetReload", + "id": "GadgetReload", + "name": "Reflexive Reload", + "kind": "Spice", + "row": 1, + "col": 2, + "maxPoints": 1, + "icon": "t_ui_iconskilltreereflexivereload_d.webp", + "url": "https://dune.gaming.tools/skills/skills-spice-gadgetreload" + }, + { + "tag": "Skills.Ability.AssaultSeeker", + "id": "AssaultSeeker", + "name": "Assault Seeker", + "kind": "Ability", + "row": 2, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_iconabilityassaultseeker_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-assaultseeker" + }, + { + "tag": "Skills.Ability.MagneticAttractor", + "id": "MagneticAttractor", + "name": "Attractor Field", + "kind": "Ability", + "row": 2, + "col": 3, + "maxPoints": 1, + "icon": "t_ui_icongadgetshigmultitoolmagneticattractor_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-magneticattractor" + }, + { + "tag": "Skills.Ability.FragGrenade", + "id": "FragGrenade", + "name": "Explosive Grenade", + "kind": "Ability", + "row": 4, + "col": 1, + "maxPoints": 3, + "icon": "t_ui_icongadgetfraggrenades_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-fraggrenade" + }, + { + "tag": "Skills.Perk.TrooperCooldowns", + "id": "TrooperCooldowns", + "name": "Battle Hardened", + "kind": "Perk", + "row": 4, + "col": 3, + "maxPoints": 3, + "icon": "t_ui_iconskilltreetroopercooldown_d.webp", + "url": "https://dune.gaming.tools/skills/skills-perk-troopercooldowns" + }, + { + "tag": "Skills.Ability.CablePull", + "id": "CablePull", + "name": "Shigawire Claw", + "kind": "Ability", + "row": 5, + "col": 2, + "maxPoints": 3, + "icon": "t_ui_icongadgetshigmultitoolsardaukarpull_d.webp", + "url": "https://dune.gaming.tools/skills/skills-ability-cablepull" + } + ], + "edges": [ + { + "from": "Skills.Ability.AssaultSeeker", + "to": "Skills.Spice.GadgetReload" + }, + { + "from": "Skills.Ability.MagneticAttractor", + "to": "Skills.Spice.GadgetReload" + }, + { + "from": "Skills.Ability.AssaultSeeker", + "to": "Skills.Ability.FragGrenade" + }, + { + "from": "Skills.Ability.MagneticAttractor", + "to": "Skills.Perk.TrooperCooldowns" + }, + { + "from": "Skills.Ability.CablePull", + "to": "Skills.Ability.FragGrenade" + }, + { + "from": "Skills.Ability.CablePull", + "to": "Skills.Perk.TrooperCooldowns" + } + ] } ] } \ No newline at end of file diff --git a/character-builder/frontend/public/icons/slot-ability.png b/character-builder/frontend/public/icons/slot-ability.png new file mode 100644 index 0000000000000000000000000000000000000000..2017cfc73a64ac1699451c0ed1bd15932992f688 GIT binary patch literal 3766 zcmV;n4oUG+Nk&Gl4gdgGMM6+kP&il$0000G0001s004^s06|PpNW=;N009}aZQC}6 zhML@n{>RfDb@f@;h6u0ye~Kj8x#A0AW|qgyOz%UcEM~@+U}ky%29ueY!RX#qbyb!u z`dT3hvG#1)xAwX^DF`nFVG5@F9)TC4zc2(Td+Yi=u1;PEQ!wcXrcE8?KEPk_HwF0s z)90)i<3vor1Uw|)qwEt1Pe@LABDI1**k<2>Z^sj>VDfF86N{B?9tjU6-^_Rt-zq*S zQdKFktqN&!@yTdS-{L7e42E|jHBz=x5Xw--;TtW4$uter7>2N&3OT##zh1FLhKGA{ zIxu^j`NNcrnz4N+^_y|NGVj_lN@)h>yt^)2;$ z^G3Mq3CO-WZ*g=^wgAD4=TJd=z`dux%0DV*3UFABx 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 literal 0 HcmV?d00001 diff --git a/character-builder/frontend/public/icons/slot-technique.png b/character-builder/frontend/public/icons/slot-technique.png new file mode 100644 index 0000000000000000000000000000000000000000..214b328ddd39d3c00478e1c17127804ddac8978f GIT binary patch literal 3086 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 literal 0 HcmV?d00001 diff --git a/character-builder/frontend/src/App.vue b/character-builder/frontend/src/App.vue index ef79cb3..1fc43f6 100644 --- a/character-builder/frontend/src/App.vue +++ b/character-builder/frontend/src/App.vue @@ -4,6 +4,7 @@ import XpProgressCard from './components/XpProgressCard.vue'; import FactionTrack from './components/FactionTrack.vue'; import SkillTree from './components/SkillTree.vue'; import CharacterSummary from './components/CharacterSummary.vue'; +import LoadoutSlots from './components/LoadoutSlots.vue'; import { applyBuild, build, @@ -143,8 +144,10 @@ const spentByClass = computed>(() => { for (const c of CLASSES) { const tree = skillTrees.value[c.id]; if (!tree) continue; - for (const node of tree.nodes) { - out[c.id] += build.skills[node.tag] || 0; + for (const st of tree.subtrees) { + for (const node of st.nodes) { + out[c.id] += build.skills[node.tag] || 0; + } } } return out; @@ -433,6 +436,14 @@ const specMeta: Record = { @update:allocations="(a) => (build.skills = a)" @reset="resetAllSkills" /> + + diff --git a/character-builder/frontend/src/components/CharacterSummary.vue b/character-builder/frontend/src/components/CharacterSummary.vue index 82f7726..969162f 100644 --- a/character-builder/frontend/src/components/CharacterSummary.vue +++ b/character-builder/frontend/src/components/CharacterSummary.vue @@ -104,17 +104,19 @@ const skillsByClass = computed(() => { const allocations: ClassSkillSummary['allocations'] = []; let spent = 0; if (tree) { - for (const node of tree.nodes) { - const pts = props.build.skills[node.tag] || 0; - if (pts > 0) { - spent += pts; - allocations.push({ - tag: node.tag, - name: node.name, - kind: node.kind, - points: pts, - max: node.maxPoints, - }); + for (const st of tree.subtrees) { + for (const node of st.nodes) { + const pts = props.build.skills[node.tag] || 0; + if (pts > 0) { + spent += pts; + allocations.push({ + tag: node.tag, + name: node.name, + kind: node.kind, + points: pts, + max: node.maxPoints, + }); + } } } } diff --git a/character-builder/frontend/src/components/LoadoutSlots.vue b/character-builder/frontend/src/components/LoadoutSlots.vue new file mode 100644 index 0000000..c1623d5 --- /dev/null +++ b/character-builder/frontend/src/components/LoadoutSlots.vue @@ -0,0 +1,424 @@ + + + + + diff --git a/character-builder/frontend/src/components/SkillTree.vue b/character-builder/frontend/src/components/SkillTree.vue index 6b36638..5dbf7be 100644 --- a/character-builder/frontend/src/components/SkillTree.vue +++ b/character-builder/frontend/src/components/SkillTree.vue @@ -1,6 +1,6 @@ + + diff --git a/character-builder/frontend/src/store.ts b/character-builder/frontend/src/store.ts index a9bfaec..995fd23 100644 --- a/character-builder/frontend/src/store.ts +++ b/character-builder/frontend/src/store.ts @@ -19,9 +19,20 @@ export function defaultBuild(): BuildState { specs, faction: { tier: 0, standingInto: 0 }, skills: {}, + loadout: { abilities: [null, null, null], techniques: [null, null, null] }, }; } +function normalizeSlots(raw: unknown, len: number): (string | null)[] { + const arr = Array.isArray(raw) ? raw : []; + const out: (string | null)[] = []; + for (let i = 0; i < len; i++) { + const v = arr[i]; + out.push(typeof v === 'string' && v.length > 0 ? v : null); + } + return out; +} + function migrate(raw: any): BuildState { const def = defaultBuild(); if (!raw || typeof raw !== 'object') return def; @@ -33,6 +44,10 @@ function migrate(raw: any): BuildState { specs: { ...def.specs }, faction: { ...def.faction, ...(raw.faction || {}) }, skills: { ...(raw.skills || {}) }, + loadout: { + abilities: normalizeSlots(raw.loadout?.abilities, 3), + techniques: normalizeSlots(raw.loadout?.techniques, 3), + }, }; if (raw.specs && typeof raw.specs === 'object') { for (const k of Object.keys(out.specs) as SpecId[]) { diff --git a/character-builder/frontend/src/types.ts b/character-builder/frontend/src/types.ts index 5ab33f6..fb39d26 100644 --- a/character-builder/frontend/src/types.ts +++ b/character-builder/frontend/src/types.ts @@ -66,11 +66,17 @@ export interface SkillEdge { to: string; } +export interface SkillSubtree { + name: string; + cols: number; + nodes: SkillNode[]; + edges: SkillEdge[]; +} + export interface SkillTree { id: ClassId; name: string; - nodes: SkillNode[]; - edges: SkillEdge[]; + subtrees: SkillSubtree[]; } export interface SpecProgress { @@ -78,6 +84,14 @@ export interface SpecProgress { xpInto: number; } +// Loadout: 3 active Ability slots + 3 active Technique slots, both pulled +// from the union of all allocated skills across all 5 trees (the cap is +// global, not per-tree). +export interface Loadout { + abilities: (string | null)[]; // length 3 — element is a skill tag, or null + techniques: (string | null)[]; // length 3 +} + export interface BuildState { v: 1; // schema version house: House; @@ -95,4 +109,5 @@ export interface BuildState { // Allocated skill points keyed by full tag (e.g. Skills.Ability.BattleCry). // Tags are globally unique across classes, so one flat map works for all 5. skills: Record; + loadout: Loadout; } diff --git a/character-builder/scripts/extract.py b/character-builder/scripts/extract.py index 9a6c3d5..fede12f 100644 --- a/character-builder/scripts/extract.py +++ b/character-builder/scripts/extract.py @@ -216,109 +216,123 @@ LINE_RE = re.compile( ) +SUBTREE_H3_RE = re.compile( + r']*class="[^"]*text-xl[^"]*"[^>]*>([^<]+)' +) +GRID_COLS_RE = re.compile(r"grid-template-columns:\s*repeat\((\d+),\s*72px\)") + + +def _extract_node(chunk: str, tag: str) -> dict | None: + gm = GRID_RE.search(chunk) + if not gm: + return None + row, col = int(gm.group(1)), int(gm.group(2)) + alt = ALT_RE.search(chunk) + href = HREF_RE.search(chunk) + icon = ICON_RE.search(chunk) + max_pts = MAX_PTS_RE.search(chunk) + kind = tag.split(".")[1] if "." in tag else "Unknown" + return { + "tag": tag, + "id": tag.split(".")[-1], + "name": alt.group(1) if alt else tag.split(".")[-1], + "kind": kind, # Ability | Attribute | Perk | Spice + "row": row, + "col": col, + "maxPoints": int(max_pts.group(1)) if max_pts else 1, + "icon": icon.group(1) if icon else None, + "url": href.group(1) if href else None, + } + + +def _map_edges(html_slice: str, nodes: list[dict]) -> list[dict]: + """Pixel-match connector endpoints to the nearest nodes in this + subtree. Returns deduped edge list.""" + lines = list(LINE_RE.finditer(html_slice)) + if not lines or not nodes: + return [] + all_x = [int(x) for ln in lines for x in (ln.group("x1"), ln.group("x2"))] + all_y = [int(y) for ln in lines for y in (ln.group("y1"), ln.group("y2"))] + min_x, max_x = min(all_x), max(all_x) + min_y, max_y = min(all_y), max(all_y) + cols = [n["col"] for n in nodes] + rows_ = [n["row"] for n in nodes] + min_c, max_c = min(cols), max(cols) + min_r, max_r = min(rows_), max(rows_) + sx = (max_x - min_x) / max(1, (max_c - min_c)) + sy = (max_y - min_y) / max(1, (max_r - min_r)) + + centers = { + n["tag"]: (min_x + (n["col"] - min_c) * sx, min_y + (n["row"] - min_r) * sy) + for n in nodes + } + + def nearest(x: int, y: int) -> str | None: + best, best_d = None, float("inf") + for tag, (cx, cy) in centers.items(): + d = (cx - x) ** 2 + (cy - y) ** 2 + if d < best_d: + best_d, best = d, tag + return best + + seen, edges = set(), [] + for ln in lines: + x1, y1, x2, y2 = (int(ln.group(k)) for k in ("x1", "y1", "x2", "y2")) + a, b = nearest(x1, y1), nearest(x2, y2) + if a and b and a != b: + key = tuple(sorted((a, b))) + if key not in seen: + seen.add(key) + edges.append({"from": key[0], "to": key[1]}) + return edges + + def extract_skill_tree(path: Path, class_id: str, class_name: str) -> dict: + """Parse a class skill tree into its named subtrees. + + Each class is composed of 3 subtrees (e.g. Swordmaster has "The Blade", + "The Will", "The Way"). Each subtree is its own CSS grid with its own + column count, node positions, and connectors. Treating them as one big + grid (the prior behavior) collapsed all 22 nodes on top of each other. + """ html = path.read_text() - nodes = [] - # We need to also find the alt text + href + icon WITHIN each node's HTML. - # Strategy: walk through all data-tag="Skills..." occurrences, slice from - # opening of the node
to a balanced close. Simple slice: take 2000 chars - # after the tag and parse first alt/href/icon/max within it. - for m in re.finditer(r'data-tag="(Skills\.[^"]+)"', html): - tag = m.group(1) - start = m.start() - chunk = html[start : start + 2500] - gm = GRID_RE.search(chunk) - if not gm: + # Split on subtree H3 headers; first chunk is preamble. + chunks = SUBTREE_H3_RE.split(html) + preamble, pairs = chunks[0], chunks[1:] + + subtrees: list[dict] = [] + for i in range(0, len(pairs), 2): + name = pairs[i].strip() + body = pairs[i + 1] if i + 1 < len(pairs) else "" + # Slice off anything that belongs to the next subtree (already handled + # by split) or to trailing page chrome — search for the closing of the + # graph div by counting from the start of the graph element. + graph_start = body.find('
node lookup. Grid is roughly square with ~73px cells based - # on observed example (grid 3/5 -> center 364,220 means col*~73, row*~73 with offset). - # We'll learn cell size from the data: if there are connectors, we map each (x,y) to - # the nearest node by Euclidean distance. - # First compute approximate node centers via grid math, calibrated from any node - # we can pin: actually a more reliable approach is to use the connector geometry. - edges = [] - lines = list(LINE_RE.finditer(html)) - if lines and nodes: - # Calibrate: find scale by looking at min/max grid coords vs min/max line coords. - all_x = [int(x) for ln in lines for x in (ln.group("x1"), ln.group("x2"))] - all_y = [int(y) for ln in lines for y in (ln.group("y1"), ln.group("y2"))] - min_x, max_x = min(all_x), max(all_x) - min_y, max_y = min(all_y), max(all_y) - cols = [n["col"] for n in nodes] - rows = [n["row"] for n in nodes] - min_c, max_c = min(cols), max(cols) - min_r, max_r = min(rows), max(rows) - # avoid div by zero - sx = (max_x - min_x) / max(1, (max_c - min_c)) - sy = (max_y - min_y) / max(1, (max_r - min_r)) - - def center(n): - return ( - min_x + (n["col"] - min_c) * sx, - min_y + (n["row"] - min_r) * sy, - ) - - centers = {n["tag"]: center(n) for n in nodes} - - def nearest(x, y): - best_tag, best_d = None, float("inf") - for t, (cx, cy) in centers.items(): - d = (cx - x) ** 2 + (cy - y) ** 2 - if d < best_d: - best_d = d - best_tag = t - return best_tag - - seen_edges = set() - for ln in lines: - x1, y1, x2, y2 = ( - int(ln.group("x1")), - int(ln.group("y1")), - int(ln.group("x2")), - int(ln.group("y2")), - ) - a = nearest(x1, y1) - b = nearest(x2, y2) - if a and b and a != b: - key = tuple(sorted((a, b))) - if key not in seen_edges: - seen_edges.add(key) - edges.append({"from": key[0], "to": key[1]}) - - return { - "id": class_id, - "name": class_name, - "nodes": nodes, - "edges": edges, - } + return {"id": class_id, "name": class_name, "subtrees": subtrees} # ---------- main ---------- @@ -413,13 +427,16 @@ def main(): continue tree = extract_skill_tree(path, cls_id, cls_name) (OUT / f"skills-{cls_id}.json").write_text(json.dumps(tree, indent=2)) + total_nodes = sum(len(st["nodes"]) for st in tree["subtrees"]) + total_edges = sum(len(st["edges"]) for st in tree["subtrees"]) manifest["skills"].append( { "id": cls_id, "name": cls_name, "file": f"skills-{cls_id}.json", - "nodes": len(tree["nodes"]), - "edges": len(tree["edges"]), + "subtrees": [st["name"] for st in tree["subtrees"]], + "nodes": total_nodes, + "edges": total_edges, } ) @@ -429,9 +446,10 @@ def main(): path = OUT / f"skills-{cls_id}.json" if path.exists(): tree = json.loads(path.read_text()) - for n in tree["nodes"]: - if n.get("icon"): - icon_names.add(n["icon"]) + for st in tree["subtrees"]: + for n in st["nodes"]: + if n.get("icon"): + icon_names.add(n["icon"]) for spec in specs: slug = spec.lower() spec_data = json.loads((OUT / f"spec-{slug}.json").read_text()) @@ -440,6 +458,18 @@ def main(): if p.get("icon"): icon_names.add(p["icon"]) copied, missing = copy_icons(icon_names) + + # Slot background images for the global Abilities + Techniques loadout — + # the source HTML references them from a CDN, but local copies live in + # the per-class _files directories. + for src_name, dst_name in [ + ("ability.png", "slot-ability.png"), + ("technique.png", "slot-technique.png"), + ]: + src = find_icon_source(src_name) + if src: + shutil.copy2(src, ICONS_OUT / dst_name) + manifest["icons"] = { "directory": "frontend/public/icons", "served_at": "/icons/",