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:
parent
c64e9be78b
commit
48a193579f
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
isOpen bool
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) 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() {
|
||||
func (m *EscapeMenu) Close() {
|
||||
m.isOpen = false
|
||||
d2gui.SetLayout(nil)
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) Open() {
|
||||
m.isOpen = true
|
||||
m.setLayout(mainLayoutID)
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) playSound() {
|
||||
m.selectSound.Play()
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) showLayout(id layoutID) {
|
||||
m.playSound()
|
||||
|
||||
if id == noLayoutID {
|
||||
m.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if id == saveLayoutID {
|
||||
mainMenu := CreateMainMenu()
|
||||
d2screen.SetNextScreen(mainMenu)
|
||||
return
|
||||
}
|
||||
|
||||
m.setLayout(id)
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) onHoverElement(id int) {
|
||||
_, y := m.layouts[m.currentLayout].actionableElements[id].GetOffset()
|
||||
m.layouts[m.currentLayout].currentEl = id
|
||||
|
||||
x, _ := m.leftPent.GetPosition()
|
||||
m.leftPent.SetPosition(x, y+10)
|
||||
x, _ = m.rightPent.GetPosition()
|
||||
m.rightPent.SetPosition(x, y+10)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) onUpdateValue(optID optionID, value string) {
|
||||
fmt.Println(fmt.Sprintf("updating value %d with %s", optID, value))
|
||||
}
|
||||
|
||||
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 {
|
||||
m.reset()
|
||||
return
|
||||
}
|
||||
m.isOpen = !m.isOpen
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) reset() {
|
||||
m.current = EscapeOptions
|
||||
}
|
||||
|
||||
func (m *EscapeMenu) OnUpKey() {
|
||||
switch m.current {
|
||||
case EscapeSaveExit:
|
||||
m.current = EscapeOptions
|
||||
case EscapeReturn:
|
||||
m.current = EscapeSaveExit
|
||||
if m.layouts[m.currentLayout].currentEl == 0 {
|
||||
return
|
||||
}
|
||||
m.layouts[m.currentLayout].currentEl--
|
||||
m.onHoverElement(m.layouts[m.currentLayout].currentEl)
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
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()
|
||||
m.selectSound.Play()
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
mainMenu := CreateMainMenu()
|
||||
mainMenu.SetScreenMode(ScreenModeMainMenu)
|
||||
d2screen.SetNextScreen(mainMenu)
|
||||
return nil
|
||||
}
|
||||
|
||||
// User clicked on "RETURN TO GAME"
|
||||
func (m *EscapeMenu) onReturnToGame() error {
|
||||
m.Toggle()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
if my < y {
|
||||
return regAbove
|
||||
}
|
||||
if my > (y + h) {
|
||||
return regBelow
|
||||
}
|
||||
return regIn
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user