1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-05 08:07:51 -05:00

Enhanced escape menu with options - d2gui bug remaining (#459)

* First improvements

Signed-off-by: William Claude <w.claude@thebeat.co>

* Make the menu more generic

Signed-off-by: William Claude <w.claude@thebeat.co>

* Handle mouse events

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove debug statement

Signed-off-by: William Claude <w.claude@thebeat.co>

* Handle left clicks better

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove unused file

Signed-off-by: William Claude <w.claude@thebeat.co>

* Handle titles in screens

Signed-off-by: William Claude <w.claude@thebeat.co>

* Improve the menu using layouts

Signed-off-by: William Claude <w.claude@thebeat.co>

* Add support for onOff labels

Signed-off-by: William Claude <w.claude@thebeat.co>

* Mutualise title creation

Signed-off-by: William Claude <w.claude@thebeat.co>

* Add gutter and mutualise things

Signed-off-by: William Claude <w.claude@thebeat.co>

* Improve menu, mutualise a lot of things and support animated sprites

Signed-off-by: William Claude <w.claude@thebeat.co>

* Use a cfg struct instead of independent handlers

Signed-off-by: William Claude <w.claude@thebeat.co>

* Fix hardcoded value

Signed-off-by: William Claude <w.claude@thebeat.co>

* Clean things a bit

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove unused property

Signed-off-by: William Claude <w.claude@thebeat.co>

* First support for hoverable elements

Signed-off-by: William Claude <w.claude@thebeat.co>

* Add support for label selection feedback

Signed-off-by: William Claude <w.claude@thebeat.co>

* Add support options

Signed-off-by: William Claude <w.claude@thebeat.co>

* Update print statement

Signed-off-by: William Claude <w.claude@thebeat.co>

* Update rendering and clean code

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove debug things

Signed-off-by: William Claude <w.claude@thebeat.co>

* Handle hovering

Signed-off-by: William Claude <w.claude@thebeat.co>

* Support enter key for labels

Signed-off-by: William Claude <w.claude@thebeat.co>

* Attach methods to layout

Signed-off-by: William Claude <w.claude@thebeat.co>

* Move things under EscapeMenu

Signed-off-by: William Claude <w.claude@thebeat.co>

* Some renaming

Signed-off-by: William Claude <w.claude@thebeat.co>

* Clean

Signed-off-by: William Claude <w.claude@thebeat.co>

* Set hovered element ID when using the mouse

Signed-off-by: William Claude <w.claude@thebeat.co>

* Clean

Signed-off-by: William Claude <w.claude@thebeat.co>

* Delete unused file

Signed-off-by: William Claude <w.claude@thebeat.co>

* Wire save & exit with a nasty hack

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove unused file

Signed-off-by: William Claude <w.claude@thebeat.co>

* Remove dead code

Signed-off-by: William Claude <w.claude@thebeat.co>

* Reorder the code a bit

Signed-off-by: William Claude <w.claude@thebeat.co>

* Rename hoverableElement into actionableElement

Signed-off-by: William Claude <w.claude@thebeat.co>

* Prevent regenerating the label if the text didn't change

Signed-off-by: William Claude <w.claude@thebeat.co>
This commit is contained in:
William 2020-06-25 22:27:16 +02:00 committed by GitHub
parent c64e9be78b
commit 48a193579f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 469 additions and 257 deletions

View File

@ -1,12 +1,15 @@
package d2gui
import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
type Label struct {
widgetBase
text string
font *d2asset.Font
surface d2render.Surface
}
@ -16,17 +19,8 @@ func createLabel(text string, fontStyle FontStyle) (*Label, error) {
return nil, err
}
width, height := font.GetTextMetrics(text)
surface, err := d2render.NewSurface(width, height, d2render.FilterNearest)
if err != nil {
return nil, err
}
if err := font.RenderText(text, surface); err != nil {
return nil, err
}
label := &Label{surface: surface}
label := &Label{font: font}
label.setText(text)
label.SetVisible(true)
return label, nil
@ -39,3 +33,28 @@ func (l *Label) render(target d2render.Surface) error {
func (l *Label) getSize() (int, int) {
return l.surface.GetSize()
}
func (l *Label) GetText() string {
return l.text
}
func (l *Label) SetText(text string) error {
if text == l.text {
return nil
}
return l.setText(text)
}
func (l *Label) setText(text string) error {
width, height := l.font.GetTextMetrics(text)
surface, err := d2render.NewSurface(width, height, d2render.FilterNearest)
if err != nil {
return err
}
if err := l.font.RenderText(text, surface); err != nil {
return err
}
l.surface = surface
l.text = text
return nil
}

View File

@ -103,6 +103,16 @@ func (l *Layout) AddSprite(imagePath, palettePath string) (*Sprite, error) {
return sprite, nil
}
func (l *Layout) AddAnimatedSprite(imagePath, palettePath string, direction AnimationDirection) (*AnimatedSprite, error) {
sprite, err := createAnimatedSprite(imagePath, palettePath, direction)
if err != nil {
return nil, err
}
l.entries = append(l.entries, &layoutEntry{widget: sprite})
return sprite, nil
}
func (l *Layout) AddLabel(text string, fontStyle FontStyle) (*Label, error) {
label, err := createLabel(text, fontStyle)
if err != nil {
@ -139,9 +149,10 @@ func (l *Layout) render(target d2render.Surface) error {
return err
}
if err := l.renderEntryDebug(entry, target); err != nil {
return err
}
// uncomment to see debug boxes
//if err := l.renderEntryDebug(entry, target); err != nil {
// return err
//}
}
return nil
@ -347,5 +358,7 @@ func (l *Layout) adjustEntryPlacement() {
case PositionTypeAbsolute:
entry.x, entry.y = entry.widget.getPosition()
}
entry.widget.setOffset(offsetX, offsetY)
}
}

View File

@ -5,6 +5,13 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
)
type AnimationDirection int
const (
DirectionForward AnimationDirection = 0
DirectionBackward = 1
)
type Sprite struct {
widgetBase
@ -15,6 +22,10 @@ type Sprite struct {
animation *d2asset.Animation
}
type AnimatedSprite struct {
*Sprite
}
func createSprite(imagePath, palettePath string) (*Sprite, error) {
animation, err := d2asset.LoadAnimation(imagePath, palettePath)
if err != nil {
@ -28,6 +39,34 @@ func createSprite(imagePath, palettePath string) (*Sprite, error) {
return sprite, nil
}
func createAnimatedSprite(imagePath, palettePath string, direction AnimationDirection) (*AnimatedSprite, error) {
animation, err := d2asset.LoadAnimation(imagePath, palettePath)
if err != nil {
return nil, err
}
sprite := &AnimatedSprite{
&Sprite{},
}
sprite.animation = animation
if direction == DirectionForward {
sprite.animation.PlayForward()
} else {
sprite.animation.PlayBackward()
}
sprite.animation.SetBlend(false)
sprite.SetVisible(true)
return sprite, nil
}
func (s *AnimatedSprite) render(target d2render.Surface) error {
_, frameHeight := s.animation.GetCurrentFrameSize()
target.PushTranslation(s.x, s.y-frameHeight)
defer target.Pop()
return s.animation.Render(target)
}
func (s *Sprite) SetSegmented(segmentsX, segmentsY, frameOffset int) {
s.segmentsX = segmentsX
s.segmentsY = segmentsY

View File

@ -21,6 +21,8 @@ type widget interface {
onMouseButtonClick(event d2input.MouseEvent) bool
getPosition() (int, int)
getOffset() (int, int)
setOffset(x, y int)
getSize() (int, int)
getLayer() int
isVisible() bool
@ -34,6 +36,9 @@ type widgetBase struct {
visible bool
expanding bool
offsetX int
offsetY int
mouseEnterHandler MouseMoveHandler
mouseLeaveHandler MouseMoveHandler
mouseClickHandler MouseHandler
@ -44,6 +49,23 @@ func (w *widgetBase) SetPosition(x, y int) {
w.y = y
}
func (w *widgetBase) GetPosition() (int, int) {
return w.x, w.y
}
func (w *widgetBase) GetOffset() (int, int) {
return w.offsetX, w.offsetY
}
func (w *widgetBase) getOffset() (int, int) {
return w.offsetX, w.offsetY
}
func (w *widgetBase) setOffset(x, y int) {
w.offsetX = x
w.offsetY = y
}
func (w *widgetBase) SetLayer(layer int) {
w.layer = layer
}

View File

@ -1,277 +1,412 @@
package d2gamescreen
import (
"log"
"fmt"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
type EscapeOption int
// TODO: fix pentagram
type (
layoutID int
optionID int
)
const (
EscapeOptions = EscapeOption(iota)
EscapeSaveExit = EscapeOption(iota)
EscapeReturn = EscapeOption(iota)
// UI
labelGutter = 10
sidePanelsSize = 80
pentSize = 52
menuSize = 500
// layouts
noLayoutID layoutID = -2
saveLayoutID = -1
mainLayoutID = 0
optionsLayoutID = 1
soundOptionsLayoutID = 2
videoOptionsLayoutID = 3
automapOptionsLayoutID = 4
configureControlsLayoutID = 5
// options
optAudioSoundVolume optionID = 0 // audio
optAudioMusicVolume = 1
optAudio3dSound = 2
optAudioHardwareAcceleration = 3
optAudioEnvEffects = 4
optAudioNpcSpeech = 5
optVideoResolution = 6 // video
optVideoLightingQuality = 7
optVideoBlendedShadows = 8
optVideoPerspective = 9
optVideoGamma = 10
optVideoContrast = 11
optAutomapSize = 12 // automap
optAutomapFade = 13
optAutomapCenterWhenCleared = 14
optAutomapShowParty = 15
optAutomapShowNames = 16
)
type mouseRegion int
const (
regAbove = mouseRegion(iota)
regIn = mouseRegion(iota)
regBelow = mouseRegion(iota)
)
// EscapeMenu is the overlay menu shown in-game when pressing Escape
type EscapeMenu struct {
current EscapeOption
isOpen bool
labels []d2ui.Label
pentLeft *d2ui.Sprite
pentRight *d2ui.Sprite
selectSound d2audio.SoundEffect
currentLayout layoutID
// pre-computations
pentWidth int
pentHeight int
textHeight int
// leftPent and rightPent are generated once and shared between the layouts
leftPent *d2gui.AnimatedSprite
rightPent *d2gui.AnimatedSprite
layouts []*layout
}
type layout struct {
*d2gui.Layout
leftPent *d2gui.AnimatedSprite
rightPent *d2gui.AnimatedSprite
currentEl int
actionableElements []actionableElement
}
func (l *layout) Trigger() {
// noop
}
type showLayoutLabel struct {
*d2gui.Label
target layoutID
showLayout func(id layoutID)
}
func (l *showLayoutLabel) Trigger() {
l.showLayout(l.target)
}
type enumLabel struct {
*d2gui.Layout
textChangingLabel *d2gui.Label
optionID optionID
values []string
current int
playSound func()
updateValue func(optID optionID, value string)
}
func (l *enumLabel) Trigger() {
l.playSound()
next := (l.current + 1) % len(l.values)
l.current = next
l.textChangingLabel.SetText(l.values[l.current])
l.updateValue(l.optionID, l.values[l.current])
}
type actionableElement interface {
GetOffset() (int, int)
Trigger()
}
// Creates an default instance of the EscapeMenu
func NewEscapeMenu() *EscapeMenu {
return &EscapeMenu{
labels: make([]d2ui.Label, 0),
m := &EscapeMenu{}
m.layouts = []*layout{
mainLayoutID: m.newMainLayout(),
optionsLayoutID: m.newOptionsLayout(),
soundOptionsLayoutID: m.newSoundOptionsLayout(),
videoOptionsLayoutID: m.newVideoOptionsLayout(),
automapOptionsLayoutID: m.newAutomapOptionsLayout(),
configureControlsLayoutID: m.newConfigureControlsLayout(),
}
return m
}
func (m *EscapeMenu) newMainLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addBigSelectionLabel(l, "OPTIONS", optionsLayoutID)
m.addBigSelectionLabel(l, "SAVE AND EXIT GAME", saveLayoutID)
m.addBigSelectionLabel(l, "RETURN TO GAME", noLayoutID)
})
}
func (m *EscapeMenu) newOptionsLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addBigSelectionLabel(l, "SOUND OPTIONS", soundOptionsLayoutID)
m.addBigSelectionLabel(l, "VIDEO OPTIONS", videoOptionsLayoutID)
m.addBigSelectionLabel(l, "AUTOMAP OPTIONS", automapOptionsLayoutID)
m.addBigSelectionLabel(l, "CONFIGURE CONTROLS", configureControlsLayoutID)
m.addBigSelectionLabel(l, "PREVIOUS MENU", mainLayoutID)
})
}
func (m *EscapeMenu) newSoundOptionsLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addTitle(l, "SOUND OPTIONS")
m.addEnumLabel(l, optAudioSoundVolume, "SOUND VOLUME", []string{"TODO"})
m.addEnumLabel(l, optAudioMusicVolume, "MUSIC VOLUME", []string{"TODO"})
m.addEnumLabel(l, optAudio3dSound, "3D BIAS", []string{"TODO"})
m.addEnumLabel(l, optAudioHardwareAcceleration, "HARDWARE ACCELERATION", []string{"ON", "OFF"})
m.addEnumLabel(l, optAudioEnvEffects, "ENVIRONMENTAL EFFECTS", []string{"ON", "OFF"})
m.addEnumLabel(l, optAudioNpcSpeech, "NPC SPEECH", []string{"AUDIO AND TEXT", "AUDIO ONLY", "TEXT ONLY"})
m.addPreviousMenuLabel(l, optionsLayoutID)
})
}
func (m *EscapeMenu) newVideoOptionsLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addTitle(l, "VIDEO OPTIONS")
m.addEnumLabel(l, optVideoResolution, "VIDEO RESOLUTION", []string{"800X600", "1024X768"})
m.addEnumLabel(l, optVideoLightingQuality, "LIGHTING QUALITY", []string{"LOW", "HIGH"})
m.addEnumLabel(l, optVideoBlendedShadows, "BLENDED SHADOWS", []string{"ON", "OFF"})
m.addEnumLabel(l, optVideoPerspective, "PERSPECTIVE", []string{"ON", "OFF"})
m.addEnumLabel(l, optVideoGamma, "GAMMA", []string{"TODO"})
m.addEnumLabel(l, optVideoContrast, "CONTRAST", []string{"TODO"})
m.addPreviousMenuLabel(l, optionsLayoutID)
})
}
func (m *EscapeMenu) newAutomapOptionsLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addTitle(l, "AUTOMAP OPTIONS")
m.addEnumLabel(l, optAutomapSize, "AUTOMAP SIZE", []string{"FULL SCREEN"})
m.addEnumLabel(l, optAutomapFade, "FADE", []string{"YES", "NO"})
m.addEnumLabel(l, optAutomapCenterWhenCleared, "CENTER WHEN CLEARED", []string{"YES", "NO"})
m.addEnumLabel(l, optAutomapShowParty, "SHOW PARTY", []string{"YES", "NO"})
m.addEnumLabel(l, optAutomapShowNames, "SHOW NAMES", []string{"YES", "NO"})
m.addPreviousMenuLabel(l, optionsLayoutID)
})
}
func (m *EscapeMenu) newConfigureControlsLayout() *layout {
return m.wrapLayout(func(l *layout) {
m.addTitle(l, "CONFIGURE CONTROLS")
m.addPreviousMenuLabel(l, optionsLayoutID)
})
}
func (m *EscapeMenu) wrapLayout(fn func(*layout)) *layout {
wrapper := d2gui.CreateLayout(d2gui.PositionTypeHorizontal)
wrapper.SetVerticalAlign(d2gui.VerticalAlignMiddle)
wrapper.AddSpacerDynamic()
center := wrapper.AddLayout(d2gui.PositionTypeHorizontal)
center.SetSize(menuSize, 0)
left := center.AddLayout(d2gui.PositionTypeHorizontal)
left.SetSize(sidePanelsSize, 0)
leftPent, _ := left.AddAnimatedSprite(d2resource.PentSpin, d2resource.PaletteUnits, d2gui.DirectionForward)
m.leftPent = leftPent
// wrap the base layout so we can pass values around more easily
base := &layout{}
baseLayout := center.AddLayout(d2gui.PositionTypeVertical)
baseLayout.SetHorizontalAlign(d2gui.HorizontalAlignCenter)
base.Layout = baseLayout
fn(base)
right := center.AddLayout(d2gui.PositionTypeHorizontal)
// For some reason, aligning the panel to the right won't align the pentagram, so we need to add a static spacer.
right.AddSpacerStatic(sidePanelsSize-pentSize, 0)
right.SetSize(sidePanelsSize, 0)
rightPent, _ := right.AddAnimatedSprite(d2resource.PentSpin, d2resource.PaletteUnits, d2gui.DirectionBackward)
m.rightPent = rightPent
wrapper.AddSpacerDynamic()
return &layout{
Layout: wrapper,
leftPent: leftPent,
rightPent: rightPent,
actionableElements: base.actionableElements,
}
}
func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool {
switch event.Key {
case d2input.KeyEscape:
m.Toggle()
case d2input.KeyUp:
m.OnUpKey()
case d2input.KeyDown:
m.OnDownKey()
case d2input.KeyEnter:
m.OnEnterKey()
default:
return false
}
return false
func (m *EscapeMenu) addTitle(l *layout, text string) {
l.AddLabel(text, d2gui.FontStyle42Units)
l.AddSpacerStatic(10, labelGutter)
}
func (m *EscapeMenu) OnLoad() error {
d2input.BindHandler(m)
m.labels = []d2ui.Label{
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
func (m *EscapeMenu) addBigSelectionLabel(l *layout, text string, targetLayout layoutID) {
guiLabel, _ := l.AddLabel(text, d2gui.FontStyle42Units)
label := &showLayoutLabel{Label: guiLabel, target: targetLayout, showLayout: m.showLayout}
label.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger()
})
elID := len(l.actionableElements)
label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID)
})
l.AddSpacerStatic(10, labelGutter)
l.actionableElements = append(l.actionableElements, label)
}
m.labels[EscapeOptions].SetText("OPTIONS")
m.labels[EscapeSaveExit].SetText("SAVE AND EXIT GAME")
m.labels[EscapeReturn].SetText("RETURN TO GAME")
for i := range m.labels {
m.labels[i].Alignment = d2ui.LabelAlignCenter
func (m *EscapeMenu) addPreviousMenuLabel(l *layout, targetLayout layoutID) {
l.AddSpacerStatic(10, labelGutter)
guiLabel, _ := l.AddLabel("PREVIOUS MENU", d2gui.FontStyle30Units)
label := &showLayoutLabel{Label: guiLabel, target: targetLayout, showLayout: m.showLayout}
label.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger()
})
elID := len(l.actionableElements)
label.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID)
})
l.actionableElements = append(l.actionableElements, label)
}
animation, _ := d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteUnits)
m.pentLeft, _ = d2ui.LoadSprite(animation)
m.pentLeft.SetBlend(false)
m.pentLeft.PlayBackward()
m.pentRight, _ = d2ui.LoadSprite(animation)
m.pentRight.SetBlend(false)
m.pentRight.PlayForward()
m.pentWidth, m.pentHeight = m.pentLeft.GetFrameBounds()
_, m.textHeight = m.labels[EscapeOptions].GetSize()
func (m *EscapeMenu) addEnumLabel(l *layout, optID optionID, text string, values []string) {
guiLayout := l.AddLayout(d2gui.PositionTypeHorizontal)
layout := &layout{Layout: guiLayout}
layout.SetSize(menuSize, 0)
layout.AddLabel(text, d2gui.FontStyle30Units)
elID := len(l.actionableElements)
layout.SetMouseEnterHandler(func(_ d2input.MouseMoveEvent) {
m.onHoverElement(elID)
})
layout.AddSpacerDynamic()
guiLabel, _ := layout.AddLabel(values[0], d2gui.FontStyle30Units)
label := &enumLabel{
Layout: guiLayout,
textChangingLabel: guiLabel,
optionID: optID,
values: values,
current: 0,
playSound: m.playSound,
updateValue: m.onUpdateValue,
}
layout.SetMouseClickHandler(func(_ d2input.MouseEvent) {
label.Trigger()
})
l.AddSpacerStatic(10, labelGutter)
l.actionableElements = append(l.actionableElements, label)
}
func (m *EscapeMenu) OnLoad() {
m.selectSound, _ = d2audio.LoadSoundEffect(d2resource.SFXCursorSelect)
return nil
}
func (m *EscapeMenu) Render(target d2render.Surface) error {
func (m *EscapeMenu) OnEscKey() {
if !m.isOpen {
return nil
m.Open()
return
}
tw, _ := target.GetSize()
// X Position of the mid-render target.
midX := tw / 2
// Y Coordinates for the center of the first option
choiceStart := 210
// Y Delta, in pixels, between center of choices
choiceDx := 50
// X Delta, in pixels, between center of pentagrams
betwPentDist := 275
for i := range m.labels {
m.labels[i].SetPosition(midX, choiceStart+i*choiceDx-m.textHeight/2)
m.labels[i].Render(target)
switch m.currentLayout {
case optionsLayoutID:
m.setLayout(mainLayoutID)
return
case soundOptionsLayoutID,
videoOptionsLayoutID,
automapOptionsLayoutID,
configureControlsLayoutID:
m.setLayout(optionsLayoutID)
return
}
m.pentLeft.SetPosition(midX-(betwPentDist+m.pentWidth/2), choiceStart+int(m.current)*choiceDx+m.pentHeight/2)
m.pentRight.SetPosition(midX+(betwPentDist-m.pentWidth/2), choiceStart+int(m.current)*choiceDx+m.pentHeight/2)
m.pentLeft.Render(target)
m.pentRight.Render(target)
return nil
}
func (m *EscapeMenu) Advance(elapsed float64) error {
if !m.isOpen {
return nil
}
m.pentLeft.Advance(elapsed)
m.pentRight.Advance(elapsed)
return nil
m.Close()
}
func (m *EscapeMenu) IsOpen() bool {
return m.isOpen
}
func (m *EscapeMenu) Toggle() {
if !m.isOpen {
m.reset()
}
m.isOpen = !m.isOpen
func (m *EscapeMenu) Close() {
m.isOpen = false
d2gui.SetLayout(nil)
}
func (m *EscapeMenu) reset() {
m.current = EscapeOptions
func (m *EscapeMenu) Open() {
m.isOpen = true
m.setLayout(mainLayoutID)
}
func (m *EscapeMenu) OnUpKey() {
switch m.current {
case EscapeSaveExit:
m.current = EscapeOptions
case EscapeReturn:
m.current = EscapeSaveExit
}
}
func (m *EscapeMenu) OnDownKey() {
switch m.current {
case EscapeOptions:
m.current = EscapeSaveExit
case EscapeSaveExit:
m.current = EscapeReturn
}
}
func (m *EscapeMenu) OnEnterKey() {
m.selectCurrent()
}
// Moves current selection marker to closes option to mouse.
func (m *EscapeMenu) OnMouseMove(event d2input.MouseMoveEvent) bool {
if !m.isOpen {
return false
}
lbl := &m.labels[EscapeSaveExit]
reg := m.toMouseRegion(event.HandlerEvent, lbl)
switch reg {
case regAbove:
m.current = EscapeOptions
case regIn:
m.current = EscapeSaveExit
case regBelow:
m.current = EscapeReturn
}
return false
}
// Allows user to click on menu options in Y coord. of mouse is over label.
func (m *EscapeMenu) OnMouseButtonDown(event d2input.MouseEvent) bool {
if !m.isOpen {
return false
}
lbl := &m.labels[EscapeOptions]
if m.toMouseRegion(event.HandlerEvent, lbl) == regIn {
m.current = EscapeOptions
m.selectCurrent()
return false
}
lbl = &m.labels[EscapeSaveExit]
if m.toMouseRegion(event.HandlerEvent, lbl) == regIn {
m.current = EscapeSaveExit
m.selectCurrent()
return false
}
lbl = &m.labels[EscapeReturn]
if m.toMouseRegion(event.HandlerEvent, lbl) == regIn {
m.current = EscapeReturn
m.selectCurrent()
return false
}
return false
}
func (m *EscapeMenu) selectCurrent() {
switch m.current {
case EscapeOptions:
m.onOptions()
m.selectSound.Play()
case EscapeSaveExit:
m.onSaveAndExit()
m.selectSound.Play()
case EscapeReturn:
m.onReturnToGame()
func (m *EscapeMenu) playSound() {
m.selectSound.Play()
}
func (m *EscapeMenu) showLayout(id layoutID) {
m.playSound()
if id == noLayoutID {
m.Close()
return
}
// User clicked on "OPTIONS"
func (m *EscapeMenu) onOptions() error {
log.Println("OPTIONS Clicked from Escape Menu")
return nil
}
// User clicked on "SAVE AND EXIT"
func (m *EscapeMenu) onSaveAndExit() error {
log.Println("SAVE AND EXIT GAME Clicked from Escape Menu")
if id == saveLayoutID {
mainMenu := CreateMainMenu()
mainMenu.SetScreenMode(ScreenModeMainMenu)
d2screen.SetNextScreen(mainMenu)
return nil
return
}
// User clicked on "RETURN TO GAME"
func (m *EscapeMenu) onReturnToGame() error {
m.Toggle()
return nil
m.setLayout(id)
}
// Where is the Y coordinate of the mouse compared to this label.
func (m *EscapeMenu) toMouseRegion(event d2input.HandlerEvent, lbl *d2ui.Label) mouseRegion {
_, h := lbl.GetSize()
y := lbl.Y
my := event.Y
func (m *EscapeMenu) onHoverElement(id int) {
_, y := m.layouts[m.currentLayout].actionableElements[id].GetOffset()
m.layouts[m.currentLayout].currentEl = id
if my < y {
return regAbove
x, _ := m.leftPent.GetPosition()
m.leftPent.SetPosition(x, y+10)
x, _ = m.rightPent.GetPosition()
m.rightPent.SetPosition(x, y+10)
return
}
if my > (y + h) {
return regBelow
func (m *EscapeMenu) onUpdateValue(optID optionID, value string) {
fmt.Println(fmt.Sprintf("updating value %d with %s", optID, value))
}
return regIn
func (m *EscapeMenu) setLayout(id layoutID) {
m.leftPent = m.layouts[id].leftPent
m.rightPent = m.layouts[id].rightPent
m.currentLayout = id
m.layouts[id].currentEl = 0
d2gui.SetLayout(m.layouts[id].Layout)
}
func (m *EscapeMenu) onUpKey() {
if !m.isOpen {
return
}
if m.layouts[m.currentLayout].currentEl == 0 {
return
}
m.layouts[m.currentLayout].currentEl--
m.onHoverElement(m.layouts[m.currentLayout].currentEl)
}
func (m *EscapeMenu) onDownKey() {
if !m.isOpen {
return
}
if m.layouts[m.currentLayout].currentEl == len(m.layouts[m.currentLayout].actionableElements)-1 {
return
}
m.layouts[m.currentLayout].currentEl++
m.onHoverElement(m.layouts[m.currentLayout].currentEl)
}
func (m *EscapeMenu) onEnterKey() {
if !m.isOpen {
return
}
m.layouts[m.currentLayout].actionableElements[m.layouts[m.currentLayout].currentEl].Trigger()
}
func (m *EscapeMenu) OnKeyDown(event d2input.KeyEvent) bool {
switch event.Key {
case d2input.KeyEscape:
m.OnEscKey()
case d2input.KeyUp:
m.onUpKey()
case d2input.KeyDown:
m.onDownKey()
case d2input.KeyEnter:
m.onEnterKey()
default:
return false
}
return false
}

View File

@ -19,9 +19,6 @@ import (
)
type Game struct {
//pentSpinLeft *d2ui.Sprite
//pentSpinRight *d2ui.Sprite
//testLabel d2ui.Label
gameClient *d2client.GameClient
mapRenderer *d2maprenderer.MapRenderer
gameControls *d2player.GameControls // TODO: Hack
@ -69,10 +66,6 @@ func (v *Game) Render(screen d2render.Surface) error {
v.gameControls.Render(screen)
}
if v.escapeMenu != nil {
v.escapeMenu.Render(screen)
}
return nil
}
@ -129,10 +122,6 @@ func (v *Game) Advance(tickTime float64) error {
}
}
if v.escapeMenu.IsOpen() {
v.escapeMenu.Advance(tickTime)
}
// Update the camera to focus on the player
if v.localPlayer != nil && !v.gameControls.FreeCam {
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)

View File

@ -216,7 +216,6 @@ func (g *GameControls) OnMouseMove(event d2input.MouseMoveEvent) bool {
}
func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
mx, my := event.X, event.Y
for i := range g.actionableRegions {
// If click is on a game control element
@ -501,10 +500,6 @@ func (g *GameControls) HideZoneChangeTextAfter(delay float64) {
})
}
// func (g *GameControls) InEscapeMenu() bool {
// return g != nil && g.escapeMenu != nil && g.escapeMenu.IsOpen()
// }
// Handles what to do when an actionable is hovered
func (g *GameControls) onHoverActionable(item ActionableType) {
switch item {