From 858fa180687868d66214d201b458737bca8e8d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20Kaymak?= Date: Wed, 12 Aug 2020 01:01:33 +0300 Subject: [PATCH] lint fixes (#703) --- d2common/d2fileformats/d2dcc/dcc_direction.go | 68 +++++----- d2core/d2asset/animation.go | 8 +- d2core/d2asset/composite.go | 3 +- d2core/d2asset/dcc_animation.go | 2 +- d2core/d2asset/font.go | 2 +- d2game/d2gamescreen/select_hero_class.go | 1 - d2game/d2player/game_controls.go | 119 +++++++++++------- d2game/d2player/hero_stats_panel.go | 16 ++- d2game/d2player/inventory.go | 49 +++++--- d2game/d2player/player_state.go | 54 +++++--- main.go | 1 + 11 files changed, 193 insertions(+), 130 deletions(-) diff --git a/d2common/d2fileformats/d2dcc/dcc_direction.go b/d2common/d2fileformats/d2dcc/dcc_direction.go index 292dadb8..0004356d 100644 --- a/d2common/d2fileformats/d2dcc/dcc_direction.go +++ b/d2common/d2fileformats/d2dcc/dcc_direction.go @@ -1,10 +1,10 @@ package d2dcc import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "log" "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) const cellsPerRow = 4 @@ -35,21 +35,22 @@ type DCCDirection struct { } // CreateDCCDirection creates an instance of a DCCDirection. -func CreateDCCDirection(bm *d2common.BitMuncher, - file *DCC) *DCCDirection { //nolint:funlen // Can't reduce +func CreateDCCDirection(bm *d2common.BitMuncher, file *DCC) *DCCDirection { var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32} - result := &DCCDirection{} - result.OutSizeCoded = int(bm.GetUInt32()) - result.CompressionFlags = int(bm.GetBits(2)) //nolint:gomnd // binary data - result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.WidthBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.HeightBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.XOffsetBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.YOffsetBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.OptionalDataBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.CodedBytesBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data - result.Frames = make([]*DCCDirectionFrame, file.FramesPerDirection) + result := &DCCDirection{ + OutSizeCoded: int(bm.GetUInt32()), + CompressionFlags: int(bm.GetBits(2)), //nolint:gomnd // binary data + Variable0Bits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + WidthBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + HeightBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + XOffsetBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + YOffsetBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + OptionalDataBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + CodedBytesBits: int(crazyBitTable[bm.GetBits(4)]), //nolint:gomnd // binary data + Frames: make([]*DCCDirectionFrame, file.FramesPerDirection), + } + minx := 100000 miny := 100000 maxx := -100000 @@ -81,10 +82,7 @@ func CreateDCCDirection(bm *d2common.BitMuncher, result.RawPixelCodesBitstreamSize = int(bm.GetBits(20)) //nolint:gomnd // binary data } - // PixelValuesKey - paletteEntryCount := 0 - - for i := 0; i < 256; i++ { + for paletteEntryCount, i := 0, 0; i < 256; i++ { valid := bm.GetBit() != 0 if valid { result.PaletteEntries[paletteEntryCount] = byte(i) @@ -132,27 +130,31 @@ func CreateDCCDirection(bm *d2common.BitMuncher, result.PixelBuffer = nil // Verify that everything we expected to read was actually read (sanity check)... - if equalCellsBitstream.BitsRead() != result.EqualCellsBitstreamSize { - log.Panic("Did not read the correct number of bits!") - } - - if pixelMaskBitstream.BitsRead() != result.PixelMaskBitstreamSize { - log.Panic("Did not read the correct number of bits!") - } - - if encodingTypeBitsream.BitsRead() != result.EncodingTypeBitsreamSize { - log.Panic("Did not read the correct number of bits!") - } - - if rawPixelCodesBitstream.BitsRead() != result.RawPixelCodesBitstreamSize { - log.Panic("Did not read the correct number of bits!") - } + result.verify(equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream) bm.SkipBits(pixelCodeandDisplacement.BitsRead()) return result } +func (v *DCCDirection) verify(equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream *d2common.BitMuncher) { + if equalCellsBitstream.BitsRead() != v.EqualCellsBitstreamSize { + log.Panic("Did not read the correct number of bits!") + } + + if pixelMaskBitstream.BitsRead() != v.PixelMaskBitstreamSize { + log.Panic("Did not read the correct number of bits!") + } + + if encodingTypeBitsream.BitsRead() != v.EncodingTypeBitsreamSize { + log.Panic("Did not read the correct number of bits!") + } + + if rawPixelCodesBitstream.BitsRead() != v.RawPixelCodesBitstreamSize { + log.Panic("Did not read the correct number of bits!") + } +} + //nolint:gocognit nolint:gocyclo // Can't reduce func (v *DCCDirection) generateFrames(pcd *d2common.BitMuncher) { pbIdx := 0 diff --git a/d2core/d2asset/animation.go b/d2core/d2asset/animation.go index 60f67932..974e69f6 100644 --- a/d2core/d2asset/animation.go +++ b/d2core/d2asset/animation.go @@ -2,12 +2,12 @@ package d2asset import ( "errors" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "image" "image/color" "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" d2iface "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -114,9 +114,9 @@ func (a *animation) Advance(elapsed float64) error { } func (a *animation) renderShadow(target d2iface.Surface) error { - //_, height := a.GetFrameBounds() direction := a.directions[a.directionIndex] frame := direction.frames[a.frameIndex] + target.PushFilter(d2enum.FilterLinear) target.PushTranslation( frame.offsetX, @@ -161,8 +161,10 @@ func (a *animation) RenderFromOrigin(target d2iface.Surface, shadow bool) error if shadow { _, height := a.GetFrameBounds() height = int(math.Abs(float64(height))) - target.PushTranslation((-height / 2), 0) + + target.PushTranslation(-height/2, 0) defer target.Pop() + return a.renderShadow(target) } diff --git a/d2core/d2asset/composite.go b/d2core/d2asset/composite.go index fc952336..63299808 100644 --- a/d2core/d2asset/composite.go +++ b/d2core/d2asset/composite.go @@ -24,7 +24,7 @@ type Composite struct { } type size struct { - Width int + Width int Height int } @@ -306,6 +306,7 @@ func (c *Composite) loadCompositeLayer(layerKey, layerValue, animationMode, weap return nil, errors.New("animation not found") } +// GetSize returns the size of the composite func (c *Composite) GetSize() (w, h int) { c.updateSize() return c.size.Width, c.size.Height diff --git a/d2core/d2asset/dcc_animation.go b/d2core/d2asset/dcc_animation.go index 7e36ab26..457b737f 100644 --- a/d2core/d2asset/dcc_animation.go +++ b/d2core/d2asset/dcc_animation.go @@ -2,10 +2,10 @@ package d2asset import ( "errors" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc" d2iface "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" diff --git a/d2core/d2asset/font.go b/d2core/d2asset/font.go index 2b5f1b7d..7683828c 100644 --- a/d2core/d2asset/font.go +++ b/d2core/d2asset/font.go @@ -3,11 +3,11 @@ package d2asset import ( "encoding/binary" "errors" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" "image/color" "strings" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" ) var _ d2interface.Font = &Font{} // Static check to confirm struct conforms to interface diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index 3f1f24b4..11867194 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -469,7 +469,6 @@ func (v *SelectHeroClass) onOkButtonClicked() { v.heroNameTextbox.GetText(), v.selectedHero, d2datadict.CharStats[v.selectedHero], - v.hardcoreCheckbox.GetCheckState(), ) v.navigator.ToCreateGame(gameState.FilePath, d2clientconnectiontype.Local, v.connectionHost) } diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index 4d149fdb..fe271b97 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -32,40 +32,41 @@ type Panel interface { } const ( - initialMissileID = 59 - expBarWidth = 120.0 - staminaBarWidth = 102.0 - globeHeight = 80 - globeWidth = 80 - hoverLabelOuterPad = 5 + initialMissileID = 59 + expBarWidth = 120.0 + staminaBarWidth = 102.0 + globeHeight = 80 + globeWidth = 80 + hoverLabelOuterPad = 5 + mouseBtnActionsTreshhold = 0.25 ) // GameControls represents the game's controls on the screen type GameControls struct { - renderer d2interface.Renderer // TODO: This shouldn't be a dependency - hero *d2mapentity.Player - mapEngine *d2mapengine.MapEngine - mapRenderer *d2maprenderer.MapRenderer - uiManager *d2ui.UIManager - inventory *Inventory - heroStatsPanel *HeroStatsPanel - inputListener InputCallbackListener - FreeCam bool - lastMouseX int - lastMouseY int - missileID int // ID of missile to create when user right clicks. - - // UI - globeSprite *d2ui.Sprite - hpManaStatusSprite *d2ui.Sprite - mainPanel *d2ui.Sprite - menuButton *d2ui.Sprite - skillIcon *d2ui.Sprite - zoneChangeText *d2ui.Label - nameLabel *d2ui.Label - runButton *d2ui.Button - isZoneTextShown bool - actionableRegions []ActionableRegion + actionableRegions []ActionableRegion + renderer d2interface.Renderer // TODO: This shouldn't be a dependency + inputListener InputCallbackListener + hero *d2mapentity.Player + mapEngine *d2mapengine.MapEngine + mapRenderer *d2maprenderer.MapRenderer + uiManager *d2ui.UIManager + inventory *Inventory + heroStatsPanel *HeroStatsPanel + lastMouseX int + lastMouseY int + missileID int + globeSprite *d2ui.Sprite + hpManaStatusSprite *d2ui.Sprite + mainPanel *d2ui.Sprite + menuButton *d2ui.Sprite + skillIcon *d2ui.Sprite + zoneChangeText *d2ui.Label + nameLabel *d2ui.Label + runButton *d2ui.Button + lastLeftBtnActionTime float64 + lastRightBtnActionTime float64 + FreeCam bool + isZoneTextShown bool } type ActionableType int @@ -87,6 +88,7 @@ const ( rightSkill = ActionableType(iota) ) +// NewGameControls creates a GameControls instance and returns a pointer to it func NewGameControls( renderer d2interface.Renderer, hero *d2mapentity.Player, @@ -97,9 +99,13 @@ func NewGameControls( ui *d2ui.UIManager, ) (*GameControls, error) { missileID := initialMissileID - term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { + + err := term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { missileID = id }) + if err != nil { + return nil, err + } zoneLabel := ui.NewLabel(d2resource.Font30, d2resource.PaletteUnits) zoneLabel.Alignment = d2gui.HorizontalAlignCenter @@ -157,9 +163,11 @@ func NewGameControls( {rightSelec, d2common.Rectangle{Left: 562, Top: 563, Width: 30, Height: 30}}, {rightSkill, d2common.Rectangle{Left: 634, Top: 550, Width: 50, Height: 50}}, }, + lastLeftBtnActionTime: 0, + lastRightBtnActionTime: 0, } - err := term.BindAction("freecam", "toggle free camera movement", func() { + err = term.BindAction("freecam", "toggle free camera movement", func() { gc.FreeCam = !gc.FreeCam }) @@ -170,6 +178,7 @@ func NewGameControls( return gc, nil } +// OnKeyRepeat is called to handle repeated key presses func (g *GameControls) OnKeyRepeat(event d2interface.KeyEvent) bool { if g.FreeCam { var moveSpeed float64 = 8 @@ -209,6 +218,7 @@ func (g *GameControls) OnKeyRepeat(event d2interface.KeyEvent) bool { return false } +// OnKeyDown handles key presses func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool { switch event.Key() { case d2enum.KeyEscape: @@ -216,6 +226,7 @@ func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool { g.inventory.Close() g.heroStatsPanel.Close() g.updateLayout() + break } case d2enum.KeyI: @@ -229,13 +240,11 @@ func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool { default: return false } + return false } -var lastLeftBtnActionTime float64 = 0 -var lastRightBtnActionTime float64 = 0 -var mouseBtnActionsTreshhold = 0.25 - +// 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 @@ -245,14 +254,14 @@ func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool { button := event.Button() isLeft := button == d2enum.MouseButtonLeft isRight := button == d2enum.MouseButtonRight - lastLeft := now - lastLeftBtnActionTime - lastRight := now - lastRightBtnActionTime + 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 { - lastLeftBtnActionTime = now + g.lastLeftBtnActionTime = now g.inputListener.OnPlayerMove(px, py) @@ -274,7 +283,7 @@ func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool { } if isRight && shouldDoRight && inRect { - lastRightBtnActionTime = now + g.lastRightBtnActionTime = now g.inputListener.OnPlayerCast(g.missileID, px, py) @@ -284,6 +293,7 @@ func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool { return true } +// OnMouseMove handles mouse movement events func (g *GameControls) OnMouseMove(event d2interface.MouseMoveEvent) bool { mx, my := event.X(), event.Y() g.lastMouseX = mx @@ -301,6 +311,7 @@ func (g *GameControls) OnMouseMove(event d2interface.MouseMoveEvent) bool { return false } +// OnMouseButtonDown handles mouse button presses func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { mx, my := event.X(), event.Y() @@ -317,7 +328,7 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { py = float64(int(py*10)) / 10.0 if event.Button() == d2enum.MouseButtonLeft && !g.isInActiveMenusRect(mx, my) { - lastLeftBtnActionTime = d2common.Now() + g.lastLeftBtnActionTime = d2common.Now() g.inputListener.OnPlayerMove(px, py) @@ -325,7 +336,7 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { } if event.Button() == d2enum.MouseButtonRight && !g.isInActiveMenusRect(mx, my) { - lastRightBtnActionTime = d2common.Now() + g.lastRightBtnActionTime = d2common.Now() g.inputListener.OnPlayerCast(g.missileID, px, py) @@ -335,6 +346,7 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool { return false } +// Load loads the resources required for the GameControls func (g *GameControls) Load() { animation, _ := d2asset.LoadAnimation(d2resource.GameGlobeOverlap, d2resource.PaletteSky) g.globeSprite, _ = g.uiManager.NewSprite(animation) @@ -376,6 +388,7 @@ func (g *GameControls) onToggleRunButton() { g.hero.SetIsRunning(g.hero.IsRunToggled()) } +// Advance advances the state of the GameControls func (g *GameControls) Advance(elapsed float64) error { g.mapRenderer.Advance(elapsed) return nil @@ -385,11 +398,12 @@ func (g *GameControls) updateLayout() { isRightPanelOpen := g.isLeftPanelOpen() isLeftPanelOpen := g.isRightPanelOpen() - if isRightPanelOpen == isLeftPanelOpen { + switch { + case isRightPanelOpen == isLeftPanelOpen: g.mapRenderer.ViewportDefault() - } else if isRightPanelOpen { + case isRightPanelOpen: g.mapRenderer.ViewportToLeft() - } else { + default: g.mapRenderer.ViewportToRight() } } @@ -404,7 +418,7 @@ func (g *GameControls) isRightPanelOpen() bool { return g.inventory.IsOpen() } -func (g *GameControls) isInActiveMenusRect(px int, py int) bool { +func (g *GameControls) isInActiveMenusRect(px, py int) bool { var bottomMenuRect = d2common.Rectangle{Left: 0, Top: 550, Width: 800, Height: 50} var leftMenuRect = d2common.Rectangle{Left: 0, Top: 0, Width: 400, Height: 600} @@ -427,6 +441,7 @@ func (g *GameControls) isInActiveMenusRect(px int, py int) bool { } // TODO: consider caching the panels to single image that is reused. +// Render draws the GameControls onto the target func (g *GameControls) Render(target d2interface.Surface) error { for entityIdx := range g.mapEngine.Entities() { entity := (g.mapEngine.Entities())[entityIdx] @@ -463,8 +478,13 @@ func (g *GameControls) Render(target d2interface.Surface) error { } } - g.heroStatsPanel.Render(target) - g.inventory.Render(target) + 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 @@ -679,14 +699,17 @@ func (g *GameControls) Render(target d2interface.Surface) error { return nil } +// SetZoneChangeText sets the zoneChangeText func (g *GameControls) SetZoneChangeText(text string) { g.zoneChangeText.SetText(text) } +// ShowZoneChangeText shows the zoneChangeText func (g *GameControls) ShowZoneChangeText() { g.isZoneTextShown = true } +// 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 diff --git a/d2game/d2player/hero_stats_panel.go b/d2game/d2player/hero_stats_panel.go index 83a1f2d7..10c16c2f 100644 --- a/d2game/d2player/hero_stats_panel.go +++ b/d2game/d2player/hero_stats_panel.go @@ -39,9 +39,6 @@ type StatsPanelLabels struct { Stamina *d2ui.Label } -// stores all the labels that can change during gameplay(e.g. current level, current hp, mana, etc.) -var StatValueLabels = make([]d2ui.Label, 13) - // HeroStatsPanel represents the hero status panel type HeroStatsPanel struct { uiManager *d2ui.UIManager @@ -162,7 +159,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { return err } - w, h = s.frame.GetCurrentFrameSize() + _, h = s.frame.GetCurrentFrameSize() s.frame.SetPosition(x, s.originY+h) @@ -177,7 +174,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { return err } - w, h = s.frame.GetCurrentFrameSize() + _, h = s.frame.GetCurrentFrameSize() s.frame.SetPosition(x, y+h) if err := s.frame.Render(target); err != nil { @@ -206,7 +203,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { return err } - w, h = s.frame.GetCurrentFrameSize() + _, h = s.frame.GetCurrentFrameSize() s.frame.SetPosition(x, y+h) @@ -239,7 +236,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { return err } - w, h = s.panel.GetCurrentFrameSize() + _, h = s.panel.GetCurrentFrameSize() s.panel.SetPosition(x, y+h) @@ -254,7 +251,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { return err } - w, h = s.panel.GetCurrentFrameSize() + _, h = s.panel.GetCurrentFrameSize() s.panel.SetPosition(x, y+h) @@ -306,6 +303,7 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error { {X: 310, Y: 468, Text: "Poison", Font: d2resource.Font6, AlignCenter: true}, {X: 310, Y: 477, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true}, } + for _, textElement := range staticTextLabels { label = s.createTextLabel(textElement) label.Render(target) @@ -368,7 +366,7 @@ func (s *HeroStatsPanel) renderStatValueNum(label *d2ui.Label, value int, label.Render(target) } -func (s *HeroStatsPanel) createStatValueLabel(stat int, x int, y int) *d2ui.Label { +func (s *HeroStatsPanel) createStatValueLabel(stat, x, y int) *d2ui.Label { text := strconv.Itoa(stat) return s.createTextLabel(PanelText{X: x, Y: y, Text: text, Font: d2resource.Font16, AlignCenter: true}) } diff --git a/d2game/d2player/inventory.go b/d2game/d2player/inventory.go index 74fd244e..ddfacc8c 100644 --- a/d2game/d2player/inventory.go +++ b/d2game/d2player/inventory.go @@ -1,6 +1,7 @@ package d2player import ( + "fmt" "image/color" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" @@ -13,24 +14,25 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) +// Inventory represents the inventory type Inventory struct { - uiManager *d2ui.UIManager - frame *d2ui.Sprite - panel *d2ui.Sprite - grid *ItemGrid - - hoverLabel *d2ui.Label - hoverX, hoverY int - hovering bool - - originX, originY int - lastMouseX, lastMouseY int - - isOpen bool + uiManager *d2ui.UIManager + frame *d2ui.Sprite + panel *d2ui.Sprite + grid *ItemGrid + hoverLabel *d2ui.Label + hoverX int + hoverY int + originX int + originY int + lastMouseX int + lastMouseY int + hovering bool + isOpen bool } +// NewInventory creates an inventory instance and returns a pointer to it func NewInventory(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *Inventory { - hoverLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic) hoverLabel.Alignment = d2gui.HorizontalAlignCenter @@ -44,22 +46,27 @@ func NewInventory(ui *d2ui.UIManager, record *d2datadict.InventoryRecord) *Inven } } +// IsOpen returns true if the inventory is open func (g *Inventory) IsOpen() bool { return g.isOpen } +// Toggle negates the open state of the inventory func (g *Inventory) Toggle() { g.isOpen = !g.isOpen } +// Open opens the inventory func (g *Inventory) Open() { g.isOpen = true } +// Close closes the inventory func (g *Inventory) Close() { g.isOpen = false } +// Load loads the resources required by the inventory func (g *Inventory) Load() { animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky) g.frame, _ = g.uiManager.NewSprite(animation) @@ -71,9 +78,10 @@ func (g *Inventory) Load() { diablo2item.NewItem("rin", "Steel", "of Shock").Identify(), diablo2item.NewItem("jav").Identify(), diablo2item.NewItem("buc").Identify(), - //diablo2item.NewItem("Arctic Furs", "qui"), + // diablo2item.NewItem("Arctic Furs", "qui"), // TODO: Load the player's actual items } + g.grid.ChangeEquippedSlot(d2enum.EquippedSlotLeftArm, diablo2item.NewItem("wnd")) g.grid.ChangeEquippedSlot(d2enum.EquippedSlotRightArm, diablo2item.NewItem("buc")) g.grid.ChangeEquippedSlot(d2enum.EquippedSlotHead, diablo2item.NewItem("crn")) @@ -85,9 +93,14 @@ func (g *Inventory) Load() { g.grid.ChangeEquippedSlot(d2enum.EquippedSlotRightHand, diablo2item.NewItem("rin")) g.grid.ChangeEquippedSlot(d2enum.EquippedSlotNeck, diablo2item.NewItem("amu")) // TODO: Load the player's actual items - g.grid.Add(items...) + + _, err := g.grid.Add(items...) + if err != nil { + fmt.Printf("could not add items to the inventory, err: %v\n", err) + } } +// Render draws the inventory onto the given surface func (g *Inventory) Render(target d2interface.Surface) error { if !g.isOpen { return nil @@ -194,7 +207,7 @@ func (g *Inventory) Render(target d2interface.Surface) error { return err } - w, h = g.panel.GetCurrentFrameSize() + _, h = g.panel.GetCurrentFrameSize() g.panel.SetPosition(x, y+h) @@ -209,7 +222,7 @@ func (g *Inventory) Render(target d2interface.Surface) error { return err } - w, h = g.panel.GetCurrentFrameSize() + _, h = g.panel.GetCurrentFrameSize() g.panel.SetPosition(x, y+h) if err := g.panel.Render(target); err != nil { diff --git a/d2game/d2player/player_state.go b/d2game/d2player/player_state.go index 8a0f3ba4..16602f21 100644 --- a/d2game/d2player/player_state.go +++ b/d2game/d2player/player_state.go @@ -2,8 +2,8 @@ package d2player import ( "encoding/json" + "fmt" "io/ioutil" - "log" "os" "path" "strconv" @@ -15,6 +15,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" ) +// PlayerState stores the state of the player type PlayerState struct { HeroName string `json:"heroName"` HeroType d2enum.Hero `json:"heroType"` @@ -22,38 +23,46 @@ type PlayerState struct { Act int `json:"act"` FilePath string `json:"-"` Equipment d2inventory.CharacterEquipment `json:"equipment"` - Stats *d2hero.HeroStatsState `json:"stats"` + Stats *d2hero.HeroStatsState `json:"stats"` X float64 `json:"x"` Y float64 `json:"y"` } +// HasGameStates returns true if the player has any previously saved game func HasGameStates() bool { basePath, _ := getGameBaseSavePath() files, _ := ioutil.ReadDir(basePath) + return len(files) > 0 } +// GetAllPlayerStates returns all player saves func GetAllPlayerStates() []*PlayerState { basePath, _ := getGameBaseSavePath() files, _ := ioutil.ReadDir(basePath) result := make([]*PlayerState, 0) + for _, file := range files { fileName := file.Name() if file.IsDir() || len(fileName) < 5 || strings.ToLower(fileName[len(fileName)-4:]) != ".od2" { continue } + gameState := LoadPlayerState(path.Join(basePath, file.Name())) if gameState == nil || gameState.HeroType == d2enum.HeroNone { + // temporarily loading default class stats if the character was created before saving stats was introduced + // to be removed in the future continue - // temporarily loading default class stats if the character was created before saving stats was introduced - // to be removed in the future } else if gameState.Stats == nil { gameState.Stats = d2hero.CreateHeroStatsState(gameState.HeroType, d2datadict.CharStats[gameState.HeroType]) - gameState.Save() + if err := gameState.Save(); err != nil { + fmt.Printf("failed to save game state!, err: %v\n", err) + } } result = append(result, gameState) } + return result } @@ -63,33 +72,41 @@ func CreateTestGameState() *PlayerState { return result } -func LoadPlayerState(path string) *PlayerState { - strData, err := ioutil.ReadFile(path) +// LoadPlayerState loads the player state from the file +func LoadPlayerState(filePath string) *PlayerState { + strData, err := ioutil.ReadFile(filePath) if err != nil { return nil } result := &PlayerState{ - FilePath: path, + FilePath: filePath, } + err = json.Unmarshal(strData, result) if err != nil { return nil } + return result } -func CreatePlayerState(heroName string, hero d2enum.Hero, classStats *d2datadict.CharStatsRecord, hardcore bool) *PlayerState { +// CreatePlayerState creates a PlayerState instance and returns a pointer to it +func CreatePlayerState(heroName string, hero d2enum.Hero, classStats *d2datadict.CharStatsRecord) *PlayerState { result := &PlayerState{ HeroName: heroName, HeroType: hero, Act: 1, - Stats: d2hero.CreateHeroStatsState(hero, classStats), + Stats: d2hero.CreateHeroStatsState(hero, classStats), Equipment: d2inventory.HeroObjects[hero], FilePath: "", } - result.Save() + if err := result.Save(); err != nil { + fmt.Printf("failed to save game state!, err: %v\n", err) + return nil + } + return result } @@ -105,6 +122,7 @@ func getGameBaseSavePath() (string, error) { func getFirstFreeFileName() string { i := 0 basePath, _ := getGameBaseSavePath() + for { filePath := path.Join(basePath, strconv.Itoa(i)+".od2") if _, err := os.Stat(filePath); os.IsNotExist(err) { @@ -114,13 +132,19 @@ func getFirstFreeFileName() string { } } -func (v *PlayerState) Save() { +// Save saves the player state to a file +func (v *PlayerState) Save() error { if v.FilePath == "" { v.FilePath = getFirstFreeFileName() } if err := os.MkdirAll(path.Dir(v.FilePath), 0755); err != nil { - log.Panic(err.Error()) + return err } - fileJson, _ := json.MarshalIndent(v, "", " ") - ioutil.WriteFile(v.FilePath, fileJson, 0644) + + fileJSON, _ := json.MarshalIndent(v, "", " ") + if err := ioutil.WriteFile(v.FilePath, fileJSON, 0644); err != nil { + return err + } + + return nil } diff --git a/main.go b/main.go index 4431fdb5..dfce2c79 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ func main() { } inputManager := d2input.NewInputManager() + term, err := d2term.New(inputManager) if err != nil { log.Fatal(err)