2019-11-10 08:51:02 -05:00
|
|
|
package d2ui
|
2019-10-25 18:40:27 -04:00
|
|
|
|
2019-10-25 22:20:36 -04:00
|
|
|
import (
|
2020-07-07 20:16:22 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
2019-10-25 23:41:54 -04:00
|
|
|
"image"
|
2019-10-25 22:20:36 -04:00
|
|
|
"image/color"
|
|
|
|
|
2020-06-30 09:58:53 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-06-29 00:41:58 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2020-01-31 23:18:11 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2020-07-03 14:00:56 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
2019-10-25 22:20:36 -04:00
|
|
|
)
|
|
|
|
|
2019-10-25 23:41:54 -04:00
|
|
|
// ButtonType defines the type of button
|
|
|
|
type ButtonType int
|
|
|
|
|
|
|
|
const (
|
2019-11-11 23:48:55 -05:00
|
|
|
ButtonTypeWide ButtonType = 1
|
|
|
|
ButtonTypeMedium ButtonType = 2
|
|
|
|
ButtonTypeNarrow ButtonType = 3
|
|
|
|
ButtonTypeCancel ButtonType = 4
|
|
|
|
ButtonTypeTall ButtonType = 5
|
|
|
|
ButtonTypeShort ButtonType = 6
|
|
|
|
ButtonTypeOkCancel ButtonType = 7
|
2019-10-25 23:41:54 -04:00
|
|
|
|
|
|
|
// Game UI
|
|
|
|
|
|
|
|
ButtonTypeSkill ButtonType = 7
|
|
|
|
ButtonTypeRun ButtonType = 8
|
|
|
|
ButtonTypeMenu ButtonType = 9
|
|
|
|
ButtonTypeGoldCoin ButtonType = 10
|
|
|
|
ButtonTypeClose ButtonType = 11
|
|
|
|
ButtonTypeSecondaryInvHand ButtonType = 12
|
|
|
|
ButtonTypeMinipanelCharacter ButtonType = 13
|
|
|
|
ButtonTypeMinipanelInventory ButtonType = 14
|
|
|
|
ButtonTypeMinipanelSkill ButtonType = 15
|
|
|
|
ButtonTypeMinipanelAutomap ButtonType = 16
|
|
|
|
ButtonTypeMinipanelMessage ButtonType = 17
|
|
|
|
ButtonTypeMinipanelQuest ButtonType = 18
|
|
|
|
ButtonTypeMinipanelMen ButtonType = 19
|
|
|
|
)
|
|
|
|
|
|
|
|
// ButtonLayout defines the type of buttons
|
|
|
|
type ButtonLayout struct {
|
2020-04-11 14:56:47 -04:00
|
|
|
XSegments int // 1
|
2019-12-21 20:53:18 -05:00
|
|
|
YSegments int // 1
|
|
|
|
ResourceName string // Font Name
|
|
|
|
PaletteName string // PaletteType
|
|
|
|
Toggleable bool // false
|
|
|
|
BaseFrame int // 0
|
|
|
|
DisabledFrame int // -1
|
|
|
|
FontPath string // ResourcePaths.FontExocet10
|
|
|
|
ClickableRect *image.Rectangle // nil
|
|
|
|
AllowFrameChange bool // true
|
|
|
|
TextOffset int // 0
|
2019-10-25 23:41:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// ButtonLayouts define the type of buttons you can have
|
|
|
|
var ButtonLayouts = map[ButtonType]ButtonLayout{
|
2019-12-21 20:53:18 -05:00
|
|
|
ButtonTypeWide: {2, 1, d2resource.WideButtonBlank, d2resource.PaletteUnits, false, 0, -1, d2resource.FontExocet10, nil, true, 1},
|
|
|
|
ButtonTypeShort: {1, 1, d2resource.ShortButtonBlank, d2resource.PaletteUnits, false, 0, -1, d2resource.FontRediculous, nil, true, -1},
|
|
|
|
ButtonTypeMedium: {1, 1, d2resource.MediumButtonBlank, d2resource.PaletteUnits, false, 0, 0, d2resource.FontExocet10, nil, true, 0},
|
|
|
|
ButtonTypeTall: {1, 1, d2resource.TallButtonBlank, d2resource.PaletteUnits, false, 0, 0, d2resource.FontExocet10, nil, true, 5},
|
|
|
|
ButtonTypeOkCancel: {1, 1, d2resource.CancelButton, d2resource.PaletteUnits, false, 0, -1, d2resource.FontRediculous, nil, true, 0},
|
2020-06-24 15:23:38 -04:00
|
|
|
ButtonTypeRun: {1, 1, d2resource.RunButton, d2resource.PaletteSky, true, 0, -1, d2resource.FontRediculous, nil, true, 0},
|
2019-10-25 23:41:54 -04:00
|
|
|
/*
|
2019-11-01 14:12:23 -04:00
|
|
|
{eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = PaletteDefs.Units } },
|
|
|
|
{eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = PaletteDefs.Units } },
|
|
|
|
{eButtonType.Cancel, new ButtonLayout { ResourceName = ResourcePaths.CancelButton, PaletteName = PaletteDefs.Units } },
|
2019-10-25 23:41:54 -04:00
|
|
|
// Minipanel
|
2019-11-01 14:12:23 -04:00
|
|
|
{eButtonType.MinipanelCharacter, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 0 } },
|
|
|
|
{eButtonType.MinipanelInventory, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 2 } },
|
|
|
|
{eButtonType.MinipanelSkill, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 4 } },
|
|
|
|
{eButtonType.MinipanelAutomap, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 8 } },
|
|
|
|
{eButtonType.MinipanelMessage, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 10 } },
|
|
|
|
{eButtonType.MinipanelQuest, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 12 } },
|
|
|
|
{eButtonType.MinipanelMenu, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = PaletteDefs.Units, BaseFrame = 14 } },
|
2019-10-25 23:41:54 -04:00
|
|
|
|
2019-11-01 14:12:23 -04:00
|
|
|
{eButtonType.SecondaryInvHand, new ButtonLayout { ResourceName = ResourcePaths.InventoryWeaponsTab, PaletteName = PaletteDefs.Units, ClickableRect = new Rectangle(0, 0, 0, 20), AllowFrameChange = false } },
|
|
|
|
{eButtonType.Run, new ButtonLayout { ResourceName = ResourcePaths.RunButton, PaletteName = PaletteDefs.Units, Toggleable = true } },
|
|
|
|
{eButtonType.Menu, new ButtonLayout { ResourceName = ResourcePaths.MenuButton, PaletteName = PaletteDefs.Units, Toggleable = true } },
|
|
|
|
{eButtonType.GoldCoin, new ButtonLayout { ResourceName = ResourcePaths.GoldCoinButton, PaletteName = PaletteDefs.Units } },
|
|
|
|
{eButtonType.Close, new ButtonLayout { ResourceName = ResourcePaths.SquareButton, PaletteName = PaletteDefs.Units, BaseFrame = 10 } },
|
|
|
|
{eButtonType.Skill, new ButtonLayout { ResourceName = ResourcePaths.AddSkillButton, PaletteName = PaletteDefs.Units, DisabledFrame = 2
|
2019-10-25 23:41:54 -04:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2019-10-25 22:20:36 -04:00
|
|
|
// Button defines a standard wide UI button
|
|
|
|
type Button struct {
|
2020-01-31 23:18:11 -05:00
|
|
|
enabled bool
|
|
|
|
x, y int
|
|
|
|
width, height int
|
|
|
|
visible bool
|
|
|
|
pressed bool
|
|
|
|
toggled bool
|
2020-06-29 00:41:58 -04:00
|
|
|
normalSurface d2interface.Surface
|
|
|
|
pressedSurface d2interface.Surface
|
|
|
|
toggledSurface d2interface.Surface
|
|
|
|
pressedToggledSurface d2interface.Surface
|
|
|
|
disabledSurface d2interface.Surface
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonLayout ButtonLayout
|
|
|
|
onClick func()
|
2019-10-25 22:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateButton creates an instance of Button
|
2020-07-03 14:00:56 -04:00
|
|
|
func CreateButton(renderer d2interface.Renderer, buttonType ButtonType, text string) Button {
|
2019-11-10 12:28:41 -05:00
|
|
|
result := Button{
|
2019-12-21 20:53:18 -05:00
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
visible: true,
|
|
|
|
enabled: true,
|
|
|
|
pressed: false,
|
2019-10-25 22:20:36 -04:00
|
|
|
}
|
2019-10-25 23:41:54 -04:00
|
|
|
buttonLayout := ButtonLayouts[buttonType]
|
2019-10-26 23:59:27 -04:00
|
|
|
result.buttonLayout = buttonLayout
|
2020-07-07 20:16:22 -04:00
|
|
|
lbl := CreateLabel(buttonLayout.FontPath, d2resource.PaletteUnits)
|
|
|
|
lbl.SetText(text)
|
|
|
|
lbl.Color = color.RGBA{R: 100, G: 100, B: 100, A: 255}
|
|
|
|
lbl.Alignment = d2gui.HorizontalAlignCenter
|
2019-11-10 03:36:53 -05:00
|
|
|
|
2020-02-01 18:55:56 -05:00
|
|
|
animation, _ := d2asset.LoadAnimation(buttonLayout.ResourceName, buttonLayout.PaletteName)
|
|
|
|
buttonSprite, _ := LoadSprite(animation)
|
2019-12-21 20:53:18 -05:00
|
|
|
totalButtonTypes := buttonSprite.GetFrameCount() / (buttonLayout.XSegments * buttonLayout.YSegments)
|
2019-10-25 23:41:54 -04:00
|
|
|
for i := 0; i < buttonLayout.XSegments; i++ {
|
2019-12-21 20:53:18 -05:00
|
|
|
w, _, _ := buttonSprite.GetFrameSize(i)
|
2019-10-25 23:41:54 -04:00
|
|
|
result.width += w
|
|
|
|
}
|
|
|
|
for i := 0; i < buttonLayout.YSegments; i++ {
|
2019-12-21 20:53:18 -05:00
|
|
|
_, h, _ := buttonSprite.GetFrameSize(i * buttonLayout.YSegments)
|
2019-10-25 23:41:54 -04:00
|
|
|
result.height += h
|
|
|
|
}
|
|
|
|
|
2020-07-06 21:26:08 -04:00
|
|
|
result.normalSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
2020-07-07 20:16:22 -04:00
|
|
|
_, labelHeight := lbl.GetSize()
|
|
|
|
|
|
|
|
textY := result.height / 2 - labelHeight / 2
|
|
|
|
xOffset := result.width / 2
|
2019-10-27 02:58:37 -04:00
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
buttonSprite.SetPosition(0, 0)
|
|
|
|
buttonSprite.SetBlend(true)
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonSprite.RenderSegmented(result.normalSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame)
|
2020-07-07 20:16:22 -04:00
|
|
|
|
|
|
|
lbl.SetPosition(xOffset, textY)
|
|
|
|
lbl.Render(result.normalSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
if buttonLayout.AllowFrameChange {
|
|
|
|
if totalButtonTypes > 1 {
|
2020-07-06 21:26:08 -04:00
|
|
|
result.pressedSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonSprite.RenderSegmented(result.pressedSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+1)
|
2020-07-07 20:16:22 -04:00
|
|
|
|
|
|
|
lbl.SetPosition(xOffset-2, textY + 2)
|
|
|
|
lbl.Render(result.pressedSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
}
|
|
|
|
if totalButtonTypes > 2 {
|
2020-07-06 21:26:08 -04:00
|
|
|
result.toggledSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonSprite.RenderSegmented(result.toggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+2)
|
2020-07-07 20:16:22 -04:00
|
|
|
|
|
|
|
lbl.SetPosition(xOffset, textY)
|
|
|
|
lbl.Render(result.toggledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
}
|
|
|
|
if totalButtonTypes > 3 {
|
2020-07-06 21:26:08 -04:00
|
|
|
result.pressedToggledSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonSprite.RenderSegmented(result.pressedToggledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.BaseFrame+3)
|
2020-07-07 20:16:22 -04:00
|
|
|
|
|
|
|
lbl.SetPosition(xOffset, textY)
|
|
|
|
lbl.Render(result.pressedToggledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
}
|
|
|
|
if buttonLayout.DisabledFrame != -1 {
|
2020-07-06 21:26:08 -04:00
|
|
|
result.disabledSurface, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
2020-01-31 23:18:11 -05:00
|
|
|
buttonSprite.RenderSegmented(result.disabledSurface, buttonLayout.XSegments, buttonLayout.YSegments, buttonLayout.DisabledFrame)
|
2020-07-07 20:16:22 -04:00
|
|
|
|
|
|
|
lbl.SetPosition(xOffset, textY)
|
|
|
|
lbl.Render(result.disabledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
}
|
|
|
|
}
|
2019-10-25 22:20:36 -04:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2019-10-25 23:41:54 -04:00
|
|
|
// OnActivated defines the callback handler for the activate event
|
|
|
|
func (v *Button) OnActivated(callback func()) {
|
|
|
|
v.onClick = callback
|
|
|
|
}
|
|
|
|
|
|
|
|
// Activate calls the on activated callback handler, if any
|
2019-11-11 23:48:55 -05:00
|
|
|
func (v *Button) Activate() {
|
2019-10-25 23:41:54 -04:00
|
|
|
if v.onClick == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v.onClick()
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
// Render renders the button
|
2020-06-29 00:41:58 -04:00
|
|
|
func (v *Button) Render(target d2interface.Surface) {
|
2020-06-30 09:58:53 -04:00
|
|
|
target.PushCompositeMode(d2enum.CompositeModeSourceAtop)
|
2020-07-06 21:26:08 -04:00
|
|
|
target.PushFilter(d2enum.FilterNearest)
|
2019-12-28 16:46:08 -05:00
|
|
|
target.PushTranslation(v.x, v.y)
|
|
|
|
defer target.PopN(3)
|
2019-10-25 23:41:54 -04:00
|
|
|
|
|
|
|
if !v.enabled {
|
2020-02-01 21:51:49 -05:00
|
|
|
target.PushColor(color.RGBA{R: 128, G: 128, B: 128, A: 195})
|
2019-12-28 16:46:08 -05:00
|
|
|
defer target.Pop()
|
2020-01-31 23:18:11 -05:00
|
|
|
target.Render(v.disabledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
} else if v.toggled && v.pressed {
|
2020-01-31 23:18:11 -05:00
|
|
|
target.Render(v.pressedToggledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
} else if v.pressed {
|
2020-01-31 23:18:11 -05:00
|
|
|
target.Render(v.pressedSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
} else if v.toggled {
|
2020-01-31 23:18:11 -05:00
|
|
|
target.Render(v.toggledSurface)
|
2019-10-25 23:41:54 -04:00
|
|
|
} else {
|
2020-01-31 23:18:11 -05:00
|
|
|
target.Render(v.normalSurface)
|
2019-10-25 22:20:36 -04:00
|
|
|
}
|
|
|
|
}
|
2020-06-24 15:23:38 -04:00
|
|
|
|
|
|
|
func (v *Button) Toggle() {
|
|
|
|
v.toggled = !v.toggled
|
|
|
|
}
|
2019-10-25 22:20:36 -04:00
|
|
|
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) Advance(elapsed float64) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-25 22:20:36 -04:00
|
|
|
// GetEnabled returns the enabled state
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) GetEnabled() bool {
|
2019-10-25 22:20:36 -04:00
|
|
|
return v.enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetEnabled sets the enabled state
|
|
|
|
func (v *Button) SetEnabled(enabled bool) {
|
|
|
|
v.enabled = enabled
|
|
|
|
}
|
|
|
|
|
2020-06-21 18:40:37 -04:00
|
|
|
// Size returns the size of the button
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) GetSize() (int, int) {
|
2019-10-25 22:20:36 -04:00
|
|
|
return v.width, v.height
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
// SetPosition moves the button
|
|
|
|
func (v *Button) SetPosition(x, y int) {
|
2019-10-25 22:20:36 -04:00
|
|
|
v.x = x
|
|
|
|
v.y = y
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:53:18 -05:00
|
|
|
// GetPosition returns the location of the button
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) GetPosition() (x, y int) {
|
2019-10-25 22:20:36 -04:00
|
|
|
return v.x, v.y
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetVisible returns the visibility of the button
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) GetVisible() bool {
|
2019-10-25 22:20:36 -04:00
|
|
|
return v.visible
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetVisible sets the visibility of the button
|
|
|
|
func (v *Button) SetVisible(visible bool) {
|
|
|
|
v.visible = visible
|
2019-10-25 18:40:27 -04:00
|
|
|
}
|
2019-10-25 23:41:54 -04:00
|
|
|
|
|
|
|
// GetPressed returns the pressed state of the button
|
2019-12-28 23:32:24 -05:00
|
|
|
func (v *Button) GetPressed() bool {
|
2019-10-25 23:41:54 -04:00
|
|
|
return v.pressed
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetPressed sets the pressed state of the button
|
|
|
|
func (v *Button) SetPressed(pressed bool) {
|
|
|
|
v.pressed = pressed
|
|
|
|
}
|