1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-12 23:43:24 -05:00
OpenDiablo2/d2game/d2player/game_controls.go

1114 lines
31 KiB
Go
Raw Normal View History

package d2player
import (
"fmt"
"image"
"image/color"
"log"
"math"
"strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
2020-09-12 16:25:09 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player/help"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
2020-06-28 21:40:52 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
2020-06-21 18:40:37 -04:00
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
2020-07-26 14:52:54 -04:00
// Panel represents the panel at the bottom of the game screen
type Panel interface {
IsOpen() bool
Toggle()
Open()
Close()
}
const (
2020-08-11 18:01:33 -04:00
expBarWidth = 120.0
staminaBarWidth = 102.0
globeHeight = 80
globeWidth = 80
hoverLabelOuterPad = 5
mouseBtnActionsTreshhold = 0.25
)
2020-07-26 14:52:54 -04:00
// GameControls represents the game's controls on the screen
type GameControls struct {
2020-08-11 18:01:33 -04:00
actionableRegions []ActionableRegion
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
asset *d2asset.AssetManager
2020-08-11 18:01:33 -04:00
renderer d2interface.Renderer // TODO: This shouldn't be a dependency
inputListener InputCallbackListener
hero *d2mapentity.Player
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
heroState *d2hero.HeroStateFactory
2020-08-11 18:01:33 -04:00
mapEngine *d2mapengine.MapEngine
mapRenderer *d2maprenderer.MapRenderer
ui *d2ui.UIManager
2020-08-11 18:01:33 -04:00
inventory *Inventory
heroStatsPanel *HeroStatsPanel
HelpOverlay *help.Overlay
miniPanel *miniPanel
2020-08-11 18:01:33 -04:00
lastMouseX int
lastMouseY int
missileID int
globeSprite *d2ui.Sprite
hpManaStatusSprite *d2ui.Sprite
mainPanel *d2ui.Sprite
menuButton *d2ui.Sprite
leftSkill *SkillResource
rightSkill *SkillResource
2020-08-11 18:01:33 -04:00
zoneChangeText *d2ui.Label
nameLabel *d2ui.Label
hpManaStatsLabel *d2ui.Label
2020-08-11 18:01:33 -04:00
runButton *d2ui.Button
lastLeftBtnActionTime float64
lastRightBtnActionTime float64
FreeCam bool
isZoneTextShown bool
hpStatsIsVisible bool
manaStatsIsVisible bool
isSinglePlayer bool
}
type ActionableType int
2020-07-26 14:52:54 -04:00
type ActionableRegion struct {
2020-09-12 16:25:09 -04:00
ActionableTypeID ActionableType
Rect d2geom.Rectangle
}
type SkillResource struct {
SkillResourcePath string
IconNumber int
SkillIcon *d2ui.Sprite
}
const (
// Since they require special handling, not considering (1) globes, (2) content of the mini panel, (3) belt
leftSkill ActionableType = iota
newStats
xp
walkRun
stamina
miniPnl
newSkills
rightSkill
hpGlobe
manaGlobe
miniPanelCharacter
miniPanelInventory
miniPanelSkillTree
miniPanelAutomap
miniPanelMessageLog
miniPanelQuestLog
miniPanelGameMenu
)
2020-08-11 18:01:33 -04:00
// NewGameControls creates a GameControls instance and returns a pointer to it
func NewGameControls(
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
asset *d2asset.AssetManager,
renderer d2interface.Renderer,
hero *d2mapentity.Player,
mapEngine *d2mapengine.MapEngine,
mapRenderer *d2maprenderer.MapRenderer,
inputListener InputCallbackListener,
term d2interface.Terminal,
ui *d2ui.UIManager,
guiManager *d2gui.GuiManager,
isSinglePlayer bool,
) (*GameControls, error) {
zoneLabel := ui.NewLabel(d2resource.Font30, d2resource.PaletteUnits)
zoneLabel.Alignment = d2gui.HorizontalAlignCenter
nameLabel := ui.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
nameLabel.Alignment = d2gui.HorizontalAlignCenter
nameLabel.SetText(d2ui.ColorTokenize("", d2ui.ColorTokenServer))
hpManaStatsLabel := ui.NewLabel(d2resource.Font16, d2resource.PaletteUnits)
hpManaStatsLabel.Alignment = d2gui.HorizontalAlignLeft
// TODO make this depend on the hero type to respect inventory.txt
var inventoryRecordKey string
2020-07-26 14:52:54 -04:00
switch hero.Class {
case d2enum.HeroAssassin:
inventoryRecordKey = "Assassin"
case d2enum.HeroAmazon:
inventoryRecordKey = "Amazon2"
case d2enum.HeroBarbarian:
inventoryRecordKey = "Barbarian2"
case d2enum.HeroDruid:
inventoryRecordKey = "Druid"
case d2enum.HeroNecromancer:
inventoryRecordKey = "Necromancer2"
case d2enum.HeroPaladin:
inventoryRecordKey = "Paladin2"
case d2enum.HeroSorceress:
inventoryRecordKey = "Sorceress2"
default:
2020-09-12 16:25:09 -04:00
return nil, fmt.Errorf("unknown hero class: %d", hero.Class)
}
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
inventoryRecord := asset.Records.Layout.Inventory[inventoryRecordKey]
hoverLabel := nameLabel
hoverLabel.SetBackgroundColor(color.RGBA{0, 0, 0, uint8(128)})
globeStatsLabel := hpManaStatsLabel
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
heroState, err := d2hero.NewHeroStateFactory(asset)
if err != nil {
return nil, err
}
gc := &GameControls{
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
asset: asset,
ui: ui,
renderer: renderer,
hero: hero,
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
heroState: heroState,
mapEngine: mapEngine,
inputListener: inputListener,
mapRenderer: mapRenderer,
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
inventory: NewInventory(asset, ui, inventoryRecord),
heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats),
HelpOverlay: help.NewHelpOverlay(asset, renderer, ui, guiManager),
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
miniPanel: newMiniPanel(asset, ui, isSinglePlayer),
nameLabel: hoverLabel,
zoneChangeText: zoneLabel,
hpManaStatsLabel: globeStatsLabel,
actionableRegions: []ActionableRegion{
{leftSkill, d2geom.Rectangle{Left: 115, Top: 550, Width: 50, Height: 50}},
{newStats, d2geom.Rectangle{Left: 206, Top: 563, Width: 30, Height: 30}},
{xp, d2geom.Rectangle{Left: 253, Top: 560, Width: 125, Height: 5}},
{walkRun, d2geom.Rectangle{Left: 255, Top: 573, Width: 17, Height: 20}},
{stamina, d2geom.Rectangle{Left: 273, Top: 573, Width: 105, Height: 20}},
{miniPnl, d2geom.Rectangle{Left: 393, Top: 563, Width: 12, Height: 23}},
{newSkills, d2geom.Rectangle{Left: 562, Top: 563, Width: 30, Height: 30}},
{rightSkill, d2geom.Rectangle{Left: 634, Top: 550, Width: 50, Height: 50}},
{hpGlobe, d2geom.Rectangle{Left: 30, Top: 525, Width: 80, Height: 60}},
{manaGlobe, d2geom.Rectangle{Left: 695, Top: 525, Width: 80, Height: 60}},
{miniPanelCharacter, d2geom.Rectangle{Left: 324, Top: 528, Width: 22, Height: 26}},
{miniPanelInventory, d2geom.Rectangle{Left: 346, Top: 528, Width: 22, Height: 26}},
{miniPanelSkillTree, d2geom.Rectangle{Left: 368, Top: 528, Width: 22, Height: 26}},
{miniPanelAutomap, d2geom.Rectangle{Left: 390, Top: 528, Width: 22, Height: 26}},
{miniPanelMessageLog, d2geom.Rectangle{Left: 412, Top: 528, Width: 22, Height: 26}},
{miniPanelQuestLog, d2geom.Rectangle{Left: 434, Top: 528, Width: 22, Height: 26}},
{miniPanelGameMenu, d2geom.Rectangle{Left: 456, Top: 528, Width: 22, Height: 26}},
},
2020-08-11 18:01:33 -04:00
lastLeftBtnActionTime: 0,
lastRightBtnActionTime: 0,
isSinglePlayer: isSinglePlayer,
}
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 = term.BindAction("freecam", "toggle free camera movement", func() {
gc.FreeCam = !gc.FreeCam
})
2020-07-26 14:52:54 -04:00
if err != nil {
return nil, err
}
err = term.BindAction("setleftskill", "set skill to fire on left click", func(id int) {
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
skillRecord := gc.asset.Records.Skill.Details[id]
skill, err := heroState.CreateHeroSkill(0, skillRecord.Skill)
if err != nil {
term.OutputErrorf("cannot create skill with ID of %d", id)
}
gc.hero.LeftSkill = skill
})
err = term.BindAction("setrightskill", "set skill to fire on right click", func(id int) {
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
skillRecord := gc.asset.Records.Skill.Details[id]
skill, err := heroState.CreateHeroSkill(0, skillRecord.Skill)
if err != nil {
term.OutputErrorf("cannot create skill with ID of %d", id)
}
gc.hero.RightSkill = skill
})
if err != nil {
return nil, err
}
2020-07-26 14:52:54 -04:00
return gc, nil
}
2020-08-11 18:01:33 -04:00
// OnKeyRepeat is called to handle repeated key presses
func (g *GameControls) OnKeyRepeat(event d2interface.KeyEvent) bool {
if g.FreeCam {
var moveSpeed float64 = 8
if event.KeyMod() == d2enum.KeyModShift {
moveSpeed *= 2
}
if event.Key() == d2enum.KeyDown {
v := d2vector.NewVector(0, moveSpeed)
g.mapRenderer.MoveCameraTargetBy(v)
return true
}
if event.Key() == d2enum.KeyUp {
v := d2vector.NewVector(0, -moveSpeed)
g.mapRenderer.MoveCameraTargetBy(v)
return true
}
if event.Key() == d2enum.KeyRight {
v := d2vector.NewVector(moveSpeed, 0)
g.mapRenderer.MoveCameraTargetBy(v)
return true
}
if event.Key() == d2enum.KeyLeft {
v := d2vector.NewVector(-moveSpeed, 0)
g.mapRenderer.MoveCameraTargetBy(v)
return true
}
}
return false
}
2020-08-11 18:01:33 -04:00
// OnKeyDown handles key presses
func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool {
switch event.Key() {
case d2enum.KeyEscape:
if g.inventory.IsOpen() || g.heroStatsPanel.IsOpen() {
g.inventory.Close()
g.heroStatsPanel.Close()
g.updateLayout()
2020-08-11 18:01:33 -04:00
break
}
case d2enum.KeyI:
g.inventory.Toggle()
g.updateLayout()
case d2enum.KeyC:
g.heroStatsPanel.Toggle()
g.updateLayout()
case d2enum.KeyR:
g.onToggleRunButton()
case d2enum.KeyH:
g.HelpOverlay.Toggle()
g.updateLayout()
default:
return false
}
2020-08-11 18:01:33 -04:00
return false
}
2020-08-11 18:01:33 -04:00
// OnMouseButtonRepeat handles repeated mouse clicks
func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool {
px, py := g.mapRenderer.ScreenToWorld(event.X(), event.Y())
px = float64(int(px*10)) / 10.0
py = float64(int(py*10)) / 10.0
now := d2util.Now()
button := event.Button()
isLeft := button == d2enum.MouseButtonLeft
isRight := button == d2enum.MouseButtonRight
2020-08-11 18:01:33 -04:00
lastLeft := now - g.lastLeftBtnActionTime
lastRight := now - g.lastRightBtnActionTime
inRect := !g.isInActiveMenusRect(event.X(), event.Y())
shouldDoLeft := lastLeft >= mouseBtnActionsTreshhold
shouldDoRight := lastRight >= mouseBtnActionsTreshhold
if isLeft && shouldDoLeft && inRect && !g.hero.IsCasting() {
2020-08-11 18:01:33 -04:00
g.lastLeftBtnActionTime = now
2020-07-26 14:52:54 -04:00
if event.KeyMod() == d2enum.KeyModShift {
g.inputListener.OnPlayerCast(g.hero.LeftSkill.ID, px, py)
} else {
g.inputListener.OnPlayerMove(px, py)
}
if g.FreeCam {
if event.Button() == d2enum.MouseButtonLeft {
camVect := g.mapRenderer.Camera.GetPosition().Vector
x, y := float64(g.lastMouseX-400)/5, float64(g.lastMouseY-300)/5
targetPosition := d2vector.NewPositionTile(x, y)
targetPosition.Add(&camVect)
g.mapRenderer.SetCameraTarget(&targetPosition)
return true
}
}
return true
}
if isRight && shouldDoRight && inRect && !g.hero.IsCasting() {
2020-08-11 18:01:33 -04:00
g.lastRightBtnActionTime = now
2020-07-26 14:52:54 -04:00
g.inputListener.OnPlayerCast(g.hero.RightSkill.ID, px, py)
2020-07-26 14:52:54 -04:00
return true
}
return true
}
2020-08-11 18:01:33 -04:00
// OnMouseMove handles mouse movement events
func (g *GameControls) OnMouseMove(event d2interface.MouseMoveEvent) bool {
mx, my := event.X(), event.Y()
g.lastMouseX = mx
g.lastMouseY = my
g.inventory.lastMouseX = mx
g.inventory.lastMouseY = my
for i := range g.actionableRegions {
// Mouse over a game control element
if g.actionableRegions[i].Rect.IsInRect(mx, my) {
2020-09-12 16:25:09 -04:00
g.onHoverActionable(g.actionableRegions[i].ActionableTypeID)
}
}
return false
}
2020-08-11 18:01:33 -04:00
// OnMouseButtonDown handles mouse button presses
func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
mx, my := event.X(), event.Y()
2020-07-26 14:52:54 -04:00
for i := range g.actionableRegions {
// If click is on a game control element
if g.actionableRegions[i].Rect.IsInRect(mx, my) {
2020-09-12 16:25:09 -04:00
g.onClickActionable(g.actionableRegions[i].ActionableTypeID)
return false
}
}
px, py := g.mapRenderer.ScreenToWorld(mx, my)
px = float64(int(px*10)) / 10.0
py = float64(int(py*10)) / 10.0
if event.Button() == d2enum.MouseButtonLeft && !g.isInActiveMenusRect(mx, my) && !g.hero.IsCasting() {
g.lastLeftBtnActionTime = d2util.Now()
2020-07-26 14:52:54 -04:00
if event.KeyMod() == d2enum.KeyModShift {
g.inputListener.OnPlayerCast(g.hero.LeftSkill.ID, px, py)
} else {
g.inputListener.OnPlayerMove(px, py)
}
2020-07-26 14:52:54 -04:00
return true
}
if event.Button() == d2enum.MouseButtonRight && !g.isInActiveMenusRect(mx, my) && !g.hero.IsCasting() {
g.lastRightBtnActionTime = d2util.Now()
2020-07-26 14:52:54 -04:00
g.inputListener.OnPlayerCast(g.hero.RightSkill.ID, px, py)
2020-07-26 14:52:54 -04:00
return true
}
return false
}
// Load the resources required for the GameControls
func (g *GameControls) Load() {
var err error
g.globeSprite, err = g.ui.NewSprite(d2resource.GameGlobeOverlap, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
g.hpManaStatusSprite, err = g.ui.NewSprite(d2resource.HealthManaIndicator, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
g.mainPanel, err = g.ui.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
g.menuButton, err = g.ui.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
err = g.menuButton.SetCurrentFrame(2)
if err != nil {
log.Print(err)
}
// TODO: temporarily hardcoded to Attack, should come from saved state for hero
genericSkillsSprite, err := g.ui.NewSprite(d2resource.GenericSkills, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
attackIconID := 2
g.leftSkill = &SkillResource{SkillIcon: genericSkillsSprite, IconNumber: attackIconID, SkillResourcePath: d2resource.GenericSkills}
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
g.rightSkill = &SkillResource{SkillIcon: genericSkillsSprite, IconNumber: attackIconID, SkillResourcePath: d2resource.GenericSkills}
g.loadUIButtons()
g.inventory.Load()
g.heroStatsPanel.Load()
g.HelpOverlay.Load()
}
func (g *GameControls) loadUIButtons() {
// Run button
g.runButton = g.ui.NewButton(d2ui.ButtonTypeRun, "")
2020-07-26 14:52:54 -04:00
g.runButton.SetPosition(255, 570)
g.runButton.OnActivated(func() { g.onToggleRunButton() })
2020-07-26 14:52:54 -04:00
if g.hero.IsRunToggled() {
g.runButton.Toggle()
}
}
func (g *GameControls) onToggleRunButton() {
g.runButton.Toggle()
g.hero.ToggleRunWalk()
// TODO: change the running menu icon
g.hero.SetIsRunning(g.hero.IsRunToggled())
}
2020-08-11 18:01:33 -04:00
// Advance advances the state of the GameControls
func (g *GameControls) Advance(elapsed float64) error {
g.mapRenderer.Advance(elapsed)
return nil
}
func (g *GameControls) updateLayout() {
isRightPanelOpen := g.isLeftPanelOpen()
isLeftPanelOpen := g.isRightPanelOpen()
2020-08-11 18:01:33 -04:00
switch {
case isRightPanelOpen == isLeftPanelOpen:
g.mapRenderer.ViewportDefault()
2020-08-11 18:01:33 -04:00
case isRightPanelOpen:
g.mapRenderer.ViewportToLeft()
2020-08-11 18:01:33 -04:00
default:
g.mapRenderer.ViewportToRight()
}
}
func (g *GameControls) isLeftPanelOpen() bool {
// TODO: add quest log panel
return g.heroStatsPanel.IsOpen()
}
func (g *GameControls) isRightPanelOpen() bool {
// TODO: add skills tree panel
return g.inventory.IsOpen()
}
2020-08-11 18:01:33 -04:00
func (g *GameControls) isInActiveMenusRect(px, py int) bool {
var bottomMenuRect = d2geom.Rectangle{Left: 0, Top: 550, Width: 800, Height: 50}
2020-07-26 14:52:54 -04:00
var leftMenuRect = d2geom.Rectangle{Left: 0, Top: 0, Width: 400, Height: 600}
2020-07-26 14:52:54 -04:00
var rightMenuRect = d2geom.Rectangle{Left: 400, Top: 0, Width: 400, Height: 600}
2020-07-26 14:52:54 -04:00
if bottomMenuRect.IsInRect(px, py) {
return true
}
if g.isLeftPanelOpen() && leftMenuRect.IsInRect(px, py) {
return true
}
if g.isRightPanelOpen() && rightMenuRect.IsInRect(px, py) {
return true
}
if g.miniPanel.IsOpen() && g.miniPanel.isInRect(px, py) {
return true
}
if g.HelpOverlay.IsOpen() && g.HelpOverlay.IsInRect(px, py) {
return true
}
return false
}
// TODO: consider caching the panels to single image that is reused.
2020-08-11 18:01:33 -04:00
// Render draws the GameControls onto the target
2020-07-26 14:52:54 -04:00
func (g *GameControls) Render(target d2interface.Surface) error {
mx, my := g.lastMouseX, g.lastMouseY
2020-09-12 16:25:09 -04:00
for entityIdx := range g.mapEngine.Entities() {
entity := (g.mapEngine.Entities())[entityIdx]
if !entity.Selectable() {
continue
}
entPos := entity.GetPosition()
entOffset := entPos.RenderOffset()
entScreenXf, entScreenYf := g.mapRenderer.WorldToScreenF(entity.GetPositionF())
entScreenX := int(math.Floor(entScreenXf))
entScreenY := int(math.Floor(entScreenYf))
entityWidth, entityHeight := entity.GetSize()
halfWidth, halfHeight := entityWidth/2, entityHeight/2
l, r := entScreenX-halfWidth-hoverLabelOuterPad, entScreenX+halfWidth+hoverLabelOuterPad
t, b := entScreenY-halfHeight-hoverLabelOuterPad, entScreenY+halfHeight-hoverLabelOuterPad
xWithin := (l <= mx) && (r >= mx)
yWithin := (t <= my) && (b >= my)
within := xWithin && yWithin
if within {
xOff, yOff := int(entOffset.X()), int(entOffset.Y())
g.nameLabel.SetText(entity.Label())
xLabel, yLabel := entScreenX-xOff, entScreenY-yOff-entityHeight-hoverLabelOuterPad
g.nameLabel.SetPosition(xLabel, yLabel)
g.nameLabel.Render(target)
entity.Highlight()
2020-07-26 14:52:54 -04:00
break
}
}
2020-08-11 18:01:33 -04:00
if err := g.heroStatsPanel.Render(target); err != nil {
return err
}
if err := g.inventory.Render(target); err != nil {
return err
}
width, height := target.GetSize()
offset := 0
// Left globe holder
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(0); err != nil {
return err
}
w, _ := g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.Render(target); err != nil {
return err
}
// Health status bar
healthPercent := float64(g.hero.Stats.Health) / float64(g.hero.Stats.MaxHealth)
hpBarHeight := int(healthPercent * float64(globeHeight))
2020-07-26 14:52:54 -04:00
if err := g.hpManaStatusSprite.SetCurrentFrame(0); err != nil {
return err
}
g.hpManaStatusSprite.SetPosition(offset+30, height-13)
2020-07-26 14:52:54 -04:00
if err := g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight)); err != nil {
return err
}
// Left globe
2020-07-26 14:52:54 -04:00
if err := g.globeSprite.SetCurrentFrame(0); err != nil {
return err
}
g.globeSprite.SetPosition(offset+28, height-5)
2020-07-26 14:52:54 -04:00
if err := g.globeSprite.Render(target); err != nil {
return err
}
offset += w
// Left skill
skillResourcePath := g.getSkillResourceByClass(g.hero.LeftSkill.Charclass)
if skillResourcePath != g.leftSkill.SkillResourcePath {
g.leftSkill.SkillIcon, _ = g.ui.NewSprite(skillResourcePath, d2resource.PaletteSky)
}
if err := g.leftSkill.SkillIcon.SetCurrentFrame(g.hero.LeftSkill.IconCel); err != nil {
2020-07-26 14:52:54 -04:00
return err
}
w, _ = g.leftSkill.SkillIcon.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.leftSkill.SkillIcon.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.leftSkill.SkillIcon.Render(target); err != nil {
2020-07-26 14:52:54 -04:00
return err
}
offset += w
// New Stats Selector
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(1); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.Render(target); err != nil {
return err
}
offset += w
// Stamina
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(2); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.Render(target); err != nil {
return err
}
offset += w
// Stamina status bar
target.PushTranslation(273, 572)
target.PushEffect(d2enum.DrawEffectModulate)
2020-07-26 14:52:54 -04:00
staminaPercent := float64(g.hero.Stats.Stamina) / float64(g.hero.Stats.MaxStamina)
2020-07-26 14:52:54 -04:00
target.DrawRect(int(staminaPercent*staminaBarWidth), 19, color.RGBA{R: 175, G: 136, B: 72, A: 200})
target.PopN(2)
// Experience status bar
target.PushTranslation(256, 561)
2020-07-26 14:52:54 -04:00
expPercent := float64(g.hero.Stats.Experience) / float64(g.hero.Stats.NextLevelExp)
2020-07-26 14:52:54 -04:00
target.DrawRect(int(expPercent*expBarWidth), 2, color.RGBA{R: 255, G: 255, B: 255, A: 255})
target.Pop()
// Center menu button
menuButtonFrameIndex := 0
if g.miniPanel.isOpen {
menuButtonFrameIndex = 2
}
if err := g.menuButton.SetCurrentFrame(menuButtonFrameIndex); err != nil {
2020-07-26 14:52:54 -04:00
return err
}
2020-09-12 16:25:09 -04:00
g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.menuButton.SetPosition((width/2)-8, height-16)
2020-07-26 14:52:54 -04:00
if err := g.menuButton.Render(target); err != nil {
return err
}
if err := g.miniPanel.Render(target); err != nil {
return err
}
// Potions
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(3); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.Render(target); err != nil {
return err
}
offset += w
// New Skills Selector
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(4); err != nil {
return err
}
w, _ = g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.Render(target); err != nil {
return err
}
offset += w
// Right skill
skillResourcePath = g.getSkillResourceByClass(g.hero.RightSkill.Charclass)
if skillResourcePath != g.rightSkill.SkillResourcePath {
g.rightSkill.SkillIcon, _ = g.ui.NewSprite(skillResourcePath, d2resource.PaletteSky)
}
if err := g.rightSkill.SkillIcon.SetCurrentFrame(g.hero.RightSkill.IconCel); err != nil {
2020-07-26 14:52:54 -04:00
return err
}
w, _ = g.rightSkill.SkillIcon.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.rightSkill.SkillIcon.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
if err := g.rightSkill.SkillIcon.Render(target); err != nil {
2020-07-26 14:52:54 -04:00
return err
}
offset += w
// Right globe holder
2020-07-26 14:52:54 -04:00
if err := g.mainPanel.SetCurrentFrame(5); err != nil {
return err
}
2020-09-12 16:25:09 -04:00
g.mainPanel.GetCurrentFrameSize()
2020-07-26 14:52:54 -04:00
g.mainPanel.SetPosition(offset, height)
2020-07-26 14:52:54 -04:00
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))
2020-07-26 14:52:54 -04:00
if err := g.hpManaStatusSprite.SetCurrentFrame(1); err != nil {
return err
}
g.hpManaStatusSprite.SetPosition(offset+7, height-12)
2020-07-26 14:52:54 -04:00
if err := g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)); err != nil {
return err
}
// Right globe
2020-07-26 14:52:54 -04:00
if err := g.globeSprite.SetCurrentFrame(1); err != nil {
return err
}
g.globeSprite.SetPosition(offset+8, height-8)
2020-07-26 14:52:54 -04:00
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(width/2, height/4)
g.zoneChangeText.Render(target)
}
// Create and format Health string from string lookup table.
fmtHealth := d2tbl.TranslateString("panelhealth")
healthCurr, healthMax := int(g.hero.Stats.Health), int(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(15, 487)
g.hpManaStatsLabel.Render(target)
}
// Create and format Mana string from string lookup table.
fmtMana := d2tbl.TranslateString("panelmana")
manaCurr, manaMax := int(g.hero.Stats.Mana), int(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 := 785 - widthManaLabel
g.hpManaStatsLabel.SetPosition(xManaLabel, 487)
g.hpManaStatsLabel.Render(target)
}
if err := g.HelpOverlay.Render(target); err != nil {
return err
}
// Minipanel is closed and minipanel button is hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPnl].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("panelcmini")) //"Close Mini Panel"
g.nameLabel.SetPosition(399, 544)
g.nameLabel.Render(target)
}
// Minipanel is open and minipanel button is hovered.
if !g.miniPanel.IsOpen() && g.actionableRegions[miniPnl].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("panelmini")) //"Open Mini Panel"
g.nameLabel.SetPosition(399, 544)
g.nameLabel.Render(target)
}
// Display character tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelCharacter].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelchar")) //"Character" no hotkey
g.nameLabel.SetPosition(340, 510)
g.nameLabel.Render(target)
}
// Display inventory tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelInventory].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelinv")) //"Inventory" no hotkey
g.nameLabel.SetPosition(360, 510)
g.nameLabel.Render(target)
}
// Display skill tree tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelSkillTree].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipaneltree")) //"Skill Treee" no hotkey
g.nameLabel.SetPosition(380, 510)
g.nameLabel.Render(target)
}
// Display automap tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelAutomap].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelautomap")) //"Automap" no hotkey
g.nameLabel.SetPosition(400, 510)
g.nameLabel.Render(target)
}
// Display message log tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelMessageLog].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelmessage")) //"Message Log" no hotkey
g.nameLabel.SetPosition(420, 510)
g.nameLabel.Render(target)
}
// Display quest log tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelQuestLog].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelquest")) //"Quest Log" no hotkey
g.nameLabel.SetPosition(440, 510)
g.nameLabel.Render(target)
}
// Display game menu tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[miniPanelGameMenu].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(d2tbl.TranslateString("minipanelmenubtn")) //"Game Menu (Esc)" // the (Esc) is hardcoded in.
g.nameLabel.SetPosition(460, 510)
g.nameLabel.Render(target)
}
// Create and format Stamina string from string lookup table.
fmtStamina := d2tbl.TranslateString("panelstamina")
staminaCurr, staminaMax := int(g.hero.Stats.Stamina), int(g.hero.Stats.MaxStamina)
strPanelStamina := fmt.Sprintf(fmtStamina, staminaCurr, staminaMax)
// Display stamina tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[stamina].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(strPanelStamina)
g.nameLabel.SetPosition(320, 535)
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) && !g.hero.IsRunToggled() {
g.nameLabel.SetText(d2tbl.TranslateString("RunOn")) //"Run" no hotkeys
g.nameLabel.SetPosition(263, 563)
g.nameLabel.Render(target)
}
if g.actionableRegions[walkRun].Rect.IsInRect(mx, my) && g.hero.IsRunToggled() {
g.nameLabel.SetText(d2tbl.TranslateString("RunOff")) //"Walk" no hotkeys
g.nameLabel.SetPosition(263, 563)
g.nameLabel.Render(target)
}
// 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)
// Display experience tooltip when hovered.
if g.miniPanel.IsOpen() && g.actionableRegions[xp].Rect.IsInRect(mx, my) {
g.nameLabel.SetText(strPanelExp)
g.nameLabel.SetPosition(255, 535)
g.nameLabel.Render(target)
}
2020-07-26 14:52:54 -04:00
return nil
}
2020-08-11 18:01:33 -04:00
// SetZoneChangeText sets the zoneChangeText
func (g *GameControls) SetZoneChangeText(text string) {
g.zoneChangeText.SetText(text)
}
2020-08-11 18:01:33 -04:00
// ShowZoneChangeText shows the zoneChangeText
func (g *GameControls) ShowZoneChangeText() {
g.isZoneTextShown = true
}
2020-08-11 18:01:33 -04:00
// HideZoneChangeTextAfter hides the zoneChangeText after the given amount of seconds
func (g *GameControls) HideZoneChangeTextAfter(delay float64) {
time.AfterFunc(time.Duration(delay)*time.Second, func() {
g.isZoneTextShown = false
})
}
// HpStatsIsVisible returns true if the hp and mana stats are visible to the player
func (g *GameControls) HpStatsIsVisible() bool {
return g.hpStatsIsVisible
}
// ManaStatsIsVisible returns true if the hp and mana stats are visible to the player
func (g *GameControls) ManaStatsIsVisible() bool {
return g.manaStatsIsVisible
}
// ToggleHpStats toggles the visibility of the hp and mana stats placed above their respective globe and load only if they do not match
func (g *GameControls) ToggleHpStats() {
g.hpStatsIsVisible = !g.hpStatsIsVisible
}
// ToggleManaStats toggles the visibility of the hp and mana stats placed above their respective globe
func (g *GameControls) ToggleManaStats() {
g.manaStatsIsVisible = !g.manaStatsIsVisible
}
// Handles what to do when an actionable is hovered
func (g *GameControls) onHoverActionable(item ActionableType) {
switch item {
case leftSkill:
return
case newStats:
return
case xp:
return
case walkRun:
return
case stamina:
return
case miniPnl:
return
case newSkills:
return
case rightSkill:
return
case hpGlobe:
return
case manaGlobe:
return
case miniPanelCharacter:
return
case miniPanelInventory:
return
case miniPanelSkillTree:
return
case miniPanelAutomap:
return
case miniPanelMessageLog:
return
case miniPanelQuestLog:
return
case miniPanelGameMenu:
return
default:
log.Printf("Unrecognized ActionableType(%d) being hovered\n", item)
}
}
// Handles what to do when an actionable is clicked
func (g *GameControls) onClickActionable(item ActionableType) {
switch item {
case leftSkill:
log.Println("Left Skill Action Pressed")
case newStats:
log.Println("New Stats Selector Action Pressed")
case xp:
log.Println("XP Action Pressed")
case walkRun:
log.Println("Walk/Run Action Pressed")
case stamina:
log.Println("Stamina Action Pressed")
case miniPnl:
log.Println("Mini Panel Action Pressed")
g.miniPanel.Toggle()
case newSkills:
log.Println("New Skills Selector Action Pressed")
case rightSkill:
log.Println("Right Skill Action Pressed")
case hpGlobe:
g.ToggleHpStats()
log.Println("HP Globe Pressed")
case manaGlobe:
g.ToggleManaStats()
log.Println("Mana Globe Pressed")
case miniPanelCharacter:
log.Println("Character button on mini panel is pressed")
g.heroStatsPanel.Toggle()
g.updateLayout()
case miniPanelInventory:
log.Println("Inventory button on mini panel is pressed")
g.inventory.Toggle()
g.updateLayout()
default:
log.Printf("Unrecognized ActionableType(%d) being clicked\n", item)
}
}
func (g *GameControls) getSkillResourceByClass(class string) string {
resource := ""
switch class {
case "":
resource = d2resource.GenericSkills
case "bar":
resource = d2resource.BarbarianSkills
case "nec":
resource = d2resource.NecromancerSkills
case "pal":
resource = d2resource.PaladinSkills
case "ass":
resource = d2resource.AssassinSkills
case "sor":
resource = d2resource.SorcererSkills
case "ama":
resource = d2resource.AmazonSkills
case "dru":
resource = d2resource.DruidSkills
default:
log.Fatalf("Unknown class token: '%s'", class)
}
return resource
}