- Joined
- Sep 30, 2009
- Messages
- 698
Here is my new Skilltree System. Should be more than MUI and contain no leaks. If you find any bugs please send me a PM or VM or post in this thread.
Have Fun
Have Fun

JASS:
library SkilltreeInit initializer onInit
globals
constant real SKILLTREE_WIDTH = 64 * 7
constant real SKILLTREE_HEIGHT = 64 * 9
constant real SKILLTREE_SIZE = 0.5
constant real SKILLTREE_STEP = 64 * SKILLTREE_SIZE
constant real SKILLTREE_POS_X = -2200
constant real SKILLTREE_POS_Y = 2400
constant integer SKILLS_PER_ROW = 5
constant integer SKILLS_PER_COLUMN = 5
constant real BORDER_OFFSET_X = SKILLTREE_WIDTH / (SKILLS_PER_ROW * 2)
constant real BORDER_OFFSET_Y = SKILLTREE_HEIGHT / (SKILLS_PER_COLUMN * 2)
trigger TRACKABLE_HIT = CreateTrigger()
trigger TRACKABLE_HOVER = CreateTrigger()
hashtable SKILLTREE_HASHTABLE = InitHashtable()
key KEY_TRACKABLES
unit SKILLTREE_DUMMY = null
rect SKILLTREE_RECT = null
constant integer SKILLTREE_DUMMY_ID = 'stdy'
constant integer C_SKILL_TITLE = 0xFFFFFF
constant integer C_SKILL_NAME = 0xFFFFFF
constant integer C_SKILL_CURR = 0xFFFFFF
constant integer C_SKILL_NEXT = 0xFFFFFF
constant integer C_SKILL_DESCRIPTION = 0xFFFFFF
timer SKILLTREE_TIMER = null
boolean array IS_PLAYER_VIEWING
boolean SKILLTREE_TIMER_RUNNING = false
SkilltreeBoard array SkilltreeBoards
endglobals
private function CreateBackground takes nothing returns nothing
local real xMin = GetRectMinX(SKILLTREE_RECT)
local real yMin = GetRectMinY(SKILLTREE_RECT)
local real xMax = GetRectMaxX(SKILLTREE_RECT)
local real yMax = GetRectMaxY(SKILLTREE_RECT)
local real x = xMin
local real y = yMin
local integer xTimes = R2I((xMax - xMin) / 64)
local integer yTimes = R2I((yMax - yMin) / 64)
local integer xCur = 0
local integer yCur = 0
loop
exitwhen xCur >= xTimes
set yCur = 0
set y = yMin
loop
exitwhen yCur >= yTimes
set y = y + 64
call SetTerrainType(x, y, 'cOc1', 0, 1, 0)
set yCur = yCur + 1
endloop
set x = x + 64
set xCur = xCur + 1
endloop
endfunction
private function CreateBorder takes nothing returns nothing
local real x = SKILLTREE_POS_X - SKILLTREE_WIDTH /2
local real y = SKILLTREE_POS_Y + SKILLTREE_HEIGHT /2
local integer i = 0
call CreateDestructable('cwlu', x, y, 270, SKILLTREE_SIZE, 0)
set y = SKILLTREE_POS_Y - SKILLTREE_HEIGHT /2
call CreateDestructable('cwld', x, y, 270, SKILLTREE_SIZE, 0)
set x = SKILLTREE_POS_X + SKILLTREE_WIDTH /2
call CreateDestructable('cwrd', x, y, 270, SKILLTREE_SIZE, 0)
set y = SKILLTREE_POS_Y + SKILLTREE_HEIGHT /2
call CreateDestructable('cwru', x, y, 270, SKILLTREE_SIZE, 0)
set x = SKILLTREE_POS_X - SKILLTREE_WIDTH /2
set y = SKILLTREE_POS_Y - SKILLTREE_HEIGHT /2
loop
set i = i + 1
set y = y + SKILLTREE_STEP
call CreateDestructable('cwlf', x, y, 270, SKILLTREE_SIZE, 0)
exitwhen i >= SKILLTREE_HEIGHT / SKILLTREE_STEP - 1
endloop
set y = SKILLTREE_POS_Y + SKILLTREE_HEIGHT /2
set i = 0
loop
set i = i + 1
set x = x + SKILLTREE_STEP
call CreateDestructable('cwup', x, y, 270, SKILLTREE_SIZE, 0)
exitwhen i >= SKILLTREE_WIDTH / SKILLTREE_STEP - 1
endloop
set x = SKILLTREE_POS_X + SKILLTREE_WIDTH /2
set i = 0
loop
set i = i + 1
set y = y - SKILLTREE_STEP
call CreateDestructable('cwrg', x, y, 270, SKILLTREE_SIZE, 0)
exitwhen i >= SKILLTREE_HEIGHT / SKILLTREE_STEP - 1
endloop
set y = SKILLTREE_POS_Y - SKILLTREE_HEIGHT /2
set i = 0
loop
set i = i + 1
set x = x - SKILLTREE_STEP
call CreateDestructable('cwbt', x, y, 270, SKILLTREE_SIZE, 0)
exitwhen i >= SKILLTREE_WIDTH / SKILLTREE_STEP - 1
endloop
set SKILLTREE_RECT = Rect(SKILLTREE_POS_X - 2048 /2, SKILLTREE_POS_Y - 1024 /2, SKILLTREE_POS_X + 2048 /2, SKILLTREE_POS_Y + 1024 /2)
endfunction
private function onInit takes nothing returns nothing
call CreateBorder()
call CreateBackground()
set SKILLTREE_DUMMY = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), SKILLTREE_DUMMY_ID, SKILLTREE_POS_X, SKILLTREE_POS_Y, 0)
call UnitAddAbility(SKILLTREE_DUMMY, 'Aloc')
call TriggerAddAction(TRACKABLE_HIT, function SkillTrackable.onHitTracker)
call TriggerAddAction(TRACKABLE_HOVER, function SkillTrackable.onHoverTracker)
endfunction
endlibrary
JASS:
library SkilltreeAPI requires SkilltreeInit
// returns the level of a Skill for any unit.
function GetUnitSkillLevel takes unit u, SkillTemplate t returns integer
local Skill s = LoadInteger(SKILLTREE_HASHTABLE, GetHandleId(u), t)
return s.level
endfunction
// returns true if a unit has the Skill in any of its Skilltrees (even if it's not learned)
function HasUnitSkill takes unit u, SkillTemplate t returns boolean
return HaveSavedInteger(SKILLTREE_HASHTABLE, GetHandleId(u), t)
endfunction
// returns true if the unit meets the requirements for the Skills next level.
// If the unit does not have that Skill it will always return false.
function IsSkillLearnable takes unit u, SkillTemplate t returns boolean
local Skill s = LoadInteger(SKILLTREE_HASHTABLE, GetHandleId(u), t)
return t.check(s.st.u, s, s.level + 1) and s.st.skillpoints >= s.skillpointsNeeded and s.level < s.maxLevel
endfunction
// shows a Skilltree to the owner of the unit associated with it.
function ShowSkilltree takes Skilltree st, boolean visible returns nothing
call st.show(visible)
endfunction
// shows the Skilltree if it is not shown, or closes the Skilltree if it is shown.
function ShowSkilltreeSwitch takes Skilltree st returns nothing
call st.show(not st.visible)
endfunction
endlibrary
JASS:
library Skilltree requires SkilltreeInit, SkilltreeBoard, Skills
struct Skilltree[8000] // I think 160 instances are more than enough
Skill array skills[50] // 50 Skills per Skilltree.
static Skill array lastSkill
integer total
integer skillpoints
destructable selector
unit u
fogmodifier fm
boolean visible = false
string name
method operator owner takes nothing returns player
return GetOwningPlayer(.u)
endmethod
method update takes Skill s returns nothing
if .selector != null then
call RemoveDestructable(.selector)
endif
if s != 0 then
set .selector = CreateDestructable('cwsl', s.x, s.y, 270, .76, 0)
endif
if GetLocalPlayer() != .owner then
call ShowDestructable(.selector, false)
endif
call SkilltreeBoards[GetPlayerId(.owner)].update(s)
endmethod
method show takes boolean visible returns nothing
local integer i = 0
set .visible = visible
call SkilltreeBoards[GetPlayerId(.owner)].show(this, .visible)
set IS_PLAYER_VIEWING[GetPlayerId(.owner)] = .visible
if GetLocalPlayer() == .owner then
call ShowDestructable(.selector, .visible)
endif
call DestroyFogModifier(.fm)
if .visible then
set .fm = CreateFogModifierRect(.owner, FOG_OF_WAR_VISIBLE, SKILLTREE_RECT, false, false)
else
set .fm = CreateFogModifierRect(.owner, FOG_OF_WAR_MASKED, SKILLTREE_RECT, false, false)
set Skilltree.lastSkill[GetPlayerId(.owner)] = 0
if .selector != null then
call RemoveDestructable(.selector)
endif
if GetLocalPlayer() == .owner then
call ResetToGameCamera(0)
endif
endif
call FogModifierStart(.fm)
loop
if GetLocalPlayer() == .owner then
call ShowDestructable(skills[i].dest.dest, .visible)
endif
set i = i + 1
exitwhen i >= total
endloop
call ShowBorder(.owner, .visible)
if not SKILLTREE_TIMER_RUNNING then
if SKILLTREE_TIMER == null then
set SKILLTREE_TIMER = CreateTimer()
endif
set SKILLTREE_TIMER_RUNNING = true
call TimerStart(SKILLTREE_TIMER, 0.1, true, function thistype.setView)
endif
endmethod
static method setView takes nothing returns nothing
local integer i = 0
loop
if IS_PLAYER_VIEWING[GetPlayerId(Player(i))] then
if GetLocalPlayer() == Player(i) then
call SetCameraPosition(SKILLTREE_POS_X, SKILLTREE_POS_Y)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270, 0)
call SetCameraField(CAMERA_FIELD_ROTATION, 90, 0)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 1200, 0)
call SetCameraTargetController(SKILLTREE_DUMMY, 0, 0, false)
endif
endif
set i = i + 1
exitwhen i >= 12
endloop
endmethod
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set .u = u
if SkilltreeBoards[GetPlayerId(.owner)] == 0 then
set SkilltreeBoards[GetPlayerId(.owner)] = SkilltreeBoard.create(.owner)
set SkilltreeBoards[GetPlayerId(.owner)].bit = SkilltreeBoards[GetPlayerId(.owner)].board[0][25]
set SkilltreeBoards[GetPlayerId(.owner)].board.all.width = .2
endif
return this
endmethod
endstruct
endlibrary
JASS:
library SkilltreeBoard requires SubStringLastWord, Board
struct SkilltreeBoard
Board board
static BoardItem bit
static integer y
method addName takes Skill s returns nothing
local string n = ""
set n = s.name + " - Level " + I2S(s.level)
loop
set n = " " + n + " "
exitwhen StringLength(n) >= 50
endloop
set bit = board[0][y]
set bit.text = n
set bit.color = C_SKILL_NAME
endmethod
method addDescription takes Skill s returns nothing
local integer x = 0
local integer l = StringLength(s.description)
local string sub = ""
set y = y + 2
if l > 42 then
loop
set sub = SubStringLastWord(s.description, x, x + 42)
set bit = board[0][y]
set bit.text = sub
set bit.color = C_SKILL_DESCRIPTION
set x = x + StringLength(sub)
exitwhen l <= x + 42
set y = y + 1
endloop
if SubString(s.description, x, l) != "" then
set y = y + 1
set bit = board[0][y]
set bit.text = SubString(s.description, x, l)
set bit.color = C_SKILL_DESCRIPTION
endif
else
set bit = board[0][y]
set bit.text = s.description
set bit.color = C_SKILL_DESCRIPTION
endif
endmethod
method addStats takes Skill s returns nothing
local integer x = 0
local integer l = StringLength(s.stats)
local string sub = ""
set y = y + 2
if l > 42 then
loop
set sub = SubStringLastWord(s.stats, x, x + 42)
set bit = board[0][y]
set bit.text = sub
set bit.color = C_SKILL_CURR
set x = x + StringLength(sub)
exitwhen l <= x + 42
set y = y + 1
endloop
if SubString(s.stats, x, l) != "" then
set y = y + 1
set bit = board[0][y]
set bit.text = SubString(s.stats, x, l)
set bit.color = C_SKILL_CURR
endif
else
set bit = board[0][y]
set bit.text = s.stats
set bit.color = C_SKILL_CURR
endif
endmethod
method addRequirements takes Skill s returns nothing
local integer x = 0
local integer l = StringLength(s.requirements)
local string sub = ""
set y = y + 2
if l > 42 then
loop
set sub = SubStringLastWord(s.requirements, x, x + 42)
set bit = board[0][y]
set bit.text = sub
set bit.color = C_SKILL_NEXT
set x = x + StringLength(sub)
exitwhen l <= x + 42
set y = y + 1
endloop
if SubString(s.description, x, l) != "" then
set y = y + 1
set bit = board[0][y]
set bit.text = SubString(s.requirements, x, l)
set bit.color = C_SKILL_NEXT
endif
else
set bit = board[0][y]
set bit.text = s.requirements
set bit.color = C_SKILL_NEXT
endif
endmethod
method addStatsNext takes Skill s returns nothing
local integer x = 0
local integer l = StringLength(s.statsNext)
local string sub = ""
set y = y + 2
if l > 42 then
loop
set sub = SubStringLastWord(s.statsNext, x, x + 42)
set bit = board[0][y]
set bit.text = sub
set bit.color = C_SKILL_NEXT
set x = x + StringLength(sub)
exitwhen l <= x + 42
set y = y + 1
endloop
if SubString(s.statsNext, x, l) != "" then
set y = y + 1
set bit = board[0][y]
set bit.text = SubString(s.statsNext, x, l)
endif
else
set bit = board[0][y]
set bit.text = s.statsNext
set bit.color = C_SKILL_NEXT
endif
endmethod
method clearText takes nothing returns nothing
set board.title = ""
loop
set bit = board[0][y]
set bit.text = ""
set y = y + 1
exitwhen y > 25
endloop
set y = 0
endmethod
method update takes Skill s returns nothing
set y = 0
call .clearText()
set board.title = s.st.name
if s != 0 then
call .addName(s)
call .addDescription(s)
if s.level > 0 then
set y = y + 4
set bit = board[0][y]
set bit.text = "Current Level"
set bit.color = C_SKILL_CURR
call .addStats(s)
else
set y = y + 4
set bit = board[0][y]
set bit.text = "Skill not learned yet."
set bit.color = C_SKILL_CURR
endif
set y = y + 3
set bit = board[0][y]
set bit.text = "-------------------------------------------"
if s.level < s.maxLevel then
set y = y + 3
set bit = board[0][y]
set bit.text = "Next Level - Costs " + I2S(s.skillpointsNeeded) + " Skillpoints"
set bit.color = C_SKILL_NEXT
call .addRequirements(s)
call .addStatsNext(s)
else
set y = y + 2
set bit = board[0][y]
set bit.text = "Maximum Level reached for this Skill"
set bit.color = C_SKILL_NEXT
endif
set y = 24
set bit = board[0][y]
set bit.text = "Skillpoints: " + I2S(s.st.skillpoints)
set bit.color = C_SKILL_NEXT
else
set y = 2
set bit = board[0][y]
set bit.text = "No skill selected."
endif
set bit = 0
set y = 0
endmethod
method show takes Skilltree st, boolean visible returns nothing
set .board.visible[st.owner] = visible
set .board.minimized = false
call .update(0)
endmethod
static method create takes player p returns thistype
local thistype this = thistype.allocate()
set .board = Board.create()
return this
endmethod
endstruct
endlibrary
JASS:
library Skills requires SkillDestructables, SkillTrackables, SimError
interface SkillTemplate
method check takes unit u, Skill s, integer newLevel returns boolean defaults true
method learn takes unit u, Skill s, integer newLevel returns nothing defaults nothing
string name
integer posX
integer posY
integer icon
integer icon_unknown
integer maxLevel
integer skillpointsNeeded
endinterface
struct Skill
Skilltree st
SkillDestructable dest
SkillTrackable tracker
SkillTemplate t
integer posX
integer posY
integer icon
integer icon_unknown
integer maxLevel
real x
real y
integer level
integer skillpointsNeeded
string name
string description
string stats
string requirements
string statsNext
method clickSkill takes nothing returns nothing
if .t.check(.st.u, this, .level + 1) and .st.skillpoints >= .skillpointsNeeded and .level < .maxLevel then
set .level = .level + 1
set .st.skillpoints = .st.skillpoints - .skillpointsNeeded
if .level == 1 then
call RemoveDestructable(.dest.dest)
set .dest.dest = CreateDestructable(.icon, .x, .y, 270, SKILLTREE_SIZE * 4/3, 0)
endif
call .t.learn(.st.u, this, .level)
call .st.update(this)
else
call SimError(.st.owner, "Can't learn this Skill")
endif
endmethod
method addToSkilltree takes Skilltree st returns nothing
set .st = st
set .st.skills[st.total] = this
set .st.total = st.total + 1
set .x = SKILLTREE_POS_X + BORDER_OFFSET_X - SKILLTREE_WIDTH / 2 + SKILLTREE_WIDTH / SKILLS_PER_ROW * .posX
set .y = SKILLTREE_POS_Y + BORDER_OFFSET_Y - SKILLTREE_HEIGHT / 2 + SKILLTREE_HEIGHT / SKILLS_PER_COLUMN * .posY
set .tracker = SkillTrackable.create(this)
set .dest = SkillDestructable.create(this)
call SaveInteger(SKILLTREE_HASHTABLE, GetHandleId(.st.u), .t, this)
if GetLocalPlayer() != .st.owner then
call ShowDestructable(.dest.dest, false)
endif
if GetLocalPlayer() == .st.owner then
call ShowDestructable(.dest.dest, .st.visible)
endif
endmethod
private method destroy takes nothing returns nothing
call .dest.destroy()
call .tracker.destroy()
call .deallocate()
endmethod
static method create takes SkillTemplate t returns thistype
local thistype this = thistype.allocate()
set .t = t
set .name = t.name
set .posX = t.posX
set .posY = t.posY
set .icon = t.icon
set .icon_unknown = t.icon_unknown
set .maxLevel = t.maxLevel
set .skillpointsNeeded = t.skillpointsNeeded
return this
endmethod
endstruct
endlibrary
JASS:
library SkillDestructables
struct SkillDestructable
Skill s
destructable dest
integer icon
integer icon_unknown
real x
real y
static method create takes Skill s returns thistype
local thistype this = thistype.allocate()
set .s = s
set .x = .s.x
set .y = .s.y
set .icon = .s.icon
set .icon_unknown = .s.icon_unknown
if .s.level > 0 then
set .dest = CreateDestructable(.icon, .x, .y, 270, SKILLTREE_SIZE * 4/3, 0)
else
set .dest = CreateDestructable(.icon_unknown, .x, .y, 270, SKILLTREE_SIZE * 4/3, 0)
endif
return this
endmethod
endstruct
endlibrary
JASS:
library SkillTrackables requires SimError
struct SkillTrackable
Skill s
trackable tracker
player owner
real x
real y
string path
static method get takes trackable tr returns thistype
return LoadInteger(SKILLTREE_HASHTABLE, KEY_TRACKABLES, GetHandleId(tr))
endmethod
static method create takes Skill s returns thistype
local thistype this = thistype.allocate()
set .s = s
set .x = .s.x
set .y = .s.y
set .owner = .s.st.owner
if GetLocalPlayer() == .owner then
set path = "Skilltree\\Tracker.mdl"
endif
set .tracker = CreateTrackable(.path, .x, .y, 0)
call SaveInteger(SKILLTREE_HASHTABLE, KEY_TRACKABLES, GetHandleId(.tracker), this)
call TriggerRegisterTrackableHitEvent(TRACKABLE_HIT, .tracker)
call TriggerRegisterTrackableTrackEvent(TRACKABLE_HOVER, .tracker)
return this
endmethod
static method onHitTracker takes nothing returns nothing
local thistype this = .get(GetTriggeringTrackable())
if .s.st.visible then
call .s.clickSkill()
endif
endmethod
static method onHoverTracker takes nothing returns nothing
local thistype this = .get(GetTriggeringTrackable())
if .s.st.visible and Skilltree.lastSkill[GetPlayerId(.owner)] != this then
set Skilltree.lastSkill[GetPlayerId(.owner)] = this
call .s.st.update(.s)
endif
endmethod
endstruct
endlibrary
- the border is now only visible to players looking at a Skilltree
- the Skill icons were shown to all players instead of only the owner, it should be fixed now (thx to aaron79 for testing help)
- added a requirement to SkilltreeAPI
- the Skill icons were shown to all players instead of only the owner, it should be fixed now (thx to aaron79 for testing help)
- added a requirement to SkilltreeAPI
- remade SkilltreeBoard again so it works now correctly
- one board per player now instead of one for all
- some minor changes
- one board per player now instead of one for all
- some minor changes
- removed null presets (thx to Bribe)
- all Skilltrees use the same Board now instead of one Board for each Skilltree
- implemented SimError
- some minor changes
- all Skilltrees use the same Board now instead of one Board for each Skilltree
- implemented SimError
- some minor changes
- added more functionality
- added another skill to the testing skilltree
- the board is now automatically resized based on the length of the description etc
- added another skill to the testing skilltree
- the board is now automatically resized based on the length of the description etc
- first release
- give proper Credits
- add description etc to the code
- make a better testmap
- add more API
- add more configurables
- make the system more user friendly
- rename some variables
- remove the variable chaos
- slay the dragon and rescue the princess
- add description etc to the code
- make a better testmap
- add more API
- add more configurables
- make the system more user friendly
- rename some variables
- remove the variable chaos
- slay the dragon and rescue the princess
Attachments
Last edited: