mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-09 10:06:35 -05:00
Adding input system, mouse cursor scene, interactive component
* added `d2common/d2input`, copied input vector logic from hellspawner * added an `InteractiveComponent` which contains input vector, enable flag, and callback function * Added an InputSystem which handles input logic and iterates over entities with interactive components * added a mouse cursor scene for rendering the mouse cursor * made the trademark sprite disappear when left mouse is clicked * various other small bugfixes in scene systems
This commit is contained in:
parent
3f5d2c0938
commit
e6d418fdb2
93
d2common/d2input/input_vector.go
Normal file
93
d2common/d2input/input_vector.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package d2input
|
||||||
|
|
||||||
|
import "github.com/gravestench/akara"
|
||||||
|
|
||||||
|
func NewInputVector() *InputVector {
|
||||||
|
v := &InputVector{
|
||||||
|
KeyVector: akara.NewBitSet(),
|
||||||
|
ModifierVector: akara.NewBitSet(),
|
||||||
|
MouseButtonVector: akara.NewBitSet(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
type InputVector struct {
|
||||||
|
KeyVector *akara.BitSet
|
||||||
|
ModifierVector *akara.BitSet
|
||||||
|
MouseButtonVector *akara.BitSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetKey(key Key) *InputVector {
|
||||||
|
return iv.SetKeys([]Key{key})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetKeys(keys []Key) *InputVector {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
iv.KeyVector.Set(int(key), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetModifier(mod Modifier) *InputVector {
|
||||||
|
return iv.SetModifiers([]Modifier{mod})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetModifiers(mods []Modifier) *InputVector {
|
||||||
|
if len(mods) == 0 {
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range mods {
|
||||||
|
iv.ModifierVector.Set(int(key), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetMouseButton(button MouseButton) *InputVector {
|
||||||
|
return iv.SetMouseButtons([]MouseButton{button})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) SetMouseButtons(buttons []MouseButton) *InputVector {
|
||||||
|
if len(buttons) == 0 {
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range buttons {
|
||||||
|
iv.MouseButtonVector.Set(int(key), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) Contains(other *InputVector) bool {
|
||||||
|
keys := iv.KeyVector.ContainsAll(other.KeyVector)
|
||||||
|
buttons := iv.MouseButtonVector.ContainsAll(other.MouseButtonVector)
|
||||||
|
|
||||||
|
// We do Equals here, because we dont want CTRL+X and CTRL+ALT+X to fire at the same time
|
||||||
|
mods := iv.ModifierVector.Equals(other.ModifierVector)
|
||||||
|
|
||||||
|
return keys && mods && buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) Intersects(other *InputVector) bool {
|
||||||
|
keys := iv.KeyVector.Intersects(other.KeyVector)
|
||||||
|
mods := iv.ModifierVector.Intersects(other.ModifierVector)
|
||||||
|
buttons := iv.MouseButtonVector.Intersects(other.MouseButtonVector)
|
||||||
|
|
||||||
|
return keys || mods || buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iv *InputVector) Clear() *InputVector {
|
||||||
|
iv.KeyVector.Clear()
|
||||||
|
iv.ModifierVector.Clear()
|
||||||
|
iv.MouseButtonVector.Clear()
|
||||||
|
|
||||||
|
return iv
|
||||||
|
}
|
105
d2common/d2input/keys.go
Normal file
105
d2common/d2input/keys.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package d2input
|
||||||
|
|
||||||
|
import "github.com/hajimehoshi/ebiten/v2"
|
||||||
|
|
||||||
|
type Key = int
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
11
d2common/d2input/modifiers.go
Normal file
11
d2common/d2input/modifiers.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package d2input
|
||||||
|
|
||||||
|
import "github.com/hajimehoshi/ebiten/v2"
|
||||||
|
|
||||||
|
type Modifier = int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModAlt = Modifier(ebiten.KeyAlt)
|
||||||
|
ModControl = Modifier(ebiten.KeyControl)
|
||||||
|
ModShift = Modifier(ebiten.KeyShift)
|
||||||
|
)
|
11
d2common/d2input/mouse_buttons.go
Normal file
11
d2common/d2input/mouse_buttons.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package d2input
|
||||||
|
|
||||||
|
import "github.com/hajimehoshi/ebiten/v2"
|
||||||
|
|
||||||
|
type MouseButton = int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MouseButtonLeft = MouseButton(ebiten.MouseButtonLeft)
|
||||||
|
MouseButtonRight = MouseButton(ebiten.MouseButtonRight)
|
||||||
|
MouseButtonMiddle = MouseButton(ebiten.MouseButtonMiddle)
|
||||||
|
)
|
51
d2core/d2components/interactive.go
Normal file
51
d2core/d2components/interactive.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package d2components
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gravestench/akara"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2input"
|
||||||
|
)
|
||||||
|
|
||||||
|
// static check that Interactive implements Component
|
||||||
|
var _ akara.Component = &Interactive{}
|
||||||
|
|
||||||
|
func noop() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive is used to flag file entities with a file type
|
||||||
|
type Interactive struct {
|
||||||
|
Enabled bool
|
||||||
|
*d2input.InputVector
|
||||||
|
Callback func() (preventPropagation bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Interactive component. By default, it contains a nil instance.
|
||||||
|
func (*Interactive) New() akara.Component {
|
||||||
|
return &Interactive{
|
||||||
|
Enabled: true,
|
||||||
|
InputVector: d2input.NewInputVector(),
|
||||||
|
Callback: noop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InteractiveFactory is a wrapper for the generic component factory that returns Interactive component instances.
|
||||||
|
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Interactive.
|
||||||
|
type InteractiveFactory struct {
|
||||||
|
Interactive *akara.ComponentFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInteractive adds a Interactive component to the given entity and returns it
|
||||||
|
func (m *InteractiveFactory) AddInteractive(id akara.EID) *Interactive {
|
||||||
|
return m.Interactive.Add(id).(*Interactive)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInteractive returns the Interactive component for the given entity, and a bool for whether or not it exists
|
||||||
|
func (m *InteractiveFactory) GetInteractive(id akara.EID) (*Interactive, bool) {
|
||||||
|
component, found := m.Interactive.Get(id)
|
||||||
|
if !found {
|
||||||
|
return nil, found
|
||||||
|
}
|
||||||
|
|
||||||
|
return component.(*Interactive), found
|
||||||
|
}
|
@ -46,6 +46,9 @@ func (m *GameClientBootstrapSystem) injectSystems() {
|
|||||||
m.Info("injecting render system")
|
m.Info("injecting render system")
|
||||||
m.AddSystem(&RenderSystem{})
|
m.AddSystem(&RenderSystem{})
|
||||||
|
|
||||||
|
m.Info("injecting input system")
|
||||||
|
m.AddSystem(&InputSystem{})
|
||||||
|
|
||||||
m.Info("injecting update counter system")
|
m.Info("injecting update counter system")
|
||||||
m.AddSystem(&UpdateCounter{})
|
m.AddSystem(&UpdateCounter{})
|
||||||
|
|
||||||
@ -54,6 +57,9 @@ func (m *GameClientBootstrapSystem) injectSystems() {
|
|||||||
|
|
||||||
m.Info("injecting main menu scene")
|
m.Info("injecting main menu scene")
|
||||||
m.AddSystem(NewMainMenuScene())
|
m.AddSystem(NewMainMenuScene())
|
||||||
|
|
||||||
|
m.Info("injecting mouse cursor scene")
|
||||||
|
m.AddSystem(NewMouseCursorScene())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update does nothing, but exists to satisfy the `akara.System` interface
|
// Update does nothing, but exists to satisfy the `akara.System` interface
|
||||||
|
151
d2core/d2systems/input_system.go
Normal file
151
d2core/d2systems/input_system.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package d2systems
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2input"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
|
||||||
|
"github.com/gravestench/akara"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
logPrefixInputSystem = "Input System"
|
||||||
|
)
|
||||||
|
|
||||||
|
// static check that InputSystem implements the System interface
|
||||||
|
var _ akara.System = &InputSystem{}
|
||||||
|
|
||||||
|
// InputSystem is responsible for handling interactive entities
|
||||||
|
type InputSystem struct {
|
||||||
|
akara.BaseSubscriberSystem
|
||||||
|
*d2util.Logger
|
||||||
|
renderer d2interface.Renderer
|
||||||
|
configs *akara.Subscription
|
||||||
|
interactives *akara.Subscription
|
||||||
|
d2components.GameConfigFactory
|
||||||
|
d2components.InteractiveFactory
|
||||||
|
inputState *d2input.InputVector
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the system with the given world, injecting the necessary components
|
||||||
|
func (m *InputSystem) Init(world *akara.World) {
|
||||||
|
m.World = world
|
||||||
|
|
||||||
|
m.setupLogger()
|
||||||
|
|
||||||
|
m.Info("initializing ...")
|
||||||
|
|
||||||
|
m.setupFactories()
|
||||||
|
m.setupSubscriptions()
|
||||||
|
|
||||||
|
m.inputState = d2input.NewInputVector()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputSystem) setupLogger() {
|
||||||
|
m.Logger = d2util.NewLogger()
|
||||||
|
m.SetPrefix(logPrefixInputSystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputSystem) setupFactories() {
|
||||||
|
m.Info("setting up component factories")
|
||||||
|
|
||||||
|
gameConfigID := m.RegisterComponent(&d2components.GameConfig{})
|
||||||
|
interactiveID := m.RegisterComponent(&d2components.Interactive{})
|
||||||
|
|
||||||
|
m.GameConfig = m.GetComponentFactory(gameConfigID)
|
||||||
|
m.Interactive = m.GetComponentFactory(interactiveID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputSystem) setupSubscriptions() {
|
||||||
|
m.Info("setting up component subscriptions")
|
||||||
|
|
||||||
|
interactives := m.NewComponentFilter().
|
||||||
|
Require(&d2components.Interactive{}).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
gameConfigs := m.NewComponentFilter().
|
||||||
|
Require(&d2components.GameConfig{}).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
m.interactives = m.AddSubscription(interactives)
|
||||||
|
m.configs = m.AddSubscription(gameConfigs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will iterate over interactive entities
|
||||||
|
func (m *InputSystem) Update() {
|
||||||
|
m.updateInputState()
|
||||||
|
|
||||||
|
for _, id := range m.interactives.GetEntities() {
|
||||||
|
preventPropagation := m.applyInputState(id)
|
||||||
|
if preventPropagation {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputSystem) updateInputState() {
|
||||||
|
var keysToCheck = []d2input.Key{
|
||||||
|
d2input.Key0, d2input.Key1, d2input.Key2, d2input.Key3, d2input.Key4, d2input.Key5, d2input.Key6,
|
||||||
|
d2input.Key7, d2input.Key8, d2input.Key9, d2input.KeyA, d2input.KeyB, d2input.KeyC, d2input.KeyD,
|
||||||
|
d2input.KeyE, d2input.KeyF, d2input.KeyG, d2input.KeyH, d2input.KeyI, d2input.KeyJ, d2input.KeyK,
|
||||||
|
d2input.KeyL, d2input.KeyM, d2input.KeyN, d2input.KeyO, d2input.KeyP, d2input.KeyQ, d2input.KeyR,
|
||||||
|
d2input.KeyS, d2input.KeyT, d2input.KeyU, d2input.KeyV, d2input.KeyW, d2input.KeyX, d2input.KeyY,
|
||||||
|
d2input.KeyZ, d2input.KeyApostrophe, d2input.KeyBackslash, d2input.KeyBackspace,
|
||||||
|
d2input.KeyCapsLock, d2input.KeyComma, d2input.KeyDelete, d2input.KeyDown,
|
||||||
|
d2input.KeyEnd, d2input.KeyEnter, d2input.KeyEqual, d2input.KeyEscape,
|
||||||
|
d2input.KeyF1, d2input.KeyF2, d2input.KeyF3, d2input.KeyF4, d2input.KeyF5, d2input.KeyF6,
|
||||||
|
d2input.KeyF7, d2input.KeyF8, d2input.KeyF9, d2input.KeyF10, d2input.KeyF11, d2input.KeyF12,
|
||||||
|
d2input.KeyGraveAccent, d2input.KeyHome, d2input.KeyInsert, d2input.KeyKP0,
|
||||||
|
d2input.KeyKP1, d2input.KeyKP2, d2input.KeyKP3, d2input.KeyKP4, d2input.KeyKP5,
|
||||||
|
d2input.KeyKP6, d2input.KeyKP7, d2input.KeyKP8, d2input.KeyKP9,
|
||||||
|
d2input.KeyKPAdd, d2input.KeyKPDecimal, d2input.KeyKPDivide, d2input.KeyKPEnter,
|
||||||
|
d2input.KeyKPEqual, d2input.KeyKPMultiply, d2input.KeyKPSubtract, d2input.KeyLeft,
|
||||||
|
d2input.KeyLeftBracket, d2input.KeyMenu, d2input.KeyMinus, d2input.KeyNumLock,
|
||||||
|
d2input.KeyPageDown, d2input.KeyPageUp, d2input.KeyPause, d2input.KeyPeriod,
|
||||||
|
d2input.KeyPrintScreen, d2input.KeyRight, d2input.KeyRightBracket,
|
||||||
|
d2input.KeyScrollLock, d2input.KeySemicolon, d2input.KeySlash,
|
||||||
|
d2input.KeySpace, d2input.KeyTab, d2input.KeyUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
var modifiersToCheck = []d2input.Modifier{
|
||||||
|
d2input.ModAlt, d2input.ModControl, d2input.ModShift,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonsToCheck = []d2input.MouseButton{
|
||||||
|
d2input.MouseButtonLeft, d2input.MouseButtonMiddle, d2input.MouseButtonRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keysToCheck {
|
||||||
|
truth := ebiten.IsKeyPressed(ebiten.Key(key))
|
||||||
|
m.inputState.KeyVector.Set(key, truth)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mod := range modifiersToCheck {
|
||||||
|
truth := ebiten.IsKeyPressed(ebiten.Key(mod))
|
||||||
|
m.inputState.ModifierVector.Set(mod, truth)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, btn := range buttonsToCheck {
|
||||||
|
truth := ebiten.IsMouseButtonPressed(ebiten.MouseButton(btn))
|
||||||
|
m.inputState.MouseButtonVector.Set(btn, truth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputSystem) applyInputState(id akara.EID) (preventPropagation bool) {
|
||||||
|
v, found := m.GetInteractive(id)
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v.Enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.inputState.Contains(v.InputVector) {
|
||||||
|
return v.Callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -37,6 +37,7 @@ var _ akara.System = &BaseScene{}
|
|||||||
|
|
||||||
type baseSystems struct {
|
type baseSystems struct {
|
||||||
*RenderSystem
|
*RenderSystem
|
||||||
|
*InputSystem
|
||||||
*GameObjectFactory
|
*GameObjectFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,8 +59,10 @@ type BaseScene struct {
|
|||||||
d2components.ViewportFactory
|
d2components.ViewportFactory
|
||||||
d2components.MainViewportFactory
|
d2components.MainViewportFactory
|
||||||
d2components.ViewportFilterFactory
|
d2components.ViewportFilterFactory
|
||||||
|
d2components.PriorityFactory
|
||||||
d2components.CameraFactory
|
d2components.CameraFactory
|
||||||
d2components.RenderableFactory
|
d2components.RenderableFactory
|
||||||
|
d2components.InteractiveFactory
|
||||||
d2components.PositionFactory
|
d2components.PositionFactory
|
||||||
d2components.ScaleFactory
|
d2components.ScaleFactory
|
||||||
d2components.AnimationFactory
|
d2components.AnimationFactory
|
||||||
@ -82,6 +85,7 @@ func (s *BaseScene) Init(world *akara.World) {
|
|||||||
s.World = world
|
s.World = world
|
||||||
|
|
||||||
if s.World == nil {
|
if s.World == nil {
|
||||||
|
s.SetActive(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,12 +103,17 @@ func (s *BaseScene) boot() {
|
|||||||
s.Add.SetPrefix(fmt.Sprintf("%s -> %s", s.key, "Object Factory"))
|
s.Add.SetPrefix(fmt.Sprintf("%s -> %s", s.key, "Object Factory"))
|
||||||
|
|
||||||
for idx := range s.Systems {
|
for idx := range s.Systems {
|
||||||
if rendersys, ok := s.Systems[idx].(*RenderSystem); ok {
|
if rendersys, ok := s.Systems[idx].(*RenderSystem); ok && s.systems.RenderSystem == nil {
|
||||||
s.systems.RenderSystem = rendersys
|
s.systems.RenderSystem = rendersys
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok {
|
if inputSys, ok := s.Systems[idx].(*InputSystem); ok && s.systems.InputSystem == nil {
|
||||||
|
s.systems.InputSystem = inputSys
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok && s.systems.GameObjectFactory == nil {
|
||||||
s.systems.GameObjectFactory = objFactory
|
s.systems.GameObjectFactory = objFactory
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -120,6 +129,13 @@ func (s *BaseScene) boot() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.systems.InputSystem == nil {
|
||||||
|
s.Info("waiting for input system")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.systems.InputSystem.renderer = s.systems.RenderSystem.renderer
|
||||||
|
|
||||||
if s.systems.GameObjectFactory == nil {
|
if s.systems.GameObjectFactory == nil {
|
||||||
s.Info("waiting for game object factory ...")
|
s.Info("waiting for game object factory ...")
|
||||||
return
|
return
|
||||||
@ -140,7 +156,9 @@ func (s *BaseScene) setupFactories() {
|
|||||||
viewportID := s.RegisterComponent(&d2components.Viewport{})
|
viewportID := s.RegisterComponent(&d2components.Viewport{})
|
||||||
viewportFilterID := s.RegisterComponent(&d2components.ViewportFilter{})
|
viewportFilterID := s.RegisterComponent(&d2components.ViewportFilter{})
|
||||||
cameraID := s.RegisterComponent(&d2components.Camera{})
|
cameraID := s.RegisterComponent(&d2components.Camera{})
|
||||||
|
priorityID := s.RegisterComponent(&d2components.Priority{})
|
||||||
renderableID := s.RegisterComponent(&d2components.Renderable{})
|
renderableID := s.RegisterComponent(&d2components.Renderable{})
|
||||||
|
interactiveID := s.RegisterComponent(&d2components.Interactive{})
|
||||||
positionID := s.RegisterComponent(&d2components.Position{})
|
positionID := s.RegisterComponent(&d2components.Position{})
|
||||||
scaleID := s.RegisterComponent(&d2components.Scale{})
|
scaleID := s.RegisterComponent(&d2components.Scale{})
|
||||||
animationID := s.RegisterComponent(&d2components.Animation{})
|
animationID := s.RegisterComponent(&d2components.Animation{})
|
||||||
@ -151,7 +169,9 @@ func (s *BaseScene) setupFactories() {
|
|||||||
s.Viewport = s.GetComponentFactory(viewportID)
|
s.Viewport = s.GetComponentFactory(viewportID)
|
||||||
s.ViewportFilter = s.GetComponentFactory(viewportFilterID)
|
s.ViewportFilter = s.GetComponentFactory(viewportFilterID)
|
||||||
s.Camera = s.GetComponentFactory(cameraID)
|
s.Camera = s.GetComponentFactory(cameraID)
|
||||||
|
s.Priority = s.GetComponentFactory(priorityID)
|
||||||
s.Renderable = s.GetComponentFactory(renderableID)
|
s.Renderable = s.GetComponentFactory(renderableID)
|
||||||
|
s.Interactive = s.GetComponentFactory(interactiveID)
|
||||||
s.Position = s.GetComponentFactory(positionID)
|
s.Position = s.GetComponentFactory(positionID)
|
||||||
s.Scale = s.GetComponentFactory(scaleID)
|
s.Scale = s.GetComponentFactory(scaleID)
|
||||||
s.Animation = s.GetComponentFactory(animationID)
|
s.Animation = s.GetComponentFactory(animationID)
|
||||||
@ -163,13 +183,11 @@ func (s *BaseScene) createDefaultViewport() {
|
|||||||
s.Info("creating default viewport")
|
s.Info("creating default viewport")
|
||||||
viewportID := s.NewEntity()
|
viewportID := s.NewEntity()
|
||||||
s.AddViewport(viewportID)
|
s.AddViewport(viewportID)
|
||||||
|
s.AddPriority(viewportID)
|
||||||
|
|
||||||
camera := s.AddCamera(viewportID)
|
camera := s.AddCamera(viewportID)
|
||||||
camera.Width = 800
|
|
||||||
camera.Height = 600
|
|
||||||
camera.Zoom = 1
|
|
||||||
|
|
||||||
sfc := s.systems.renderer.NewSurface(camera.Width, camera.Height)
|
sfc := s.systems.RenderSystem.renderer.NewSurface(camera.Width, camera.Height)
|
||||||
|
|
||||||
sfc.Clear(color.Transparent)
|
sfc.Clear(color.Transparent)
|
||||||
|
|
||||||
@ -266,9 +284,11 @@ func (s *BaseScene) renderViewport(idx int, objects []akara.EID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sfc.Surface == nil {
|
if sfc.Surface == nil {
|
||||||
sfc.Surface = s.systems.renderer.NewSurface(camera.Width, camera.Height)
|
sfc.Surface = s.systems.RenderSystem.renderer.NewSurface(camera.Width, camera.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sfc.Clear(color.Transparent)
|
||||||
|
|
||||||
cx, cy := int(camera.X()), int(camera.Y())
|
cx, cy := int(camera.X()), int(camera.Y())
|
||||||
|
|
||||||
sfc.Surface.PushTranslation(cx, cy) // negative because we're offsetting everything that gets rendered
|
sfc.Surface.PushTranslation(cx, cy) // negative because we're offsetting everything that gets rendered
|
||||||
@ -298,6 +318,11 @@ func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) {
|
|||||||
scale = s.AddScale(id)
|
scale = s.AddScale(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alpha, found := s.GetAlpha(id)
|
||||||
|
if !found {
|
||||||
|
alpha = s.AddAlpha(id)
|
||||||
|
}
|
||||||
|
|
||||||
x, y := int(position.X()), int(position.Y())
|
x, y := int(position.X()), int(position.Y())
|
||||||
|
|
||||||
target.PushTranslation(x, y)
|
target.PushTranslation(x, y)
|
||||||
@ -306,6 +331,9 @@ func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) {
|
|||||||
target.PushScale(scale.X(), scale.Y())
|
target.PushScale(scale.X(), scale.Y())
|
||||||
defer target.Pop()
|
defer target.Pop()
|
||||||
|
|
||||||
|
target.PushColor(color.Alpha{A: uint8(alpha.Alpha * 255)})
|
||||||
|
defer target.Pop()
|
||||||
|
|
||||||
segment, found := s.systems.SpriteFactory.GetSegmentedSprite(id)
|
segment, found := s.systems.SpriteFactory.GetSegmentedSprite(id)
|
||||||
if found {
|
if found {
|
||||||
animation, found := s.GetAnimation(id)
|
animation, found := s.GetAnimation(id)
|
||||||
@ -352,10 +380,10 @@ type sceneObjectFactory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *sceneObjectFactory) addBasicComponenets(id akara.EID) {
|
func (s *sceneObjectFactory) addBasicComponenets(id akara.EID) {
|
||||||
_ = s.AddScale(id)
|
|
||||||
_ = s.AddOrigin(id)
|
|
||||||
_ = s.AddPosition(id)
|
_ = s.AddPosition(id)
|
||||||
|
_ = s.AddScale(id)
|
||||||
_ = s.AddAlpha(id)
|
_ = s.AddAlpha(id)
|
||||||
|
_ = s.AddOrigin(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID {
|
func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID {
|
||||||
|
@ -72,6 +72,7 @@ func (s *LoadingScene) Init(world *akara.World) {
|
|||||||
|
|
||||||
func (s *LoadingScene) boot() {
|
func (s *LoadingScene) boot() {
|
||||||
if !s.BaseScene.booted {
|
if !s.BaseScene.booted {
|
||||||
|
s.BaseScene.boot()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package d2systems
|
package d2systems
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2input"
|
||||||
"github.com/gravestench/akara"
|
"github.com/gravestench/akara"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
@ -28,7 +29,11 @@ var _ d2interface.Scene = &MainMenuScene{}
|
|||||||
// or start the map engine test.
|
// or start the map engine test.
|
||||||
type MainMenuScene struct {
|
type MainMenuScene struct {
|
||||||
*BaseScene
|
*BaseScene
|
||||||
booted bool
|
booted bool
|
||||||
|
sprites struct {
|
||||||
|
trademark akara.EID
|
||||||
|
mainBackground akara.EID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the main menu scene
|
// Init the main menu scene
|
||||||
@ -43,16 +48,20 @@ func (s *MainMenuScene) boot() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.createTrademarkScreen()
|
|
||||||
s.createButtons()
|
|
||||||
s.createBackground()
|
s.createBackground()
|
||||||
|
s.createButtons()
|
||||||
|
s.createTrademarkScreen()
|
||||||
|
|
||||||
s.booted = true
|
s.booted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MainMenuScene) createBackground() {
|
func (s *MainMenuScene) createBackground() {
|
||||||
s.Info("creating background")
|
s.Info("creating background")
|
||||||
s.Add.SegmentedSprite(0, 0, d2resource.GameSelectScreen, d2resource.PaletteSky, 4, 3, 0)
|
|
||||||
|
imgPath := d2resource.GameSelectScreen
|
||||||
|
palPath := d2resource.PaletteSky
|
||||||
|
|
||||||
|
s.sprites.mainBackground = s.Add.SegmentedSprite(0, 0, imgPath, palPath, 4, 3, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MainMenuScene) createButtons() {
|
func (s *MainMenuScene) createButtons() {
|
||||||
@ -61,7 +70,26 @@ func (s *MainMenuScene) createButtons() {
|
|||||||
|
|
||||||
func (s *MainMenuScene) createTrademarkScreen() {
|
func (s *MainMenuScene) createTrademarkScreen() {
|
||||||
s.Info("creating trademark screen")
|
s.Info("creating trademark screen")
|
||||||
s.Add.SegmentedSprite(0, 0, d2resource.TrademarkScreen, d2resource.PaletteSky, 4, 3, 0)
|
|
||||||
|
imgPath := d2resource.TrademarkScreen
|
||||||
|
palPath := d2resource.PaletteSky
|
||||||
|
|
||||||
|
s.sprites.trademark = s.Add.SegmentedSprite(0, 0, imgPath, palPath, 4, 3, 0)
|
||||||
|
|
||||||
|
interactive := s.AddInteractive(s.sprites.trademark)
|
||||||
|
|
||||||
|
interactive.InputVector.SetMouseButton(d2input.MouseButtonLeft)
|
||||||
|
|
||||||
|
interactive.Callback = func() bool {
|
||||||
|
s.Info("hiding trademark sprite")
|
||||||
|
|
||||||
|
alpha, _ := s.GetAlpha(s.sprites.trademark)
|
||||||
|
alpha.Alpha = 0
|
||||||
|
|
||||||
|
interactive.Enabled = false
|
||||||
|
|
||||||
|
return true // prevent propagation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the main menu scene
|
// Update the main menu scene
|
||||||
|
83
d2core/d2systems/scene_mouse_cursor.go
Normal file
83
d2core/d2systems/scene_mouse_cursor.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package d2systems
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gravestench/akara"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sceneKeyMouseCursor = "Mouse Cursor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewMouseCursorScene creates a new main menu scene. This is the first screen that the user
|
||||||
|
// will see when launching the game.
|
||||||
|
func NewMouseCursorScene() *MouseCursorScene {
|
||||||
|
scene := &MouseCursorScene{
|
||||||
|
BaseScene: NewBaseScene(sceneKeyMouseCursor),
|
||||||
|
}
|
||||||
|
|
||||||
|
return scene
|
||||||
|
}
|
||||||
|
|
||||||
|
// static check that MouseCursorScene implements the scene interface
|
||||||
|
var _ d2interface.Scene = &MouseCursorScene{}
|
||||||
|
|
||||||
|
// MouseCursorScene represents the game's main menu, where users can select single or multi player,
|
||||||
|
// or start the map engine test.
|
||||||
|
type MouseCursorScene struct {
|
||||||
|
*BaseScene
|
||||||
|
booted bool
|
||||||
|
cursor akara.EID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the main menu scene
|
||||||
|
func (s *MouseCursorScene) Init(world *akara.World) {
|
||||||
|
s.World = world
|
||||||
|
|
||||||
|
s.Info("initializing ...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MouseCursorScene) boot() {
|
||||||
|
if !s.BaseScene.booted {
|
||||||
|
s.BaseScene.boot()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.createMouseCursor()
|
||||||
|
|
||||||
|
s.booted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MouseCursorScene) createMouseCursor() {
|
||||||
|
s.Info("creating mouse cursor")
|
||||||
|
s.cursor = s.Add.Sprite(0, 0, d2resource.CursorDefault, d2resource.PaletteUnits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the main menu scene
|
||||||
|
func (s *MouseCursorScene) Update() {
|
||||||
|
if s.Paused() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.booted {
|
||||||
|
s.boot()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.updateCursorPosition()
|
||||||
|
|
||||||
|
s.BaseScene.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MouseCursorScene) updateCursorPosition() {
|
||||||
|
spritePosition, found := s.GetPosition(s.cursor)
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cx, cy := s.systems.InputSystem.renderer.GetCursorPos()
|
||||||
|
|
||||||
|
spritePosition.Set(float64(cx), float64(cy))
|
||||||
|
}
|
4
go.sum
4
go.sum
@ -19,10 +19,6 @@ github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
|
|||||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gravestench/akara v0.0.0-20201119221449-924b47999403 h1:hoCEhoSD+4Hvg3xdbfbVleJXhyHqP/1jUa1QqexE1UQ=
|
|
||||||
github.com/gravestench/akara v0.0.0-20201119221449-924b47999403/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
|
|
||||||
github.com/gravestench/akara v0.0.0-20201122210148-a1ee8ea83994 h1:Wp+4kZ0Pkap2ueAkTrE22rk++3VZE8TsU1bewpnzmsM=
|
|
||||||
github.com/gravestench/akara v0.0.0-20201122210148-a1ee8ea83994/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
|
|
||||||
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b h1:Ngfdn7O3wXQBzbOLsL6vQ9G4F7utUiKjQqKnwHbY5uI=
|
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b h1:Ngfdn7O3wXQBzbOLsL6vQ9G4F7utUiKjQqKnwHbY5uI=
|
||||||
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
|
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
|
||||||
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
||||||
|
Loading…
Reference in New Issue
Block a user