1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-12 18:50:42 +00:00

Ui minipanel refactor (#926)

* d2player/hud: Make minipanel button a real ui/button

* d2ui/button: Add implicit tooltips

for now it is only for close buttons.

* d2ui/frame: Add size caluclation

now frame.GetSize() returns meaningful values.

* d2ui/button: Add minipanel button types

* d2ui/hero_stats_panel: Fix cached image being way to big

* d2ui/widget_group: Fix widget groups size calculation

* d2ui/widget_group: Add debug rendering

* d2ui/widget_group: SetVisible() now sets the visibility of the group object

* d2player: Refactor mini_panel

we converted all elements to widgets. Thus rendering from game_controls
is no longer neccessary.

* d2ui/button: Add disabled color to layouts

* d2player/gamecontrols: temp hide minipanel when in esc menu

* d2ui/widget_group: Add OffsetPosition() method

* d2player/mini_panel: Implement moving of minipanel

this only occours when other panels are opened.

* d2player/minipanel: Fix inv/skilltree/char closebuttons

these would screw up the moving of the mini panel.

* Fix linter

* d2player/minipanel: Add tooltips to buttons

* d2player/skilltree: Fix icon rendering
This commit is contained in:
juander-ux 2020-11-16 10:41:01 +01:00 committed by GitHub
parent 12821147ce
commit ba5ea334cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 661 additions and 319 deletions

View File

@ -40,6 +40,8 @@ const (
ButtonTypeMinipanelMen ButtonType = 19 ButtonTypeMinipanelMen ButtonType = 19
ButtonTypeSquareClose ButtonType = 20 ButtonTypeSquareClose ButtonType = 20
ButtonTypeSkillTreeTab ButtonType = 21 ButtonTypeSkillTreeTab ButtonType = 21
ButtonTypeMinipanelOpenClose ButtonType = 22
ButtonTypeMinipanelParty ButtonType = 23
ButtonNoFixedWidth int = -1 ButtonNoFixedWidth int = -1
ButtonNoFixedHeight int = -1 ButtonNoFixedHeight int = -1
@ -71,6 +73,7 @@ type ButtonLayout struct {
YSegments int YSegments int
BaseFrame int BaseFrame int
DisabledFrame int DisabledFrame int
DisabledColor uint32
TextOffset int TextOffset int
FixedWidth int FixedWidth int
FixedHeight int FixedHeight int
@ -78,8 +81,21 @@ type ButtonLayout struct {
Toggleable bool Toggleable bool
AllowFrameChange bool AllowFrameChange bool
HasImage bool HasImage bool
Tooltip int
TooltipXOffset int
TooltipYOffset int
} }
const (
buttonTooltipNone int = iota
buttonTooltipClose
)
const (
buttonCloseTooltipXOffset = 15
buttonCloseTooltipYOffset = -2
)
const ( const (
buttonWideSegmentsX = 2 buttonWideSegmentsX = 2
buttonWideSegmentsY = 1 buttonWideSegmentsY = 1
@ -106,13 +122,28 @@ const (
buttonBuySellSegmentsY = 1 buttonBuySellSegmentsY = 1
buttonBuySellDisabledFrame = 1 buttonBuySellDisabledFrame = 1
buttonSkillTreeTabXSegments = 1 buttonSkillTreeTabXSegments = 1
buttonSkillTreeTabYSegments = 1 buttonSkillTreeTabYSegments = 1
buttonSkillTreeTabDisabledFrame = 7 buttonSkillTreeTabDisabledFrame = 7
buttonSkillTreeTabBaseFrame = 7 buttonSkillTreeTabBaseFrame = 7
buttonSkillTreeTabFixedWidth = 93 buttonSkillTreeTabFixedWidth = 93
buttonSkillTreeTabFixedHeight = 107 buttonSkillTreeTabFixedHeight = 107
buttonMinipanelOpenCloseBaseFrame = 0
buttonMinipanelDisabledFrame = 2
buttonMinipanelXSegments = 1
buttonMinipanelYSegments = 1
buttonMinipanelCharacterBaseFrame = 0
buttonMinipanelInventoryBaseFrame = 2
buttonMinipanelSkilltreeBaseFrame = 4
buttonMinipanelPartyBaseFrame = 6
buttonMinipanelAutomapBaseFrame = 8
buttonMinipanelMessageBaseFrame = 10
buttonMinipanelQuestBaseFrame = 12
buttonMinipanelMenBaseFrame = 14
buttonRunSegmentsX = 1 buttonRunSegmentsX = 1
buttonRunSegmentsY = 1 buttonRunSegmentsY = 1
buttonRunDisabledFrame = -1 buttonRunDisabledFrame = -1
@ -127,6 +158,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonWideSegmentsX, XSegments: buttonWideSegmentsX,
YSegments: buttonWideSegmentsY, YSegments: buttonWideSegmentsY,
DisabledFrame: buttonWideDisabledFrame, DisabledFrame: buttonWideDisabledFrame,
DisabledColor: lightGreyAlpha75,
TextOffset: buttonWideTextOffset, TextOffset: buttonWideTextOffset,
ResourceName: d2resource.WideButtonBlank, ResourceName: d2resource.WideButtonBlank,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
@ -141,6 +173,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonShortSegmentsX, XSegments: buttonShortSegmentsX,
YSegments: buttonShortSegmentsY, YSegments: buttonShortSegmentsY,
DisabledFrame: buttonShortDisabledFrame, DisabledFrame: buttonShortDisabledFrame,
DisabledColor: lightGreyAlpha75,
TextOffset: buttonShortTextOffset, TextOffset: buttonShortTextOffset,
ResourceName: d2resource.ShortButtonBlank, ResourceName: d2resource.ShortButtonBlank,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
@ -154,6 +187,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
ButtonTypeMedium: { ButtonTypeMedium: {
XSegments: buttonMediumSegmentsX, XSegments: buttonMediumSegmentsX,
YSegments: buttonMediumSegmentsY, YSegments: buttonMediumSegmentsY,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.MediumButtonBlank, ResourceName: d2resource.MediumButtonBlank,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10, FontPath: d2resource.FontExocet10,
@ -167,6 +201,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonTallSegmentsX, XSegments: buttonTallSegmentsX,
YSegments: buttonTallSegmentsY, YSegments: buttonTallSegmentsY,
TextOffset: buttonTallTextOffset, TextOffset: buttonTallTextOffset,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.TallButtonBlank, ResourceName: d2resource.TallButtonBlank,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10, FontPath: d2resource.FontExocet10,
@ -180,6 +215,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonOkCancelSegmentsX, XSegments: buttonOkCancelSegmentsX,
YSegments: buttonOkCancelSegmentsY, YSegments: buttonOkCancelSegmentsY,
DisabledFrame: buttonOkCancelDisabledFrame, DisabledFrame: buttonOkCancelDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.CancelButton, ResourceName: d2resource.CancelButton,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontRediculous, FontPath: d2resource.FontRediculous,
@ -193,6 +229,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonRunSegmentsX, XSegments: buttonRunSegmentsX,
YSegments: buttonRunSegmentsY, YSegments: buttonRunSegmentsY,
DisabledFrame: buttonRunDisabledFrame, DisabledFrame: buttonRunDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.RunButton, ResourceName: d2resource.RunButton,
PaletteName: d2resource.PaletteSky, PaletteName: d2resource.PaletteSky,
Toggleable: true, Toggleable: true,
@ -207,6 +244,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonBuySellSegmentsX, XSegments: buttonBuySellSegmentsX,
YSegments: buttonBuySellSegmentsY, YSegments: buttonBuySellSegmentsY,
DisabledFrame: buttonBuySellDisabledFrame, DisabledFrame: buttonBuySellDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.BuySellButton, ResourceName: d2resource.BuySellButton,
PaletteName: d2resource.PaletteUnits, PaletteName: d2resource.PaletteUnits,
Toggleable: true, Toggleable: true,
@ -217,11 +255,15 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
FixedWidth: ButtonNoFixedWidth, FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight, FixedHeight: ButtonNoFixedHeight,
LabelColor: greyAlpha100, LabelColor: greyAlpha100,
Tooltip: buttonTooltipClose,
TooltipXOffset: buttonCloseTooltipXOffset,
TooltipYOffset: buttonCloseTooltipYOffset,
}, },
ButtonTypeSkillTreeTab: { ButtonTypeSkillTreeTab: {
XSegments: buttonSkillTreeTabXSegments, XSegments: buttonSkillTreeTabXSegments,
YSegments: buttonSkillTreeTabYSegments, YSegments: buttonSkillTreeTabYSegments,
DisabledFrame: buttonSkillTreeTabDisabledFrame, DisabledFrame: buttonSkillTreeTabDisabledFrame,
DisabledColor: lightGreyAlpha75,
BaseFrame: buttonSkillTreeTabBaseFrame, BaseFrame: buttonSkillTreeTabBaseFrame,
ResourceName: d2resource.SkillsPanelAmazon, ResourceName: d2resource.SkillsPanelAmazon,
PaletteName: d2resource.PaletteSky, PaletteName: d2resource.PaletteSky,
@ -233,6 +275,134 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
FixedHeight: buttonSkillTreeTabFixedHeight, FixedHeight: buttonSkillTreeTabFixedHeight,
LabelColor: whiteAlpha100, LabelColor: whiteAlpha100,
}, },
ButtonTypeMinipanelOpenClose: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
DisabledFrame: buttonMinipanelDisabledFrame,
DisabledColor: whiteAlpha100,
BaseFrame: buttonMinipanelOpenCloseBaseFrame,
ResourceName: d2resource.MenuButton,
PaletteName: d2resource.PaletteSky,
Toggleable: true,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelCharacter: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelCharacterBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelInventory: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelInventoryBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelSkill: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelSkilltreeBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelParty: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelPartyBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelAutomap: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelAutomapBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelMessage: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelMessageBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelQuest: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelQuestBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelMen: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelMenBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
} }
} }
@ -251,6 +421,7 @@ type Button struct {
enabled bool enabled bool
pressed bool pressed bool
toggled bool toggled bool
tooltip *Tooltip
} }
// NewButton creates an instance of Button // NewButton creates an instance of Button
@ -311,6 +482,8 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button {
buttonSprite.SetPosition(0, 0) buttonSprite.SetPosition(0, 0)
buttonSprite.SetEffect(d2enum.DrawEffectModulate) buttonSprite.SetEffect(d2enum.DrawEffectModulate)
btn.createTooltip()
ui.addWidget(btn) // important that this comes before prerenderStates! ui.addWidget(btn) // important that this comes before prerenderStates!
btn.prerenderStates(buttonSprite, &buttonLayout, lbl) btn.prerenderStates(buttonSprite, &buttonLayout, lbl)
@ -325,6 +498,21 @@ type buttonStateDescriptor struct {
fmtErr string fmtErr string
} }
func (v *Button) createTooltip() {
var t *Tooltip
switch v.buttonLayout.Tooltip {
case buttonTooltipNone:
return
case buttonTooltipClose:
t = v.manager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, TooltipXCenter, TooltipYBottom)
t.SetText(v.manager.asset.TranslateString("strClose"))
}
t.SetVisible(false)
v.SetTooltip(t)
}
func (v *Button) prerenderStates(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) { func (v *Button) prerenderStates(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) {
numButtonStates := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments) numButtonStates := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments)
@ -438,7 +626,7 @@ func (v *Button) Render(target d2interface.Surface) {
switch { switch {
case !v.enabled: case !v.enabled:
target.PushColor(d2util.Color(lightGreyAlpha75)) target.PushColor(d2util.Color(v.buttonLayout.DisabledColor))
defer target.Pop() defer target.Pop()
target.Render(v.disabledSurface) target.Render(v.disabledSurface)
case v.toggled && v.pressed: case v.toggled && v.pressed:
@ -486,6 +674,32 @@ func (v *Button) SetPressed(pressed bool) {
v.pressed = pressed v.pressed = pressed
} }
// SetVisible sets the pressed state of the button
func (v *Button) SetVisible(visible bool) {
v.BaseWidget.SetVisible(visible)
if v.isHovered() && !visible {
v.hoverEnd()
}
}
// SetPosition sets the position of the widget
func (v *Button) SetPosition(x, y int) {
v.BaseWidget.SetPosition(x, y)
if v.buttonLayout.Tooltip != buttonTooltipNone {
v.tooltip.SetPosition(x+v.buttonLayout.TooltipXOffset, y+v.buttonLayout.TooltipYOffset)
}
}
// SetTooltip adds a tooltip to the button
func (v *Button) SetTooltip(t *Tooltip) {
v.tooltip = t
v.manager.addWidget(t)
v.OnHoverStart(func() { log.Print("HoverStart"); v.tooltip.SetVisible(true) })
v.OnHoverEnd(func() { v.tooltip.SetVisible(false) })
}
func half(n int) int { func half(n int) int {
return n / 2 return n / 2
} }

View File

@ -32,7 +32,7 @@ type UIFrame struct {
const ( const (
leftFrameTopLeft = iota leftFrameTopLeft = iota
leftFrameTopRight leftFrameTopRight
leftFrameMiddleRight leftFrameMiddleLeft
leftFrameBottomLeft leftFrameBottomLeft
leftFrameBottomRight leftFrameBottomRight
rightFrameTopLeft rightFrameTopLeft
@ -80,6 +80,51 @@ func (u *UIFrame) Load() {
} }
u.frame = sprite u.frame = sprite
u.calculateSize()
}
func (u *UIFrame) calculateSize() {
var framesWidth, framesHeight []int
if u.frameOrientation == FrameLeft {
framesWidth = []int{
leftFrameTopLeft,
leftFrameTopRight,
}
framesHeight = []int{
leftFrameTopLeft,
leftFrameMiddleLeft,
leftFrameBottomLeft,
}
} else if u.frameOrientation == FrameRight {
framesWidth = []int{
rightFrameTopLeft,
rightFrameTopRight,
}
framesHeight = []int{
rightFrameTopRight,
rightFrameMiddleRight,
rightFrameBottomRight,
}
}
for i := range framesWidth {
w, _, err := u.frame.GetFrameSize(framesWidth[i])
if err != nil {
log.Print(err)
}
u.width += w
}
for i := range framesHeight {
_, h, err := u.frame.GetFrameSize(framesHeight[i])
if err != nil {
log.Print(err)
}
u.height += h
}
} }
// Render the frame to the target surface // Render the frame to the target surface
@ -101,7 +146,7 @@ func (u *UIFrame) renderLeft(target d2interface.Surface) error {
framePieces := []int{ framePieces := []int{
leftFrameTopLeft, leftFrameTopLeft,
leftFrameTopRight, leftFrameTopRight,
leftFrameMiddleRight, leftFrameMiddleLeft,
leftFrameBottomLeft, leftFrameBottomLeft,
leftFrameBottomRight, leftFrameBottomRight,
} }
@ -129,7 +174,7 @@ func (u *UIFrame) renderLeft(target d2interface.Surface) error {
case leftFrameTopRight: case leftFrameTopRight:
c.x, c.y = currentX, startY+height c.x, c.y = currentX, startY+height
currentX = startX currentX = startX
case leftFrameMiddleRight: case leftFrameMiddleLeft:
c.x, c.y = currentX, currentY+height c.x, c.y = currentX, currentY+height
currentY += height currentY += height
case leftFrameBottomLeft: case leftFrameBottomLeft:

View File

@ -105,6 +105,12 @@ func (ui *UIManager) OnMouseMove(event d2interface.MouseMoveEvent) bool {
} }
} }
for _, w := range ui.widgets {
if w.GetVisible() {
w.OnMouseMove(event.X(), event.Y())
}
}
return false return false
} }
@ -134,17 +140,17 @@ func (ui *UIManager) OnMouseButtonDown(event d2interface.MouseEvent) bool {
// Render renders all of the UI elements // Render renders all of the UI elements
func (ui *UIManager) Render(target d2interface.Surface) { func (ui *UIManager) Render(target d2interface.Surface) {
for _, widget := range ui.widgets {
if widget.GetVisible() {
widget.Render(target)
}
}
for _, widgetGroup := range ui.widgetsGroups { for _, widgetGroup := range ui.widgetsGroups {
if widgetGroup.GetVisible() { if widgetGroup.GetVisible() {
widgetGroup.Render(target) widgetGroup.Render(target)
} }
} }
for _, widget := range ui.widgets {
if widget.GetVisible() {
widget.Render(target)
}
}
} }
// Advance updates all of the UI elements // Advance updates all of the UI elements

View File

@ -11,6 +11,8 @@ const (
RenderPrioritySkilltree RenderPrioritySkilltree
// RenderPrioritySkilltreeIcon is the priority for the skilltree icons // RenderPrioritySkilltreeIcon is the priority for the skilltree icons
RenderPrioritySkilltreeIcon RenderPrioritySkilltreeIcon
// RenderPriorityMinipanel is the priority for the minipanel icons
RenderPriorityMinipanel
// RenderPriorityHeroStatsPanel is the priority for the hero_stats_panel // RenderPriorityHeroStatsPanel is the priority for the hero_stats_panel
RenderPriorityHeroStatsPanel RenderPriorityHeroStatsPanel
// RenderPriorityForeground is the last element drawn // RenderPriorityForeground is the last element drawn
@ -22,6 +24,7 @@ type Widget interface {
Drawable Drawable
bindManager(ui *UIManager) bindManager(ui *UIManager)
GetManager() (ui *UIManager) GetManager() (ui *UIManager)
OnMouseMove(x int, y int)
OnHoverStart(callback func()) OnHoverStart(callback func())
OnHoverEnd(callback func()) OnHoverEnd(callback func())
isHovered() bool isHovered() bool
@ -156,3 +159,14 @@ func (b *BaseWidget) Contains(x, y int) bool {
func (b *BaseWidget) GetManager() (ui *UIManager) { func (b *BaseWidget) GetManager() (ui *UIManager) {
return b.manager return b.manager
} }
// OnMouseMove is called when the mouse is moved
func (b *BaseWidget) OnMouseMove(x, y int) {
if b.Contains(x, y) {
if !b.isHovered() {
b.hoverStart()
}
} else if b.isHovered() {
b.hoverEnd()
}
}

View File

@ -1,11 +1,14 @@
package d2ui package d2ui
import ( import (
"image/color"
"sort" "sort"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
) )
const widgetGroupDebug = false // turns on debug rendering stuff for groups
// static check that WidgetGroup implements widget // static check that WidgetGroup implements widget
var _ Widget = &WidgetGroup{} var _ Widget = &WidgetGroup{}
@ -44,8 +47,8 @@ func (wg *WidgetGroup) adjustSize(w Widget) {
x, y := w.GetPosition() x, y := w.GetPosition()
width, height := w.GetSize() width, height := w.GetSize()
if x+width > wg.width { if x+width > wg.x+wg.width {
wg.width = x + width wg.width += (x + width) - (wg.x + wg.width)
} }
if wg.x > x { if wg.x > x {
@ -53,8 +56,8 @@ func (wg *WidgetGroup) adjustSize(w Widget) {
wg.x = x wg.x = x
} }
if y+height > wg.height { if y+height > wg.y+wg.height {
wg.height = x + height wg.height += (y + height) - (wg.y + wg.height)
} }
if wg.y > y { if wg.y > y {
@ -76,15 +79,42 @@ func (wg *WidgetGroup) Render(target d2interface.Surface) {
entry.Render(target) entry.Render(target)
} }
} }
if widgetGroupDebug && wg.GetVisible() {
wg.renderDebug(target)
}
}
func (wg *WidgetGroup) renderDebug(target d2interface.Surface) {
target.PushTranslation(wg.GetPosition())
defer target.Pop()
target.DrawLine(wg.width, 0, color.White)
target.DrawLine(0, wg.height, color.White)
target.PushTranslation(wg.width, wg.height)
target.DrawLine(-wg.width, 0, color.White)
target.DrawLine(0, -wg.height, color.White)
target.Pop()
} }
// SetVisible sets the visibility of all widgets in the group // SetVisible sets the visibility of all widgets in the group
func (wg *WidgetGroup) SetVisible(visible bool) { func (wg *WidgetGroup) SetVisible(visible bool) {
wg.BaseWidget.SetVisible(visible)
for _, entry := range wg.entries { for _, entry := range wg.entries {
entry.SetVisible(visible) entry.SetVisible(visible)
} }
} }
// OffsetPosition moves all widgets by x and y
func (wg *WidgetGroup) OffsetPosition(x, y int) {
wg.BaseWidget.OffsetPosition(x, y)
for _, entry := range wg.entries {
entry.OffsetPosition(x, y)
}
}
// OnMouseMove handles mouse move events // OnMouseMove handles mouse move events
func (wg *WidgetGroup) OnMouseMove(x, y int) { func (wg *WidgetGroup) OnMouseMove(x, y int) {
for _, entry := range wg.entries { for _, entry := range wg.entries {

View File

@ -80,6 +80,8 @@ type EscapeMenu struct {
assetManager *d2asset.AssetManager assetManager *d2asset.AssetManager
keyMap *KeyMap keyMap *KeyMap
keyBindingMenu *KeyBindingMenu keyBindingMenu *KeyBindingMenu
onCloseCb func()
} }
type layout struct { type layout struct {
@ -410,10 +412,16 @@ func (m *EscapeMenu) OnEscKey() {
m.close() m.close()
} }
// SetOnCloseCb sets the callback that is run when close() is called
func (m *EscapeMenu) SetOnCloseCb(cb func()) {
m.onCloseCb = cb
}
func (m *EscapeMenu) close() { func (m *EscapeMenu) close() {
m.isOpen = false m.isOpen = false
m.guiManager.SetLayout(nil) m.guiManager.SetLayout(nil)
m.onCloseCb()
} }
func (m *EscapeMenu) open() { func (m *EscapeMenu) open() {

View File

@ -39,18 +39,10 @@ const (
xp xp
walkRun walkRun
stamina stamina
miniPnl
newSkills newSkills
rightSkill rightSkill
hpGlobe hpGlobe
manaGlobe manaGlobe
miniPanelCharacter
miniPanelInventory
miniPanelSkillTree
miniPanelAutomap
miniPanelMessageLog
miniPanelQuestLog
miniPanelGameMenu
) )
const ( const (
@ -79,11 +71,6 @@ const (
staminaWidth, staminaWidth,
staminaHeight = 273, 573, 105, 20 staminaHeight = 273, 573, 105, 20
miniPnlX,
miniPnlY,
miniPnlWidth,
miniPnlHeight = 393, 563, 12, 23
newSkillsX, newSkillsX,
newSkillsY, newSkillsY,
newSkillsWidth, newSkillsWidth,
@ -103,41 +90,6 @@ const (
manaGlobeY, manaGlobeY,
manaGlobeWidth, manaGlobeWidth,
manaGlobeHeight = 695, 525, 80, 60 manaGlobeHeight = 695, 525, 80, 60
miniPanelCharacterX,
miniPanelCharacterY,
miniPanelCharacterWidth,
miniPanelCharacterHeight = 324, 528, 22, 26
miniPanelInventoryX,
miniPanelInventoryY,
miniPanelInventoryWidth,
miniPanelInventoryHeight = 346, 528, 22, 26
miniPanelSkillTreeX,
miniPanelSkillTreeY,
miniPanelSkillTreeWidth,
miniPanelSkillTreeHeight = 368, 528, 22, 26
miniPanelAutomapX,
miniPanelAutomapY,
miniPanelAutomapWidth,
miniPanelAutomapHeight = 390, 528, 22, 26
miniPanelMessageLogX,
miniPanelMessageLogY,
miniPanelMessageLogWidth,
miniPanelMessageLogHeight = 412, 528, 22, 26
miniPanelQuestLogX,
miniPanelQuestLogY,
miniPanelQuestLogWidth,
miniPanelQuestLogHeight = 434, 528, 22, 26
miniPanelGameMenuX,
miniPanelGameMenuY,
miniPanelGameMenuWidth,
miniPanelGameMenuHeight = 456, 528, 22, 26
) )
const ( const (
@ -269,12 +221,6 @@ func NewGameControls(
Width: staminaWidth, Width: staminaWidth,
Height: staminaHeight, Height: staminaHeight,
}}, }},
{miniPnl, d2geom.Rectangle{
Left: miniPnlX,
Top: miniPnlY,
Width: miniPnlWidth,
Height: miniPnlHeight,
}},
{newSkills, d2geom.Rectangle{ {newSkills, d2geom.Rectangle{
Left: newSkillsX, Left: newSkillsX,
Top: newSkillsY, Top: newSkillsY,
@ -299,59 +245,22 @@ func NewGameControls(
Width: manaGlobeWidth, Width: manaGlobeWidth,
Height: manaGlobeHeight, Height: manaGlobeHeight,
}}, }},
{miniPanelCharacter, d2geom.Rectangle{
Left: miniPanelCharacterX,
Top: miniPanelCharacterY,
Width: miniPanelCharacterWidth,
Height: miniPanelCharacterHeight,
}},
{miniPanelInventory, d2geom.Rectangle{
Left: miniPanelInventoryX,
Top: miniPanelInventoryY,
Width: miniPanelInventoryWidth,
Height: miniPanelInventoryHeight,
}},
{miniPanelSkillTree, d2geom.Rectangle{
Left: miniPanelSkillTreeX,
Top: miniPanelSkillTreeY,
Width: miniPanelSkillTreeWidth,
Height: miniPanelSkillTreeHeight,
}},
{miniPanelAutomap, d2geom.Rectangle{
Left: miniPanelAutomapX,
Top: miniPanelAutomapY,
Width: miniPanelAutomapWidth,
Height: miniPanelAutomapHeight,
}},
{miniPanelMessageLog, d2geom.Rectangle{
Left: miniPanelMessageLogX,
Top: miniPanelMessageLogY,
Width: miniPanelMessageLogWidth,
Height: miniPanelMessageLogHeight,
}},
{miniPanelQuestLog, d2geom.Rectangle{
Left: miniPanelQuestLogX,
Top: miniPanelQuestLogY,
Width: miniPanelQuestLogWidth,
Height: miniPanelQuestLogHeight,
}},
{miniPanelGameMenu, d2geom.Rectangle{
Left: miniPanelGameMenuX,
Top: miniPanelGameMenuY,
Width: miniPanelGameMenuWidth,
Height: miniPanelGameMenuHeight,
}},
} }
inventoryRecord := asset.Records.Layout.Inventory[inventoryRecordKey] inventoryRecord := asset.Records.Layout.Inventory[inventoryRecordKey]
heroStatsPanel := NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats)
inventory := NewInventory(asset, ui, inventoryRecord)
skilltree := newSkillTree(hero.Skills, hero.Class, asset, ui)
miniPanel := newMiniPanel(asset, ui, isSinglePlayer)
heroState, err := d2hero.NewHeroStateFactory(asset) heroState, err := d2hero.NewHeroStateFactory(asset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
helpOverlay := NewHelpOverlay(asset, renderer, ui, guiManager, keyMap) helpOverlay := NewHelpOverlay(asset, renderer, ui, guiManager, keyMap)
hud := NewHUD(asset, ui, hero, helpOverlay, newMiniPanel(asset, ui, isSinglePlayer), actionableRegions, mapEngine, mapRenderer) hud := NewHUD(asset, ui, hero, helpOverlay, miniPanel, actionableRegions, mapEngine, mapRenderer)
const blackAlpha50percent = 0x0000007f const blackAlpha50percent = 0x0000007f
@ -367,9 +276,9 @@ func NewGameControls(
escapeMenu: escapeMenu, escapeMenu: escapeMenu,
inputListener: inputListener, inputListener: inputListener,
mapRenderer: mapRenderer, mapRenderer: mapRenderer,
inventory: NewInventory(asset, ui, inventoryRecord), inventory: inventory,
skilltree: newSkillTree(hero.Skills, hero.Class, asset, ui), skilltree: skilltree,
heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats), heroStatsPanel: heroStatsPanel,
HelpOverlay: helpOverlay, HelpOverlay: helpOverlay,
keyMap: keyMap, keyMap: keyMap,
hud: hud, hud: hud,
@ -397,10 +306,11 @@ func NewGameControls(
isSinglePlayer: isSinglePlayer, isSinglePlayer: isSinglePlayer,
} }
closeCb := func() { gc.updateLayout() } gc.heroStatsPanel.SetOnCloseCb(gc.onCloseHeroStatsPanel)
gc.heroStatsPanel.SetOnCloseCb(closeCb) gc.inventory.SetOnCloseCb(gc.onCloseInventory)
gc.inventory.SetOnCloseCb(closeCb) gc.skilltree.SetOnCloseCb(gc.onCloseSkilltree)
gc.skilltree.SetOnCloseCb(closeCb)
gc.escapeMenu.SetOnCloseCb(gc.hud.restoreMinipanelFromTempClose)
err = gc.bindTerminalCommands(term) err = gc.bindTerminalCommands(term)
if err != nil { if err != nil {
@ -467,14 +377,11 @@ func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool {
g.HelpOverlay.Close() g.HelpOverlay.Close()
g.updateLayout() g.updateLayout()
case d2enum.ToggleInventoryPanel: case d2enum.ToggleInventoryPanel:
g.inventory.Toggle() g.toggleInventoryPanel()
g.updateLayout()
case d2enum.ToggleSkillTreePanel: case d2enum.ToggleSkillTreePanel:
g.skilltree.Toggle() g.toggleInventoryPanel()
g.updateLayout()
case d2enum.ToggleCharacterPanel: case d2enum.ToggleCharacterPanel:
g.heroStatsPanel.Toggle() g.toggleHeroStatsPanel()
g.updateLayout()
case d2enum.ToggleRunWalk: case d2enum.ToggleRunWalk:
g.hud.onToggleRunButton(false) g.hud.onToggleRunButton(false)
case d2enum.HoldRun: case d2enum.HoldRun:
@ -547,7 +454,7 @@ func (g *GameControls) onEscKey() {
if g.escapeMenu.IsOpen() { if g.escapeMenu.IsOpen() {
g.escapeMenu.OnEscKey() g.escapeMenu.OnEscKey()
} else { } else {
g.escapeMenu.open() g.openEscMenu()
} }
} }
} }
@ -690,6 +597,50 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
return false return false
} }
func (g *GameControls) toggleHeroStatsPanel() {
g.heroStatsPanel.Toggle()
g.hud.miniPanel.SetMovedRight(g.heroStatsPanel.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseHeroStatsPanel() {
g.hud.miniPanel.SetMovedRight(g.heroStatsPanel.IsOpen())
g.updateLayout()
}
func (g *GameControls) toggleInventoryPanel() {
g.skilltree.Close()
g.inventory.Toggle()
g.hud.miniPanel.SetMovedLeft(g.inventory.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseInventory() {
g.hud.miniPanel.SetMovedLeft(g.inventory.IsOpen())
g.updateLayout()
}
func (g *GameControls) toggleSkilltreePanel() {
g.inventory.Close()
g.skilltree.Toggle()
g.hud.miniPanel.SetMovedLeft(g.skilltree.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseSkilltree() {
g.hud.miniPanel.SetMovedLeft(g.skilltree.IsOpen())
g.updateLayout()
}
func (g *GameControls) openEscMenu() {
g.inventory.Close()
g.skilltree.Close()
g.heroStatsPanel.Close()
g.hud.closeMinipanelTemporary()
g.escapeMenu.open()
g.updateLayout()
}
// Load the resources required for the GameControls // Load the resources required for the GameControls
func (g *GameControls) Load() { func (g *GameControls) Load() {
g.hud.Load() g.hud.Load()
@ -697,6 +648,14 @@ func (g *GameControls) Load() {
g.skilltree.load() g.skilltree.load()
g.heroStatsPanel.Load() g.heroStatsPanel.Load()
g.HelpOverlay.Load() g.HelpOverlay.Load()
miniPanelActions := &miniPanelActions{
characterToggle: g.toggleHeroStatsPanel,
inventoryToggle: g.toggleInventoryPanel,
skilltreeToggle: g.toggleSkilltreePanel,
menuToggle: g.openEscMenu,
}
g.hud.miniPanel.load(miniPanelActions)
} }
// Advance advances the state of the GameControls // Advance advances the state of the GameControls
@ -746,7 +705,7 @@ func (g *GameControls) isInActiveMenusRect(px, py int) bool {
return true return true
} }
if g.hud.miniPanel.IsOpen() && g.hud.miniPanel.isInRect(px, py) { if g.hud.miniPanel.IsOpen() && g.hud.miniPanel.IsInRect(px, py) {
return true return true
} }
@ -828,23 +787,15 @@ func (g *GameControls) ToggleManaStats() {
// Handles what to do when an actionable is hovered // Handles what to do when an actionable is hovered
func (g *GameControls) onHoverActionable(item actionableType) { func (g *GameControls) onHoverActionable(item actionableType) {
hoverMap := map[actionableType]func(){ hoverMap := map[actionableType]func(){
leftSkill: func() {}, leftSkill: func() {},
newStats: func() {}, newStats: func() {},
xp: func() {}, xp: func() {},
walkRun: func() {}, walkRun: func() {},
stamina: func() {}, stamina: func() {},
miniPnl: func() {}, newSkills: func() {},
newSkills: func() {}, rightSkill: func() {},
rightSkill: func() {}, hpGlobe: func() {},
hpGlobe: func() {}, manaGlobe: func() {},
manaGlobe: func() {},
miniPanelCharacter: func() {},
miniPanelInventory: func() {},
miniPanelSkillTree: func() {},
miniPanelAutomap: func() {},
miniPanelMessageLog: func() {},
miniPanelQuestLog: func() {},
miniPanelGameMenu: func() {},
} }
onHover, found := hoverMap[item] onHover, found := hoverMap[item]
@ -879,12 +830,6 @@ func (g *GameControls) onClickActionable(item actionableType) {
log.Println("Stamina Action Pressed") log.Println("Stamina Action Pressed")
}, },
miniPnl: func() {
log.Println("Mini Panel Action Pressed")
g.hud.miniPanel.Toggle()
},
newSkills: func() { newSkills: func() {
log.Println("New Skills Selector Action Pressed") log.Println("New Skills Selector Action Pressed")
}, },
@ -902,32 +847,6 @@ func (g *GameControls) onClickActionable(item actionableType) {
g.ToggleManaStats() g.ToggleManaStats()
log.Println("Mana Globe Pressed") log.Println("Mana Globe Pressed")
}, },
miniPanelCharacter: func() {
log.Println("Character button on mini panel is pressed")
g.heroStatsPanel.Toggle()
g.updateLayout()
},
miniPanelInventory: func() {
log.Println("Inventory button on mini panel is pressed")
g.inventory.Toggle()
g.updateLayout()
},
miniPanelSkillTree: func() {
log.Println("Skilltree button on mini panel is pressed")
g.skilltree.Toggle()
g.updateLayout()
},
miniPanelGameMenu: func() {
g.hud.miniPanel.Close()
g.escapeMenu.open()
},
} }
action, found := actionMap[item] action, found := actionMap[item]

View File

@ -131,9 +131,7 @@ func (s *HeroStatsPanel) Load() {
log.Print(err) log.Print(err)
} }
fw, fh := frame.GetFrameBounds() w, h := frame.GetSize()
fc := frame.GetFrameCount()
w, h := fw*fc, fh*fc
staticPanel := s.uiManager.NewCustomWidgetCached(s.renderStaticMenu, w, h) staticPanel := s.uiManager.NewCustomWidgetCached(s.renderStaticMenu, w, h)
s.panelGroup.AddWidget(staticPanel) s.panelGroup.AddWidget(staticPanel)

View File

@ -47,7 +47,6 @@ const (
) )
const ( const (
frameMenuButton = 2
frameHealthStatus = 0 frameHealthStatus = 0
frameManaStatus = 1 frameManaStatus = 1
frameNewStatsSelector = 1 frameNewStatsSelector = 1
@ -69,7 +68,7 @@ const (
rightGlobeOffsetY = -8 rightGlobeOffsetY = -8
miniPanelButtonOffsetX = -8 miniPanelButtonOffsetX = -8
miniPanelButtonOffsetY = -16 miniPanelButtonOffsetY = -38
) )
const ( const (
@ -91,13 +90,14 @@ type HUD struct {
hero *d2mapentity.Player hero *d2mapentity.Player
mainPanel *d2ui.Sprite mainPanel *d2ui.Sprite
globeSprite *d2ui.Sprite globeSprite *d2ui.Sprite
menuButton *d2ui.Sprite menuButton *d2ui.Button
hpManaStatusSprite *d2ui.Sprite hpManaStatusSprite *d2ui.Sprite
leftSkillResource *SkillResource leftSkillResource *SkillResource
rightSkillResource *SkillResource rightSkillResource *SkillResource
runButton *d2ui.Button runButton *d2ui.Button
zoneChangeText *d2ui.Label zoneChangeText *d2ui.Label
miniPanel *miniPanel miniPanel *miniPanel
isMiniPanelOpen bool
isZoneTextShown bool isZoneTextShown bool
hpStatsIsVisible bool hpStatsIsVisible bool
manaStatsIsVisible bool manaStatsIsVisible bool
@ -241,16 +241,6 @@ func (h *HUD) loadSprites() {
log.Print(err) log.Print(err)
} }
h.menuButton, err = h.uiManager.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
err = h.menuButton.SetCurrentFrame(frameMenuButton)
if err != nil {
log.Print(err)
}
h.mainPanel, err = h.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky) h.mainPanel, err = h.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
@ -325,6 +315,17 @@ func (h *HUD) loadUIButtons() {
if h.hero.IsRunToggled() { if h.hero.IsRunToggled() {
h.runButton.Toggle() h.runButton.Toggle()
} }
// minipanel button
h.menuButton = h.uiManager.NewButton(d2ui.ButtonTypeMinipanelOpenClose, "")
//nolint:golint,gomnd // 2 is not a magic number
x := screenWidth/2 + miniPanelButtonOffsetX
y := screenHeight + miniPanelButtonOffsetY
h.menuButton.SetPosition(x, y)
h.menuButton.OnActivated(func() {
h.menuButton.Toggle()
h.miniPanel.Toggle()
})
} }
func (h *HUD) onToggleRunButton(noButton bool) { func (h *HUD) onToggleRunButton(noButton bool) {
@ -493,65 +494,6 @@ func (h *HUD) renderExperienceBar(target d2interface.Surface) {
target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100)) target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100))
} }
func (h *HUD) renderMiniPanel(target d2interface.Surface) error {
width, height := target.GetSize()
mx, my := h.lastMouseX, h.lastMouseY
menuButtonFrameIndex := 0
if h.miniPanel.isOpen {
menuButtonFrameIndex = 2
}
if err := h.menuButton.SetCurrentFrame(menuButtonFrameIndex); err != nil {
return err
}
buttonX, buttonY := (width>>1)+miniPanelButtonOffsetX, height+miniPanelButtonOffsetY
h.menuButton.SetPosition(buttonX, buttonY)
h.menuButton.Render(target)
h.miniPanel.Render(target)
miniPanelButtons := map[actionableType]string{
miniPanelCharacter: "minipanelchar",
miniPanelInventory: "minipanelinv",
miniPanelSkillTree: "minipaneltree",
miniPanelAutomap: "minipanelautomap",
miniPanelMessageLog: "minipanelmessage",
miniPanelQuestLog: "minipanelquest",
miniPanelGameMenu: "minipanelmenubtn",
}
if !h.miniPanel.IsOpen() {
return nil
}
for miniPanelButton, stringTableKey := range miniPanelButtons {
if !h.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
continue
}
rect := &h.actionableRegions[miniPanelButton].rect
h.miniPanelTooltip.SetText(h.asset.TranslateString(stringTableKey))
halfButtonWidth := rect.Width >> 1
halfButtonHeight := rect.Height >> 1
centerX := rect.Left + halfButtonWidth
centerY := rect.Top + halfButtonHeight
_, labelHeight := h.miniPanelTooltip.GetSize()
labelX := centerX
labelY := centerY - halfButtonHeight - labelHeight
h.miniPanelTooltip.SetPosition(labelX, labelY)
h.miniPanelTooltip.Render(target)
}
return nil
}
func (h *HUD) renderPotions(x, _ int, target d2interface.Surface) error { func (h *HUD) renderPotions(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize() _, height := target.GetSize()
@ -726,11 +668,6 @@ func (h *HUD) Render(target d2interface.Surface) error {
h.widgetStamina.Render(target) h.widgetStamina.Render(target)
h.widgetExperience.Render(target) h.widgetExperience.Render(target)
// Mini Panel and button
if err := h.renderMiniPanel(target); err != nil {
return err
}
if err := h.help.Render(target); err != nil { if err := h.help.Render(target); err != nil {
return err return err
} }
@ -784,3 +721,20 @@ func (h *HUD) OnMouseMove(event d2interface.MouseMoveEvent) bool {
return false return false
} }
func (h *HUD) closeMinipanelTemporary() {
h.isMiniPanelOpen = h.miniPanel.IsOpen()
if h.isMiniPanelOpen {
h.menuButton.SetEnabled(false)
h.menuButton.Toggle()
h.miniPanel.Close()
}
}
func (h *HUD) restoreMinipanelFromTempClose() {
if h.isMiniPanelOpen {
h.menuButton.SetEnabled(true)
h.menuButton.Toggle()
h.miniPanel.Open()
}
}

View File

@ -3,74 +3,192 @@ package d2player
import ( import (
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
) )
const ( const (
miniPanelX = 325 miniPanelX = 325
miniPanelY = 526 miniPanelY = 526
miniPanelWidth = 156
miniPanelHeight = 26 panelOffsetLeft = 130
panelOffsetRight = 130
) )
const ( const (
containerOffsetX = -75 containerOffsetX = -75
containerOffsetY = -48 containerOffsetY = -49
buttonOffsetX = -72 buttonOffsetX = -72
buttonOffsetY = -51 buttonOffsetY = -52
) )
type miniPanelContent struct {
buttonType d2ui.ButtonType
onActivate func()
tooltip string
}
type miniPanelActions struct {
characterToggle func()
inventoryToggle func()
skilltreeToggle func()
partyToggle func()
automapToggle func()
messageToggle func()
questToggle func()
menuToggle func()
}
type miniPanel struct { type miniPanel struct {
ui *d2ui.UIManager
asset *d2asset.AssetManager asset *d2asset.AssetManager
container *d2ui.Sprite container *d2ui.Sprite
button *d2ui.Sprite sprite *d2ui.Sprite
isOpen bool isOpen bool
isSinglePlayer bool isSinglePlayer bool
rectangle d2geom.Rectangle movedLeft bool
movedRight bool
panelGroup *d2ui.WidgetGroup
tooltipGroup *d2ui.WidgetGroup
} }
func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel { func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel {
return &miniPanel{
ui: uiManager,
asset: asset,
isOpen: false,
isSinglePlayer: isSinglePlayer,
}
}
func (m *miniPanel) load(actions *miniPanelActions) {
var err error
m.sprite, err = m.ui.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
return
}
m.createWidgets(actions)
}
func (m *miniPanel) createWidgets(actions *miniPanelActions) {
var err error
m.panelGroup = m.ui.NewWidgetGroup(d2ui.RenderPriorityMinipanel)
m.panelGroup.SetPosition(miniPanelX, miniPanelY)
m.tooltipGroup = m.ui.NewWidgetGroup(d2ui.RenderPriorityForeground)
miniPanelContainerPath := d2resource.Minipanel miniPanelContainerPath := d2resource.Minipanel
if isSinglePlayer { if m.isSinglePlayer {
miniPanelContainerPath = d2resource.MinipanelSmall miniPanelContainerPath = d2resource.MinipanelSmall
} }
containerSprite, err := uiManager.NewSprite(miniPanelContainerPath, d2resource.PaletteSky) m.container, err = m.ui.NewSprite(miniPanelContainerPath, d2resource.PaletteSky)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return nil return
} }
buttonSprite, err := uiManager.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky) if err = m.container.SetCurrentFrame(0); err != nil {
log.Print(err)
return
}
// nolint:golint,gomnd // divide by 2 does not need a magic number
x, y := screenWidth/2+containerOffsetX, screenHeight+containerOffsetY
m.container.SetPosition(x, y)
m.panelGroup.AddWidget(m.container)
buttonWidth, buttonHeight, err := m.sprite.GetFrameSize(0)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return nil return
} }
rectangle := d2geom.Rectangle{ buttonWidth++
Left: miniPanelX,
Top: miniPanelY, // nolint:golint,gomnd // divide by 2 does not need a magic number
Width: miniPanelWidth, x, y = screenWidth/2+buttonOffsetX, screenHeight+buttonOffsetY-buttonHeight
Height: miniPanelHeight, buttonsFirst := []miniPanelContent{
{d2ui.ButtonTypeMinipanelCharacter,
actions.characterToggle,
m.asset.TranslateString("minipanelchar"),
},
{d2ui.ButtonTypeMinipanelInventory,
actions.inventoryToggle,
m.asset.TranslateString("minipanelinv"),
},
{d2ui.ButtonTypeMinipanelSkill,
actions.skilltreeToggle,
m.asset.TranslateString("minipaneltree"),
},
} }
if !isSinglePlayer { for i := range buttonsFirst {
rectangle.Width = 182 btn := m.createButton(buttonsFirst[i], x+(i*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
} }
return &miniPanel{ idxOffset := len(buttonsFirst)
asset: asset,
container: containerSprite, if !m.isSinglePlayer {
button: buttonSprite, partyContent := miniPanelContent{d2ui.ButtonTypeMinipanelParty,
isOpen: false, actions.partyToggle,
isSinglePlayer: isSinglePlayer, m.asset.TranslateString("minipanelparty"),
rectangle: rectangle, }
btn := m.createButton(partyContent, x+(3*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
idxOffset++
} }
buttonsLast := []miniPanelContent{
{d2ui.ButtonTypeMinipanelAutomap,
actions.automapToggle,
m.asset.TranslateString("minipanelautomap"),
},
{d2ui.ButtonTypeMinipanelMessage,
actions.messageToggle,
m.asset.TranslateString("minipanelmessage"),
},
{d2ui.ButtonTypeMinipanelQuest,
actions.questToggle,
m.asset.TranslateString("minipanelquest"),
},
{d2ui.ButtonTypeMinipanelMen,
actions.menuToggle,
m.asset.TranslateString("minipanelmenubtn"),
},
}
for i := range buttonsLast {
idx := i + idxOffset
btn := m.createButton(buttonsLast[i], x+(idx*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
}
m.panelGroup.SetVisible(false)
}
func (m *miniPanel) createButton(content miniPanelContent, x, y, buttonHeight int) *d2ui.Button {
// Tooltip
tt := m.ui.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
tt.SetPosition(x, y-buttonHeight)
tt.SetText(content.tooltip)
tt.SetVisible(false)
m.tooltipGroup.AddWidget(tt)
// Button
btn := m.ui.NewButton(content.buttonType, "")
btn.SetPosition(x, y)
btn.OnActivated(content.onActivate)
btn.SetTooltip(tt)
return btn
} }
func (m *miniPanel) IsOpen() bool { func (m *miniPanel) IsOpen() bool {
@ -78,56 +196,91 @@ func (m *miniPanel) IsOpen() bool {
} }
func (m *miniPanel) Toggle() { func (m *miniPanel) Toggle() {
m.isOpen = !m.isOpen if m.isOpen {
m.Close()
} else {
m.Open()
}
} }
func (m *miniPanel) Open() { func (m *miniPanel) Open() {
m.panelGroup.SetVisible(true)
m.isOpen = true m.isOpen = true
} }
func (m *miniPanel) Close() { func (m *miniPanel) Close() {
m.panelGroup.SetVisible(false)
m.isOpen = false m.isOpen = false
} }
func (m *miniPanel) Render(target d2interface.Surface) { func (m *miniPanel) IsInRect(px, py int) bool {
if !m.isOpen { return m.panelGroup.Contains(px, py)
}
func (m *miniPanel) moveRight() {
m.panelGroup.OffsetPosition(panelOffsetRight, 0)
m.tooltipGroup.OffsetPosition(panelOffsetRight, 0)
}
func (m *miniPanel) undoMoveRight() {
m.panelGroup.OffsetPosition(-panelOffsetRight, 0)
m.tooltipGroup.OffsetPosition(-panelOffsetRight, 0)
}
func (m *miniPanel) moveLeft() {
m.panelGroup.OffsetPosition(-panelOffsetLeft, 0)
m.tooltipGroup.OffsetPosition(-panelOffsetLeft, 0)
}
func (m *miniPanel) undoMoveLeft() {
m.panelGroup.OffsetPosition(panelOffsetLeft, 0)
m.tooltipGroup.OffsetPosition(panelOffsetLeft, 0)
}
func (m *miniPanel) SetMovedLeft(moveLeft bool) {
if m.movedLeft == moveLeft {
return return
} }
if err := m.container.SetCurrentFrame(0); err != nil { if m.movedRight {
if moveLeft {
m.undoMoveRight()
m.panelGroup.SetVisible(false)
} else {
m.moveRight()
m.panelGroup.SetVisible(true)
}
} else {
if moveLeft {
m.moveLeft()
} else {
m.undoMoveLeft()
}
}
m.movedLeft = moveLeft
}
func (m *miniPanel) SetMovedRight(moveRight bool) {
if m.movedRight == moveRight {
return return
} }
width, height := target.GetSize() if m.movedLeft {
halfW := width >> 1 if moveRight {
x, y := halfW+containerOffsetX, height+containerOffsetY m.undoMoveLeft()
m.panelGroup.SetVisible(false)
m.container.SetPosition(x, y) } else {
m.moveLeft()
m.container.Render(target) m.panelGroup.SetVisible(true)
buttonWidth, _ := m.button.GetCurrentFrameSize()
buttonWidth++
for i, j := 0, 0; j < 16; i++ {
if m.isSinglePlayer && j == 6 { // skip Party Screen button if the game is single player
j += 2
} }
} else {
if err := m.button.SetCurrentFrame(j); err != nil { if moveRight {
return m.moveRight()
} else {
m.undoMoveRight()
} }
offsetX := buttonOffsetX + (buttonWidth * i)
x, y := halfW+offsetX, height+buttonOffsetY
m.button.SetPosition(x, y)
m.button.Render(target)
j += 2
} }
}
func (m *miniPanel) isInRect(x, y int) bool { m.movedRight = moveRight
return m.rectangle.IsInRect(x, y)
} }

View File

@ -357,6 +357,7 @@ func (s *skillTree) Open() {
s.isOpen = true s.isOpen = true
s.panelGroup.SetVisible(true) s.panelGroup.SetVisible(true)
s.iconGroup.SetVisible(true)
// we only want to enable the icons of our current tab again // we only want to enable the icons of our current tab again
s.setTab(s.selectedTab) s.setTab(s.selectedTab)