1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-30 02:55:23 +00:00
OpenDiablo2/d2game/d2player/skilltree.go
juander-ux e5dae4e5d8
Inital skilltree panel implementation (#782)
* d2ui/UIFrame: Refactor into its own class

it's not useful to have the handling of frames for the
inventory/herostate/skilltree/quest panels individually in each of
those.

* d2ui/button: Fix crash when a buttonlayout was not allowing FrameChange

When AllowFrameChange is false we do not create pressedSurface. So if we
press the button the game will crash.

* d2ui/button: Allow label-only buttons

At least for the skillmenu we need buttons were the graphic size does
not match the buttonsize. So let's render the graphic in there and make
the button label only.

* d2hero/hero_state_factory: Give all heroes their class specific skills

* d2player/gamecontrols: Fix wrong inventory/stats layouts for exp chars

For Druid/Assassin the inventory frame was rendered for a 640x480
resolution. This brings it in line with all other characters.

* d2player: Add inital Skilltree panel

* d2player/game_controls: Enable skilltree

Note here, that the inventory panel and skilltree panel can overlap.

* d2player/skilltree: Add skillicon rendering

Note here, that I couldn't figure out how to render them dark if no
skillpoints are invested.

Signed-off-by: juander <juander@rumtueddeln.de>
2020-10-22 12:54:45 -04:00

416 lines
11 KiB
Go

package d2player
import (
"fmt"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"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
SkillIconXOff = 346
SkillIconYOff = 59
SkillIconDistX = 69
SkillIconDistY = 68
)
type SkillTreeTab struct {
buttonText string
button *d2ui.Button
}
func (st *SkillTreeTab) CreateButton(uiManager *d2ui.UIManager, x int, y int) {
st.button = uiManager.NewButton(d2ui.ButtonTypeSkillTreeTab, st.buttonText)
st.button.SetVisible(false)
st.button.SetPosition(x, y)
}
type SkillTreeHeroTypeResources struct {
skillIcon *d2ui.Sprite
skillIconPath string
skillPanel *d2ui.Sprite
skillPanelPath string
}
type SkillTree struct {
resources *SkillTreeHeroTypeResources
asset *d2asset.AssetManager
renderer d2interface.Renderer
guiManager *d2gui.GuiManager
uiManager *d2ui.UIManager
layout *d2gui.Layout
skills map[int]*d2hero.HeroSkill
heroClass d2enum.Hero
frame *d2ui.UIFrame
availSPLabel *d2ui.Label
tab [3]*SkillTreeTab
isOpen bool
originX int
originY int
selectedTab int
}
func NewSkillTree(
skills map[int]*d2hero.HeroSkill,
heroClass d2enum.Hero,
asset *d2asset.AssetManager,
renderer d2interface.Renderer,
ui *d2ui.UIManager,
guiManager *d2gui.GuiManager,
) *SkillTree {
st := &SkillTree {
skills: skills,
heroClass: heroClass,
asset: asset,
renderer: renderer,
uiManager: ui,
guiManager: guiManager,
originX: 401,
originY: 64,
tab: [3]*SkillTreeTab{
{},
{},
{},
},
}
return st
}
func (s *SkillTree) Load() {
s.frame = d2ui.NewUIFrame(s.asset, s.uiManager, d2ui.FrameRight)
s.setHeroTypeResourcePath()
s.LoadForHeroType()
s.setTab(0)
}
func (s *SkillTree) LoadForHeroType() {
sp, err := s.uiManager.NewSprite(s.resources.skillPanelPath, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
s.resources.skillPanel = sp
si, err := s.uiManager.NewSprite(s.resources.skillIconPath, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
s.resources.skillIcon = si
s.tab[0].CreateButton(s.uiManager, TabButtonX, TabButton0Y)
s.tab[0].button.OnActivated(func() { s.setTab(0) })
s.tab[1].CreateButton(s.uiManager, TabButtonX, TabButton1Y)
s.tab[1].button.OnActivated(func() { s.setTab(1) })
s.tab[2].CreateButton(s.uiManager, TabButtonX, TabButton2Y)
s.tab[2].button.OnActivated(func() { s.setTab(2) })
s.availSPLabel = s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
s.availSPLabel.SetPosition(AvailSPLabelX, AvailSPLabelY)
s.availSPLabel.Alignment = d2gui.HorizontalAlignCenter
s.availSPLabel.SetText(fmt.Sprintf("%s\n%s\n%s",
d2tbl.TranslateString("StrSklTree1"),
d2tbl.TranslateString("StrSklTree2"),
d2tbl.TranslateString("StrSklTree3"),
))
}
func (s *SkillTree) setHeroTypeResourcePath() {
var res *SkillTreeHeroTypeResources
switch s.heroClass {
case d2enum.HeroBarbarian:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelBarbarian,
skillIconPath: d2resource.BarbarianSkills,
}
s.tab[0].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree21"),
d2tbl.TranslateString("StrSklTree4"))
s.tab[1].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree21"),
d2tbl.TranslateString("StrSklTree22"))
s.tab[2].buttonText = d2tbl.TranslateString("StrSklTree20")
case d2enum.HeroNecromancer:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelNecromancer,
skillIconPath: d2resource.NecromancerSkills,
}
s.tab[0].buttonText = d2tbl.TranslateString("StrSklTree19")
s.tab[1].buttonText = fmt.Sprintf("%s\n%s\n%s",
d2tbl.TranslateString("StrSklTree17"),
d2tbl.TranslateString("StrSklTree18"),
d2tbl.TranslateString("StrSklTree5"))
s.tab[2].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree16"),
d2tbl.TranslateString("StrSklTree5"))
case d2enum.HeroPaladin:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelPaladin,
skillIconPath: d2resource.PaladinSkills,
}
s.tab[0].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree15"),
d2tbl.TranslateString("StrSklTree4"))
s.tab[1].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree14"),
d2tbl.TranslateString("StrSklTree13"))
s.tab[2].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree12"),
d2tbl.TranslateString("StrSklTree13"))
case d2enum.HeroAssassin:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelAssassin,
skillIconPath: d2resource.AssassinSkills,
}
s.tab[0].buttonText = d2tbl.TranslateString("StrSklTree30")
s.tab[1].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree31"),
d2tbl.TranslateString("StrSklTree32"))
s.tab[2].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree33"),
d2tbl.TranslateString("StrSklTree34"))
case d2enum.HeroSorceress:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelSorcerer,
skillIconPath: d2resource.SorcererSkills,
}
s.tab[0].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree25"),
d2tbl.TranslateString("StrSklTree5"))
s.tab[1].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree24"),
d2tbl.TranslateString("StrSklTree5"))
s.tab[2].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree23"),
d2tbl.TranslateString("StrSklTree5"))
case d2enum.HeroAmazon:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelAmazon,
skillIconPath: d2resource.AmazonSkills,
}
s.tab[0].buttonText = fmt.Sprintf("%s\n%s\n%s",
d2tbl.TranslateString("StrSklTree10"),
d2tbl.TranslateString("StrSklTree11"),
d2tbl.TranslateString("StrSklTree4"))
s.tab[1].buttonText = fmt.Sprintf("%s\n%s\n%s",
d2tbl.TranslateString("StrSklTree8"),
d2tbl.TranslateString("StrSklTree9"),
d2tbl.TranslateString("StrSklTree4"))
s.tab[2].buttonText = fmt.Sprintf("%s\n%s\n%s",
d2tbl.TranslateString("StrSklTree6"),
d2tbl.TranslateString("StrSklTree7"),
d2tbl.TranslateString("StrSklTree4"))
case d2enum.HeroDruid:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelDruid,
skillIconPath: d2resource.DruidSkills,
}
s.tab[0].buttonText = d2tbl.TranslateString("StrSklTree26")
s.tab[1].buttonText = fmt.Sprintf("%s\n%s",
d2tbl.TranslateString("StrSklTree27"),
d2tbl.TranslateString("StrSklTree28"))
s.tab[2].buttonText = d2tbl.TranslateString("StrSklTree29")
default:
log.Fatal("Unknown Hero Type")
}
s.resources = res
}
func (s *SkillTree) Toggle() {
fmt.Println("SkillTree toggled")
if s.isOpen {
s.Close()
} else {
s.Open()
}
}
func (s *SkillTree) Close() {
s.isOpen = false
s.guiManager.SetLayout(nil)
for i:=0; i < 3; i++ {
s.tab[i].button.SetVisible(false)
}
}
func (s *SkillTree) Open() {
s.isOpen = true
if s.layout == nil {
s.layout = d2gui.CreateLayout(s.renderer, d2gui.PositionTypeHorizontal, s.asset)
}
for i:=0; i < 3; i++ {
s.tab[i].button.SetVisible(true)
}
s.guiManager.SetLayout(s.layout)
}
func (s *SkillTree) IsOpen() bool {
return s.isOpen
}
func (s *SkillTree) setTab(tab int) {
s.selectedTab = tab
}
func (s *SkillTree) renderPanelSegment(
target d2interface.Surface,
frame int) error {
if err := s.resources.skillPanel.SetCurrentFrame(frame); err != nil {
return err
}
if err := s.resources.skillPanel.Render(target); err != nil {
return err
}
return nil
}
func (s *SkillTree) renderTabCommon (target d2interface.Surface) error {
skillPanel := s.resources.skillPanel
x, y := s.originX, s.originY
// top
w, h, err := skillPanel.GetFrameSize(0)
if err != nil {
return err
}
y += h
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, 0); err != nil {
return err
}
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 1); err != nil {
return err
}
// bottom
_, h, err = skillPanel.GetFrameSize(2)
if err != nil {
return err
}
y += h
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, 2); err != nil {
return err
}
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 3); err != nil {
return err
}
// available skill points label
s.availSPLabel.Render(target)
return nil
}
func (s *SkillTree) renderTab (target d2interface.Surface, tab int) error {
var frameID [2]int
frameID[0] = 4 + (4*tab)
frameID[1] = 6 + (4*tab)
skillPanel := s.resources.skillPanel
x, y := s.originX, s.originY
// top
_, h0, err := skillPanel.GetFrameSize(frameID[0])
if err != nil {
return err
}
y += h0
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, frameID[0]); err != nil {
return err
}
// bottom
w, h1, err := skillPanel.GetFrameSize(frameID[1])
if err != nil {
return err
}
skillPanel.SetPosition(x, y+h1)
if err:= s.renderPanelSegment(target, frameID[1]); err != nil {
return err
}
// tab button highlighted
switch tab {
case 0:
skillPanel.SetPosition(x+w, y+h1)
if err:= s.renderPanelSegment(target, 7); err != nil {
return err
}
case 1:
x += w
skillPanel.SetPosition(x, s.originY + h0)
if err:= s.renderPanelSegment(target, 9); err != nil {
return err
}
skillPanel.SetPosition(x, y + h1)
if err:= s.renderPanelSegment(target, 11); err != nil {
return err
}
case 2:
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 13); err != nil {
return err
}
}
return nil
}
func (s *SkillTree) renderSkillIcons(target d2interface.Surface, tab int) error {
skillIcon := s.resources.skillIcon
for idx:=range s.skills {
skill := s.skills[idx]
if skill.SkillPage != tab + 1 {
continue
}
if err := skillIcon.SetCurrentFrame(skill.IconCel); err != nil {
return err
}
skillIcon.SetPosition(SkillIconXOff + skill.SkillColumn * SkillIconDistX, SkillIconYOff + skill.SkillRow * SkillIconDistY)
if err := skillIcon.Render(target); err != nil {
return err
}
}
return nil
}
func (s *SkillTree) Render (target d2interface.Surface) error {
if !s.isOpen {
return nil
}
s.frame.Render(target)
s.renderTabCommon(target)
s.renderTab(target, s.selectedTab)
s.renderSkillIcons(target, s.selectedTab)
return nil
}