mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-06 11:34:15 -04:00
* d2ui.Frame refactor * removed unneccessery d2asset.AssetManager argument from d2ui.NewUIFrame * d2ui.Frame refactor * removed unneccessery d2asset.AssetManager argument from d2ui.NewUIFrame Co-authored-by: M. Sz <mszeptuch@protonmail.com> Co-authored-by: Tim Sarbin <tim.sarbin@gmail.com> Co-authored-by: gravestench <dknuth0101@gmail.com>
513 lines
12 KiB
Go
513 lines
12 KiB
Go
package d2player
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
|
)
|
|
|
|
const (
|
|
tabButtonX = 628
|
|
tabButton0Y = 385
|
|
tabButton1Y = 277
|
|
tabButton2Y = 170
|
|
|
|
availSPLabelX = 677
|
|
availSPLabelY = 72
|
|
|
|
skillCloseButtonXLeft = 416
|
|
skillCloseButtonXMiddle = 501
|
|
skillCloseButtonXRight = 572
|
|
skillCloseButtonY = 449
|
|
)
|
|
|
|
const (
|
|
firstTab = iota
|
|
secondTab
|
|
thirdTab
|
|
numTabs
|
|
)
|
|
|
|
const (
|
|
tabIndexOffset = 4
|
|
|
|
frameOffsetTop = 4
|
|
frameOffsetBottom = 6
|
|
)
|
|
|
|
const (
|
|
frameCommonTabTopLeft = iota
|
|
frameCommonTabTopRight
|
|
frameCommonTabBottomLeft
|
|
frameCommonTabBottomRight
|
|
)
|
|
|
|
const (
|
|
frameSelectedTab1Full = 7
|
|
frameSelectedTab2Top = 9 // tab2 top and bottom portions are in 2 frames :(
|
|
frameSelectedTab2Bottom = 11
|
|
frameSelectedTab3Full = 13
|
|
)
|
|
|
|
const (
|
|
remainingPointsLabelX, remainingPointsLabelY = 677, 128
|
|
)
|
|
|
|
const (
|
|
skillTreePanelX = 401
|
|
skillTreePanelY = 64
|
|
)
|
|
|
|
const (
|
|
skillIconGreySat = 0.2
|
|
skillIconGreyBright = 0.44
|
|
)
|
|
|
|
type skillTreeTab struct {
|
|
buttonText string
|
|
button *d2ui.Button
|
|
closeButtonPosX int
|
|
}
|
|
|
|
func (st *skillTreeTab) createButton(uiManager *d2ui.UIManager, x, y int) {
|
|
st.button = uiManager.NewButton(d2ui.ButtonTypeSkillTreeTab, st.buttonText)
|
|
st.button.SetPosition(x, y)
|
|
}
|
|
|
|
type skillTreeHeroTypeResources struct {
|
|
skillSprite *d2ui.Sprite
|
|
skillIconPath string
|
|
skillPanel *d2ui.Sprite
|
|
skillPanelPath string
|
|
}
|
|
|
|
func newSkillTree(
|
|
skills map[int]*d2hero.HeroSkill,
|
|
heroClass d2enum.Hero,
|
|
hero *d2hero.HeroStatsState,
|
|
asset *d2asset.AssetManager,
|
|
l d2util.LogLevel,
|
|
ui *d2ui.UIManager,
|
|
) *skillTree {
|
|
st := &skillTree{
|
|
skills: skills,
|
|
heroClass: heroClass,
|
|
asset: asset,
|
|
uiManager: ui,
|
|
originX: skillTreePanelX,
|
|
originY: skillTreePanelY,
|
|
stats: hero,
|
|
tab: [numTabs]*skillTreeTab{
|
|
{},
|
|
{},
|
|
{},
|
|
},
|
|
l: l,
|
|
}
|
|
|
|
st.Logger = d2util.NewLogger()
|
|
st.Logger.SetLevel(l)
|
|
st.Logger.SetPrefix(logPrefix)
|
|
|
|
return st
|
|
}
|
|
|
|
type skillTree struct {
|
|
resources *skillTreeHeroTypeResources
|
|
asset *d2asset.AssetManager
|
|
uiManager *d2ui.UIManager
|
|
skills map[int]*d2hero.HeroSkill
|
|
skillIcons []*skillIcon
|
|
heroClass d2enum.Hero
|
|
availSPLabel *d2ui.Label
|
|
closeButton *d2ui.Button
|
|
tab [numTabs]*skillTreeTab
|
|
remainingPoints *d2ui.Label
|
|
isOpen bool
|
|
originX int
|
|
originY int
|
|
selectedTab int
|
|
onCloseCb func()
|
|
panelGroup *d2ui.WidgetGroup
|
|
iconGroup *d2ui.WidgetGroup
|
|
panel *d2ui.CustomWidget
|
|
stats *d2hero.HeroStatsState
|
|
|
|
*d2util.Logger
|
|
l d2util.LogLevel
|
|
}
|
|
|
|
func (s *skillTree) load() {
|
|
s.panelGroup = s.uiManager.NewWidgetGroup(d2ui.RenderPrioritySkilltree)
|
|
s.iconGroup = s.uiManager.NewWidgetGroup(d2ui.RenderPrioritySkilltreeIcon)
|
|
|
|
s.panel = s.uiManager.NewCustomWidget(s.Render, 400, 600)
|
|
s.panelGroup.AddWidget(s.panel)
|
|
|
|
frame := s.uiManager.NewUIFrame(d2ui.FrameRight)
|
|
s.panelGroup.AddWidget(frame)
|
|
|
|
s.closeButton = s.uiManager.NewButton(d2ui.ButtonTypeSquareClose, "")
|
|
s.closeButton.SetVisible(false)
|
|
s.closeButton.OnActivated(func() { s.Close() })
|
|
s.panelGroup.AddWidget(s.closeButton)
|
|
|
|
s.remainingPoints = s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
|
|
s.remainingPoints.SetPosition(remainingPointsLabelX, remainingPointsLabelY)
|
|
s.remainingPoints.Alignment = d2ui.HorizontalAlignCenter
|
|
s.remainingPoints.SetText(strconv.Itoa(s.stats.SkillPoints))
|
|
s.panelGroup.AddWidget(s.remainingPoints)
|
|
|
|
if err := s.setHeroTypeResourcePath(); err != nil {
|
|
s.Error(err.Error())
|
|
}
|
|
|
|
s.loadForHeroType()
|
|
|
|
for _, skill := range s.skills {
|
|
si := newSkillIcon(s.uiManager, s.resources.skillSprite, s.l, skill)
|
|
s.skillIcons = append(s.skillIcons, si)
|
|
s.iconGroup.AddWidget(si)
|
|
}
|
|
|
|
s.panelGroup.SetVisible(false)
|
|
s.setTab(0)
|
|
s.iconGroup.SetVisible(false)
|
|
}
|
|
|
|
func (s *skillTree) loadForHeroType() {
|
|
sp, err := s.uiManager.NewSprite(s.resources.skillPanelPath, d2resource.PaletteSky)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
}
|
|
|
|
s.resources.skillPanel = sp
|
|
|
|
si, err := s.uiManager.NewSprite(s.resources.skillIconPath, d2resource.PaletteSky)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
}
|
|
|
|
s.resources.skillSprite = si
|
|
|
|
s.tab[firstTab].createButton(s.uiManager, tabButtonX, tabButton0Y)
|
|
s.tab[firstTab].button.OnActivated(func() { s.setTab(firstTab) })
|
|
s.panelGroup.AddWidget(s.tab[firstTab].button)
|
|
|
|
s.tab[secondTab].createButton(s.uiManager, tabButtonX, tabButton1Y)
|
|
s.tab[secondTab].button.OnActivated(func() { s.setTab(secondTab) })
|
|
s.panelGroup.AddWidget(s.tab[secondTab].button)
|
|
|
|
s.tab[thirdTab].createButton(s.uiManager, tabButtonX, tabButton2Y)
|
|
s.tab[thirdTab].button.OnActivated(func() { s.setTab(thirdTab) })
|
|
s.panelGroup.AddWidget(s.tab[thirdTab].button)
|
|
|
|
s.availSPLabel = s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
|
|
s.availSPLabel.SetPosition(availSPLabelX, availSPLabelY)
|
|
s.availSPLabel.Alignment = d2ui.HorizontalAlignCenter
|
|
s.availSPLabel.SetText(s.makeTabString("StrSklTree1", "StrSklTree2", "StrSklTree3"))
|
|
s.panelGroup.AddWidget(s.availSPLabel)
|
|
}
|
|
|
|
type heroTabData struct {
|
|
resources *skillTreeHeroTypeResources
|
|
str1, str2, str3 string
|
|
closeButtonPos [numTabs]int
|
|
}
|
|
|
|
func (s *skillTree) makeTabString(keys ...interface{}) string {
|
|
translations := make([]interface{}, len(keys))
|
|
|
|
token := "%s"
|
|
format := token
|
|
|
|
for idx, key := range keys {
|
|
if idx > 0 {
|
|
format += "\n" + token
|
|
}
|
|
|
|
translations[idx] = s.asset.TranslateString(key.(string))
|
|
}
|
|
|
|
return fmt.Sprintf(format, translations...)
|
|
}
|
|
|
|
func makeCloseButtonPos(close1, close2, close3 int) [numTabs]int {
|
|
return [numTabs]int{close1, close2, close3}
|
|
}
|
|
|
|
func (s *skillTree) getTab(class d2enum.Hero) *heroTabData {
|
|
tabMap := map[d2enum.Hero]*heroTabData{
|
|
d2enum.HeroBarbarian: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelBarbarian,
|
|
skillIconPath: d2resource.BarbarianSkills,
|
|
},
|
|
s.makeTabString("StrSklTree21", "StrSklTree4"),
|
|
s.makeTabString("StrSklTree21", "StrSklTree22"),
|
|
s.makeTabString("StrSklTree20"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXLeft,
|
|
skillCloseButtonXRight),
|
|
},
|
|
d2enum.HeroNecromancer: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelNecromancer,
|
|
skillIconPath: d2resource.NecromancerSkills,
|
|
},
|
|
s.makeTabString("StrSklTree19"),
|
|
s.makeTabString("StrSklTree17", "StrSklTree18", "StrSklTree5"),
|
|
s.makeTabString("StrSklTree16", "StrSklTree5"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXLeft,
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXLeft),
|
|
},
|
|
d2enum.HeroPaladin: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelPaladin,
|
|
skillIconPath: d2resource.PaladinSkills,
|
|
},
|
|
s.makeTabString("StrSklTree15", "StrSklTree4"),
|
|
s.makeTabString("StrSklTree14", "StrSklTree13"),
|
|
s.makeTabString("StrSklTree12", "StrSklTree13"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXLeft,
|
|
skillCloseButtonXMiddle,
|
|
skillCloseButtonXLeft),
|
|
},
|
|
d2enum.HeroAssassin: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelAssassin,
|
|
skillIconPath: d2resource.AssassinSkills,
|
|
},
|
|
s.makeTabString("StrSklTree30"),
|
|
s.makeTabString("StrSklTree31", "StrSklTree32"),
|
|
s.makeTabString("StrSklTree33", "StrSklTree34"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXMiddle,
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXLeft),
|
|
},
|
|
d2enum.HeroSorceress: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelSorcerer,
|
|
skillIconPath: d2resource.SorcererSkills,
|
|
},
|
|
s.makeTabString("StrSklTree25", "StrSklTree5"),
|
|
s.makeTabString("StrSklTree24", "StrSklTree5"),
|
|
s.makeTabString("StrSklTree23", "StrSklTree5"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXLeft,
|
|
skillCloseButtonXLeft,
|
|
skillCloseButtonXRight),
|
|
},
|
|
d2enum.HeroAmazon: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelAmazon,
|
|
skillIconPath: d2resource.AmazonSkills,
|
|
},
|
|
s.makeTabString("StrSklTree10", "StrSklTree11", "StrSklTree4"),
|
|
s.makeTabString("StrSklTree8", "StrSklTree9", "StrSklTree4"),
|
|
s.makeTabString("StrSklTree6", "StrSklTree7", "StrSklTree4"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXMiddle,
|
|
skillCloseButtonXLeft),
|
|
},
|
|
d2enum.HeroDruid: {
|
|
&skillTreeHeroTypeResources{
|
|
skillPanelPath: d2resource.SkillsPanelDruid,
|
|
skillIconPath: d2resource.DruidSkills,
|
|
},
|
|
s.makeTabString("StrSklTree26"),
|
|
s.makeTabString("StrSklTree27", "StrSklTree28"),
|
|
s.makeTabString("StrSklTree29"),
|
|
makeCloseButtonPos(
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXRight,
|
|
skillCloseButtonXRight),
|
|
},
|
|
}
|
|
|
|
return tabMap[class]
|
|
}
|
|
|
|
func (s *skillTree) setHeroTypeResourcePath() error {
|
|
entry := s.getTab(s.heroClass)
|
|
|
|
if entry == nil {
|
|
return errors.New("unknown hero type")
|
|
}
|
|
|
|
s.resources = entry.resources
|
|
s.tab[firstTab].buttonText = entry.str1
|
|
s.tab[secondTab].buttonText = entry.str2
|
|
s.tab[thirdTab].buttonText = entry.str3
|
|
|
|
for i := 0; i < numTabs; i++ {
|
|
s.tab[i].closeButtonPosX = entry.closeButtonPos[i]
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Toggle the skill tree visibility
|
|
func (s *skillTree) Toggle() {
|
|
s.Info("SkillTree toggled")
|
|
|
|
if s.isOpen {
|
|
s.Close()
|
|
} else {
|
|
s.Open()
|
|
}
|
|
}
|
|
|
|
// Close the skill tree
|
|
func (s *skillTree) Close() {
|
|
s.isOpen = false
|
|
|
|
s.panelGroup.SetVisible(false)
|
|
s.iconGroup.SetVisible(false)
|
|
|
|
s.onCloseCb()
|
|
}
|
|
|
|
// Open the skill tree
|
|
func (s *skillTree) Open() {
|
|
s.isOpen = true
|
|
|
|
s.panelGroup.SetVisible(true)
|
|
s.iconGroup.SetVisible(true)
|
|
|
|
// we only want to enable the icons of our current tab again
|
|
s.setTab(s.selectedTab)
|
|
}
|
|
|
|
func (s *skillTree) IsOpen() bool {
|
|
return s.isOpen
|
|
}
|
|
|
|
// Set the callback run on closing the skilltree
|
|
func (s *skillTree) SetOnCloseCb(cb func()) {
|
|
s.onCloseCb = cb
|
|
}
|
|
|
|
func (s *skillTree) setTab(tab int) {
|
|
s.selectedTab = tab
|
|
s.closeButton.SetPosition(s.tab[tab].closeButtonPosX, skillCloseButtonY)
|
|
|
|
for _, si := range s.skillIcons {
|
|
si.SetVisible(si.skill.SkillPage == tab+1)
|
|
}
|
|
}
|
|
|
|
func (s *skillTree) renderPanelSegment(
|
|
target d2interface.Surface,
|
|
frame int) {
|
|
if err := s.resources.skillPanel.SetCurrentFrame(frame); err != nil {
|
|
s.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
s.resources.skillPanel.Render(target)
|
|
}
|
|
|
|
func (s *skillTree) renderTabCommon(target d2interface.Surface) {
|
|
skillPanel := s.resources.skillPanel
|
|
x, y := s.originX, s.originY
|
|
|
|
// top
|
|
w, h, err := skillPanel.GetFrameSize(frameCommonTabTopLeft)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
y += h
|
|
|
|
skillPanel.SetPosition(x, y)
|
|
s.renderPanelSegment(target, frameCommonTabTopLeft)
|
|
|
|
skillPanel.SetPosition(x+w, y)
|
|
s.renderPanelSegment(target, frameCommonTabTopRight)
|
|
|
|
// bottom
|
|
_, h, err = skillPanel.GetFrameSize(frameCommonTabBottomLeft)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
y += h
|
|
|
|
skillPanel.SetPosition(x, y)
|
|
s.renderPanelSegment(target, frameCommonTabBottomLeft)
|
|
|
|
skillPanel.SetPosition(x+w, y)
|
|
s.renderPanelSegment(target, frameCommonTabBottomRight)
|
|
}
|
|
|
|
func (s *skillTree) renderTab(target d2interface.Surface, tab int) {
|
|
topFrame := frameOffsetTop + (tabIndexOffset * tab)
|
|
bottomFrame := frameOffsetBottom + (tabIndexOffset * tab)
|
|
|
|
skillPanel := s.resources.skillPanel
|
|
x, y := s.originX, s.originY
|
|
|
|
// top
|
|
_, h0, err := skillPanel.GetFrameSize(topFrame)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
y += h0
|
|
|
|
skillPanel.SetPosition(x, y)
|
|
s.renderPanelSegment(target, topFrame)
|
|
|
|
// bottom
|
|
w, h1, err := skillPanel.GetFrameSize(bottomFrame)
|
|
if err != nil {
|
|
s.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
skillPanel.SetPosition(x, y+h1)
|
|
|
|
s.renderPanelSegment(target, bottomFrame)
|
|
|
|
// tab button highlighted
|
|
switch tab {
|
|
case firstTab:
|
|
skillPanel.SetPosition(x+w, y+h1)
|
|
s.renderPanelSegment(target, frameSelectedTab1Full)
|
|
case secondTab:
|
|
x += w
|
|
skillPanel.SetPosition(x, s.originY+h0)
|
|
s.renderPanelSegment(target, frameSelectedTab2Top)
|
|
|
|
skillPanel.SetPosition(x, y+h1)
|
|
s.renderPanelSegment(target, frameSelectedTab2Bottom)
|
|
case thirdTab:
|
|
skillPanel.SetPosition(x+w, y)
|
|
s.renderPanelSegment(target, frameSelectedTab3Full)
|
|
}
|
|
}
|
|
|
|
// Render the skill tree panel
|
|
func (s *skillTree) Render(target d2interface.Surface) {
|
|
s.renderTabCommon(target)
|
|
s.renderTab(target, s.selectedTab)
|
|
}
|