`d2game/d2player/` lint error cleanup (#788)

* d2game/d2player/inventory.go: magic number lint cleanup

* d2game/d2player/mini_panel.go: magic number lint cleanup

* d2game/d2player/skill_select_panel.go: lint cleanup

* d2game/d2player/skilltree.go: removed all lint errors

* removed the rest of the magic number errors from d2game

* hotfix for bug i added in map engine test

* all magic numbers removed (excluding  mapgen)
This commit is contained in:
gravestench 2020-10-25 07:42:31 +00:00 committed by GitHub
parent fb8923185f
commit 18c9e85cbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 522 additions and 311 deletions

View File

@ -250,7 +250,7 @@ func (m *MapEngine) RemoveEntity(entity d2interface.MapEntity) {
// GetTiles returns a slice of all tiles matching the given style,
// sequence and tileType.
func (m *MapEngine) GetTiles(style, sequence, tileType int) []d2dt1.Tile {
func (m *MapEngine) GetTiles(style, sequence int, tileType d2enum.TileType) []d2dt1.Tile {
tiles := make([]d2dt1.Tile, 0, len(m.dt1TileData))
for idx := range m.dt1TileData {

View File

@ -30,7 +30,7 @@ func (t *MapTile) GetSubTileFlags(x, y int) *d2dt1.SubTileFlags {
func (t *MapTile) PrepareTile(x, y int, me *MapEngine) {
for wIdx := range t.Components.Walls {
wall := &t.Components.Walls[wIdx]
options := me.GetTiles(int(wall.Style), int(wall.Sequence), int(wall.Type))
options := me.GetTiles(int(wall.Style), int(wall.Sequence), wall.Type)
if options == nil {
break

View File

@ -18,7 +18,9 @@ import (
)
const (
subtilesPerTile = 5
subtilesPerTile = 5
retailFps = 25.0
millisecondsPerSecond = 1000.0
)
// NewMapEntityFactory creates a MapEntityFactory instance with the given asset manager
@ -233,15 +235,15 @@ func (f *MapEntityFactory) NewCastOverlay(x, y int, overlayRecord *d2records.Ove
d2enum.DrawEffectModulate,
)
// TODO: Frame index and played count seem to be shared across the cloned animation objects when we retrieve the animation from the asset manager cache.
animation.Rewind()
animation.ResetPlayedCount()
if err != nil {
return nil, err
}
animationSpeed := float64(overlayRecord.AnimRate*25.0) / 1000.0
// TODO: Frame index and played count seem to be shared across the cloned animation objects when we retrieve the animation from the asset manager cache.
animation.Rewind()
animation.ResetPlayedCount()
animationSpeed := float64(overlayRecord.AnimRate*retailFps) / millisecondsPerSecond
playLoop := false // TODO: should be based on the overlay record, some overlays can repeat(e.g. Bone Shield, Frozen Armor)
animation.SetPlayLength(animationSpeed)

View File

@ -84,7 +84,7 @@ func (ob *Object) Render(target d2interface.Surface) {
)
if ob.highlight {
target.PushBrightness(2)
target.PushBrightness(highlightBrightness)
defer target.Pop()
}

View File

@ -78,6 +78,10 @@ func (p *Player) IsInTown() bool {
return p.isInTown
}
const (
half = 0.5
)
// Advance is called once per frame and processes a
// single game tick.
func (p *Player) Advance(tickTime float64) {
@ -92,7 +96,9 @@ func (p *Player) Advance(tickTime float64) {
}
// skills are casted after the first half of the casting animation is played
isHalfDoneCasting := float64(p.composite.GetCurrentFrame())/float64(p.composite.GetFrameCount()) >= 0.5
percentDone := float64(p.composite.GetCurrentFrame()) / float64(p.composite.GetFrameCount())
isHalfDoneCasting := percentDone >= half
if isHalfDoneCasting && p.onFinishedCasting != nil {
p.onFinishedCasting()
p.onFinishedCasting = nil

View File

@ -557,16 +557,20 @@ func (mr *MapRenderer) renderTileDebug(ax, ay, debugVisLevel int, target d2inter
}
}
// Advance is called once per frame and maintains the MapRenderer's record previous render timestamp and current frame.
func (mr *MapRenderer) Advance(elapsed float64) {
frameLength := 0.1
const (
frameOverflow = 10
frameLength = 1.0 / frameOverflow
)
// Advance is called once per frame and maintains the MapRenderer's previous
// render timestamp and current frame.
func (mr *MapRenderer) Advance(elapsed float64) {
mr.lastFrameTime += elapsed
framesAdvanced := int(mr.lastFrameTime / frameLength)
mr.lastFrameTime -= float64(framesAdvanced) * frameLength
mr.currentFrame += framesAdvanced
if mr.currentFrame > 9 {
if mr.currentFrame >= frameOverflow {
mr.currentFrame = 0
}

View File

@ -17,6 +17,12 @@ const (
defaultFloorTileHeight = 10
)
const (
blockOffsetY = 32
tileSurfaceWidth = 160
tileSurfaceHeight = 80
)
func (mr *MapRenderer) generateTileCache() {
var err error
mr.palette, err = mr.loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID))
@ -113,10 +119,8 @@ func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadowRecord) {
}
}
const shadowTileType = 13
func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadowRecord) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), shadowTileType)
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), d2enum.TileShadow)
var tileData *d2dt1.Tile
@ -165,7 +169,7 @@ func (mr *MapRenderer) generateShadowCache(tile *d2ds1.FloorShadowRecord) {
}
func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) {
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), int(tile.Type))
tileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), tile.Type)
var tileData *d2dt1.Tile
@ -177,8 +181,11 @@ func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) {
var newTileData *d2dt1.Tile = nil
if tile.Type == 3 {
newTileOptions := mr.mapEngine.GetTiles(int(tile.Style), int(tile.Sequence), int(4))
if tile.Type == d2enum.TileRightPartOfNorthCornerWall {
newTileOptions := mr.mapEngine.GetTiles(
int(tile.Style), int(tile.Sequence),
d2enum.TileLeftPartOfNorthCornerWall,
)
newTileData = &newTileOptions[tile.RandomIndex]
}
@ -193,16 +200,16 @@ func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) {
for _, block := range target.Blocks {
tileMinY = d2math.MinInt32(tileMinY, int32(block.Y))
tileMaxY = d2math.MaxInt32(tileMaxY, int32(block.Y+32))
tileMaxY = d2math.MaxInt32(tileMaxY, int32(block.Y+blockOffsetY))
}
realHeight := d2math.MaxInt32(d2math.AbsInt32(tileData.Height), tileMaxY-tileMinY)
tileYOffset := -tileMinY
if tile.Type == 15 {
if tile.Type == d2enum.TileRoof {
tile.YAdjust = -int(tileData.RoofHeight)
} else {
tile.YAdjust = int(tileMinY) + 80
tile.YAdjust = int(tileMinY) + tileSurfaceHeight
}
cachedImage := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex)
@ -215,17 +222,17 @@ func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) {
return
}
image, err := mr.renderer.NewSurface(160, int(realHeight), d2enum.FilterNearest)
image, err := mr.renderer.NewSurface(tileSurfaceWidth, int(realHeight), d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
indexData := make([]byte, 160*realHeight)
indexData := make([]byte, tileSurfaceWidth*realHeight)
d2dt1.DecodeTileGfxData(tileData.Blocks, &indexData, tileYOffset, 160)
d2dt1.DecodeTileGfxData(tileData.Blocks, &indexData, tileYOffset, tileSurfaceWidth)
if newTileData != nil {
d2dt1.DecodeTileGfxData(newTileData.Blocks, &indexData, tileYOffset, 160)
d2dt1.DecodeTileGfxData(newTileData.Blocks, &indexData, tileYOffset, tileSurfaceWidth)
}
pixels := d2util.ImgIndexToRGBA(indexData, mr.palette)

View File

@ -20,6 +20,10 @@ const (
half = 2
)
const (
worldToOrthoOffsetX = 3
)
// Viewport is used for converting vectors between screen (pixel), orthogonal (Camera) and world (isometric) space.
type Viewport struct {
defaultScreenRect d2geom.Rectangle
@ -113,8 +117,8 @@ func (v *Viewport) OrthoToScreenF(x, y float64) (screenX, screenY float64) {
// IsTileVisible returns false if no part of the tile is within the game screen.
func (v *Viewport) IsTileVisible(x, y float64) bool {
orthoX1, orthoY1 := v.WorldToOrtho(x-3, y)
orthoX2, orthoY2 := v.WorldToOrtho(x+3, y)
orthoX1, orthoY1 := v.WorldToOrtho(x-worldToOrthoOffsetX, y)
orthoX2, orthoY2 := v.WorldToOrtho(x+worldToOrthoOffsetX, y)
return v.IsOrthoRectVisible(orthoX1, orthoY1, orthoX2, orthoY2)
}

View File

@ -31,6 +31,10 @@ const (
spawnItemErrStr = "failed to send SpawnItem packet to the server: (%d, %d) %+v"
)
const (
black50alpha = 0x0000007f // rgba
)
// Game represents the Gameplay screen
type Game struct {
*d2mapentity.MapEntityFactory
@ -205,8 +209,7 @@ func (v *Game) Render(screen d2interface.Surface) error {
if v.gameControls != nil {
if v.gameControls.HelpOverlay != nil && v.gameControls.HelpOverlay.IsOpen() {
// When help overlay is open, put transparent black screen. Magic noumber is hex for RGBA.
screen.DrawRect(800, 600, d2util.Color(0x0000007f))
screen.DrawRect(screenWidth, screenHeight, d2util.Color(black50alpha))
}
if err := v.gameControls.Render(screen); err != nil {
@ -232,7 +235,7 @@ func (v *Game) Advance(elapsed float64) error {
}
v.ticksSinceLevelCheck += elapsed
if v.ticksSinceLevelCheck > 1.0 {
if v.ticksSinceLevelCheck > 1 {
v.ticksSinceLevelCheck = 0
if v.localPlayer != nil {
tilePosition := v.localPlayer.Position.Tile()

View File

@ -20,6 +20,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
)
const (
subtilesPerTile = 5
)
type regionSpec struct {
regionType d2enum.RegionIdType
startPresetIndex int
@ -226,108 +230,137 @@ func (met *MapEngineTest) OnUnload() error {
return nil
}
const (
lineSmallOffsetY = 12
lineNormalOffsetY = 16
lineSmallIndentX = 10
lineNormalIndentX = 15
lineBigIndentX = 170 // distance between text columns
)
// Render renders the Map Engine Test screen
func (met *MapEngineTest) Render(screen d2interface.Surface) error {
met.mapRenderer.Render(screen)
screen.PushTranslation(0, 16)
screen.DrawTextf("N - next region, P - previous region")
screen.PushTranslation(0, 16)
screen.DrawTextf("Shift+N - next preset, Shift+P - previous preset")
screen.PushTranslation(0, 16)
screen.DrawTextf("Ctrl+N - next file, Ctrl+P - previous file")
screen.PushTranslation(0, 16)
screen.DrawTextf("Left click selects tile, right click unselects")
screen.PushTranslation(0, 16)
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
popN := 5
screen.DrawTextf("N - next region, P - previous region")
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
screen.DrawTextf("Shift+N - next preset, Shift+P - previous preset")
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
screen.DrawTextf("Ctrl+N - next file, Ctrl+P - previous file")
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
screen.DrawTextf("Left click selects tile, right click unselects")
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
if met.selectedTile == nil {
screen.PushTranslation(15, 16)
popN++
screen.PushTranslation(lineNormalIndentX, lineNormalOffsetY)
defer screen.Pop()
screen.DrawTextf("No tile selected")
} else {
screen.PushTranslation(10, 32)
screen.PushTranslation(lineSmallIndentX, lineNormalOffsetY)
defer screen.Pop()
screen.PushTranslation(0, lineNormalOffsetY) // extra vspace
defer screen.Pop()
screen.DrawTextf("Tile %v,%v", met.selX, met.selY)
screen.PushTranslation(15, 16)
screen.PushTranslation(lineNormalIndentX, lineNormalOffsetY)
defer screen.Pop()
screen.DrawTextf("Walls")
tpop := 0
for _, wall := range met.selectedTile.Components.Walls {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
tmpString := fmt.Sprintf("%#v", wall)
stringSlice := strings.Split(tmpString, " ")
tmp2 := strings.Split(stringSlice[0], "{")
stringSlice[0] = tmp2[1]
for _, str := range stringSlice {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
screen.DrawTextf(str)
}
}
screen.PopN(tpop)
screen.PushTranslation(170, 0)
screen.PushTranslation(lineBigIndentX, 0)
defer screen.Pop()
screen.DrawTextf("Floors")
tpop = 0
for _, floor := range met.selectedTile.Components.Floors {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
tmpString := fmt.Sprintf("%#v", floor)
stringSlice := strings.Split(tmpString, " ")
tmp2 := strings.Split(stringSlice[0], "{")
stringSlice[0] = tmp2[1]
for _, str := range stringSlice {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
screen.DrawTextf(str)
}
}
screen.PopN(tpop)
tpop = 0
screen.PushTranslation(170, 0)
screen.PushTranslation(lineBigIndentX, 0)
defer screen.Pop()
screen.DrawTextf("Shadows")
tpop = 0
for _, shadow := range met.selectedTile.Components.Shadows {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
tmpString := fmt.Sprintf("%#v", shadow)
stringSlice := strings.Split(tmpString, " ")
tmp2 := strings.Split(stringSlice[0], "{")
stringSlice[0] = tmp2[1]
for _, str := range stringSlice {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
screen.DrawTextf(str)
}
}
screen.PopN(tpop)
tpop = 0
screen.PushTranslation(170, 0)
screen.PushTranslation(lineBigIndentX, 0)
defer screen.Pop()
screen.DrawTextf("Substitutions")
tpop = 0
for _, subst := range met.selectedTile.Components.Substitutions {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
tmpString := fmt.Sprintf("%#v", subst)
stringSlice := strings.Split(tmpString, " ")
tmp2 := strings.Split(stringSlice[0], "{")
stringSlice[0] = tmp2[1]
for _, str := range stringSlice {
screen.PushTranslation(0, 12)
screen.PushTranslation(0, lineSmallOffsetY)
tpop++
screen.DrawTextf(str)
}
}
screen.PopN(tpop)
popN += 5
}
screen.PopN(popN)
return nil
}
@ -376,7 +409,11 @@ func (met *MapEngineTest) handleLeftClick() {
camVect := met.mapRenderer.Camera.GetPosition().Vector
x, y := float64(met.lastMouseX-400)/5, float64(met.lastMouseY-300)/5
halfScreenWidth, halfScreenHeight := screenWidth>>1, screenHeight>>1
x := float64(met.lastMouseX-halfScreenWidth) / subtilesPerTile
y := float64(met.lastMouseY-halfScreenHeight) / subtilesPerTile
targetPosition := d2vector.NewPositionTile(x, y)
targetPosition.Add(&camVect)

View File

@ -242,7 +242,7 @@ type GameControls struct {
escapeMenu *EscapeMenu
ui *d2ui.UIManager
inventory *Inventory
skilltree *SkillTree
skilltree *skillTree
heroStatsPanel *HeroStatsPanel
HelpOverlay *help.Overlay
miniPanel *miniPanel
@ -362,7 +362,7 @@ func NewGameControls(
mapRenderer: mapRenderer,
inventory: NewInventory(asset, ui, inventoryRecord),
skillSelectMenu: NewSkillSelectMenu(asset, ui, hero),
skilltree: NewSkillTree(hero.Skills, hero.Class, asset, renderer, ui, guiManager),
skilltree: newSkillTree(hero.Skills, hero.Class, asset, renderer, ui, guiManager),
heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats),
HelpOverlay: help.NewHelpOverlay(asset, renderer, ui, guiManager),
miniPanel: newMiniPanel(asset, ui, isSinglePlayer),
@ -817,7 +817,7 @@ func (g *GameControls) Load() {
g.loadUIButtons()
g.inventory.Load()
g.skilltree.Load()
g.skilltree.load()
g.heroStatsPanel.Load()
g.HelpOverlay.Load()
}

View File

@ -2,8 +2,8 @@ package d2player
import (
"fmt"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
@ -15,6 +15,17 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
const (
frameInventoryTopLeft = 4
frameInventoryTopRight = 5
frameInventoryBottomLeft = 6
frameInventoryBottomRight = 7
)
const (
blackAlpha70 = 0x000000C8
)
// Inventory represents the inventory
type Inventory struct {
asset *d2asset.AssetManager
@ -135,14 +146,16 @@ func (g *Inventory) Render(target d2interface.Surface) error {
return nil
}
g.frame.Render(target)
if err := g.frame.Render(target); err != nil {
return err
}
x, y := g.originX+1, g.originY
y += 64
// Panel
// Top left
if err := g.panel.SetCurrentFrame(4); err != nil {
if err := g.panel.SetCurrentFrame(frameInventoryTopLeft); err != nil {
return err
}
@ -157,7 +170,7 @@ func (g *Inventory) Render(target d2interface.Surface) error {
x += w
// Top right
if err := g.panel.SetCurrentFrame(5); err != nil {
if err := g.panel.SetCurrentFrame(frameInventoryTopRight); err != nil {
return err
}
@ -172,7 +185,7 @@ func (g *Inventory) Render(target d2interface.Surface) error {
y += h
// Bottom right
if err := g.panel.SetCurrentFrame(7); err != nil {
if err := g.panel.SetCurrentFrame(frameInventoryBottomRight); err != nil {
return err
}
@ -184,7 +197,7 @@ func (g *Inventory) Render(target d2interface.Surface) error {
}
// Bottom left
if err := g.panel.SetCurrentFrame(6); err != nil {
if err := g.panel.SetCurrentFrame(frameInventoryBottomLeft); err != nil {
return err
}
@ -241,21 +254,27 @@ func (g *Inventory) renderItemDescription(target d2interface.Surface, i Inventor
maxH += h
}
halfW, halfH := maxW/2, maxH/2
halfW, halfH := maxW>>1, maxH>>1
centerX, centerY := g.hoverX, iy-halfH
if (centerX + halfW) > 800 {
centerX = 800 - halfW
if (centerX + halfW) > screenWidth {
centerX = screenWidth - halfW
}
if (centerY + halfH) > 600 {
centerY = 600 - halfH
if (centerY + halfH) > screenHeight {
centerY = screenHeight - halfH
}
target.PushTranslation(centerX, centerY)
defer target.Pop()
target.PushTranslation(-halfW, -halfH)
target.DrawRect(maxW, maxH, color.RGBA{0, 0, 0, uint8(200)})
defer target.Pop()
target.DrawRect(maxW, maxH, d2util.Color(blackAlpha70))
target.PushTranslation(halfW, 0)
defer target.Pop()
for idx := range lines {
g.hoverLabel.SetText(lines[idx])
@ -265,5 +284,4 @@ func (g *Inventory) renderItemDescription(target d2interface.Surface, i Inventor
}
target.PopN(len(lines))
target.PopN(3)
}

View File

@ -10,6 +10,21 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
const (
miniPanelX = 325
miniPanelY = 526
miniPanelWidth = 156
miniPanelHeight = 26
)
const (
containerOffsetX = -75
containerOffsetY = -48
buttonOffsetX = -72
buttonOffsetY = -51
)
type miniPanel struct {
asset *d2asset.AssetManager
container *d2ui.Sprite
@ -37,7 +52,12 @@ func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSing
return nil
}
rectangle := d2geom.Rectangle{Left: 325, Top: 526, Width: 156, Height: 26}
rectangle := d2geom.Rectangle{
Left: miniPanelX,
Top: miniPanelY,
Width: miniPanelWidth,
Height: miniPanelHeight,
}
if !isSinglePlayer {
rectangle.Width = 182
@ -79,8 +99,10 @@ func (m *miniPanel) Render(target d2interface.Surface) error {
}
width, height := target.GetSize()
halfW, halfH := width>>1, height>>1
x, y := halfW+containerOffsetX, halfH+containerOffsetY
m.container.SetPosition((width/2)-75, height-48)
m.container.SetPosition(x, y)
if err := m.container.Render(target); err != nil {
return err
@ -98,7 +120,10 @@ func (m *miniPanel) Render(target d2interface.Surface) error {
return err
}
m.button.SetPosition((width/2)-72+(buttonWidth*i), height-51)
offsetX := buttonOffsetX + (buttonWidth * i)
x, y := halfW+offsetX, height+buttonOffsetY
m.button.SetPosition(x, y)
if err := m.button.Render(target); err != nil {
return err

View File

@ -2,10 +2,11 @@ package d2player
import (
"fmt"
"image/color"
"log"
"sort"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
@ -36,16 +37,16 @@ const (
type SkillPanel struct {
asset *d2asset.AssetManager
activeSkill *d2hero.HeroSkill
isOpen bool
regenerateImageCache bool
hero *d2mapentity.Player
ListRows []*SkillListRow
isLeftPanel bool
renderer d2interface.Renderer
ui *d2ui.UIManager
hoveredSkill *d2hero.HeroSkill
hoverTooltipRect *d2geom.Rectangle
hoverTooltipText *d2ui.Label
isOpen bool
regenerateImageCache bool
isLeftPanel bool
}
// NewHeroSkillsPanel creates a new hero status panel
@ -85,11 +86,10 @@ func (s *SkillPanel) Close() {
}
// IsInRect returns whether the X Y coordinates are in some of the list rows of the panel.
func (s *SkillPanel) IsInRect(X int, Y int) bool {
func (s *SkillPanel) IsInRect(x, y int) bool {
for _, listRow := range s.ListRows {
// TODO: investigate why listRow can be nil
if listRow != nil && listRow.IsInRect(X, Y) {
if listRow != nil && listRow.IsInRect(x, y) {
return true
}
}
@ -98,9 +98,9 @@ func (s *SkillPanel) IsInRect(X int, Y int) bool {
}
// GetListRowByPos returns the skill list row for a given X and Y, based on the width and height of the skills list.
func (s *SkillPanel) GetListRowByPos(X int, Y int) *SkillListRow {
func (s *SkillPanel) GetListRowByPos(x, y int) *SkillListRow {
for _, listRow := range s.ListRows {
if listRow.IsInRect(X, Y) {
if listRow.IsInRect(x, y) {
return listRow
}
}
@ -115,11 +115,15 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
}
if s.regenerateImageCache {
s.generateSkillRowImageCache(target)
if err := s.generateSkillRowImageCache(); err != nil {
return err
}
s.regenerateImageCache = false
}
renderedRows := 0
for _, skillListRow := range s.ListRows {
if len(skillListRow.Skills) == 0 {
continue
@ -129,7 +133,11 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
rowOffsetY := skillPanelOffsetY - (renderedRows * skillIconHeight)
target.PushTranslation(startX, rowOffsetY)
target.Render(skillListRow.cachedImage)
if err := target.Render(skillListRow.cachedImage); err != nil {
return err
}
target.Pop()
renderedRows++
@ -137,20 +145,25 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
if s.hoveredSkill != nil {
target.PushTranslation(s.hoverTooltipRect.Left, s.hoverTooltipRect.Top)
target.DrawRect(s.hoverTooltipRect.Width, s.hoverTooltipRect.Height, color.RGBA{0, 0, 0, uint8(200)})
black70 := d2util.Color(blackAlpha70)
target.DrawRect(s.hoverTooltipRect.Width, s.hoverTooltipRect.Height, black70)
// the text should be centered horizontally in the tooltip rect
target.PushTranslation(s.hoverTooltipRect.Width/2, 0)
centerX := s.hoverTooltipRect.Width >> 1
target.PushTranslation(centerX, 0)
s.hoverTooltipText.Render(target)
target.PopN(2)
target.Pop()
target.Pop()
}
return nil
}
// RegenerateImageCache will force re-generating the cached menu image on next Render.
// Somewhat expensive operation, should not be called often. Currently called every time the panel is opened or when the player learns a new skill.
// Somewhat expensive operation, should not be called often.
// Currently called every time the panel is opened or when the player learns a new skill.
func (s *SkillPanel) RegenerateImageCache() {
s.regenerateImageCache = true
}
@ -169,7 +182,7 @@ func (s *SkillPanel) Toggle() {
}
}
func (s *SkillPanel) generateSkillRowImageCache(target d2interface.Surface) error {
func (s *SkillPanel) generateSkillRowImageCache() error {
for idx := range s.ListRows {
s.ListRows[idx] = &SkillListRow{Skills: make([]*d2hero.HeroSkill, 0), Rectangle: d2geom.Rectangle{Height: 0, Width: 0}}
}
@ -189,6 +202,7 @@ func (s *SkillPanel) generateSkillRowImageCache(target d2interface.Surface) erro
}
visibleRows := 0
for idx, skillListRow := range s.ListRows {
// row won't be considered as visible
if len(skillListRow.Skills) == 0 {
@ -202,13 +216,15 @@ func (s *SkillPanel) generateSkillRowImageCache(target d2interface.Surface) erro
Top: skillPanelOffsetY - (visibleRows * skillIconHeight),
}
skillRow := skillListRow
sort.SliceStable(skillListRow.Skills, func(a, b int) bool {
// left panel skills are aligned by ID (low to high), right panel is the opposite
if s.isLeftPanel {
return skillListRow.Skills[a].ID < skillListRow.Skills[b].ID
return skillRow.Skills[a].ID < skillRow.Skills[b].ID
}
return skillListRow.Skills[a].ID > skillListRow.Skills[b].ID
return skillRow.Skills[a].ID > skillRow.Skills[b].ID
})
cachedImage, err := s.createSkillListImage(skillListRow)
@ -234,6 +250,7 @@ func (s *SkillPanel) createSkillListImage(skillsListRow *SkillListRow) (d2interf
lastSkillResourcePath := d2resource.GenericSkills
skillSprite, _ := s.ui.NewSprite(s.getSkillResourceByClass(""), d2resource.PaletteSky)
for idx, skill := range skillsListRow.Skills {
currentResourcePath := s.getSkillResourceByClass(skill.Charclass)
// only load a new sprite if the DCC file path changed
@ -257,6 +274,7 @@ func (s *SkillPanel) createSkillListImage(skillsListRow *SkillListRow) (d2interf
if err := skillSprite.Render(surface); err != nil {
return nil, err
}
surface.Pop()
}
@ -272,38 +290,39 @@ func (s *SkillPanel) getRowStartX(skillRow *SkillListRow) int {
return rightPanelEndX - skillRow.GetWidth()
}
func (s *SkillPanel) getSkillAtPos(X int, Y int) *d2hero.HeroSkill {
listRow := s.GetListRowByPos(X, Y)
func (s *SkillPanel) getSkillAtPos(x, y int) *d2hero.HeroSkill {
listRow := s.GetListRowByPos(x, y)
if listRow == nil {
return nil
}
skillIndex := (X - s.getRowStartX(listRow)) / skillIconWidth
skillIndex := (x - s.getRowStartX(listRow)) / skillIconWidth
skill := listRow.Skills[skillIndex]
return skill
}
func (s *SkillPanel) getSkillIdxAtPos(X int, Y int) int {
listRow := s.GetListRowByPos(X, Y)
func (s *SkillPanel) getSkillIdxAtPos(x, y int) int {
listRow := s.GetListRowByPos(x, y)
if listRow == nil {
return -1
}
skillIndex := (X - s.getRowStartX(listRow)) / skillIconWidth
skillIndex := (x - s.getRowStartX(listRow)) / skillIconWidth
return skillIndex
}
// HandleClick will change the hero's active(left or right) skill and return true. Returns false if the given X, Y is out of panel boundaries.
func (s *SkillPanel) HandleClick(X int, Y int) bool {
if !s.isOpen || !s.IsInRect(X, Y) {
// HandleClick will change the hero's active(left or right) skill and return true.
// Returns false if the given X, Y is out of panel boundaries.
func (s *SkillPanel) HandleClick(x, y int) bool {
if !s.isOpen || !s.IsInRect(x, y) {
return false
}
clickedSkill := s.getSkillAtPos(X, Y)
clickedSkill := s.getSkillAtPos(x, y)
if clickedSkill == nil {
return false
@ -319,28 +338,28 @@ func (s *SkillPanel) HandleClick(X int, Y int) bool {
}
// HandleMouseMove will process a mouse move event, if inside the panel.
func (s *SkillPanel) HandleMouseMove(X int, Y int) bool {
func (s *SkillPanel) HandleMouseMove(x, y int) bool {
if !s.isOpen {
return false
}
if !s.IsInRect(X, Y) {
if !s.IsInRect(x, y) {
// panel still open but player hovered outside panel - hide the previously hovered skill(if any)
s.hoveredSkill = nil
return false
}
previousHovered := s.hoveredSkill
s.hoveredSkill = s.getSkillAtPos(X, Y)
s.hoveredSkill = s.getSkillAtPos(x, y)
if previousHovered != s.hoveredSkill && s.hoveredSkill != nil {
skillDescription := d2tbl.TranslateString(s.hoveredSkill.ShortKey)
s.hoverTooltipText.SetText(fmt.Sprintf("%s\n%s", s.hoveredSkill.Skill, skillDescription))
listRow := s.GetListRowByPos(X, Y)
listRow := s.GetListRowByPos(x, y)
textWidth, textHeight := s.hoverTooltipText.GetSize()
tooltipX := (s.getSkillIdxAtPos(X, Y) * skillIconWidth) + s.getRowStartX(listRow)
tooltipX := (s.getSkillIdxAtPos(x, y) * skillIconWidth) + s.getRowStartX(listRow)
tooltipWidth := textWidth + tooltipPadLeft + tooltipPadRight
if tooltipX+tooltipWidth >= screenWidth {

View File

@ -15,40 +15,72 @@ import (
)
const (
TabButtonX = 628
TabButton0Y = 385
TabButton1Y = 277
TabButton2Y = 170
tabButtonX = 628
tabButton0Y = 385
tabButton1Y = 277
tabButton2Y = 170
AvailSPLabelX = 677
AvailSPLabelY = 72
availSPLabelX = 677
availSPLabelY = 72
SkillIconXOff = 346
SkillIconYOff = 59
SkillIconDistX = 69
SkillIconDistY = 68
skillIconXOff = 346
skillIconYOff = 59
skillIconDistX = 69
skillIconDistY = 68
)
type SkillTreeTab struct {
buttonText string
button *d2ui.Button
const (
firstTab = iota
secondTab
thirdTab
)
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 (
skillTreePanelX = 401
skillTreePanelY = 64
)
type skillTreeTab struct {
buttonText string
button *d2ui.Button
}
func (st *SkillTreeTab) CreateButton(uiManager *d2ui.UIManager, x int, y int) {
func (st *skillTreeTab) createButton(uiManager *d2ui.UIManager, x, 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
type skillTreeHeroTypeResources struct {
skillIcon *d2ui.Sprite
skillIconPath string
skillPanel *d2ui.Sprite
skillPanelPath string
}
type SkillTree struct {
resources *SkillTreeHeroTypeResources
type skillTree struct {
resources *skillTreeHeroTypeResources
asset *d2asset.AssetManager
renderer d2interface.Renderer
guiManager *d2gui.GuiManager
@ -58,187 +90,192 @@ type SkillTree struct {
heroClass d2enum.Hero
frame *d2ui.UIFrame
availSPLabel *d2ui.Label
tab [3]*SkillTreeTab
tab [3]*skillTreeTab
isOpen bool
originX int
originY int
selectedTab int
}
func NewSkillTree(
func newSkillTree(
skills map[int]*d2hero.HeroSkill,
heroClass d2enum.Hero,
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,
) *skillTree {
st := &skillTree{
skills: skills,
heroClass: heroClass,
asset: asset,
renderer: renderer,
uiManager: ui,
guiManager: guiManager,
originX: 401,
originY: 64,
tab: [3]*SkillTreeTab{
originX: skillTreePanelX,
originY: skillTreePanelY,
tab: [3]*skillTreeTab{
{},
{},
{},
},
}
return st
}
func (s *SkillTree) Load() {
func (s *skillTree) load() {
s.frame = d2ui.NewUIFrame(s.asset, s.uiManager, d2ui.FrameRight)
s.setHeroTypeResourcePath()
s.LoadForHeroType()
s.loadForHeroType()
s.setTab(0)
}
func (s *SkillTree) LoadForHeroType() {
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.tab[firstTab].createButton(s.uiManager, tabButtonX, tabButton0Y)
s.tab[firstTab].button.OnActivated(func() { s.setTab(firstTab) })
s.tab[secondTab].createButton(s.uiManager, tabButtonX, tabButton1Y)
s.tab[secondTab].button.OnActivated(func() { s.setTab(secondTab) })
s.tab[thirdTab].createButton(s.uiManager, tabButtonX, tabButton2Y)
s.tab[thirdTab].button.OnActivated(func() { s.setTab(thirdTab) })
s.availSPLabel = s.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
s.availSPLabel.SetPosition(AvailSPLabelX, AvailSPLabelY)
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"),
))
s.availSPLabel.SetText(makeTabString("StrSklTree1", "StrSklTree2", "StrSklTree3"))
}
func (s *SkillTree) setHeroTypeResourcePath() {
var res *SkillTreeHeroTypeResources
type heroTabData struct {
resources *skillTreeHeroTypeResources
str1, str2, str3 string
}
switch s.heroClass {
case d2enum.HeroBarbarian:
res = &SkillTreeHeroTypeResources {
skillPanelPath: d2resource.SkillsPanelBarbarian,
skillIconPath: d2resource.BarbarianSkills,
func makeTabString(keys ...interface{}) string {
translations := make([]interface{}, len(keys))
token := "%s"
format := token
for idx, key := range keys {
if idx > 0 {
format += "\n" + token
}
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")
translations[idx] = d2tbl.TranslateString(key.(string))
}
s.resources = res
return fmt.Sprintf(format, translations...)
}
func (s *SkillTree) Toggle() {
func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
tabMap := map[d2enum.Hero]heroTabData{
d2enum.HeroBarbarian: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelBarbarian,
skillIconPath: d2resource.BarbarianSkills,
},
makeTabString("StrSklTree21", "StrSklTree4"),
makeTabString("StrSklTree21", "StrSklTree22"),
makeTabString("StrSklTree20"),
},
d2enum.HeroNecromancer: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelNecromancer,
skillIconPath: d2resource.NecromancerSkills,
},
makeTabString("StrSklTree19"),
makeTabString("StrSklTree17", "StrSklTree18", "StrSklTree5"),
makeTabString("StrSklTree16", "StrSklTree5"),
},
d2enum.HeroPaladin: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelPaladin,
skillIconPath: d2resource.PaladinSkills,
},
makeTabString("StrSklTree15", "StrSklTree4"),
makeTabString("StrSklTree14", "StrSklTree13"),
makeTabString("StrSklTree12", "StrSklTree13"),
},
d2enum.HeroAssassin: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelAssassin,
skillIconPath: d2resource.AssassinSkills,
},
makeTabString("StrSklTree30"),
makeTabString("StrSklTree31", "StrSklTree32"),
makeTabString("StrSklTree33", "StrSklTree34"),
},
d2enum.HeroSorceress: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelSorcerer,
skillIconPath: d2resource.SorcererSkills,
},
makeTabString("StrSklTree25", "StrSklTree5"),
makeTabString("StrSklTree24", "StrSklTree5"),
makeTabString("StrSklTree23", "StrSklTree5"),
},
d2enum.HeroAmazon: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelAmazon,
skillIconPath: d2resource.AmazonSkills,
},
makeTabString("StrSklTree10", "StrSklTree11", "StrSklTree4"),
makeTabString("StrSklTree8", "StrSklTree9", "StrSklTree4"),
makeTabString("StrSklTree6", "StrSklTree7", "StrSklTree4"),
},
d2enum.HeroDruid: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelDruid,
skillIconPath: d2resource.DruidSkills,
},
makeTabString("StrSklTree26"),
makeTabString("StrSklTree27", "StrSklTree28"),
makeTabString("StrSklTree29"),
},
}
entry, found := tabMap[class]
return entry, found
}
func (s *skillTree) setHeroTypeResourcePath() {
entry, found := s.getTab(s.heroClass)
if !found {
log.Fatal("Unknown Hero Type")
}
s.resources = entry.resources
s.tab[firstTab].buttonText = entry.str1
s.tab[secondTab].buttonText = entry.str1
s.tab[thirdTab].buttonText = entry.str1
}
// Toggle the skill tree visibility
func (s *skillTree) Toggle() {
fmt.Println("SkillTree toggled")
if s.isOpen {
s.Close()
} else {
@ -246,170 +283,219 @@ func (s *SkillTree) Toggle() {
}
}
func (s *SkillTree) Close() {
// Close the skill tree
func (s *skillTree) Close() {
s.isOpen = false
s.guiManager.SetLayout(nil)
for i:=0; i < 3; i++ {
for i := 0; i < 3; i++ {
s.tab[i].button.SetVisible(false)
}
}
func (s *SkillTree) Open() {
// Open the skill tree
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++ {
for i := 0; i < 3; i++ {
s.tab[i].button.SetVisible(true)
}
s.guiManager.SetLayout(s.layout)
s.guiManager.SetLayout(s.layout)
}
func (s *SkillTree) IsOpen() bool {
func (s *skillTree) IsOpen() bool {
return s.isOpen
}
func (s *SkillTree) setTab(tab int) {
func (s *skillTree) setTab(tab int) {
s.selectedTab = tab
}
func (s *SkillTree) renderPanelSegment(
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 {
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)
w, h, err := skillPanel.GetFrameSize(frameCommonTabTopLeft)
if err != nil {
return err
}
y += h
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, 0); err != nil {
err = s.renderPanelSegment(target, frameCommonTabTopLeft)
if err != nil {
return err
}
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 1); err != nil {
err = s.renderPanelSegment(target, frameCommonTabTopRight)
if err != nil {
return err
}
// bottom
_, h, err = skillPanel.GetFrameSize(2)
_, h, err = skillPanel.GetFrameSize(frameCommonTabBottomLeft)
if err != nil {
return err
}
y += h
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, 2); err != nil {
err = s.renderPanelSegment(target, frameCommonTabBottomLeft)
if err != nil {
return err
}
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 3); err != nil {
err = s.renderPanelSegment(target, frameCommonTabBottomRight)
if 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)
func (s *skillTree) renderTab(target d2interface.Surface, tab int) error {
topFrame := frameOffsetTop + (tabIndexOffset * tab)
bottomFrame := frameOffsetBottom + (tabIndexOffset * tab)
skillPanel := s.resources.skillPanel
x, y := s.originX, s.originY
// top
_, h0, err := skillPanel.GetFrameSize(frameID[0])
_, h0, err := skillPanel.GetFrameSize(topFrame)
if err != nil {
return err
}
y += h0
skillPanel.SetPosition(x, y)
if err:= s.renderPanelSegment(target, frameID[0]); err != nil {
err = s.renderPanelSegment(target, topFrame)
if err != nil {
return err
}
// bottom
w, h1, err := skillPanel.GetFrameSize(frameID[1])
w, h1, err := skillPanel.GetFrameSize(bottomFrame)
if err != nil {
return err
}
skillPanel.SetPosition(x, y+h1)
if err:= s.renderPanelSegment(target, frameID[1]); err != nil {
if err := s.renderPanelSegment(target, bottomFrame); err != nil {
return err
}
// tab button highlighted
switch tab {
case 0:
case firstTab:
skillPanel.SetPosition(x+w, y+h1)
if err:= s.renderPanelSegment(target, 7); err != nil {
if err := s.renderPanelSegment(target, frameSelectedTab1Full); err != nil {
return err
}
case 1:
case secondTab:
x += w
skillPanel.SetPosition(x, s.originY + h0)
if err:= s.renderPanelSegment(target, 9); err != nil {
skillPanel.SetPosition(x, s.originY+h0)
if err := s.renderPanelSegment(target, frameSelectedTab2Top); err != nil {
return err
}
skillPanel.SetPosition(x, y + h1)
if err:= s.renderPanelSegment(target, 11); err != nil {
skillPanel.SetPosition(x, y+h1)
if err := s.renderPanelSegment(target, frameSelectedTab2Bottom); err != nil {
return err
}
case 2:
case thirdTab:
skillPanel.SetPosition(x+w, y)
if err:= s.renderPanelSegment(target, 13); err != nil {
if err := s.renderPanelSegment(target, frameSelectedTab3Full); err != nil {
return err
}
}
return nil
}
func (s *SkillTree) renderSkillIcons(target d2interface.Surface, tab int) error {
func (s *skillTree) renderSkillIcons(target d2interface.Surface, tab int) error {
skillIcon := s.resources.skillIcon
for idx:=range s.skills {
for idx := range s.skills {
skill := s.skills[idx]
if skill.SkillPage != tab + 1 {
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)
x := skillIconXOff + skill.SkillColumn*skillIconDistX
y := skillIconYOff + skill.SkillRow*skillIconDistY
skillIcon.SetPosition(x, y)
if err := skillIcon.Render(target); err != nil {
return err
}
}
return nil
return nil
}
func (s *SkillTree) Render (target d2interface.Surface) error {
// Render the skill tree panel
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)
if err := s.frame.Render(target); err != nil {
return err
}
if err := s.renderTabCommon(target); err != nil {
return err
}
if err := s.renderTab(target, s.selectedTab); err != nil {
return err
}
if err := s.renderSkillIcons(target, s.selectedTab); err != nil {
return err
}
return nil
}