1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-04 09:17:17 -05:00
OpenDiablo2/d2game/d2gamescreen/select_hero_class.go

761 lines
25 KiB
Go
Raw Normal View History

package d2gamescreen
2019-11-10 03:36:53 -05:00
import (
"fmt"
2019-11-10 03:36:53 -05:00
"image"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
2020-02-08 21:02:37 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
2020-02-08 21:02:37 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
2019-11-10 03:36:53 -05:00
)
type heroRenderConfig struct {
idleAnimationPath string
idleSelectedAnimationPath string
forwardWalkAnimationPath string
forwardWalkOverlayAnimationPath string
forwardWalkOverlayBlend bool
selectedAnimationPath string
selectedOverlayAnimationPath string
backWalkAnimationPath string
backWalkOverlayAnimationPath string
selectionBounds image.Rectangle
selectSfx string
deselectSfx string
position image.Point
idlePlayLengthMs int
forwardWalkPlayLengthMs int
backWalkPlayLengthMs int
}
func point(x, y int) image.Point {
return image.Point{X: x, Y: y}
}
func rect(x1, y1, x2, y2 int) image.Rectangle {
return image.Rectangle{Min: point(x1, y1), Max: point(x2, y2)}
}
// animation position, selection box bound, animation play lengths in ms
const (
barbPosX, barbPosY = 400, 330
barbRectMinX, barbRectMinY, barbRectMaxX, barbRectMaxY = 364, 201, 90, 170
barbIdleLength, barbForwardLength, barbBackLength = 0, 2500, 1000
sorcPosX, sorcPosY = 626, 352
sorcRectMinX, sorcRectMinY, sorcRectMaxX, sorcRectMaxY = 580, 240, 65, 160
sorcIdleLength, sorcForwardLength, sorcBackLength = 2500, 2300, 1200
necPosX, necPosY = 300, 335
necRectMinX, necRectMinY, necRectMaxX, necRectMaxY = 265, 220, 55, 175
necIdleLength, necForwardLength, necBackLength = 1200, 2000, 1500
palPosX, palPosY = 521, 338
palRectMinX, palRectMinY, palRectMaxX, palRectMaxY = 490, 210, 65, 180
palIdleLength, palForwardLength, palBackLength = 2500, 3400, 1300
amaPosX, amaPosY = 100, 339
amaRectMinX, amaRectMinY, amaRectMaxX, amaRectMaxY = 70, 220, 55, 200
amaIdleLength, amaForwardLength, amaBackLength = 2500, 2200, 1500
assPosX, assPosY = 231, 365
assRectMinX, assRectMinY, assRectMaxX, assRectMaxY = 175, 235, 50, 180
assIdleLength, assForwardLength, assBackLength = 2500, 3800, 1500
druPosX, druPosY = 720, 370
druRectMinX, druRectMinY, druRectMaxX, druRectMaxY = 680, 220, 70, 195
druIdleLength, druForwardLength, druBackLength = 1500, 4800, 1500
campfirePosX, campfirePosY = 380, 335
)
// label and button positions
const (
headingX, headingY = 400, 17
heroClassLabelX, heroClassLabelY = 400, 65
heroDescLine1X, heroDescLine1Y = 400, 100
heroDescLine2X, heroDescLine2Y = 400, 115
heroDescLine3X, heroDescLine3Y = 400, 130
heroNameLabelX, heroNameLabelY = 321, 475
expansionLabelX, expansionLabelY = 339, 526
hardcoreLabelX, hardcoreLabelY = 339, 548
selHeroExitBtnX, selHeroExitBtnY = 33, 537
selHeroOkBtnX, selHeroOkBtnY = 630, 537
heroNameTextBoxX, heoNameTextBoxY = 318, 493
expandsionCheckboxX, expansionCheckboxY = 318, 526
hardcoreCheckoxX, hardcoreCheckboxY = 318, 548
)
const heroDescCharWidth = 37
//nolint:funlen // this func returns a map of structs and the structs are big, deal with it
func getHeroRenderConfiguration() map[d2enum.Hero]*heroRenderConfig {
configs := make(map[d2enum.Hero]*heroRenderConfig)
configs[d2enum.HeroBarbarian] = &heroRenderConfig{
d2resource.CharacterSelectBarbarianUnselected,
d2resource.CharacterSelectBarbarianUnselectedH,
d2resource.CharacterSelectBarbarianForwardWalk,
d2resource.CharacterSelectBarbarianForwardWalkOverlay,
false,
d2resource.CharacterSelectBarbarianSelected,
"",
d2resource.CharacterSelectBarbarianBackWalk,
"",
rect(barbRectMinX, barbRectMinY, barbRectMaxX, barbRectMaxY),
d2resource.SFXBarbarianSelect,
d2resource.SFXBarbarianDeselect,
point(barbPosX, barbPosY),
barbIdleLength,
barbForwardLength,
barbBackLength,
}
configs[d2enum.HeroSorceress] = &heroRenderConfig{
d2resource.CharacterSelectSorceressUnselected,
d2resource.CharacterSelectSorceressUnselectedH,
d2resource.CharacterSelectSorceressForwardWalk,
d2resource.CharacterSelectSorceressForwardWalkOverlay,
true,
d2resource.CharacterSelectSorceressSelected,
d2resource.CharacterSelectSorceressSelectedOverlay,
d2resource.CharacterSelectSorceressBackWalk,
d2resource.CharacterSelectSorceressBackWalkOverlay,
rect(sorcRectMinX, sorcRectMinY, sorcRectMaxX, sorcRectMaxY),
d2resource.SFXSorceressSelect,
d2resource.SFXSorceressDeselect,
point(sorcPosX, sorcPosY),
sorcIdleLength,
sorcForwardLength,
sorcBackLength,
}
configs[d2enum.HeroNecromancer] = &heroRenderConfig{
d2resource.CharacterSelectNecromancerUnselected,
d2resource.CharacterSelectNecromancerUnselectedH,
d2resource.CharacterSelectNecromancerForwardWalk,
d2resource.CharacterSelectNecromancerForwardWalkOverlay,
true,
d2resource.CharacterSelectNecromancerSelected,
d2resource.CharacterSelectNecromancerSelectedOverlay,
d2resource.CharacterSelectNecromancerBackWalk,
d2resource.CharacterSelectNecromancerBackWalkOverlay,
rect(necRectMinX, necRectMinY, necRectMaxX, necRectMaxY),
d2resource.SFXNecromancerSelect,
d2resource.SFXNecromancerDeselect,
point(necPosX, necPosY),
necIdleLength,
necForwardLength,
necBackLength,
}
configs[d2enum.HeroPaladin] = &heroRenderConfig{
d2resource.CharacterSelectPaladinUnselected,
d2resource.CharacterSelectPaladinUnselectedH,
d2resource.CharacterSelectPaladinForwardWalk,
d2resource.CharacterSelectPaladinForwardWalkOverlay,
false,
d2resource.CharacterSelectPaladinSelected,
"",
d2resource.CharacterSelectPaladinBackWalk,
"",
rect(palRectMinX, palRectMinY, palRectMaxX, palRectMaxY),
d2resource.SFXPaladinSelect,
d2resource.SFXPaladinDeselect,
point(palPosX, palPosY),
palIdleLength,
palForwardLength,
palBackLength,
}
configs[d2enum.HeroAmazon] = &heroRenderConfig{
d2resource.CharacterSelectAmazonUnselected,
d2resource.CharacterSelectAmazonUnselectedH,
d2resource.CharacterSelectAmazonForwardWalk,
"",
false,
d2resource.CharacterSelectAmazonSelected,
"",
d2resource.CharacterSelectAmazonBackWalk,
"",
rect(amaRectMinX, amaRectMinY, amaRectMaxX, amaRectMaxY),
d2resource.SFXAmazonSelect,
d2resource.SFXAmazonDeselect,
point(amaPosX, amaPosY),
amaIdleLength,
amaForwardLength,
amaBackLength,
}
configs[d2enum.HeroAssassin] = &heroRenderConfig{
d2resource.CharacterSelectAssassinUnselected,
d2resource.CharacterSelectAssassinUnselectedH,
d2resource.CharacterSelectAssassinForwardWalk,
"",
false,
d2resource.CharacterSelectAssassinSelected,
"",
d2resource.CharacterSelectAssassinBackWalk,
"",
rect(assRectMinX, assRectMinY, assRectMaxX, assRectMaxY),
d2resource.SFXAssassinSelect,
d2resource.SFXAssassinDeselect,
point(assPosX, assPosY),
assIdleLength,
assForwardLength,
assBackLength,
}
configs[d2enum.HeroDruid] = &heroRenderConfig{
d2resource.CharacterSelectDruidUnselected,
d2resource.CharacterSelectDruidUnselectedH,
d2resource.CharacterSelectDruidForwardWalk,
"",
false,
d2resource.CharacterSelectDruidSelected,
"",
d2resource.CharacterSelectDruidBackWalk,
"",
rect(druRectMinX, druRectMinY, druRectMaxX, druRectMaxY),
d2resource.SFXDruidSelect,
d2resource.SFXDruidDeselect,
point(druPosX, druPosY),
druIdleLength,
druForwardLength,
druBackLength,
}
return configs
}
// HeroRenderInfo stores the rendering information of a hero for the Select Hero Class screen
2019-11-10 03:36:53 -05:00
type HeroRenderInfo struct {
Stance d2enum.HeroStance
IdleSprite *d2ui.Sprite
IdleSelectedSprite *d2ui.Sprite
ForwardWalkSprite *d2ui.Sprite
ForwardWalkSpriteOverlay *d2ui.Sprite
SelectedSprite *d2ui.Sprite
SelectedSpriteOverlay *d2ui.Sprite
BackWalkSprite *d2ui.Sprite
BackWalkSpriteOverlay *d2ui.Sprite
2019-11-10 03:36:53 -05:00
SelectionBounds image.Rectangle
2020-06-28 19:31:10 -04:00
SelectSfx d2interface.SoundEffect
DeselectSfx d2interface.SoundEffect
2019-11-10 03:36:53 -05:00
}
func (hri *HeroRenderInfo) advance(elapsed float64) {
advanceSprite(hri.IdleSprite, elapsed)
advanceSprite(hri.IdleSelectedSprite, elapsed)
advanceSprite(hri.ForwardWalkSprite, elapsed)
advanceSprite(hri.ForwardWalkSpriteOverlay, elapsed)
advanceSprite(hri.SelectedSprite, elapsed)
advanceSprite(hri.SelectedSpriteOverlay, elapsed)
advanceSprite(hri.BackWalkSprite, elapsed)
advanceSprite(hri.BackWalkSpriteOverlay, elapsed)
}
// SelectHeroClass represents the Select Hero Class screen
2019-11-10 03:36:53 -05:00
type SelectHeroClass struct {
bgImage *d2ui.Sprite
campfire *d2ui.Sprite
headingLabel d2ui.Label
heroClassLabel d2ui.Label
heroDesc1Label d2ui.Label
heroDesc2Label d2ui.Label
heroDesc3Label d2ui.Label
heroNameTextbox d2ui.TextBox
heroNameLabel d2ui.Label
heroRenderInfo map[d2enum.Hero]*HeroRenderInfo
selectedHero d2enum.Hero
exitButton d2ui.Button
okButton d2ui.Button
expansionCheckbox d2ui.Checkbox
expansionCharLabel d2ui.Label
hardcoreCheckbox d2ui.Checkbox
hardcoreCharLabel d2ui.Label
2020-06-18 14:11:04 -04:00
connectionType d2clientconnectiontype.ClientConnectionType
connectionHost string
audioProvider d2interface.AudioProvider
renderer d2interface.Renderer
navigator Navigator
2019-11-10 03:36:53 -05:00
}
// CreateSelectHeroClass creates an instance of a SelectHeroClass
func CreateSelectHeroClass(
navigator Navigator,
renderer d2interface.Renderer,
audioProvider d2interface.AudioProvider,
connectionType d2clientconnectiontype.ClientConnectionType,
connectionHost string,
) *SelectHeroClass {
2019-11-10 03:36:53 -05:00
result := &SelectHeroClass{
heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo),
selectedHero: d2enum.HeroNone,
2020-06-18 14:11:04 -04:00
connectionType: connectionType,
connectionHost: connectionHost,
2020-06-28 19:31:10 -04:00
audioProvider: audioProvider,
renderer: renderer,
navigator: navigator,
2019-11-10 03:36:53 -05:00
}
2019-11-10 03:36:53 -05:00
return result
}
// OnLoad loads the resources for the Select Hero Class screen
func (v *SelectHeroClass) OnLoad(loading d2screen.LoadingState) {
2020-06-28 19:31:10 -04:00
v.audioProvider.PlayBGM(d2resource.BGMTitle)
loading.Progress(tenPercent)
v.bgImage = loadSprite(
d2resource.CharacterSelectBackground,
point(0, 0),
0,
true,
false,
)
loading.Progress(thirtyPercent)
v.createLabels()
loading.Progress(fourtyPercent)
v.createButtons()
v.campfire = loadSprite(
d2resource.CharacterSelectCampfire,
point(campfirePosX, campfirePosY),
0,
true,
true,
)
v.createCheckboxes(v.renderer)
loading.Progress(fiftyPercent)
for hero, config := range getHeroRenderConfiguration() {
position := config.position
forwardWalkOverlaySprite := loadSprite(
config.forwardWalkOverlayAnimationPath,
position,
config.forwardWalkPlayLengthMs,
false,
config.forwardWalkOverlayBlend,
)
v.heroRenderInfo[hero] = &HeroRenderInfo{
Stance: d2enum.HeroStanceIdle,
IdleSprite: loadSprite(config.idleAnimationPath, position, config.idlePlayLengthMs, true, false),
IdleSelectedSprite: loadSprite(config.idleSelectedAnimationPath, position, config.idlePlayLengthMs, true, false),
ForwardWalkSprite: loadSprite(config.forwardWalkAnimationPath, position, config.forwardWalkPlayLengthMs, false, false),
ForwardWalkSpriteOverlay: forwardWalkOverlaySprite,
SelectedSprite: loadSprite(config.selectedAnimationPath, position, config.idlePlayLengthMs, true, false),
SelectedSpriteOverlay: loadSprite(config.selectedOverlayAnimationPath, position, config.idlePlayLengthMs, true, true),
BackWalkSprite: loadSprite(config.backWalkAnimationPath, position, config.backWalkPlayLengthMs, false, false),
BackWalkSpriteOverlay: loadSprite(config.backWalkOverlayAnimationPath, position, config.backWalkPlayLengthMs, false, true),
SelectionBounds: config.selectionBounds,
SelectSfx: v.loadSoundEffect(config.selectSfx),
DeselectSfx: v.loadSoundEffect(config.deselectSfx),
}
}
}
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) createLabels() {
v.headingLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits)
2020-02-08 21:02:37 -05:00
fontWidth, _ := v.headingLabel.GetSize()
half := 2
halfFontWidth := fontWidth / half
v.headingLabel.SetPosition(headingX-halfFontWidth, headingY)
2020-02-08 21:02:37 -05:00
v.headingLabel.SetText("Select Hero Class")
v.headingLabel.Alignment = d2gui.HorizontalAlignCenter
2020-02-08 21:02:37 -05:00
v.heroClassLabel = d2ui.CreateLabel(d2resource.Font30, d2resource.PaletteUnits)
v.heroClassLabel.Alignment = d2gui.HorizontalAlignCenter
v.heroClassLabel.SetPosition(heroClassLabelX, heroClassLabelY)
2020-02-08 21:02:37 -05:00
v.heroDesc1Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc1Label.Alignment = d2gui.HorizontalAlignCenter
v.heroDesc1Label.SetPosition(heroDescLine1X, heroDescLine1Y)
2020-02-08 21:02:37 -05:00
v.heroDesc2Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc2Label.Alignment = d2gui.HorizontalAlignCenter
v.heroDesc2Label.SetPosition(heroDescLine2X, heroDescLine2Y)
2020-02-08 21:02:37 -05:00
v.heroDesc3Label = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc3Label.Alignment = d2gui.HorizontalAlignCenter
v.heroDesc3Label.SetPosition(heroDescLine3X, heroDescLine3Y)
2020-02-08 21:02:37 -05:00
v.heroNameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroNameLabel.Alignment = d2gui.HorizontalAlignLeft
v.heroNameLabel.Color = rgbaColor(gold)
v.heroNameLabel.SetText("Character Name")
v.heroNameLabel.SetPosition(heroNameLabelX, heroNameLabelY)
v.expansionCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.expansionCharLabel.Alignment = d2gui.HorizontalAlignLeft
v.expansionCharLabel.Color = rgbaColor(gold)
v.expansionCharLabel.SetText("EXPANSION CHARACTER")
v.expansionCharLabel.SetPosition(expansionLabelX, expansionLabelY)
v.hardcoreCharLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.hardcoreCharLabel.Alignment = d2gui.HorizontalAlignLeft
v.hardcoreCharLabel.Color = rgbaColor(gold)
v.hardcoreCharLabel.SetText("Hardcore")
v.hardcoreCharLabel.SetPosition(hardcoreLabelX, hardcoreLabelY)
}
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) createButtons() {
v.exitButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "EXIT")
v.exitButton.SetPosition(selHeroExitBtnX, selHeroExitBtnY)
2020-02-08 21:02:37 -05:00
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
d2ui.AddWidget(&v.exitButton)
v.okButton = d2ui.CreateButton(v.renderer, d2ui.ButtonTypeMedium, "OK")
v.okButton.SetPosition(selHeroOkBtnX, selHeroOkBtnY)
2020-02-08 21:02:37 -05:00
v.okButton.OnActivated(func() { v.onOkButtonClicked() })
v.okButton.SetVisible(false)
v.okButton.SetEnabled(false)
d2ui.AddWidget(&v.okButton)
}
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) createCheckboxes(renderer d2interface.Renderer) {
v.heroNameTextbox = d2ui.CreateTextbox(renderer)
v.heroNameTextbox.SetPosition(heroNameTextBoxX, heoNameTextBoxY)
2020-02-08 21:02:37 -05:00
v.heroNameTextbox.SetVisible(false)
d2ui.AddWidget(&v.heroNameTextbox)
v.expansionCheckbox = d2ui.CreateCheckbox(v.renderer, true)
v.expansionCheckbox.SetPosition(expandsionCheckboxX, expansionCheckboxY)
2020-02-08 21:02:37 -05:00
v.expansionCheckbox.SetVisible(false)
d2ui.AddWidget(&v.expansionCheckbox)
v.hardcoreCheckbox = d2ui.CreateCheckbox(renderer, false)
v.hardcoreCheckbox.SetPosition(hardcoreCheckoxX, hardcoreCheckboxY)
2020-02-08 21:02:37 -05:00
v.hardcoreCheckbox.SetVisible(false)
d2ui.AddWidget(&v.hardcoreCheckbox)
2019-11-10 03:36:53 -05:00
}
// OnUnload releases the resources of the Select Hero Class screen
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) OnUnload() error {
for i := range v.heroRenderInfo {
v.heroRenderInfo[i].SelectSfx.Stop()
v.heroRenderInfo[i].DeselectSfx.Stop()
}
2019-11-10 03:36:53 -05:00
v.heroRenderInfo = nil
2020-02-08 21:02:37 -05:00
return nil
2019-11-10 03:36:53 -05:00
}
2020-06-28 19:31:10 -04:00
func (v *SelectHeroClass) onExitButtonClicked() {
v.navigator.ToCharacterSelect(v.connectionType, v.connectionHost)
2019-11-10 03:36:53 -05:00
}
2020-06-28 19:31:10 -04:00
func (v *SelectHeroClass) onOkButtonClicked() {
gameState := d2player.CreatePlayerState(
v.heroNameTextbox.GetText(),
v.selectedHero,
d2datadict.CharStats[v.selectedHero],
v.hardcoreCheckbox.GetCheckState(),
)
v.navigator.ToCreateGame(gameState.FilePath, d2clientconnectiontype.Local, v.connectionHost)
}
// Render renders the Select Hero Class screen
func (v *SelectHeroClass) Render(screen d2interface.Surface) error {
if err := v.bgImage.RenderSegmented(screen, 4, 3, 0); err != nil {
return err
}
v.headingLabel.Render(screen)
2019-11-10 03:36:53 -05:00
if v.selectedHero != d2enum.HeroNone {
v.heroClassLabel.Render(screen)
v.heroDesc1Label.Render(screen)
v.heroDesc2Label.Render(screen)
v.heroDesc3Label.Render(screen)
2019-11-10 03:36:53 -05:00
}
2019-11-10 03:36:53 -05:00
for heroClass, heroInfo := range v.heroRenderInfo {
if heroInfo.Stance == d2enum.HeroStanceIdle || heroInfo.Stance == d2enum.HeroStanceIdleSelected {
v.renderHero(screen, heroClass)
}
}
2019-11-10 03:36:53 -05:00
for heroClass, heroInfo := range v.heroRenderInfo {
if heroInfo.Stance != d2enum.HeroStanceIdle && heroInfo.Stance != d2enum.HeroStanceIdleSelected {
v.renderHero(screen, heroClass)
}
}
if err := v.campfire.Render(screen); err != nil {
return err
}
if v.heroNameTextbox.GetVisible() {
v.heroNameLabel.Render(screen)
v.expansionCharLabel.Render(screen)
v.hardcoreCharLabel.Render(screen)
}
2020-02-08 21:02:37 -05:00
return nil
2019-11-10 03:36:53 -05:00
}
// Advance runs the update logic on the Select Hero Class screen
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) Advance(tickTime float64) error {
2019-11-10 03:36:53 -05:00
canSelect := true
if err := v.campfire.Advance(tickTime); err != nil {
return err
}
for infoIdx := range v.heroRenderInfo {
v.heroRenderInfo[infoIdx].advance(tickTime)
if v.heroRenderInfo[infoIdx].Stance != d2enum.HeroStanceIdle &&
v.heroRenderInfo[infoIdx].Stance != d2enum.HeroStanceIdleSelected &&
v.heroRenderInfo[infoIdx].Stance != d2enum.HeroStanceSelected {
2019-11-10 03:36:53 -05:00
canSelect = false
}
}
for heroType := range v.heroRenderInfo {
v.updateHeroSelectionHover(heroType, canSelect)
2019-11-10 03:36:53 -05:00
}
v.okButton.SetEnabled(len(v.heroNameTextbox.GetText()) >= 2 && v.selectedHero != d2enum.HeroNone)
2020-02-08 21:02:37 -05:00
return nil
2019-11-10 03:36:53 -05:00
}
func (v *SelectHeroClass) updateHeroSelectionHover(hero d2enum.Hero, canSelect bool) {
renderInfo := v.heroRenderInfo[hero]
switch renderInfo.Stance {
case d2enum.HeroStanceApproaching:
if renderInfo.ForwardWalkSprite.IsOnLastFrame() {
2019-11-10 03:36:53 -05:00
renderInfo.Stance = d2enum.HeroStanceSelected
setSpriteToFirstFrame(renderInfo.SelectedSprite)
setSpriteToFirstFrame(renderInfo.SelectedSpriteOverlay)
2019-11-10 03:36:53 -05:00
}
2019-11-10 03:36:53 -05:00
return
case d2enum.HeroStanceRetreating:
if renderInfo.BackWalkSprite.IsOnLastFrame() {
2019-11-10 03:36:53 -05:00
renderInfo.Stance = d2enum.HeroStanceIdle
setSpriteToFirstFrame(renderInfo.IdleSprite)
2019-11-10 03:36:53 -05:00
}
2019-11-10 03:36:53 -05:00
return
}
if !canSelect || renderInfo.Stance == d2enum.HeroStanceSelected {
2019-11-10 03:36:53 -05:00
return
}
mouseX, mouseY := d2ui.CursorPosition()
2019-11-10 03:36:53 -05:00
b := renderInfo.SelectionBounds
mouseHover := (mouseX >= b.Min.X) && (mouseX <= b.Min.X+b.Max.X) && (mouseY >= b.Min.Y) && (mouseY <= b.Min.Y+b.Max.Y)
if mouseHover && d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) {
v.handleCursorButtonPress(hero, renderInfo)
return
}
v.setCurrentFrame(mouseHover, renderInfo)
if v.selectedHero == d2enum.HeroNone && mouseHover {
2019-11-10 03:36:53 -05:00
v.selectedHero = hero
v.updateHeroText()
}
}
2019-11-10 03:36:53 -05:00
func (v *SelectHeroClass) handleCursorButtonPress(hero d2enum.Hero, renderInfo *HeroRenderInfo) {
v.heroNameTextbox.SetVisible(true)
v.heroNameTextbox.Activate()
v.okButton.SetVisible(true)
v.expansionCheckbox.SetVisible(true)
v.hardcoreCheckbox.SetVisible(true)
renderInfo.Stance = d2enum.HeroStanceApproaching
setSpriteToFirstFrame(renderInfo.ForwardWalkSprite)
setSpriteToFirstFrame(renderInfo.ForwardWalkSpriteOverlay)
for _, heroInfo := range v.heroRenderInfo {
if heroInfo.Stance != d2enum.HeroStanceSelected {
continue
}
heroInfo.SelectSfx.Stop()
heroInfo.DeselectSfx.Play()
heroInfo.Stance = d2enum.HeroStanceRetreating
setSpriteToFirstFrame(heroInfo.BackWalkSprite)
setSpriteToFirstFrame(heroInfo.BackWalkSpriteOverlay)
2019-11-10 03:36:53 -05:00
}
v.selectedHero = hero
v.updateHeroText()
renderInfo.SelectSfx.Play()
}
func (v *SelectHeroClass) setCurrentFrame(mouseHover bool, renderInfo *HeroRenderInfo) {
if mouseHover && renderInfo.Stance != d2enum.HeroStanceIdleSelected {
if err := renderInfo.IdleSelectedSprite.SetCurrentFrame(renderInfo.IdleSprite.GetCurrentFrame()); err != nil {
fmt.Printf("could not set current frame to: %d\n", renderInfo.IdleSprite.GetCurrentFrame())
}
2019-11-10 03:36:53 -05:00
renderInfo.Stance = d2enum.HeroStanceIdleSelected
} else if !mouseHover && renderInfo.Stance != d2enum.HeroStanceIdle {
if err := renderInfo.IdleSprite.SetCurrentFrame(renderInfo.IdleSelectedSprite.GetCurrentFrame()); err != nil {
fmt.Printf("could not set current frame to: %d\n", renderInfo.IdleSelectedSprite.GetCurrentFrame())
}
2019-11-10 03:36:53 -05:00
renderInfo.Stance = d2enum.HeroStanceIdle
}
}
func (v *SelectHeroClass) renderHero(screen d2interface.Surface, hero d2enum.Hero) {
2019-11-10 03:36:53 -05:00
renderInfo := v.heroRenderInfo[hero]
switch renderInfo.Stance {
case d2enum.HeroStanceIdle:
drawSprite(renderInfo.IdleSprite, screen)
2019-11-10 03:36:53 -05:00
case d2enum.HeroStanceIdleSelected:
drawSprite(renderInfo.IdleSelectedSprite, screen)
2019-11-10 03:36:53 -05:00
case d2enum.HeroStanceApproaching:
drawSprite(renderInfo.ForwardWalkSprite, screen)
drawSprite(renderInfo.ForwardWalkSpriteOverlay, screen)
2019-11-10 03:36:53 -05:00
case d2enum.HeroStanceSelected:
drawSprite(renderInfo.SelectedSprite, screen)
drawSprite(renderInfo.SelectedSpriteOverlay, screen)
2019-11-10 03:36:53 -05:00
case d2enum.HeroStanceRetreating:
drawSprite(renderInfo.BackWalkSprite, screen)
drawSprite(renderInfo.BackWalkSpriteOverlay, screen)
2019-11-10 03:36:53 -05:00
}
}
func (v *SelectHeroClass) updateHeroText() {
// v.setDescLabels("") really takes a string translation key, but temporarily disabled.
2019-11-10 03:36:53 -05:00
switch v.selectedHero {
case d2enum.HeroNone:
return
case d2enum.HeroBarbarian:
v.heroClassLabel.SetText(d2common.TranslateString("partycharbar"))
v.setDescLabels("He is unequaled in close-quarters combat and mastery of weapons.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroNecromancer:
v.heroClassLabel.SetText(d2common.TranslateString("partycharnec"))
v.setDescLabels("Summoning undead minions and cursing his enemies are his specialties.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroPaladin:
v.heroClassLabel.SetText(d2common.TranslateString("partycharpal"))
v.setDescLabels("He is a natural party leader, holy man, and blessed warrior.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroAssassin:
v.heroClassLabel.SetText(d2common.TranslateString("partycharass"))
v.setDescLabels("Schooled in the Martial Arts, her mind and body are deadly weapons.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroSorceress:
v.heroClassLabel.SetText(d2common.TranslateString("partycharsor"))
v.setDescLabels("She has mastered the elemental magicks -- fire, lightning, and ice.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroAmazon:
v.heroClassLabel.SetText(d2common.TranslateString("partycharama"))
v.setDescLabels("Skilled with the spear and the bow, she is a very versatile fighter.")
2019-11-10 03:36:53 -05:00
case d2enum.HeroDruid:
v.heroClassLabel.SetText(d2common.TranslateString("partychardru"))
v.setDescLabels("Commanding the forces of nature, he summons wild beasts and raging storms to his side.")
2019-11-10 03:36:53 -05:00
}
}
const (
oneLine = 1
twoLine = 2
)
2019-11-10 03:36:53 -05:00
func (v *SelectHeroClass) setDescLabels(descKey string) {
heroDesc := d2common.TranslateString(descKey)
parts := d2common.SplitIntoLinesWithMaxWidth(heroDesc, heroDescCharWidth)
numLines := len(parts)
if numLines > oneLine {
2019-11-10 03:36:53 -05:00
v.heroDesc1Label.SetText(parts[0])
v.heroDesc2Label.SetText(parts[1])
} else {
v.heroDesc1Label.SetText("")
2019-11-10 03:36:53 -05:00
v.heroDesc2Label.SetText("")
}
if numLines > twoLine {
2019-11-10 03:36:53 -05:00
v.heroDesc3Label.SetText(parts[2])
} else {
v.heroDesc3Label.SetText("")
}
}
func setSpriteToFirstFrame(sprite *d2ui.Sprite) {
if sprite != nil {
sprite.Rewind()
}
}
func drawSprite(sprite *d2ui.Sprite, target d2interface.Surface) {
if sprite != nil {
if err := sprite.Render(target); err != nil {
x, y := sprite.GetPosition()
fmt.Printf("could not render the sprite to the position(x: %d, y: %d)\n", x, y)
}
}
}
func advanceSprite(sprite *d2ui.Sprite, elapsed float64) {
if sprite != nil {
if err := sprite.Advance(elapsed); err != nil {
fmt.Printf("could not advance the sprite\n")
}
}
}
func loadSprite(animationPath string, position image.Point, playLength int, playLoop,
blend bool) *d2ui.Sprite {
if animationPath == "" {
return nil
}
animation, err := d2asset.LoadAnimation(animationPath, d2resource.PaletteFechar)
if err != nil {
fmt.Printf("could not load animation: %s\n", animationPath)
return nil
}
animation.PlayForward()
animation.SetPlayLoop(playLoop)
if blend {
animation.SetEffect(d2enum.DrawEffectModulate)
}
if playLength != 0 {
animation.SetPlayLengthMs(playLength)
}
sprite, err := d2ui.LoadSprite(animation)
if err != nil {
fmt.Printf("could not load sprite for the animation: %s\n", animationPath)
return nil
}
sprite.SetPosition(position.X, position.Y)
return sprite
}
2020-06-28 19:31:10 -04:00
func (v *SelectHeroClass) loadSoundEffect(sfx string) d2interface.SoundEffect {
result, _ := v.audioProvider.LoadSoundEffect(sfx)
return result
}