2019-12-16 11:04:39 -05:00
|
|
|
package d2player
|
|
|
|
|
|
|
|
import (
|
2020-08-24 15:50:33 -04:00
|
|
|
"fmt"
|
2020-06-30 12:43:13 -04:00
|
|
|
"image"
|
2020-06-23 12:28:05 -04:00
|
|
|
"log"
|
2020-06-25 00:39:09 -04:00
|
|
|
"math"
|
2020-09-24 19:28:14 -04:00
|
|
|
"strings"
|
2020-06-22 15:55:32 -04:00
|
|
|
"time"
|
|
|
|
|
2020-09-24 19:28:14 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player/help"
|
|
|
|
|
2020-08-25 09:10:26 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-07-22 15:03:03 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
2020-09-20 11:55:44 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
2020-07-22 15:03:03 -04:00
|
|
|
|
2020-06-28 21:40:52 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
2020-01-26 00:39:13 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
2020-02-01 18:55:56 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
2020-06-21 18:40:37 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer"
|
2020-02-01 18:55:56 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
2019-12-16 11:04:39 -05:00
|
|
|
)
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// Panel represents the panel at the bottom of the game screen
|
2019-12-28 23:32:53 -05:00
|
|
|
type Panel interface {
|
|
|
|
IsOpen() bool
|
|
|
|
Toggle()
|
|
|
|
Open()
|
|
|
|
Close()
|
|
|
|
}
|
|
|
|
|
2020-07-22 15:03:03 -04:00
|
|
|
const (
|
2020-08-11 18:01:33 -04:00
|
|
|
expBarWidth = 120.0
|
|
|
|
staminaBarWidth = 102.0
|
2020-10-24 22:52:26 -04:00
|
|
|
staminaBarHeight = 19.0
|
2020-08-11 18:01:33 -04:00
|
|
|
globeHeight = 80
|
|
|
|
globeWidth = 80
|
|
|
|
hoverLabelOuterPad = 5
|
2020-10-25 13:15:28 -04:00
|
|
|
mouseBtnActionsThreshold = 0.25
|
|
|
|
percentStaminaBarLow = 0.25
|
2020-07-22 15:03:03 -04:00
|
|
|
)
|
2020-02-22 20:44:30 -05:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
const (
|
|
|
|
// Since they require special handling, not considering (1) globes, (2) content of the mini panel, (3) belt
|
|
|
|
leftSkill actionableType = iota
|
|
|
|
newStats
|
|
|
|
xp
|
|
|
|
walkRun
|
|
|
|
stamina
|
|
|
|
miniPnl
|
|
|
|
newSkills
|
|
|
|
rightSkill
|
|
|
|
hpGlobe
|
|
|
|
manaGlobe
|
|
|
|
miniPanelCharacter
|
|
|
|
miniPanelInventory
|
|
|
|
miniPanelSkillTree
|
|
|
|
miniPanelAutomap
|
|
|
|
miniPanelMessageLog
|
|
|
|
miniPanelQuestLog
|
|
|
|
miniPanelGameMenu
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
leftSkillX,
|
|
|
|
leftSkillY,
|
|
|
|
leftSkillWidth,
|
|
|
|
leftSkillHeight = 115, 550, 50, 50
|
|
|
|
|
|
|
|
newStatsX,
|
|
|
|
newStatsY,
|
|
|
|
newStatsWidth,
|
|
|
|
newStatsHeight = 206, 563, 30, 30
|
|
|
|
|
|
|
|
xpX,
|
|
|
|
xpY,
|
|
|
|
xpWidth,
|
|
|
|
xpHeight = 253, 560, 125, 5
|
|
|
|
|
|
|
|
walkRunX,
|
|
|
|
walkRunY,
|
|
|
|
walkRunWidth,
|
|
|
|
walkRunHeight = 255, 573, 17, 20
|
|
|
|
|
|
|
|
staminaX,
|
|
|
|
staminaY,
|
|
|
|
staminaWidth,
|
|
|
|
staminaHeight = 273, 573, 105, 20
|
|
|
|
|
|
|
|
miniPnlX,
|
|
|
|
miniPnlY,
|
|
|
|
miniPnlWidth,
|
|
|
|
miniPnlHeight = 393, 563, 12, 23
|
|
|
|
|
|
|
|
newSkillsX,
|
|
|
|
newSkillsY,
|
|
|
|
newSkillsWidth,
|
|
|
|
newSkillsHeight = 562, 563, 30, 30
|
|
|
|
|
|
|
|
rightSkillX,
|
|
|
|
rightSkillY,
|
|
|
|
rightSkillWidth,
|
|
|
|
rightSkillHeight = 634, 550, 50, 50
|
|
|
|
|
|
|
|
hpGlobeX,
|
|
|
|
hpGlobeY,
|
|
|
|
hpGlobeWidth,
|
|
|
|
hpGlobeHeight = 30, 525, 80, 60
|
|
|
|
|
|
|
|
manaGlobeX,
|
|
|
|
manaGlobeY,
|
|
|
|
manaGlobeWidth,
|
|
|
|
manaGlobeHeight = 695, 525, 80, 60
|
|
|
|
|
|
|
|
miniPanelCharacterX,
|
|
|
|
miniPanelCharacterY,
|
|
|
|
miniPanelCharacterWidth,
|
|
|
|
miniPanelCharacterHeight = 324, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelInventoryX,
|
|
|
|
miniPanelInventoryY,
|
|
|
|
miniPanelInventoryWidth,
|
|
|
|
miniPanelInventoryHeight = 346, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelSkillTreeX,
|
|
|
|
miniPanelSkillTreeY,
|
|
|
|
miniPanelSkillTreeWidth,
|
|
|
|
miniPanelSkillTreeHeight = 368, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelAutomapX,
|
|
|
|
miniPanelAutomapY,
|
|
|
|
miniPanelAutomapWidth,
|
|
|
|
miniPanelAutomapHeight = 390, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelMessageLogX,
|
|
|
|
miniPanelMessageLogY,
|
|
|
|
miniPanelMessageLogWidth,
|
|
|
|
miniPanelMessageLogHeight = 412, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelQuestLogX,
|
|
|
|
miniPanelQuestLogY,
|
|
|
|
miniPanelQuestLogWidth,
|
|
|
|
miniPanelQuestLogHeight = 434, 528, 22, 26
|
|
|
|
|
|
|
|
miniPanelGameMenuX,
|
|
|
|
miniPanelGameMenuY,
|
|
|
|
miniPanelGameMenuWidth,
|
|
|
|
miniPanelGameMenuHeight = 456, 528, 22, 26
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
hpLabelX = 15
|
|
|
|
hpLabelY = 487
|
|
|
|
|
|
|
|
manaLabelX = 785
|
|
|
|
manaLabelY = 487
|
2020-10-26 18:55:13 -04:00
|
|
|
|
|
|
|
staminaExperienceY = 535
|
2020-10-24 22:52:26 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
runButtonX = 255
|
|
|
|
runButtonY = 570
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
frameMenuButton = 2
|
|
|
|
frameHealthStatus = 0
|
|
|
|
frameManaStatus = 1
|
|
|
|
frameNewStatsSelector = 1
|
|
|
|
frameStamina = 2
|
|
|
|
framePotions = 3
|
|
|
|
frameNewSkillsSelector = 4
|
|
|
|
frameRightGlobeHolder = 5
|
|
|
|
frameRightGlobe = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
manaStatusOffsetX = 7
|
|
|
|
manaStatusOffsetY = -12
|
|
|
|
|
|
|
|
healthStatusOffsetX = 30
|
|
|
|
healthStatusOffsetY = -13
|
|
|
|
|
|
|
|
globeSpriteOffsetX = 28
|
|
|
|
globeSpriteOffsetY = -5
|
|
|
|
|
|
|
|
staminaBarOffsetX = 273
|
|
|
|
staminaBarOffsetY = 572
|
|
|
|
|
|
|
|
experienceBarOffsetX = 256
|
|
|
|
experienceBarOffsetY = 561
|
|
|
|
|
|
|
|
rightGlobeOffsetX = 8
|
|
|
|
rightGlobeOffsetY = -8
|
|
|
|
|
|
|
|
miniPanelButtonOffsetX = -8
|
|
|
|
miniPanelButtonOffsetY = -16
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
lightBrownAlpha72 = 0xaf8848c8
|
2020-10-25 13:15:28 -04:00
|
|
|
redAlpha72 = 0xff0000c8
|
2020-10-24 22:52:26 -04:00
|
|
|
whiteAlpha100 = 0xffffffff
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
zoneChangeTextX = screenWidth / 2
|
|
|
|
zoneChangeTextY = screenHeight / 4
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
menuBottomRectX,
|
|
|
|
menuBottomRectY,
|
|
|
|
menuBottomRectW,
|
|
|
|
menuBottomRectH = 0, 550, 800, 50
|
|
|
|
|
|
|
|
menuLeftRectX,
|
|
|
|
menuLeftRectY,
|
|
|
|
menuLeftRectW,
|
2020-10-25 10:54:39 -04:00
|
|
|
menuLeftRectH = 0, 0, 400, 600
|
2020-10-24 22:52:26 -04:00
|
|
|
|
|
|
|
menuRightRectX,
|
|
|
|
menuRightRectY,
|
|
|
|
menuRightRectW,
|
2020-10-25 10:54:39 -04:00
|
|
|
menuRightRectH = 400, 0, 400, 600
|
2020-10-24 22:52:26 -04:00
|
|
|
)
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
// GameControls represents the game's controls on the screen
|
2019-12-16 11:04:39 -05:00
|
|
|
type GameControls struct {
|
2020-10-22 02:41:21 -04:00
|
|
|
actionableRegions []actionableRegion
|
2020-09-12 16:51:30 -04:00
|
|
|
asset *d2asset.AssetManager
|
2020-10-25 10:21:14 -04:00
|
|
|
renderer d2interface.Renderer // https://github.com/OpenDiablo2/OpenDiablo2/issues/798
|
2020-10-22 02:41:21 -04:00
|
|
|
inputListener inputCallbackListener
|
2020-08-11 18:01:33 -04:00
|
|
|
hero *d2mapentity.Player
|
2020-09-20 17:52:01 -04:00
|
|
|
heroState *d2hero.HeroStateFactory
|
2020-08-11 18:01:33 -04:00
|
|
|
mapEngine *d2mapengine.MapEngine
|
|
|
|
mapRenderer *d2maprenderer.MapRenderer
|
2020-10-07 21:20:05 -04:00
|
|
|
escapeMenu *EscapeMenu
|
2020-09-23 13:30:54 -04:00
|
|
|
ui *d2ui.UIManager
|
2020-08-11 18:01:33 -04:00
|
|
|
inventory *Inventory
|
2020-10-25 03:42:31 -04:00
|
|
|
skilltree *skillTree
|
2020-08-11 18:01:33 -04:00
|
|
|
heroStatsPanel *HeroStatsPanel
|
2020-09-26 11:14:34 -04:00
|
|
|
HelpOverlay *help.Overlay
|
2020-08-25 09:10:26 -04:00
|
|
|
miniPanel *miniPanel
|
2020-10-24 22:52:26 -04:00
|
|
|
bottomMenuRect *d2geom.Rectangle
|
|
|
|
leftMenuRect *d2geom.Rectangle
|
|
|
|
rightMenuRect *d2geom.Rectangle
|
2020-08-11 18:01:33 -04:00
|
|
|
lastMouseX int
|
|
|
|
lastMouseY int
|
|
|
|
globeSprite *d2ui.Sprite
|
|
|
|
hpManaStatusSprite *d2ui.Sprite
|
|
|
|
mainPanel *d2ui.Sprite
|
|
|
|
menuButton *d2ui.Sprite
|
2020-10-22 16:53:18 -04:00
|
|
|
skillSelectMenu *SkillSelectMenu
|
2020-10-10 18:47:51 -04:00
|
|
|
leftSkillResource *SkillResource
|
|
|
|
rightSkillResource *SkillResource
|
2020-08-11 18:01:33 -04:00
|
|
|
zoneChangeText *d2ui.Label
|
|
|
|
nameLabel *d2ui.Label
|
2020-08-24 15:50:33 -04:00
|
|
|
hpManaStatsLabel *d2ui.Label
|
2020-08-11 18:01:33 -04:00
|
|
|
runButton *d2ui.Button
|
|
|
|
lastLeftBtnActionTime float64
|
|
|
|
lastRightBtnActionTime float64
|
|
|
|
FreeCam bool
|
|
|
|
isZoneTextShown bool
|
2020-08-24 15:50:33 -04:00
|
|
|
hpStatsIsVisible bool
|
|
|
|
manaStatsIsVisible bool
|
2020-08-25 09:10:26 -04:00
|
|
|
isSinglePlayer bool
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
|
|
|
|
2020-10-22 02:41:21 -04:00
|
|
|
type actionableType int
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-22 02:41:21 -04:00
|
|
|
type actionableRegion struct {
|
|
|
|
actionableTypeID actionableType
|
|
|
|
rect d2geom.Rectangle
|
2020-06-24 13:49:13 -04:00
|
|
|
}
|
2020-06-23 12:28:05 -04:00
|
|
|
|
2020-10-10 18:47:51 -04:00
|
|
|
// SkillResource represents a Skill with its corresponding icon sprite, path to DC6 file and icon number.
|
|
|
|
// SkillResourcePath points to a DC6 resource which contains the icons of multiple skills as frames.
|
|
|
|
// The IconNumber is the frame at which we can find our skill sprite in the DC6 file.
|
2020-09-20 11:55:44 -04:00
|
|
|
type SkillResource struct {
|
2020-10-10 18:47:51 -04:00
|
|
|
SkillResourcePath string // path to a skills DC6 file(see getSkillResourceByClass)
|
|
|
|
IconNumber int // the index of the frame in the DC6 file
|
2020-09-20 11:55:44 -04:00
|
|
|
SkillIcon *d2ui.Sprite
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// NewGameControls creates a GameControls instance and returns a pointer to it
|
2020-10-24 22:52:26 -04:00
|
|
|
// nolint:funlen // doesn't make sense to split this up
|
2020-08-06 10:30:23 -04:00
|
|
|
func NewGameControls(
|
2020-09-12 16:51:30 -04:00
|
|
|
asset *d2asset.AssetManager,
|
2020-08-06 10:30:23 -04:00
|
|
|
renderer d2interface.Renderer,
|
|
|
|
hero *d2mapentity.Player,
|
|
|
|
mapEngine *d2mapengine.MapEngine,
|
2020-10-07 21:20:05 -04:00
|
|
|
escapeMenu *EscapeMenu,
|
2020-08-06 10:30:23 -04:00
|
|
|
mapRenderer *d2maprenderer.MapRenderer,
|
2020-10-22 02:41:21 -04:00
|
|
|
inputListener inputCallbackListener,
|
2020-08-06 10:30:23 -04:00
|
|
|
term d2interface.Terminal,
|
|
|
|
ui *d2ui.UIManager,
|
2020-09-18 16:10:52 -04:00
|
|
|
guiManager *d2gui.GuiManager,
|
2020-10-07 21:20:05 -04:00
|
|
|
|
2020-08-25 09:10:26 -04:00
|
|
|
isSinglePlayer bool,
|
2020-08-06 10:30:23 -04:00
|
|
|
) (*GameControls, error) {
|
|
|
|
zoneLabel := ui.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
|
2020-07-07 20:16:22 -04:00
|
|
|
zoneLabel.Alignment = d2gui.HorizontalAlignCenter
|
2020-06-22 15:55:32 -04:00
|
|
|
|
2020-09-23 22:58:56 -04:00
|
|
|
nameLabel := ui.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
|
2020-07-07 20:16:22 -04:00
|
|
|
nameLabel.Alignment = d2gui.HorizontalAlignCenter
|
2020-08-02 21:26:07 -04:00
|
|
|
nameLabel.SetText(d2ui.ColorTokenize("", d2ui.ColorTokenServer))
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-08-24 15:50:33 -04:00
|
|
|
hpManaStatsLabel := ui.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
|
|
|
|
hpManaStatsLabel.Alignment = d2gui.HorizontalAlignLeft
|
|
|
|
|
2020-07-11 11:25:34 -04:00
|
|
|
var inventoryRecordKey string
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-07-11 11:25:34 -04:00
|
|
|
switch hero.Class {
|
|
|
|
case d2enum.HeroAssassin:
|
2020-10-22 12:54:45 -04:00
|
|
|
inventoryRecordKey = "Assassin2"
|
2020-07-11 11:25:34 -04:00
|
|
|
case d2enum.HeroAmazon:
|
|
|
|
inventoryRecordKey = "Amazon2"
|
|
|
|
case d2enum.HeroBarbarian:
|
|
|
|
inventoryRecordKey = "Barbarian2"
|
|
|
|
case d2enum.HeroDruid:
|
2020-10-22 12:54:45 -04:00
|
|
|
inventoryRecordKey = "Druid2"
|
2020-07-11 11:25:34 -04:00
|
|
|
case d2enum.HeroNecromancer:
|
|
|
|
inventoryRecordKey = "Necromancer2"
|
|
|
|
case d2enum.HeroPaladin:
|
|
|
|
inventoryRecordKey = "Paladin2"
|
|
|
|
case d2enum.HeroSorceress:
|
|
|
|
inventoryRecordKey = "Sorceress2"
|
|
|
|
default:
|
2020-09-12 16:25:09 -04:00
|
|
|
return nil, fmt.Errorf("unknown hero class: %d", hero.Class)
|
2020-07-11 11:25:34 -04:00
|
|
|
}
|
2020-07-22 15:03:03 -04:00
|
|
|
|
2020-09-20 17:52:01 -04:00
|
|
|
inventoryRecord := asset.Records.Layout.Inventory[inventoryRecordKey]
|
2020-07-11 11:25:34 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
const blackAlpha50percent = 0x0000007f
|
|
|
|
|
2020-08-06 10:30:23 -04:00
|
|
|
hoverLabel := nameLabel
|
2020-10-24 22:52:26 -04:00
|
|
|
hoverLabel.SetBackgroundColor(d2util.Color(blackAlpha50percent))
|
2020-08-03 00:48:17 -04:00
|
|
|
|
2020-08-24 15:50:33 -04:00
|
|
|
globeStatsLabel := hpManaStatsLabel
|
|
|
|
|
2020-09-20 17:52:01 -04:00
|
|
|
heroState, err := d2hero.NewHeroStateFactory(asset)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-21 20:46:00 -04:00
|
|
|
gc := &GameControls{
|
2020-09-12 16:51:30 -04:00
|
|
|
asset: asset,
|
2020-09-23 13:30:54 -04:00
|
|
|
ui: ui,
|
2020-08-24 15:50:33 -04:00
|
|
|
renderer: renderer,
|
|
|
|
hero: hero,
|
2020-09-20 17:52:01 -04:00
|
|
|
heroState: heroState,
|
2020-08-24 15:50:33 -04:00
|
|
|
mapEngine: mapEngine,
|
2020-10-07 21:20:05 -04:00
|
|
|
escapeMenu: escapeMenu,
|
2020-08-24 15:50:33 -04:00
|
|
|
inputListener: inputListener,
|
|
|
|
mapRenderer: mapRenderer,
|
2020-09-12 16:51:30 -04:00
|
|
|
inventory: NewInventory(asset, ui, inventoryRecord),
|
2020-10-22 16:53:18 -04:00
|
|
|
skillSelectMenu: NewSkillSelectMenu(asset, ui, hero),
|
2020-10-25 03:42:31 -04:00
|
|
|
skilltree: newSkillTree(hero.Skills, hero.Class, asset, renderer, ui, guiManager),
|
2020-09-12 16:51:30 -04:00
|
|
|
heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats),
|
2020-09-26 11:14:34 -04:00
|
|
|
HelpOverlay: help.NewHelpOverlay(asset, renderer, ui, guiManager),
|
2020-09-12 16:51:30 -04:00
|
|
|
miniPanel: newMiniPanel(asset, ui, isSinglePlayer),
|
2020-08-24 15:50:33 -04:00
|
|
|
nameLabel: hoverLabel,
|
|
|
|
zoneChangeText: zoneLabel,
|
|
|
|
hpManaStatsLabel: globeStatsLabel,
|
2020-10-24 22:52:26 -04:00
|
|
|
bottomMenuRect: &d2geom.Rectangle{
|
|
|
|
Left: menuBottomRectX,
|
|
|
|
Top: menuBottomRectY,
|
|
|
|
Width: menuBottomRectW,
|
|
|
|
Height: menuBottomRectH,
|
|
|
|
},
|
|
|
|
leftMenuRect: &d2geom.Rectangle{
|
|
|
|
Left: menuLeftRectX,
|
|
|
|
Top: menuLeftRectY,
|
|
|
|
Width: menuLeftRectW,
|
|
|
|
Height: menuLeftRectH,
|
|
|
|
},
|
|
|
|
rightMenuRect: &d2geom.Rectangle{
|
|
|
|
Left: menuRightRectX,
|
|
|
|
Top: menuRightRectY,
|
|
|
|
Width: menuRightRectW,
|
|
|
|
Height: menuRightRectH,
|
|
|
|
},
|
2020-10-22 02:41:21 -04:00
|
|
|
actionableRegions: []actionableRegion{
|
2020-10-24 22:52:26 -04:00
|
|
|
{leftSkill, d2geom.Rectangle{
|
|
|
|
Left: leftSkillX,
|
|
|
|
Top: leftSkillY,
|
|
|
|
Width: leftSkillWidth,
|
|
|
|
Height: leftSkillHeight,
|
|
|
|
}},
|
|
|
|
{newStats, d2geom.Rectangle{
|
|
|
|
Left: newStatsX,
|
|
|
|
Top: newStatsY,
|
|
|
|
Width: newStatsWidth,
|
|
|
|
Height: newStatsHeight,
|
|
|
|
}},
|
|
|
|
{xp, d2geom.Rectangle{
|
|
|
|
Left: xpX,
|
|
|
|
Top: xpY,
|
|
|
|
Width: xpWidth,
|
|
|
|
Height: xpHeight,
|
|
|
|
}},
|
|
|
|
{walkRun, d2geom.Rectangle{
|
|
|
|
Left: walkRunX,
|
|
|
|
Top: walkRunY,
|
|
|
|
Width: walkRunWidth,
|
|
|
|
Height: walkRunHeight,
|
|
|
|
}},
|
|
|
|
{stamina, d2geom.Rectangle{
|
|
|
|
Left: staminaX,
|
|
|
|
Top: staminaY,
|
|
|
|
Width: staminaWidth,
|
|
|
|
Height: staminaHeight,
|
|
|
|
}},
|
|
|
|
{miniPnl, d2geom.Rectangle{
|
|
|
|
Left: miniPnlX,
|
|
|
|
Top: miniPnlY,
|
|
|
|
Width: miniPnlWidth,
|
|
|
|
Height: miniPnlHeight,
|
|
|
|
}},
|
|
|
|
{newSkills, d2geom.Rectangle{
|
|
|
|
Left: newSkillsX,
|
|
|
|
Top: newSkillsY,
|
|
|
|
Width: newSkillsWidth,
|
|
|
|
Height: newSkillsHeight,
|
|
|
|
}},
|
|
|
|
{rightSkill, d2geom.Rectangle{
|
|
|
|
Left: rightSkillX,
|
|
|
|
Top: rightSkillY,
|
|
|
|
Width: rightSkillWidth,
|
|
|
|
Height: rightSkillHeight,
|
|
|
|
}},
|
|
|
|
{hpGlobe, d2geom.Rectangle{
|
|
|
|
Left: hpGlobeX,
|
|
|
|
Top: hpGlobeY,
|
|
|
|
Width: hpGlobeWidth,
|
|
|
|
Height: hpGlobeHeight,
|
|
|
|
}},
|
|
|
|
{manaGlobe, d2geom.Rectangle{
|
|
|
|
Left: manaGlobeX,
|
|
|
|
Top: manaGlobeY,
|
|
|
|
Width: manaGlobeWidth,
|
|
|
|
Height: manaGlobeHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelCharacter, d2geom.Rectangle{
|
|
|
|
Left: miniPanelCharacterX,
|
|
|
|
Top: miniPanelCharacterY,
|
|
|
|
Width: miniPanelCharacterWidth,
|
|
|
|
Height: miniPanelCharacterHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelInventory, d2geom.Rectangle{
|
|
|
|
Left: miniPanelInventoryX,
|
|
|
|
Top: miniPanelInventoryY,
|
|
|
|
Width: miniPanelInventoryWidth,
|
|
|
|
Height: miniPanelInventoryHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelSkillTree, d2geom.Rectangle{
|
|
|
|
Left: miniPanelSkillTreeX,
|
|
|
|
Top: miniPanelSkillTreeY,
|
|
|
|
Width: miniPanelSkillTreeWidth,
|
|
|
|
Height: miniPanelSkillTreeHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelAutomap, d2geom.Rectangle{
|
|
|
|
Left: miniPanelAutomapX,
|
|
|
|
Top: miniPanelAutomapY,
|
|
|
|
Width: miniPanelAutomapWidth,
|
|
|
|
Height: miniPanelAutomapHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelMessageLog, d2geom.Rectangle{
|
|
|
|
Left: miniPanelMessageLogX,
|
|
|
|
Top: miniPanelMessageLogY,
|
|
|
|
Width: miniPanelMessageLogWidth,
|
|
|
|
Height: miniPanelMessageLogHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelQuestLog, d2geom.Rectangle{
|
|
|
|
Left: miniPanelQuestLogX,
|
|
|
|
Top: miniPanelQuestLogY,
|
|
|
|
Width: miniPanelQuestLogWidth,
|
|
|
|
Height: miniPanelQuestLogHeight,
|
|
|
|
}},
|
|
|
|
{miniPanelGameMenu, d2geom.Rectangle{
|
|
|
|
Left: miniPanelGameMenuX,
|
|
|
|
Top: miniPanelGameMenuY,
|
|
|
|
Width: miniPanelGameMenuWidth,
|
|
|
|
Height: miniPanelGameMenuHeight,
|
|
|
|
}},
|
2020-06-23 12:28:05 -04:00
|
|
|
},
|
2020-08-11 18:01:33 -04:00
|
|
|
lastLeftBtnActionTime: 0,
|
|
|
|
lastRightBtnActionTime: 0,
|
2020-08-25 09:10:26 -04:00
|
|
|
isSinglePlayer: isSinglePlayer,
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
2020-06-21 20:46:00 -04:00
|
|
|
|
2020-10-25 10:54:39 -04:00
|
|
|
closeCb := func() { gc.updateLayout() }
|
|
|
|
gc.heroStatsPanel.SetOnCloseCb(closeCb)
|
|
|
|
gc.inventory.SetOnCloseCb(closeCb)
|
|
|
|
gc.skilltree.SetOnCloseCb(closeCb)
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
err = gc.bindTerminalCommands(term)
|
2020-09-20 11:55:44 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-26 14:52:54 -04:00
|
|
|
return gc, nil
|
2020-06-21 20:46:00 -04:00
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// OnKeyRepeat is called to handle repeated key presses
|
2020-07-03 15:09:16 -04:00
|
|
|
func (g *GameControls) OnKeyRepeat(event d2interface.KeyEvent) bool {
|
2020-06-21 20:46:00 -04:00
|
|
|
if g.FreeCam {
|
|
|
|
var moveSpeed float64 = 8
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.KeyMod() == d2enum.KeyModShift {
|
2020-06-21 20:46:00 -04:00
|
|
|
moveSpeed *= 2
|
|
|
|
}
|
|
|
|
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Key() == d2enum.KeyDown {
|
2020-07-18 23:37:35 -04:00
|
|
|
v := d2vector.NewVector(0, moveSpeed)
|
2020-07-25 09:36:54 -04:00
|
|
|
g.mapRenderer.MoveCameraTargetBy(v)
|
2020-07-18 23:37:35 -04:00
|
|
|
|
2020-06-21 20:46:00 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Key() == d2enum.KeyUp {
|
2020-07-18 23:37:35 -04:00
|
|
|
v := d2vector.NewVector(0, -moveSpeed)
|
2020-07-25 09:36:54 -04:00
|
|
|
g.mapRenderer.MoveCameraTargetBy(v)
|
2020-07-18 23:37:35 -04:00
|
|
|
|
2020-06-21 20:46:00 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Key() == d2enum.KeyRight {
|
2020-07-18 23:37:35 -04:00
|
|
|
v := d2vector.NewVector(moveSpeed, 0)
|
2020-07-25 09:36:54 -04:00
|
|
|
g.mapRenderer.MoveCameraTargetBy(v)
|
2020-07-18 23:37:35 -04:00
|
|
|
|
2020-06-21 20:46:00 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-07-06 21:26:08 -04:00
|
|
|
if event.Key() == d2enum.KeyLeft {
|
2020-07-18 23:37:35 -04:00
|
|
|
v := d2vector.NewVector(-moveSpeed, 0)
|
2020-07-25 09:36:54 -04:00
|
|
|
g.mapRenderer.MoveCameraTargetBy(v)
|
2020-07-18 23:37:35 -04:00
|
|
|
|
2020-06-21 20:46:00 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// OnKeyDown handles key presses
|
2020-07-03 15:09:16 -04:00
|
|
|
func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool {
|
|
|
|
switch event.Key() {
|
2020-07-06 21:26:08 -04:00
|
|
|
case d2enum.KeyEscape:
|
2020-10-07 21:20:05 -04:00
|
|
|
g.onEscKey()
|
2020-07-06 21:26:08 -04:00
|
|
|
case d2enum.KeyI:
|
2020-01-25 23:28:21 -05:00
|
|
|
g.inventory.Toggle()
|
2020-06-22 09:23:56 -04:00
|
|
|
g.updateLayout()
|
2020-10-22 12:54:45 -04:00
|
|
|
case d2enum.KeyT:
|
|
|
|
g.skilltree.Toggle()
|
|
|
|
g.updateLayout()
|
2020-07-06 21:26:08 -04:00
|
|
|
case d2enum.KeyC:
|
2020-06-25 14:56:49 -04:00
|
|
|
g.heroStatsPanel.Toggle()
|
2020-06-22 09:23:56 -04:00
|
|
|
g.updateLayout()
|
2020-10-07 16:12:56 -04:00
|
|
|
case d2enum.KeyR, d2enum.KeyControl:
|
2020-06-24 15:23:38 -04:00
|
|
|
g.onToggleRunButton()
|
2020-09-09 08:21:27 -04:00
|
|
|
case d2enum.KeyH:
|
2020-09-26 11:14:34 -04:00
|
|
|
g.HelpOverlay.Toggle()
|
2020-09-09 08:21:27 -04:00
|
|
|
g.updateLayout()
|
2020-06-21 18:44:33 -04:00
|
|
|
default:
|
|
|
|
return false
|
2020-02-08 10:51:11 -05:00
|
|
|
}
|
2020-08-11 18:01:33 -04:00
|
|
|
|
2020-06-22 15:55:32 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-10-07 16:12:56 -04:00
|
|
|
// OnKeyUp handles key release
|
|
|
|
func (g *GameControls) OnKeyUp(event d2interface.KeyEvent) bool {
|
|
|
|
switch event.Key() {
|
|
|
|
case d2enum.KeyControl:
|
|
|
|
g.onToggleRunButton()
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-10-22 01:12:06 -04:00
|
|
|
// When escape is pressed:
|
|
|
|
// 1. If there was some overlay or panel open, close it
|
|
|
|
// 2. Otherwise, if the Escape Menu was open, let the Escape Menu handle it
|
|
|
|
// 3. If nothing was open, open the Escape Menu
|
2020-10-07 21:20:05 -04:00
|
|
|
func (g *GameControls) onEscKey() {
|
|
|
|
escHandled := false
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
if g.skillSelectMenu.IsOpen() {
|
|
|
|
g.skillSelectMenu.ClosePanels()
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
escHandled = true
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
if g.inventory.IsOpen() {
|
|
|
|
g.inventory.Close()
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
escHandled = true
|
|
|
|
}
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-22 12:54:45 -04:00
|
|
|
if g.skilltree.IsOpen() {
|
|
|
|
g.skilltree.Close()
|
|
|
|
|
|
|
|
escHandled = true
|
|
|
|
}
|
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
if g.heroStatsPanel.IsOpen() {
|
|
|
|
g.heroStatsPanel.Close()
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
escHandled = true
|
|
|
|
}
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
if g.HelpOverlay.IsOpen() {
|
|
|
|
g.HelpOverlay.Toggle()
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
escHandled = true
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
switch escHandled {
|
|
|
|
case true:
|
2020-10-07 21:20:05 -04:00
|
|
|
g.updateLayout()
|
2020-10-24 22:52:26 -04:00
|
|
|
case false:
|
|
|
|
if g.escapeMenu.IsOpen() {
|
|
|
|
g.escapeMenu.OnEscKey()
|
|
|
|
} else {
|
|
|
|
g.escapeMenu.open()
|
|
|
|
}
|
2020-10-07 21:20:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
func truncateFloat64(n float64) float64 {
|
|
|
|
const ten = 10.0
|
|
|
|
return float64(int(n*ten)) / ten
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// OnMouseButtonRepeat handles repeated mouse clicks
|
2020-07-03 15:09:16 -04:00
|
|
|
func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool {
|
2020-10-24 22:52:26 -04:00
|
|
|
const (
|
|
|
|
screenWidth, screenHeight = 800, 600
|
|
|
|
halfScreenWidth, halfScreenHeight = screenWidth / 2, screenHeight / 2
|
|
|
|
subtilesPerTile = 5
|
|
|
|
)
|
|
|
|
|
2020-07-03 15:09:16 -04:00
|
|
|
px, py := g.mapRenderer.ScreenToWorld(event.X(), event.Y())
|
2020-10-24 22:52:26 -04:00
|
|
|
px = truncateFloat64(px)
|
|
|
|
py = truncateFloat64(py)
|
2020-06-22 15:55:32 -04:00
|
|
|
|
2020-09-08 15:58:35 -04:00
|
|
|
now := d2util.Now()
|
2020-07-03 15:09:16 -04:00
|
|
|
button := event.Button()
|
2020-07-06 21:26:08 -04:00
|
|
|
isLeft := button == d2enum.MouseButtonLeft
|
|
|
|
isRight := button == d2enum.MouseButtonRight
|
2020-08-11 18:01:33 -04:00
|
|
|
lastLeft := now - g.lastLeftBtnActionTime
|
|
|
|
lastRight := now - g.lastRightBtnActionTime
|
2020-07-03 15:09:16 -04:00
|
|
|
inRect := !g.isInActiveMenusRect(event.X(), event.Y())
|
2020-10-25 13:15:28 -04:00
|
|
|
shouldDoLeft := lastLeft >= mouseBtnActionsThreshold
|
|
|
|
shouldDoRight := lastRight >= mouseBtnActionsThreshold
|
2020-07-03 15:09:16 -04:00
|
|
|
|
2020-09-14 14:49:31 -04:00
|
|
|
if isLeft && shouldDoLeft && inRect && !g.hero.IsCasting() {
|
2020-08-11 18:01:33 -04:00
|
|
|
g.lastLeftBtnActionTime = now
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
if event.KeyMod() == d2enum.KeyModShift {
|
|
|
|
g.inputListener.OnPlayerCast(g.hero.LeftSkill.ID, px, py)
|
|
|
|
} else {
|
|
|
|
g.inputListener.OnPlayerMove(px, py)
|
|
|
|
}
|
2020-07-18 23:37:35 -04:00
|
|
|
|
|
|
|
if g.FreeCam {
|
|
|
|
if event.Button() == d2enum.MouseButtonLeft {
|
|
|
|
camVect := g.mapRenderer.Camera.GetPosition().Vector
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
x := float64(halfScreenWidth) / subtilesPerTile
|
|
|
|
y := float64(halfScreenHeight) / subtilesPerTile
|
|
|
|
|
2020-07-18 23:37:35 -04:00
|
|
|
targetPosition := d2vector.NewPositionTile(x, y)
|
|
|
|
targetPosition.Add(&camVect)
|
|
|
|
|
|
|
|
g.mapRenderer.SetCameraTarget(&targetPosition)
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:55:32 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:49:31 -04:00
|
|
|
if isRight && shouldDoRight && inRect && !g.hero.IsCasting() {
|
2020-08-11 18:01:33 -04:00
|
|
|
g.lastRightBtnActionTime = now
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
g.inputListener.OnPlayerCast(g.hero.RightSkill.ID, px, py)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-22 15:55:32 -04:00
|
|
|
return true
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-06-21 18:44:33 -04:00
|
|
|
return true
|
2020-01-25 23:28:21 -05:00
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// OnMouseMove handles mouse movement events
|
2020-07-03 15:09:16 -04:00
|
|
|
func (g *GameControls) OnMouseMove(event d2interface.MouseMoveEvent) bool {
|
|
|
|
mx, my := event.X(), event.Y()
|
2020-06-25 00:39:09 -04:00
|
|
|
g.lastMouseX = mx
|
|
|
|
g.lastMouseY = my
|
2020-08-03 13:44:00 -04:00
|
|
|
g.inventory.lastMouseX = mx
|
|
|
|
g.inventory.lastMouseY = my
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-06-23 12:28:05 -04:00
|
|
|
for i := range g.actionableRegions {
|
|
|
|
// Mouse over a game control element
|
2020-10-22 02:41:21 -04:00
|
|
|
if g.actionableRegions[i].rect.IsInRect(mx, my) {
|
|
|
|
g.onHoverActionable(g.actionableRegions[i].actionableTypeID)
|
2020-06-23 12:28:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
g.skillSelectMenu.LeftPanel.HandleMouseMove(mx, my)
|
|
|
|
g.skillSelectMenu.RightPanel.HandleMouseMove(mx, my)
|
|
|
|
|
2020-06-21 16:06:52 -04:00
|
|
|
return false
|
2020-06-21 15:26:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// OnMouseButtonDown handles mouse button presses
|
2020-07-03 15:09:16 -04:00
|
|
|
func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
|
|
|
mx, my := event.X(), event.Y()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-23 12:28:05 -04:00
|
|
|
for i := range g.actionableRegions {
|
|
|
|
// If click is on a game control element
|
2020-10-22 02:41:21 -04:00
|
|
|
if g.actionableRegions[i].rect.IsInRect(mx, my) {
|
|
|
|
g.onClickActionable(g.actionableRegions[i].actionableTypeID)
|
2020-06-23 12:28:05 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
if g.skillSelectMenu.IsOpen() && event.Button() == d2enum.MouseButtonLeft {
|
|
|
|
g.lastLeftBtnActionTime = d2util.Now()
|
|
|
|
g.skillSelectMenu.HandleClick(mx, my)
|
|
|
|
g.skillSelectMenu.ClosePanels()
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-06-23 12:28:05 -04:00
|
|
|
px, py := g.mapRenderer.ScreenToWorld(mx, my)
|
2020-10-24 22:52:26 -04:00
|
|
|
px = truncateFloat64(px)
|
|
|
|
py = truncateFloat64(py)
|
2020-02-22 20:44:30 -05:00
|
|
|
|
2020-09-14 14:49:31 -04:00
|
|
|
if event.Button() == d2enum.MouseButtonLeft && !g.isInActiveMenusRect(mx, my) && !g.hero.IsCasting() {
|
2020-09-08 15:58:35 -04:00
|
|
|
g.lastLeftBtnActionTime = d2util.Now()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
if event.KeyMod() == d2enum.KeyModShift {
|
|
|
|
g.inputListener.OnPlayerCast(g.hero.LeftSkill.ID, px, py)
|
|
|
|
} else {
|
|
|
|
g.inputListener.OnPlayerMove(px, py)
|
|
|
|
}
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-02-22 20:44:30 -05:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:49:31 -04:00
|
|
|
if event.Button() == d2enum.MouseButtonRight && !g.isInActiveMenusRect(mx, my) && !g.hero.IsCasting() {
|
2020-09-08 15:58:35 -04:00
|
|
|
g.lastRightBtnActionTime = d2util.Now()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
g.inputListener.OnPlayerCast(g.hero.RightSkill.ID, px, py)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-26 20:03:00 -04:00
|
|
|
return true
|
2019-12-28 23:32:53 -05:00
|
|
|
}
|
2020-02-22 20:44:30 -05:00
|
|
|
|
2020-01-25 23:28:21 -05:00
|
|
|
return false
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
|
|
|
|
2020-09-14 14:47:11 -04:00
|
|
|
// Load the resources required for the GameControls
|
2019-12-16 11:04:39 -05:00
|
|
|
func (g *GameControls) Load() {
|
2020-09-23 13:30:54 -04:00
|
|
|
var err error
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-09-23 13:30:54 -04:00
|
|
|
g.globeSprite, err = g.ui.NewSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky)
|
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2020-01-31 23:18:11 -05:00
|
|
|
|
2020-09-23 13:30:54 -04:00
|
|
|
g.hpManaStatusSprite, err = g.ui.NewSprite(d2resource.HealthManaIndicator, d2resource.PaletteSky)
|
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-09-23 13:30:54 -04:00
|
|
|
g.mainPanel, err = g.ui.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
|
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2020-01-31 23:18:11 -05:00
|
|
|
|
2020-09-23 13:30:54 -04:00
|
|
|
g.menuButton, err = g.ui.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
|
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
err = g.menuButton.SetCurrentFrame(frameMenuButton)
|
2020-09-23 13:30:54 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
2020-01-31 23:18:11 -05:00
|
|
|
|
2020-10-25 10:21:14 -04:00
|
|
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/799
|
2020-09-23 13:30:54 -04:00
|
|
|
genericSkillsSprite, err := g.ui.NewSprite(d2resource.GenericSkills, d2resource.PaletteSky)
|
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
attackIconID := 2
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
g.leftSkillResource = &SkillResource{
|
|
|
|
SkillIcon: genericSkillsSprite,
|
|
|
|
IconNumber: attackIconID,
|
|
|
|
SkillResourcePath: d2resource.GenericSkills,
|
|
|
|
}
|
|
|
|
|
|
|
|
g.rightSkillResource = &SkillResource{
|
|
|
|
SkillIcon: genericSkillsSprite,
|
|
|
|
IconNumber: attackIconID,
|
|
|
|
SkillResourcePath: d2resource.GenericSkills,
|
|
|
|
}
|
2020-01-31 23:18:11 -05:00
|
|
|
|
2020-06-24 15:23:38 -04:00
|
|
|
g.loadUIButtons()
|
|
|
|
|
2019-12-28 23:32:53 -05:00
|
|
|
g.inventory.Load()
|
2020-10-25 03:42:31 -04:00
|
|
|
g.skilltree.load()
|
2020-06-25 14:56:49 -04:00
|
|
|
g.heroStatsPanel.Load()
|
2020-09-26 11:14:34 -04:00
|
|
|
g.HelpOverlay.Load()
|
2020-06-21 16:06:52 -04:00
|
|
|
}
|
|
|
|
|
2020-06-24 15:23:38 -04:00
|
|
|
func (g *GameControls) loadUIButtons() {
|
|
|
|
// Run button
|
2020-09-23 13:30:54 -04:00
|
|
|
g.runButton = g.ui.NewButton(d2ui.ButtonTypeRun, "")
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
g.runButton.SetPosition(runButtonX, runButtonY)
|
2020-06-24 15:23:38 -04:00
|
|
|
g.runButton.OnActivated(func() { g.onToggleRunButton() })
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-24 15:23:38 -04:00
|
|
|
if g.hero.IsRunToggled() {
|
|
|
|
g.runButton.Toggle()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) onToggleRunButton() {
|
|
|
|
g.runButton.Toggle()
|
|
|
|
g.hero.ToggleRunWalk()
|
2020-10-25 10:21:14 -04:00
|
|
|
|
|
|
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/800
|
2020-06-24 15:23:38 -04:00
|
|
|
g.hero.SetIsRunning(g.hero.IsRunToggled())
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// Advance advances the state of the GameControls
|
2020-06-21 16:06:52 -04:00
|
|
|
func (g *GameControls) Advance(elapsed float64) error {
|
2020-07-18 23:37:35 -04:00
|
|
|
g.mapRenderer.Advance(elapsed)
|
2020-06-21 16:06:52 -04:00
|
|
|
return nil
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
|
|
|
|
2020-06-22 09:23:56 -04:00
|
|
|
func (g *GameControls) updateLayout() {
|
2020-06-25 19:18:37 -04:00
|
|
|
isRightPanelOpen := g.isLeftPanelOpen()
|
|
|
|
isLeftPanelOpen := g.isRightPanelOpen()
|
2020-06-22 09:23:56 -04:00
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
switch {
|
|
|
|
case isRightPanelOpen == isLeftPanelOpen:
|
2020-06-22 09:23:56 -04:00
|
|
|
g.mapRenderer.ViewportDefault()
|
2020-08-11 18:01:33 -04:00
|
|
|
case isRightPanelOpen:
|
2020-06-22 09:23:56 -04:00
|
|
|
g.mapRenderer.ViewportToLeft()
|
2020-08-11 18:01:33 -04:00
|
|
|
default:
|
2020-06-22 09:23:56 -04:00
|
|
|
g.mapRenderer.ViewportToRight()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 19:18:37 -04:00
|
|
|
func (g *GameControls) isLeftPanelOpen() bool {
|
2020-10-25 10:21:14 -04:00
|
|
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/801
|
2020-06-25 19:18:37 -04:00
|
|
|
return g.heroStatsPanel.IsOpen()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) isRightPanelOpen() bool {
|
2020-10-22 12:54:45 -04:00
|
|
|
return g.inventory.IsOpen() || g.skilltree.IsOpen()
|
2020-06-25 19:18:37 -04:00
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
func (g *GameControls) isInActiveMenusRect(px, py int) bool {
|
2020-10-24 22:52:26 -04:00
|
|
|
if g.bottomMenuRect.IsInRect(px, py) {
|
2020-06-25 19:18:37 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if g.isLeftPanelOpen() && g.leftMenuRect.IsInRect(px, py) {
|
2020-06-25 19:18:37 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if g.isRightPanelOpen() && g.rightMenuRect.IsInRect(px, py) {
|
2020-06-25 19:18:37 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-08-25 09:10:26 -04:00
|
|
|
if g.miniPanel.IsOpen() && g.miniPanel.isInRect(px, py) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-07 21:20:05 -04:00
|
|
|
if g.escapeMenu.IsOpen() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-26 11:14:34 -04:00
|
|
|
if g.HelpOverlay.IsOpen() && g.HelpOverlay.IsInRect(px, py) {
|
2020-09-15 23:37:08 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
if g.skillSelectMenu.IsOpen() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-25 19:18:37 -04:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// Render draws the GameControls onto the target
|
2020-07-26 14:52:54 -04:00
|
|
|
func (g *GameControls) Render(target d2interface.Surface) error {
|
2020-10-24 22:52:26 -04:00
|
|
|
g.renderForSelectableEntitiesHovered(target)
|
|
|
|
|
|
|
|
if err := g.renderPanels(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.renderHUD(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderForSelectableEntitiesHovered(target d2interface.Surface) {
|
2020-08-24 15:50:33 -04:00
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-05 21:27:45 -04:00
|
|
|
for entityIdx := range g.mapEngine.Entities() {
|
|
|
|
entity := (g.mapEngine.Entities())[entityIdx]
|
2020-06-27 18:58:41 -04:00
|
|
|
if !entity.Selectable() {
|
2020-06-25 00:39:09 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-08-01 19:03:09 -04:00
|
|
|
entPos := entity.GetPosition()
|
|
|
|
entOffset := entPos.RenderOffset()
|
2020-06-25 00:39:09 -04:00
|
|
|
entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF())
|
|
|
|
entScreenX := int(math.Floor(entScreenXf))
|
|
|
|
entScreenY := int(math.Floor(entScreenYf))
|
2020-08-01 19:03:09 -04:00
|
|
|
entityWidth, entityHeight := entity.GetSize()
|
2020-10-24 22:52:26 -04:00
|
|
|
halfWidth, halfHeight := entityWidth>>1, entityHeight>>1
|
2020-08-01 19:03:09 -04:00
|
|
|
l, r := entScreenX-halfWidth-hoverLabelOuterPad, entScreenX+halfWidth+hoverLabelOuterPad
|
|
|
|
t, b := entScreenY-halfHeight-hoverLabelOuterPad, entScreenY+halfHeight-hoverLabelOuterPad
|
|
|
|
xWithin := (l <= mx) && (r >= mx)
|
|
|
|
yWithin := (t <= my) && (b >= my)
|
|
|
|
within := xWithin && yWithin
|
|
|
|
|
|
|
|
if within {
|
|
|
|
xOff, yOff := int(entOffset.X()), int(entOffset.Y())
|
2020-08-03 00:48:17 -04:00
|
|
|
|
2020-08-02 21:26:07 -04:00
|
|
|
g.nameLabel.SetText(entity.Label())
|
|
|
|
|
2020-08-01 19:03:09 -04:00
|
|
|
xLabel, yLabel := entScreenX-xOff, entScreenY-yOff-entityHeight-hoverLabelOuterPad
|
|
|
|
g.nameLabel.SetPosition(xLabel, yLabel)
|
2020-08-03 00:48:17 -04:00
|
|
|
|
2020-06-25 00:39:09 -04:00
|
|
|
g.nameLabel.Render(target)
|
2020-06-27 18:58:41 -04:00
|
|
|
entity.Highlight()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-25 00:39:09 -04:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
}
|
2020-06-25 00:39:09 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
func (g *GameControls) renderPanels(target d2interface.Surface) error {
|
2020-10-28 14:17:42 -04:00
|
|
|
g.heroStatsPanel.Render(target)
|
|
|
|
g.inventory.Render(target)
|
2019-12-28 23:32:53 -05:00
|
|
|
|
2020-10-28 14:17:42 -04:00
|
|
|
err := g.skilltree.Render(target)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
2020-10-22 12:54:45 -04:00
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderHUD(target d2interface.Surface) error {
|
2020-10-26 18:55:13 -04:00
|
|
|
if err := g.renderGameControlPanelElements(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
if err := g.HelpOverlay.Render(target); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
if g.isZoneTextShown {
|
|
|
|
g.zoneChangeText.SetPosition(zoneChangeTextX, zoneChangeTextY)
|
|
|
|
g.zoneChangeText.Render(target)
|
|
|
|
}
|
|
|
|
|
|
|
|
g.renderHealthTooltip(target)
|
|
|
|
g.renderManaTooltip(target)
|
|
|
|
g.renderRunWalkTooltip(target)
|
|
|
|
g.renderStaminaTooltip(target)
|
|
|
|
g.renderExperienceTooltip(target)
|
|
|
|
|
|
|
|
if g.skillSelectMenu.IsOpen() {
|
|
|
|
g.skillSelectMenu.Render(target)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: the positioning of all of the panel elements is coupled to the rendering order :(
|
|
|
|
// don't change the order in which the render methods are called, as there is an x,y offset
|
|
|
|
// that is updated between render calls
|
|
|
|
func (g *GameControls) renderGameControlPanelElements(target d2interface.Surface) error {
|
|
|
|
_, height := target.GetSize()
|
|
|
|
offsetX, offsetY := 0, 0
|
|
|
|
|
|
|
|
// Main panel background
|
|
|
|
offsetY = height
|
|
|
|
if err := g.renderPanel(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Health globe
|
2019-12-21 20:53:18 -05:00
|
|
|
w, _ := g.mainPanel.GetCurrentFrameSize()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
if err := g.renderHealthGlobe(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Left Skill
|
|
|
|
offsetX += w
|
|
|
|
if err := g.renderLeftSkill(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// New Stats Button
|
|
|
|
w, _ = g.leftSkillResource.SkillIcon.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderNewStatsButton(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stamina
|
|
|
|
w, _ = g.mainPanel.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderStamina(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stamina status bar
|
|
|
|
w, _ = g.mainPanel.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderStaminaBar(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Experience status bar
|
|
|
|
if err := g.renderExperienceBar(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mini Panel and button
|
|
|
|
if err := g.renderMiniPanel(target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Potions
|
|
|
|
if err := g.renderPotions(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// New Skills Button
|
|
|
|
w, _ = g.mainPanel.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderNewSkillsButton(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right skill
|
|
|
|
w, _ = g.mainPanel.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderRightSkill(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mana Globe
|
|
|
|
w, _ = g.rightSkillResource.SkillIcon.GetCurrentFrameSize()
|
|
|
|
offsetX += w
|
|
|
|
|
|
|
|
if err := g.renderManaGlobe(offsetX, offsetY, target); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderPanel(x, y int, target d2interface.Surface) error {
|
|
|
|
if err := g.mainPanel.SetCurrentFrame(0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
g.mainPanel.SetPosition(x, y)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderHealthGlobe(x, y int, target d2interface.Surface) error {
|
2020-06-25 14:56:49 -04:00
|
|
|
healthPercent := float64(g.hero.Stats.Health) / float64(g.hero.Stats.MaxHealth)
|
|
|
|
hpBarHeight := int(healthPercent * float64(globeHeight))
|
2020-07-26 14:52:54 -04:00
|
|
|
|
|
|
|
if err := g.hpManaStatusSprite.SetCurrentFrame(0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.hpManaStatusSprite.SetPosition(x+healthStatusOffsetX, y+healthStatusOffsetY)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
healthMaskRect := image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.hpManaStatusSprite.RenderSection(target, healthMaskRect)
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-06-30 12:43:13 -04:00
|
|
|
// Left globe
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.globeSprite.SetCurrentFrame(frameHealthStatus); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.globeSprite.SetPosition(x+globeSpriteOffsetX, y+globeSpriteOffsetY)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.globeSprite.Render(target)
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderLeftSkill(x, y int, target d2interface.Surface) error {
|
2020-10-22 16:53:18 -04:00
|
|
|
newSkillResourcePath := g.getSkillResourceByClass(g.hero.LeftSkill.Charclass)
|
|
|
|
if newSkillResourcePath != g.leftSkillResource.SkillResourcePath {
|
|
|
|
g.leftSkillResource.SkillResourcePath = newSkillResourcePath
|
|
|
|
g.leftSkillResource.SkillIcon, _ = g.ui.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
|
2020-09-20 11:55:44 -04:00
|
|
|
}
|
|
|
|
|
2020-10-10 18:47:51 -04:00
|
|
|
if err := g.leftSkillResource.SkillIcon.SetCurrentFrame(g.hero.LeftSkill.IconCel); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.leftSkillResource.SkillIcon.SetPosition(x, y)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.leftSkillResource.SkillIcon.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderNewStatsButton(x, y int, target d2interface.Surface) error {
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.mainPanel.SetCurrentFrame(frameNewStatsSelector); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.mainPanel.SetPosition(x, y)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderStamina(x, y int, target d2interface.Surface) error {
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.mainPanel.SetCurrentFrame(frameStamina); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.mainPanel.SetPosition(x, y)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderStaminaBar(target d2interface.Surface) error {
|
2020-10-24 22:52:26 -04:00
|
|
|
target.PushTranslation(staminaBarOffsetX, staminaBarOffsetY)
|
2020-10-26 18:55:13 -04:00
|
|
|
defer target.Pop()
|
|
|
|
|
2020-07-08 21:57:35 -04:00
|
|
|
target.PushEffect(d2enum.DrawEffectModulate)
|
2020-10-26 18:55:13 -04:00
|
|
|
defer target.Pop()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
staminaPercent := g.hero.Stats.Stamina / float64(g.hero.Stats.MaxStamina)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
staminaBarColor := d2util.Color(lightBrownAlpha72)
|
2020-10-25 13:15:28 -04:00
|
|
|
if staminaPercent < percentStaminaBarLow {
|
|
|
|
staminaBarColor = d2util.Color(redAlpha72)
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
target.DrawRect(int(staminaPercent*staminaBarWidth), staminaBarHeight, staminaBarColor)
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderExperienceBar(target d2interface.Surface) error {
|
2020-10-24 22:52:26 -04:00
|
|
|
target.PushTranslation(experienceBarOffsetX, experienceBarOffsetY)
|
2020-10-26 18:55:13 -04:00
|
|
|
defer target.Pop()
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-06-25 14:56:49 -04:00
|
|
|
expPercent := float64(g.hero.Stats.Experience) / float64(g.hero.Stats.NextLevelExp)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100))
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderMiniPanel(target d2interface.Surface) error {
|
|
|
|
width, height := target.GetSize()
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
|
|
|
|
2020-08-25 09:10:26 -04:00
|
|
|
menuButtonFrameIndex := 0
|
|
|
|
if g.miniPanel.isOpen {
|
|
|
|
menuButtonFrameIndex = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.menuButton.SetCurrentFrame(menuButtonFrameIndex); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
buttonX, buttonY := (width>>1)+miniPanelButtonOffsetX, height+miniPanelButtonOffsetY
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
g.menuButton.SetPosition(buttonX, buttonY)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.menuButton.Render(target)
|
|
|
|
g.miniPanel.Render(target)
|
2020-08-25 09:10:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
miniPanelButtons := map[actionableType]string{
|
|
|
|
miniPanelCharacter: "minipanelchar",
|
|
|
|
miniPanelInventory: "minipanelinv",
|
|
|
|
miniPanelSkillTree: "minipaneltree",
|
|
|
|
miniPanelAutomap: "minipanelautomap",
|
|
|
|
miniPanelMessageLog: "minipanelmessage",
|
|
|
|
miniPanelQuestLog: "minipanelquest",
|
|
|
|
miniPanelGameMenu: "minipanelmenubtn",
|
|
|
|
}
|
|
|
|
|
|
|
|
if !g.miniPanel.IsOpen() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for miniPanelButton, stringTableKey := range miniPanelButtons {
|
|
|
|
if !g.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
rect := &g.actionableRegions[miniPanelButton].rect
|
|
|
|
g.nameLabel.SetText(d2tbl.TranslateString(stringTableKey))
|
|
|
|
|
|
|
|
halfButtonWidth := rect.Width >> 1
|
|
|
|
halfButtonHeight := rect.Height >> 1
|
|
|
|
|
|
|
|
centerX := rect.Left + halfButtonWidth
|
|
|
|
centerY := rect.Top + halfButtonHeight
|
|
|
|
|
|
|
|
_, labelHeight := g.nameLabel.GetSize()
|
|
|
|
|
|
|
|
labelX := centerX
|
|
|
|
labelY := centerY - halfButtonHeight - labelHeight
|
|
|
|
|
|
|
|
g.nameLabel.SetPosition(labelX, labelY)
|
|
|
|
g.nameLabel.Render(target)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderPotions(x, _ int, target d2interface.Surface) error {
|
|
|
|
_, height := target.GetSize()
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.mainPanel.SetCurrentFrame(framePotions); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.mainPanel.SetPosition(x, height)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderNewSkillsButton(x, _ int, target d2interface.Surface) error {
|
|
|
|
_, height := target.GetSize()
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.mainPanel.SetCurrentFrame(frameNewSkillsSelector); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.mainPanel.SetPosition(x, height)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderRightSkill(x, _ int, target d2interface.Surface) error {
|
|
|
|
_, height := target.GetSize()
|
|
|
|
|
|
|
|
newSkillResourcePath := g.getSkillResourceByClass(g.hero.RightSkill.Charclass)
|
2020-10-22 16:53:18 -04:00
|
|
|
if newSkillResourcePath != g.rightSkillResource.SkillResourcePath {
|
|
|
|
g.rightSkillResource.SkillIcon, _ = g.ui.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
|
|
|
|
g.rightSkillResource.SkillResourcePath = newSkillResourcePath
|
2020-09-20 11:55:44 -04:00
|
|
|
}
|
|
|
|
|
2020-10-10 18:47:51 -04:00
|
|
|
if err := g.rightSkillResource.SkillIcon.SetCurrentFrame(g.hero.RightSkill.IconCel); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.rightSkillResource.SkillIcon.SetPosition(x, height)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.rightSkillResource.SkillIcon.Render(target)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderManaGlobe(x, _ int, target d2interface.Surface) error {
|
|
|
|
_, height := target.GetSize()
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.mainPanel.SetCurrentFrame(frameRightGlobeHolder); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.mainPanel.SetPosition(x, height)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-28 14:17:42 -04:00
|
|
|
g.mainPanel.Render(target)
|
2019-12-16 11:04:39 -05:00
|
|
|
|
2020-06-30 12:43:13 -04:00
|
|
|
// Mana status bar
|
|
|
|
manaPercent := float64(g.hero.Stats.Mana) / float64(g.hero.Stats.MaxMana)
|
|
|
|
manaBarHeight := int(manaPercent * float64(globeHeight))
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.hpManaStatusSprite.SetCurrentFrame(frameManaStatus); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.hpManaStatusSprite.SetPosition(x+manaStatusOffsetX, height+manaStatusOffsetY)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
manaMaskRect := image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)
|
2020-10-28 14:17:42 -04:00
|
|
|
g.hpManaStatusSprite.RenderSection(target, manaMaskRect)
|
2020-06-30 12:43:13 -04:00
|
|
|
|
2019-12-16 11:04:39 -05:00
|
|
|
// Right globe
|
2020-10-24 22:52:26 -04:00
|
|
|
if err := g.globeSprite.SetCurrentFrame(frameRightGlobe); err != nil {
|
2020-07-26 14:52:54 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.globeSprite.SetPosition(x+rightGlobeOffsetX, height+rightGlobeOffsetY)
|
2020-07-26 14:52:54 -04:00
|
|
|
|
2020-10-28 14:17:42 -04:00
|
|
|
g.globeSprite.Render(target)
|
|
|
|
g.globeSprite.Render(target)
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderHealthTooltip(target d2interface.Surface) {
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
2020-06-25 14:56:49 -04:00
|
|
|
|
2020-09-24 19:28:14 -04:00
|
|
|
// Create and format Health string from string lookup table.
|
|
|
|
fmtHealth := d2tbl.TranslateString("panelhealth")
|
2020-10-24 22:52:26 -04:00
|
|
|
healthCurr, healthMax := g.hero.Stats.Health, g.hero.Stats.MaxHealth
|
2020-09-24 19:28:14 -04:00
|
|
|
strPanelHealth := fmt.Sprintf(fmtHealth, healthCurr, healthMax)
|
|
|
|
|
2020-08-24 15:50:33 -04:00
|
|
|
// Display current hp and mana stats hpGlobe or manaGlobe region is clicked
|
2020-10-26 18:55:13 -04:00
|
|
|
if !g.actionableRegions[hpGlobe].rect.IsInRect(mx, my) || g.hpStatsIsVisible {
|
|
|
|
return
|
2020-08-24 15:50:33 -04:00
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.hpManaStatsLabel.SetText(strPanelHealth)
|
|
|
|
g.hpManaStatsLabel.SetPosition(hpLabelX, hpLabelY)
|
|
|
|
g.hpManaStatsLabel.Render(target)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) renderManaTooltip(target d2interface.Surface) {
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
|
|
|
|
2020-09-24 19:28:14 -04:00
|
|
|
// Create and format Mana string from string lookup table.
|
|
|
|
fmtMana := d2tbl.TranslateString("panelmana")
|
2020-10-24 22:52:26 -04:00
|
|
|
manaCurr, manaMax := g.hero.Stats.Mana, g.hero.Stats.MaxMana
|
2020-09-24 19:28:14 -04:00
|
|
|
strPanelMana := fmt.Sprintf(fmtMana, manaCurr, manaMax)
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
if !g.actionableRegions[manaGlobe].rect.IsInRect(mx, my) || g.manaStatsIsVisible {
|
|
|
|
return
|
2020-09-23 22:59:36 -04:00
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.hpManaStatsLabel.SetText(strPanelMana)
|
|
|
|
// In case if the mana value gets higher, we need to shift the
|
|
|
|
// label to the left a little, hence widthManaLabel.
|
|
|
|
widthManaLabel, _ := g.hpManaStatsLabel.GetSize()
|
|
|
|
xManaLabel := manaLabelX - widthManaLabel
|
|
|
|
g.hpManaStatsLabel.SetPosition(xManaLabel, manaLabelY)
|
|
|
|
g.hpManaStatsLabel.Render(target)
|
|
|
|
}
|
2020-09-23 22:59:36 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderRunWalkTooltip(target d2interface.Surface) {
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
2020-09-23 22:59:36 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
// Display run/walk tooltip when hovered.
|
|
|
|
// Note that whether the player is walking or running, the tooltip is the same in Diablo 2.
|
|
|
|
if !g.actionableRegions[walkRun].rect.IsInRect(mx, my) {
|
|
|
|
return
|
|
|
|
}
|
2020-09-23 22:59:36 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
var stringTableKey string
|
2020-09-23 22:59:36 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
if g.hero.IsRunToggled() {
|
|
|
|
stringTableKey = "RunOff"
|
|
|
|
} else {
|
|
|
|
stringTableKey = "RunOn"
|
2020-09-23 22:59:36 -04:00
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetText(d2tbl.TranslateString(stringTableKey))
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
rect := &g.actionableRegions[walkRun].rect
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
halfButtonWidth := rect.Width >> 1
|
|
|
|
halfButtonHeight := rect.Height >> 1
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
centerX := rect.Left + halfButtonWidth
|
|
|
|
centerY := rect.Top + halfButtonHeight
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
_, labelHeight := g.nameLabel.GetSize()
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
labelX := centerX
|
|
|
|
labelY := centerY - halfButtonHeight - labelHeight
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetPosition(labelX, labelY)
|
|
|
|
g.nameLabel.Render(target)
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderStaminaTooltip(target d2interface.Surface) {
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
// Display stamina tooltip when hovered.
|
|
|
|
if !g.actionableRegions[stamina].rect.IsInRect(mx, my) {
|
|
|
|
return
|
2020-09-23 22:59:36 -04:00
|
|
|
}
|
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
// Create and format Stamina string from string lookup table.
|
|
|
|
fmtStamina := d2tbl.TranslateString("panelstamina")
|
|
|
|
staminaCurr, staminaMax := int(g.hero.Stats.Stamina), g.hero.Stats.MaxStamina
|
|
|
|
strPanelStamina := fmt.Sprintf(fmtStamina, staminaCurr, staminaMax)
|
2020-09-24 19:28:14 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetText(strPanelStamina)
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
rect := &g.actionableRegions[stamina].rect
|
2020-09-23 22:59:36 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
halfButtonWidth := rect.Width >> 1
|
|
|
|
centerX := rect.Left + halfButtonWidth
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
_, labelHeight := g.nameLabel.GetSize()
|
|
|
|
halfLabelHeight := labelHeight >> 1
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
labelX := centerX
|
|
|
|
labelY := staminaExperienceY - halfLabelHeight
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetPosition(labelX, labelY)
|
|
|
|
g.nameLabel.Render(target)
|
|
|
|
}
|
2020-09-24 19:28:14 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
func (g *GameControls) renderExperienceTooltip(target d2interface.Surface) {
|
|
|
|
mx, my := g.lastMouseX, g.lastMouseY
|
2020-09-24 19:28:14 -04:00
|
|
|
|
2020-09-23 22:59:36 -04:00
|
|
|
// Display experience tooltip when hovered.
|
2020-10-26 18:55:13 -04:00
|
|
|
if !g.actionableRegions[xp].rect.IsInRect(mx, my) {
|
|
|
|
return
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
// Create and format Experience string from string lookup table.
|
|
|
|
fmtExp := d2tbl.TranslateString("panelexp")
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
// The English string for "panelexp" is "Experience: %u / %u", however %u doesn't
|
|
|
|
// translate well. So we need to rewrite %u into a formatable Go verb. %d is used in other
|
|
|
|
// strings, so we go with that, keeping in mind that %u likely referred to
|
|
|
|
// an unsigned integer.
|
|
|
|
fmtExp = strings.ReplaceAll(fmtExp, "%u", "%d")
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
expCurr, expMax := uint(g.hero.Stats.Experience), uint(g.hero.Stats.NextLevelExp)
|
|
|
|
strPanelExp := fmt.Sprintf(fmtExp, expCurr, expMax)
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetText(strPanelExp)
|
|
|
|
rect := &g.actionableRegions[stamina].rect
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
halfButtonWidth := rect.Width >> 1
|
|
|
|
centerX := rect.Left + halfButtonWidth
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
_, labelHeight := g.nameLabel.GetSize()
|
|
|
|
halfLabelHeight := labelHeight >> 1
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
labelX := centerX
|
|
|
|
labelY := staminaExperienceY - halfLabelHeight
|
2020-10-22 16:53:18 -04:00
|
|
|
|
2020-10-26 18:55:13 -04:00
|
|
|
g.nameLabel.SetPosition(labelX, labelY)
|
|
|
|
g.nameLabel.Render(target)
|
2020-06-22 15:55:32 -04:00
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// SetZoneChangeText sets the zoneChangeText
|
2020-06-22 15:55:32 -04:00
|
|
|
func (g *GameControls) SetZoneChangeText(text string) {
|
|
|
|
g.zoneChangeText.SetText(text)
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// ShowZoneChangeText shows the zoneChangeText
|
2020-06-22 15:55:32 -04:00
|
|
|
func (g *GameControls) ShowZoneChangeText() {
|
|
|
|
g.isZoneTextShown = true
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:01:33 -04:00
|
|
|
// HideZoneChangeTextAfter hides the zoneChangeText after the given amount of seconds
|
2020-06-22 15:55:32 -04:00
|
|
|
func (g *GameControls) HideZoneChangeTextAfter(delay float64) {
|
|
|
|
time.AfterFunc(time.Duration(delay)*time.Second, func() {
|
|
|
|
g.isZoneTextShown = false
|
|
|
|
})
|
2019-12-16 11:04:39 -05:00
|
|
|
}
|
2020-06-21 16:06:52 -04:00
|
|
|
|
2020-08-24 15:50:33 -04:00
|
|
|
// HpStatsIsVisible returns true if the hp and mana stats are visible to the player
|
|
|
|
func (g *GameControls) HpStatsIsVisible() bool {
|
|
|
|
return g.hpStatsIsVisible
|
|
|
|
}
|
|
|
|
|
|
|
|
// ManaStatsIsVisible returns true if the hp and mana stats are visible to the player
|
|
|
|
func (g *GameControls) ManaStatsIsVisible() bool {
|
|
|
|
return g.manaStatsIsVisible
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
// ToggleHpStats toggles the visibility of the hp and mana stats placed above their respective globe and load only if they do not match
|
2020-08-24 15:50:33 -04:00
|
|
|
func (g *GameControls) ToggleHpStats() {
|
|
|
|
g.hpStatsIsVisible = !g.hpStatsIsVisible
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToggleManaStats toggles the visibility of the hp and mana stats placed above their respective globe
|
|
|
|
func (g *GameControls) ToggleManaStats() {
|
|
|
|
g.manaStatsIsVisible = !g.manaStatsIsVisible
|
|
|
|
}
|
|
|
|
|
2020-06-23 12:28:05 -04:00
|
|
|
// Handles what to do when an actionable is hovered
|
2020-10-22 02:41:21 -04:00
|
|
|
func (g *GameControls) onHoverActionable(item actionableType) {
|
2020-10-24 22:52:26 -04:00
|
|
|
hoverMap := map[actionableType]func(){
|
|
|
|
leftSkill: func() {},
|
|
|
|
newStats: func() {},
|
|
|
|
xp: func() {},
|
|
|
|
walkRun: func() {},
|
|
|
|
stamina: func() {},
|
|
|
|
miniPnl: func() {},
|
|
|
|
newSkills: func() {},
|
|
|
|
rightSkill: func() {},
|
|
|
|
hpGlobe: func() {},
|
|
|
|
manaGlobe: func() {},
|
|
|
|
miniPanelCharacter: func() {},
|
|
|
|
miniPanelInventory: func() {},
|
|
|
|
miniPanelSkillTree: func() {},
|
|
|
|
miniPanelAutomap: func() {},
|
|
|
|
miniPanelMessageLog: func() {},
|
|
|
|
miniPanelQuestLog: func() {},
|
|
|
|
miniPanelGameMenu: func() {},
|
|
|
|
}
|
|
|
|
|
|
|
|
onHover, found := hoverMap[item]
|
|
|
|
if !found {
|
2020-10-22 02:41:21 -04:00
|
|
|
log.Printf("Unrecognized actionableType(%d) being hovered\n", item)
|
2020-10-24 22:52:26 -04:00
|
|
|
return
|
2020-06-23 12:28:05 -04:00
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
|
|
|
onHover()
|
2020-06-23 12:28:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handles what to do when an actionable is clicked
|
2020-10-22 02:41:21 -04:00
|
|
|
func (g *GameControls) onClickActionable(item actionableType) {
|
2020-10-24 22:52:26 -04:00
|
|
|
actionMap := map[actionableType]func(){
|
|
|
|
leftSkill: func() {
|
|
|
|
g.skillSelectMenu.ToggleLeftPanel()
|
|
|
|
},
|
2020-08-25 09:10:26 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
newStats: func() {
|
|
|
|
log.Println("New Stats Selector Action Pressed")
|
|
|
|
},
|
2020-08-25 09:10:26 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
xp: func() {
|
|
|
|
log.Println("XP Action Pressed")
|
|
|
|
},
|
2020-10-22 12:54:45 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
walkRun: func() {
|
|
|
|
log.Println("Walk/Run Action Pressed")
|
|
|
|
},
|
|
|
|
|
|
|
|
stamina: func() {
|
|
|
|
log.Println("Stamina Action Pressed")
|
|
|
|
},
|
|
|
|
|
|
|
|
miniPnl: func() {
|
|
|
|
log.Println("Mini Panel Action Pressed")
|
|
|
|
|
|
|
|
g.miniPanel.Toggle()
|
|
|
|
},
|
|
|
|
|
|
|
|
newSkills: func() {
|
|
|
|
log.Println("New Skills Selector Action Pressed")
|
|
|
|
},
|
|
|
|
|
|
|
|
rightSkill: func() {
|
|
|
|
g.skillSelectMenu.ToggleRightPanel()
|
|
|
|
},
|
|
|
|
|
|
|
|
hpGlobe: func() {
|
|
|
|
g.ToggleHpStats()
|
|
|
|
log.Println("HP Globe Pressed")
|
|
|
|
},
|
|
|
|
|
|
|
|
manaGlobe: func() {
|
|
|
|
g.ToggleManaStats()
|
|
|
|
log.Println("Mana Globe Pressed")
|
|
|
|
},
|
|
|
|
|
|
|
|
miniPanelCharacter: func() {
|
|
|
|
log.Println("Character button on mini panel is pressed")
|
|
|
|
|
|
|
|
g.heroStatsPanel.Toggle()
|
|
|
|
g.updateLayout()
|
|
|
|
},
|
|
|
|
|
|
|
|
miniPanelInventory: func() {
|
|
|
|
log.Println("Inventory button on mini panel is pressed")
|
|
|
|
|
|
|
|
g.inventory.Toggle()
|
|
|
|
g.updateLayout()
|
|
|
|
},
|
|
|
|
|
|
|
|
miniPanelSkillTree: func() {
|
|
|
|
log.Println("Skilltree button on mini panel is pressed")
|
|
|
|
|
|
|
|
g.skilltree.Toggle()
|
|
|
|
g.updateLayout()
|
|
|
|
},
|
|
|
|
|
|
|
|
miniPanelGameMenu: func() {
|
|
|
|
g.miniPanel.Close()
|
|
|
|
g.escapeMenu.open()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
action, found := actionMap[item]
|
|
|
|
if !found {
|
2020-10-22 02:41:21 -04:00
|
|
|
log.Printf("Unrecognized actionableType(%d) being clicked\n", item)
|
2020-10-24 22:52:26 -04:00
|
|
|
return
|
2020-06-23 12:28:05 -04:00
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
|
|
|
action()
|
2020-06-23 12:28:05 -04:00
|
|
|
}
|
2020-09-20 11:55:44 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
func (g *GameControls) bindFreeCamCommand(term d2interface.Terminal) error {
|
|
|
|
return term.BindAction("freecam", "toggle free camera movement", func() {
|
2020-10-22 16:53:18 -04:00
|
|
|
g.FreeCam = !g.FreeCam
|
|
|
|
})
|
2020-10-24 22:52:26 -04:00
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
func (g *GameControls) bindSetLeftSkillCommand(term d2interface.Terminal) error {
|
|
|
|
setLeftSkill := func(id int) {
|
2020-10-22 16:53:18 -04:00
|
|
|
skillRecord := g.asset.Records.Skill.Details[id]
|
|
|
|
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
if err != nil {
|
|
|
|
term.OutputErrorf("cannot create skill with ID of %d, error: %s", id, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
g.hero.LeftSkill = skill
|
2020-10-24 22:52:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return term.BindAction(
|
|
|
|
"setleftskill",
|
|
|
|
"set skill to fire on left click",
|
|
|
|
setLeftSkill,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) bindSetRightSkillCommand(term d2interface.Terminal) error {
|
|
|
|
setRightSkill := func(id int) {
|
|
|
|
skillRecord := g.asset.Records.Skill.Details[id]
|
|
|
|
skill, err := g.heroState.CreateHeroSkill(0, skillRecord.Skill)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
term.OutputErrorf("cannot create skill with ID of %d, error: %s", id, err)
|
|
|
|
return
|
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
g.hero.RightSkill = skill
|
|
|
|
}
|
|
|
|
|
|
|
|
return term.BindAction(
|
|
|
|
"setrightskill",
|
|
|
|
"set skill to fire on right click",
|
|
|
|
setRightSkill,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const classTokenLength = 3
|
|
|
|
|
|
|
|
func (g *GameControls) bindLearnSkillsCommand(term d2interface.Terminal) error {
|
|
|
|
learnSkills := func(token string) {
|
|
|
|
if len(token) < classTokenLength {
|
2020-10-22 16:53:18 -04:00
|
|
|
term.OutputErrorf("The given class token should be at least 3 characters")
|
|
|
|
return
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
validPrefixes := []string{"ama", "ass", "nec", "bar", "sor", "dru", "pal"}
|
|
|
|
classToken := strings.ToLower(token)
|
|
|
|
tokenPrefix := classToken[0:3]
|
|
|
|
isValidToken := false
|
|
|
|
|
|
|
|
for idx := range validPrefixes {
|
|
|
|
if strings.Compare(tokenPrefix, validPrefixes[idx]) == 0 {
|
|
|
|
isValidToken = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isValidToken {
|
2020-10-24 22:52:26 -04:00
|
|
|
fmtInvalid := "Invalid class, must be a value starting with(case insensitive): %s"
|
|
|
|
term.OutputErrorf(fmtInvalid, strings.Join(validPrefixes, ", "))
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
var err error
|
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
learnedSkillsCount := 0
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
for _, skillDetailRecord := range g.asset.Records.Skill.Details {
|
2020-10-29 09:50:15 -04:00
|
|
|
if skillDetailRecord.Charclass != classToken {
|
2020-10-24 22:52:26 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
skill, skillErr := g.heroState.CreateHeroSkill(1, skillDetailRecord.Skill)
|
|
|
|
if skill == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
learnedSkillsCount++
|
|
|
|
|
|
|
|
g.hero.Skills[skill.ID] = skill
|
|
|
|
|
|
|
|
if skillErr != nil {
|
|
|
|
err = skillErr
|
|
|
|
break
|
2020-10-22 16:53:18 -04:00
|
|
|
}
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
|
2020-10-22 16:53:18 -04:00
|
|
|
g.skillSelectMenu.RegenerateImageCache()
|
|
|
|
log.Printf("Learned %d skills", learnedSkillsCount)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
term.OutputErrorf("cannot learn skill for class, error: %s", err)
|
|
|
|
return
|
|
|
|
}
|
2020-10-24 22:52:26 -04:00
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
return term.BindAction(
|
|
|
|
"learnskills",
|
|
|
|
"learn all skills for the a given class",
|
|
|
|
learnSkills,
|
|
|
|
)
|
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
func (g *GameControls) bindLearnSkillByIDCommand(term d2interface.Terminal) error {
|
|
|
|
learnByID := func(id int) {
|
2020-10-22 16:53:18 -04:00
|
|
|
skillRecord := g.asset.Records.Skill.Details[id]
|
|
|
|
if skillRecord == nil {
|
|
|
|
term.OutputErrorf("cannot find a skill record for ID: %d", id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
skill, err := g.heroState.CreateHeroSkill(1, skillRecord.Skill)
|
2020-10-24 22:52:26 -04:00
|
|
|
if skill == nil {
|
|
|
|
term.OutputErrorf("cannot create skill: %s", skillRecord.Skill)
|
|
|
|
return
|
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
|
|
|
g.hero.Skills[skill.ID] = skill
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
term.OutputErrorf("cannot learn skill for class, error: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
g.skillSelectMenu.RegenerateImageCache()
|
|
|
|
log.Println("Learned skill: ", skill.Skill)
|
2020-10-24 22:52:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return term.BindAction(
|
|
|
|
"learnskillid",
|
|
|
|
"learn a skill by a given ID",
|
|
|
|
learnByID,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GameControls) bindTerminalCommands(term d2interface.Terminal) error {
|
|
|
|
if err := g.bindFreeCamCommand(term); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.bindSetLeftSkillCommand(term); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.bindSetRightSkillCommand(term); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.bindLearnSkillsCommand(term); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.bindLearnSkillByIDCommand(term); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-22 16:53:18 -04:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:55:44 -04:00
|
|
|
func (g *GameControls) getSkillResourceByClass(class string) string {
|
2020-10-24 22:52:26 -04:00
|
|
|
resourceMap := map[string]string{
|
|
|
|
"": d2resource.GenericSkills,
|
|
|
|
"bar": d2resource.BarbarianSkills,
|
|
|
|
"nec": d2resource.NecromancerSkills,
|
|
|
|
"pal": d2resource.PaladinSkills,
|
|
|
|
"ass": d2resource.AssassinSkills,
|
|
|
|
"sor": d2resource.SorcererSkills,
|
|
|
|
"ama": d2resource.AmazonSkills,
|
|
|
|
"dru": d2resource.DruidSkills,
|
|
|
|
}
|
|
|
|
|
|
|
|
entry, found := resourceMap[class]
|
|
|
|
if !found {
|
2020-09-20 11:55:44 -04:00
|
|
|
log.Fatalf("Unknown class token: '%s'", class)
|
|
|
|
}
|
|
|
|
|
2020-10-24 22:52:26 -04:00
|
|
|
return entry
|
2020-09-20 11:55:44 -04:00
|
|
|
}
|