Lint cleanup funlen (#854)

* d2ui/button.go: refactored button state prerender to fix funlen lint error

* d2player/help/help.go: break up Load method to reduce complexity

* d2game/d2player/game_controls.go: refactored renderHUD method, fixed funlen lint error

* d2player/skilltree.go: fixed funlen lint error

* map_engine_test.go: fixed funlen lint error
This commit is contained in:
gravestench 2020-10-26 22:55:13 +00:00 committed by GitHub
parent 7d5cd10850
commit 33368e5aa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 524 additions and 363 deletions

View File

@ -47,6 +47,12 @@ const (
ButtonNoFixedHeight int = -1
)
const (
buttonStatePressed = iota + 1
buttonStateToggled
buttonStatePressedToggled
)
const (
closeButtonBaseFrame = 10 // base frame offset of the "close" button dc6
)
@ -315,21 +321,28 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button {
buttonSprite.SetPosition(0, 0)
buttonSprite.SetEffect(d2enum.DrawEffectModulate)
ui.addWidget(btn) // important that this comes before renderFrames!
ui.addWidget(btn) // important that this comes before prerenderStates!
btn.renderFrames(buttonSprite, &buttonLayout, lbl)
btn.prerenderStates(buttonSprite, &buttonLayout, lbl)
return btn
}
func (v *Button) renderFrames(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) {
type buttonStateDescriptor struct {
baseFrame int
offsetX, offsetY int
prerenderdestination *d2interface.Surface
fmtErr string
}
func (v *Button) prerenderStates(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) {
var err error
totalButtonTypes := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments)
numButtonStates := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments)
// buttons always have a base image
if v.buttonLayout.HasImage {
err = btnSprite.RenderSegmented(v.normalSurface, btnLayout.XSegments, btnLayout.YSegments, btnLayout.BaseFrame)
if err != nil {
fmt.Printf("failed to render button normalSurface, err: %v\n", err)
}
@ -342,89 +355,82 @@ func (v *Button) renderFrames(btnSprite *Sprite, btnLayout *ButtonLayout, label
label.SetPosition(xOffset, textY)
label.Render(v.normalSurface)
if btnLayout.HasImage && btnLayout.AllowFrameChange {
frameOffset := 0
xSeg, ySeg, baseFrame := btnLayout.XSegments, btnLayout.YSegments, btnLayout.BaseFrame
if !btnLayout.HasImage || !btnLayout.AllowFrameChange {
return
}
totalButtonTypes--
if totalButtonTypes > 0 { // button has more than one type
frameOffset++
xSeg, ySeg, baseFrame := btnLayout.XSegments, btnLayout.YSegments, btnLayout.BaseFrame
v.pressedSurface, err = v.manager.renderer.NewSurface(v.width, v.height,
d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
buttonStateConfigs := make([]*buttonStateDescriptor, 0)
err = btnSprite.RenderSegmented(v.pressedSurface, xSeg, ySeg, baseFrame+frameOffset)
if err != nil {
fmt.Printf("failed to render button pressedSurface, err: %v\n", err)
}
label.SetPosition(xOffset-pressedButtonOffset, textY+pressedButtonOffset)
label.Render(v.pressedSurface)
// pressed button
if numButtonStates >= buttonStatePressed {
state := &buttonStateDescriptor{
baseFrame + buttonStatePressed,
xOffset - pressedButtonOffset, textY + pressedButtonOffset,
&v.pressedSurface,
"failed to render button pressedSurface, err: %v\n",
}
if btnLayout.ResourceName == d2resource.BuySellButton {
// Without returning early, the button UI gets all subsequent (unrelated) frames stacked on top
// Only 2 frames from this sprite are applicable to the button in question
// The presentation is incorrect without this hack
return
buttonStateConfigs = append(buttonStateConfigs, state)
}
// toggle button
if numButtonStates >= buttonStateToggled {
buttonStateConfigs = append(buttonStateConfigs, &buttonStateDescriptor{
baseFrame + buttonStateToggled,
xOffset, textY,
&v.toggledSurface,
"failed to render button toggledSurface, err: %v\n",
})
}
// pressed+toggled
if numButtonStates >= buttonStatePressedToggled {
buttonStateConfigs = append(buttonStateConfigs, &buttonStateDescriptor{
baseFrame + buttonStatePressedToggled,
xOffset, textY,
&v.pressedToggledSurface,
"failed to render button pressedToggledSurface, err: %v\n",
})
}
// disabled button
if btnLayout.DisabledFrame != -1 {
disabledState := &buttonStateDescriptor{
btnLayout.DisabledFrame,
xOffset, textY,
&v.disabledSurface,
"failed to render button disabledSurface, err: %v\n",
}
totalButtonTypes--
if totalButtonTypes > 0 { // button has more than two types
frameOffset++
buttonStateConfigs = append(buttonStateConfigs, disabledState)
}
v.toggledSurface, err = v.manager.renderer.NewSurface(v.width, v.height,
d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
for stateIdx, w, h := 0, v.width, v.height; stateIdx < len(buttonStateConfigs); stateIdx++ {
state := buttonStateConfigs[stateIdx]
err = btnSprite.RenderSegmented(v.toggledSurface, xSeg, ySeg, baseFrame+frameOffset)
if err != nil {
fmt.Printf("failed to render button toggledSurface, err: %v\n", err)
}
label.SetPosition(xOffset, textY)
label.Render(v.toggledSurface)
if stateIdx >= 2 && btnLayout.ResourceName == d2resource.BuySellButton {
// Without returning early, the button UI gets all subsequent (unrelated) frames
// stacked on top. Only 2 frames from this sprite are applicable to the button
// in question. The presentation is incorrect without this hack!
continue
}
totalButtonTypes--
if totalButtonTypes > 0 { // button has more than three types
frameOffset++
v.pressedToggledSurface, err = v.manager.renderer.NewSurface(v.width, v.height,
d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
err = btnSprite.RenderSegmented(v.pressedSurface, xSeg, ySeg, baseFrame+frameOffset)
if err != nil {
fmt.Printf("failed to render button pressedToggledSurface, err: %v\n", err)
}
label.SetPosition(xOffset, textY)
label.Render(v.pressedToggledSurface)
surface, err := v.manager.renderer.NewSurface(w, h, d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
if btnLayout.DisabledFrame != -1 {
v.disabledSurface, err = v.manager.renderer.NewSurface(v.width, v.height,
d2enum.FilterNearest)
if err != nil {
log.Print(err)
}
*state.prerenderdestination = surface
err = btnSprite.RenderSegmented(v.disabledSurface, xSeg, ySeg, btnLayout.DisabledFrame)
if err != nil {
fmt.Printf("failed to render button disabledSurface, err: %v\n", err)
}
label.SetPosition(xOffset, textY)
label.Render(v.disabledSurface)
err = btnSprite.RenderSegmented(*state.prerenderdestination, xSeg, ySeg, state.baseFrame)
if err != nil {
fmt.Printf(state.fmtErr, err)
}
label.SetPosition(state.offsetX, state.offsetY)
label.Render(*state.prerenderdestination)
}
}

View File

@ -522,12 +522,8 @@ func (v *MainMenu) OnMouseButtonDown(event d2interface.MouseEvent) bool {
}
// OnKeyUp is called when a key is released
func (v *MainMenu) OnKeyUp(event d2interface.KeyEvent) bool {
/*
On retail version of D2 any key event puts you onto the main menu, so this is a supplement to that code up there
on line 515.
*/
func (v *MainMenu) OnKeyUp(_ d2interface.KeyEvent) bool {
// On retail version of D2, any key event puts you onto the main menu.
if v.screenMode == ScreenModeTrademark {
v.SetScreenMode(ScreenModeMainMenu)
return true

View File

@ -265,6 +265,14 @@ func (met *MapEngineTest) Render(screen d2interface.Surface) error {
screen.PushTranslation(0, lineNormalOffsetY)
defer screen.Pop()
if err := met.renderTileInfo(screen); err != nil {
return err
}
return nil
}
func (met *MapEngineTest) renderTileInfo(screen d2interface.Surface) error {
if met.selectedTile == nil {
screen.PushTranslation(lineNormalIndentX, lineNormalOffsetY)
defer screen.Pop()

View File

@ -161,6 +161,8 @@ const (
manaLabelX = 785
manaLabelY = 487
staminaExperienceY = 535
)
const (
@ -978,24 +980,139 @@ func (g *GameControls) renderPanels(target d2interface.Surface) error {
}
func (g *GameControls) renderHUD(target d2interface.Surface) error {
mx, my := g.lastMouseX, g.lastMouseY
width, height := target.GetSize()
offsetX := 0
if err := g.renderGameControlPanelElements(target); err != nil {
return err
}
// Left globe holder
if err := g.HelpOverlay.Render(target); err != nil {
return err
}
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
w, _ := g.mainPanel.GetCurrentFrameSize()
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
}
w, _ := g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
g.mainPanel.SetPosition(x, y)
if err := g.mainPanel.Render(target); err != nil {
return err
}
// Health status bar
return nil
}
func (g *GameControls) renderHealthGlobe(x, y int, target d2interface.Surface) error {
healthPercent := float64(g.hero.Stats.Health) / float64(g.hero.Stats.MaxHealth)
hpBarHeight := int(healthPercent * float64(globeHeight))
@ -1003,7 +1120,7 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
return err
}
g.hpManaStatusSprite.SetPosition(offsetX+healthStatusOffsetX, height+healthStatusOffsetY)
g.hpManaStatusSprite.SetPosition(x+healthStatusOffsetX, y+healthStatusOffsetY)
healthMaskRect := image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight)
if err := g.hpManaStatusSprite.RenderSection(target, healthMaskRect); err != nil {
@ -1015,15 +1132,16 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
return err
}
g.globeSprite.SetPosition(offsetX+globeSpriteOffsetX, height+globeSpriteOffsetY)
g.globeSprite.SetPosition(x+globeSpriteOffsetX, y+globeSpriteOffsetY)
if err := g.globeSprite.Render(target); err != nil {
return err
}
offsetX += w
return nil
}
// Left skill
func (g *GameControls) renderLeftSkill(x, y int, target d2interface.Surface) error {
newSkillResourcePath := g.getSkillResourceByClass(g.hero.LeftSkill.Charclass)
if newSkillResourcePath != g.leftSkillResource.SkillResourcePath {
g.leftSkillResource.SkillResourcePath = newSkillResourcePath
@ -1034,49 +1152,49 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
return err
}
w, _ = g.leftSkillResource.SkillIcon.GetCurrentFrameSize()
g.leftSkillResource.SkillIcon.SetPosition(offsetX, height)
g.leftSkillResource.SkillIcon.SetPosition(x, y)
if err := g.leftSkillResource.SkillIcon.Render(target); err != nil {
return err
}
offsetX += w
return nil
}
// New Stats Selector
func (g *GameControls) renderNewStatsButton(x, y int, target d2interface.Surface) error {
if err := g.mainPanel.SetCurrentFrame(frameNewStatsSelector); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
g.mainPanel.SetPosition(x, y)
if err := g.mainPanel.Render(target); err != nil {
return err
}
offsetX += w
return nil
}
// Stamina
func (g *GameControls) renderStamina(x, y int, target d2interface.Surface) error {
if err := g.mainPanel.SetCurrentFrame(frameStamina); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
g.mainPanel.SetPosition(x, y)
if err := g.mainPanel.Render(target); err != nil {
return err
}
offsetX += w
return nil
}
// Stamina status bar
func (g *GameControls) renderStaminaBar(target d2interface.Surface) error {
target.PushTranslation(staminaBarOffsetX, staminaBarOffsetY)
defer target.Pop()
target.PushEffect(d2enum.DrawEffectModulate)
defer target.Pop()
staminaPercent := g.hero.Stats.Stamina / float64(g.hero.Stats.MaxStamina)
@ -1086,18 +1204,25 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
}
target.DrawRect(int(staminaPercent*staminaBarWidth), staminaBarHeight, staminaBarColor)
target.Pop()
target.Pop()
// Experience status bar
return nil
}
func (g *GameControls) renderExperienceBar(target d2interface.Surface) error {
target.PushTranslation(experienceBarOffsetX, experienceBarOffsetY)
defer target.Pop()
expPercent := float64(g.hero.Stats.Experience) / float64(g.hero.Stats.NextLevelExp)
target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100))
target.Pop()
// Center menu button
return nil
}
func (g *GameControls) renderMiniPanel(target d2interface.Surface) error {
width, height := target.GetSize()
mx, my := g.lastMouseX, g.lastMouseY
menuButtonFrameIndex := 0
if g.miniPanel.isOpen {
menuButtonFrameIndex = 2
@ -1119,135 +1244,6 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
return err
}
// Potions
if err := g.mainPanel.SetCurrentFrame(framePotions); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
if err := g.mainPanel.Render(target); err != nil {
return err
}
offsetX += w
// New Skills Selector
if err := g.mainPanel.SetCurrentFrame(frameNewSkillsSelector); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
if err := g.mainPanel.Render(target); err != nil {
return err
}
offsetX += w
// Right skill
newSkillResourcePath = g.getSkillResourceByClass(g.hero.RightSkill.Charclass)
if newSkillResourcePath != g.rightSkillResource.SkillResourcePath {
g.rightSkillResource.SkillIcon, _ = g.ui.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
g.rightSkillResource.SkillResourcePath = newSkillResourcePath
}
if err := g.rightSkillResource.SkillIcon.SetCurrentFrame(g.hero.RightSkill.IconCel); err != nil {
return err
}
w, _ = g.rightSkillResource.SkillIcon.GetCurrentFrameSize()
g.rightSkillResource.SkillIcon.SetPosition(offsetX, height)
if err := g.rightSkillResource.SkillIcon.Render(target); err != nil {
return err
}
offsetX += w
// Right globe holder
if err := g.mainPanel.SetCurrentFrame(frameRightGlobeHolder); err != nil {
return err
}
g.mainPanel.GetCurrentFrameSize()
g.mainPanel.SetPosition(offsetX, height)
if err := g.mainPanel.Render(target); err != nil {
return err
}
// Mana status bar
manaPercent := float64(g.hero.Stats.Mana) / float64(g.hero.Stats.MaxMana)
manaBarHeight := int(manaPercent * float64(globeHeight))
if err := g.hpManaStatusSprite.SetCurrentFrame(frameManaStatus); err != nil {
return err
}
g.hpManaStatusSprite.SetPosition(offsetX+manaStatusOffsetX, height+manaStatusOffsetY)
manaMaskRect := image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)
if err := g.hpManaStatusSprite.RenderSection(target, manaMaskRect); err != nil {
return err
}
// Right globe
if err := g.globeSprite.SetCurrentFrame(frameRightGlobe); err != nil {
return err
}
g.globeSprite.SetPosition(offsetX+rightGlobeOffsetX, height+rightGlobeOffsetY)
if err := g.globeSprite.Render(target); err != nil {
return err
}
if err := g.globeSprite.Render(target); err != nil {
return err
}
if g.isZoneTextShown {
g.zoneChangeText.SetPosition(zoneChangeTextX, zoneChangeTextY)
g.zoneChangeText.Render(target)
}
// Create and format Health string from string lookup table.
fmtHealth := d2tbl.TranslateString("panelhealth")
healthCurr, healthMax := g.hero.Stats.Health, g.hero.Stats.MaxHealth
strPanelHealth := fmt.Sprintf(fmtHealth, healthCurr, healthMax)
// Display current hp and mana stats hpGlobe or manaGlobe region is clicked
if g.actionableRegions[hpGlobe].rect.IsInRect(mx, my) || g.hpStatsIsVisible {
g.hpManaStatsLabel.SetText(strPanelHealth)
g.hpManaStatsLabel.SetPosition(hpLabelX, hpLabelY)
g.hpManaStatsLabel.Render(target)
}
// Create and format Mana string from string lookup table.
fmtMana := d2tbl.TranslateString("panelmana")
manaCurr, manaMax := g.hero.Stats.Mana, g.hero.Stats.MaxMana
strPanelMana := fmt.Sprintf(fmtMana, manaCurr, manaMax)
if g.actionableRegions[manaGlobe].rect.IsInRect(mx, my) || g.manaStatsIsVisible {
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)
}
if err := g.HelpOverlay.Render(target); err != nil {
return err
}
miniPanelButtons := map[actionableType]string{
miniPanelCharacter: "minipanelchar",
miniPanelInventory: "minipanelinv",
@ -1258,46 +1254,18 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
miniPanelGameMenu: "minipanelmenubtn",
}
if !g.miniPanel.IsOpen() {
return nil
}
for miniPanelButton, stringTableKey := range miniPanelButtons {
if !g.miniPanel.IsOpen() {
if !g.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
continue
}
if g.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
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)
}
}
// 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) {
var stringTableKey string
if g.hero.IsRunToggled() {
stringTableKey = "RunOff"
} else {
stringTableKey = "RunOn"
}
rect := &g.actionableRegions[miniPanelButton].rect
g.nameLabel.SetText(d2tbl.TranslateString(stringTableKey))
rect := &g.actionableRegions[walkRun].rect
halfButtonWidth := rect.Width >> 1
halfButtonHeight := rect.Height >> 1
@ -1313,71 +1281,250 @@ func (g *GameControls) renderHUD(target d2interface.Surface) error {
g.nameLabel.Render(target)
}
const (
staminaExperienceY = 535
)
return nil
}
// Display stamina tooltip when hovered.
if g.actionableRegions[stamina].rect.IsInRect(mx, my) {
// 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)
func (g *GameControls) renderPotions(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
g.nameLabel.SetText(strPanelStamina)
rect := &g.actionableRegions[stamina].rect
halfButtonWidth := rect.Width >> 1
centerX := rect.Left + halfButtonWidth
_, labelHeight := g.nameLabel.GetSize()
halfLabelHeight := labelHeight >> 1
labelX := centerX
labelY := staminaExperienceY - halfLabelHeight
g.nameLabel.SetPosition(labelX, labelY)
g.nameLabel.Render(target)
if err := g.mainPanel.SetCurrentFrame(framePotions); err != nil {
return err
}
// Display experience tooltip when hovered.
if g.actionableRegions[xp].rect.IsInRect(mx, my) {
// Create and format Experience string from string lookup table.
fmtExp := d2tbl.TranslateString("panelexp")
g.mainPanel.SetPosition(x, height)
// 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(g.hero.Stats.Experience), uint(g.hero.Stats.NextLevelExp)
strPanelExp := fmt.Sprintf(fmtExp, expCurr, expMax)
g.nameLabel.SetText(strPanelExp)
rect := &g.actionableRegions[stamina].rect
halfButtonWidth := rect.Width >> 1
centerX := rect.Left + halfButtonWidth
_, labelHeight := g.nameLabel.GetSize()
halfLabelHeight := labelHeight >> 1
labelX := centerX
labelY := staminaExperienceY - halfLabelHeight
g.nameLabel.SetPosition(labelX, labelY)
g.nameLabel.Render(target)
}
if g.skillSelectMenu.IsOpen() {
g.skillSelectMenu.Render(target)
if err := g.mainPanel.Render(target); err != nil {
return err
}
return nil
}
func (g *GameControls) renderNewSkillsButton(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
if err := g.mainPanel.SetCurrentFrame(frameNewSkillsSelector); err != nil {
return err
}
g.mainPanel.SetPosition(x, height)
if err := g.mainPanel.Render(target); err != nil {
return err
}
return nil
}
func (g *GameControls) renderRightSkill(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
newSkillResourcePath := g.getSkillResourceByClass(g.hero.RightSkill.Charclass)
if newSkillResourcePath != g.rightSkillResource.SkillResourcePath {
g.rightSkillResource.SkillIcon, _ = g.ui.NewSprite(newSkillResourcePath, d2resource.PaletteSky)
g.rightSkillResource.SkillResourcePath = newSkillResourcePath
}
if err := g.rightSkillResource.SkillIcon.SetCurrentFrame(g.hero.RightSkill.IconCel); err != nil {
return err
}
g.rightSkillResource.SkillIcon.SetPosition(x, height)
if err := g.rightSkillResource.SkillIcon.Render(target); err != nil {
return err
}
return nil
}
func (g *GameControls) renderManaGlobe(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
if err := g.mainPanel.SetCurrentFrame(frameRightGlobeHolder); err != nil {
return err
}
g.mainPanel.SetPosition(x, height)
if err := g.mainPanel.Render(target); err != nil {
return err
}
// Mana status bar
manaPercent := float64(g.hero.Stats.Mana) / float64(g.hero.Stats.MaxMana)
manaBarHeight := int(manaPercent * float64(globeHeight))
if err := g.hpManaStatusSprite.SetCurrentFrame(frameManaStatus); err != nil {
return err
}
g.hpManaStatusSprite.SetPosition(x+manaStatusOffsetX, height+manaStatusOffsetY)
manaMaskRect := image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)
if err := g.hpManaStatusSprite.RenderSection(target, manaMaskRect); err != nil {
return err
}
// Right globe
if err := g.globeSprite.SetCurrentFrame(frameRightGlobe); err != nil {
return err
}
g.globeSprite.SetPosition(x+rightGlobeOffsetX, height+rightGlobeOffsetY)
if err := g.globeSprite.Render(target); err != nil {
return err
}
if err := g.globeSprite.Render(target); err != nil {
return err
}
return nil
}
func (g *GameControls) renderHealthTooltip(target d2interface.Surface) {
mx, my := g.lastMouseX, g.lastMouseY
// Create and format Health string from string lookup table.
fmtHealth := d2tbl.TranslateString("panelhealth")
healthCurr, healthMax := g.hero.Stats.Health, g.hero.Stats.MaxHealth
strPanelHealth := fmt.Sprintf(fmtHealth, healthCurr, healthMax)
// Display current hp and mana stats hpGlobe or manaGlobe region is clicked
if !g.actionableRegions[hpGlobe].rect.IsInRect(mx, my) || g.hpStatsIsVisible {
return
}
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
// Create and format Mana string from string lookup table.
fmtMana := d2tbl.TranslateString("panelmana")
manaCurr, manaMax := g.hero.Stats.Mana, g.hero.Stats.MaxMana
strPanelMana := fmt.Sprintf(fmtMana, manaCurr, manaMax)
if !g.actionableRegions[manaGlobe].rect.IsInRect(mx, my) || g.manaStatsIsVisible {
return
}
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)
}
func (g *GameControls) renderRunWalkTooltip(target d2interface.Surface) {
mx, my := g.lastMouseX, g.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 !g.actionableRegions[walkRun].rect.IsInRect(mx, my) {
return
}
var stringTableKey string
if g.hero.IsRunToggled() {
stringTableKey = "RunOff"
} else {
stringTableKey = "RunOn"
}
g.nameLabel.SetText(d2tbl.TranslateString(stringTableKey))
rect := &g.actionableRegions[walkRun].rect
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)
}
func (g *GameControls) renderStaminaTooltip(target d2interface.Surface) {
mx, my := g.lastMouseX, g.lastMouseY
// Display stamina tooltip when hovered.
if !g.actionableRegions[stamina].rect.IsInRect(mx, my) {
return
}
// 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)
g.nameLabel.SetText(strPanelStamina)
rect := &g.actionableRegions[stamina].rect
halfButtonWidth := rect.Width >> 1
centerX := rect.Left + halfButtonWidth
_, labelHeight := g.nameLabel.GetSize()
halfLabelHeight := labelHeight >> 1
labelX := centerX
labelY := staminaExperienceY - halfLabelHeight
g.nameLabel.SetPosition(labelX, labelY)
g.nameLabel.Render(target)
}
func (g *GameControls) renderExperienceTooltip(target d2interface.Surface) {
mx, my := g.lastMouseX, g.lastMouseY
// Display experience tooltip when hovered.
if !g.actionableRegions[xp].rect.IsInRect(mx, my) {
return
}
// Create and format Experience string from string lookup table.
fmtExp := d2tbl.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(g.hero.Stats.Experience), uint(g.hero.Stats.NextLevelExp)
strPanelExp := fmt.Sprintf(fmtExp, expCurr, expMax)
g.nameLabel.SetText(strPanelExp)
rect := &g.actionableRegions[stamina].rect
halfButtonWidth := rect.Width >> 1
centerX := rect.Left + halfButtonWidth
_, labelHeight := g.nameLabel.GetSize()
halfLabelHeight := labelHeight >> 1
labelX := centerX
labelY := staminaExperienceY - halfLabelHeight
g.nameLabel.SetPosition(labelX, labelY)
g.nameLabel.Render(target)
}
// SetZoneChangeText sets the zoneChangeText
func (g *GameControls) SetZoneChangeText(text string) {
g.zoneChangeText.SetText(text)

View File

@ -15,6 +15,25 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
/*
the 800x600 help screen dc6 file frames look like this
the position we set for frames is the lower-left corner x,y
+----+------------------+-------------------+------------+----+
| 1 | 3 | 4 | 5 | 6 |
| |------------------+-------------------| | |
| | | | |
| | | | |
+----+ +------------+----+
| 2 | | 7 |
| | | |
| | | |
+----+ +----+
*/
const (
// if you add up frame widths 1,3,4,5,6 you get (65+255+255+245+20) = 840
magicHelpBorderOffsetX = -40
)
const (
frameTopLeft = iota
frameBottomLeft
@ -217,25 +236,13 @@ func (h *Overlay) IsInRect(px, py int) bool {
// Load the overlay graphical assets
func (h *Overlay) Load() {
/*
the 800x600 help screen dc6 file frames look like this
the position we set for frames is the lower-left corner x,y
+----+------------------+-------------------+------------+----+
| 1 | 3 | 4 | 5 | 6 |
| |------------------+-------------------| | |
| | | | |
| | | | |
+----+ +------------+----+
| 2 | | 7 |
| | | |
| | | |
+----+ +----+
*/
const (
// if you add up frame widths 1,3,4,5,6 you get (65+255+255+245+20) = 840
magicHelpBorderOffsetX = -40
)
h.setupOverlayFrame()
h.setupTitleAndButton()
h.setupBulletedList()
h.setupLabelsWithLines()
}
func (h *Overlay) setupOverlayFrame() {
frames := []int{
frameTopLeft,
frameBottomLeft,
@ -295,9 +302,10 @@ func (h *Overlay) Load() {
h.frames = append(h.frames, f)
}
}
func (h *Overlay) setupTitleAndButton() {
// Title
text := d2tbl.TranslateString("Strhelp1") // "Diablo II Help"
newLabel := h.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteSky)
newLabel.SetText(text)
@ -307,8 +315,7 @@ func (h *Overlay) Load() {
newLabel.SetPosition((windowWidth/inHalf)-(titleLabelWidth/inHalf)+titleLabelOffsetX, 0)
h.text = append(h.text, newLabel)
// Close
// Button
h.closeButton = h.uiManager.NewButton(d2ui.ButtonTypeSquareClose, "")
h.closeButton.SetPosition(closeButtonX, closeButtonY)
h.closeButton.SetVisible(false)
@ -318,7 +325,9 @@ func (h *Overlay) Load() {
newLabel.SetText(d2tbl.TranslateString("strClose")) // "Close"
newLabel.SetPosition(closeButtonLabelX, closeButtonLabelY)
h.text = append(h.text, newLabel)
}
func (h *Overlay) setupBulletedList() {
// Bullets
// the hotkeys displayed here should be pulled from a mapping of input events to game events
// https://github.com/OpenDiablo2/OpenDiablo2/issues/793
@ -360,9 +369,10 @@ func (h *Overlay) Load() {
DotY: listBulletRootY + listItemOffsetY,
})
}
}
// Callouts
// nolint:funlen // can't reduce
func (h *Overlay) setupLabelsWithLines() {
h.createCallout(callout{
LabelText: d2tbl.TranslateString("strlvlup"), // "New Stats"
LabelX: newStatsLabelX,

View File

@ -201,8 +201,8 @@ func makeCloseButtonPos(close1, close2, close3 int) [numTabs]int {
return [numTabs]int{close1, close2, close3}
}
func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
tabMap := map[d2enum.Hero]heroTabData{
func (s *skillTree) getTab(class d2enum.Hero) *heroTabData {
tabMap := map[d2enum.Hero]*heroTabData{
d2enum.HeroBarbarian: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelBarbarian,
@ -242,13 +242,11 @@ func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
skillCloseButtonXMiddle,
skillCloseButtonXLeft),
},
d2enum.HeroAssassin: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelAssassin,
skillIconPath: d2resource.AssassinSkills,
},
makeTabString("StrSklTree30"),
makeTabString("StrSklTree31", "StrSklTree32"),
makeTabString("StrSklTree33", "StrSklTree34"),
@ -270,7 +268,6 @@ func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
skillCloseButtonXLeft,
skillCloseButtonXRight),
},
d2enum.HeroAmazon: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelAmazon,
@ -284,7 +281,6 @@ func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
skillCloseButtonXMiddle,
skillCloseButtonXLeft),
},
d2enum.HeroDruid: {
&skillTreeHeroTypeResources{
skillPanelPath: d2resource.SkillsPanelDruid,
@ -300,14 +296,12 @@ func (s *skillTree) getTab(class d2enum.Hero) (heroTabData, bool) {
},
}
entry, found := tabMap[class]
return entry, found
return tabMap[class]
}
func (s *skillTree) setHeroTypeResourcePath() {
entry, found := s.getTab(s.heroClass)
if !found {
entry := s.getTab(s.heroClass)
if entry == nil {
log.Fatal("Unknown Hero Type")
}