Abstract away remaining ebiten references (#409)

* 337 - remove ebiten from character selection

* 337 - abstract d2input away from ebiten implementation

* WIP 337 - remove ebiten use from d2ui

* 337 - fix accidental left->right change

* 337 - fix ui button selection bugs

* 337 - fix textbox bugs

* 337 - fix scrollbar bugs

* 337 - address PR comments

* 337 - fix invalid hero selection bug

Co-authored-by: David Carrell <carrelda@Davids-MacBook-Pro.local>
This commit is contained in:
David Carrell 2020-06-23 17:12:08 -05:00 committed by GitHub
parent f729ff6101
commit 37ae98d81b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 506 additions and 336 deletions

View File

@ -17,6 +17,7 @@ Intyre
Gürkan Kaymak
Maxime "malavv" Lavigne
Ripolak
dafe
* DIABLO2 LOGO
Jose Pardilla (th3-prophetman)

View File

@ -2,8 +2,6 @@ package d2input
import (
"errors"
"github.com/hajimehoshi/ebiten"
)
var (
@ -19,136 +17,6 @@ const (
PriorityHigh
)
type Key int
//noinspection GoUnusedConst
const (
Key0 = Key(ebiten.Key0)
Key1 = Key(ebiten.Key1)
Key2 = Key(ebiten.Key2)
Key3 = Key(ebiten.Key3)
Key4 = Key(ebiten.Key4)
Key5 = Key(ebiten.Key5)
Key6 = Key(ebiten.Key6)
Key7 = Key(ebiten.Key7)
Key8 = Key(ebiten.Key8)
Key9 = Key(ebiten.Key9)
KeyA = Key(ebiten.KeyA)
KeyB = Key(ebiten.KeyB)
KeyC = Key(ebiten.KeyC)
KeyD = Key(ebiten.KeyD)
KeyE = Key(ebiten.KeyE)
KeyF = Key(ebiten.KeyF)
KeyG = Key(ebiten.KeyG)
KeyH = Key(ebiten.KeyH)
KeyI = Key(ebiten.KeyI)
KeyJ = Key(ebiten.KeyJ)
KeyK = Key(ebiten.KeyK)
KeyL = Key(ebiten.KeyL)
KeyM = Key(ebiten.KeyM)
KeyN = Key(ebiten.KeyN)
KeyO = Key(ebiten.KeyO)
KeyP = Key(ebiten.KeyP)
KeyQ = Key(ebiten.KeyQ)
KeyR = Key(ebiten.KeyR)
KeyS = Key(ebiten.KeyS)
KeyT = Key(ebiten.KeyT)
KeyU = Key(ebiten.KeyU)
KeyV = Key(ebiten.KeyV)
KeyW = Key(ebiten.KeyW)
KeyX = Key(ebiten.KeyX)
KeyY = Key(ebiten.KeyY)
KeyZ = Key(ebiten.KeyZ)
KeyApostrophe = Key(ebiten.KeyApostrophe)
KeyBackslash = Key(ebiten.KeyBackslash)
KeyBackspace = Key(ebiten.KeyBackspace)
KeyCapsLock = Key(ebiten.KeyCapsLock)
KeyComma = Key(ebiten.KeyComma)
KeyDelete = Key(ebiten.KeyDelete)
KeyDown = Key(ebiten.KeyDown)
KeyEnd = Key(ebiten.KeyEnd)
KeyEnter = Key(ebiten.KeyEnter)
KeyEqual = Key(ebiten.KeyEqual)
KeyEscape = Key(ebiten.KeyEscape)
KeyF1 = Key(ebiten.KeyF1)
KeyF2 = Key(ebiten.KeyF2)
KeyF3 = Key(ebiten.KeyF3)
KeyF4 = Key(ebiten.KeyF4)
KeyF5 = Key(ebiten.KeyF5)
KeyF6 = Key(ebiten.KeyF6)
KeyF7 = Key(ebiten.KeyF7)
KeyF8 = Key(ebiten.KeyF8)
KeyF9 = Key(ebiten.KeyF9)
KeyF10 = Key(ebiten.KeyF10)
KeyF11 = Key(ebiten.KeyF11)
KeyF12 = Key(ebiten.KeyF12)
KeyGraveAccent = Key(ebiten.KeyGraveAccent)
KeyHome = Key(ebiten.KeyHome)
KeyInsert = Key(ebiten.KeyInsert)
KeyKP0 = Key(ebiten.KeyKP0)
KeyKP1 = Key(ebiten.KeyKP1)
KeyKP2 = Key(ebiten.KeyKP2)
KeyKP3 = Key(ebiten.KeyKP3)
KeyKP4 = Key(ebiten.KeyKP4)
KeyKP5 = Key(ebiten.KeyKP5)
KeyKP6 = Key(ebiten.KeyKP6)
KeyKP7 = Key(ebiten.KeyKP7)
KeyKP8 = Key(ebiten.KeyKP8)
KeyKP9 = Key(ebiten.KeyKP9)
KeyKPAdd = Key(ebiten.KeyKPAdd)
KeyKPDecimal = Key(ebiten.KeyKPDecimal)
KeyKPDivide = Key(ebiten.KeyKPDivide)
KeyKPEnter = Key(ebiten.KeyKPEnter)
KeyKPEqual = Key(ebiten.KeyKPEqual)
KeyKPMultiply = Key(ebiten.KeyKPMultiply)
KeyKPSubtract = Key(ebiten.KeyKPSubtract)
KeyLeft = Key(ebiten.KeyLeft)
KeyLeftBracket = Key(ebiten.KeyLeftBracket)
KeyMenu = Key(ebiten.KeyMenu)
KeyMinus = Key(ebiten.KeyMinus)
KeyNumLock = Key(ebiten.KeyNumLock)
KeyPageDown = Key(ebiten.KeyPageDown)
KeyPageUp = Key(ebiten.KeyPageUp)
KeyPause = Key(ebiten.KeyPause)
KeyPeriod = Key(ebiten.KeyPeriod)
KeyPrintScreen = Key(ebiten.KeyPrintScreen)
KeyRight = Key(ebiten.KeyRight)
KeyRightBracket = Key(ebiten.KeyRightBracket)
KeyScrollLock = Key(ebiten.KeyScrollLock)
KeySemicolon = Key(ebiten.KeySemicolon)
KeySlash = Key(ebiten.KeySlash)
KeySpace = Key(ebiten.KeySpace)
KeyTab = Key(ebiten.KeyTab)
KeyUp = Key(ebiten.KeyUp)
KeyAlt = Key(ebiten.KeyAlt)
KeyControl = Key(ebiten.KeyControl)
KeyShift = Key(ebiten.KeyShift)
)
type KeyMod int
const (
KeyModAlt = 1 << iota
KeyModControl
KeyModShift
)
type MouseButton int
const (
MouseButtonLeft = MouseButton(ebiten.MouseButtonLeft)
MouseButtonMiddle = MouseButton(ebiten.MouseButtonMiddle)
MouseButtonRight = MouseButton(ebiten.MouseButtonRight)
)
type MouseButtonMod int
const (
MouseButtonModLeft MouseButtonMod = 1 << iota
MouseButtonModMiddle
MouseButtonModRight
)
type HandlerEvent struct {
KeyMod KeyMod
ButtonMod MouseButtonMod
@ -159,6 +27,8 @@ type HandlerEvent struct {
type KeyEvent struct {
HandlerEvent
Key Key
// Duration represents the number of frames this key has been pressed for
Duration int
}
type KeyCharsEvent struct {
@ -211,6 +81,12 @@ type MouseMoveHandler interface {
var singleton inputManager
func Initialize(inputService InputService) {
singleton = inputManager{
inputService: inputService,
}
}
func Advance(elapsed float64) error {
return singleton.advance(elapsed)
}

View File

@ -0,0 +1,156 @@
package ebiten
import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
var (
keyToEbiten = map[d2input.Key]ebiten.Key{
d2input.Key0: ebiten.Key0,
d2input.Key1: ebiten.Key1,
d2input.Key2: ebiten.Key2,
d2input.Key3: ebiten.Key3,
d2input.Key4: ebiten.Key4,
d2input.Key5: ebiten.Key5,
d2input.Key6: ebiten.Key6,
d2input.Key7: ebiten.Key7,
d2input.Key8: ebiten.Key8,
d2input.Key9: ebiten.Key9,
d2input.KeyA: ebiten.KeyA,
d2input.KeyB: ebiten.KeyB,
d2input.KeyC: ebiten.KeyC,
d2input.KeyD: ebiten.KeyD,
d2input.KeyE: ebiten.KeyE,
d2input.KeyF: ebiten.KeyF,
d2input.KeyG: ebiten.KeyG,
d2input.KeyH: ebiten.KeyH,
d2input.KeyI: ebiten.KeyI,
d2input.KeyJ: ebiten.KeyJ,
d2input.KeyK: ebiten.KeyK,
d2input.KeyL: ebiten.KeyL,
d2input.KeyM: ebiten.KeyM,
d2input.KeyN: ebiten.KeyN,
d2input.KeyO: ebiten.KeyO,
d2input.KeyP: ebiten.KeyP,
d2input.KeyQ: ebiten.KeyQ,
d2input.KeyR: ebiten.KeyR,
d2input.KeyS: ebiten.KeyS,
d2input.KeyT: ebiten.KeyT,
d2input.KeyU: ebiten.KeyU,
d2input.KeyV: ebiten.KeyV,
d2input.KeyW: ebiten.KeyW,
d2input.KeyX: ebiten.KeyX,
d2input.KeyY: ebiten.KeyY,
d2input.KeyZ: ebiten.KeyZ,
d2input.KeyApostrophe: ebiten.KeyApostrophe,
d2input.KeyBackslash: ebiten.KeyBackslash,
d2input.KeyBackspace: ebiten.KeyBackspace,
d2input.KeyCapsLock: ebiten.KeyCapsLock,
d2input.KeyComma: ebiten.KeyComma,
d2input.KeyDelete: ebiten.KeyDelete,
d2input.KeyDown: ebiten.KeyDown,
d2input.KeyEnd: ebiten.KeyEnd,
d2input.KeyEnter: ebiten.KeyEnter,
d2input.KeyEqual: ebiten.KeyEqual,
d2input.KeyEscape: ebiten.KeyEscape,
d2input.KeyF1: ebiten.KeyF1,
d2input.KeyF2: ebiten.KeyF2,
d2input.KeyF3: ebiten.KeyF3,
d2input.KeyF4: ebiten.KeyF4,
d2input.KeyF5: ebiten.KeyF5,
d2input.KeyF6: ebiten.KeyF6,
d2input.KeyF7: ebiten.KeyF7,
d2input.KeyF8: ebiten.KeyF8,
d2input.KeyF9: ebiten.KeyF9,
d2input.KeyF10: ebiten.KeyF10,
d2input.KeyF11: ebiten.KeyF11,
d2input.KeyF12: ebiten.KeyF12,
d2input.KeyGraveAccent: ebiten.KeyGraveAccent,
d2input.KeyHome: ebiten.KeyHome,
d2input.KeyInsert: ebiten.KeyInsert,
d2input.KeyKP0: ebiten.KeyKP0,
d2input.KeyKP1: ebiten.KeyKP1,
d2input.KeyKP2: ebiten.KeyKP2,
d2input.KeyKP3: ebiten.KeyKP3,
d2input.KeyKP4: ebiten.KeyKP4,
d2input.KeyKP5: ebiten.KeyKP5,
d2input.KeyKP6: ebiten.KeyKP6,
d2input.KeyKP7: ebiten.KeyKP7,
d2input.KeyKP8: ebiten.KeyKP8,
d2input.KeyKP9: ebiten.KeyKP9,
d2input.KeyKPAdd: ebiten.KeyKPAdd,
d2input.KeyKPDecimal: ebiten.KeyKPDecimal,
d2input.KeyKPDivide: ebiten.KeyKPDivide,
d2input.KeyKPEnter: ebiten.KeyKPEnter,
d2input.KeyKPEqual: ebiten.KeyKPEqual,
d2input.KeyKPMultiply: ebiten.KeyKPMultiply,
d2input.KeyKPSubtract: ebiten.KeyKPSubtract,
d2input.KeyLeft: ebiten.KeyLeft,
d2input.KeyLeftBracket: ebiten.KeyLeftBracket,
d2input.KeyMenu: ebiten.KeyMenu,
d2input.KeyMinus: ebiten.KeyMinus,
d2input.KeyNumLock: ebiten.KeyNumLock,
d2input.KeyPageDown: ebiten.KeyPageDown,
d2input.KeyPageUp: ebiten.KeyPageUp,
d2input.KeyPause: ebiten.KeyPause,
d2input.KeyPeriod: ebiten.KeyPeriod,
d2input.KeyPrintScreen: ebiten.KeyPrintScreen,
d2input.KeyRight: ebiten.KeyRight,
d2input.KeyRightBracket: ebiten.KeyRightBracket,
d2input.KeyScrollLock: ebiten.KeyScrollLock,
d2input.KeySemicolon: ebiten.KeySemicolon,
d2input.KeySlash: ebiten.KeySlash,
d2input.KeySpace: ebiten.KeySpace,
d2input.KeyTab: ebiten.KeyTab,
d2input.KeyUp: ebiten.KeyUp,
d2input.KeyAlt: ebiten.KeyAlt,
d2input.KeyControl: ebiten.KeyControl,
d2input.KeyShift: ebiten.KeyShift,
}
mouseButtonToEbiten = map[d2input.MouseButton]ebiten.MouseButton{
d2input.MouseButtonLeft: ebiten.MouseButtonLeft,
d2input.MouseButtonMiddle: ebiten.MouseButtonMiddle,
d2input.MouseButtonRight: ebiten.MouseButtonRight,
}
)
// InputService provides an abstraction on ebiten to support handling input events
type InputService struct{}
func (is InputService) CursorPosition() (x int, y int) {
return ebiten.CursorPosition()
}
func (is InputService) InputChars() []rune {
return ebiten.InputChars()
}
func (is InputService) IsKeyPressed(key d2input.Key) bool {
return ebiten.IsKeyPressed(keyToEbiten[key])
}
func (is InputService) IsKeyJustPressed(key d2input.Key) bool {
return inpututil.IsKeyJustPressed(keyToEbiten[key])
}
func (is InputService) IsKeyJustReleased(key d2input.Key) bool {
return inpututil.IsKeyJustReleased(keyToEbiten[key])
}
func (is InputService) IsMouseButtonPressed(button d2input.MouseButton) bool {
return ebiten.IsMouseButtonPressed(mouseButtonToEbiten[button])
}
func (is InputService) IsMouseButtonJustPressed(button d2input.MouseButton) bool {
return inpututil.IsMouseButtonJustPressed(mouseButtonToEbiten[button])
}
func (is InputService) IsMouseButtonJustReleased(button d2input.MouseButton) bool {
return inpututil.IsMouseButtonJustReleased(mouseButtonToEbiten[button])
}
func (is InputService) KeyPressDuration(key d2input.Key) int {
return inpututil.KeyPressDuration(keyToEbiten[key])
}

View File

@ -2,9 +2,6 @@ package d2input
import (
"sort"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
type handlerEntry struct {
@ -26,9 +23,22 @@ func (lel handlerEntryList) Less(i, j int) bool {
return lel[i].priority > lel[j].priority
}
type InputService interface {
CursorPosition() (x int, y int)
InputChars() []rune
IsKeyPressed(key Key) bool
IsKeyJustPressed(key Key) bool
IsKeyJustReleased(key Key) bool
IsMouseButtonPressed(button MouseButton) bool
IsMouseButtonJustPressed(button MouseButton) bool
IsMouseButtonJustReleased(button MouseButton) bool
KeyPressDuration(key Key) int
}
type inputManager struct {
cursorX int
cursorY int
inputService InputService
cursorX int
cursorY int
buttonMod MouseButtonMod
keyMod KeyMod
@ -36,28 +46,28 @@ type inputManager struct {
entries handlerEntryList
}
func (im *inputManager) advance(elapsed float64) error {
cursorX, cursorY := ebiten.CursorPosition()
func (im *inputManager) advance(_ float64) error {
cursorX, cursorY := im.inputService.CursorPosition()
im.keyMod = 0
if ebiten.IsKeyPressed(ebiten.KeyAlt) {
if im.inputService.IsKeyPressed(KeyAlt) {
im.keyMod |= KeyModAlt
}
if ebiten.IsKeyPressed(ebiten.KeyControl) {
if im.inputService.IsKeyPressed(KeyControl) {
im.keyMod |= KeyModControl
}
if ebiten.IsKeyPressed(ebiten.KeyShift) {
if im.inputService.IsKeyPressed(KeyShift) {
im.keyMod |= KeyModShift
}
im.buttonMod = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
if im.inputService.IsMouseButtonPressed(MouseButtonLeft) {
im.buttonMod |= MouseButtonModLeft
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) {
if im.inputService.IsMouseButtonPressed(MouseButtonMiddle) {
im.buttonMod |= MouseButtonModMiddle
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
if im.inputService.IsMouseButtonPressed(MouseButtonRight) {
im.buttonMod |= MouseButtonModRight
}
@ -68,9 +78,9 @@ func (im *inputManager) advance(elapsed float64) error {
cursorY,
}
for key := ebiten.Key0; key < ebiten.KeyMax; key++ {
if inpututil.IsKeyJustPressed(key) {
event := KeyEvent{eventBase, Key(key)}
for key := keyMin; key < keyMax; key++ {
if im.inputService.IsKeyJustPressed(key) {
event := KeyEvent{HandlerEvent: eventBase, Key: key}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyDownHandler); ok {
return l.OnKeyDown(event)
@ -80,8 +90,8 @@ func (im *inputManager) advance(elapsed float64) error {
})
}
if ebiten.IsKeyPressed(key) {
event := KeyEvent{eventBase, Key(key)}
if im.inputService.IsKeyPressed(key) {
event := KeyEvent{HandlerEvent: eventBase, Key: key, Duration: im.inputService.KeyPressDuration(key)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyRepeatHandler); ok {
return l.OnKeyRepeat(event)
@ -91,8 +101,8 @@ func (im *inputManager) advance(elapsed float64) error {
})
}
if inpututil.IsKeyJustReleased(key) {
event := KeyEvent{eventBase, Key(key)}
if im.inputService.IsKeyJustReleased(key) {
event := KeyEvent{HandlerEvent: eventBase, Key: key}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyUpHandler); ok {
return l.OnKeyUp(event)
@ -103,7 +113,7 @@ func (im *inputManager) advance(elapsed float64) error {
}
}
if chars := ebiten.InputChars(); len(chars) > 0 {
if chars := im.inputService.InputChars(); len(chars) > 0 {
event := KeyCharsEvent{eventBase, chars}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyCharsHandler); ok {
@ -114,8 +124,8 @@ func (im *inputManager) advance(elapsed float64) error {
})
}
for button := ebiten.MouseButtonLeft; button < ebiten.MouseButtonMiddle; button++ {
if inpututil.IsMouseButtonJustPressed(button) {
for button := mouseButtonMin; button < mouseButtonMax; button++ {
if im.inputService.IsMouseButtonJustPressed(button) {
event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonDownHandler); ok {
@ -126,24 +136,21 @@ func (im *inputManager) advance(elapsed float64) error {
})
}
for button := ebiten.MouseButtonLeft; button < ebiten.MouseButtonMiddle; button++ {
if ebiten.IsMouseButtonPressed(button) {
if im.inputService.IsMouseButtonJustReleased(button) {
event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonRepeatHandler); ok {
return l.OnMouseButtonRepeat(event)
if l, ok := handler.(MouseButtonUpHandler); ok {
return l.OnMouseButtonUp(event)
}
return false
})
}
}
if inpututil.IsMouseButtonJustReleased(button) {
if im.inputService.IsMouseButtonPressed(button) {
event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonUpHandler); ok {
return l.OnMouseButtonUp(event)
if l, ok := handler.(MouseButtonRepeatHandler); ok {
return l.OnMouseButtonRepeat(event)
}
return false

138
d2core/d2input/key.go Normal file
View File

@ -0,0 +1,138 @@
package d2input
// Key is the physical key of keyboard input
type Key int
const (
Key0 Key = iota
Key1
Key2
Key3
Key4
Key5
Key6
Key7
Key8
Key9
KeyA
KeyB
KeyC
KeyD
KeyE
KeyF
KeyG
KeyH
KeyI
KeyJ
KeyK
KeyL
KeyM
KeyN
KeyO
KeyP
KeyQ
KeyR
KeyS
KeyT
KeyU
KeyV
KeyW
KeyX
KeyY
KeyZ
KeyApostrophe
KeyBackslash
KeyBackspace
KeyCapsLock
KeyComma
KeyDelete
KeyDown
KeyEnd
KeyEnter
KeyEqual
KeyEscape
KeyF1
KeyF2
KeyF3
KeyF4
KeyF5
KeyF6
KeyF7
KeyF8
KeyF9
KeyF10
KeyF11
KeyF12
KeyGraveAccent
KeyHome
KeyInsert
KeyKP0
KeyKP1
KeyKP2
KeyKP3
KeyKP4
KeyKP5
KeyKP6
KeyKP7
KeyKP8
KeyKP9
KeyKPAdd
KeyKPDecimal
KeyKPDivide
KeyKPEnter
KeyKPEqual
KeyKPMultiply
KeyKPSubtract
KeyLeft
KeyLeftBracket
KeyMenu
KeyMinus
KeyNumLock
KeyPageDown
KeyPageUp
KeyPause
KeyPeriod
KeyPrintScreen
KeyRight
KeyRightBracket
KeyScrollLock
KeySemicolon
KeySlash
KeySpace
KeyTab
KeyUp
KeyAlt
KeyControl
KeyShift
keyMin = Key0
keyMax = KeyShift
)
type KeyMod int
const (
KeyModAlt KeyMod = 1 << iota
KeyModControl
KeyModShift
)
// MouseButton is the physical button for mouse input
type MouseButton int
const (
MouseButtonLeft MouseButton = iota
MouseButtonMiddle
MouseButtonRight
mouseButtonMin = MouseButtonLeft
mouseButtonMax = MouseButtonRight
)
type MouseButtonMod int
const (
MouseButtonModLeft MouseButtonMod = 1 << iota
MouseButtonModMiddle
MouseButtonModRight
)

View File

@ -1,10 +1,12 @@
package d2ui
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/hajimehoshi/ebiten"
)
// CursorButton represents a mouse button
@ -17,122 +19,106 @@ const (
CursorButtonRight CursorButton = 2
)
var widgets []Widget
var cursorButtons CursorButton
var pressedIndex int
var CursorX int
var CursorY int
type UI struct {
widgets []Widget
cursorButtons CursorButton // TODO (carrelld) convert dependent code and remove
CursorX int // TODO (carrelld) convert dependent code and remove
CursorY int // TODO (carrelld) convert dependent code and remove
pressedWidget Widget
}
var singleton UI
var clickSfx d2audio.SoundEffect
var waitForLeftMouseUp bool
func Initialize() {
pressedIndex = -1
clickSfx, _ = d2audio.LoadSoundEffect(d2resource.SFXButtonClick)
waitForLeftMouseUp = false
sfx, err := d2audio.LoadSoundEffect(d2resource.SFXButtonClick)
if err != nil {
log.Fatalf("failed to initialize ui: %v", err)
}
clickSfx = sfx
d2input.BindHandler(&singleton)
}
// Reset resets the state of the UI manager. Typically called for new screens
func Reset() {
widgets = make([]Widget, 0)
pressedIndex = -1
waitForLeftMouseUp = true
singleton.widgets = nil
singleton.pressedWidget = nil
}
// AddWidget adds a widget to the UI manager
func AddWidget(widget Widget) {
widgets = append(widgets, widget)
d2input.BindHandler(widget)
singleton.widgets = append(singleton.widgets, widget)
}
func WaitForMouseRelease() {
waitForLeftMouseUp = true
func (u *UI) OnMouseButtonUp(event d2input.MouseEvent) bool {
singleton.CursorX, singleton.CursorY = event.X, event.Y
if event.Button == d2input.MouseButtonLeft {
singleton.cursorButtons |= CursorButtonLeft
// activate previously pressed widget if cursor is still hovering
w := singleton.pressedWidget
if w != nil && contains(w, singleton.CursorX, singleton.CursorY) && w.GetVisible() && w.GetEnabled() {
w.Activate()
}
// unpress all widgets that are pressed
for _, w := range singleton.widgets {
w.SetPressed(false)
}
}
return false
}
func (u *UI) OnMouseButtonDown(event d2input.MouseEvent) bool {
singleton.CursorX, singleton.CursorY = event.X, event.Y
if event.Button == d2input.MouseButtonLeft {
// find and press a widget on screen
singleton.pressedWidget = nil
for _, w := range singleton.widgets {
if contains(w, singleton.CursorX, singleton.CursorY) && w.GetVisible() && w.GetEnabled() {
w.SetPressed(true)
singleton.pressedWidget = w
clickSfx.Play()
break
}
}
}
if event.Button == d2input.MouseButtonRight {
singleton.cursorButtons |= CursorButtonRight
}
return false
}
// Render renders all of the UI elements
func Render(target d2render.Surface) {
for _, widget := range widgets {
for _, widget := range singleton.widgets {
if widget.GetVisible() {
widget.Render(target)
}
}
}
// contains determines whether a given x,y coordinate lands within a Widget
func contains(w Widget, x, y int) bool {
wx, wy := w.GetPosition()
ww, wh := w.GetSize()
return x >= wx && x <= wx+ww && y >= wy && y <= wy+wh
}
// Update updates all of the UI elements
func Advance(elapsed float64) {
for _, widget := range widgets {
for _, widget := range singleton.widgets {
if widget.GetVisible() {
widget.Advance(elapsed)
}
}
cursorButtons = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
if !waitForLeftMouseUp {
cursorButtons |= CursorButtonLeft
}
} else {
if waitForLeftMouseUp {
waitForLeftMouseUp = false
}
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
cursorButtons |= CursorButtonRight
}
CursorX, CursorY = ebiten.CursorPosition()
if CursorButtonPressed(CursorButtonLeft) {
found := false
for i, widget := range widgets {
if !widget.GetVisible() || !widget.GetEnabled() {
continue
}
wx, wy := widget.GetPosition()
ww, wh := widget.GetSize()
if CursorX >= wx && CursorX <= wx+ww && CursorY >= wy && CursorY <= wy+wh {
widget.SetPressed(true)
if pressedIndex == -1 {
found = true
pressedIndex = i
clickSfx.Play()
} else if pressedIndex > -1 && pressedIndex != i {
widgets[i].SetPressed(false)
} else {
found = true
}
} else {
widget.SetPressed(false)
}
}
if !found {
if pressedIndex > -1 {
widgets[pressedIndex].SetPressed(false)
} else {
pressedIndex = -2
}
}
} else {
if pressedIndex > -1 {
widget := widgets[pressedIndex]
wx, wy := widget.GetPosition()
ww, wh := widget.GetSize()
if CursorX >= wx && CursorX <= wx+ww && CursorY >= wy && CursorY <= wy+wh {
widget.Activate()
}
} else {
for _, widget := range widgets {
if !widget.GetVisible() || !widget.GetEnabled() {
continue
}
widget.SetPressed(false)
}
}
pressedIndex = -1
}
}
// CursorButtonPressed determines if the specified button has been pressed
func CursorButtonPressed(button CursorButton) bool {
return cursorButtons&button > 0
return singleton.cursorButtons&button > 0
}
func KeyPressed(key ebiten.Key) bool {
return ebiten.IsKeyPressed(key)
func CursorPosition() (x, y int) {
return singleton.CursorX, singleton.CursorY
}

View File

@ -4,7 +4,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/hajimehoshi/ebiten"
)
type Scrollbar struct {
@ -52,7 +51,7 @@ func (v Scrollbar) getBarPosition() int {
}
func (v *Scrollbar) Activate() {
_, my := ebiten.CursorPosition()
_, my := CursorPosition()
barPosition := v.getBarPosition()
if my <= v.y+barPosition+15 {
if v.currentOffset > 0 {
@ -65,6 +64,7 @@ func (v *Scrollbar) Activate() {
v.lastDirChange = 1
}
}
if v.onActivate != nil {
v.onActivate()
}

View File

@ -4,15 +4,15 @@ import (
"strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/hajimehoshi/ebiten/inpututil"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/hajimehoshi/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
// TextBox with cursor focus
var focusedTextBox *TextBox
// TextBox represents a text input box
type TextBox struct {
text string
@ -29,7 +29,7 @@ type TextBox struct {
func CreateTextbox() TextBox {
animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits)
bgSprite, _ := LoadSprite(animation)
result := TextBox{
tb := TextBox{
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
bgSprite: bgSprite,
textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
@ -37,29 +37,15 @@ func CreateTextbox() TextBox {
enabled: true,
visible: true,
}
result.lineBar.SetText("_")
return result
tb.lineBar.SetText("_")
return tb
}
func (v *TextBox) SetFilter(filter string) {
v.filter = filter
}
func repeatingKeyPressed(key ebiten.Key) bool {
const (
delay = 30
interval = 3
)
d := inpututil.KeyPressDuration(key)
if d == 1 {
return true
}
if d >= delay && (d-delay)%interval == 0 {
return true
}
return false
}
func (v *TextBox) Render(target d2render.Surface) {
if !v.visible {
return
@ -71,21 +57,47 @@ func (v *TextBox) Render(target d2render.Surface) {
}
}
func (v *TextBox) Advance(elapsed float64) {
if !v.visible || !v.enabled {
return
func (v *TextBox) OnKeyChars(event d2input.KeyCharsEvent) bool {
if !(focusedTextBox == v) || !v.visible || !v.enabled {
return false
}
newText := string(ebiten.InputChars())
newText := string(event.Chars)
if len(newText) > 0 {
v.text += newText
v.SetText(v.text)
return true
}
if repeatingKeyPressed(ebiten.KeyBackspace) {
return false
}
func (v *TextBox) OnKeyRepeat(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyBackspace && debounceEvents(event.Duration) {
if len(v.text) >= 1 {
v.text = v.text[:len(v.text)-1]
}
v.SetText(v.text)
}
return false
}
func debounceEvents(numFrames int) bool {
const (
delay = 30
interval = 3
)
if numFrames == 1 {
return true
}
if numFrames >= delay && (numFrames-delay)%interval == 0 {
return true
}
return false
}
func (v *TextBox) Advance(_ float64) {
if !v.visible || !v.enabled {
return
}
}
func (v *TextBox) Update() {
@ -164,5 +176,5 @@ func (v *TextBox) OnActivated(callback func()) {
}
func (v *TextBox) Activate() {
//no op
focusedTextBox = v
}

View File

@ -6,24 +6,20 @@ import (
"os"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
"github.com/hajimehoshi/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
)
type CharacterSelect struct {
@ -46,7 +42,6 @@ type CharacterSelect struct {
characterImage [8]*d2mapentity.Player
gameStates []*d2player.PlayerState
selectedCharacter int
mouseButtonPressed bool
showDeleteConfirmation bool
connectionType d2clientconnectiontype.ClientConnectionType
connectionHost string
@ -62,6 +57,7 @@ func CreateCharacterSelect(connectionType d2clientconnectiontype.ClientConnectio
func (v *CharacterSelect) OnLoad() error {
d2audio.PlayBGM(d2resource.BGMTitle)
d2input.BindHandler(v)
animation, _ := d2asset.LoadAnimation(d2resource.CharacterSelectionBackground, d2resource.PaletteSky)
v.background, _ = d2ui.LoadSprite(animation)
@ -222,32 +218,32 @@ func (v *CharacterSelect) moveSelectionBox() {
v.d2HeroTitle.SetText(v.gameStates[v.selectedCharacter].HeroName)
}
func (v *CharacterSelect) Advance(tickTime float64) error {
func (v *CharacterSelect) OnMouseButtonDown(event d2input.MouseEvent) bool {
if !v.showDeleteConfirmation {
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
if !v.mouseButtonPressed {
v.mouseButtonPressed = true
mx, my := ebiten.CursorPosition()
bw := 272
bh := 92
localMouseX := mx - 37
localMouseY := my - 86
if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 {
adjustY := localMouseY / bh
selectedIndex := adjustY * 2
if localMouseX > bw {
selectedIndex += 1
}
if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) {
v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex
v.moveSelectionBox()
}
if event.Button == d2input.MouseButtonLeft {
mx, my := event.X, event.Y
bw := 272
bh := 92
localMouseX := mx - 37
localMouseY := my - 86
if localMouseX > 0 && localMouseX < bw*2 && localMouseY >= 0 && localMouseY < bh*4 {
adjustY := localMouseY / bh
selectedIndex := adjustY * 2
if localMouseX > bw {
selectedIndex += 1
}
if (v.charScrollbar.GetCurrentOffset()*2)+selectedIndex < len(v.gameStates) {
v.selectedCharacter = (v.charScrollbar.GetCurrentOffset() * 2) + selectedIndex
v.moveSelectionBox()
}
}
} else {
v.mouseButtonPressed = false
return true
}
}
return false
}
func (v *CharacterSelect) Advance(tickTime float64) error {
for _, hero := range v.characterImage {
if hero != nil {
hero.AnimatedComposite.Advance(tickTime)

View File

@ -8,6 +8,8 @@ import (
"os/exec"
"runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
@ -238,6 +240,9 @@ func (v *MainMenu) OnLoad() error {
} else {
v.SetScreenMode(ScreenModeMainMenu)
}
d2input.BindHandler(v)
return nil
}
@ -343,23 +348,17 @@ func (v *MainMenu) Advance(tickTime float64) error {
v.diabloLogoRight.Advance(tickTime)
}
switch v.screenMode {
case ScreenModeTrademark:
if d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) {
if v.leftButtonHeld {
return nil
}
d2ui.WaitForMouseRelease()
v.SetScreenMode(ScreenModeMainMenu)
v.leftButtonHeld = true
} else {
v.leftButtonHeld = false
}
}
return nil
}
func (v *MainMenu) OnMouseButtonDown(event d2input.MouseEvent) bool {
if v.screenMode == ScreenModeTrademark && event.Button == d2input.MouseButtonLeft {
v.SetScreenMode(ScreenModeMainMenu)
return true
}
return false
}
func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.screenMode = screenMode
isMainMenu := screenMode == ScreenModeMainMenu
@ -379,6 +378,9 @@ func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.btnTcpIpHostGame.SetVisible(isTcpIp)
v.btnTcpIpJoinGame.SetVisible(isTcpIp)
v.tcpJoinGameEntry.SetVisible(isServerIp)
if isServerIp {
v.tcpJoinGameEntry.Activate()
}
v.btnServerIpOk.SetVisible(isServerIp)
v.btnServerIpCancel.SetVisible(isServerIp)
}

View File

@ -472,17 +472,10 @@ func (v *SelectHeroClass) Advance(tickTime float64) error {
canSelect = false
}
}
allIdle := true
for heroType, data := range v.heroRenderInfo {
if allIdle && data.Stance != d2enum.HeroStanceIdle {
allIdle = false
}
for heroType, _ := range v.heroRenderInfo {
v.updateHeroSelectionHover(heroType, canSelect)
}
if v.selectedHero != d2enum.HeroNone && allIdle {
v.selectedHero = d2enum.HeroNone
}
v.okButton.SetEnabled(len(v.heroNameTextbox.GetText()) >= 2)
v.okButton.SetEnabled(len(v.heroNameTextbox.GetText()) >= 2 && v.selectedHero != d2enum.HeroNone)
return nil
}
@ -509,12 +502,12 @@ func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect b
if renderInfo.Stance == d2enum.HeroStanceSelected {
return
}
mouseX := d2ui.CursorX
mouseY := d2ui.CursorY
mouseX, mouseY := d2ui.CursorPosition()
b := renderInfo.SelectionBounds
mouseHover := (mouseX >= b.Min.X) && (mouseX <= b.Min.X+b.Max.X) && (mouseY >= b.Min.Y) && (mouseY <= b.Min.Y+b.Max.Y)
if mouseHover && d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) {
v.heroNameTextbox.SetVisible(true)
v.heroNameTextbox.Activate()
v.okButton.SetVisible(true)
v.expansionCheckbox.SetVisible(true)
v.hardcoreCheckbox.SetVisible(true)

View File

@ -25,6 +25,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
ebiten_input "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten"
@ -113,6 +114,8 @@ func initialize() error {
config := d2config.Get()
d2resource.LanguageCode = config.Language
d2input.Initialize(ebiten_input.InputService{})
renderer, err := ebiten.CreateRenderer()
if err != nil {
return err