1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-03 23:26:41 -05:00
OpenDiablo2/d2game/d2gamescreen/select_hero_class.go

801 lines
29 KiB
Go
Raw Normal View History

package d2gamescreen
2019-11-10 03:36:53 -05:00
import (
2020-09-12 16:25:09 -04:00
"image"
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
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"
remove d2asset singleton (#726) * export d2asset singleton * add *d2asset.AssetManager to d2app - d2app now has a reference to an asset manager which it will use for loading - added asset loader methods to the asset manager - functions in d2asset are now wrappers for asset manager methods * add asset manager reference to audio provider - d2app asset manager reference is now passed to audio provider - asset manager is created in main.go for now to pass into audio provider - CreateSoundEffect is now a method, no longer exported, uses the asset manager reference * d2app passes asset manager refence to map engine test * in d2asset, all calls to LoadFile replaced with call to Singleton.Loadfile * blizzard intro and credits screen - d2app passes reference to the asset manager to these screens * asset manager for d2map - adding MapStampFactory, takes an asset manager reference - embedded MapStampFactory into the MapEngine - LoadStamp is now a method of the MapStampFactory * d2asset: removed LoadFileStream, LoadFile, and FileExists * d2gui changes - singleton now has an asset manager reference - calls to d2asset loader functions removed - createButton is now a method of LayoutManager - moved LayoutEntry to its own file * map entity factory - Map engine has an embedded map entity factory - Map stamp factory gets a reference to the map engine's entity factory - Stamps are given a reference to the map engine entity factory when created - Character select gets a map entity factory - Embedded the stamp factory into the MapEngine * asset manager for d2ui - d2ui is passed an asset manager reference when created - all calls to d2asset loader functions in d2ui now refer to the asset manager - d2gamescreen gets a ui manager when created - help overlay is now passed a ui manager when created * d2gamescreen + d2player: asset manager references added an asset manager reference to - inventory panel + inventory grid - mini panel - game controls - help overlay - character select - main menu - select hero class - hero stats panel * Removed d2asset.LoadAnimation all references to this function have been replaced with calls to the asset manager method * adding asset to help overlay, bugfix for 4d59c91 * Removed d2asset.LoadFont and d2asset.LoadAnimationWithEffect all references to these have been replaced with calls to the asset manager methods * MapRenderer now gets an asset manager reference * removed d2asset.LoadPalette all references have been replaced with calls to an asset manager instance * merged d2object with d2mapentity d2object was only being used to create objects in the map, so the provider function is now a method of the map entity factory. calls to d2asset have been removed. * removed d2asset.LoadComposite all calls are now made to the asset manager method * removed d2asset singleton all singleton references have been removed, a single instance of the asset manager is passed around the entire app * rename Initialize to NewAssetManager
2020-09-12 16:51:30 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
2020-02-08 21:02:37 -05:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
2019-11-10 03:36:53 -05:00
)
const (
millisecondsPerSecond = 1000.0
)
type heroRenderConfig struct {
2021-06-13 11:10:38 -04:00
selectSfx string
idleSelectedAnimationPath string
forwardWalkAnimationPath string
forwardWalkOverlayAnimationPath string
2021-06-13 11:10:38 -04:00
deselectSfx string
selectedAnimationPath string
selectedOverlayAnimationPath string
backWalkAnimationPath string
backWalkOverlayAnimationPath string
2021-06-13 11:10:38 -04:00
idleAnimationPath string
selectionBounds image.Rectangle
position image.Point
idlePlayLengthMs int
forwardWalkPlayLengthMs int
backWalkPlayLengthMs int
2021-06-13 11:10:38 -04:00
forwardWalkOverlayBlend bool
}
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{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectBarbarianUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectBarbarianUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectBarbarianForwardWalk,
forwardWalkOverlayAnimationPath: d2resource.CharacterSelectBarbarianForwardWalkOverlay,
forwardWalkOverlayBlend: false,
selectedAnimationPath: d2resource.CharacterSelectBarbarianSelected,
selectedOverlayAnimationPath: "",
backWalkAnimationPath: d2resource.CharacterSelectBarbarianBackWalk,
backWalkOverlayAnimationPath: "",
selectionBounds: rect(barbRectMinX, barbRectMinY, barbRectMaxX, barbRectMaxY),
selectSfx: d2resource.SFXBarbarianSelect,
deselectSfx: d2resource.SFXBarbarianDeselect,
position: point(barbPosX, barbPosY),
idlePlayLengthMs: barbIdleLength,
forwardWalkPlayLengthMs: barbForwardLength,
backWalkPlayLengthMs: barbBackLength,
}
configs[d2enum.HeroSorceress] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectSorceressUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectSorceressUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectSorceressForwardWalk,
forwardWalkOverlayAnimationPath: d2resource.CharacterSelectSorceressForwardWalkOverlay,
forwardWalkOverlayBlend: true,
selectedAnimationPath: d2resource.CharacterSelectSorceressSelected,
selectedOverlayAnimationPath: d2resource.CharacterSelectSorceressSelectedOverlay,
backWalkAnimationPath: d2resource.CharacterSelectSorceressBackWalk,
backWalkOverlayAnimationPath: d2resource.CharacterSelectSorceressBackWalkOverlay,
selectionBounds: rect(sorcRectMinX, sorcRectMinY, sorcRectMaxX, sorcRectMaxY),
selectSfx: d2resource.SFXSorceressSelect,
deselectSfx: d2resource.SFXSorceressDeselect,
position: point(sorcPosX, sorcPosY),
idlePlayLengthMs: sorcIdleLength,
forwardWalkPlayLengthMs: sorcForwardLength,
backWalkPlayLengthMs: sorcBackLength,
}
configs[d2enum.HeroNecromancer] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectNecromancerUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectNecromancerUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectNecromancerForwardWalk,
forwardWalkOverlayAnimationPath: d2resource.CharacterSelectNecromancerForwardWalkOverlay,
forwardWalkOverlayBlend: true,
selectedAnimationPath: d2resource.CharacterSelectNecromancerSelected,
selectedOverlayAnimationPath: d2resource.CharacterSelectNecromancerSelectedOverlay,
backWalkAnimationPath: d2resource.CharacterSelectNecromancerBackWalk,
backWalkOverlayAnimationPath: d2resource.CharacterSelectNecromancerBackWalkOverlay,
selectionBounds: rect(necRectMinX, necRectMinY, necRectMaxX, necRectMaxY),
selectSfx: d2resource.SFXNecromancerSelect,
deselectSfx: d2resource.SFXNecromancerDeselect,
position: point(necPosX, necPosY),
idlePlayLengthMs: necIdleLength,
forwardWalkPlayLengthMs: necForwardLength,
backWalkPlayLengthMs: necBackLength,
}
configs[d2enum.HeroPaladin] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectPaladinUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectPaladinUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectPaladinForwardWalk,
forwardWalkOverlayAnimationPath: d2resource.CharacterSelectPaladinForwardWalkOverlay,
forwardWalkOverlayBlend: false,
selectedAnimationPath: d2resource.CharacterSelectPaladinSelected,
selectedOverlayAnimationPath: "",
backWalkAnimationPath: d2resource.CharacterSelectPaladinBackWalk,
backWalkOverlayAnimationPath: "",
selectionBounds: rect(palRectMinX, palRectMinY, palRectMaxX, palRectMaxY),
selectSfx: d2resource.SFXPaladinSelect,
deselectSfx: d2resource.SFXPaladinDeselect,
position: point(palPosX, palPosY),
idlePlayLengthMs: palIdleLength,
forwardWalkPlayLengthMs: palForwardLength,
backWalkPlayLengthMs: palBackLength,
}
configs[d2enum.HeroAmazon] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectAmazonUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectAmazonUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectAmazonForwardWalk,
forwardWalkOverlayAnimationPath: "",
forwardWalkOverlayBlend: false,
selectedAnimationPath: d2resource.CharacterSelectAmazonSelected,
selectedOverlayAnimationPath: "",
backWalkAnimationPath: d2resource.CharacterSelectAmazonBackWalk,
backWalkOverlayAnimationPath: "",
selectionBounds: rect(amaRectMinX, amaRectMinY, amaRectMaxX, amaRectMaxY),
selectSfx: d2resource.SFXAmazonSelect,
deselectSfx: d2resource.SFXAmazonDeselect,
position: point(amaPosX, amaPosY),
idlePlayLengthMs: amaIdleLength,
forwardWalkPlayLengthMs: amaForwardLength,
backWalkPlayLengthMs: amaBackLength,
}
configs[d2enum.HeroAssassin] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectAssassinUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectAssassinUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectAssassinForwardWalk,
forwardWalkOverlayAnimationPath: "",
forwardWalkOverlayBlend: false,
selectedAnimationPath: d2resource.CharacterSelectAssassinSelected,
selectedOverlayAnimationPath: "",
backWalkAnimationPath: d2resource.CharacterSelectAssassinBackWalk,
backWalkOverlayAnimationPath: "",
selectionBounds: rect(assRectMinX, assRectMinY, assRectMaxX, assRectMaxY),
selectSfx: d2resource.SFXAssassinSelect,
deselectSfx: d2resource.SFXAssassinDeselect,
position: point(assPosX, assPosY),
idlePlayLengthMs: assIdleLength,
forwardWalkPlayLengthMs: assForwardLength,
backWalkPlayLengthMs: assBackLength,
}
configs[d2enum.HeroDruid] = &heroRenderConfig{
2021-06-13 11:10:38 -04:00
idleAnimationPath: d2resource.CharacterSelectDruidUnselected,
idleSelectedAnimationPath: d2resource.CharacterSelectDruidUnselectedH,
forwardWalkAnimationPath: d2resource.CharacterSelectDruidForwardWalk,
forwardWalkOverlayAnimationPath: "",
forwardWalkOverlayBlend: false,
selectedAnimationPath: d2resource.CharacterSelectDruidSelected,
selectedOverlayAnimationPath: "",
backWalkAnimationPath: d2resource.CharacterSelectDruidBackWalk,
backWalkOverlayAnimationPath: "",
selectionBounds: rect(druRectMinX, druRectMinY, druRectMaxX, druRectMaxY),
selectSfx: d2resource.SFXDruidSelect,
deselectSfx: d2resource.SFXDruidDeselect,
position: point(druPosX, druPosY),
idlePlayLengthMs: druIdleLength,
forwardWalkPlayLengthMs: druForwardLength,
backWalkPlayLengthMs: 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 {
2021-06-13 11:10:38 -04:00
DeselectSfx d2interface.SoundEffect
SelectSfx d2interface.SoundEffect
shc *SelectHeroClass
ForwardWalkSprite *d2ui.Sprite
ForwardWalkSpriteOverlay *d2ui.Sprite
SelectedSprite *d2ui.Sprite
SelectedSpriteOverlay *d2ui.Sprite
BackWalkSprite *d2ui.Sprite
BackWalkSpriteOverlay *d2ui.Sprite
2021-06-13 11:10:38 -04:00
IdleSelectedSprite *d2ui.Sprite
IdleSprite *d2ui.Sprite
2019-11-10 03:36:53 -05:00
SelectionBounds image.Rectangle
2021-06-13 11:10:38 -04:00
Stance d2enum.HeroStance
2019-11-10 03:36:53 -05:00
}
func (hri *HeroRenderInfo) advance(elapsed float64) {
advanceSprite(hri.shc, hri.IdleSprite, elapsed)
advanceSprite(hri.shc, hri.IdleSelectedSprite, elapsed)
advanceSprite(hri.shc, hri.ForwardWalkSprite, elapsed)
advanceSprite(hri.shc, hri.ForwardWalkSpriteOverlay, elapsed)
advanceSprite(hri.shc, hri.SelectedSprite, elapsed)
advanceSprite(hri.shc, hri.SelectedSpriteOverlay, elapsed)
advanceSprite(hri.shc, hri.BackWalkSprite, elapsed)
advanceSprite(hri.shc, hri.BackWalkSpriteOverlay, elapsed)
2019-11-10 03:36:53 -05:00
}
// CreateSelectHeroClass creates an instance of a SelectHeroClass
func CreateSelectHeroClass(
navigator d2interface.Navigator,
asset *d2asset.AssetManager,
renderer d2interface.Renderer,
audioProvider d2interface.AudioProvider,
ui *d2ui.UIManager,
connectionType d2clientconnectiontype.ClientConnectionType,
l d2util.LogLevel,
connectionHost string,
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
) (*SelectHeroClass, error) {
playerStateFactory, err := d2hero.NewHeroStateFactory(asset)
if err != nil {
return nil, err
}
inventoryItemFactory, err := d2inventory.NewInventoryItemFactory(asset)
if err != nil {
return nil, err
}
selectHeroClass := &SelectHeroClass{
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
asset: asset,
heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo),
selectedHero: d2enum.HeroNone,
connectionType: connectionType,
connectionHost: connectionHost,
audioProvider: audioProvider,
renderer: renderer,
navigator: navigator,
uiManager: ui,
HeroStateFactory: playerStateFactory,
InventoryItemFactory: inventoryItemFactory,
2019-11-10 03:36:53 -05:00
}
selectHeroClass.Logger = d2util.NewLogger()
selectHeroClass.Logger.SetLevel(l)
selectHeroClass.Logger.SetPrefix(logPrefix)
return selectHeroClass, nil
}
// SelectHeroClass represents the Select Hero Class screen
type SelectHeroClass struct {
2021-06-13 11:10:38 -04:00
navigator d2interface.Navigator
audioProvider d2interface.AudioProvider
renderer d2interface.Renderer
asset *d2asset.AssetManager
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
*d2inventory.InventoryItemFactory
*d2hero.HeroStateFactory
2021-06-13 11:10:38 -04:00
campfire *d2ui.Sprite
exitButton *d2ui.Button
okButton *d2ui.Button
expansionCheckbox *d2ui.Checkbox
expansionCharLabel *d2ui.Label
hardcoreCheckbox *d2ui.Checkbox
hardcoreCharLabel *d2ui.Label
2021-06-13 11:10:38 -04:00
bgImage *d2ui.Sprite
uiManager *d2ui.UIManager
*d2util.Logger
2021-06-13 11:10:38 -04:00
connectionHost string
connectionType d2clientconnectiontype.ClientConnectionType
selectedHero d2enum.Hero
2019-11-10 03:36:53 -05:00
}
// 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 = v.loadSprite(
d2resource.CharacterSelectBackground,
point(0, 0),
0,
true,
false,
)
loading.Progress(thirtyPercent)
v.createLabels()
loading.Progress(fourtyPercent)
v.createButtons()
v.campfire = v.loadSprite(
d2resource.CharacterSelectCampfire,
point(campfirePosX, campfirePosY),
0,
true,
true,
)
v.createCheckboxes()
loading.Progress(fiftyPercent)
for hero, config := range getHeroRenderConfiguration() {
position := config.position
forwardWalkOverlaySprite := v.loadSprite(
config.forwardWalkOverlayAnimationPath,
position,
config.forwardWalkPlayLengthMs,
false,
config.forwardWalkOverlayBlend,
)
v.heroRenderInfo[hero] = &HeroRenderInfo{
Stance: d2enum.HeroStanceIdle,
IdleSprite: v.loadSprite(config.idleAnimationPath, position,
config.idlePlayLengthMs, true, false),
IdleSelectedSprite: v.loadSprite(config.idleSelectedAnimationPath,
position,
config.idlePlayLengthMs, true, false),
ForwardWalkSprite: v.loadSprite(config.forwardWalkAnimationPath, position,
config.forwardWalkPlayLengthMs, false, false),
ForwardWalkSpriteOverlay: forwardWalkOverlaySprite,
SelectedSprite: v.loadSprite(config.selectedAnimationPath, position,
config.idlePlayLengthMs, true, false),
SelectedSpriteOverlay: v.loadSprite(config.selectedOverlayAnimationPath, position,
config.idlePlayLengthMs, true, true),
BackWalkSprite: v.loadSprite(config.backWalkAnimationPath, position,
config.backWalkPlayLengthMs, false, false),
BackWalkSpriteOverlay: v.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 = v.uiManager.NewLabel(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)
v.headingLabel.SetText(v.asset.TranslateString(d2enum.SelectHeroClassLabel))
v.headingLabel.Alignment = d2ui.HorizontalAlignCenter
2020-02-08 21:02:37 -05:00
v.heroClassLabel = v.uiManager.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
v.heroClassLabel.Alignment = d2ui.HorizontalAlignCenter
v.heroClassLabel.SetPosition(heroClassLabelX, heroClassLabelY)
2020-02-08 21:02:37 -05:00
v.heroDesc1Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc1Label.Alignment = d2ui.HorizontalAlignCenter
v.heroDesc1Label.SetPosition(heroDescLine1X, heroDescLine1Y)
2020-02-08 21:02:37 -05:00
v.heroDesc2Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc2Label.Alignment = d2ui.HorizontalAlignCenter
v.heroDesc2Label.SetPosition(heroDescLine2X, heroDescLine2Y)
2020-02-08 21:02:37 -05:00
v.heroDesc3Label = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroDesc3Label.Alignment = d2ui.HorizontalAlignCenter
v.heroDesc3Label.SetPosition(heroDescLine3X, heroDescLine3Y)
2020-02-08 21:02:37 -05:00
v.heroNameLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.heroNameLabel.Alignment = d2ui.HorizontalAlignLeft
v.heroNameLabel.SetText(d2ui.ColorTokenize(v.asset.TranslateString(d2enum.CharNameLabel), d2ui.ColorTokenGold))
v.heroNameLabel.SetPosition(heroNameLabelX, heroNameLabelY)
v.expansionCharLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.expansionCharLabel.Alignment = d2ui.HorizontalAlignLeft
2020-11-26 07:51:39 -05:00
v.expansionCharLabel.SetText(d2ui.ColorTokenize(v.asset.TranslateString("#803"), d2ui.ColorTokenGold))
v.expansionCharLabel.SetPosition(expansionLabelX, expansionLabelY)
v.hardcoreCharLabel = v.uiManager.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
v.hardcoreCharLabel.Alignment = d2ui.HorizontalAlignLeft
v.hardcoreCharLabel.SetText(d2ui.ColorTokenize(v.asset.TranslateString(d2enum.HardCoreLabel), d2ui.ColorTokenGold))
v.hardcoreCharLabel.SetPosition(hardcoreLabelX, hardcoreLabelY)
}
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) createButtons() {
v.exitButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, v.asset.TranslateString(d2enum.ExitLabel))
v.exitButton.SetPosition(selHeroExitBtnX, selHeroExitBtnY)
2020-02-08 21:02:37 -05:00
v.exitButton.OnActivated(func() { v.onExitButtonClicked() })
v.okButton = v.uiManager.NewButton(d2ui.ButtonTypeMedium, v.asset.TranslateString(d2enum.OKLabel))
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)
}
2020-02-08 21:02:37 -05:00
func (v *SelectHeroClass) createCheckboxes() {
v.heroNameTextbox = v.uiManager.NewTextbox()
v.heroNameTextbox.SetPosition(heroNameTextBoxX, heoNameTextBoxY)
2020-02-08 21:02:37 -05:00
v.heroNameTextbox.SetVisible(false)
v.expansionCheckbox = v.uiManager.NewCheckbox(true)
v.expansionCheckbox.SetPosition(expandsionCheckboxX, expansionCheckboxY)
2020-02-08 21:02:37 -05:00
v.expansionCheckbox.SetVisible(false)
v.hardcoreCheckbox = v.uiManager.NewCheckbox(false)
v.hardcoreCheckbox.SetPosition(hardcoreCheckoxX, hardcoreCheckboxY)
2020-02-08 21:02:37 -05:00
v.hardcoreCheckbox.SetVisible(false)
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() {
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
heroName := v.heroNameTextbox.GetText()
defaultStats := v.asset.Records.Character.Stats[v.selectedHero]
statsState := v.CreateHeroStatsState(v.selectedHero, defaultStats)
playerState, err := v.CreateHeroState(heroName, v.selectedHero, statsState)
if err != nil {
v.Errorf("failed to create hero state!, err: %v", err.Error())
return
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
}
err = v.Save(playerState)
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
if err != nil {
v.Errorf("failed to save game state!, err: %v", err.Error())
Removing d2datadict singletons (#738) * Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
2020-09-20 17:52:01 -04:00
return
}
playerState.Equipment = v.InventoryItemFactory.DefaultHeroItems[v.selectedHero]
v.navigator.ToCreateGame(playerState.FilePath, v.connectionType, v.connectionHost)
}
// Render renders the Select Hero Class screen
func (v *SelectHeroClass) Render(screen d2interface.Surface) {
v.bgImage.RenderSegmented(screen, 4, 3, 0)
v.headingLabel.Render(screen)
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 v.selectedHero != d2enum.HeroNone {
v.heroClassLabel.Render(screen)
v.heroDesc1Label.Render(screen)
v.heroDesc2Label.Render(screen)
v.heroDesc3Label.Render(screen)
}
v.campfire.Render(screen)
if v.heroNameTextbox.GetVisible() {
v.heroNameLabel.Render(screen)
v.expansionCharLabel.Render(screen)
v.hardcoreCharLabel.Render(screen)
}
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 := v.uiManager.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 && v.uiManager.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 {
v.Errorf("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 {
v.Errorf("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(v.asset.TranslateString("partycharbar"))
v.setDescLabels(d2enum.BarbarianDescr, "")
2019-11-10 03:36:53 -05:00
case d2enum.HeroNecromancer:
v.heroClassLabel.SetText(v.asset.TranslateString("partycharnec"))
v.setDescLabels(d2enum.NecromancerDescr, "")
2019-11-10 03:36:53 -05:00
case d2enum.HeroPaladin:
v.heroClassLabel.SetText(v.asset.TranslateString("partycharpal"))
v.setDescLabels(d2enum.PaladinDescr, "")
2019-11-10 03:36:53 -05:00
case d2enum.HeroAssassin:
v.heroClassLabel.SetText(v.asset.TranslateString("partycharass"))
v.setDescLabels(0, "#305")
2019-11-10 03:36:53 -05:00
case d2enum.HeroSorceress:
v.heroClassLabel.SetText(v.asset.TranslateString("partycharsor"))
v.setDescLabels(d2enum.SorceressDescr, "")
2019-11-10 03:36:53 -05:00
case d2enum.HeroAmazon:
v.heroClassLabel.SetText(v.asset.TranslateString("partycharama"))
v.setDescLabels(d2enum.AmazonDescr, "")
2019-11-10 03:36:53 -05:00
case d2enum.HeroDruid:
v.heroClassLabel.SetText(v.asset.TranslateString("partychardru"))
// here is a problem with polish language: in polish string table, there are two items with key "#304"
v.setDescLabels(0, "#304")
2019-11-10 03:36:53 -05:00
}
}
const (
oneLine = 1
twoLine = 2
)
func (v *SelectHeroClass) setDescLabels(descKey int, key string) {
var heroDesc string
2020-11-25 13:25:19 -05:00
if key != "" {
heroDesc = v.asset.TranslateString(key)
} else {
heroDesc = v.asset.TranslateString(descKey)
}
2020-11-25 13:25:19 -05:00
parts := d2util.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 {
sprite.Render(target)
}
}
func advanceSprite(v *SelectHeroClass, sprite *d2ui.Sprite, elapsed float64) {
if sprite != nil {
if err := sprite.Advance(elapsed); err != nil {
v.Error("could not advance the sprite:" + err.Error())
}
}
}
func (v *SelectHeroClass) loadSprite(animationPath string, position image.Point,
playLength int,
playLoop,
blend bool) *d2ui.Sprite {
if animationPath == "" {
return nil
}
Decouple asset manager from renderer (#730) * improve AssetManager implementation Notable changes are: * removed the individual managers inside of d2asset, only one asset manager * AssetManager now has caches for the types of files it loads * created a type for TextDictionary (the txt file structs) * fixed a file path bug in d2loader Source * fixed a asset stream bug in d2loader Asset * d2loader.Loader now needs a d2config.Config on creation (for resolving locale files) * updated the mpq file in d2asset test data, added test case for "sub-directory" * added a Data method to d2asset.Asset. The data is cached on first full read. * renamed ArchiveDataStream to DataStream in d2interface * moved palette utility func out of d2asset and into d2util * bugfix for MacOS mpq loader issue * lint fixes, added data caching to filesystem asset * adding comment for mpq asset close * Decouple d2asset from d2render Notable changes in d2common: * d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct * un-exported dcc.decodeDirection, it is only used in d2dcc * removed font interface from d2interface, we only have one font implementation * added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need * added `BindRenderer` method to animation interface Notable changes in d2common/d2asset: * **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render * exported Animation * Animation implementation binds to the renderer to create surfaces only on the first time it is rendered * font, dcc, dc6 initialization logic moved out of asset_manager.go * for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods * the d2asset.Font struct now stores font table data for initialization purposes Notable changes in d2core/d2render: * Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time **These last changes should have been a separate PR, sorry.** Notable changes in d2core/d2ui: * ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments Notable Changes in d2game: Because of the change in d2ui, all instances of this code pattern... ```golang animation, err := screen.asset.LoadAnimation(imgPath, palettePath) sprite, err := screen.ui.NewSprite(animation) ``` ... becomes this ... ```golang sprite, err := screen.ui.NewSprite(imgPath, palettePath) ```
2020-09-14 17:31:45 -04:00
sprite, err := v.uiManager.NewSprite(animationPath, d2resource.PaletteFechar)
if err != nil {
v.Error("could not load sprite for the animation: %s\n" + animationPath + "with error: " + err.Error())
return nil
}
Decouple asset manager from renderer (#730) * improve AssetManager implementation Notable changes are: * removed the individual managers inside of d2asset, only one asset manager * AssetManager now has caches for the types of files it loads * created a type for TextDictionary (the txt file structs) * fixed a file path bug in d2loader Source * fixed a asset stream bug in d2loader Asset * d2loader.Loader now needs a d2config.Config on creation (for resolving locale files) * updated the mpq file in d2asset test data, added test case for "sub-directory" * added a Data method to d2asset.Asset. The data is cached on first full read. * renamed ArchiveDataStream to DataStream in d2interface * moved palette utility func out of d2asset and into d2util * bugfix for MacOS mpq loader issue * lint fixes, added data caching to filesystem asset * adding comment for mpq asset close * Decouple d2asset from d2render Notable changes in d2common: * d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct * un-exported dcc.decodeDirection, it is only used in d2dcc * removed font interface from d2interface, we only have one font implementation * added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need * added `BindRenderer` method to animation interface Notable changes in d2common/d2asset: * **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render * exported Animation * Animation implementation binds to the renderer to create surfaces only on the first time it is rendered * font, dcc, dc6 initialization logic moved out of asset_manager.go * for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods * the d2asset.Font struct now stores font table data for initialization purposes Notable changes in d2core/d2render: * Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time **These last changes should have been a separate PR, sorry.** Notable changes in d2core/d2ui: * ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments Notable Changes in d2game: Because of the change in d2ui, all instances of this code pattern... ```golang animation, err := screen.asset.LoadAnimation(imgPath, palettePath) sprite, err := screen.ui.NewSprite(animation) ``` ... becomes this ... ```golang sprite, err := screen.ui.NewSprite(imgPath, palettePath) ```
2020-09-14 17:31:45 -04:00
sprite.PlayForward()
sprite.SetPlayLoop(playLoop)
if blend {
Decouple asset manager from renderer (#730) * improve AssetManager implementation Notable changes are: * removed the individual managers inside of d2asset, only one asset manager * AssetManager now has caches for the types of files it loads * created a type for TextDictionary (the txt file structs) * fixed a file path bug in d2loader Source * fixed a asset stream bug in d2loader Asset * d2loader.Loader now needs a d2config.Config on creation (for resolving locale files) * updated the mpq file in d2asset test data, added test case for "sub-directory" * added a Data method to d2asset.Asset. The data is cached on first full read. * renamed ArchiveDataStream to DataStream in d2interface * moved palette utility func out of d2asset and into d2util * bugfix for MacOS mpq loader issue * lint fixes, added data caching to filesystem asset * adding comment for mpq asset close * Decouple d2asset from d2render Notable changes in d2common: * d2dcc.Load now fully decodes the dcc and stores the directions/frames in the dcc struct * un-exported dcc.decodeDirection, it is only used in d2dcc * removed font interface from d2interface, we only have one font implementation * added `Renderer` method to d2interface.Surface, animations use this to bind to a renderer and create surfaces as they need * added `BindRenderer` method to animation interface Notable changes in d2common/d2asset: * **d2asset.NewAssetManager only needs to be passed a d2config.Config**, it is decoupled from d2render * exported Animation * Animation implementation binds to the renderer to create surfaces only on the first time it is rendered * font, dcc, dc6 initialization logic moved out of asset_manager.go * for dc6 and dcc animations, the process of decoding and creating render surfaces has been broken into different methods * the d2asset.Font struct now stores font table data for initialization purposes Notable changes in d2core/d2render: * Surfaces store a renderer reference, this allows animations to bind to the renderer and create a surface just-in-time **These last changes should have been a separate PR, sorry.** Notable changes in d2core/d2ui: * ui.NewSprite now handles creating an animation internally, only needs image and palette path as arguments Notable Changes in d2game: Because of the change in d2ui, all instances of this code pattern... ```golang animation, err := screen.asset.LoadAnimation(imgPath, palettePath) sprite, err := screen.ui.NewSprite(animation) ``` ... becomes this ... ```golang sprite, err := screen.ui.NewSprite(imgPath, palettePath) ```
2020-09-14 17:31:45 -04:00
sprite.SetEffect(d2enum.DrawEffectModulate)
}
if playLength != 0 {
sprite.SetPlayLength(float64(playLength) / millisecondsPerSecond)
}
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, err := v.audioProvider.LoadSound(sfx, false, false)
if err != nil {
v.Error(err.Error())
return nil
}
return result
}