1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-23 15:45:24 +00:00
OpenDiablo2/d2game/d2player/hud.go
juander 01927d0f3b d2core/d2ui: Add checks to all widgets if they implement Widget
this also adds missing methods to elements not implementing widget. Note
here that we do not enable sprite and label, as this would produce a
crazy amount of linter warnings due to render() requiering error
handling then, which non of the callers handle. Since we remove the
render calls later anyways, we can postpone this static check for now.
2020-11-09 18:13:17 +01:00

844 lines
22 KiB
Go

package d2player
import (
"fmt"
"image"
"log"
"math"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
const (
runButtonX = 255
runButtonY = 570
)
const (
zoneChangeTextX = screenWidth / 2
zoneChangeTextY = screenHeight / 4
)
const (
expBarWidth = 120.0
staminaBarWidth = 102.0
staminaBarHeight = 19.0
globeHeight = 80
globeWidth = 80
hoverLabelOuterPad = 5
percentStaminaBarLow = 0.25
)
const (
hpLabelX = 15
hpLabelY = 487
manaLabelX = 785
manaLabelY = 487
staminaExperienceY = 535
)
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
redAlpha72 = 0xff0000c8
whiteAlpha100 = 0xffffffff
)
// HUD represents the always visible user interface of the game
type HUD struct {
actionableRegions []actionableRegion
asset *d2asset.AssetManager
uiManager *d2ui.UIManager
help *HelpOverlay
mapEngine *d2mapengine.MapEngine
mapRenderer *d2maprenderer.MapRenderer
lastMouseX int
lastMouseY int
hero *d2mapentity.Player
mainPanel *d2ui.Sprite
globeSprite *d2ui.Sprite
menuButton *d2ui.Sprite
hpManaStatusSprite *d2ui.Sprite
leftSkillResource *SkillResource
rightSkillResource *SkillResource
runButton *d2ui.Button
zoneChangeText *d2ui.Label
miniPanel *miniPanel
isZoneTextShown bool
hpStatsIsVisible bool
manaStatsIsVisible bool
skillSelectMenu *SkillSelectMenu
staminaTooltip *d2ui.Tooltip
runWalkTooltip *d2ui.Tooltip
experienceTooltip *d2ui.Tooltip
healthTooltip *d2ui.Tooltip
manaTooltip *d2ui.Tooltip
miniPanelTooltip *d2ui.Tooltip
nameLabel *d2ui.Label
}
// NewHUD creates a HUD object
func NewHUD(
asset *d2asset.AssetManager,
ui *d2ui.UIManager,
hero *d2mapentity.Player,
help *HelpOverlay,
miniPanel *miniPanel,
actionableRegions []actionableRegion,
mapEngine *d2mapengine.MapEngine,
mapRenderer *d2maprenderer.MapRenderer,
) *HUD {
nameLabel := ui.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
nameLabel.Alignment = d2gui.HorizontalAlignCenter
nameLabel.SetText(d2ui.ColorTokenize("", d2ui.ColorTokenServer))
zoneLabel := ui.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
zoneLabel.Alignment = d2gui.HorizontalAlignCenter
return &HUD{
asset: asset,
uiManager: ui,
hero: hero,
help: help,
mapEngine: mapEngine,
mapRenderer: mapRenderer,
miniPanel: miniPanel,
actionableRegions: actionableRegions,
nameLabel: nameLabel,
skillSelectMenu: NewSkillSelectMenu(asset, ui, hero),
zoneChangeText: zoneLabel,
}
}
// Load creates the ui elemets
func (h *HUD) Load() {
var err error
h.globeSprite, err = h.uiManager.NewSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
h.hpManaStatusSprite, err = h.uiManager.NewSprite(d2resource.HealthManaIndicator, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
h.menuButton, err = h.uiManager.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
err = h.menuButton.SetCurrentFrame(frameMenuButton)
if err != nil {
log.Print(err)
}
h.mainPanel, err = h.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
// https://github.com/OpenDiablo2/OpenDiablo2/issues/799
genericSkillsSprite, err := h.uiManager.NewSprite(d2resource.GenericSkills, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
attackIconID := 2
h.leftSkillResource = &SkillResource{
SkillIcon: genericSkillsSprite,
IconNumber: attackIconID,
SkillResourcePath: d2resource.GenericSkills,
}
h.rightSkillResource = &SkillResource{
SkillIcon: genericSkillsSprite,
IconNumber: attackIconID,
SkillResourcePath: d2resource.GenericSkills,
}
h.loadUIButtons()
h.loadTooltips()
}
func (h *HUD) loadTooltips() {
// stamina tooltip
h.staminaTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
rect := &h.actionableRegions[stamina].rect
halfButtonWidth := rect.Width >> 1
centerX := rect.Left + halfButtonWidth
_, labelHeight := h.staminaTooltip.GetSize()
halfLabelHeight := labelHeight >> 1
labelX := centerX
labelY := staminaExperienceY - halfLabelHeight
h.staminaTooltip.SetPosition(labelX, labelY)
// runwalk tooltip
h.runWalkTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYBottom)
rect = &h.actionableRegions[walkRun].rect
halfButtonWidth = rect.Width >> 1
halfButtonHeight := rect.Height >> 1
centerX = rect.Left + halfButtonWidth
centerY := rect.Top + halfButtonHeight
_, labelHeight = h.runWalkTooltip.GetSize()
labelX = centerX
labelY = centerY - halfButtonHeight - labelHeight
h.runWalkTooltip.SetPosition(labelX, labelY)
// experience tooltip
h.experienceTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
rect = &h.actionableRegions[stamina].rect
halfButtonWidth = rect.Width >> 1
centerX = rect.Left + halfButtonWidth
_, labelHeight = h.experienceTooltip.GetSize()
halfLabelHeight = labelHeight >> 1
labelX = centerX
labelY = staminaExperienceY - halfLabelHeight
h.experienceTooltip.SetPosition(labelX, labelY)
// Health tooltip
h.healthTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteUnits, d2ui.TooltipXLeft, d2ui.TooltipYTop)
h.healthTooltip.SetPosition(hpLabelX, hpLabelY)
h.healthTooltip.SetBoxEnabled(false)
// Health tooltip
h.manaTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteUnits, d2ui.TooltipXLeft, d2ui.TooltipYTop)
h.manaTooltip.SetPosition(manaLabelX, manaLabelY)
h.manaTooltip.SetBoxEnabled(false)
// minipanel tooltip
h.miniPanelTooltip = h.uiManager.NewTooltip(d2resource.Font16, d2resource.PaletteUnits, d2ui.TooltipXCenter, d2ui.TooltipYTop)
}
func (h *HUD) loadUIButtons() {
// Run button
h.runButton = h.uiManager.NewButton(d2ui.ButtonTypeRun, "")
h.runButton.SetPosition(runButtonX, runButtonY)
h.runButton.OnActivated(func() { h.onToggleRunButton(false) })
if h.hero.IsRunToggled() {
h.runButton.Toggle()
}
}
func (h *HUD) onToggleRunButton(noButton bool) {
if !noButton {
h.runButton.Toggle()
}
h.hero.ToggleRunWalk()
// https://github.com/OpenDiablo2/OpenDiablo2/issues/800
h.hero.SetIsRunning(h.hero.IsRunToggled())
}
// 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 (h *HUD) renderGameControlPanelElements(target d2interface.Surface) error {
_, height := target.GetSize()
offsetX, offsetY := 0, 0
// Main panel background
offsetY = height
if err := h.renderPanel(offsetX, offsetY, target); err != nil {
return err
}
// Health globe
w, _ := h.mainPanel.GetCurrentFrameSize()
if err := h.renderHealthGlobe(offsetX, offsetY, target); err != nil {
return err
}
// Left Skill
offsetX += w
if err := h.renderLeftSkill(offsetX, offsetY, target); err != nil {
return err
}
// New Stats Button
w, _ = h.leftSkillResource.SkillIcon.GetCurrentFrameSize()
offsetX += w
if err := h.renderNewStatsButton(offsetX, offsetY, target); err != nil {
return err
}
// Stamina
w, _ = h.mainPanel.GetCurrentFrameSize()
offsetX += w
if err := h.renderStamina(offsetX, offsetY, target); err != nil {
return err
}
// Stamina status bar
w, _ = h.mainPanel.GetCurrentFrameSize()
offsetX += w
if err := h.renderStaminaBar(target); err != nil {
return err
}
// Experience status bar
if err := h.renderExperienceBar(target); err != nil {
return err
}
// Mini Panel and button
if err := h.renderMiniPanel(target); err != nil {
return err
}
// Potions
if err := h.renderPotions(offsetX, offsetY, target); err != nil {
return err
}
// New Skills Button
w, _ = h.mainPanel.GetCurrentFrameSize()
offsetX += w
if err := h.renderNewSkillsButton(offsetX, offsetY, target); err != nil {
return err
}
// Right skill
w, _ = h.mainPanel.GetCurrentFrameSize()
offsetX += w
if err := h.renderRightSkill(offsetX, offsetY, target); err != nil {
return err
}
// Mana Globe
w, _ = h.rightSkillResource.SkillIcon.GetCurrentFrameSize()
offsetX += w
if err := h.renderManaGlobe(offsetX, offsetY, target); err != nil {
return err
}
return nil
}
func (h *HUD) renderManaGlobe(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
if err := h.mainPanel.SetCurrentFrame(frameRightGlobeHolder); err != nil {
return err
}
h.mainPanel.SetPosition(x, height)
h.mainPanel.Render(target)
// Mana status bar
manaPercent := float64(h.hero.Stats.Mana) / float64(h.hero.Stats.MaxMana)
manaBarHeight := int(manaPercent * float64(globeHeight))
if err := h.hpManaStatusSprite.SetCurrentFrame(frameManaStatus); err != nil {
return err
}
h.hpManaStatusSprite.SetPosition(x+manaStatusOffsetX, height+manaStatusOffsetY)
manaMaskRect := image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)
h.hpManaStatusSprite.RenderSection(target, manaMaskRect)
// Right globe
if err := h.globeSprite.SetCurrentFrame(frameRightGlobe); err != nil {
return err
}
h.globeSprite.SetPosition(x+rightGlobeOffsetX, height+rightGlobeOffsetY)
h.globeSprite.Render(target)
h.globeSprite.Render(target)
return nil
}
func (h *HUD) renderHealthGlobe(x, y int, target d2interface.Surface) error {
healthPercent := float64(h.hero.Stats.Health) / float64(h.hero.Stats.MaxHealth)
hpBarHeight := int(healthPercent * float64(globeHeight))
if err := h.hpManaStatusSprite.SetCurrentFrame(0); err != nil {
return err
}
h.hpManaStatusSprite.SetPosition(x+healthStatusOffsetX, y+healthStatusOffsetY)
healthMaskRect := image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight)
h.hpManaStatusSprite.RenderSection(target, healthMaskRect)
// Left globe
if err := h.globeSprite.SetCurrentFrame(frameHealthStatus); err != nil {
return err
}
h.globeSprite.SetPosition(x+globeSpriteOffsetX, y+globeSpriteOffsetY)
h.globeSprite.Render(target)
return nil
}
func (h *HUD) renderPanel(x, y int, target d2interface.Surface) error {
if err := h.mainPanel.SetCurrentFrame(0); err != nil {
return err
}
h.mainPanel.SetPosition(x, y)
h.mainPanel.Render(target)
return nil
}
func (h *HUD) renderLeftSkill(x, y int, target d2interface.Surface) error {
newSkillResourcePath := h.getSkillResourceByClass(h.hero.LeftSkill.Charclass)
if newSkillResourcePath != h.leftSkillResource.SkillResourcePath {
h.leftSkillResource.SkillResourcePath = newSkillResourcePath
h.leftSkillResource.SkillIcon, _ = h.uiManager.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
}
if err := h.leftSkillResource.SkillIcon.SetCurrentFrame(h.hero.LeftSkill.IconCel); err != nil {
return err
}
h.leftSkillResource.SkillIcon.SetPosition(x, y)
h.leftSkillResource.SkillIcon.Render(target)
return nil
}
func (h *HUD) renderRightSkill(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
newSkillResourcePath := h.getSkillResourceByClass(h.hero.RightSkill.Charclass)
if newSkillResourcePath != h.rightSkillResource.SkillResourcePath {
h.rightSkillResource.SkillIcon, _ = h.uiManager.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
h.rightSkillResource.SkillResourcePath = newSkillResourcePath
}
if err := h.rightSkillResource.SkillIcon.SetCurrentFrame(h.hero.RightSkill.IconCel); err != nil {
return err
}
h.rightSkillResource.SkillIcon.SetPosition(x, height)
h.rightSkillResource.SkillIcon.Render(target)
return nil
}
func (h *HUD) renderNewStatsButton(x, y int, target d2interface.Surface) error {
if err := h.mainPanel.SetCurrentFrame(frameNewStatsSelector); err != nil {
return err
}
h.mainPanel.SetPosition(x, y)
h.mainPanel.Render(target)
return nil
}
func (h *HUD) renderStamina(x, y int, target d2interface.Surface) error {
if err := h.mainPanel.SetCurrentFrame(frameStamina); err != nil {
return err
}
h.mainPanel.SetPosition(x, y)
h.mainPanel.Render(target)
return nil
}
func (h *HUD) renderStaminaBar(target d2interface.Surface) error {
target.PushTranslation(staminaBarOffsetX, staminaBarOffsetY)
defer target.Pop()
target.PushEffect(d2enum.DrawEffectModulate)
defer target.Pop()
staminaPercent := h.hero.Stats.Stamina / float64(h.hero.Stats.MaxStamina)
staminaBarColor := d2util.Color(lightBrownAlpha72)
if staminaPercent < percentStaminaBarLow {
staminaBarColor = d2util.Color(redAlpha72)
}
target.DrawRect(int(staminaPercent*staminaBarWidth), staminaBarHeight, staminaBarColor)
return nil
}
func (h *HUD) renderExperienceBar(target d2interface.Surface) error {
target.PushTranslation(experienceBarOffsetX, experienceBarOffsetY)
defer target.Pop()
expPercent := float64(h.hero.Stats.Experience) / float64(h.hero.Stats.NextLevelExp)
target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100))
return nil
}
func (h *HUD) renderMiniPanel(target d2interface.Surface) error {
width, height := target.GetSize()
mx, my := h.lastMouseX, h.lastMouseY
menuButtonFrameIndex := 0
if h.miniPanel.isOpen {
menuButtonFrameIndex = 2
}
if err := h.menuButton.SetCurrentFrame(menuButtonFrameIndex); err != nil {
return err
}
buttonX, buttonY := (width>>1)+miniPanelButtonOffsetX, height+miniPanelButtonOffsetY
h.menuButton.SetPosition(buttonX, buttonY)
h.menuButton.Render(target)
h.miniPanel.Render(target)
miniPanelButtons := map[actionableType]string{
miniPanelCharacter: "minipanelchar",
miniPanelInventory: "minipanelinv",
miniPanelSkillTree: "minipaneltree",
miniPanelAutomap: "minipanelautomap",
miniPanelMessageLog: "minipanelmessage",
miniPanelQuestLog: "minipanelquest",
miniPanelGameMenu: "minipanelmenubtn",
}
if !h.miniPanel.IsOpen() {
return nil
}
for miniPanelButton, stringTableKey := range miniPanelButtons {
if !h.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
continue
}
rect := &h.actionableRegions[miniPanelButton].rect
h.miniPanelTooltip.SetText(h.asset.TranslateString(stringTableKey))
halfButtonWidth := rect.Width >> 1
halfButtonHeight := rect.Height >> 1
centerX := rect.Left + halfButtonWidth
centerY := rect.Top + halfButtonHeight
_, labelHeight := h.miniPanelTooltip.GetSize()
labelX := centerX
labelY := centerY - halfButtonHeight - labelHeight
h.miniPanelTooltip.SetPosition(labelX, labelY)
if err := h.miniPanelTooltip.Render(target); err != nil {
return err
}
}
return nil
}
func (h *HUD) renderPotions(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
if err := h.mainPanel.SetCurrentFrame(framePotions); err != nil {
return err
}
h.mainPanel.SetPosition(x, height)
h.mainPanel.Render(target)
return nil
}
func (h *HUD) renderNewSkillsButton(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
if err := h.mainPanel.SetCurrentFrame(frameNewSkillsSelector); err != nil {
return err
}
h.mainPanel.SetPosition(x, height)
h.mainPanel.Render(target)
return nil
}
//nolint:golint,dupl // we clean this up later
func (h *HUD) renderHealthTooltip(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
// Create and format Health string from string lookup table.
fmtHealth := h.asset.TranslateString("panelhealth")
healthCurr, healthMax := h.hero.Stats.Health, h.hero.Stats.MaxHealth
strPanelHealth := fmt.Sprintf(fmtHealth, healthCurr, healthMax)
// Display current hp and mana stats hpGlobe or manaGlobe region is clicked
if !(h.actionableRegions[hpGlobe].rect.IsInRect(mx, my) || h.hpStatsIsVisible) {
return
}
h.healthTooltip.SetText(strPanelHealth)
if err := h.healthTooltip.Render(target); err != nil {
log.Printf("Cannot render tooltip, %e", err)
}
}
//nolint:golint,dupl // we clean this up later
func (h *HUD) renderManaTooltip(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
// Create and format Mana string from string lookup table.
fmtMana := h.asset.TranslateString("panelmana")
manaCurr, manaMax := h.hero.Stats.Mana, h.hero.Stats.MaxMana
strPanelMana := fmt.Sprintf(fmtMana, manaCurr, manaMax)
if !(h.actionableRegions[manaGlobe].rect.IsInRect(mx, my) || h.manaStatsIsVisible) {
return
}
h.manaTooltip.SetText(strPanelMana)
if err := h.manaTooltip.Render(target); err != nil {
log.Printf("Cannot render tooltip, %e", err)
}
}
func (h *HUD) renderRunWalkTooltip(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
// Display run/walk tooltip when hovered.
// Note that whether the player is walking or running, the tooltip is the same in Diablo 2.
if !h.actionableRegions[walkRun].rect.IsInRect(mx, my) {
return
}
var stringTableKey string
if h.hero.IsRunToggled() {
stringTableKey = "RunOff"
} else {
stringTableKey = "RunOn"
}
h.runWalkTooltip.SetText(h.asset.TranslateString(stringTableKey))
if err := h.runWalkTooltip.Render(target); err != nil {
log.Printf("Cannot render tooltip, %e", err)
}
}
func (h *HUD) renderStaminaTooltip(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
// Display stamina tooltip when hovered.
if !h.actionableRegions[stamina].rect.IsInRect(mx, my) {
return
}
// Create and format Stamina string from string lookup table.
fmtStamina := h.asset.TranslateString("panelstamina")
staminaCurr, staminaMax := int(h.hero.Stats.Stamina), h.hero.Stats.MaxStamina
strPanelStamina := fmt.Sprintf(fmtStamina, staminaCurr, staminaMax)
h.staminaTooltip.SetText(strPanelStamina)
if err := h.staminaTooltip.Render(target); err != nil {
log.Printf("Cannot render tooltip, %e", err)
}
}
func (h *HUD) renderExperienceTooltip(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
// Display experience tooltip when hovered.
if !h.actionableRegions[xp].rect.IsInRect(mx, my) {
return
}
// Create and format Experience string from string lookup table.
fmtExp := h.asset.TranslateString("panelexp")
// 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")
expCurr, expMax := uint(h.hero.Stats.Experience), uint(h.hero.Stats.NextLevelExp)
strPanelExp := fmt.Sprintf(fmtExp, expCurr, expMax)
h.experienceTooltip.SetText(strPanelExp)
if err := h.experienceTooltip.Render(target); err != nil {
log.Printf("Cannot render tooltip, %e", err)
}
}
func (h *HUD) renderForSelectableEntitiesHovered(target d2interface.Surface) {
mx, my := h.lastMouseX, h.lastMouseY
for entityIdx := range h.mapEngine.Entities() {
entity := (h.mapEngine.Entities())[entityIdx]
if !entity.Selectable() {
continue
}
entPos := entity.GetPosition()
entOffset := entPos.RenderOffset()
entScreenXf, entScreenYf := h.mapRenderer.WorldToScreenF(entity.GetPositionF())
entScreenX := int(math.Floor(entScreenXf))
entScreenY := int(math.Floor(entScreenYf))
entityWidth, entityHeight := entity.GetSize()
halfWidth, halfHeight := entityWidth>>1, entityHeight>>1
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())
h.nameLabel.SetText(entity.Label())
xLabel, yLabel := entScreenX-xOff, entScreenY-yOff-entityHeight-hoverLabelOuterPad
h.nameLabel.SetPosition(xLabel, yLabel)
h.nameLabel.Render(target)
entity.Highlight()
break
}
}
}
// Render draws the HUD to the screen
func (h *HUD) Render(target d2interface.Surface) error {
h.renderForSelectableEntitiesHovered(target)
if err := h.renderGameControlPanelElements(target); err != nil {
return err
}
if err := h.help.Render(target); err != nil {
return err
}
if h.isZoneTextShown {
h.zoneChangeText.SetPosition(zoneChangeTextX, zoneChangeTextY)
h.zoneChangeText.Render(target)
}
h.renderHealthTooltip(target)
h.renderManaTooltip(target)
h.renderRunWalkTooltip(target)
h.renderStaminaTooltip(target)
h.renderExperienceTooltip(target)
if h.skillSelectMenu.IsOpen() {
h.skillSelectMenu.Render(target)
}
return nil
}
func (h *HUD) getSkillResourceByClass(class string) string {
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 {
log.Fatalf("Unknown class token: '%s'", class)
}
return entry
}
// OnMouseMove handles mouse move events
func (h *HUD) OnMouseMove(event d2interface.MouseMoveEvent) bool {
mx, my := event.X(), event.Y()
h.lastMouseX = mx
h.lastMouseY = my
h.skillSelectMenu.LeftPanel.HandleMouseMove(mx, my)
h.skillSelectMenu.RightPanel.HandleMouseMove(mx, my)
return false
}