diff --git a/d2common/timeutils.go b/d2common/timeutils.go index 5c5dfd85..ad95e4fd 100644 --- a/d2common/timeutils.go +++ b/d2common/timeutils.go @@ -2,6 +2,8 @@ package d2common import "time" +// Now returns how many seconds have elapsed since Unix time (January 1, 1970 UTC) func Now() float64 { + // Unix time in nanoseconds divided by how many nanoseconds in a second return float64(time.Now().UnixNano()) / 1000000000.0 } diff --git a/d2core/d2input/d2input.go b/d2core/d2input/d2input.go index 15c0268b..59620f4d 100644 --- a/d2core/d2input/d2input.go +++ b/d2core/d2input/d2input.go @@ -5,18 +5,15 @@ import ( ) var ( + // ErrHasReg shows the input system already has a registered handler ErrHasReg = errors.New("input system already has provided handler") + // ErrNotReg shows the input system has no registered handler ErrNotReg = errors.New("input system does not have provided handler") ) -type Priority int - -const ( - PriorityLow Priority = iota - PriorityDefault - PriorityHigh -) +var singleton inputManager +// HandlerEvent holds the qualifiers for a key or mouse event type HandlerEvent struct { KeyMod KeyMod ButtonMod MouseButtonMod @@ -24,6 +21,7 @@ type HandlerEvent struct { Y int } +// KeyEvent represents an event associated with a keyboard key type KeyEvent struct { HandlerEvent Key Key @@ -31,74 +29,86 @@ type KeyEvent struct { Duration int } +// KeyCharsEvent represents an event associated with a keyboard character being pressed type KeyCharsEvent struct { HandlerEvent Chars []rune } +// KeyDownHandler represents a handler for a keyboard key pressed event +type KeyDownHandler interface { + OnKeyDown(event KeyEvent) bool +} + +// KeyRepeatHandler represents a handler for a keyboard key held-down event; between a pressed and released. +type KeyRepeatHandler interface { + OnKeyRepeat(event KeyEvent) bool +} + +// KeyUpHandler represents a handler for a keyboard key release event +type KeyUpHandler interface { + OnKeyUp(event KeyEvent) bool +} + +// KeyCharsHandler represents a handler associated with a keyboard character pressed event +type KeyCharsHandler interface { + OnKeyChars(event KeyCharsEvent) bool +} + +// MouseEvent represents a mouse event type MouseEvent struct { HandlerEvent Button MouseButton } +// MouseEvent represents a mouse movement event 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 -} - +// MouseButtonDownHandler represents a handler for a mouse button pressed event type MouseButtonDownHandler interface { OnMouseButtonDown(event MouseEvent) bool } +// MouseButtonRepeatHandler represents a handler for a mouse button held-down event; between a pressed and released. type MouseButtonRepeatHandler interface { OnMouseButtonRepeat(event MouseEvent) bool } +// MouseButtonUpHandler represents a handler for a mouse button release event type MouseButtonUpHandler interface { OnMouseButtonUp(event MouseEvent) bool } +// MouseMoveHandler represents a handler for a mouse button release event type MouseMoveHandler interface { OnMouseMove(event MouseMoveEvent) bool } -var singleton inputManager - +// Initialize creates a single global input manager based on a specific input service func Initialize(inputService InputService) { singleton = inputManager{ inputService: inputService, } } +// Advance moves the input manager with the elapsed number of seconds. func Advance(elapsed float64) error { return singleton.advance(elapsed) } +// BindHandlerWithPriority adds an event handler with a specific call priority func BindHandlerWithPriority(handler Handler, priority Priority) error { return singleton.bindHandler(handler, priority) } +// BindHandler adds an event handler func BindHandler(handler Handler) error { return BindHandlerWithPriority(handler, PriorityDefault) } +// UnbindHandler removes a previously bound event handler func UnbindHandler(handler Handler) error { return singleton.unbindHandler(handler) } diff --git a/d2core/d2input/ebiten/ebiten_input.go b/d2core/d2input/ebiten/ebiten_input.go index 12205b17..b3f676ba 100644 --- a/d2core/d2input/ebiten/ebiten_input.go +++ b/d2core/d2input/ebiten/ebiten_input.go @@ -1,12 +1,15 @@ +// Package ebiten provides graphics and input API to develop a 2D game. package ebiten import ( - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/inpututil" + + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" ) var ( + //nolint:gochecknoglobals This is a constant in all but by name, no constant map in go keyToEbiten = map[d2input.Key]ebiten.Key{ d2input.Key0: ebiten.Key0, d2input.Key1: ebiten.Key1, @@ -109,6 +112,7 @@ var ( d2input.KeyControl: ebiten.KeyControl, d2input.KeyShift: ebiten.KeyShift, } + //nolint:gochecknoglobals This is a constant in all but by name, no constant map in go mouseButtonToEbiten = map[d2input.MouseButton]ebiten.MouseButton{ d2input.MouseButtonLeft: ebiten.MouseButtonLeft, d2input.MouseButtonMiddle: ebiten.MouseButtonMiddle, @@ -119,38 +123,47 @@ var ( // InputService provides an abstraction on ebiten to support handling input events type InputService struct{} -func (is InputService) CursorPosition() (x int, y int) { +// CursorPosition returns a position of a mouse cursor relative to the game screen (window). +func (is InputService) CursorPosition() (x, y int) { return ebiten.CursorPosition() } +// InputChars return "printable" runes read from the keyboard at the time update is called. func (is InputService) InputChars() []rune { return ebiten.InputChars() } +// IsKeyPressed checks if the provided key is down. func (is InputService) IsKeyPressed(key d2input.Key) bool { return ebiten.IsKeyPressed(keyToEbiten[key]) } +// IsKeyJustPressed checks if the provided key is just transitioned from up to down. func (is InputService) IsKeyJustPressed(key d2input.Key) bool { return inpututil.IsKeyJustPressed(keyToEbiten[key]) } +// IsKeyJustReleased checks if the provided key is just transitioned from down to up. func (is InputService) IsKeyJustReleased(key d2input.Key) bool { return inpututil.IsKeyJustReleased(keyToEbiten[key]) } +// IsMouseButtonPressed checks if the provided mouse button is down. func (is InputService) IsMouseButtonPressed(button d2input.MouseButton) bool { return ebiten.IsMouseButtonPressed(mouseButtonToEbiten[button]) } +// IsMouseButtonJustPressed checks if the provided mouse button is just transitioned from up to down. func (is InputService) IsMouseButtonJustPressed(button d2input.MouseButton) bool { return inpututil.IsMouseButtonJustPressed(mouseButtonToEbiten[button]) } +// IsMouseButtonJustReleased checks if the provided mouse button is just transitioned from down to up. func (is InputService) IsMouseButtonJustReleased(button d2input.MouseButton) bool { return inpututil.IsMouseButtonJustReleased(mouseButtonToEbiten[button]) } +// KeyPressDuration returns how long the key is pressed in frames. func (is InputService) KeyPressDuration(key d2input.Key) int { return inpututil.KeyPressDuration(keyToEbiten[key]) } diff --git a/d2core/d2input/input_manager.go b/d2core/d2input/input_manager.go index 678d661f..4fc653ca 100644 --- a/d2core/d2input/input_manager.go +++ b/d2core/d2input/input_manager.go @@ -4,36 +4,21 @@ import ( "sort" ) -type handlerEntry struct { - handler Handler - priority Priority -} +// Priority of the event handler +type Priority int -type handlerEntryList []handlerEntry +//noinspection GoUnusedConst +const ( + // PriorityLow is a low priority handler + PriorityLow Priority = iota + // PriorityDefault is a default priority handler + PriorityDefault + // PriorityHigh is a high priority handler + PriorityHigh +) -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 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 -} +// Handler is an event handler +type Handler interface{} type inputManager struct { inputService InputService @@ -126,7 +111,7 @@ func (im *inputManager) advance(_ float64) error { for button := mouseButtonMin; button <= mouseButtonMax; button++ { if im.inputService.IsMouseButtonJustPressed(button) { - event := MouseEvent{eventBase, MouseButton(button)} + event := MouseEvent{eventBase, button} im.propagate(func(handler Handler) bool { if l, ok := handler.(MouseButtonDownHandler); ok { return l.OnMouseButtonDown(event) @@ -137,7 +122,7 @@ func (im *inputManager) advance(_ float64) error { } if im.inputService.IsMouseButtonJustReleased(button) { - event := MouseEvent{eventBase, MouseButton(button)} + event := MouseEvent{eventBase, button} im.propagate(func(handler Handler) bool { if l, ok := handler.(MouseButtonUpHandler); ok { return l.OnMouseButtonUp(event) @@ -147,7 +132,7 @@ func (im *inputManager) advance(_ float64) error { }) } if im.inputService.IsMouseButtonPressed(button) { - event := MouseEvent{eventBase, MouseButton(button)} + event := MouseEvent{eventBase, button} im.propagate(func(handler Handler) bool { if l, ok := handler.(MouseButtonRepeatHandler); ok { return l.OnMouseButtonRepeat(event) @@ -215,3 +200,22 @@ func (im *inputManager) propagate(callback func(Handler) bool) { priority = entry.priority } } + +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 +} diff --git a/d2core/d2input/input_service.go b/d2core/d2input/input_service.go new file mode 100644 index 00000000..f01508ba --- /dev/null +++ b/d2core/d2input/input_service.go @@ -0,0 +1,23 @@ +package d2input + +// InputService represents an interface offering Keyboard and Mouse interactions. +type InputService interface { + // CursorPosition returns a position of a mouse cursor relative to the game screen (window). + CursorPosition() (x int, y int) + // InputChars return "printable" runes read from the keyboard at the time update is called. + InputChars() []rune + // IsKeyPressed checks if the provided key is down. + IsKeyPressed(key Key) bool + // IsKeyJustPressed checks if the provided key is just transitioned from up to down. + IsKeyJustPressed(key Key) bool + // IsKeyJustReleased checks if the provided key is just transitioned from down to up. + IsKeyJustReleased(key Key) bool + // IsMouseButtonPressed checks if the provided mouse button is down. + IsMouseButtonPressed(button MouseButton) bool + // IsMouseButtonJustPressed checks if the provided mouse button is just transitioned from up to down. + IsMouseButtonJustPressed(button MouseButton) bool + // IsMouseButtonJustReleased checks if the provided mouse button is just transitioned from down to up. + IsMouseButtonJustReleased(button MouseButton) bool + // KeyPressDuration returns how long the key is pressed in frames. + KeyPressDuration(key Key) int +} diff --git a/d2core/d2input/key.go b/d2core/d2input/key.go index f735fa7d..6a4855bf 100644 --- a/d2core/d2input/key.go +++ b/d2core/d2input/key.go @@ -1,138 +1,225 @@ +// Package d2input provides interaction with input services providing key and mouse interactions. package d2input -// Key is the physical key of keyboard input +// Key represents button on a traditional keyboard. type Key int const ( + // Key0 is the number 0 Key0 Key = iota + // Key1 is the number 1 Key1 + // Key2 is the number 2 Key2 + // Key3 is the number 3 Key3 + // Key4 is the number 4 Key4 + // Key5 is the number 5 Key5 + // Key6 is the number 6 Key6 + // Key7 is the number 7 Key7 + // Key8 is the number 8 Key8 + // Key9 is the number 9 Key9 + // KeyA is the letter A KeyA + // KeyB is the letter B KeyB + // KeyC is the letter C KeyC + // KeyD is the letter D KeyD + // KeyE is the letter E KeyE + // KeyF is the letter F KeyF + // KeyG is the letter G KeyG + // KeyH is the letter H KeyH + // KeyI is the letter I KeyI + // KeyJ is the letter J KeyJ + // KeyK is the letter K KeyK + // KeyL is the letter L KeyL + // KeyM is the letter M KeyM + // KeyN is the letter N KeyN + // KeyO is the letter O KeyO + // KeyP is the letter P KeyP + // KeyQ is the letter Q KeyQ + // KeyR is the letter R KeyR + // KeyS is the letter S KeyS + // KeyT is the letter T KeyT + // KeyU is the letter U KeyU + // KeyV is the letter V KeyV + // KeyW is the letter W KeyW + // KeyX is the letter X KeyX + // KeyY is the letter Y KeyY + // KeyZ is the letter Z KeyZ + // KeyApostrophe is the Apostrophe KeyApostrophe + // KeyBackslash is the Backslash KeyBackslash + // KeyBackspace is the Backspace KeyBackspace + // KeyCapsLock is the CapsLock KeyCapsLock + // KeyComma is the Comma KeyComma + // KeyDelete is the Delete KeyDelete + // KeyDown is the down arrow key KeyDown + // KeyEnd is the End KeyEnd + // KeyEnter is the Enter KeyEnter + // KeyEqual is the Equal KeyEqual + // KeyEscape is the Escape KeyEscape + // KeyF1 is the function F1 KeyF1 + // KeyF2 is the function F2 KeyF2 + // KeyF3 is the function F3 KeyF3 + // KeyF4 is the function F4 KeyF4 + // KeyF5 is the function F5 KeyF5 + // KeyF6 is the function F6 KeyF6 + // KeyF7 is the function F7 KeyF7 + // KeyF8 is the function F8 KeyF8 + // KeyF9 is the function F9 KeyF9 + // KeyF10 is the function F10 KeyF10 + // KeyF11 is the function F11 KeyF11 + // KeyF12 is the function F12 KeyF12 + // KeyGraveAccent is the Grave Accent KeyGraveAccent + // KeyHome is the home key KeyHome + // KeyInsert is the insert key KeyInsert + // KeyKP0 is keypad 0 KeyKP0 + // KeyKP1 is keypad 1 KeyKP1 + // KeyKP2 is keypad 2 KeyKP2 + // KeyKP3 is keypad 3 KeyKP3 + // KeyKP4 is keypad 4 KeyKP4 + // KeyKP5 is keypad 5 KeyKP5 + // KeyKP6 is keypad 6 KeyKP6 + // KeyKP7 is keypad 7 KeyKP7 + // KeyKP8 is keypad 8 KeyKP8 + // KeyKP9 is keypad 9 KeyKP9 + // KeyKPAdd is keypad Add KeyKPAdd + // KeyKPDecimal is keypad Decimal KeyKPDecimal + // KeyKPDivide is keypad Divide KeyKPDivide + // KeyKPEnter is keypad Enter KeyKPEnter + // KeyKPEqual is keypad Equal KeyKPEqual + // KeyKPMultiply is keypad Multiply KeyKPMultiply + // KeyKPSubtract is keypad Subtract KeyKPSubtract + // KeyLeft is the left arrow key KeyLeft + // KeyLeftBracket is the left bracket KeyLeftBracket + // KeyMenu is the Menu key KeyMenu + // KeyMinus is the Minus key KeyMinus + // KeyNumLock is the NumLock key KeyNumLock + // KeyPageDown is the PageDown key KeyPageDown + // KeyPageUp is the PageUp key KeyPageUp + // KeyPause is the Pause key KeyPause + // KeyPeriod is the Period key KeyPeriod + // KeyPrintScreen is the PrintScreen key KeyPrintScreen + // KeyRight is the right arrow key KeyRight + // KeyRightBracket is the right bracket key KeyRightBracket + // KeyScrollLock is the scroll lock key KeyScrollLock + // KeySemicolon is the semicolon key KeySemicolon + // KeySlash is the front slash key KeySlash + // KeySpace is the space key KeySpace + // KeyTab is the tab key KeyTab + // KeyUp is the up arrow key KeyUp + // KeyAlt is the alt key KeyAlt + // KeyControl is the control key KeyControl + // KeyShift is the shift key KeyShift + // Lowest key in key constants keyMin = Key0 + // Highest key is key constants keyMax = KeyShift ) +// KeyMod represents a "modified" key action. This could mean, for example, ctrl-S type KeyMod int const ( + // KeyModAlt is the Alt key modifier KeyModAlt KeyMod = 1 << iota + // KeyModControl is the Control key modifier KeyModControl + // KeyModShift is the Shift key modifier 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 -) diff --git a/d2core/d2input/mouse_button.go b/d2core/d2input/mouse_button.go new file mode 100644 index 00000000..65a55700 --- /dev/null +++ b/d2core/d2input/mouse_button.go @@ -0,0 +1,28 @@ +package d2input + +// MouseButton represents a traditional 3-button mouse +type MouseButton int + +const ( + // MouseButtonLeft is the left mouse button + MouseButtonLeft MouseButton = iota + // MouseButtonMiddle is the middle mouse button + MouseButtonMiddle + // MouseButtonRight is the right mouse button + MouseButtonRight + + mouseButtonMin = MouseButtonLeft + mouseButtonMax = MouseButtonRight +) + +// MouseButtonMod represents a "modified" mouse button action. This could mean, for example, ctrl-mouse_left +type MouseButtonMod int + +const ( + // MouseButtonLeft is a modified left mouse button + MouseButtonModLeft MouseButtonMod = 1 << iota + // MouseButtonModMiddle is a modified middle mouse button + MouseButtonModMiddle + // MouseButtonModRight is a modified right mouse button + MouseButtonModRight +)