1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-12-27 20:46:32 -05:00

Added Menu on Escape Key Press (#353) (#371)

- New menu in d2player.
 - Added OnMouseMove and Advance to GameControl.
 - Pause the MapEngine when single player.
This commit is contained in:
Maxime Lavigne (malavv) 2020-06-21 16:06:52 -04:00 committed by GitHub
parent 7ba3cb702d
commit 8bdbef60ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 255 additions and 18 deletions

View File

@ -59,7 +59,13 @@ func (v *Game) Render(screen d2render.Surface) error {
}
func (v *Game) Advance(tickTime float64) error {
v.gameClient.MapEngine.Advance(tickTime) // TODO: Hack
if !v.gameControls.InEscapeMenu() || len(v.gameClient.Players) != 1 {
v.gameClient.MapEngine.Advance(tickTime) // TODO: Hack
}
if v.gameControls != nil {
v.gameControls.Advance(tickTime)
}
v.ticksSinceLevelCheck += tickTime
if v.ticksSinceLevelCheck > 1.0 {

View File

@ -0,0 +1,223 @@
package d2player
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
type EscapeOption int
const (
EscapeOptions = EscapeOption(iota)
EscapeSaveExit = EscapeOption(iota)
EscapeReturn = EscapeOption(iota)
)
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
// pre-computations
pentWidth int
pentHeight int
textHeight int
}
// Creates an default instance of the EscapeMenu
func NewEscapeMenu() *EscapeMenu {
return &EscapeMenu{
labels: make([]d2ui.Label, 0),
}
}
// ScreenLoadHandler
func (m *EscapeMenu) OnLoad() error {
m.labels = []d2ui.Label{
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteSky),
}
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()
return nil
}
// ScreenUnloadHandler
func (m *EscapeMenu) OnUnload() error {
return nil
}
// ScreenRenderHandler
func (m *EscapeMenu) Render(target d2render.Surface) error {
if !m.isOpen {
return nil
}
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)
}
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
}
// ScreenAdvanceHandler
func (m *EscapeMenu) Advance(elapsed float64) error {
if !m.isOpen {
return nil
}
m.pentLeft.Advance(elapsed)
m.pentRight.Advance(elapsed)
return nil
}
func (m *EscapeMenu) IsOpen() bool {
return m.isOpen
}
func (m *EscapeMenu) Toggle() {
m.isOpen = !m.isOpen
}
func (m *EscapeMenu) Open() {
m.isOpen = true
}
func (m *EscapeMenu) Close() {
m.isOpen = false
}
// 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.onOptions()
return false
}
lbl = &m.labels[EscapeSaveExit]
if m.toMouseRegion(event.HandlerEvent, lbl) == regIn {
m.onSaveAndExit()
return false
}
lbl = &m.labels[EscapeReturn]
if m.toMouseRegion(event.HandlerEvent, lbl) == regIn {
m.onReturnToGame()
return false
}
return false
}
// 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")
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
}

View File

@ -28,6 +28,7 @@ type GameControls struct {
mapRenderer *d2map.MapRenderer
inventory *Inventory
heroStats *HeroStats
escapeMenu *EscapeMenu
inputListener InputCallbackListener
// UI
@ -49,42 +50,37 @@ func NewGameControls(hero *d2map.Player, mapEngine *d2map.MapEngine, mapRenderer
mapRenderer: mapRenderer,
inventory: NewInventory(),
heroStats: NewHeroStats(),
escapeMenu: NewEscapeMenu(),
}
}
func (g *GameControls) OnKeyDown(event d2input.KeyEvent) bool {
if event.Key == d2input.KeyEscape {
g.escapeMenu.Toggle()
return true
}
if event.Key == d2input.KeyI {
g.inventory.Toggle()
g.updateLayout()
return true
}
if event.Key == d2input.KeyC {
g.heroStats.Toggle()
g.updateLayout()
return true
}
return false
}
func (g *GameControls) updateLayout() {
isRightPanelOpen := false
isLeftPanelOpen := false
// todo : add same logic when adding quest log and skill tree
isRightPanelOpen = g.inventory.isOpen || isRightPanelOpen
isLeftPanelOpen = g.heroStats.isOpen || isLeftPanelOpen
if isRightPanelOpen == isLeftPanelOpen {
g.mapRenderer.ViewportDefault()
} else if isRightPanelOpen == true {
g.mapRenderer.ViewportToLeft()
} else {
g.mapRenderer.ViewportToRight()
}
func (g *GameControls) OnMouseMove(event d2input.MouseMoveEvent) bool {
g.escapeMenu.OnMouseMove(event)
return false
}
func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
if g.escapeMenu.IsOpen() {
return g.escapeMenu.OnMouseButtonDown(event)
}
px, py := g.mapRenderer.ScreenToWorld(event.X, event.Y)
px = float64(int(px*10)) / 10.0
py = float64(int(py*10)) / 10.0
@ -136,12 +132,20 @@ func (g *GameControls) Load() {
g.inventory.Load()
g.heroStats.Load()
g.escapeMenu.OnLoad()
}
// ScreenAdvanceHandler
func (g *GameControls) Advance(elapsed float64) error {
g.escapeMenu.Advance(elapsed)
return nil
}
// TODO: consider caching the panels to single image that is reused.
func (g *GameControls) Render(target d2render.Surface) {
g.inventory.Render(target)
g.heroStats.Render(target)
g.escapeMenu.Render(target)
width, height := target.GetSize()
offset := 0
@ -218,3 +222,7 @@ func (g *GameControls) Render(target d2render.Surface) {
g.globeSprite.Render(target)
}
func (g *GameControls) InEscapeMenu() bool {
return g != nil && g.escapeMenu != nil && g.escapeMenu.IsOpen()
}