// exp
Name | Type | is_array | initial_value |
Hero | unit | No | |
TempStr | string | No | |
TempTalentTree | integer | No |
/* Entities within the system
=> TalentTree <============================================================================
A game logic object owned by and pointing to a single unit. These are some of its responsibilities:
- is built using Talents, holds their reference and their position on the grid
- keeps track of Talent (active) ranks and temporary (allocated) ranks
- keeps track of and manages "Talent points"
- manages things like talent tree title, background image, number of columns and rows
- responsible for activating/deactivating, applying/removing effects of Talents upon its unit
[How to use]
TalentTree struct contains behaviors integrated with the system itself.
It is meant to be INHERITED by the user, creating a custom talent tree, in example; Shepherd. In rpgs these could be Warrior, Mage, Cleric etc.
After creating a new type of TalentTree, override "Initialize", and other methods as needed
[Properties]
-> public string title
Name of the talent tree, shown on the Talent tree window
this.title
-> public integer talentPoints
Internal variable for tracking talent points. Unused if talent points are held externally e.g. Lumber
this.talentPoints
-> public string backgroundImage
Path to the texture that will be rendered in TalentTree background.
[Overrideable methods]
-> stub method Initialize takes nothing returns nothing
Always override when creating a new type of talent tree. Used for setting up the talent tree with row number, column number, background image, talent points, title. Then add Talents to it and customize their behavior.
-> stub method SetTalentPoints takes integer points returns nothing
Override when your Talent points are held externally, e.g. if you are using Lumber to track talent points, change player's Lumber to given value.
-> stub method GetTalentPoints takes nothing returns integer
Override when your Talent points are held externally, e.g. if you are using Lumber to track talent points, return player's Lumber value.
-> stub method GetTitle takes nothing returns string
Override when you want to show some dynamic info in the title, e.g. number of Talent points
[Public methods used during Initialize]
-> method SetIdColumnsRows takes integer id, integer columns, integer rows returns nothing
Sets TalentTree Id which is important for save-loading
Sets number of columns and rows according which Talents will be rendered on the grid
call this.SetColumnsRows(4, 7)
-> method CreateTalent takes nothing returns STKTalent_Talent
Creates an unmodified Talent object, ready to be configured
local STKTalent_Talent t = this.CreateTalent()
-> method CreateTalentCopy tales STKTalent_Talent data returns STKTalent_Talent
Creates a copy of given talent and returns it, ready to be tweaked.
local STKTalent_Talent t = this.CreateTalentCopy(t)
-> method AddTalent takes integer x, integer y, STKTalent_Talent talent returns STKTalent_Talent
Adds talent to the talent tree at grid position (starts from bottom left being (0, 0))
Given talent object should be configured
call this.AddTalent(1, 0, t)
-> method AddTalentCopy takes integer x, integer y, STKTalent_Talent data returns STKTalent_Talent
Creates a copy of given talent and adds it to the talent tree at grid position (starts from bottom left being (0, 0))
Given talent object should be configured
local STKTalent_Talent t = this.AddTalentCopy(1, 2, oldTalent)
-> method SaveTalentRankState takes nothing returns nothing
Call this at the end of Initialize when some Talents start with certain rank
call this.SaveTalentRankState()
[Event methods used inside Activate/Deactivate/Allocate/Deallocate/Requirements callback functions]
-> static method GetEventUnit takes nothing returns unit
Returns unit that owns the talent tree
local unit u = 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/deactivated/allocated/deallocated
local integer r = thistype.GetEventRank()
-> static method GetEventTalentTree takes nothing returns TalentTree
Returns the talent tree instance whose Talent is triggering the event
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.
If method is not called, requirements is considered fulfilled.
Example below requires "8 litres of milk".
thistype.SetTalentRequirementsResult("8 litres of milk")
===========================================================================================
=> Talent <================================================================================
Created inside Initialize method of a TalentTree, it contains all necessary data for showing/rendering and logic to apply on unit upon activation/deactivation/allocation. By itself, it does not keep track of ranks, one talent object corresponds to a single rank on its TalentTree. Each talent rank is its own Talent object, with its own icon, title, description, logic etc. When adding a Talent to a grid position where there is already a Talent assigned, it is added to its chain. Talents can be chained, and each new one added as a new rank and increasing their max rank.
[Public methods]
-> method SetName takes string name returns Talent
Name of the talent, visible on hover
-> method SetDescription takes string description returns Talent
Talent description, visible on hover
-> method SetIcon takes string name returns Talent
Sets both enabled and disabled icon, automatically appends name to "ReplaceableTextures\\CommandButtons\\BTN" and "DISBTN"
-> method SetIconEnabled takes string path returns Talent
Sets full path of enabled icon
-> method SetIconDisabled takes string path returns Talent
Sets full path of enabled icon
-> method SetOnAllocate takes code func returns Talent
Sets callback function that is triggered when talent point is allocated into this talent (before being activated)
-> method SetOnDeallocate takes code func returns Talent
Sets callback function that is triggered when talent point is removed from this talent, e.g. through Cancel button (before being activated)
-> method SetOnActivate takes code func returns Talent
Sets callback function that is triggered when talent is activated (Confirm is clicked and point is allocated)
-> method SetOnDeactivate takes code func returns Talent
Sets callback function that is triggered when talent is deactivated (When talent tree is fully reset)
-> method SetRequirements takes code func returns Talent
Sets callback function that can prevent allocating points into this talent with a custom message
-> method SetDependencies takes integer left, integer up, integer right, integer down returns Talent
A talent with dependency will require talent in that direction to have given amount of levels before points can be allocated into itself
-> method SetCost takes integer cost returns Talent
Cost is how much points the talent requires to be taken
-> method SetChainId takes integer saveId returns Talent
Important for save-loading, each talent chain should have its own id (unique per talent tree). Do not reuse old ids for new talents.
===========================================================================================
=> ITalentTreeView and TalentTreeView =====================================================
Connects the TalentTreeViewModel object with actual which make up the TalentTree screen, with Confirm, Cancel and Close buttons.
TalentTreeView contains a method that returns a ITalentTreeView instance for a single TalentTree.
Can write your own version if you already have existing UI creation code, have created UI through .fdf files and want to connect it to the system. Create a struct that implements ITalentTreeView and write a function that creates/hydrates and returns this struct. If player can only ever be shown a single Talent tree, it is okay to return the same set of frame references every time.
===========================================================================================
=> ITalentView and TalentView =============================================================
Connects the TalentViewModel object with actual frames which make up a single Talent. It will be manipulated as players click and hover, changing title, icons, enabling/disabling etc. Within setup, these structs are used to hook up the system with actual UI frames.
TalentView contains a method that returns a ITalentView instance for a single Talent.
Can write your own version but have a warning, when called, this function should always create a new set of frames, otherwise, only one Talent button will ever be visible.
===========================================================================================
=> ITalentSlot and TalentViewModel ========================================================
Contains behavior of a TalentView during events specified by ITalentSlot and handles rendering of the view based on data from a Talent instance.
Can write your own version but not recommended without a good knowledge of this system.
===========================================================================================
=> TalentTreeViewModel ====================================================================
Internal class that holds most of the system logic, connecting TalentView, TalentTree view, TalentTree and Talent instances and gluing it all together. It keeps track of, and handles player input (Talent clicking, Confirm/Cancel clicking, talent points change etc). It reacts to Talent changes and re-renders the view.
Does not have an interface because it's not recommended or intended to write your own version.
===========================================================================================
*/
/* Save Load API
-> function RegisterTalentTree takes integer id, TalentTreeFactory factoryMethod returns boolean
Should be called on init for every type of TalentTree implementation. This must be done so that system knows which TalentTree to
create for the unit when loading
call STKSaveLoad_RegisterTalentTree(1, Shepherd.LoadCreate)
-> function LoadForUnit takes unit owner, string bitString returns nothing
Takes the bitString string, creates TalentTree instances for the unit, loads the ranks and activates talents
call STKSaveLoad_LoadForUnit(udg_Hero, udg_BitString)
-> function SaveForUnit takes unit owner returns string
Takes the unit's TalentTree objects and creates a bit string with rank data
set udg_BitString = STKSaveLoad_SaveForUnit(udg_Hero)
-> function LoadForUnitEncoded takes unit owner, string saveCode returns nothing
Does the same thing as LoadForUnit, but it expect saveCode to be in base64 charset abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$#0123456789
call STKSaveLoad_LoadForUnitEncoded(udg_Hero, udg_SaveCode)
-> function SaveForUnitEncoded takes unit owner returns string
Does the same thing as SaveForUnit, but it returns a string encoded in base64 charset abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$#0123456789
set udg_SaveCode = STKSaveLoad_SaveForUnitEncoded(udg_Hero)
-> function Inspect takes string bitString returns nothing
Prints out the structure of bitString, used for check and debugging.
-> function InspectEncoded takes string saveCode returns nothing
Decodes the saveCode and prints out the structure of bitString, used for check and debugging.
*/
library STK initializer init requires STKTalentTreeViewModel, STKITalentSlot, STKITalentView, STKTalentView, STKTalentTreeView, STKConstants, STKStore, STKSaveLoad
globals
public constant integer MAX_TALENT_SLOTS = STKConstants_MAX_TALENT_SLOTS
public constant integer MAX_PLAYER_COUNT = STKConstants_MAX_PLAYER_COUNT
public constant integer PANEL_ID = 1
private STKStore Store
endglobals
// This function links talent framehandles like buttons, highlight textures, links to the system
function GenerateTalentSlot takes framehandle parent, integer index, integer panelId returns ITalentSlot
local ITalentView view
local STKTalentViewModel_TalentViewModel talentSlot
if (Store.GetTalentView(panelId, index) == 0) then
// The system is creating frames here. If need to link existing, Comment this line and implement lines below
call Store.SetTalentView(panelId, index, STKTalentView_GenerateTalentView(parent))
// Uncomment to link your own frames =?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=
// set view = STKTalentView_TalentView.create()
// set TalentSlotFrames[index] = view
// set view.buttonMain =
// set view.buttonImage =
// set view.tooltipBox =
// set view.tooltipText =
// set view.tooltipRank =
// set view.rankImage =
// set view.rankText =
// set view.highlight =
// set view.linkLeft =
// set view.linkUp =
// set view.linkRight =
// set view.linkDown =
// =?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=
endif
return STKTalentViewModel_TalentViewModel.create(Store.GetTalentView(panelId, index))
endfunction
// Use to add talent tree to a unit, only need to do it once
public function AssignTalentTree takes integer panelId, unit u, STKTalentTree_TalentTree createdTalentTree returns nothing
local integer playerId = GetPlayerId(GetOwningPlayer(u))
call Store.SetUnitTalentTree(panelId, u, createdTalentTree)
call createdTalentTree.Initialize()
call Store.GetPlayerTalentTreeViewModel(panelId, playerId).SetTree(createdTalentTree)
endfunction
// Use to show the talent tree view to a player
public function OpenTalentScreen takes player p returns nothing
local integer playerId = GetPlayerId(p)
local STKTalentTreeViewModel_TalentTreeViewModel ttvm
local integer i = PANEL_ID
loop
set ttvm = Store.GetPlayerTalentTreeViewModel(i, playerId)
exitwhen ttvm == 0
call ttvm.RenderTree()
set i = i + 1
endloop
endfunction
public function ToggleTalentsView takes player p returns nothing
local integer playerId = GetPlayerId(p)
if (Store.GetPlayerTalentTreeViewModel(PANEL_ID, playerId).IsWatched()) then
call Store.GetPlayerTalentTreeViewModel(PANEL_ID, playerId).Hide()
else
call Store.GetPlayerTalentTreeViewModel(PANEL_ID, playerId).RenderTree()
endif
endfunction
// Use to add or remove available talent points from a unit
public function UpdateUnitTalentPoints takes integer panelId, unit u, integer points returns nothing
local integer playerId = GetPlayerId(GetOwningPlayer(u))
local STKTalentTree_TalentTree tree = Store.GetUnitTalentTree(panelId, u)
local STKTalentTreeViewModel_TalentTreeViewModel ttvm
call tree.SetTalentPoints(tree.GetTalentPoints() + points)
set ttvm = Store.GetPlayerTalentTreeViewModel(panelId, playerId)
call ttvm.ResetTalentViewModels()
endfunction
// Use to reset a unit's talent tree
public function ResetUnitTalentTree takes integer panelId, unit u returns nothing
local STKTalentTree_TalentTree tree = Store.GetUnitTalentTree(panelId, u)
call tree.ResetTalentRankState()
call Store.GetPlayerTalentTreeViewModel(panelId, GetPlayerId(GetOwningPlayer(u))).ResetTalentViewModels()
endfunction
// Use to make a player watch unit's talent tree
public function PlayerLookAtUnitsTree takes integer panelId, player p, unit u returns nothing
local STKTalentTree_TalentTree tree = Store.GetUnitTalentTree(panelId, u)
call Store.GetPlayerTalentTreeViewModel(panelId, GetPlayerId(p)).SetTree(tree)
endfunction
function GameBeginningSetup takes nothing returns nothing
local integer i = 0
local STKTalentTreeViewModel_TalentTreeViewModel ttvm
// Initialize Talent UI for all players
// Can substitute with your own TalentTreeView generating function =?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=
// It creates the box, confirm cancel and close buttons
local ITalentTreeView talentTreeView = STKTalentTreeView_GenerateTalentTreeView(BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
loop
exitwhen i >= MAX_PLAYER_COUNT // Generating talent view for all 24 players. It's recommended to only do it for necessary playerIds
set ttvm = STKTalentTreeViewModel_TalentTreeViewModel.createSingleView(Player(i), talentTreeView, GenerateTalentSlot)
call Store.SetPlayerTalentTreeViewModel(PANEL_ID, i, ttvm)
// Talents are auto-positioned based on these params and talentree's column/row count ?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=
set ttvm.boxWidth = STKConstants_BOX_WIDTH
set ttvm.boxHeight = STKConstants_BOX_HEIGHT
set ttvm.sideMargin = STKConstants_SIDE_MARGIN
set ttvm.verticalMargin = STKConstants_VERTICAL_MARGIN
// =?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=?=
set i = i + 1
endloop
endfunction
function init takes nothing returns nothing
local timer tim = CreateTimer()
call TimerStart(tim, 1, false, function GameBeginningSetup)
set Store = STKStore.create()
call STKSaveLoad_Initialize(Store, AssignTalentTree)
endfunction
endlibrary
library STKConstants
globals
public constant integer MAX_ROWS = 7
public constant integer MAX_COLUMNS = 4
public constant integer MAX_TALENT_SLOTS = 28
public constant integer MAX_PLAYER_COUNT = 24
public constant string ACTIVE_LINK_TEXTURE = "Textures/Water00.blp"
public constant string INACTIVE_LINK_TEXTURE = "UI/Widgets/Console/Human/human-inventory-slotfiller.blp"
// "UI/Widgets/Glues/GlueScreen-Checkbox-Background.blp"
// "Textures/Water00.blp"
// "UI/Widgets/Console/Human/human-inventory-slotfiller.blp"
// TalentView Config ============================================================================================
// (Ignore this if using custom GenerateTalentView function)
// Talent button height width scaling
public constant real TTV_BUTTON_WIDTH = 0.035
public constant real TTV_BUTTON_HEIGHT = 0.035
// Talent-hover tooltip box
public constant real TOOLTIP_WIDTH = 0.28
public constant real TOOLTIP_HEIGHT = 0.16
public constant real TOOLTIP_TEXT_X = 0
public constant real TOOLTIP_TEXT_Y = 0
public constant real TOOLTIP_TEXT_WIDTH = 0.25
public constant real TOOLTIP_TEXT_HEIGHT = 0.13
// Small rank box for multi-rank talents
public constant real RANK_X = -0.0006
public constant real RANK_Y = 0.0015
public constant real RANK_SIZE_WIDTH = 0.020
public constant real RANK_SIZE_HEIGHT = 0.014
public constant real RANK_TEXT_SCALE = 0.7
public constant string RANK_TEXTURE = "UI/Widgets/Console/Human/human-transport-slot.blp"
// Highlight when talent is available to put points into
public constant real HIGHLIGHT_WIDTH = 0.03
public constant real HIGHLIGHT_HEIGHT = 0.03
public constant string HIGHLIGHT_TEXTURE = "UI/Widgets/Console/Human/CommandButton/human-activebutton.blp"
// Link between talents (dependency)
public constant real LINK_WIDTH = 0.006
public constant real LINK_INTERSECTION_SCALE = 0.80
// End TalentView Config =========================================================================================
// TalentTreeViewModel Config ====================================================================================
// Affects automatic positioning of the talents
public constant real BOX_WIDTH = 0.3
public constant real BOX_HEIGHT = 0.44
public constant real SIDE_MARGIN = 0.1
public constant real VERTICAL_MARGIN = 0.15
// End TalentTreeViewModel Config ================================================================================
// Save-Load Config ==============================================================================================
public constant integer MAX_SAVED_TALENT_POINTS = 32
public constant integer TALENTTREE_ID_THRESHOLD_SMALL = 16
public constant integer TALENTTREE_ID_THRESHOLD_LARGE = 64
public constant integer TALENT_ID_THRESHOLD_SMALL = 32
public constant integer TALENT_ID_THRESHOLD_LARGE = 128
public constant integer TALENT_RANK_THRESHOLD_SMALL = 16
public constant integer TALENT_RANK_THRESHOLD_MEDIUM = 32
public constant integer TALENT_RANK_THRESHOLD_LARGE = 64
public constant integer TALENT_RANK_THRESHOLD_EXTRA = 256
// End Save-Load Config ==========================================================================================
endglobals
endlibrary
library STKITalentView
interface ITalentView
framehandle buttonMain // Button frame for allocating points into a Talent
framehandle buttonImage // Image frame that shows Talent icon
framehandle tooltipBox // Window frame that shows/hides talent hover tooltip
framehandle tooltipText // Text frame that shows Talent title and description
framehandle tooltipRank // Text frame that shows current Talent rank within tooltip
framehandle rankImage // Image background of current rank on the Talent icon
framehandle rankText // Text frame showing current rank on the Talent icon
framehandle highlight // Image frame shown when button can be clicked
framehandle linkLeft // Image frame left dependency
framehandle linkUp // Image frame up dependency
framehandle linkRight // Image frame right dependency
framehandle linkDown // Image frame down dependency
framehandle linkIntersection // Image frame shown when Talent itself acts as a dependency link
endinterface
endlibrary
library STKITalentTreeView
interface ITalentTreeView
framehandle box // Window frame that is shown and hidden when player opens and closes their Talent Tree/s.
framehandle container // Window frame that envelops talent tree itself, acts as a boundary for Talent views. Same size as TalentTree background image.
framehandle containerImage // Image frame that shows TalentTree background image. The system will attempt to change background image according to the loaded TalentTree object.
framehandle title // Text frame that shows Title of loaded TalentTree.
framehandle confirmButtonMain // Button frame whose click will apply temporary talents and activate them.
framehandle cancelButtonMain // Button frame whose click will reset temporary talents.
framehandle closeButton // Button frame whose click will hide Talent Tree window.
endinterface
endlibrary
library STKITalentSlot
interface ITalentSlot
method GetButtonFrame takes nothing returns framehandle // Used for setting up Click triggers
method SetVisible takes boolean isVisible returns nothing // Showing or hiding whole talent view. [LOCAL CODE]
method GetTalent takes nothing returns STKTalent_Talent // Returns its Talent model
method SetTalent takes STKTalent_Talent talent returns nothing // Makes this TalentSlot point to given Talent
method GetRank takes nothing returns integer // Returns rank currently shown in this slot's view
method SetRank takes integer rank returns nothing // Sets rank currently shown in this slot's view
method GetIndex takes nothing returns integer // Not used
method GetWatcher takes nothing returns player // Not used
method SetWatcher takes player watcher returns nothing // Sets watcher player of this TalentSlot. Subsequent local code needs this value
method GetState takes nothing returns integer // Returns TalentSlot state (state is internal representation of the TalentSlot)
method SetState takes integer state returns nothing // Sets TalentSlot state (state is internal representation of the TalentSlot)
method GetErrorText takes nothing returns string // Returns currently shown error text
method SetErrorText takes string text returns nothing // Sets currently shown error text
// Changes position of this TalentSlot's TalentView frames according to the grid position of its Talent model
method MoveTo takes framepointtype point, framehandle relative, framepointtype relativePoint, real x, real y, real linkWidth, real linkHeight returns nothing
// Used to update this slot's view according to dependencies of its Talent model
method RenderLinks takes string dependencyLeft, string dependencyUp, string dependencyRight, string dependencyDown returns nothing
endinterface
endlibrary
library STKTalentTreeView requires STKTalentViewModel
public struct TalentTreeView extends ITalentTreeView
framehandle box
framehandle container
framehandle containerImage
framehandle title
framehandle closeButton
framehandle confirmButtonMain
// framehandle confirmButtonImage
framehandle cancelButtonMain
// framehandle cancelButtonImage
endstruct
public function GenerateTalentTreeView takes framehandle parent returns ITalentTreeView
// ========= SETUP ===============================================================================
local real boxWidth = 0.3
local real boxHeight = 0.44
local real boxSideMargin = 0.1
local real boxVerticalMargin = 0.1
local real boxY = 0.394
local real boxX = 0.134
local string boxText = "X"
local real cancelWidth = 0.12
local real cancelHeight = 0.03
local real cancelY = 0.01
local real cancelX = 0
local string cancelText = "Cancel"
local real confirmWidth = 0.12
local real confirmHeight = 0.03
local real confirmY = 0.01
local real confirmX = 0
local string confirmText = "Confirm"
local real closeButtonWidth = 0.03
local real closeButtonHeight = 0.03
local real closeButtonY = 0.01 // 0.394
local real closeButtonX = 0.01 // 0.134
local string closeButtonText = "X"
// ========= ENDSETUP ============================================================================
// Creating the view
local ITalentTreeView view = TalentTreeView.create()
local framehandle box = BlzCreateFrame("SuspendDialog", parent, 0, 0)
local framehandle title = BlzGetFrameByName("SuspendTitleText", 0)
local framehandle closeButton = BlzGetFrameByName("SuspendDropPlayersButton", 0)
local framehandle closeText = BlzGetFrameByName("SuspendDropPlayersButtonText", 0)
local framehandle confirmButton = BlzCreateFrame("ScriptDialogButton", box, 0, 0)
local framehandle confirmTextf = BlzGetFrameByName("ScriptDialogButtonText", 0)
local framehandle cancelButton = BlzCreateFrame("ScriptDialogButton", box, 0, 0)
local framehandle cancelTextf = BlzGetFrameByName("ScriptDialogButtonText", 0)
call BlzFrameClearAllPoints(box)
call BlzFrameSetAbsPoint(box, FRAMEPOINT_CENTER, 0.35, 0.34)
call BlzFrameSetSize(box, boxWidth, boxHeight)
call BlzFrameSetText(title, "Talent Tree")
call BlzFrameClearAllPoints(closeButton)
call BlzFrameSetPoint(closeButton, FRAMEPOINT_TOPRIGHT, box, FRAMEPOINT_TOPRIGHT, closeButtonX, closeButtonY)
call BlzFrameSetSize(closeButton, closeButtonWidth, closeButtonHeight)
call BlzFrameSetText(closeButton, closeButtonText)
call BlzFrameClearAllPoints(confirmButton)
call BlzFrameSetPoint(confirmButton, FRAMEPOINT_BOTTOMRIGHT, box, FRAMEPOINT_BOTTOM, confirmX, confirmY)
call BlzFrameSetSize(confirmButton, confirmWidth, confirmHeight)
call BlzFrameSetText(confirmButton, confirmText)
call BlzFrameClearAllPoints(cancelButton)
call BlzFrameSetPoint(cancelButton, FRAMEPOINT_BOTTOMLEFT, box, FRAMEPOINT_BOTTOM, cancelX, cancelY)
call BlzFrameSetSize(cancelButton, cancelWidth, cancelHeight)
call BlzFrameSetText(cancelButton, cancelText)
set view.box = box
set view.container = box
set view.title = title
set view.closeButton = closeButton
set view.confirmButtonMain = confirmButton
set view.cancelButtonMain = cancelButton
return view
endfunction
endlibrary
library STKTalentView requires STKTalentViewModel, STKConstants
public struct TalentView extends ITalentView
framehandle buttonMain
framehandle buttonImage
framehandle tooltipBox
framehandle tooltipText
framehandle tooltipRank
framehandle rankImage
framehandle rankText
framehandle highlight
framehandle linkLeft
framehandle linkUp
framehandle linkRight
framehandle linkDown
framehandle linkIntersection
endstruct
function SetUpLink takes framehandle frame, framehandle parent, real width returns nothing
call BlzFrameClearAllPoints(frame)
call BlzFrameSetPoint(frame, FRAMEPOINT_CENTER, parent, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(frame, width, width)
call BlzFrameSetTexture(frame, "UI/Widgets/Console/Human/human-inventory-slotfiller.blp", 0, true)
call BlzFrameSetLevel(frame, 1)
call BlzFrameSetVisible(frame, false)
endfunction
public function GenerateTalentView takes framehandle parent returns ITalentView
// ========= SETUP ===============================================================================
local real buttonWidth = STKConstants_TTV_BUTTON_WIDTH
local real buttonHeight = STKConstants_TTV_BUTTON_HEIGHT
local string buttonTexture = "ReplaceableTextures/CommandButtons/BTNPeasant.blp"
local real tooltipWidth = STKConstants_TOOLTIP_WIDTH
local real tooltipHeight = STKConstants_TOOLTIP_HEIGHT
local real tooltipTextX = STKConstants_TOOLTIP_TEXT_X
local real tooltipTextY = STKConstants_TOOLTIP_TEXT_Y
local real tooltipTextWidth = STKConstants_TOOLTIP_TEXT_WIDTH
local real tooltipTextHeight = STKConstants_TOOLTIP_TEXT_HEIGHT
local string tooltipDefaultText = "Default talent name \n\nDefault talent description"
local real rankX = STKConstants_RANK_X
local real rankY = STKConstants_RANK_Y
local real rankSizeWidth = STKConstants_RANK_SIZE_WIDTH
local real rankSizeHeight = STKConstants_RANK_SIZE_HEIGHT
local real rankTextScale = STKConstants_RANK_TEXT_SCALE
local string rankTexture = STKConstants_RANK_TEXTURE
local real highlightWidth = STKConstants_HIGHLIGHT_WIDTH
local real highlightHeight = STKConstants_HIGHLIGHT_HEIGHT
local string highlightTexture = STKConstants_HIGHLIGHT_TEXTURE
local real linkWidth = STKConstants_LINK_WIDTH
local real linkIntersectionScale = STKConstants_LINK_INTERSECTION_SCALE
// ========= ENDSETUP ============================================================================
// Creating the view
local TalentView view = TalentView.create()
local framehandle linkLeft = BlzCreateFrameByType("BACKDROP", "LeftLink", parent, "", 0)
local framehandle linkUp = BlzCreateFrameByType("BACKDROP", "UpLink", parent, "", 0)
local framehandle linkRight = BlzCreateFrameByType("BACKDROP", "RightLink", parent, "", 0)
local framehandle linkDown = BlzCreateFrameByType("BACKDROP", "DownLink", parent, "", 0)
local framehandle linkIntersection = BlzCreateFrameByType("BACKDROP", "DownLink", parent, "", 0)
local framehandle highlight = BlzCreateFrameByType("BACKDROP", "AvailableImage", parent, "", 0)
local framehandle buttonMain = BlzCreateFrame("ScoreScreenBottomButtonTemplate", parent, 0, 0)
local framehandle buttonImage = BlzGetFrameByName("ScoreScreenButtonBackdrop", 0)
local framehandle toolBox = BlzCreateFrame("ListBoxWar3", buttonMain, 0, 0)
local framehandle toolText = BlzCreateFrameByType("TEXT", "StandardInfoTextTemplate", toolBox, "StandardInfoTextTemplate", 0)
local framehandle rankImage = BlzCreateFrameByType("BACKDROP", "Counter", buttonMain, "", 0)
local framehandle rankText = BlzCreateFrameByType("TEXT", "FaceFrameTooltip", buttonMain, "", 0)
local framehandle toolRank = BlzCreateFrameByType("TEXT", "FaceFrameTooltip", toolBox, "", 0)
call BlzFrameSetTooltip(buttonMain, toolBox)
call BlzFrameSetTextAlignment(rankText, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE)
call BlzFrameSetTextAlignment(toolRank, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_RIGHT)
call BlzFrameSetPoint(buttonMain, FRAMEPOINT_CENTER, parent, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(buttonMain, buttonWidth, buttonHeight)
call BlzFrameSetLevel(buttonMain, 2)
call BlzFrameSetPoint(toolBox, FRAMEPOINT_TOPLEFT, parent, FRAMEPOINT_TOPRIGHT, 0, 0)
call BlzFrameSetSize(toolBox, tooltipWidth, tooltipHeight)
call BlzFrameClearAllPoints(toolText)
call BlzFrameSetPoint(toolText, FRAMEPOINT_CENTER, toolBox, FRAMEPOINT_CENTER, tooltipTextY, tooltipTextY)
call BlzFrameSetSize(toolText, tooltipTextWidth, tooltipTextHeight)
call BlzFrameSetText(toolText, tooltipDefaultText)
call BlzFrameSetPoint(rankImage, FRAMEPOINT_BOTTOMRIGHT, buttonMain, FRAMEPOINT_BOTTOMRIGHT, rankX, rankY)
call BlzFrameSetSize(rankImage, rankSizeWidth, rankSizeHeight)
call BlzFrameSetTexture(rankImage, rankTexture, 0, true)
call BlzFrameClearAllPoints(rankText)
call BlzFrameSetPoint(rankText, FRAMEPOINT_TOPLEFT, rankImage, FRAMEPOINT_TOPLEFT, 0, 0)
call BlzFrameSetPoint(rankText, FRAMEPOINT_BOTTOMRIGHT, rankImage, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
call BlzFrameSetText(rankText, "0")
call BlzFrameSetTextAlignment(rankText, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE)
call BlzFrameSetScale(rankText, rankTextScale)
call BlzFrameSetPoint(highlight, FRAMEPOINT_CENTER, buttonMain, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(highlight, highlightWidth, highlightHeight)
call BlzFrameSetTexture(highlight, highlightTexture, 0, true)
call BlzFrameSetTexture(buttonImage, buttonTexture, 0, true)
call BlzFrameClearAllPoints(toolRank)
call BlzFrameSetPoint(toolRank, FRAMEPOINT_TOP, toolBox, FRAMEPOINT_TOP, 0.0, -0.015)
call BlzFrameSetSize(toolRank, tooltipWidth - 0.03, tooltipHeight - 0.03)
call BlzFrameSetText(toolRank, "Rank 1/3")
call SetUpLink(linkLeft, buttonMain, linkWidth)
call SetUpLink(linkUp, buttonMain, linkWidth)
call SetUpLink(linkRight, buttonMain, linkWidth)
call SetUpLink(linkDown, buttonMain, linkWidth)
call SetUpLink(linkIntersection, buttonMain, linkWidth)
call BlzFrameSetScale(linkIntersection, linkIntersectionScale)
// EXPERIEMENTAL
call BlzFrameSetVisible(buttonMain, false)
call BlzFrameSetVisible(highlight, false)
set view.highlight = highlight
set view.buttonMain = buttonMain
set view.buttonImage = buttonImage
set view.tooltipBox = toolBox
set view.tooltipText = toolText
set view.tooltipRank = toolRank
set view.rankImage = rankImage
set view.rankText = rankText
set view.linkLeft = linkLeft
set view.linkUp = linkUp
set view.linkRight = linkRight
set view.linkDown = linkDown
set view.linkIntersection = linkIntersection
return view
endfunction
endlibrary
library STKTalentTree initializer init requires STKTalent, STKConstants
globals
public constant integer MAX_ROWS = STKConstants_MAX_ROWS
public constant integer MAX_COLUMNS = STKConstants_MAX_COLUMNS
public constant integer MAX_TALENTS = STKConstants_MAX_TALENT_SLOTS
// Event variables
public unit EventUnit
public integer EventTalentTree
public integer EventTalent
public integer EventRank
public integer EventTalentIndex
// Event result
public string ResultTalentRequirements
endglobals
public struct TalentTree
public unit ownerUnit
public player ownerPlayer
public string icon = ""
private integer id
public string title = ""
public integer talentPoints = 0
public string backgroundImage = ""
public STKTalent_Talent array talents[MAX_TALENTS]
public integer array rankState[MAX_TALENTS]
public integer array tempRankState [MAX_TALENTS]
private boolean isDirty = false
private integer array linkTalentIndex[MAX_TALENTS]
public integer array chainIdTalentIndex [MAX_TALENTS]
private integer linkTalentCount = 0
public integer rows = MAX_ROWS
public integer columns = MAX_COLUMNS
public integer maxTalents = MAX_TALENTS
stub method Initialize takes nothing returns nothing
endmethod
stub method SetTalentPoints takes integer points returns nothing
set this.talentPoints = points
endmethod
stub method GetTalentPoints takes nothing returns integer
return this.talentPoints
endmethod
stub method GetTitle takes nothing returns string
return this.title + " (" + I2S(this.GetTalentPoints()) + " points available)"
endmethod
private method AddTalentRaw takes integer x, integer y, STKTalent_Talent talent returns STKTalent_Talent
local integer index = x + y * this.columns
local STKTalent_Talent existing
// If talent already exists
if (this.talents[index] != 0) then
// If talent is multirank, handle it differently
if (this.talents[index].maxRank > 1) then
call this.talents[index].RemoveTalentFinalDescription()
call this.talents[index].SetLastRank(talent)
call this.talents[index].SetTalentFinalDescription()
call this.talents[index].UpdateMaxRank()
else
call this.talents[index].SetLastRank(talent)
call this.talents[index].SetTalentFinalDescription()
call this.talents[index].UpdateMaxRank()
endif
else
set this.talents[index] = talent
set this.rankState[index] = 0
call this.talents[index].UpdateMaxRank()
endif
if (talent.isLink) then
set this.linkTalentIndex[this.linkTalentCount] = index
set this.linkTalentCount = this.linkTalentCount + 1
endif
if (this.tempRankState[index] != talent.startingLevel) then
set this.isDirty = true
endif
set this.tempRankState[index] = talent.startingLevel
return talent
endmethod
// Grid starts from bottom left being (0, 0)
method AddTalent takes integer x, integer y, STKTalent_Talent talent returns STKTalent_Talent
return this.AddTalentRaw(x, y, talent)
endmethod
// Grid starts from bottom left being (0, 0)
method AddTalentCopy takes integer x, integer y, STKTalent_Talent data returns STKTalent_Talent
local STKTalent_Talent talent = STKTalent_Talent.createCopy(data)
return this.AddTalentRaw(x, y, talent)
endmethod
method CreateTalent takes nothing returns STKTalent_Talent
local STKTalent_Talent talent = STKTalent_Talent.create()
call talent.SetCost(1)
return talent
endmethod
method CreateTalentCopy takes STKTalent_Talent data returns STKTalent_Talent
local STKTalent_Talent talent = STKTalent_Talent.createCopy(data)
return talent
endmethod
method GetId takes nothing returns integer
return this.id
endmethod
// Talent callbacks =======================================================================================
method ActivateTalent takes STKTalent_Talent talent, integer rank returns nothing
set EventUnit = this.ownerUnit
set EventTalentTree = this
set EventTalent = talent
set EventRank = rank
if (talent.onActivate != null) then
call TriggerEvaluate(talent.onActivate)
endif
endmethod
method DeactivateTalent takes STKTalent_Talent talent, integer rank returns nothing
set EventUnit = this.ownerUnit
set EventTalentTree = this
set EventTalent = talent
set EventRank = rank
if (talent.onActivate != null) then
call TriggerEvaluate(talent.onDeactivate)
endif
endmethod
method AllocateTalent takes STKTalent_Talent talent returns nothing
set EventUnit = this.ownerUnit
set EventTalentTree = this
set EventTalent = talent
set EventRank = talent.GetTalentRank()
if (talent.onAllocate != null) then
call TriggerEvaluate(talent.onAllocate)
endif
endmethod
method DeallocateTalent takes STKTalent_Talent talent returns nothing
set EventUnit = this.ownerUnit
set EventTalentTree = this
set EventTalent = talent
set EventRank = talent.GetTalentRank()
if (talent.onDeallocate != null) then
call TriggerEvaluate(talent.onDeallocate)
endif
endmethod
method ActivateTalentRecursively takes STKTalent_Talent talent, integer count, integer initialRank returns nothing
if (talent.previousRank != 0 and count > 1) then
call this.ActivateTalentRecursively(talent.previousRank, count - 1, initialRank)
endif
call this.ActivateTalent(talent, initialRank + count)
endmethod
method DeactivateTalentRecursively takes STKTalent_Talent talent, integer count, integer targetRank returns nothing
if (talent.previousRank != 0 and count > 1) then
call this.DeactivateTalentRecursively(talent.previousRank, count - 1, targetRank)
endif
call this.DeactivateTalent(talent, targetRank + count)
endmethod
method CalculateTalentRequirements takes STKTalent_Talent talent, integer index returns string
set EventUnit = this.ownerUnit
set EventTalentTree = this
set EventTalent = talent
set EventTalentIndex = index
set ResultTalentRequirements = null
if (talent.requirements != null) then
call TriggerEvaluate(talent.requirements)
endif
return ResultTalentRequirements
endmethod
// End Talent callbacks =======================================================================================
method SaveTalentRankState takes nothing returns nothing
local integer i = 0
if (this.isDirty == false) then
return
endif
loop
exitwhen i == this.maxTalents
if (this.talents[i] != 0) then
if (this.rankState[i] != this.tempRankState[i]) then
if (this.talents[i].previousRank != 0) then
call this.ActivateTalentRecursively(this.talents[i].previousRank, this.tempRankState[i] - this.rankState[i], this.rankState[i])
else
call this.ActivateTalentRecursively(this.talents[i], this.tempRankState[i] - this.rankState[i], this.rankState[i])
endif
set this.rankState[i] = this.tempRankState[i]
endif
endif
set i = i + 1
endloop
call this.ResetTempRankState()
endmethod
method ResetTempRankState takes nothing returns nothing
local integer i = 0
local integer j = 0
local STKTalent_Talent talent
local STKTalent_Talent talent2
if (this.isDirty == false) then
return
endif
loop
exitwhen i == this.maxTalents
set talent = this.talents[i]
if (talent != 0 and this.rankState[i] != this.tempRankState[i]) then
set j = this.tempRankState[i]
if (talent.maxRank == 1 and j > this.rankState[i]) then
call this.DeallocateTalent(talent)
call this.SetTalentPoints(this.GetTalentPoints() + talent.cost)
else
set talent2 = talent.previousRank
loop
// TODO
exitwhen j <= this.rankState[i] or talent2 == 0
call this.DeallocateTalent(talent2)
call this.SetTalentPoints(this.GetTalentPoints() + talent2.cost)
set talent2 = talent2.previousRank
set j = j - 1
endloop
endif
endif
// Loop until zero level
set j = this.tempRankState[i]
loop
exitwhen j < 0
// Reset the UI to lowest
if (talent.previousRank != 0) then
set talent = talent.previousRank
endif
set j = j - 1
endloop
// Then level it up back to the current rankState
set j = 1
loop
exitwhen j > this.rankState[i]
// Reset the UI towards current
if (talent.nextRank != 0) then
set talent = talent.nextRank
endif
set j = j + 1
endloop
set this.talents[i] = talent
// EXPERIMENTAL
set this.tempRankState[i] = this.rankState[i]
set i = i + 1
endloop
set this.isDirty = false
endmethod
method ResetTalentRankState takes nothing returns nothing
local integer i = 0
loop
exitwhen i == this.maxTalents
if (this.talents[i] != 0) then
if (this.rankState[i] != 0) then
if (this.talents[i].previousRank != 0) then
call this.DeactivateTalentRecursively(this.talents[i].previousRank, this.rankState[i] - 0, 0)
else
call this.DeactivateTalentRecursively(this.talents[i], this.rankState[i] - 0, 0)
endif
set this.rankState[i] = 0
endif
endif
set i = i + 1
endloop
set this.isDirty = true
call this.ResetTempRankState()
endmethod
method ApplyTalentTemporary takes integer index returns nothing
local STKTalent_Talent talent = this.talents[index]
// We assume there is definitely a talent on this index
set this.isDirty = true
if (this.tempRankState[index] < talent.maxRank) then
call this.SetTalentPoints(this.GetTalentPoints() - talent.cost)
set this.tempRankState[index] = this.tempRankState[index] + 1
// Fire talent allocate event
call this.AllocateTalent(talent)
if (talent.nextRank != 0) then
set this.talents[index] = talent.nextRank
endif
else
// Rollback
call this.SetTalentPoints(this.GetTalentPoints() + talent.cost)
set this.tempRankState[index] = this.tempRankState[index] - 1
endif
endmethod
method ApplyTalentChainTemporary takes integer chainId, integer rank returns nothing
local integer index = this.chainIdTalentIndex[chainId]
set this.isDirty = true
set this.tempRankState[index] = rank
endmethod
method CheckDependencyKey takes integer requiredLevel, integer index, integer depIndex returns string
local STKTalent_Talent talent = this.talents[index]
local STKTalent_Talent talentDependency = this.talents[depIndex]
local string errorText = null
if (requiredLevel == 0 or talent == 0) then
set errorText = null
elseif (requiredLevel == -1) then
set errorText = ""
endif
if (this.tempRankState[depIndex] < requiredLevel) then
set errorText = errorText + this.talents[depIndex].name
if (talentDependency.maxRank > 1) then
set errorText = errorText + " (" + I2S(requiredLevel) + ")"
endif
endif
return errorText
endmethod
method CheckDependencyKeyLeft takes integer requiredLevel, integer index returns string
local integer depIndex = index - 1
return this.CheckDependencyKey(requiredLevel, index, depIndex)
endmethod
method CheckDependencyKeyUp takes integer requiredLevel, integer index returns string
local integer depIndex = index + this.columns
return this.CheckDependencyKey(requiredLevel, index, depIndex)
endmethod
method CheckDependencyKeyRight takes integer requiredLevel, integer index returns string
local integer depIndex = index + 1
return this.CheckDependencyKey(requiredLevel, index, depIndex)
endmethod
method CheckDependencyKeyDown takes integer requiredLevel, integer index returns string
local integer depIndex = index - this.columns
return this.CheckDependencyKey(requiredLevel, index, depIndex)
endmethod
method UpdateLinkState takes integer index returns integer
local STKTalent_Talent talent = this.talents[index]
local integer depIndex = 0
local integer reqLevel = 0
local integer i = index
local integer level = 0
if (talent != 0 and talent.isLink) then
if (talent.dependencyLeft > 0) then
set depIndex = i - 1
set reqLevel = talent.dependencyLeft
if (this.talents[depIndex] != 0 and this.talents[depIndex].isLink) then
call this.UpdateLinkState(depIndex)
endif
if (this.CheckDependencyKey(reqLevel, i, depIndex) == null) then
set level = level + 1
endif
endif
if (talent.dependencyUp > 0) then
set depIndex = i + this.columns
set reqLevel = talent.dependencyUp
if (this.talents[depIndex] != 0 and this.talents[depIndex].isLink) then
call this.UpdateLinkState(depIndex)
endif
if (this.CheckDependencyKey(reqLevel, i, depIndex) == null) then
set level = level + 1
endif
endif
if (talent.dependencyRight > 0) then
set depIndex = i + 1
set reqLevel = talent.dependencyRight
if (this.talents[depIndex] != 0 and this.talents[depIndex].isLink) then
call this.UpdateLinkState(depIndex)
endif
if (this.CheckDependencyKey(reqLevel, i, depIndex) == null) then
set level = level + 1
endif
endif
if (talent.dependencyDown > 0) then
set depIndex = i - this.columns
set reqLevel = talent.dependencyDown
if (this.talents[depIndex] != 0 and this.talents[depIndex].isLink) then
call this.UpdateLinkState(depIndex)
endif
if (this.CheckDependencyKey(reqLevel, i, depIndex) == null) then
set level = level + 1
endif
endif
set this.rankState[i] = level
set this.tempRankState[i] = level
endif
return level
endmethod
method UpdateLinkStates takes nothing returns nothing
local STKTalent_Talent talent
local integer i = 0
local integer level = 0
loop
exitwhen i == this.linkTalentCount
if (this.linkTalentIndex[i] != 0) then
call this.UpdateLinkState(this.linkTalentIndex[i])
endif
set i = i + 1
endloop
endmethod
method UpdateChainIdTalentIndex takes nothing returns nothing
local integer i = 0
local STKTalent_Talent t
loop
exitwhen i == this.maxTalents
set t = this.talents[i]
if (t != 0) then
set chainIdTalentIndex[t.GetChainId()] = i
endif
set i = i + 1
endloop
endmethod
// [TalentDepType.up]: (index: number, cols: number) => [index + cols, 1],
// [TalentDepType.right]: (index: number, cols: number) => [index + 1, 0],
// [TalentDepType.down]: (index: number, cols: number) => [index - cols, 1],
// Event methods ==========================================================
static method GetEventUnit takes nothing returns unit
return EventUnit
endmethod
static method GetEventTalent takes nothing returns STKTalent_Talent
return EventTalent
endmethod
static method GetEventTalentIndex takes nothing returns integer
return EventTalentIndex
endmethod
static method GetEventRank takes nothing returns integer
return EventRank
endmethod
static method GetEventTalentTree takes nothing returns TalentTree
return EventTalentTree
endmethod
static method SetTalentRequirementsResult takes string requirements returns nothing
set ResultTalentRequirements = requirements
endmethod
// End Event methods ======================================================
static method create takes unit owner returns TalentTree
local TalentTree tt = TalentTree.allocate()
set tt.ownerUnit = owner
set tt.ownerPlayer = GetOwningPlayer(owner)
return tt
endmethod
method SetIdColumnsRows takes integer id, integer columns, integer rows returns nothing
set this.id = id
set this.columns = columns
set this.rows = rows
endmethod
method onDestroy takes nothing returns nothing
set this.ownerUnit = null
set this.ownerPlayer = null
endmethod
endstruct
private function init takes nothing returns nothing
endfunction
endlibrary
library STKTalent initializer init
globals
private boolexpr defaultCallback
endglobals
private function ReturnsTrue takes nothing returns boolean
return true
endfunction
function interface Callback takes nothing returns boolean
public struct Talent
public string name
public string description
public string iconEnabled
public string iconDisabled
public trigger onAllocate //= defaultCallback
public trigger onDeallocate //= defaultCallback
public trigger onActivate //= defaultCallback
public trigger onDeactivate //= defaultCallback
public trigger requirements //= defaultCallback
public integer dependencyLeft = 0
public integer dependencyUp = 0
public integer dependencyRight = 0
public integer dependencyDown = 0
public integer startingLevel = 0
public integer cost = 1 // Default cost is 1
public boolean isLink = false
public boolean isFinalDescription = false
public Talent nextRank = 0
public Talent previousRank = 0
public integer maxRank = 0
private integer rank = 0
private integer chainId = 0
// Tag?: any;
static method create takes nothing returns Talent
local Talent this = Talent.allocate()
return this
endmethod
static method createCopy takes Talent talent returns Talent
local Talent new = Talent.allocate()
set new.name = talent.name
set new.description = talent.description
set new.iconEnabled = talent.iconEnabled
set new.iconDisabled = talent.iconDisabled
set new.onAllocate = talent.onAllocate
set new.onDeallocate = talent.onDeallocate
set new.onActivate = talent.onActivate
set new.onDeactivate = talent.onDeactivate
set new.requirements = talent.requirements
set new.dependencyLeft = talent.dependencyLeft
set new.dependencyUp = talent.dependencyUp
set new.dependencyRight = talent.dependencyRight
set new.dependencyDown = talent.dependencyDown
set new.startingLevel = talent.startingLevel
set new.cost = talent.cost
set new.isLink = talent.isLink
set new.chainId = talent.chainId
return new
endmethod
method SetName takes string name returns Talent
set this.name = name
return this
endmethod
method SetDescription takes string description returns Talent
set this.description = description
return this
endmethod
method SetIcon takes string name returns Talent
set this.iconEnabled = "ReplaceableTextures\\CommandButtons\\BTN" + name
set this.iconDisabled = "ReplaceableTextures\\CommandButtonsDisabled\\DISBTN" + name
return this
endmethod
method SetIconEnabled takes string path returns Talent
set this.iconEnabled = path
return this
endmethod
method SetIconDisabled takes string path returns Talent
set this.iconDisabled = path
return this
endmethod
method SetOnAllocate takes code func returns Talent
set this.onAllocate = CreateTrigger()
call TriggerAddCondition(this.onAllocate, Condition(func))
return this
endmethod
method SetOnDeallocate takes code func returns Talent
set this.onDeallocate = CreateTrigger()
call TriggerAddCondition(this.onDeallocate, Condition(func))
return this
endmethod
method SetOnActivate takes code func returns Talent
set this.onActivate = CreateTrigger()
call TriggerAddCondition(this.onActivate, Condition(func))
return this
endmethod
method SetOnDeactivate takes code func returns Talent
set this.onDeactivate = CreateTrigger()
call TriggerAddCondition(this.onDeactivate, Condition(func))
return this
endmethod
method SetRequirements takes code func returns Talent
set this.requirements = CreateTrigger()
call TriggerAddCondition(this.requirements, Condition(func))
return this
endmethod
method SetDependencies takes integer left, integer up, integer right, integer down returns Talent
set this.dependencyLeft = left
set this.dependencyUp = up
set this.dependencyRight = right
set this.dependencyDown = down
return this
endmethod
method SetCost takes integer cost returns Talent
set this.cost = cost
return this
endmethod
method SetChainId takes integer chainId returns Talent
set this.chainId = chainId
return this
endmethod
method GetChainId takes nothing returns integer
return this.chainId
endmethod
method GetNextRank takes nothing returns Talent
if (this.nextRank != 0) then
return this.nextRank
endif
return this
endmethod
method SetNextRank takes Talent talent returns nothing
if (this == talent) then
call BJDebugMsg("ERROR - ASSIGNING SAME TALENT " + I2S(this))
endif
set this.nextRank = talent
set talent.previousRank = this
endmethod
method SetLastRank takes Talent talent returns nothing
if (this.nextRank != 0) then
call this.nextRank.SetLastRank(talent)
return
endif
call this.SetNextRank(talent)
endmethod
method UpdateMaxRank takes nothing returns nothing
local thistype talent
local integer maxRank = 1
set talent = this
loop
set talent = talent.previousRank
exitwhen talent == 0 or talent.isFinalDescription
set maxRank = maxRank + 1
endloop
set talent = this
loop
set talent = talent.nextRank
exitwhen talent == 0 or talent.isFinalDescription
set maxRank = maxRank + 1
endloop
set this.maxRank = maxRank
set talent = this
loop
set talent = talent.previousRank
exitwhen talent == 0
set talent.maxRank = maxRank
endloop
set talent = this
loop
set talent = talent.nextRank
exitwhen talent == 0
set talent.maxRank = maxRank
endloop
endmethod
method GetTalentRank takes nothing returns integer
return this.rank
endmethod
method CreateTalentFinalDescription takes nothing returns STKTalent_Talent
local STKTalent_Talent talent = STKTalent_Talent.create()
set talent.name = this.name
set talent.description = this.description
set talent.iconEnabled = this.iconEnabled
set talent.iconDisabled = this.iconDisabled
set talent.dependencyLeft = this.dependencyLeft
set talent.dependencyUp = this.dependencyUp
set talent.dependencyRight = this.dependencyRight
set talent.dependencyDown = this.dependencyDown
set talent.maxRank = 0
set talent.chainId = this.chainId
set talent.isFinalDescription = true
call this.SetNextRank(talent)
set talent.maxRank = talent.maxRank
return talent
endmethod
method SetTalentFinalDescription takes nothing returns nothing
local thistype finalDesc
if (this.nextRank != 0 and this.nextRank.isFinalDescription == false) then
call this.nextRank.SetTalentFinalDescription()
return
endif
set finalDesc = this.CreateTalentFinalDescription()
call this.SetNextRank(finalDesc)
endmethod
method RemoveTalentFinalDescription takes nothing returns nothing
if (this.nextRank != 0 and this.nextRank.isFinalDescription) then
set this.nextRank = 0
return
elseif (this.nextRank != 0) then
call this.nextRank.RemoveTalentFinalDescription()
endif
endmethod
method onDestroy takes nothing returns nothing
endmethod
endstruct
private function init takes nothing returns nothing
set defaultCallback = Condition(function ReturnsTrue)
endfunction
endlibrary
library STKTalentTreeViewModel requires STKITalentSlot
globals
private constant integer FRAME_INDEX_KEY = 0
private constant integer FRAME_PANEL_ID = 0
private integer array Instances[300]
private integer InstanceCount = 0
private hashtable FrameIndex = InitHashtable()
private trigger ClickTrigger = null
private trigger ConfirmTrigger = null
private trigger CancelTrigger = null
private trigger CloseTrigger = null
// This block is used to update view without lag =============================================
private constant integer MAX_PLAYER_ID = 24
private STKTalentTreeViewModel_TalentTreeViewModel array talentTreeViewModelsToUpdate[2400]
private hashtable Hash = InitHashtable()
private constant integer HASH_TIMER_TALENTTREE_KEY = 11
private constant integer HASH_TALENTTREE_TIMER_KEY = 12
// ==========================================================================================
endglobals
function interface TalentSlotFactory takes framehandle parent, integer index, integer panelId returns ITalentSlot
private function ConcatenateErrors takes string err1, string err2 returns string
if (err1 == null and err2 == null) then
return null
elseif (err1 == null) then
return err2
elseif (err2 == null) then
return err1
endif
return err1 + ", " + err2
endfunction
private function DefaultUpdateTreeViewModelOnViewChanged takes nothing returns nothing
local integer i = 0
local timer tim = GetExpiredTimer()
local integer timerKey = GetHandleId(tim)
local STKTalentTree_TalentTree tree = LoadInteger(Hash, HASH_TIMER_TALENTTREE_KEY, timerKey)
local STKTalentTreeViewModel_TalentTreeViewModel ttvm
call DestroyTimer(tim)
loop
exitwhen i == MAX_PLAYER_ID
set ttvm = talentTreeViewModelsToUpdate[i * MAX_PLAYER_ID + tree]
if (ttvm != 0 and ttvm != -1) then
call ttvm.ResetTalentViewModels()
endif
set i = i + 1
endloop
set tim = null
endfunction
function DefaultOnTalentViewChanged takes STKTalentTreeViewModel_TalentTreeViewModel ttvm, player watcher, STKTalentTree_TalentTree tree returns nothing
local integer playerId
local timer tim
set playerId = GetPlayerId(watcher)
set tim = LoadTimerHandle(Hash, HASH_TALENTTREE_TIMER_KEY, tree)
if (tim == null) then
set tim = CreateTimer()
call SaveTimerHandle(Hash, HASH_TALENTTREE_TIMER_KEY, tree, tim)
call SaveInteger(Hash, HASH_TIMER_TALENTTREE_KEY, GetHandleId(tim), tree)
call TimerStart(tim, 0, false, function DefaultUpdateTreeViewModelOnViewChanged)
endif
set tim = null
endfunction
public struct TalentTreeViewModel
private player watcher
private boolean watched = false
private STKTalentTree_TalentTree tree = 0
private ITalentTreeView view
private framehandle parent
private integer panelId
private ITalentSlot array slots[STKConstants_MAX_TALENT_SLOTS]
private integer slotCount = STKConstants_MAX_TALENT_SLOTS
private ViewChanged onViewChanged
// UI config ===============================================================
public real boxWidth = 0
public real boxHeight = 0
public real sideMargin = 0
public real verticalMargin = 0
// End UI config ===============================================================
static method SetUpTriggersIfNeeded takes nothing returns nothing
if (ClickTrigger == null) then
set ClickTrigger = CreateTrigger()
call TriggerAddAction(ClickTrigger, function thistype.TalentButtonClicked)
endif
if (ConfirmTrigger == null) then
set ConfirmTrigger = CreateTrigger()
call TriggerAddAction(ConfirmTrigger, function thistype.ConfirmButtonClicked)
endif
if (CancelTrigger == null) then
set CancelTrigger = CreateTrigger()
call TriggerAddAction(CancelTrigger, function thistype.CancelButtonClicked)
endif
if (CloseTrigger == null) then
set CloseTrigger = CreateTrigger()
call TriggerAddAction(CloseTrigger, function thistype.CloseButtonClicked)
endif
endmethod
static method SetUpSlotTriggersIfNeeded takes ITalentSlot slot, integer i, integer panelId returns nothing
local integer fi = LoadInteger(FrameIndex, FRAME_INDEX_KEY + panelId, GetHandleId(slot.GetButtonFrame()))
if (fi == 0) then
call BlzTriggerRegisterFrameEvent(ClickTrigger, slot.GetButtonFrame(), FRAMEEVENT_CONTROL_CLICK)
call SaveInteger(FrameIndex, FRAME_PANEL_ID, GetHandleId(slot.GetButtonFrame()), panelId)
call SaveInteger(FrameIndex, FRAME_INDEX_KEY + panelId, GetHandleId(slot.GetButtonFrame()), i + 1)
endif
endmethod
static method SetUpActionTriggersIfNeeded takes ITalentTreeView view returns nothing
if (LoadInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.confirmButtonMain)) == 0) then
call BlzTriggerRegisterFrameEvent(ConfirmTrigger, view.confirmButtonMain, FRAMEEVENT_CONTROL_CLICK)
call SaveInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.confirmButtonMain), 1)
endif
if (LoadInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.cancelButtonMain)) == 0) then
call BlzTriggerRegisterFrameEvent(CancelTrigger, view.cancelButtonMain, FRAMEEVENT_CONTROL_CLICK)
call SaveInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.cancelButtonMain), 1)
endif
if (LoadInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.closeButton)) == 0) then
call BlzTriggerRegisterFrameEvent(CloseTrigger, view.closeButton, FRAMEEVENT_CONTROL_CLICK)
call SaveInteger(FrameIndex, FRAME_INDEX_KEY, GetHandleId(view.closeButton), 1)
endif
endmethod
private static method create takes player watcher, ITalentTreeView view, TalentSlotFactory talentSlotFactory, integer panelId, ViewChanged onViewChanged returns TalentTreeViewModel
local TalentTreeViewModel this = TalentTreeViewModel.allocate()
local ITalentSlot slot
local integer i = 0
local framehandle f
set this.view = view
set this.watcher = watcher
set this.parent = view.box
set this.panelId = panelId
set this.onViewChanged = onViewChanged
if (onViewChanged == -1 or onViewChanged == 0) then
set this.onViewChanged = DefaultOnTalentViewChanged
endif
call thistype.SetUpTriggersIfNeeded()
// Set up the talent slots
loop
exitwhen i == STKTalentTree_MAX_TALENTS
// Create a new slot
set slot = talentSlotFactory.evaluate(view.box, i, panelId)
set this.slots[i] = slot
// Set its watcher to this watcher
call slot.SetWatcher(watcher)
call thistype.SetUpSlotTriggersIfNeeded(slot, i, panelId)
set i = i + 1
endloop
call thistype.SetUpActionTriggersIfNeeded(view)
set Instances[InstanceCount] = this
set InstanceCount = InstanceCount + 1
call this.Hide()
return this
endmethod
static method createSingleView takes player watcher, ITalentTreeView view, TalentSlotFactory talentSlotFactory returns TalentTreeViewModel
return TalentTreeViewModel.create(watcher, view, talentSlotFactory, 1, -1)
endmethod
static method createPanel takes player watcher, ITalentTreeView view, TalentSlotFactory talentSlotFactory, integer panelId, ViewChanged onViewChanged returns TalentTreeViewModel
return TalentTreeViewModel.create(watcher, view, talentSlotFactory, panelId, onViewChanged)
endmethod
method TalentClicked takes integer index returns nothing
local ITalentSlot slot = this.slots[index]
local STKTalent_Talent talent = 0
local integer tempState
if (this.watched == false) then
return
endif
if (this.tree == 0) then
return
endif
set talent = this.tree.talents[index]
if (talent == 0) then
return
endif
set tempState = this.tree.tempRankState[index]
if (this.tree.GetTalentPoints() >= talent.cost and tempState < talent.maxRank) then
call this.tree.ApplyTalentTemporary(index)
set tempState = this.tree.tempRankState[index]
// Check for link states
call this.tree.UpdateLinkStates()
if (this.onViewChanged != -1) then
call this.onViewChanged.execute(this, this.watcher, this.tree)
endif
endif
endmethod
method OnConfirm takes nothing returns nothing
if (this.tree != 0) then
call this.tree.SaveTalentRankState()
// call this.ResetTalentViewModels()
if (this.onViewChanged != -1) then
call this.onViewChanged.execute(this, this.watcher, this.tree)
endif
endif
endmethod
method OnCancel takes nothing returns nothing
if (this.tree != 0) then
call this.tree.ResetTempRankState()
call this.tree.UpdateLinkStates()
// call this.ResetTalentViewModels()
if (this.onViewChanged != -1) then
call this.onViewChanged.execute(this, this.watcher, this.tree)
endif
endif
endmethod
method UpdatePointsAndTitle takes nothing returns nothing
if (GetLocalPlayer() != this.watcher) then
return
endif
if (tree != 0) then
call BlzFrameSetText(this.view.title, this.tree.GetTitle())
call BlzFrameSetTexture(this.view.containerImage, this.tree.backgroundImage, 0, true)
endif
endmethod
method ResetTalentViewModels takes nothing returns nothing
local integer i = 0
local ITalentSlot slot
local STKTalent_Talent talent
if (this.tree == 0 or this.tree == null) then
return
endif
loop
exitwhen i == this.slotCount
set slot = this.slots[i]
set talent = this.tree.talents[i]
if (talent != 0) then
// Update Talent Slot from Talent
call slot.SetTalent(talent)
call this.UpdateTalentSlot(slot, talent, this.tree, i)
else
call slot.SetState(0)
endif
set i = i + 1
endloop
// Update points
call this.UpdatePointsAndTitle()
endmethod
method UnregisterFromTreeViewChanges takes STKTalentTree_TalentTree oldWatchedTree returns nothing
// call BJDebugMsg("Unregister (" + I2S(GetPlayerId(this.watcher) * MAX_PLAYER_ID + oldWatchedTree) + ") TTVM: " + I2S(this))
set talentTreeViewModelsToUpdate[MAX_PLAYER_ID * GetPlayerId(this.watcher) + oldWatchedTree] = -1
endmethod
method RegisterToTreeViewChanges takes STKTalentTree_TalentTree newWatchedTree returns nothing
// call BJDebugMsg("Register (" + I2S(GetPlayerId(this.watcher) * MAX_PLAYER_ID + newWatchedTree) + ") TTVM: " + I2S(this))
set talentTreeViewModelsToUpdate[MAX_PLAYER_ID * GetPlayerId(this.watcher) + newWatchedTree] = this
endmethod
method ScheduleRedraw takes nothing returns nothing
call DefaultOnTalentViewChanged(this, this.watcher, this.tree)
endmethod
method SetTree takes STKTalentTree_TalentTree tree returns nothing
if (this.watched == true) then
// Unregister from the old tree
if (this.tree != tree) then
call this.UnregisterFromTreeViewChanges(this.tree)
call this.RegisterToTreeViewChanges(tree)
endif
endif
set this.tree = tree
call this.ResetTalentViewModels()
endmethod
method GetTree takes nothing returns STKTalentTree_TalentTree
return this.tree
endmethod
method IsWatched takes nothing returns boolean
return this.watched
endmethod
// Show
method RenderTree takes nothing returns nothing
local STKTalentTree_TalentTree tree
local integer cols = 0
local integer rows = 0
local real xIncrem = 0
local real yIncrem = 0
local integer i = 0
local ITalentSlot slot
local real x
local real y
set this.watched = true
if (this.tree == 0) then
return
endif
// Reorganize the talents
set tree = this.tree
set cols = tree.columns
set rows = tree.rows
set xIncrem = (this.boxWidth * (1 - this.sideMargin)) / (cols + 1)
set yIncrem = (this.boxHeight * (1 - this.verticalMargin)) / (rows + 1)
call this.ResetTalentViewModels()
set i = 0
loop
exitwhen i >= this.slotCount
set slot = this.slots[i]
set x = R2I(ModuloInteger(i, cols)) * xIncrem - ((cols - 1) * 0.5) * xIncrem
set y = R2I((i) / cols) * yIncrem - ((rows - 1) * 0.5) * yIncrem
call slot.MoveTo(FRAMEPOINT_CENTER, this.view.container, FRAMEPOINT_CENTER, x, y, xIncrem, yIncrem)
if (tree.talents[i] != 0) then
// call slot.SetTalent(tree.talents[i])
// call UpdateTalentSlot(slot, tree.talents[i], tree, i)
// call BJDebugMsg("Setting visible")
call slot.SetVisible(true)
else
call slot.SetVisible(false)
endif
set i = i + 1
endloop
// Register TalentTreeViewModel to the view changes
call this.RegisterToTreeViewChanges(tree)
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetVisible(this.view.box, true)
call BlzFrameSetVisible(this.view.container, true)
endmethod
method Hide takes nothing returns nothing
local integer i = 0
set this.watched = false
// for (let i = 0; i < this._slots.length; i++) {
// this._slots[i].visible = false;
// }
// Unreegister TalentTreeViewModel from the view changes
call this.UnregisterFromTreeViewChanges(this.tree)
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetVisible(this.view.box, false)
endmethod
method UpdateTalentSlot takes ITalentSlot slot, STKTalent_Talent talent, STKTalentTree_TalentTree tree, integer index returns nothing
local integer tempState = tree.tempRankState[index]
// State Available
local integer state = 3
local boolean dep = false
local boolean req = false
local boolean depOk = true
local boolean reqOk = true
local string depError
local string reqError
local string depLeft = tree.CheckDependencyKeyLeft(talent.dependencyLeft, index)
local string depUp = tree.CheckDependencyKeyUp(talent.dependencyUp, index)
local string depRight = tree.CheckDependencyKeyRight(talent.dependencyRight, index)
local string depDown = tree.CheckDependencyKeyDown(talent.dependencyDown, index)
call slot.SetRank(tempState)
call slot.RenderLinks(depLeft, depUp, depRight, depDown)
if (talent.isLink == true) then
call slot.SetState(5) // Link
return
endif
call slot.SetErrorText("")
set depOk = true
set reqOk = true
set depError = ""
set reqError = ""
if (depLeft == null and depUp == null and depRight == null and depDown == null) then
set depOk = true
else
set depOk = false
set depError = ConcatenateErrors(depLeft, depUp)
set depError = ConcatenateErrors(depError, depRight)
set depError = ConcatenateErrors(depError, depDown)
endif
set reqError = tree.CalculateTalentRequirements(talent, index)
set reqOk = false
if (reqError == null) then
set reqOk = true
set reqError = null
endif
if (tempState == talent.maxRank) then
// call BJDebugMsg("STATE 4")
call slot.SetState(4) // Maxed
elseif (depOk and reqOk and talent.cost <= tree.GetTalentPoints()) then
// call BJDebugMsg("STATE 3")
call slot.SetState(3) // Available
else
call slot.SetErrorText(depError + reqError)
call slot.SetState(1)
if (reqOk) then
// call BJDebugMsg("STATE 1")
call slot.SetState(1) // RequireDisabled
elseif (depOk) then
// call BJDebugMsg("STATE 2")
call slot.SetState(2) // DependDisabled
endif
endif
endmethod
static method TalentButtonClicked takes nothing returns nothing
local framehandle frame = BlzGetTriggerFrame()
local integer panelId = LoadInteger(FrameIndex, FRAME_PANEL_ID, GetHandleId(frame))
local integer index = LoadInteger(FrameIndex, FRAME_INDEX_KEY + panelId, GetHandleId(frame)) - 1
local player clickingPlayer = GetTriggerPlayer()
local thistype ttvm
local integer i = 0
call BlzFrameSetEnable(frame, false)
call BlzFrameSetEnable(frame, true)
loop
exitwhen i == InstanceCount
set ttvm = Instances[i]
if (ttvm != 0 and ttvm.watcher == clickingPlayer and ttvm.panelId == panelId) then
call ttvm.TalentClicked(index)
endif
set i = i + 1
endloop
set frame = null
set clickingPlayer = null
endmethod
static method ConfirmButtonClicked takes nothing returns nothing
local framehandle frame = BlzGetTriggerFrame()
local player clickingPlayer = GetTriggerPlayer()
local thistype ttvm
local integer i = 0
call BlzFrameSetEnable(frame, false)
call BlzFrameSetEnable(frame, true)
loop
exitwhen i == InstanceCount
set ttvm = Instances[i]
if (ttvm != 0 and ttvm.watcher == clickingPlayer) then
call ttvm.OnConfirm()
endif
set i = i + 1
endloop
set frame = null
set clickingPlayer = null
endmethod
static method CancelButtonClicked takes nothing returns nothing
local framehandle frame = BlzGetTriggerFrame()
local player clickingPlayer = GetTriggerPlayer()
local thistype ttvm
local integer i = 0
call BlzFrameSetEnable(frame, false)
call BlzFrameSetEnable(frame, true)
loop
exitwhen i == InstanceCount
set ttvm = Instances[i]
if (ttvm != 0 and ttvm.watcher == clickingPlayer) then
call ttvm.OnCancel()
endif
set i = i + 1
endloop
set frame = null
set clickingPlayer = null
endmethod
static method CloseButtonClicked takes nothing returns nothing
local framehandle frame = BlzGetTriggerFrame()
local player clickingPlayer = GetTriggerPlayer()
local thistype ttvm
local integer i = 0
call BlzFrameSetEnable(frame, false)
call BlzFrameSetEnable(frame, true)
loop
exitwhen i == InstanceCount
set ttvm = Instances[i]
if (ttvm != 0 and ttvm.watcher == clickingPlayer) then
// call ttvm.OnClose()
call ttvm.Hide()
endif
set i = i + 1
endloop
set frame = null
set clickingPlayer = null
endmethod
method onDestroy takes nothing returns nothing
set Instances[this] = 0
endmethod
endstruct
function interface ViewChanged takes TalentTreeViewModel ttvm, player watcher, STKTalentTree_TalentTree tree returns nothing
endlibrary
library STKTalentViewModel requires STKITalentSlot, STKITalentView
public struct TalentViewModel extends ITalentSlot
public static constant string ActiveLinkTexture = STKConstants_ACTIVE_LINK_TEXTURE
public static constant string InactiveLinkTexture = STKConstants_INACTIVE_LINK_TEXTURE
private ITalentView view
private player watcher
private integer state = 0
private integer rank = 0
private STKTalent_Talent talent = 0
private string errorText
private boolean visible = false
private boolean isAvailable = false
private boolean linkLeftVisible = false
private boolean linkUpVisible = false
private boolean linkRightVisible = false
private boolean linkDownVisible = false
static method create takes ITalentView view returns TalentViewModel
local TalentViewModel this = TalentViewModel.allocate()
set this.view = view
return this
endmethod
method RenderView takes nothing returns nothing
// call BJDebugMsg("Rendering talent " + I2S(this.state))
if (this.state == 0) then
// Empty
call this.SetVisible(false)
elseif (this.state == 1) then
// RequireDisabled
call this.SetTooltip(this.errorText, this.talent.cost)
call this.SetRank(this.rank)
call this.SetAvailable(false)
elseif (this.state == 2) then
// DependDisabled
call this.SetTooltip(this.errorText, this.talent.cost)
call this.SetRank(this.rank)
call this.SetAvailable(false)
elseif (this.state == 3) then
// Available
call this.SetTooltip(this.errorText, this.talent.cost)
call this.SetRank(this.rank)
call this.SetAvailable(true)
elseif (this.state == 4) then
// Maxed
call this.SetTooltip(this.errorText, 0)
call this.SetRank(this.rank)
call this.SetMaxed()
elseif (this.state == 5) then
// Link
call this.SetAsLink()
endif
endmethod
// Internal ===========================================================
method SetTooltip takes string requirements, integer cost returns nothing
local string description = ""
local string text = ""
if (this.talent == 0 or GetLocalPlayer() != this.watcher) then
return
endif
set description = this.talent.description
if (this.talent.nextRank != 0 and this.talent.previousRank != 0) then
set description = this.talent.previousRank.description + "\n\nNext rank:\n" + description
endif
set text = this.talent.name
if (cost > 0) then
set text = text + "\n\n|cffffc04d[Cost " + I2S(cost) + "]|r "
endif
if (cost > 0 and requirements != null and requirements != "") then
set text = text + "|cffff6450Requires: " + requirements + "|r\n\n" + description
elseif (requirements != null and requirements != "") then
set text = text + "\n\n|cffff6450Requires: " + requirements + "|r\n\n" + description
else
set text = text + "\n\n" + description
endif
call BlzFrameSetText(this.view.tooltipText, text)
endmethod
method SetAvailable takes boolean isAvailable returns nothing
local string texture
set this.isAvailable = isAvailable
if (this.talent == 0) then
return
endif
// icon to disabled/enabled, button enable, highlight, tooltip
set texture = this.talent.iconDisabled
if (this.GetRank() > 0) then
set texture = this.talent.iconEnabled
endif
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetTexture(this.view.buttonImage, texture, 0, true)
call BlzFrameSetEnable(this.view.buttonMain, isAvailable)
call BlzFrameSetVisible(this.view.highlight, isAvailable)
endmethod
method SetMaxed takes nothing returns nothing
if (this.talent == 0) then
return
endif
set this.isAvailable = false
call this.SetTooltip(null, 0)
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetTexture(this.view.buttonImage, this.talent.iconEnabled, 0, true)
call BlzFrameSetEnable(this.view.buttonMain, false)
call BlzFrameSetVisible(this.view.highlight, false)
endmethod
method SetAsLink takes nothing returns nothing
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetVisible(this.view.highlight, false)
if (this.talent != 0 and this.talent.iconEnabled != null) then
call BlzFrameSetVisible(this.view.buttonMain, true)
call BlzFrameSetEnable(this.view.buttonMain, false)
if (this.rank > 0) then
call BlzFrameSetTexture(this.view.buttonImage, this.talent.iconEnabled, 0, true)
endif
else
call BlzFrameSetVisible(this.view.buttonMain, false)
call BlzFrameSetVisible(this.view.linkIntersection, true)
if (this.rank > 0) then
call BlzFrameSetTexture(this.view.linkIntersection, ActiveLinkTexture, 0, true)
else
call BlzFrameSetTexture(this.view.linkIntersection, InactiveLinkTexture, 0, true)
endif
endif
call this.UpdateLinkVisibility()
endmethod
// End Internal ===========================================================
method GetButtonFrame takes nothing returns framehandle
return this.view.buttonMain
endmethod
method SetVisible takes boolean isVisible returns nothing
set this.visible = isVisible
if (isVisible == false) then
// call BJDebugMsg("Setting invisible ")
endif
if (GetLocalPlayer() != this.watcher) then
return
endif
if (this.talent != 0 and this.talent.isLink) then
call BlzFrameSetVisible(this.view.linkIntersection, isVisible and this.talent.isLink)
return
endif
call BlzFrameSetVisible(this.view.linkIntersection, false)
call BlzFrameSetVisible(this.view.buttonMain, isVisible)
call BlzFrameSetVisible(this.view.highlight, isVisible and this.isAvailable)
call BlzFrameSetVisible(this.view.linkLeft, isVisible and this.linkLeftVisible)
call BlzFrameSetVisible(this.view.linkUp, isVisible and this.linkUpVisible)
call BlzFrameSetVisible(this.view.linkRight, isVisible and this.linkRightVisible)
call BlzFrameSetVisible(this.view.linkDown, isVisible and this.linkDownVisible)
endmethod
method GetTalent takes nothing returns STKTalent_Talent
return this.talent
endmethod
method SetTalent takes STKTalent_Talent talent returns nothing
set this.talent = talent
call this.RenderView()
endmethod
method GetRank takes nothing returns integer
return this.rank
endmethod
method SetRank takes integer rank returns nothing
local string tooltipText
local string rankText
set this.rank = rank
if (this.talent == 0) then
return
endif
set rankText = I2S(rank) + "/" + I2S(this.talent.maxRank)
set tooltipText = "Rank " + rankText
if (GetLocalPlayer() != this.watcher) then
return
endif
if (this.talent.maxRank == 1) then
call BlzFrameSetVisible(this.view.rankImage, false)
call BlzFrameSetVisible(this.view.rankText, false)
else
call BlzFrameSetVisible(this.view.rankImage, true)
call BlzFrameSetVisible(this.view.rankText, true)
call BlzFrameSetText(this.view.rankText, rankText)
endif
call BlzFrameSetText(this.view.tooltipRank, tooltipText)
endmethod
method GetIndex takes nothing returns integer
return 1
endmethod
method GetWatcher takes nothing returns player
return GetTriggerPlayer()
endmethod
method SetWatcher takes player watcher returns nothing
set this.watcher = watcher
endmethod
method GetState takes nothing returns integer
return this.state
endmethod
method SetState takes integer state returns nothing
set this.state = state
// call BJDebugMsg("Set Talent state " + I2S(state))
call this.RenderView()
endmethod
method GetErrorText takes nothing returns string
return this.errorText
endmethod
method SetErrorText takes string text returns nothing
set this.errorText = text
endmethod
method MoveTo takes framepointtype point, framehandle relative, framepointtype relativePoint, real x, real y, real linkWidth, real linkHeight returns nothing
local real width = 0.
local real height = 0.
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetPoint(this.view.buttonMain, point, relative, relativePoint, x, y)
// Links
set height = BlzFrameGetHeight(this.view.linkLeft)
set width = BlzFrameGetWidth(this.view.linkUp)
call BlzFrameClearAllPoints(this.view.linkLeft)
call BlzFrameSetPoint(this.view.linkLeft, FRAMEPOINT_RIGHT, this.view.buttonMain, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(this.view.linkLeft, linkWidth, height)
call BlzFrameClearAllPoints(this.view.linkUp)
call BlzFrameSetPoint(this.view.linkUp, FRAMEPOINT_BOTTOM, this.view.buttonMain, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(this.view.linkUp, width, linkHeight)
call BlzFrameClearAllPoints(this.view.linkRight)
call BlzFrameSetPoint(this.view.linkRight, FRAMEPOINT_LEFT, this.view.buttonMain, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(this.view.linkRight, linkWidth, height)
call BlzFrameClearAllPoints(this.view.linkDown)
call BlzFrameSetPoint(this.view.linkDown, FRAMEPOINT_TOP, this.view.buttonMain, FRAMEPOINT_CENTER, 0, 0)
call BlzFrameSetSize(this.view.linkDown, width, linkHeight)
endmethod
method UpdateLinkVisibility takes nothing returns nothing
if (GetLocalPlayer() != this.watcher) then
return
endif
call BlzFrameSetVisible(this.view.linkLeft, this.linkLeftVisible)
call BlzFrameSetVisible(this.view.linkUp, this.linkUpVisible)
call BlzFrameSetVisible(this.view.linkRight, this.linkRightVisible)
call BlzFrameSetVisible(this.view.linkDown, this.linkDownVisible)
endmethod
method RenderLinks takes string dependencyLeft, string dependencyUp, string dependencyRight, string dependencyDown returns nothing
set this.linkLeftVisible = this.talent.dependencyLeft != 0 // dependencyLeft != null and dependencyLeft != ""
set this.linkUpVisible = this.talent.dependencyUp != 0 // dependencyLeft != null and dependencyUp != ""
set this.linkRightVisible = this.talent.dependencyRight != 0 // dependencyLeft != null and dependencyRight != ""
set this.linkDownVisible = this.talent.dependencyDown != 0 // dependencyLeft != null and dependencyDown != ""
if (GetLocalPlayer() != this.watcher) then
return
endif
call this.UpdateLinkVisibility()
if (dependencyLeft == null and this.linkLeftVisible) then
call BlzFrameSetTexture(this.view.linkLeft, thistype.ActiveLinkTexture, 0, true)
else
call BlzFrameSetTexture(this.view.linkLeft, thistype.InactiveLinkTexture, 0, true)
endif
if (dependencyUp == null and this.linkUpVisible) then
call BlzFrameSetTexture(this.view.linkUp, thistype.ActiveLinkTexture, 0, true)
else
call BlzFrameSetTexture(this.view.linkUp, thistype.InactiveLinkTexture, 0, true)
endif
if (dependencyRight == null and this.linkRightVisible) then
call BlzFrameSetTexture(this.view.linkRight, thistype.ActiveLinkTexture, 0, true)
else
call BlzFrameSetTexture(this.view.linkRight, thistype.InactiveLinkTexture, 0, true)
endif
if (dependencyDown == null and this.linkDownVisible) then
call BlzFrameSetTexture(this.view.linkDown, thistype.ActiveLinkTexture, 0, true)
else
call BlzFrameSetTexture(this.view.linkDown, thistype.InactiveLinkTexture, 0, true)
endif
endmethod
endstruct
endlibrary
library STKStore requires STKConstants, STKTalentTree
globals
// Configurable globals
private constant integer MAX_TALENT_SLOTS = STKConstants_MAX_TALENT_SLOTS
private constant integer MAX_PLAYER_COUNT = STKConstants_MAX_PLAYER_COUNT
private constant integer MAX_PANELS_VISIBLE = 3
// Internal globals
private constant integer MAX_TALENT_VIEWS = MAX_TALENT_SLOTS * MAX_PANELS_VISIBLE
private constant integer MAX_TALENT_VIEW_MODELS = MAX_PLAYER_COUNT * MAX_PANELS_VISIBLE
private constant integer HASH_UNIT_TREE_KEY = 100
endglobals
struct STKStore
private hashtable hash
private ITalentView array talentViews[MAX_TALENT_VIEWS]
private STKTalentTreeViewModel_TalentTreeViewModel array talentTreeViewModels[MAX_TALENT_VIEW_MODELS]
static method create takes nothing returns STKStore
local thistype this = thistype.allocate()
set this.hash = InitHashtable()
return this
endmethod
// Talent Trees
public method SetUnitTalentTree takes integer panelId, unit whichUnit, STKTalentTree_TalentTree talentTree returns STKTalentTree_TalentTree
local integer playerId = GetPlayerId(GetOwningPlayer(whichUnit))
call SaveInteger(this.hash, HASH_UNIT_TREE_KEY + panelId, GetHandleId(whichUnit), talentTree)
return talentTree
endmethod
public method GetUnitTalentTree takes integer panelId, unit whichUnit returns STKTalentTree_TalentTree
local integer playerId = GetPlayerId(GetOwningPlayer(whichUnit))
return LoadInteger(this.hash, HASH_UNIT_TREE_KEY + panelId, GetHandleId(whichUnit))
endmethod
// Talent Views
public method SetTalentView takes integer panelId, integer talentViewIndex, ITalentView talentView returns ITalentView
local integer index = MAX_TALENT_SLOTS * panelId + talentViewIndex
set this.talentViews[index] = talentView
return talentView
endmethod
public method GetTalentView takes integer panelId, integer talentViewIndex returns ITalentView
local integer index = MAX_TALENT_SLOTS * panelId + talentViewIndex
return this.talentViews[index]
endmethod
// Talent Tree View Models
public method SetPlayerTalentTreeViewModel takes integer panelId, integer playerId, STKTalentTreeViewModel_TalentTreeViewModel talentTreeViewModel returns STKTalentTreeViewModel_TalentTreeViewModel
local integer index = MAX_PANELS_VISIBLE * playerId + panelId
set this.talentTreeViewModels[index] = talentTreeViewModel
return talentTreeViewModel
endmethod
public method GetPlayerTalentTreeViewModel takes integer panelId, integer playerId returns STKTalentTreeViewModel_TalentTreeViewModel
local integer index = MAX_PANELS_VISIBLE * playerId + panelId
return this.talentTreeViewModels[index]
endmethod
endstruct
endlibrary
library STKSaveLoad requires BSRW
globals
public constant integer MAX_TALENT_SLOTS = STKConstants_MAX_TALENT_SLOTS
public constant integer MAX_SAVED_TALENT_POINTS = STKConstants_MAX_SAVED_TALENT_POINTS
public constant integer TALENTTREE_ID_THRESHOLD_SMALL = STKConstants_TALENTTREE_ID_THRESHOLD_SMALL
public constant integer TALENTTREE_ID_THRESHOLD_LARGE = STKConstants_TALENTTREE_ID_THRESHOLD_LARGE
public constant integer TALENT_ID_THRESHOLD_SMALL = STKConstants_TALENT_ID_THRESHOLD_SMALL
public constant integer TALENT_ID_THRESHOLD_LARGE = STKConstants_TALENT_ID_THRESHOLD_LARGE
public constant integer TALENT_RANK_THRESHOLD_SMALL = STKConstants_TALENT_RANK_THRESHOLD_SMALL
public constant integer TALENT_RANK_THRESHOLD_MEDIUM = STKConstants_TALENT_RANK_THRESHOLD_MEDIUM
public constant integer TALENT_RANK_THRESHOLD_LARGE = STKConstants_TALENT_RANK_THRESHOLD_LARGE
public constant integer TALENT_RANK_THRESHOLD_EXTRA = STKConstants_TALENT_RANK_THRESHOLD_EXTRA
endglobals
function interface AssignTalentTreeFunction takes integer panelId, unit u, STKTalentTree_TalentTree createdTalentTree returns nothing
function interface TalentTreeFactory takes unit u returns STKTalentTree_TalentTree
// Private globals
globals
private constant string array Threshold2SizeName[2]
private constant string array Threshold4SizeName[4]
private constant integer array TalentTreeIdThreshold[2]
private constant integer array TalentIdThreshold[2]
private constant integer array TalentRankThreshold[4]
private TalentTreeFactory array TalentTreeFactories[TALENT_ID_THRESHOLD_LARGE]
private integer MaxTalentTreeId = 0
private integer array TempArrayChainIndex[TALENT_ID_THRESHOLD_LARGE]
private string SaveCodeCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$#0123456789"
private integer SaveCodeCharsetBase = 6
private STKStore Store
private AssignTalentTreeFunction AssignTalentTree
endglobals
private function getCharIndex takes string char returns integer
local integer i = 0
local integer length = StringLength(SaveCodeCharset)
loop
exitwhen i >= length or char == SubString(SaveCodeCharset, i, i+1)
set i = i + 1
endloop
return i
endfunction
private function nextPowerOfTwo takes integer n returns integer
local integer value = 1
loop
exitwhen value >= n
set value = value * 2
endloop
return value
endfunction
private function getThresholdValue takes integer size, integer whichThreshold returns integer
local integer value = -1
if (whichThreshold == 0) then // talentTreeId
set value = TalentTreeIdThreshold[size]
elseif (whichThreshold == 1) then // talentId
set value = TalentIdThreshold[size]
elseif (whichThreshold == 2) then // talentRank
set value = TalentRankThreshold[size]
elseif (whichThreshold == 3) then // talentPoints
set value = MAX_SAVED_TALENT_POINTS
endif
return value
endfunction
private function findSizeBits takes integer value, integer whichThreshold returns integer
local integer thresholdKey = 0
local integer i = 0
local integer threshold = 0
local integer bits = 0
loop
set threshold = getThresholdValue(i, whichThreshold)
exitwhen value < threshold or threshold == 0
set thresholdKey = i
set i = i + 1
endloop
set threshold = getThresholdValue(thresholdKey, whichThreshold)
loop
set bits = bits + 1
set threshold = threshold / 2
exitwhen threshold <= 1
endloop
return bits
endfunction
private function findThresholdSize takes integer value, integer whichThreshold returns integer
local integer size = 0
local integer threshold = getThresholdValue(size, whichThreshold)
loop
exitwhen value < threshold or threshold == 0
set size = size + 1
set threshold = getThresholdValue(size, whichThreshold)
endloop
return size
endfunction
public function RegisterTalentTree takes integer id, TalentTreeFactory factoryMethod returns boolean
local TalentTreeFactory f = TalentTreeFactories[id]
if (id == 0) then
call BJDebugMsg("|cffdd0808STK-Error: IDs should start from 1, not 0. (SaveLoad.RegisterTalentTree)")
return false
endif
if (f != 0) then
call BJDebugMsg("|cffdd0808STK-Error: " + I2S(id) + " is already used. (SaveLoad.RegisterTalentTree)")
return false
endif
set TalentTreeFactories[id] = factoryMethod
if (id > MaxTalentTreeId) then
set MaxTalentTreeId = id
endif
return true
endfunction
public function InspectTalentTree takes integer panelId, BSRW_BitStreamReader stream, integer talentTreeIdBits, integer talentIdBits, integer talentRankBits, integer talentPointBits returns nothing
local integer talentTreeId = 0
local integer talentPoints = 0
local integer readMode = 0
local integer talentCount = 0
local integer talentId = 0
local integer talentRank = 0
local integer i = 0
set talentTreeId = stream.readInt(talentTreeIdBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent Tree Id: " + I2S(talentTreeId))
set talentPoints = stream.readInt(talentPointBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent Points: " + I2S(talentPoints))
set readMode = stream.readInt(1)
// Sequential
if (readMode == 0) then
call BJDebugMsg(stream.lastReadChunk + " - Read mode: Sequential")
set talentCount = stream.readInt(talentIdBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent with Ranks Count: " + I2S(talentCount))
set i = 0
loop
exitwhen i == talentCount
set talentId = stream.readInt(talentIdBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent Id: " + I2S(talentId))
set talentRank = stream.readInt(talentRankBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent [" + I2S(talentId) + "] Rank: " + I2S(talentRank))
set i = i + 1
endloop
endif
// Positional
if (readMode == 1) then
call BJDebugMsg(stream.lastReadChunk + " - Read mode: Positional")
set talentCount = stream.readInt(talentIdBits)
call BJDebugMsg(stream.lastReadChunk + " - Max Talent Id: " + I2S(talentCount))
set i = 0
loop
exitwhen i == talentCount
set talentId = i + 1
set talentRank = stream.readInt(talentRankBits)
call BJDebugMsg(stream.lastReadChunk + " - Talent [" + I2S(talentId) + "] Rank: " + I2S(talentRank))
set i = i + 1
endloop
endif
endfunction
public function LoadTalentTree takes integer panelId, BSRW_BitStreamReader stream, integer talentTreeIdBits, integer talentIdBits, integer talentRankBits, integer talentPointBits, unit owner returns STKTalentTree_TalentTree
local integer talentTreeId = 0
local integer talentPoints = 0
local integer readMode = 0
local integer talentCount = 0
local integer talentId = 0
local integer talentRank = 0
local integer i = 0
local STKTalentTree_TalentTree tree
set talentTreeId = stream.readInt(talentTreeIdBits)
set talentPoints = stream.readInt(talentPointBits)
set tree = TalentTreeFactories[talentTreeId].evaluate(owner)
call AssignTalentTree.execute(panelId, owner, tree)
call tree.UpdateChainIdTalentIndex()
call tree.SetTalentPoints(talentPoints)
set readMode = stream.readInt(1)
// Sequential
if (readMode == 0) then
set talentCount = stream.readInt(talentIdBits)
set i = 0
loop
exitwhen i == talentCount
set talentId = stream.readInt(talentIdBits)
set talentRank = stream.readInt(talentRankBits)
call tree.ApplyTalentChainTemporary(talentId, talentRank)
set i = i + 1
endloop
endif
// Positional
if (readMode == 1) then
set talentCount = stream.readInt(talentIdBits)
set i = 0
loop
exitwhen i == talentCount
set talentId = i + 1
set talentRank = stream.readInt(talentRankBits)
if (talentRank > 0) then
call tree.ApplyTalentChainTemporary(talentId, talentRank)
endif
set i = i + 1
endloop
endif
call tree.SaveTalentRankState()
return tree
endfunction
public function SaveTalentTree takes BSRW_BitStreamWriter stream, integer talentTreeIdBits, integer talentIdBits, integer talentRankBits, integer talentPointBits, STKTalentTree_TalentTree tree returns nothing
local integer i = 0
local integer talentCount = 0
local integer minChainId = 0
local integer maxChainId = 0
local integer chainId = 0
local string seq = ""
local string pos = ""
local BSRW_BitStreamWriter tempWriter = BSRW_BitStreamWriter.create()
set i = 0
loop
exitwhen i == MAX_TALENT_SLOTS
set TempArrayChainIndex[i] = 0
set i = i + 1
endloop
// Talent Tree Id
call stream.write(tree.GetId(), talentTreeIdBits)
// Talent Points
call stream.write(tree.GetTalentPoints(), talentPointBits)
// Sequential
set talentCount = 0
set i = 0
loop
exitwhen i == MAX_TALENT_SLOTS
if (tree.rankState[i] > 0 and tree.talents[i] > 0) then
set talentCount = talentCount + 1
call tempWriter.write(tree.talents[i].GetChainId(), talentIdBits)
call tempWriter.write(tree.rankState[i], talentRankBits)
endif
set i = i + 1
endloop
set seq = tempWriter.get()
call tempWriter.flush().write(talentCount, talentIdBits)
set seq = tempWriter.get() + seq
// Positional
set maxChainId = 0
set minChainId = MAX_TALENT_SLOTS
set i = 0
loop
exitwhen i == MAX_TALENT_SLOTS
if (tree.rankState[i] > 0 and tree.talents[i] > 0) then
set chainId = tree.talents[i].GetChainId()
if (chainId > maxChainId) then
// Highest chain id
set maxChainId = chainId
endif
if (chainId < minChainId) then
set minChainId = chainId
endif
if (TempArrayChainIndex[chainId] > 0) then
call BJDebugMsg("|cffdd0808STK-Error: Talents on different grid positions should not share Chain ID " + I2S(chainId) + " (" + tree.talents[i].name + "). (SaveLoad.SaveTalentTree)")
endif
set TempArrayChainIndex[chainId] = i
endif
set i = i + 1
endloop
call tempWriter.flush().write(talentCount, talentIdBits)
set i = minChainId
loop
exitwhen i > maxChainId
call tempWriter.write(tree.rankState[TempArrayChainIndex[i]], talentRankBits)
set i = i + 1
endloop
set pos = tempWriter.get()
if (StringLength(seq) < StringLength(pos)) then
call stream.write(0, 1)
call stream.writeStream(seq)
else
call stream.write(1, 1)
call stream.writeStream(pos)
endif
call tempWriter.destroy()
set seq = null
set pos = null
endfunction
public function Inspect takes string bitString returns nothing
local integer i = 0
local integer talentTreeIdSize = 0
local integer talentRankSize = 0
local integer talentIdSize = 0
local integer talentTreeIdBits = 0
local integer talentIdBits = 0
local integer talentRankBits = 0
local integer talentPointBits = 0
local integer talentTreeId = 0
local integer talentTreeCount = 0
local BSRW_BitStreamReader stream = BSRW_BitStreamReader.create(bitString)
set talentTreeIdSize = stream.readInt(1)
call BJDebugMsg(stream.lastReadChunk + " - Talent Tree Id size: " + Threshold2SizeName[talentTreeIdSize] + " threshold: " + I2S(TalentTreeIdThreshold[talentTreeIdSize]))
set talentRankSize = stream.readInt(2)
call BJDebugMsg(stream.lastReadChunk + " - Talent Rank size: " + Threshold4SizeName[talentRankSize] + " threshold: " + I2S(TalentRankThreshold[talentRankSize]))
set talentIdSize = stream.readInt(1)
call BJDebugMsg(stream.lastReadChunk + " - Talent Id size: " + Threshold2SizeName[talentIdSize] + " threshold: " + I2S(TalentIdThreshold[talentIdSize]))
// Talent Tree Count
set talentTreeCount = stream.readInt(4)
call BJDebugMsg(stream.lastReadChunk + " - Talent Tree count: " + I2S(talentTreeCount))
// Bit sizes
set talentTreeIdBits = findSizeBits(talentTreeIdSize, 0)
set talentIdBits = findSizeBits(talentIdSize, 1)
set talentRankBits = findSizeBits(talentRankSize, 2)
set talentPointBits = findSizeBits(0, 3)
// Talent Trees
set i = 0
loop
exitwhen i == talentTreeCount
call InspectTalentTree(i + 1, stream, talentTreeIdBits, talentIdBits, talentRankBits, talentPointBits)
set i = i + 1
endloop
call stream.destroy()
endfunction
public function LoadForUnit takes unit owner, string bitString returns nothing
local integer i = 0
local STKTalentTree_TalentTree tt
local integer talentTreeIdSize = 0
local integer talentRankSize = 0
local integer talentIdSize = 0
local integer talentTreeIdBits = 0
local integer talentIdBits = 0
local integer talentRankBits = 0
local integer talentPointBits = 0
local integer talentTreeId = 0
local integer talentTreeCount = 0
local BSRW_BitStreamReader stream = BSRW_BitStreamReader.create(bitString)
set talentTreeIdSize = stream.readInt(1)
set talentRankSize = stream.readInt(2)
set talentIdSize = stream.readInt(1)
// Talent Tree Count
set talentTreeCount = stream.readInt(4)
// Bit sizes
set talentTreeIdBits = findSizeBits(talentTreeIdSize, 0)
set talentIdBits = findSizeBits(talentIdSize, 1)
set talentRankBits = findSizeBits(talentRankSize, 2)
set talentPointBits = findSizeBits(0, 3)
// Talent Trees
set i = 0
loop
exitwhen i == talentTreeCount
set tt = LoadTalentTree(i + 1, stream, talentTreeIdBits, talentIdBits, talentRankBits, talentPointBits, owner)
set i = i + 1
endloop
call stream.destroy()
endfunction
public function SaveForUnit takes unit owner returns string
local integer i = 0
local integer j = 0
local integer maxTalentRank = 0
local integer maxTalentId = 0
local integer talentTreeCount = 0
local integer talentTreeIdSize = 0
local integer talentIdSize = 0
local integer talentRankSize = 0
local integer talentTreeIdBits = 0
local integer talentIdBits = 0
local integer talentRankBits = 0
local integer talentPointBits = 0
local STKTalentTree_TalentTree tt
local integer talentTreeId = 0
local BSRW_BitStreamWriter stream
local string bitString
// Loop over unit's talent trees
// And find maxTalentRank, maxTalentId
set j = 1
loop
set tt = Store.GetUnitTalentTree(j, owner)
exitwhen tt <= 0
set talentTreeCount = talentTreeCount + 1
set i = 0
loop
exitwhen i == MAX_TALENT_SLOTS
if (tt.rankState[i] > maxTalentRank) then
set maxTalentRank = tt.rankState[i]
endif
if (tt.talents[i].GetChainId() > maxTalentId) then
set maxTalentId = tt.talents[i].GetChainId()
endif
set i = i + 1
endloop
set j = j + 1
endloop
set talentTreeIdSize = findThresholdSize(MaxTalentTreeId, 0)
set talentIdSize = findThresholdSize(maxTalentId, 1)
set talentRankSize = findThresholdSize(maxTalentRank, 2)
set stream = BSRW_BitStreamWriter.create()
call stream.write(talentTreeIdSize, 1)
call stream.write(talentRankSize, 2)
call stream.write(talentIdSize, 1)
// Talent Tree Count
call stream.write(talentTreeCount, 4)
// Bit sizes
set talentTreeIdBits = findSizeBits(talentTreeIdSize, 0)
set talentIdBits = findSizeBits(talentIdSize, 1)
set talentRankBits = findSizeBits(talentRankSize, 2)
set talentPointBits = findSizeBits(0, 3)
// Talent Trees
set i = 1
loop
set tt = Store.GetUnitTalentTree(i, owner)
exitwhen tt <= 0
call SaveTalentTree(stream, talentTreeIdBits, talentIdBits, talentRankBits, talentPointBits, tt)
set i = i + 1
endloop
set bitString = stream.get()
call stream.destroy()
return bitString
endfunction
public function InspectEncoded takes string saveCode returns nothing
local BSRW_BitStreamWriter writer = BSRW_BitStreamWriter.create()
local integer length = StringLength(saveCode)
local integer index = 0
local integer i = 0
local string char = ""
set i = 0
loop
exitwhen i >= length
set char = SubString(saveCode, i, i+1)
set index = getCharIndex(char)
if (index >= 0) then
call writer.write(index, SaveCodeCharsetBase)
endif
set i = i + 1
endloop
call Inspect(writer.get())
call writer.destroy()
set char = null
endfunction
public function LoadForUnitEncoded takes unit owner, string saveCode returns nothing
local BSRW_BitStreamWriter writer = BSRW_BitStreamWriter.create()
local integer length = StringLength(saveCode)
local integer index = 0
local integer i = 0
local string character = ""
set i = 0
loop
exitwhen i >= length
set character = SubString(saveCode, i, i+1)
set index = getCharIndex(character)
if (index >= 0) then
call writer.write(index, SaveCodeCharsetBase)
endif
set i = i + 1
endloop
set character = writer.get()
call LoadForUnit(owner, character)
call writer.destroy()
set character = null
endfunction
public function SaveForUnitEncoded takes unit owner returns string
local string encoded = ""
local string bitString = SaveForUnit(owner)
local integer bitStringLength = StringLength(bitString)
local integer remainder = ModuloInteger(bitStringLength, SaveCodeCharsetBase)
local integer i = 0
local integer value = 0
local BSRW_BitStreamReader reader
// Padding up to the SaveCodeCharset base
if (remainder != 0) then
set i = SaveCodeCharsetBase - remainder
loop
exitwhen i <= 0
set bitString = bitString + "0"
set i = i - 1
endloop
endif
set reader = BSRW_BitStreamReader.create(bitString)
set i = 0
loop
exitwhen i >= bitStringLength
set value = reader.readInt(SaveCodeCharsetBase)
set encoded = encoded + SubString(SaveCodeCharset, value, value + 1)
set i = i + SaveCodeCharsetBase
endloop
call reader.destroy()
return encoded
endfunction
public function Initialize takes STKStore store, AssignTalentTreeFunction assignTalentTree returns nothing
local integer i = 0
local string char = ""
set Threshold2SizeName[0] = "SMALL"
set Threshold2SizeName[1] = "LARGE"
set Threshold4SizeName[0] = "SMALL"
set Threshold4SizeName[1] = "MEDIUM"
set Threshold4SizeName[2] = "LARGE"
set Threshold4SizeName[3] = "EXTRA"
set TalentTreeIdThreshold[0] = TALENTTREE_ID_THRESHOLD_SMALL
set TalentTreeIdThreshold[1] = TALENTTREE_ID_THRESHOLD_LARGE
set TalentIdThreshold[0] = TALENT_ID_THRESHOLD_SMALL
set TalentIdThreshold[1] = TALENT_ID_THRESHOLD_LARGE
set TalentRankThreshold[0] = TALENT_RANK_THRESHOLD_SMALL
set TalentRankThreshold[1] = TALENT_RANK_THRESHOLD_MEDIUM
set TalentRankThreshold[2] = TALENT_RANK_THRESHOLD_LARGE
set TalentRankThreshold[3] = TALENT_RANK_THRESHOLD_EXTRA
set TalentTreeIdThreshold[2] = 0
set TalentIdThreshold[2] = 0
set TalentRankThreshold[4] = 0
set Store = store
set AssignTalentTree = assignTalentTree
endfunction
endlibrary
library BSRW
globals
public constant integer MAX_BIT_LENGTH = 1000
endglobals
public struct BitStreamReader
private string data
private integer pointer
private integer array bitArray[MAX_BIT_LENGTH]
public string lastReadChunk
static method create takes string data returns BitStreamReader
local BitStreamReader bsr = BitStreamReader.allocate()
local integer length = StringLength(data)
local integer i = 0
set bsr.data = data
set bsr.pointer = 0
loop
exitwhen i >= length
set bsr.bitArray[i] = S2I(SubString(data, i, i+1))
set i = i + 1
endloop
return bsr
endmethod
public method readStr takes integer bitCount returns string
local string readBits = SubString(this.data, this.pointer, this.pointer + bitCount)
set this.pointer = this.pointer + bitCount
return readBits
endmethod
public method readInt takes integer bitCount returns integer
local integer value = 0
local integer end = this.pointer + bitCount
set this.lastReadChunk = SubString(this.data, this.pointer, end)
loop
exitwhen this.pointer >= end
set value = value * 2 + this.bitArray[this.pointer]
set this.pointer = this.pointer + 1
endloop
return value
endmethod
endstruct
public struct BitStreamWriter
private string data
private integer length
public string lastWrittenChunk
static method create takes nothing returns BitStreamWriter
local thistype this = thistype.allocate()
set this.data = ""
set this.length = 0
return this
endmethod
public method writeStream takes string bitStream returns nothing
set this.lastWrittenChunk = bitStream
set this.data = this.data + this.lastWrittenChunk
set this.length = this.length + StringLength(this.lastWrittenChunk)
endmethod
public method write takes integer value, integer bitCount returns nothing
local integer i = 0
set this.lastWrittenChunk = ""
loop
exitwhen i >= bitCount
set this.lastWrittenChunk = I2S(ModuloInteger(value, 2)) + this.lastWrittenChunk
set value = value / 2
set i = i + 1
endloop
if (value > 0) then
call BJDebugMsg("BitStreamWriter: Value could not be fully represented in " + I2S(bitCount) + " bits.")
endif
set this.data = this.data + this.lastWrittenChunk
set this.length = this.length + bitCount
endmethod
public method writeInt takes integer value returns nothing
call this.write(value, 32)
endmethod
public method get takes nothing returns string
return this.data
endmethod
public method flush takes nothing returns BitStreamWriter
set this.data = ""
set this.length = 0
set this.lastWrittenChunk = ""
return this
endmethod
endstruct
endlibrary
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