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 Gürkan Kaymak
Maxime "malavv" Lavigne Maxime "malavv" Lavigne
Ripolak Ripolak
dafe
* DIABLO2 LOGO * DIABLO2 LOGO
Jose Pardilla (th3-prophetman) Jose Pardilla (th3-prophetman)

View File

@ -2,8 +2,6 @@ package d2input
import ( import (
"errors" "errors"
"github.com/hajimehoshi/ebiten"
) )
var ( var (
@ -19,136 +17,6 @@ const (
PriorityHigh 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 { type HandlerEvent struct {
KeyMod KeyMod KeyMod KeyMod
ButtonMod MouseButtonMod ButtonMod MouseButtonMod
@ -159,6 +27,8 @@ type HandlerEvent struct {
type KeyEvent struct { type KeyEvent struct {
HandlerEvent HandlerEvent
Key Key Key Key
// Duration represents the number of frames this key has been pressed for
Duration int
} }
type KeyCharsEvent struct { type KeyCharsEvent struct {
@ -211,6 +81,12 @@ type MouseMoveHandler interface {
var singleton inputManager var singleton inputManager
func Initialize(inputService InputService) {
singleton = inputManager{
inputService: inputService,
}
}
func Advance(elapsed float64) error { func Advance(elapsed float64) error {
return singleton.advance(elapsed) 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 ( import (
"sort" "sort"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
) )
type handlerEntry struct { type handlerEntry struct {
@ -26,9 +23,22 @@ func (lel handlerEntryList) Less(i, j int) bool {
return lel[i].priority > lel[j].priority 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 { type inputManager struct {
cursorX int inputService InputService
cursorY int cursorX int
cursorY int
buttonMod MouseButtonMod buttonMod MouseButtonMod
keyMod KeyMod keyMod KeyMod
@ -36,28 +46,28 @@ type inputManager struct {
entries handlerEntryList entries handlerEntryList
} }
func (im *inputManager) advance(elapsed float64) error { func (im *inputManager) advance(_ float64) error {
cursorX, cursorY := ebiten.CursorPosition() cursorX, cursorY := im.inputService.CursorPosition()
im.keyMod = 0 im.keyMod = 0
if ebiten.IsKeyPressed(ebiten.KeyAlt) { if im.inputService.IsKeyPressed(KeyAlt) {
im.keyMod |= KeyModAlt im.keyMod |= KeyModAlt
} }
if ebiten.IsKeyPressed(ebiten.KeyControl) { if im.inputService.IsKeyPressed(KeyControl) {
im.keyMod |= KeyModControl im.keyMod |= KeyModControl
} }
if ebiten.IsKeyPressed(ebiten.KeyShift) { if im.inputService.IsKeyPressed(KeyShift) {
im.keyMod |= KeyModShift im.keyMod |= KeyModShift
} }
im.buttonMod = 0 im.buttonMod = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if im.inputService.IsMouseButtonPressed(MouseButtonLeft) {
im.buttonMod |= MouseButtonModLeft im.buttonMod |= MouseButtonModLeft
} }
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) { if im.inputService.IsMouseButtonPressed(MouseButtonMiddle) {
im.buttonMod |= MouseButtonModMiddle im.buttonMod |= MouseButtonModMiddle
} }
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) { if im.inputService.IsMouseButtonPressed(MouseButtonRight) {
im.buttonMod |= MouseButtonModRight im.buttonMod |= MouseButtonModRight
} }
@ -68,9 +78,9 @@ func (im *inputManager) advance(elapsed float64) error {
cursorY, cursorY,
} }
for key := ebiten.Key0; key < ebiten.KeyMax; key++ { for key := keyMin; key < keyMax; key++ {
if inpututil.IsKeyJustPressed(key) { if im.inputService.IsKeyJustPressed(key) {
event := KeyEvent{eventBase, Key(key)} event := KeyEvent{HandlerEvent: eventBase, Key: key}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyDownHandler); ok { if l, ok := handler.(KeyDownHandler); ok {
return l.OnKeyDown(event) return l.OnKeyDown(event)
@ -80,8 +90,8 @@ func (im *inputManager) advance(elapsed float64) error {
}) })
} }
if ebiten.IsKeyPressed(key) { if im.inputService.IsKeyPressed(key) {
event := KeyEvent{eventBase, Key(key)} event := KeyEvent{HandlerEvent: eventBase, Key: key, Duration: im.inputService.KeyPressDuration(key)}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyRepeatHandler); ok { if l, ok := handler.(KeyRepeatHandler); ok {
return l.OnKeyRepeat(event) return l.OnKeyRepeat(event)
@ -91,8 +101,8 @@ func (im *inputManager) advance(elapsed float64) error {
}) })
} }
if inpututil.IsKeyJustReleased(key) { if im.inputService.IsKeyJustReleased(key) {
event := KeyEvent{eventBase, Key(key)} event := KeyEvent{HandlerEvent: eventBase, Key: key}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyUpHandler); ok { if l, ok := handler.(KeyUpHandler); ok {
return l.OnKeyUp(event) 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} event := KeyCharsEvent{eventBase, chars}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyCharsHandler); ok { 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++ { for button := mouseButtonMin; button < mouseButtonMax; button++ {
if inpututil.IsMouseButtonJustPressed(button) { if im.inputService.IsMouseButtonJustPressed(button) {
event := MouseEvent{eventBase, MouseButton(button)} event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonDownHandler); ok { 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 im.inputService.IsMouseButtonJustReleased(button) {
if ebiten.IsMouseButtonPressed(button) {
event := MouseEvent{eventBase, MouseButton(button)} event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonRepeatHandler); ok { if l, ok := handler.(MouseButtonUpHandler); ok {
return l.OnMouseButtonRepeat(event) return l.OnMouseButtonUp(event)
} }
return false return false
}) })
} }
} if im.inputService.IsMouseButtonPressed(button) {
if inpututil.IsMouseButtonJustReleased(button) {
event := MouseEvent{eventBase, MouseButton(button)} event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool { im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonUpHandler); ok { if l, ok := handler.(MouseButtonRepeatHandler); ok {
return l.OnMouseButtonUp(event) return l.OnMouseButtonRepeat(event)
} }
return false 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 package d2ui
import ( import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/hajimehoshi/ebiten"
) )
// CursorButton represents a mouse button // CursorButton represents a mouse button
@ -17,122 +19,106 @@ const (
CursorButtonRight CursorButton = 2 CursorButtonRight CursorButton = 2
) )
var widgets []Widget type UI struct {
var cursorButtons CursorButton widgets []Widget
var pressedIndex int cursorButtons CursorButton // TODO (carrelld) convert dependent code and remove
var CursorX int CursorX int // TODO (carrelld) convert dependent code and remove
var CursorY int CursorY int // TODO (carrelld) convert dependent code and remove
pressedWidget Widget
}
var singleton UI
var clickSfx d2audio.SoundEffect var clickSfx d2audio.SoundEffect
var waitForLeftMouseUp bool
func Initialize() { func Initialize() {
pressedIndex = -1 sfx, err := d2audio.LoadSoundEffect(d2resource.SFXButtonClick)
clickSfx, _ = d2audio.LoadSoundEffect(d2resource.SFXButtonClick) if err != nil {
waitForLeftMouseUp = false 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 // Reset resets the state of the UI manager. Typically called for new screens
func Reset() { func Reset() {
widgets = make([]Widget, 0) singleton.widgets = nil
pressedIndex = -1 singleton.pressedWidget = nil
waitForLeftMouseUp = true
} }
// AddWidget adds a widget to the UI manager // AddWidget adds a widget to the UI manager
func AddWidget(widget Widget) { func AddWidget(widget Widget) {
widgets = append(widgets, widget) d2input.BindHandler(widget)
singleton.widgets = append(singleton.widgets, widget)
} }
func WaitForMouseRelease() { func (u *UI) OnMouseButtonUp(event d2input.MouseEvent) bool {
waitForLeftMouseUp = true 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 // Render renders all of the UI elements
func Render(target d2render.Surface) { func Render(target d2render.Surface) {
for _, widget := range widgets { for _, widget := range singleton.widgets {
if widget.GetVisible() { if widget.GetVisible() {
widget.Render(target) 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 // Update updates all of the UI elements
func Advance(elapsed float64) { func Advance(elapsed float64) {
for _, widget := range widgets { for _, widget := range singleton.widgets {
if widget.GetVisible() { if widget.GetVisible() {
widget.Advance(elapsed) 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 // CursorButtonPressed determines if the specified button has been pressed
func CursorButtonPressed(button CursorButton) bool { func CursorButtonPressed(button CursorButton) bool {
return cursorButtons&button > 0 return singleton.cursorButtons&button > 0
} }
func KeyPressed(key ebiten.Key) bool { func CursorPosition() (x, y int) {
return ebiten.IsKeyPressed(key) return singleton.CursorX, singleton.CursorY
} }

View File

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

View File

@ -4,15 +4,15 @@ import (
"strings" "strings"
"time" "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/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 // TextBox represents a text input box
type TextBox struct { type TextBox struct {
text string text string
@ -29,7 +29,7 @@ type TextBox struct {
func CreateTextbox() TextBox { func CreateTextbox() TextBox {
animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits)
bgSprite, _ := LoadSprite(animation) bgSprite, _ := LoadSprite(animation)
result := TextBox{ tb := TextBox{
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
bgSprite: bgSprite, bgSprite: bgSprite,
textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
@ -37,29 +37,15 @@ func CreateTextbox() TextBox {
enabled: true, enabled: true,
visible: true, visible: true,
} }
result.lineBar.SetText("_") tb.lineBar.SetText("_")
return result
return tb
} }
func (v *TextBox) SetFilter(filter string) { func (v *TextBox) SetFilter(filter string) {
v.filter = filter 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) { func (v *TextBox) Render(target d2render.Surface) {
if !v.visible { if !v.visible {
return return
@ -71,21 +57,47 @@ func (v *TextBox) Render(target d2render.Surface) {
} }
} }
func (v *TextBox) Advance(elapsed float64) { func (v *TextBox) OnKeyChars(event d2input.KeyCharsEvent) bool {
if !v.visible || !v.enabled { if !(focusedTextBox == v) || !v.visible || !v.enabled {
return return false
} }
newText := string(ebiten.InputChars()) newText := string(event.Chars)
if len(newText) > 0 { if len(newText) > 0 {
v.text += newText v.text += newText
v.SetText(v.text) 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 { if len(v.text) >= 1 {
v.text = v.text[:len(v.text)-1] v.text = v.text[:len(v.text)-1]
} }
v.SetText(v.text) 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() { func (v *TextBox) Update() {
@ -164,5 +176,5 @@ func (v *TextBox) OnActivated(callback func()) {
} }
func (v *TextBox) Activate() { func (v *TextBox) Activate() {
//no op focusedTextBox = v
} }

View File

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

View File

@ -8,6 +8,8 @@ import (
"os/exec" "os/exec"
"runtime" "runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
@ -238,6 +240,9 @@ func (v *MainMenu) OnLoad() error {
} else { } else {
v.SetScreenMode(ScreenModeMainMenu) v.SetScreenMode(ScreenModeMainMenu)
} }
d2input.BindHandler(v)
return nil return nil
} }
@ -343,23 +348,17 @@ func (v *MainMenu) Advance(tickTime float64) error {
v.diabloLogoRight.Advance(tickTime) 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 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) { func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.screenMode = screenMode v.screenMode = screenMode
isMainMenu := screenMode == ScreenModeMainMenu isMainMenu := screenMode == ScreenModeMainMenu
@ -379,6 +378,9 @@ func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.btnTcpIpHostGame.SetVisible(isTcpIp) v.btnTcpIpHostGame.SetVisible(isTcpIp)
v.btnTcpIpJoinGame.SetVisible(isTcpIp) v.btnTcpIpJoinGame.SetVisible(isTcpIp)
v.tcpJoinGameEntry.SetVisible(isServerIp) v.tcpJoinGameEntry.SetVisible(isServerIp)
if isServerIp {
v.tcpJoinGameEntry.Activate()
}
v.btnServerIpOk.SetVisible(isServerIp) v.btnServerIpOk.SetVisible(isServerIp)
v.btnServerIpCancel.SetVisible(isServerIp) v.btnServerIpCancel.SetVisible(isServerIp)
} }

View File

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

View File

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