1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-22 23:25:23 +00:00

Added button logic.

This commit is contained in:
Tim Sarbin 2019-10-25 23:41:54 -04:00
parent b0de898fff
commit 054329cf1f
5 changed files with 243 additions and 29 deletions

View File

@ -61,7 +61,7 @@ func CreateSprite(data []byte, palette Palette) *Sprite {
result.Frames = make([]*SpriteFrame, totalFrames)
for i := uint32(0); i < totalFrames; i++ {
dataPointer = framePointers[i]
result.Frames[i] = &SpriteFrame{}
result.Frames[i] = &SpriteFrame{Loaded: false}
result.Frames[i].Flip = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
dataPointer += 4
result.Frames[i].Width = binary.LittleEndian.Uint32(data[dataPointer : dataPointer+4])
@ -130,6 +130,9 @@ func CreateSprite(data []byte, palette Palette) *Sprite {
// GetSize returns the size of the sprite
func (v *Sprite) GetSize() (uint32, uint32) {
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
for frame.Loaded == false {
time.Sleep(time.Millisecond)
}
return frame.Width, frame.Height
}
@ -148,11 +151,29 @@ func (v *Sprite) updateAnimation() {
}
}
// GetFrameSize returns the size of the specific frame
func (v *Sprite) GetFrameSize(frame int) (width, height uint32) {
for v.Frames[frame].Loaded == false {
time.Sleep(time.Millisecond)
}
width = v.Frames[frame].Width
height = v.Frames[frame].Height
return
}
// GetTotalFrames returns the number of frames in this sprite (for all directions)
func (v *Sprite) GetTotalFrames() int {
return len(v.Frames)
}
// Draw draws the sprite onto the target
func (v *Sprite) Draw(target *ebiten.Image) {
v.updateAnimation()
opts := &ebiten.DrawImageOptions{}
frame := v.Frames[uint32(v.Frame)+(uint32(v.Direction)*v.FramesPerDirection)]
for frame.Loaded == false {
time.Sleep(time.Millisecond)
}
opts.GeoM.Translate(
float64(int32(v.X)+frame.OffsetX),
float64((int32(v.Y) - int32(frame.Height) + frame.OffsetY)),
@ -165,9 +186,6 @@ func (v *Sprite) Draw(target *ebiten.Image) {
if v.ColorMod != nil {
opts.ColorM = ColorToColorM(v.ColorMod)
}
for frame.Image == nil {
time.Sleep(time.Millisecond)
}
target.DrawImage(frame.Image, opts)
}
@ -193,7 +211,7 @@ func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset
if v.ColorMod != nil {
opts.ColorM = ColorToColorM(v.ColorMod)
}
for frame.Image == nil {
for frame.Loaded == false {
time.Sleep(time.Millisecond)
}
target.DrawImage(frame.Image, opts)

View File

@ -2,6 +2,7 @@ package Scenes
import (
"image/color"
"os"
"github.com/essial/OpenDiablo2/Common"
"github.com/essial/OpenDiablo2/Palettes"
@ -88,14 +89,19 @@ func (v *MainMenu) Load() []func() {
v.diabloLogoRightBack.MoveTo(400, 120)
},
func() {
v.exitDiabloButton = UI.CreateButton(v.fileProvider, "EXIT DIABLO II")
v.exitDiabloButton = UI.CreateButton(UI.ButtonTypeWide, v.fileProvider, "EXIT DIABLO II")
v.exitDiabloButton.MoveTo(264, 535)
v.exitDiabloButton.SetVisible(false)
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
v.uiManager.AddWidget(v.exitDiabloButton)
},
}
}
func (v *MainMenu) onExitButtonClicked() {
os.Exit(0)
}
// Unload unloads the data for the main menu
func (v *MainMenu) Unload() {

View File

@ -1,6 +1,7 @@
package UI
import (
"image"
"image/color"
"github.com/essial/OpenDiablo2/Common"
@ -9,44 +10,160 @@ import (
"github.com/hajimehoshi/ebiten"
)
// ButtonType defines the type of button
type ButtonType int
const (
ButtonTypeWide ButtonType = 1
ButtonTypeMedium ButtonType = 2
ButtonTypeNarrow ButtonType = 3
ButtonTypeCancel ButtonType = 4
ButtonTypeTall ButtonType = 5
ButtonTypeShort ButtonType = 6
// 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 {
XSegments int //1
YSegments int // 1
ResourceName string
PaletteName Palettes.Palette
Toggleable bool // false
BaseFrame int // 0
DisabledFrame int // -1
FontPath string // ResourcePaths.FontExocet10
ClickableRect *image.Rectangle // nil
AllowFrameChange bool // true
}
// ButtonLayouts define the type of buttons you can have
var ButtonLayouts = map[ButtonType]ButtonLayout{
ButtonTypeWide: {2, 1, ResourcePaths.WideButtonBlank, Palettes.Units, false, 0, -1, ResourcePaths.FontExocet10, nil, true},
ButtonTypeShort: {1, 1, ResourcePaths.ShortButtonBlank, Palettes.Units, false, 0, -1, ResourcePaths.FontExocet8, nil, true},
/*
{eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Medium, new ButtonLayout{ ResourceName = ResourcePaths.MediumButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Tall, new ButtonLayout { ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Cancel, new ButtonLayout { ResourceName = ResourcePaths.CancelButton, PaletteName = Palettes.Units } },
// Minipanel
{eButtonType.MinipanelCharacter, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 0 } },
{eButtonType.MinipanelInventory, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 2 } },
{eButtonType.MinipanelSkill, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 4 } },
{eButtonType.MinipanelAutomap, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 8 } },
{eButtonType.MinipanelMessage, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.MinipanelQuest, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 12 } },
{eButtonType.MinipanelMenu, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 14 } },
{eButtonType.SecondaryInvHand, new ButtonLayout { ResourceName = ResourcePaths.InventoryWeaponsTab, PaletteName = Palettes.Units, ClickableRect = new Rectangle(0, 0, 0, 20), AllowFrameChange = false } },
{eButtonType.Run, new ButtonLayout { ResourceName = ResourcePaths.RunButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.Menu, new ButtonLayout { ResourceName = ResourcePaths.MenuButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.GoldCoin, new ButtonLayout { ResourceName = ResourcePaths.GoldCoinButton, PaletteName = Palettes.Units } },
{eButtonType.Close, new ButtonLayout { ResourceName = ResourcePaths.SquareButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.Skill, new ButtonLayout { ResourceName = ResourcePaths.AddSkillButton, PaletteName = Palettes.Units, DisabledFrame = 2
*/
}
// Button defines a standard wide UI button
type Button struct {
enabled bool
x, y int
width, height uint32
visible bool
pressed bool
fileProvider Common.FileProvider
normalImage *ebiten.Image
pressedImage *ebiten.Image
enabled bool
x, y int
width, height uint32
visible bool
pressed bool
toggled bool
fileProvider Common.FileProvider
normalImage *ebiten.Image
pressedImage *ebiten.Image
toggledImage *ebiten.Image
pressedToggledImage *ebiten.Image
disabledImage *ebiten.Image
onClick func()
}
// CreateButton creates an instance of Button
func CreateButton(fileProvider Common.FileProvider, text string) *Button {
func CreateButton(buttonType ButtonType, fileProvider Common.FileProvider, text string) *Button {
result := &Button{
fileProvider: fileProvider,
width: 272,
height: 35,
width: 0,
height: 0,
visible: true,
enabled: true,
pressed: false,
}
font := GetFont(ResourcePaths.FontExocet10, Palettes.Units, fileProvider)
result.normalImage, _ = ebiten.NewImage(272, 35, ebiten.FilterNearest)
result.pressedImage, _ = ebiten.NewImage(272, 35, ebiten.FilterNearest)
textWidth, textHeight := font.GetTextMetrics(text)
textX := (272 / 2) - (textWidth / 2)
textY := (35 / 2) - (textHeight / 2) + 5
buttonLayout := ButtonLayouts[buttonType]
font := GetFont(buttonLayout.FontPath, buttonLayout.PaletteName, fileProvider)
buttonSprite := fileProvider.LoadSprite(ResourcePaths.WideButtonBlank, Palettes.Units)
totalButtonTypes := buttonSprite.GetTotalFrames() / (buttonLayout.XSegments * buttonLayout.YSegments)
for i := 0; i < buttonLayout.XSegments; i++ {
w, _ := buttonSprite.GetFrameSize(i)
result.width += w
}
for i := 0; i < buttonLayout.YSegments; i++ {
_, h := buttonSprite.GetFrameSize(i * buttonLayout.YSegments)
result.height += h
}
result.normalImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest)
result.pressedImage, _ = ebiten.NewImage(int(result.width), int(result.height), ebiten.FilterNearest)
textWidth, textHeight := font.GetTextMetrics(text)
textX := (result.width / 2) - (textWidth / 2)
textY := (result.height / 2) - (textHeight / 2) + 5
buttonSprite.MoveTo(0, 0)
buttonSprite.Blend = true
buttonSprite.DrawSegments(result.normalImage, 2, 1, 0)
buttonSprite.DrawSegments(result.normalImage, 2, 1, buttonLayout.BaseFrame)
font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.normalImage)
buttonSprite.DrawSegments(result.pressedImage, 2, 1, 1)
font.Draw(int(textX-2), int(textY+2), text, color.Black, result.pressedImage)
if buttonLayout.AllowFrameChange {
if totalButtonTypes > 1 {
buttonSprite.DrawSegments(result.pressedImage, 2, 1, buttonLayout.BaseFrame+1)
font.Draw(int(textX-2), int(textY+2), text, color.RGBA{100, 100, 100, 255}, result.pressedImage)
}
if totalButtonTypes > 2 {
buttonSprite.DrawSegments(result.toggledImage, 2, 1, buttonLayout.BaseFrame+2)
font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.toggledImage)
}
if totalButtonTypes > 3 {
buttonSprite.DrawSegments(result.pressedToggledImage, 2, 1, buttonLayout.BaseFrame+3)
font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.pressedToggledImage)
}
if buttonLayout.DisabledFrame != -1 {
buttonSprite.DrawSegments(result.disabledImage, 2, 1, buttonLayout.DisabledFrame)
font.Draw(int(textX), int(textY), text, color.RGBA{100, 100, 100, 255}, result.disabledImage)
}
}
return result
}
// 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
func (v *Button) Activate() {
if v.onClick == nil {
return
}
v.onClick()
}
// Draw renders the button
func (v *Button) Draw(target *ebiten.Image) {
opts := &ebiten.DrawImageOptions{
@ -54,11 +171,18 @@ func (v *Button) Draw(target *ebiten.Image) {
Filter: ebiten.FilterNearest,
}
opts.GeoM.Translate(float64(v.x), float64(v.y))
if v.pressed {
if !v.enabled {
target.DrawImage(v.disabledImage, opts)
} else if v.toggled && v.pressed {
target.DrawImage(v.pressedToggledImage, opts)
} else if v.pressed {
target.DrawImage(v.pressedImage, opts)
return
} else if v.toggled {
target.DrawImage(v.toggledImage, opts)
} else {
target.DrawImage(v.normalImage, opts)
}
target.DrawImage(v.normalImage, opts)
}
// GetEnabled returns the enabled state
@ -96,3 +220,13 @@ func (v *Button) GetVisible() bool {
func (v *Button) SetVisible(visible bool) {
v.visible = visible
}
// GetPressed returns the pressed state of the button
func (v *Button) GetPressed() bool {
return v.pressed
}
// SetPressed sets the pressed state of the button
func (v *Button) SetPressed(pressed bool) {
v.pressed = pressed
}

View File

@ -22,6 +22,7 @@ type Manager struct {
widgets []Widget
cursorSprite *Common.Sprite
cursorButtons CursorButton
pressedIndex int
CursorX int
CursorY int
}
@ -29,6 +30,7 @@ type Manager struct {
// CreateManager creates a new instance of a UI manager
func CreateManager(provider Common.FileProvider) *Manager {
result := &Manager{
pressedIndex: -1,
widgets: make([]Widget, 0),
cursorSprite: provider.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units),
}
@ -38,6 +40,7 @@ func CreateManager(provider Common.FileProvider) *Manager {
// Reset resets the state of the UI manager. Typically called for new scenes
func (v *Manager) Reset() {
v.widgets = make([]Widget, 0)
v.pressedIndex = -1
}
// AddWidget adds a widget to the UI manager
@ -69,6 +72,55 @@ func (v *Manager) Update() {
v.cursorButtons |= CursorButtonRight
}
v.CursorX, v.CursorY = ebiten.CursorPosition()
if v.CursorButtonPressed(CursorButtonLeft) {
found := false
for i, widget := range v.widgets {
if !widget.GetVisible() || !widget.GetEnabled() {
continue
}
wx, wy := widget.GetLocation()
ww, wh := widget.GetSize()
if v.CursorX >= wx && v.CursorX <= wx+int(ww) && v.CursorY >= wy && v.CursorY <= wy+int(wh) {
widget.SetPressed(true)
if v.pressedIndex == -1 {
found = true
v.pressedIndex = i
} else if v.pressedIndex > -1 && v.pressedIndex != i {
v.widgets[i].SetPressed(false)
} else {
v.widgets[i].SetPressed(true)
found = true
}
break
} else {
widget.SetPressed(false)
}
}
if !found {
if v.pressedIndex > -1 {
v.widgets[v.pressedIndex].SetPressed(false)
} else {
v.pressedIndex = -2
}
}
} else {
if v.pressedIndex > -1 {
widget := v.widgets[v.pressedIndex]
wx, wy := widget.GetLocation()
ww, wh := widget.GetSize()
if v.CursorX >= wx && v.CursorX <= wx+int(ww) && v.CursorY >= wy && v.CursorY <= wy+int(wh) {
widget.Activate()
}
} else {
for _, widget := range v.widgets {
if !widget.GetVisible() || !widget.GetEnabled() {
continue
}
widget.SetPressed(false)
}
}
v.pressedIndex = -1
}
}
// CursorButtonPressed determines if the specified button has been pressed

View File

@ -9,4 +9,8 @@ type Widget interface {
Common.Drawable
GetEnabled() bool
SetEnabled(enabled bool)
SetPressed(pressed bool)
GetPressed() bool
OnActivated(callback func())
Activate()
}