1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-04 23:10:42 +00:00

Event-based input manager with priority system (#274)

This commit is contained in:
Alex Yatskov 2020-01-25 20:28:21 -08:00 committed by Tim Sarbin
parent 1653ffc362
commit 8de0cde818
9 changed files with 737 additions and 223 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
@ -80,11 +81,13 @@ func (v *Game) Load() []func() {
func() {
v.gameControls = d2player.NewGameControls(v.hero, v.mapEngine)
v.gameControls.Load()
d2input.BindHandler(v.gameControls)
},
}
}
func (v *Game) Unload() {
d2input.UnbindHandler(v.gameControls)
}
func (v Game) Render(screen *d2surface.Surface) {
@ -98,6 +101,4 @@ func (v *Game) Advance(tickTime float64) {
rx, ry := v.mapEngine.WorldToOrtho(v.hero.AnimatedEntity.LocationX/5, v.hero.AnimatedEntity.LocationY/5)
v.mapEngine.MoveCameraTo(rx, ry)
v.gameControls.Update(tickTime)
}

View File

@ -5,6 +5,7 @@ import (
"os"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
@ -13,8 +14,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
type RegionSpec struct {
@ -145,7 +144,7 @@ func (v *MapEngineTest) LoadRegionByIndex(n int, levelPreset, fileIndex int) {
func (v *MapEngineTest) Load() []func() {
// TODO: Game seed comes from the game state object
d2input.BindHandler(v)
v.soundManager.PlayBGM("")
return []func(){
func() {
@ -156,7 +155,7 @@ func (v *MapEngineTest) Load() []func() {
}
func (v *MapEngineTest) Unload() {
d2input.UnbindHandler(v)
}
func (v *MapEngineTest) Render(screen *d2surface.Surface) {
@ -208,77 +207,85 @@ func (v *MapEngineTest) Render(screen *d2surface.Surface) {
func (v *MapEngineTest) Advance(tickTime float64) {
v.mapEngine.Advance(tickTime)
}
ctrlPressed := v.uiManager.KeyPressed(ebiten.KeyControl)
shiftPressed := v.uiManager.KeyPressed(ebiten.KeyShift)
var moveSpeed float64 = 800
if v.uiManager.KeyPressed(ebiten.KeyShift) {
moveSpeed = 200
func (met *MapEngineTest) OnKeyRepeat(event d2input.KeyEvent) bool {
var moveSpeed float64 = 8
if event.KeyMod == d2input.KeyModShift {
moveSpeed *= 2
}
if v.uiManager.KeyPressed(ebiten.KeyDown) {
v.mapEngine.MoveCameraBy(0, moveSpeed*tickTime)
}
if v.uiManager.KeyPressed(ebiten.KeyUp) {
v.mapEngine.MoveCameraBy(0, -moveSpeed*tickTime)
}
if v.uiManager.KeyPressed(ebiten.KeyLeft) {
v.mapEngine.MoveCameraBy(-moveSpeed*tickTime, 0)
}
if v.uiManager.KeyPressed(ebiten.KeyRight) {
v.mapEngine.MoveCameraBy(moveSpeed*tickTime, 0)
if event.Key == d2input.KeyDown {
met.mapEngine.MoveCameraBy(0, moveSpeed)
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyF7) {
if v.debugVisLevel < 2 {
v.debugVisLevel++
if event.Key == d2input.KeyUp {
met.mapEngine.MoveCameraBy(0, -moveSpeed)
return true
}
if event.Key == d2input.KeyRight {
met.mapEngine.MoveCameraBy(moveSpeed, 0)
return true
}
if event.Key == d2input.KeyLeft {
met.mapEngine.MoveCameraBy(-moveSpeed, 0)
return true
}
return false
}
func (met *MapEngineTest) OnKeyDown(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyEscape {
os.Exit(0)
return true
}
if event.Key == d2input.KeyN {
if event.KeyMod == d2input.KeyModControl {
met.fileIndex = increment(met.fileIndex, 0, met.filesCount-1)
met.sceneProvider.SetNextScene(met)
} else if event.KeyMod == d2input.KeyModShift {
met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
met.sceneProvider.SetNextScene(met)
} else {
v.debugVisLevel = 0
met.currentRegion = increment(met.currentRegion, 0, len(regions))
met.sceneProvider.SetNextScene(met)
}
v.mapEngine.SetDebugVisLevel(v.debugVisLevel)
return true
}
if v.uiManager.KeyPressed(ebiten.KeyEscape) {
os.Exit(0)
if event.Key == d2input.KeyP {
if event.KeyMod == d2input.KeyModControl {
met.fileIndex = decrement(met.fileIndex, 0, met.filesCount-1)
met.sceneProvider.SetNextScene(met)
} else if event.KeyMod == d2input.KeyModShift {
met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
met.sceneProvider.SetNextScene(met)
} else {
met.currentRegion = decrement(met.currentRegion, 0, len(regions))
met.sceneProvider.SetNextScene(met)
}
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyN) && ctrlPressed {
v.fileIndex = increment(v.fileIndex, 0, v.filesCount-1)
v.sceneProvider.SetNextScene(v)
return
if event.Key == d2input.KeyF7 {
if met.debugVisLevel < 2 {
met.debugVisLevel++
} else {
met.debugVisLevel = 0
}
met.mapEngine.SetDebugVisLevel(met.debugVisLevel)
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyP) && ctrlPressed {
v.fileIndex = decrement(v.fileIndex, 0, v.filesCount-1)
v.sceneProvider.SetNextScene(v)
return
}
if inpututil.IsKeyJustPressed(ebiten.KeyN) && shiftPressed {
v.levelPreset = increment(v.levelPreset, v.regionSpec.startPresetIndex, v.regionSpec.endPresetIndex)
v.sceneProvider.SetNextScene(v)
return
}
if inpututil.IsKeyJustPressed(ebiten.KeyP) && shiftPressed {
v.levelPreset = decrement(v.levelPreset, v.regionSpec.startPresetIndex, v.regionSpec.endPresetIndex)
v.sceneProvider.SetNextScene(v)
return
}
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
v.currentRegion = increment(v.currentRegion, 0, len(regions))
v.sceneProvider.SetNextScene(v)
return
}
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
v.currentRegion = decrement(v.currentRegion, 0, len(regions))
v.sceneProvider.SetNextScene(v)
return
}
return false
}
func increment(v, min, max int) int {

View File

@ -11,6 +11,7 @@ import (
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2term"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
@ -28,7 +29,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
// Engine is the core OpenDiablo2 engine
@ -44,7 +44,6 @@ type Engine struct {
UIManager *d2ui.Manager // The UI manager
SoundManager *d2audio.Manager // The sound manager
nextScene d2coreinterface.Scene // The next scene to be loaded at the end of the game loop
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
lastTime float64 // Last time we updated the scene
showFPS bool
timeScale float64
@ -96,6 +95,8 @@ func CreateEngine() *Engine {
}
})
d2input.BindHandler(result)
return result
}
@ -140,25 +141,27 @@ func (v *Engine) updateScene() {
v.ResetLoading()
}
func (e *Engine) OnKeyDown(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyEnter && event.KeyMod == d2input.KeyModAlt {
ebiten.SetFullscreen(!ebiten.IsFullscreen())
return true
}
if event.Key == d2input.KeyF6 {
e.showFPS = !e.showFPS
return true
}
if event.Key == d2input.KeyF8 {
ebiten.SetVsyncEnabled(!ebiten.IsVsyncEnabled())
return true
}
return false
}
// Advance updates the internal state of the engine
func (v *Engine) Advance() {
if ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyEnter) {
if !v.fullscreenKey {
ebiten.SetFullscreen(!ebiten.IsFullscreen())
}
v.fullscreenKey = true
} else {
v.fullscreenKey = false
}
if inpututil.IsKeyJustPressed(ebiten.KeyF6) {
v.showFPS = !v.showFPS
}
if inpututil.IsKeyJustPressed(ebiten.KeyF8) {
ebiten.SetVsyncEnabled(!ebiten.IsVsyncEnabled())
}
v.updateScene()
if v.CurrentScene == nil {
log.Fatal("no scene loaded")
@ -174,7 +177,9 @@ func (v *Engine) Advance() {
v.CurrentScene.Advance(deltaTime)
v.UIManager.Advance(deltaTime)
d2term.Advance(deltaTime)
d2input.Advance(deltaTime)
}
// Draw draws the game

250
d2input/d2input.go Normal file
View File

@ -0,0 +1,250 @@
package d2input
import (
"errors"
"github.com/hajimehoshi/ebiten"
)
var (
ErrHasInit error = errors.New("input system is already initialized")
ErrNotInit error = errors.New("input system is not initialized")
ErrHasReg error = errors.New("input system already has provided handler")
ErrNotReg error = errors.New("input system does not have provided handler")
)
type Priority int
const (
PriorityLow Priority = iota
PriorityDefault
PriorityHigh
)
type Key int
const (
Key0 Key = Key(ebiten.Key0)
Key1 Key = Key(ebiten.Key1)
Key2 Key = Key(ebiten.Key2)
Key3 Key = Key(ebiten.Key3)
Key4 Key = Key(ebiten.Key4)
Key5 Key = Key(ebiten.Key5)
Key6 Key = Key(ebiten.Key6)
Key7 Key = Key(ebiten.Key7)
Key8 Key = Key(ebiten.Key8)
Key9 Key = Key(ebiten.Key9)
KeyA Key = Key(ebiten.KeyA)
KeyB Key = Key(ebiten.KeyB)
KeyC Key = Key(ebiten.KeyC)
KeyD Key = Key(ebiten.KeyD)
KeyE Key = Key(ebiten.KeyE)
KeyF Key = Key(ebiten.KeyF)
KeyG Key = Key(ebiten.KeyG)
KeyH Key = Key(ebiten.KeyH)
KeyI Key = Key(ebiten.KeyI)
KeyJ Key = Key(ebiten.KeyJ)
KeyK Key = Key(ebiten.KeyK)
KeyL Key = Key(ebiten.KeyL)
KeyM Key = Key(ebiten.KeyM)
KeyN Key = Key(ebiten.KeyN)
KeyO Key = Key(ebiten.KeyO)
KeyP Key = Key(ebiten.KeyP)
KeyQ Key = Key(ebiten.KeyQ)
KeyR Key = Key(ebiten.KeyR)
KeyS Key = Key(ebiten.KeyS)
KeyT Key = Key(ebiten.KeyT)
KeyU Key = Key(ebiten.KeyU)
KeyV Key = Key(ebiten.KeyV)
KeyW Key = Key(ebiten.KeyW)
KeyX Key = Key(ebiten.KeyX)
KeyY Key = Key(ebiten.KeyY)
KeyZ Key = Key(ebiten.KeyZ)
KeyApostrophe Key = Key(ebiten.KeyApostrophe)
KeyBackslash Key = Key(ebiten.KeyBackslash)
KeyBackspace Key = Key(ebiten.KeyBackspace)
KeyCapsLock Key = Key(ebiten.KeyCapsLock)
KeyComma Key = Key(ebiten.KeyComma)
KeyDelete Key = Key(ebiten.KeyDelete)
KeyDown Key = Key(ebiten.KeyDown)
KeyEnd Key = Key(ebiten.KeyEnd)
KeyEnter Key = Key(ebiten.KeyEnter)
KeyEqual Key = Key(ebiten.KeyEqual)
KeyEscape Key = Key(ebiten.KeyEscape)
KeyF1 Key = Key(ebiten.KeyF1)
KeyF2 Key = Key(ebiten.KeyF2)
KeyF3 Key = Key(ebiten.KeyF3)
KeyF4 Key = Key(ebiten.KeyF4)
KeyF5 Key = Key(ebiten.KeyF5)
KeyF6 Key = Key(ebiten.KeyF6)
KeyF7 Key = Key(ebiten.KeyF7)
KeyF8 Key = Key(ebiten.KeyF8)
KeyF9 Key = Key(ebiten.KeyF9)
KeyF10 Key = Key(ebiten.KeyF10)
KeyF11 Key = Key(ebiten.KeyF11)
KeyF12 Key = Key(ebiten.KeyF12)
KeyGraveAccent Key = Key(ebiten.KeyGraveAccent)
KeyHome Key = Key(ebiten.KeyHome)
KeyInsert Key = Key(ebiten.KeyInsert)
KeyKP0 Key = Key(ebiten.KeyKP0)
KeyKP1 Key = Key(ebiten.KeyKP1)
KeyKP2 Key = Key(ebiten.KeyKP2)
KeyKP3 Key = Key(ebiten.KeyKP3)
KeyKP4 Key = Key(ebiten.KeyKP4)
KeyKP5 Key = Key(ebiten.KeyKP5)
KeyKP6 Key = Key(ebiten.KeyKP6)
KeyKP7 Key = Key(ebiten.KeyKP7)
KeyKP8 Key = Key(ebiten.KeyKP8)
KeyKP9 Key = Key(ebiten.KeyKP9)
KeyKPAdd Key = Key(ebiten.KeyKPAdd)
KeyKPDecimal Key = Key(ebiten.KeyKPDecimal)
KeyKPDivide Key = Key(ebiten.KeyKPDivide)
KeyKPEnter Key = Key(ebiten.KeyKPEnter)
KeyKPEqual Key = Key(ebiten.KeyKPEqual)
KeyKPMultiply Key = Key(ebiten.KeyKPMultiply)
KeyKPSubtract Key = Key(ebiten.KeyKPSubtract)
KeyLeft Key = Key(ebiten.KeyLeft)
KeyLeftBracket Key = Key(ebiten.KeyLeftBracket)
KeyMenu Key = Key(ebiten.KeyMenu)
KeyMinus Key = Key(ebiten.KeyMinus)
KeyNumLock Key = Key(ebiten.KeyNumLock)
KeyPageDown Key = Key(ebiten.KeyPageDown)
KeyPageUp Key = Key(ebiten.KeyPageUp)
KeyPause Key = Key(ebiten.KeyPause)
KeyPeriod Key = Key(ebiten.KeyPeriod)
KeyPrintScreen Key = Key(ebiten.KeyPrintScreen)
KeyRight Key = Key(ebiten.KeyRight)
KeyRightBracket Key = Key(ebiten.KeyRightBracket)
KeyScrollLock Key = Key(ebiten.KeyScrollLock)
KeySemicolon Key = Key(ebiten.KeySemicolon)
KeySlash Key = Key(ebiten.KeySlash)
KeySpace Key = Key(ebiten.KeySpace)
KeyTab Key = Key(ebiten.KeyTab)
KeyUp Key = Key(ebiten.KeyUp)
KeyAlt Key = Key(ebiten.KeyAlt)
KeyControl Key = Key(ebiten.KeyControl)
KeyShift Key = Key(ebiten.KeyShift)
)
type KeyMod int
const (
KeyModAlt = 1 << iota
KeyModControl
KeyModShift
)
type MouseButton int
const (
MouseButtonLeft MouseButton = MouseButton(ebiten.MouseButtonLeft)
MouseButtonMiddle MouseButton = MouseButton(ebiten.MouseButtonMiddle)
MouseButtonRight MouseButton = MouseButton(ebiten.MouseButtonRight)
)
type MouseButtonMod int
const (
MouseButtonModLeft MouseButtonMod = 1 << iota
MouseButtonModMiddle
MouseButtonModRight
)
type HandlerEvent struct {
KeyMod KeyMod
ButtonMod MouseButtonMod
X int
Y int
}
type KeyEvent struct {
HandlerEvent
Key Key
}
type KeyCharsEvent struct {
HandlerEvent
Chars []rune
}
type MouseEvent struct {
HandlerEvent
Button MouseButton
}
type MouseMoveEvent struct {
HandlerEvent
}
type Handler interface{}
type KeyDownHandler interface {
OnKeyDown(event KeyEvent) bool
}
type KeyRepeatHandler interface {
OnKeyRepeat(event KeyEvent) bool
}
type KeyUpHandler interface {
OnKeyUp(event KeyEvent) bool
}
type KeyCharsHandler interface {
OnKeyChars(event KeyCharsEvent) bool
}
type MouseButtonDownHandler interface {
OnMouseButtonDown(event MouseEvent) bool
}
type MouseButtonUpHandler interface {
OnMouseButtonUp(event MouseEvent) bool
}
type MouseMoveHandler interface {
OnMouseMove(event MouseMoveEvent) bool
}
var singleton *inputManager
func Initialize() error {
if singleton != nil {
return ErrHasInit
}
singleton = &inputManager{}
return nil
}
func Shutdown() {
singleton = nil
}
func Advance(elapsed float64) error {
if singleton == nil {
return ErrNotInit
}
return singleton.advance(elapsed)
}
func BindHandlerWithPriority(handler Handler, priority Priority) error {
if singleton == nil {
return ErrNotInit
}
return singleton.bindHandler(handler, priority)
}
func BindHandler(handler Handler) error {
return BindHandlerWithPriority(handler, PriorityDefault)
}
func UnbindHandler(handler Handler) error {
if singleton == nil {
return ErrNotInit
}
return singleton.unbindHandler(handler)
}

197
d2input/input_manager.go Normal file
View File

@ -0,0 +1,197 @@
package d2input
import (
"sort"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
type handlerEntry struct {
handler Handler
priority Priority
}
type handlerEntryList []handlerEntry
func (lel handlerEntryList) Len() int {
return len(lel)
}
func (lel handlerEntryList) Swap(i, j int) {
lel[i], lel[j] = lel[j], lel[i]
}
func (lel handlerEntryList) Less(i, j int) bool {
return lel[i].priority > lel[j].priority
}
type inputManager struct {
cursorX int
cursorY int
buttonMod MouseButtonMod
keyMod KeyMod
entries handlerEntryList
}
func (im *inputManager) advance(elapsed float64) error {
cursorX, cursorY := ebiten.CursorPosition()
im.keyMod = 0
if ebiten.IsKeyPressed(ebiten.KeyAlt) {
im.keyMod |= KeyModAlt
}
if ebiten.IsKeyPressed(ebiten.KeyControl) {
im.keyMod |= KeyModControl
}
if ebiten.IsKeyPressed(ebiten.KeyShift) {
im.keyMod |= KeyModShift
}
im.buttonMod = 0
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
im.buttonMod |= MouseButtonModLeft
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonMiddle) {
im.buttonMod |= MouseButtonModMiddle
}
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonRight) {
im.buttonMod |= MouseButtonModRight
}
eventBase := HandlerEvent{
im.keyMod,
im.buttonMod,
cursorX,
cursorY,
}
for key := ebiten.Key0; key < ebiten.KeyMax; key++ {
if inpututil.IsKeyJustPressed(key) {
event := KeyEvent{eventBase, Key(key)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyDownHandler); ok {
return l.OnKeyDown(event)
}
return false
})
}
if ebiten.IsKeyPressed(key) {
event := KeyEvent{eventBase, Key(key)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyRepeatHandler); ok {
return l.OnKeyRepeat(event)
}
return false
})
}
if inpututil.IsKeyJustReleased(key) {
event := KeyEvent{eventBase, Key(key)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyUpHandler); ok {
return l.OnKeyUp(event)
}
return false
})
}
}
if chars := ebiten.InputChars(); len(chars) > 0 {
event := KeyCharsEvent{eventBase, chars}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(KeyCharsHandler); ok {
l.OnKeyChars(event)
}
return false
})
}
for button := ebiten.MouseButtonLeft; button < ebiten.MouseButtonMiddle; button++ {
if inpututil.IsMouseButtonJustPressed(button) {
event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonDownHandler); ok {
return l.OnMouseButtonDown(event)
}
return false
})
}
if inpututil.IsMouseButtonJustReleased(button) {
event := MouseEvent{eventBase, MouseButton(button)}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseButtonUpHandler); ok {
return l.OnMouseButtonUp(event)
}
return false
})
}
}
if im.cursorX != cursorX || im.cursorY != cursorY {
event := MouseMoveEvent{eventBase}
im.propagate(func(handler Handler) bool {
if l, ok := handler.(MouseMoveHandler); ok {
return l.OnMouseMove(event)
}
return false
})
im.cursorX, im.cursorY = cursorX, cursorY
}
return nil
}
func (im *inputManager) bindHandler(handler Handler, priority Priority) error {
for _, entry := range im.entries {
if entry.handler == handler {
return ErrHasReg
}
}
im.entries = append(im.entries, handlerEntry{handler, priority})
sort.Sort(im.entries)
return nil
}
func (im *inputManager) unbindHandler(handler Handler) error {
for i, entry := range im.entries {
if entry.handler == handler {
copy(im.entries[i:], im.entries[i+1:])
im.entries = im.entries[:len(im.entries)-1]
return nil
}
}
return ErrNotReg
}
func (im *inputManager) propagate(callback func(Handler) bool) {
var priority Priority
var handled bool
for _, entry := range im.entries {
if priority > entry.priority && handled {
break
}
if callback(entry.handler) {
handled = true
}
priority = entry.priority
}
}

View File

@ -3,11 +3,10 @@ package d2player
import (
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
type Panel interface {
@ -37,41 +36,23 @@ func NewGameControls(hero *d2core.Hero, mapEngine *d2mapengine.MapEngine) *GameC
}
}
func (g *GameControls) Update(tickTime float64) {
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
px, py := g.mapEngine.ScreenToWorld(ebiten.CursorPosition())
g.hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
}
arrowDistance := 1.0
moveX := 0.0
moveY := 0.0
if ebiten.IsKeyPressed(ebiten.KeyW) || ebiten.IsKeyPressed(ebiten.KeyUp) {
moveY -= arrowDistance
moveX -= arrowDistance
}
if ebiten.IsKeyPressed(ebiten.KeyS) || ebiten.IsKeyPressed(ebiten.KeyDown) {
moveY += arrowDistance
moveX += arrowDistance
}
if ebiten.IsKeyPressed(ebiten.KeyA) || ebiten.IsKeyPressed(ebiten.KeyLeft) {
moveY += arrowDistance
moveX -= arrowDistance
}
if ebiten.IsKeyPressed(ebiten.KeyD) || ebiten.IsKeyPressed(ebiten.KeyRight) {
moveY -= arrowDistance
moveX += arrowDistance
}
if moveY != 0 || moveX != 0 {
g.hero.AnimatedEntity.SetTarget(g.hero.AnimatedEntity.LocationX+moveX, g.hero.AnimatedEntity.LocationY+moveY, 1)
}
if inpututil.IsKeyJustPressed(ebiten.KeyI) {
func (g *GameControls) OnKeyDown(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyI {
g.inventory.Toggle()
return true
}
return false
}
func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
if event.Button == d2input.MouseButtonLeft {
px, py := g.mapEngine.ScreenToWorld(event.X, event.Y)
g.hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
return true
}
return false
}
func (g *GameControls) Load() {

114
d2term/d2term.go Normal file
View File

@ -0,0 +1,114 @@
package d2term
import (
"errors"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
)
var (
ErrHasInit error = errors.New("terminal system is already initialized")
ErrNotInit error = errors.New("terminal system is not initialized")
)
var singleton *terminal
func Initialize() error {
if singleton != nil {
return ErrHasInit
}
terminal, err := createTerminal()
if err != nil {
return err
}
if err := d2input.BindHandlerWithPriority(terminal, d2input.PriorityHigh); err != nil {
log.Println(err)
return err
}
singleton = terminal
return nil
}
func Shutdown() {
if singleton != nil {
d2input.UnbindHandler(singleton)
singleton = nil
}
}
func Advance(elapsed float64) error {
if singleton == nil {
return ErrNotInit
}
if singleton != nil {
return singleton.advance(elapsed)
}
return ErrNotInit
}
func Output(format string, params ...interface{}) error {
if singleton == nil {
return ErrNotInit
}
return singleton.output(format, params...)
}
func OutputInfo(format string, params ...interface{}) error {
if singleton == nil {
return ErrNotInit
}
return singleton.outputInfo(format, params...)
}
func OutputWarning(format string, params ...interface{}) error {
if singleton == nil {
return ErrNotInit
}
return singleton.outputWarning(format, params...)
}
func OutputError(format string, params ...interface{}) error {
if singleton == nil {
return ErrNotInit
}
return singleton.outputError(format, params...)
}
func BindAction(name, description string, action interface{}) error {
if singleton == nil {
return ErrNotInit
}
return singleton.bindAction(name, description, action)
}
func UnbindAction(name string) error {
if singleton == nil {
return ErrNotInit
}
return singleton.unbindAction(name)
}
func Render(surface *d2surface.Surface) error {
if singleton == nil {
return ErrNotInit
}
return singleton.render(surface)
}
func BindLogger() {
log.SetOutput(&terminalLogger{writer: log.Writer()})
}

View File

@ -7,7 +7,6 @@ import (
"fmt"
"image/color"
"io"
"log"
"math"
"reflect"
"sort"
@ -15,9 +14,8 @@ import (
"strings"
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
)
const (
@ -111,15 +109,6 @@ func createTerminal() (*terminal, error) {
}
func (t *terminal) advance(elapsed float64) error {
if inpututil.IsKeyJustPressed(ebiten.KeyGraveAccent) {
switch t.visState {
case termVisShowing, termVisShown:
t.hide()
case termVisHiding, termVisHidden:
t.show()
}
}
switch t.visState {
case termVisShowing:
t.visAnim = math.Min(1.0, t.visAnim+elapsed/termAnimLength)
@ -137,34 +126,56 @@ func (t *terminal) advance(elapsed float64) error {
return nil
}
return nil
}
func (t *terminal) OnKeyDown(event d2input.KeyEvent) bool {
maxOutputIndex := d2helper.MaxInt(0, len(t.outputHistory)-t.lineCount)
if inpututil.IsKeyJustPressed(ebiten.KeyHome) {
if event.Key == d2input.KeyGraveAccent {
switch t.visState {
case termVisShowing, termVisShown:
t.hide()
case termVisHiding, termVisHidden:
t.show()
}
return true
}
if event.Key == d2input.KeyEscape {
t.command = ""
return true
}
if event.Key == d2input.KeyHome {
t.outputIndex = maxOutputIndex
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyEnd) {
if event.Key == d2input.KeyEnd {
t.outputIndex = 0
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyPageUp) {
if event.Key == d2input.KeyPageUp {
if t.outputIndex += t.lineCount; t.outputIndex >= maxOutputIndex {
t.outputIndex = maxOutputIndex
}
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyPageDown) {
if event.Key == d2input.KeyPageDown {
if t.outputIndex -= t.lineCount; t.outputIndex < 0 {
t.outputIndex = 0
}
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
t.command = ""
}
if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
if ebiten.IsKeyPressed(ebiten.KeyControl) {
if event.Key == d2input.KeyUp {
if event.KeyMod == d2input.KeyModControl {
t.lineCount = d2helper.MaxInt(0, t.lineCount-1)
} else if len(t.commandHistory) > 0 {
t.command = t.commandHistory[t.commandIndex]
@ -174,17 +185,16 @@ func (t *terminal) advance(elapsed float64) error {
t.commandIndex--
}
}
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyDown) && ebiten.IsKeyPressed(ebiten.KeyControl) {
if event.Key == d2input.KeyDown && event.KeyMod == d2input.KeyModControl {
t.lineCount = d2helper.MinInt(t.lineCount+1, termRowCountMax)
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyBackspace) && len(t.command) > 0 {
t.command = t.command[:len(t.command)-1]
}
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) && len(t.command) > 0 {
if event.Key == d2input.KeyEnter && len(t.command) > 0 {
var commandHistory []string
for _, command := range t.commandHistory {
if command != t.command {
@ -201,15 +211,28 @@ func (t *terminal) advance(elapsed float64) error {
t.commandIndex = len(t.commandHistory) - 1
t.command = ""
return true
}
for _, c := range ebiten.InputChars() {
if event.Key == d2input.KeyBackspace && len(t.command) > 0 {
t.command = t.command[:len(t.command)-1]
return true
}
return false
}
func (t *terminal) OnKeyChars(event d2input.KeyCharsEvent) bool {
var handled bool
for _, c := range event.Chars {
if c != '`' {
t.command += string(c)
handled = true
}
}
return nil
return handled
}
func (t *terminal) render(surface *d2surface.Surface) error {
@ -328,7 +351,7 @@ func (t *terminal) execute(command string) error {
return nil
}
func (t *terminal) outputRaw(text string, category termCategory) {
func (t *terminal) outputRaw(text string, category termCategory) error {
var line string
for _, word := range strings.Split(text, " ") {
if len(line) > 0 {
@ -347,22 +370,23 @@ func (t *terminal) outputRaw(text string, category termCategory) {
}
t.outputHistory = append(t.outputHistory, termHistroyEntry{line, category})
return nil
}
func (t *terminal) output(format string, params ...interface{}) {
t.outputRaw(fmt.Sprintf(format, params...), termCategoryNone)
func (t *terminal) output(format string, params ...interface{}) error {
return t.outputRaw(fmt.Sprintf(format, params...), termCategoryNone)
}
func (t *terminal) outputInfo(format string, params ...interface{}) {
t.outputRaw(fmt.Sprintf(format, params...), termCategoryInfo)
func (t *terminal) outputInfo(format string, params ...interface{}) error {
return t.outputRaw(fmt.Sprintf(format, params...), termCategoryInfo)
}
func (t *terminal) outputWarning(format string, params ...interface{}) {
t.outputRaw(fmt.Sprintf(format, params...), termCategoryWarning)
func (t *terminal) outputWarning(format string, params ...interface{}) error {
return t.outputRaw(fmt.Sprintf(format, params...), termCategoryWarning)
}
func (t *terminal) outputError(format string, params ...interface{}) {
t.outputRaw(fmt.Sprintf(format, params...), termCategoryError)
func (t *terminal) outputError(format string, params ...interface{}) error {
return t.outputRaw(fmt.Sprintf(format, params...), termCategoryError)
}
func (t *terminal) outputClear() {
@ -409,71 +433,8 @@ func (t *terminal) bindAction(name, description string, action interface{}) erro
return nil
}
func (t *terminal) unbindAction(name string) {
func (t *terminal) unbindAction(name string) error {
delete(t.actions, name)
}
var singleton *terminal
func Initialize() error {
if singleton != nil {
return errors.New("terminal system is already initialized")
}
var err error
singleton, err = createTerminal()
return err
}
func Advance(elapsed float64) error {
if singleton != nil {
return singleton.advance(elapsed)
}
return nil
}
func Output(format string, params ...interface{}) {
if singleton != nil {
singleton.output(format, params...)
}
}
func OutputInfo(format string, params ...interface{}) {
if singleton != nil {
singleton.outputInfo(format, params...)
}
}
func OutputWarning(format string, params ...interface{}) {
if singleton != nil {
singleton.outputWarning(format, params...)
}
}
func OutputError(format string, params ...interface{}) {
if singleton != nil {
singleton.outputError(format, params...)
}
}
func BindAction(name, description string, action interface{}) {
if singleton != nil {
singleton.bindAction(name, description, action)
}
}
func UnbindAction(name string) {
if singleton != nil {
singleton.unbindAction(name)
}
}
func Render(surface *d2surface.Surface) error {
if singleton != nil {
return singleton.render(surface)
}
return nil
}
@ -508,10 +469,6 @@ func (t *terminalLogger) Write(p []byte) (int, error) {
return t.writer.Write(p)
}
func BindLogger() {
log.SetOutput(&terminalLogger{writer: log.Writer()})
}
func easeInOut(t float64) float64 {
t *= 2
if t < 1 {

View File

@ -5,6 +5,7 @@ import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2scene"
"github.com/OpenDiablo2/OpenDiablo2/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2surface"
"github.com/OpenDiablo2/OpenDiablo2/d2term"
@ -29,6 +30,7 @@ var region = kingpin.Arg("region", "Region type id").Int()
var preset = kingpin.Arg("preset", "Level preset").Int()
func main() {
d2input.Initialize()
d2term.Initialize()
d2term.BindLogger()