• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Spellweaver's Talent Kitchen

A vJass system made for implementing talent systems into a map. It provides a way to build a talent tree from an easy to use API and link game logic to it. It also provides a user interface and handles the changes automatically.

Some features it has...
  • Single rank and multi rank talents. Each rank is defined separately and can have its own text/icon/behavior
  • Logic binding on Activation/Deactivation (when talent effects are applied), Allocation (when talent is taken before confirming), Requirements (custom disable logic)
  • Talent point tracking, each talent/rank can have a varying talent point cost (0, 1 or more)
  • Requirements (custom logic based) talent disable, the user defined text reason will be shown in the tooltip
  • Talent Dependencies, in cardinal direction, talent can require its adjacent talents to have a specified level, this is shown in interface as "links" or "lines"
  • Talent-tree based configurable rows and columns, for example Pyromancer can have 6 rows, 3 columns and Fighter can have 4x4
  • Grid based talent positioning (X, Y) and automatic button placement based on rows/columns
  • Temporary talent point allocation, points can be put into tree and reset as many times as needed before "Confirm" is clicked, only then all the effects will apply
  • Talent tree reset, deactivates all talents and refunds all points spent.
  • Save-Load API for integration with save-load systems

An API reference is included, and examples should provide more than enough information for implementation/trying things out.

18.11.2021 - Posted
21.11.2021 - Multi-panel trees, Link-talents, More accessible customizability, RUID integration, github repo link + DruidClassic example
22.11.2021 - Added Relative version of the Druid map UI. Resizing or moving the frames will affect the rest of the frames.
04.12.2021 - Sneakily added a typescript version here's the repo
23.03.2023 - Various minor fixes, added Talent reset, flexible talent icon methods, performance optimizations. Updated STK map (Shepherd version)
29.03.2023 - Updated Druid example map with changes
31.03.2023 - Added interface summary and API Documentation + fix talent reset tooltip not updating bug. STK and Druid map updated.
12.07.2023 - Fixed single rank dependency message not showing required rank, updated both STK and Druid map. Updated readme.md in git.
17.07.2023 - Added Save-Load API, updated both maps. This change affected most of the files (didn't touch Interfaces and Views folder) and added new ones (Services). Setup.j was completely reworked and consolidated between Druid and STK maps. TalentTree implementations (Shepherd, Balance, Feral, Restoration) should work as is, but require a slight change (registration) in order to utilize Save-Load API.
10.09.2024 - Fixed function PlayerLookAtUnitsTree not using player parameter, fixed GetEventRank (inside talent callbacks) not returning correct value. Updated STK and Druid map with changes.

Instead of pasting code here (since it lags a lot) here's a link to a Github Repo. Code from all current and future examples will be there as well.


Tags: Talent system, talent tree, ui

vJASS:
scope Shepherd initializer init

    public struct Shepherd extends STKTalentTree_TalentTree

        // Overriden stub methods, can delete this =================================
        // method GetTalentPoints takes nothing returns integer
        //     return GetPlayerState(this.ownerPlayer, PLAYER_STATE_RESOURCE_LUMBER)
        // endmethod

        // method SetTalentPoints takes integer points returns nothing
        //     call SetPlayerState(this.ownerPlayer, PLAYER_STATE_RESOURCE_LUMBER, points)
        // endmethod

        // method GetTitle takes nothing returns string
        //     return this.title
        // endmethod
        // =========================================================================

        method Initialize takes nothing returns nothing
            local STKTalent_Talent t

            call this.SetIdColumnsRows(1, 3, 4)
            set this.title = "Shepherd"
            call this.SetTalentPoints(6)
            // set this.icon = "FireBolt"
            // TODO: set tree background texture here
            // set this.backgroundImage = "arms.dds"

            // The tree should be built with talents here
            // ==============================================

            // Wondrous Flute <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(1)
            call t.SetName("Wondrous Flute")
            call t.SetDescription("Calls a sheep to your side.")
            call t.SetIcon("AlleriaFlute")
            call t.SetOnActivate(function thistype.Activate_CallSheep)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetCost(0) // First level of this talent is free
            call this.AddTalent(0, 0, t)

            // Rank 2
            set t = this.CreateTalent().SetChainId(1)
            call t.SetName("Wondrous Flute")
            call t.SetDescription("Calls another sheep to your side.")
            call t.SetIcon("AlleriaFlute")
            call t.SetOnActivate(function thistype.Activate_CallSheep)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call this.AddTalent(0, 0, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Soothing Song <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(2)
            call t.SetName("Soothing Song")
            call t.SetDescription("Calls a flying sheep to your side.")
            call t.SetIcon("DispelMagic")
            call t.SetOnActivate(function thistype.Activate_CallFlyingSheep)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetDependencies(1, 0, 0, 0) // left 1 (left up right down)
            call this.AddTalent(1, 0, t)

            // Rank 2
            set t = this.CreateTalent().SetChainId(2)
            call t.SetName("Soothing Song")
            call t.SetDescription("Calls another flying sheep to your side.")
            call t.SetIcon("DispelMagic")
            call t.SetOnActivate(function thistype.Activate_CallFlyingSheep)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetDependencies(1, 0, 0, 0) // left 1 (left up right down)
            call this.AddTalent(1, 0, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Shepherd Apprentice <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(3)
            call t.SetName("Shepherd Apprentice")
            call t.SetDescription("Gain an apprentice.")
            call t.SetIcon("Peasant")
            call t.SetOnActivate(function thistype.Activate_GainApprentice)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call this.AddTalent(2, 0, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Lots of Apprentices <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(4)
            call t.SetName("Lots of Apprentices")
            call t.SetDescription("Gain 2 guard apprentices.")
            call t.SetIcon("Footman")
            call t.SetOnActivate(function thistype.Activate_Gain2Guards)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetDependencies(0, 0, 0, 1) // down 1 (left up right down)
            call this.AddTalent(2, 1, t)

            // Rank 2
            set t = this.CreateTalent().SetChainId(4)
            call t.SetName("Lots of Apprentices")
            call t.SetDescription("Gain 2 guard apprentices.")
            call t.SetIcon("Footman")
            call t.SetOnActivate(function thistype.Activate_Gain2Guards)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetDependencies(0, 0, 0, 1) // down 1 (left up right down)
            call this.AddTalent(2, 1, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Coming of the Lambs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(5)
            call t.SetName("Coming of the Lambs")
            call t.SetDescription("Gain a Sheep and a Flying Sheep.")
            call t.SetIcon("Sheep")
            call t.SetOnActivate(function thistype.Activate_ComingOfTheLambs)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetDependencies(0, 0, 1, 1) // right 1 down 1 (left up right down)
            call this.AddTalent(1, 1, t)

            // Rank 2
            set t = this.CreateTalentCopy(t)
            call t.SetDescription("Gain 2 Sheep and 2 Flying Sheep.")
            call this.AddTalent(1, 1, t)

            // Rank 3
            set t = this.CreateTalentCopy(t)
            call t.SetDescription("Gain 3 Sheep and 3 Flying Sheep.")
            call this.AddTalent(1, 1, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Call of the Wilds <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // Rank 1
            set t = this.CreateTalent().SetChainId(6)
            call t.SetName("Call of the Wilds")
            call t.SetDescription("Five hostile wolves appear.")
            call t.SetIcon("TimberWolf")
            call t.SetOnActivate(function thistype.Activate_CallOfTheWilds)
            call t.SetOnDeactivate(function thistype.Deactivate_Generic)
            call t.SetRequirements(function thistype.Requirement_CallOfTheWilds)
            call this.AddTalent(1, 2, t)
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

            // Only need to call this if some talents start with certain rank
            // call this.SaveTalentRankState()
        endmethod


        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // Can use these methods inside Activate/Deactivate/Allocate/Deallocate/Requirements functions
     
        // static method GetEventUnit takes nothing returns unit
        // Returns unit that owns the talent tree
        // thistype.GetEventUnit()

        // static method GetEventTalent takes nothing returns STKTalent_Talent
        // Returns talent object that is being resolved
        // local STKTalent_Talent t = thistype.GetEventTalent()

        // static method GetEventRank takes nothing returns integer
        // Returns rank of the talent that is being activated
        // local integer r = thistype.GetEventRank()

        // static method GetEventTalentTree takes nothing returns TalentTree
        // Returns "this"
        // local STKTalentTree_TalentTree tt = thistype.GetEventTalentTree()

        // static method SetTalentRequirementsResult takes string requirements returns nothing
        // Needs to be called within Requirements function to disable the talent
        // thistype.SetTalentRequirementsResult("8 litres of milk")

        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        static method Activate_CallSheep takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            call CreateUnit(GetOwningPlayer(u), 'nshe', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
        endmethod

        static method Activate_CallFlyingSheep takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            call CreateUnit(GetOwningPlayer(u), 'nshf', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
        endmethod

        static method Activate_GainApprentice takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            call CreateUnit(GetOwningPlayer(u), 'hpea', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
        endmethod

        static method Activate_Gain2Guards takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            call CreateUnit(GetOwningPlayer(u), 'hfoo', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
            call CreateUnit(GetOwningPlayer(u), 'hfoo', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
        endmethod

        static method Activate_ComingOfTheLambs takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            local integer i = thistype.GetEventRank()
            loop
                exitwhen i <= 0
                call CreateUnit(GetOwningPlayer(u), 'nshe', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
                call CreateUnit(GetOwningPlayer(u), 'nshf', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
                set i = i - 1
            endloop
        endmethod

        static method Activate_CallOfTheWilds takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            local integer i = 0
            loop
                exitwhen i > 5
                call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), 'nwlt', GetUnitX(u), GetUnitY(u), GetRandomDirectionDeg())
                set i = i + 1
            endloop
        endmethod

        static method IsEnumUnitSheepFilter takes nothing returns boolean
            return GetUnitTypeId(GetFilterUnit()) == 'nshe' or GetUnitTypeId(GetFilterUnit()) == 'nshf'
        endmethod

        static method Requirement_CallOfTheWilds takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            local group g = CreateGroup()
            call GroupEnumUnitsInRange(g, GetUnitX(u), GetUnitY(u), 5000, Filter(function thistype.IsEnumUnitSheepFilter))
            if (CountUnitsInGroup(g) < 8) then
                call thistype.SetTalentRequirementsResult("8 nearby sheep")
            endif
        endmethod

        static method Deactivate_Generic takes nothing returns nothing
            local unit u = thistype.GetEventUnit()
            local STKTalent_Talent t = thistype.GetEventTalent()
            local integer r = thistype.GetEventRank()
            call BJDebugMsg("Deactivated " + t.name + " " + I2S(r))
        endmethod

        static method LoadCreate takes unit u returns Shepherd
            return thistype.create(u)
        endmethod
    endstruct

    private function init takes nothing returns nothing
        // Register Talent Trees
        call STKSaveLoad_RegisterTalentTree(1, Shepherd.LoadCreate)
    endfunction

endscope
Previews
Contents

Spellweaver's Talent Kitchen (Map)

STK Druid Talents (Map)

Reviews
Wrda
Must have been in STKTalentTree library (TalentTree script) DRUID version only: local player clickingPlayer variable is still leaking in TalentTreeViewModel script. By the way, cancel functionality is functioning like the "reset" in the other...
Is it possible to have multiple talent trees that can be opened independently?
Let's say I have three abilities, and each one can open a unique talent tree. How would I go about doing this?

Is there a function I need to call before calling STK_OpenTalentScreen(GetTriggerPlayer()) to open the specific tree that I want?
 
Level 6
Joined
Nov 3, 2018
Messages
79
I Apologize you are right i did not said which version i use.
the version i use is 1.31.1 which is prereforged patch or 1.32 is fine too because 1.31 uses 1.32 natives
 
This was released after 1.31.1 was the newest patch, so there is no such thing as legacy version of this.
Anyways, it worked with 1.31.1, go here: GitHub - Gohagga/STK_vJass and copy-paste them to a testmap

See my comment here for how I got it to work:

I'm not sure, but this could be my modified files: I think you'll need all? I don't really remember tbh :p
(Note: I think this is the version released on 31.03.2023)
 

Attachments

  • SpellweaversTalentKitchen.j
    60.4 KB · Views: 9
  • SpellweaversTalentKitchen_SetupAndConstants.j
    8 KB · Views: 8
  • SpellwaversTalentKitchen_Views.j
    11.3 KB · Views: 7
  • SpellweaversTalentKitchen.j
    60.4 KB · Views: 10
Level 6
Joined
Nov 3, 2018
Messages
79
i checked the druid example plus i double checked on the shephard example but for some reason background refuses to appear i tried with your texture , my texture , in game textures (yeah no /war3imported) i'm lost on what's the reason my background texture its not showing (tried my texture on the druid example map changing the texture name and it worked...)

1708669046555.png
 
Last edited:
Level 3
Joined
Apr 10, 2023
Messages
12
Hey there. :)
Just wondering: Is there a comparable framework (one that achieves similar results) for V 1.24? (Yes, I know, that version is really outdated and I should update the game, but it's the version we are playing at LAN parties).
Best one I found was Talent/Skill Tree 1.7, but it's lacking convenience.
 
I LOVE this system but I found a bug. If you allocate points a talent twice and then click confirm thistype.GetEventRank() will return the wrong value. Attached is a sample map showing the bug. I edited your sample map so it sets the level of Iron Forged Swords to the talent level of Wondrous Flute. If you get both levels of flute at the same time Iron Forged Swords becomes level 1 when it should be 2.
 

Attachments

  • STK bug example.w3m
    103.4 KB · Views: 5
I LOVE this system but I found a bug. If you allocate points a talent twice and then click confirm thistype.GetEventRank() will return the wrong value. Attached is a sample map showing the bug. I edited your sample map so it sets the level of Iron Forged Swords to the talent level of Wondrous Flute. If you get both levels of flute at the same time Iron Forged Swords becomes level 1 when it should be 2.
After some careful examination, I realized that I've already made a fix for that, but I forgot to merge it into main branch. I uploaded the updated maps, it should now work correctly, sorry.

Thank you for the report!
 
Top