mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-04 07:37:48 -05:00
Lint cleanup (#628)
This commit is contained in:
parent
53599928f7
commit
7da1843f49
@ -219,7 +219,6 @@ func decode(input *d2common.BitStream, head *linkedNode) *linkedNode {
|
||||
return node
|
||||
}
|
||||
|
||||
// TODO: these consts for buildList need better names
|
||||
const (
|
||||
decompVal1 = 256
|
||||
decompVal2 = 257
|
||||
@ -374,6 +373,7 @@ func buildTree(tail *linkedNode) *linkedNode {
|
||||
}
|
||||
|
||||
// HuffmanDecompress decompresses huffman-compressed data
|
||||
//nolint:gomnd // binary decode magic
|
||||
func HuffmanDecompress(data []byte) []byte {
|
||||
comptype := data[0]
|
||||
primes := getPrimes()
|
||||
@ -399,6 +399,7 @@ Loop:
|
||||
break Loop
|
||||
case 257:
|
||||
newvalue := bitstream.ReadBits(8)
|
||||
|
||||
outputstream.PushByte(byte(newvalue))
|
||||
tail = insertNode(tail, newvalue)
|
||||
default:
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// WavDecompress decompresses wav files
|
||||
//nolint:gomnd // binary decode magic
|
||||
func WavDecompress(data []byte, channelCount int) []byte { //nolint:funlen doesn't make sense to split
|
||||
Array1 := []int{0x2c, 0x2c}
|
||||
Array2 := make([]int, channelCount)
|
||||
|
@ -6,34 +6,54 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||
)
|
||||
|
||||
// BinkVideoMode is the video mode type
|
||||
type BinkVideoMode uint32
|
||||
|
||||
const (
|
||||
BinkVideoModeNormal BinkVideoMode = 0
|
||||
BinkVideoModeHeightDoubled BinkVideoMode = 1
|
||||
BinkVideoModeHeightInterlaced BinkVideoMode = 2
|
||||
BinkVideoModeWidthDoubled BinkVideoMode = 3
|
||||
BinkVideoModeWidthAndHeightDoubled BinkVideoMode = 4
|
||||
// BinkVideoModeNormal is a normal video
|
||||
BinkVideoModeNormal BinkVideoMode = 0
|
||||
|
||||
// BinkVideoModeHeightDoubled is a height-doubled video
|
||||
BinkVideoModeHeightDoubled BinkVideoMode = 1
|
||||
|
||||
// BinkVideoModeHeightInterlaced is a height-interlaced video
|
||||
BinkVideoModeHeightInterlaced BinkVideoMode = 2
|
||||
|
||||
// BinkVideoModeWidthDoubled is a width-doubled video
|
||||
BinkVideoModeWidthDoubled BinkVideoMode = 3
|
||||
|
||||
// BinkVideoModeWidthAndHeightDoubled is a width and height-doubled video
|
||||
BinkVideoModeWidthAndHeightDoubled BinkVideoMode = 4
|
||||
|
||||
// BinkVideoModeWidthAndHeightInterlaced is a width and height interlaced video
|
||||
BinkVideoModeWidthAndHeightInterlaced BinkVideoMode = 5
|
||||
)
|
||||
|
||||
// BinkAudioAlgorithm represents the type of bink audio algorithm
|
||||
type BinkAudioAlgorithm uint32
|
||||
|
||||
const (
|
||||
// BinkAudioAlgorithmFFT is the FTT audio algorithm
|
||||
BinkAudioAlgorithmFFT BinkAudioAlgorithm = 0
|
||||
|
||||
// BinkAudioAlgorithmDCT is the DCT audio algorithm
|
||||
BinkAudioAlgorithmDCT BinkAudioAlgorithm = 1
|
||||
)
|
||||
|
||||
// BinkAudioTrack represents an audio track
|
||||
type BinkAudioTrack struct {
|
||||
AudioChannels uint16
|
||||
AudioSampleRateHz uint16
|
||||
Stereo bool
|
||||
Algorithm BinkAudioAlgorithm
|
||||
AudioTrackId uint32
|
||||
AudioTrackID uint32
|
||||
}
|
||||
|
||||
// BinkDecoder represents the bink decoder
|
||||
type BinkDecoder struct {
|
||||
videoCodecRevision byte
|
||||
AudioTracks []BinkAudioTrack
|
||||
FrameIndexTable []uint32
|
||||
streamReader *d2common.StreamReader
|
||||
fileSize uint32
|
||||
numberOfFrames uint32
|
||||
largestFrameSizeBytes uint32
|
||||
@ -41,39 +61,50 @@ type BinkDecoder struct {
|
||||
VideoHeight uint32
|
||||
FPS uint32
|
||||
FrameTimeMS uint32
|
||||
streamReader *d2common.StreamReader
|
||||
VideoMode BinkVideoMode
|
||||
frameIndex uint32
|
||||
videoCodecRevision byte
|
||||
HasAlphaPlane bool
|
||||
Grayscale bool
|
||||
AudioTracks []BinkAudioTrack
|
||||
FrameIndexTable []uint32 // Mask bit 0, as this is defined as a keyframe
|
||||
frameIndex uint32
|
||||
|
||||
// Mask bit 0, as this is defined as a keyframe
|
||||
|
||||
}
|
||||
|
||||
// CreateBinkDecoder returns a new instance of the bink decoder
|
||||
func CreateBinkDecoder(source []byte) *BinkDecoder {
|
||||
result := &BinkDecoder{
|
||||
streamReader: d2common.CreateStreamReader(source),
|
||||
}
|
||||
|
||||
result.loadHeaderInformation()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetNextFrame gets the next frame
|
||||
func (v *BinkDecoder) GetNextFrame() {
|
||||
//v.streamReader.SetPosition(uint64(v.FrameIndexTable[i] & 0xFFFFFFFE))
|
||||
lengthOfAudioPackets := v.streamReader.GetUInt32() - 4
|
||||
//nolint:gocritic // v.streamReader.SetPosition(uint64(v.FrameIndexTable[i] & 0xFFFFFFFE))
|
||||
|
||||
lengthOfAudioPackets := v.streamReader.GetUInt32() - 4 //nolint:gomnd // decode magic
|
||||
samplesInPacket := v.streamReader.GetUInt32()
|
||||
|
||||
v.streamReader.SkipBytes(int(lengthOfAudioPackets))
|
||||
|
||||
log.Printf("Frame %d:\tSamp: %d", v.frameIndex, samplesInPacket)
|
||||
|
||||
v.frameIndex++
|
||||
}
|
||||
|
||||
//nolint:gomnd // Decoder magic
|
||||
func (v *BinkDecoder) loadHeaderInformation() {
|
||||
v.streamReader.SetPosition(0)
|
||||
headerBytes := v.streamReader.ReadBytes(3)
|
||||
|
||||
if string(headerBytes) != "BIK" {
|
||||
log.Fatal("Invalid header for bink video")
|
||||
}
|
||||
|
||||
v.videoCodecRevision = v.streamReader.GetByte()
|
||||
v.fileSize = v.streamReader.GetUInt32()
|
||||
v.numberOfFrames = v.streamReader.GetUInt32()
|
||||
@ -91,20 +122,25 @@ func (v *BinkDecoder) loadHeaderInformation() {
|
||||
v.Grayscale = ((videoFlags >> 17) & 0x1) == 1
|
||||
numberOfAudioTracks := v.streamReader.GetUInt32()
|
||||
v.AudioTracks = make([]BinkAudioTrack, numberOfAudioTracks)
|
||||
|
||||
for i := 0; i < int(numberOfAudioTracks); i++ {
|
||||
v.streamReader.SkipBytes(2) // Unknown
|
||||
v.AudioTracks[i].AudioChannels = v.streamReader.GetUInt16()
|
||||
}
|
||||
|
||||
for i := 0; i < int(numberOfAudioTracks); i++ {
|
||||
v.AudioTracks[i].AudioSampleRateHz = v.streamReader.GetUInt16()
|
||||
flags := v.streamReader.GetUInt16()
|
||||
v.AudioTracks[i].Stereo = ((flags >> 13) & 0x1) == 1
|
||||
v.AudioTracks[i].Algorithm = BinkAudioAlgorithm((flags >> 12) & 0x1)
|
||||
}
|
||||
|
||||
for i := 0; i < int(numberOfAudioTracks); i++ {
|
||||
v.AudioTracks[i].AudioTrackId = v.streamReader.GetUInt32()
|
||||
v.AudioTracks[i].AudioTrackID = v.streamReader.GetUInt32()
|
||||
}
|
||||
|
||||
v.FrameIndexTable = make([]uint32, v.numberOfFrames+1)
|
||||
|
||||
for i := 0; i < int(v.numberOfFrames+1); i++ {
|
||||
v.FrameIndexTable[i] = v.streamReader.GetUInt32()
|
||||
}
|
||||
|
@ -28,9 +28,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
imgWidth = 256
|
||||
imgHeight = 128
|
||||
|
||||
CharWidth = 8
|
||||
CharHeight = 16
|
||||
)
|
||||
|
@ -2,6 +2,8 @@ package d2input
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -10,3 +12,10 @@ var (
|
||||
// ErrNotReg shows the input system has no registered handler
|
||||
ErrNotReg = errors.New("input system does not have provided handler")
|
||||
)
|
||||
|
||||
// Static checks to confirm struct conforms to interface
|
||||
var _ d2interface.InputEventHandler = &HandlerEvent{}
|
||||
var _ d2interface.KeyEvent = &KeyEvent{}
|
||||
var _ d2interface.KeyCharsEvent = &KeyCharsEvent{}
|
||||
var _ d2interface.MouseEvent = &MouseEvent{}
|
||||
var _ d2interface.MouseMoveEvent = &MouseMoveEvent{}
|
||||
|
33
d2core/d2input/handler_event.go
Normal file
33
d2core/d2input/handler_event.go
Normal file
@ -0,0 +1,33 @@
|
||||
package d2input
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
)
|
||||
|
||||
// HandlerEvent is an event that EventHandlers will process and respond to
|
||||
type HandlerEvent struct {
|
||||
keyMod d2enum.KeyMod
|
||||
buttonMod d2enum.MouseButtonMod
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
// KeyMod yields the modifier for a key action
|
||||
func (e *HandlerEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.keyMod
|
||||
}
|
||||
|
||||
// ButtonMod yields the modifier for a button action
|
||||
func (e *HandlerEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.buttonMod
|
||||
}
|
||||
|
||||
// X returns the x screen coordinate for the event
|
||||
func (e *HandlerEvent) X() int {
|
||||
return e.x
|
||||
}
|
||||
|
||||
// Y returns the y screen coordinate for the event
|
||||
func (e *HandlerEvent) Y() int {
|
||||
return e.y
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package d2input
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
)
|
||||
|
||||
// Static checks to confirm struct conforms to interface
|
||||
var _ d2interface.InputEventHandler = &HandlerEvent{}
|
||||
var _ d2interface.KeyEvent = &KeyEvent{}
|
||||
var _ d2interface.KeyCharsEvent = &KeyCharsEvent{}
|
||||
var _ d2interface.MouseEvent = &MouseEvent{}
|
||||
var _ d2interface.MouseMoveEvent = &MouseMoveEvent{}
|
||||
|
||||
// HandlerEvent is an event that EventHandlers will process and respond to
|
||||
type HandlerEvent struct {
|
||||
keyMod d2enum.KeyMod
|
||||
buttonMod d2enum.MouseButtonMod
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
// KeyMod yields the modifier for a key action
|
||||
func (e *HandlerEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.keyMod
|
||||
}
|
||||
|
||||
// ButtonMod yields the modifier for a button action
|
||||
func (e *HandlerEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.buttonMod
|
||||
}
|
||||
|
||||
// X returns the x screen coordinate for the event
|
||||
func (e *HandlerEvent) X() int {
|
||||
return e.x
|
||||
}
|
||||
|
||||
//Y returns the y screen coordinate for the event
|
||||
func (e *HandlerEvent) Y() int {
|
||||
return e.y
|
||||
}
|
||||
|
||||
type KeyCharsEvent struct {
|
||||
HandlerEvent
|
||||
chars []rune
|
||||
}
|
||||
|
||||
func (e *KeyCharsEvent) Chars() []rune {
|
||||
return e.chars
|
||||
}
|
||||
|
||||
type KeyEvent struct {
|
||||
HandlerEvent
|
||||
key d2enum.Key
|
||||
// Duration represents the number of frames this key has been pressed for
|
||||
duration int
|
||||
}
|
||||
|
||||
func (e *KeyEvent) Key() d2enum.Key {
|
||||
return e.key
|
||||
}
|
||||
func (e *KeyEvent) Duration() int {
|
||||
return e.duration
|
||||
}
|
||||
|
||||
type MouseEvent struct {
|
||||
HandlerEvent
|
||||
mouseButton d2enum.MouseButton
|
||||
}
|
||||
|
||||
func (e *MouseEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.HandlerEvent.keyMod
|
||||
}
|
||||
|
||||
func (e *MouseEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.HandlerEvent.buttonMod
|
||||
}
|
||||
|
||||
func (e *MouseEvent) X() int {
|
||||
return e.HandlerEvent.x
|
||||
}
|
||||
|
||||
func (e *MouseEvent) Y() int {
|
||||
return e.HandlerEvent.y
|
||||
}
|
||||
|
||||
func (e *MouseEvent) Button() d2enum.MouseButton {
|
||||
return e.mouseButton
|
||||
}
|
||||
|
||||
type MouseMoveEvent struct {
|
||||
HandlerEvent
|
||||
}
|
||||
|
||||
func (e *MouseMoveEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.HandlerEvent.keyMod
|
||||
}
|
||||
|
||||
func (e *MouseMoveEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.HandlerEvent.buttonMod
|
||||
}
|
||||
|
||||
func (e *MouseMoveEvent) X() int {
|
||||
return e.HandlerEvent.x
|
||||
}
|
||||
|
||||
func (e *MouseMoveEvent) Y() int {
|
||||
return e.HandlerEvent.y
|
||||
}
|
21
d2core/d2input/key_event.go
Normal file
21
d2core/d2input/key_event.go
Normal file
@ -0,0 +1,21 @@
|
||||
package d2input
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
// KeyEvent represents key events
|
||||
type KeyEvent struct {
|
||||
HandlerEvent
|
||||
key d2enum.Key
|
||||
// Duration represents the number of frames this key has been pressed for
|
||||
duration int
|
||||
}
|
||||
|
||||
// Key returns the key
|
||||
func (e *KeyEvent) Key() d2enum.Key {
|
||||
return e.key
|
||||
}
|
||||
|
||||
// Duration returns the duration
|
||||
func (e *KeyEvent) Duration() int {
|
||||
return e.duration
|
||||
}
|
12
d2core/d2input/keychars_event.go
Normal file
12
d2core/d2input/keychars_event.go
Normal file
@ -0,0 +1,12 @@
|
||||
package d2input
|
||||
|
||||
// KeyCharsEvent represents a key character event
|
||||
type KeyCharsEvent struct {
|
||||
HandlerEvent
|
||||
chars []rune
|
||||
}
|
||||
|
||||
// Chars returns the characters
|
||||
func (e *KeyCharsEvent) Chars() []rune {
|
||||
return e.chars
|
||||
}
|
34
d2core/d2input/mouse_event.go
Normal file
34
d2core/d2input/mouse_event.go
Normal file
@ -0,0 +1,34 @@
|
||||
package d2input
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
// MouseEvent represents a mouse event
|
||||
type MouseEvent struct {
|
||||
HandlerEvent
|
||||
mouseButton d2enum.MouseButton
|
||||
}
|
||||
|
||||
// KeyMod returns the key mod
|
||||
func (e *MouseEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.HandlerEvent.keyMod
|
||||
}
|
||||
|
||||
// ButtonMod represents a button mod
|
||||
func (e *MouseEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.HandlerEvent.buttonMod
|
||||
}
|
||||
|
||||
// X returns the event's X position
|
||||
func (e *MouseEvent) X() int {
|
||||
return e.HandlerEvent.x
|
||||
}
|
||||
|
||||
// Y returns the event's Y position
|
||||
func (e *MouseEvent) Y() int {
|
||||
return e.HandlerEvent.y
|
||||
}
|
||||
|
||||
// Button returns the mouse button
|
||||
func (e *MouseEvent) Button() d2enum.MouseButton {
|
||||
return e.mouseButton
|
||||
}
|
28
d2core/d2input/mousemove_event.go
Normal file
28
d2core/d2input/mousemove_event.go
Normal file
@ -0,0 +1,28 @@
|
||||
package d2input
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
// MouseMoveEvent represents a mouse movement event
|
||||
type MouseMoveEvent struct {
|
||||
HandlerEvent
|
||||
}
|
||||
|
||||
// KeyMod represents the key mod
|
||||
func (e *MouseMoveEvent) KeyMod() d2enum.KeyMod {
|
||||
return e.HandlerEvent.keyMod
|
||||
}
|
||||
|
||||
// ButtonMod represents the button mod
|
||||
func (e *MouseMoveEvent) ButtonMod() d2enum.MouseButtonMod {
|
||||
return e.HandlerEvent.buttonMod
|
||||
}
|
||||
|
||||
// X represents the X position
|
||||
func (e *MouseMoveEvent) X() int {
|
||||
return e.HandlerEvent.x
|
||||
}
|
||||
|
||||
// Y represents the Y position
|
||||
func (e *MouseMoveEvent) Y() int {
|
||||
return e.HandlerEvent.y
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
//nolint:gomnd
|
||||
package d2mapgen
|
||||
|
||||
import (
|
||||
@ -37,6 +38,7 @@ func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
||||
townSize := townStamp.Size()
|
||||
|
||||
log.Printf("Region Path: %s", townStamp.RegionPath())
|
||||
|
||||
if strings.Contains(townStamp.RegionPath(), "E1") {
|
||||
// East Exit
|
||||
mapEngine.PlaceStamp(townStamp, 0, 0)
|
||||
@ -63,8 +65,6 @@ func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
||||
// North Exit
|
||||
mapEngine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height)
|
||||
}
|
||||
|
||||
//mapEngine.RegenerateWalkPaths()
|
||||
}
|
||||
|
||||
func generateWilderness1TownEast(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
||||
|
@ -17,6 +17,10 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
||||
)
|
||||
|
||||
const (
|
||||
screenMiddleX = 400
|
||||
)
|
||||
|
||||
// MapRenderer manages the game viewport and Camera. It requests tile and entity data from MapEngine and renders it.
|
||||
type MapRenderer struct {
|
||||
renderer d2interface.Renderer // Used for drawing operations
|
||||
@ -32,7 +36,8 @@ type MapRenderer struct {
|
||||
}
|
||||
|
||||
// CreateMapRenderer creates a new MapRenderer, sets the required fields and returns a pointer to it.
|
||||
func CreateMapRenderer(renderer d2interface.Renderer, mapEngine *d2mapengine.MapEngine, term d2interface.Terminal, startX, startY float64) *MapRenderer {
|
||||
func CreateMapRenderer(renderer d2interface.Renderer, mapEngine *d2mapengine.MapEngine,
|
||||
term d2interface.Terminal, startX, startY float64) *MapRenderer {
|
||||
result := &MapRenderer{
|
||||
renderer: renderer,
|
||||
mapEngine: mapEngine,
|
||||
@ -93,8 +98,8 @@ func (mr *MapRenderer) SetMapEngine(mapEngine *d2mapengine.MapEngine) {
|
||||
func (mr *MapRenderer) Render(target d2interface.Surface) {
|
||||
mapSize := mr.mapEngine.Size()
|
||||
|
||||
stxf, styf := mr.viewport.ScreenToWorld(400, -200)
|
||||
etxf, etyf := mr.viewport.ScreenToWorld(400, 1050)
|
||||
stxf, styf := mr.viewport.ScreenToWorld(screenMiddleX, -200)
|
||||
etxf, etyf := mr.viewport.ScreenToWorld(screenMiddleX, 1050)
|
||||
|
||||
startX := int(math.Max(0, math.Floor(stxf)))
|
||||
startY := int(math.Max(0, math.Floor(styf)))
|
||||
@ -322,7 +327,7 @@ func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadowRecord, target d2inter
|
||||
defer mr.viewport.PushTranslationOrtho(-80, float64(tile.YAdjust)).PopTranslation()
|
||||
|
||||
target.PushTranslation(mr.viewport.GetTranslationScreen())
|
||||
target.PushColor(color.RGBA{R: 255, G: 255, B: 255, A: 160})
|
||||
target.PushColor(color.RGBA{R: 255, G: 255, B: 255, A: 160}) //nolint:gomnd // Not a magic number...
|
||||
|
||||
defer target.PopN(2)
|
||||
|
||||
@ -343,6 +348,7 @@ func (mr *MapRenderer) renderMapDebug(mapDebugVisLevel int, target d2interface.S
|
||||
|
||||
func (mr *MapRenderer) renderEntityDebug(target d2interface.Surface) {
|
||||
entities := *mr.mapEngine.Entities()
|
||||
|
||||
for idx := range entities {
|
||||
e := entities[idx]
|
||||
pos := e.GetPosition()
|
||||
|
@ -12,13 +12,15 @@ type worldTrans struct {
|
||||
}
|
||||
|
||||
const (
|
||||
center = 0
|
||||
left = 1
|
||||
right = 2
|
||||
center = 0
|
||||
left = 1
|
||||
right = 2
|
||||
tileWidth = 80
|
||||
tileHeight = 40
|
||||
half = 2
|
||||
)
|
||||
|
||||
// Viewport is used for converting vectors between screen (pixel), orthogonal (Camera) and world (isometric) space.
|
||||
// TODO: Has a coordinate (issue #456)
|
||||
type Viewport struct {
|
||||
defaultScreenRect d2common.Rectangle
|
||||
screenRect d2common.Rectangle
|
||||
@ -68,16 +70,16 @@ func (v *Viewport) ScreenToWorld(x, y int) (worldX, worldY float64) {
|
||||
|
||||
// OrthoToWorld returns the world position for the given orthogonal coordinates.
|
||||
func (v *Viewport) OrthoToWorld(x, y float64) (worldX, worldY float64) {
|
||||
worldX = (x/80 + y/40) / 2
|
||||
worldY = (y/40 - x/80) / 2
|
||||
worldX = (x/80 + y/40) / half
|
||||
worldY = (y/40 - x/80) / half
|
||||
|
||||
return worldX, worldY
|
||||
}
|
||||
|
||||
// WorldToOrtho returns the orthogonal position for the given world coordinates.
|
||||
func (v *Viewport) WorldToOrtho(x, y float64) (orthoX, orthoY float64) {
|
||||
orthoX = (x - y) * 80
|
||||
orthoY = (x + y) * 40
|
||||
orthoX = (x - y) * tileWidth
|
||||
orthoY = (x + y) * tileHeight
|
||||
|
||||
return orthoX, orthoY
|
||||
}
|
||||
@ -119,10 +121,10 @@ func (v *Viewport) IsTileVisible(x, y float64) bool {
|
||||
|
||||
// IsTileRectVisible returns false if none of the tiles rects are within the game screen.
|
||||
func (v *Viewport) IsTileRectVisible(rect d2common.Rectangle) bool {
|
||||
left := float64((rect.Left - rect.Bottom()) * 80)
|
||||
top := float64((rect.Left + rect.Top) * 40)
|
||||
right := float64((rect.Right() - rect.Top) * 80)
|
||||
bottom := float64((rect.Right() + rect.Bottom()) * 40)
|
||||
left := float64((rect.Left - rect.Bottom()) * tileWidth)
|
||||
top := float64((rect.Left + rect.Top) * tileHeight)
|
||||
right := float64((rect.Right() - rect.Top) * tileWidth)
|
||||
bottom := float64((rect.Right() + rect.Bottom()) * tileHeight)
|
||||
|
||||
return v.IsOrthoRectVisible(left, top, right, bottom)
|
||||
}
|
||||
@ -181,8 +183,8 @@ func (v *Viewport) getCameraOffset() (camX, camY float64) {
|
||||
camX, camY = camPosition.X(), camPosition.Y()
|
||||
}
|
||||
|
||||
camX -= float64(v.screenRect.Width / 2)
|
||||
camY -= float64(v.screenRect.Height / 2)
|
||||
camX -= float64(v.screenRect.Width / half)
|
||||
camY -= float64(v.screenRect.Height / half)
|
||||
|
||||
return camX, camY
|
||||
}
|
||||
@ -192,8 +194,8 @@ func (v *Viewport) toLeft() {
|
||||
return
|
||||
}
|
||||
|
||||
v.screenRect.Width = v.defaultScreenRect.Width / 2
|
||||
v.screenRect.Left = v.defaultScreenRect.Left + v.defaultScreenRect.Width/2
|
||||
v.screenRect.Width = v.defaultScreenRect.Width / half
|
||||
v.screenRect.Left = v.defaultScreenRect.Left + v.defaultScreenRect.Width/half
|
||||
v.align = left
|
||||
}
|
||||
|
||||
@ -202,7 +204,7 @@ func (v *Viewport) toRight() {
|
||||
return
|
||||
}
|
||||
|
||||
v.screenRect.Width = v.defaultScreenRect.Width / 2
|
||||
v.screenRect.Width = v.defaultScreenRect.Width / half
|
||||
v.align = right
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
)
|
||||
|
||||
// Finds an init function for the given object
|
||||
func initObject(ob *Object) bool {
|
||||
funcs := map[int]func(*Object){
|
||||
func initObject(ob *Object) (bool, error) {
|
||||
funcs := map[int]func(*Object) error{
|
||||
8: initTorch,
|
||||
14: initTorch,
|
||||
17: initWaypoint,
|
||||
@ -17,35 +17,41 @@ func initObject(ob *Object) bool {
|
||||
|
||||
fun, ok := funcs[ob.objectRecord.InitFn]
|
||||
if !ok {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
fun(ob)
|
||||
if err := fun(ob); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Initializes torch/brazier type objects
|
||||
func initTorch(ob *Object) {
|
||||
func initTorch(ob *Object) error {
|
||||
if ob.objectRecord.HasAnimationMode[d2enum.ObjectAnimationModeOpened] {
|
||||
ob.setMode(d2enum.ObjectAnimationModeOpened, 0, true)
|
||||
return ob.setMode(d2enum.ObjectAnimationModeOpened, 0, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initWaypoint(ob *Object) {
|
||||
func initWaypoint(ob *Object) error {
|
||||
// Turn these on unconditionally for now, they look nice :)
|
||||
if ob.objectRecord.HasAnimationMode[d2enum.ObjectAnimationModeOpened] {
|
||||
ob.setMode(d2enum.ObjectAnimationModeOpened, 0, true)
|
||||
return ob.setMode(d2enum.ObjectAnimationModeOpened, 0, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Randomly spawns in either NU or OP
|
||||
func initTorchRnd(ob *Object) {
|
||||
func initTorchRnd(ob *Object) error {
|
||||
n := rand.Intn(2)
|
||||
|
||||
if n > 0 {
|
||||
ob.setMode(d2enum.ObjectAnimationModeNeutral, 0, true)
|
||||
} else {
|
||||
ob.setMode(d2enum.ObjectAnimationModeOperating, 0, true)
|
||||
return ob.setMode(d2enum.ObjectAnimationModeNeutral, 0, true)
|
||||
}
|
||||
|
||||
return ob.setMode(d2enum.ObjectAnimationModeOperating, 0, true)
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ func (v *Button) Activate() {
|
||||
}
|
||||
|
||||
// Render renders the button
|
||||
func (v *Button) Render(target d2interface.Surface) {
|
||||
func (v *Button) Render(target d2interface.Surface) error {
|
||||
target.PushFilter(d2enum.FilterNearest)
|
||||
target.PushTranslation(v.x, v.y)
|
||||
|
||||
@ -248,6 +248,8 @@ func (v *Button) Render(target d2interface.Surface) {
|
||||
if err != nil {
|
||||
fmt.Printf("failed to render button surface, err: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Toggle negates the toggled state of the button
|
||||
|
@ -7,17 +7,21 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
|
||||
// Checkbox represents a checkbox UI element
|
||||
type Checkbox struct {
|
||||
x, y int
|
||||
checkState bool
|
||||
visible bool
|
||||
width, height int
|
||||
Image d2interface.Surface
|
||||
checkedImage d2interface.Surface
|
||||
onClick func()
|
||||
enabled bool
|
||||
Image d2interface.Surface
|
||||
checkedImage d2interface.Surface
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
onClick func()
|
||||
checkState bool
|
||||
visible bool
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// CreateCheckbox creates a new instance of a checkbox
|
||||
func CreateCheckbox(renderer d2interface.Renderer, checkState bool) Checkbox {
|
||||
result := Checkbox{
|
||||
checkState: checkState,
|
||||
@ -39,12 +43,15 @@ func CreateCheckbox(renderer d2interface.Renderer, checkState bool) Checkbox {
|
||||
result.checkedImage, _ = renderer.NewSurface(result.width, result.height, d2enum.FilterNearest)
|
||||
|
||||
_ = checkboxSprite.RenderSegmented(result.checkedImage, 1, 1, 1)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *Checkbox) Render(target d2interface.Surface) {
|
||||
// Render renders the checkbox
|
||||
func (v *Checkbox) Render(target d2interface.Surface) error {
|
||||
target.PushTranslation(v.x, v.y)
|
||||
target.PushFilter(d2enum.FilterNearest)
|
||||
|
||||
defer target.PopN(2)
|
||||
|
||||
if v.checkState {
|
||||
@ -52,39 +59,50 @@ func (v *Checkbox) Render(target d2interface.Surface) {
|
||||
} else {
|
||||
_ = target.Render(v.Image)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Advance does nothing for checkboxes
|
||||
func (v *Checkbox) Advance(elapsed float64) {
|
||||
|
||||
}
|
||||
|
||||
// GetEnabled returns the enabled state of the checkbox
|
||||
func (v *Checkbox) GetEnabled() bool {
|
||||
return v.enabled
|
||||
}
|
||||
|
||||
// SetEnabled sets the enabled state of the checkbox
|
||||
func (v *Checkbox) SetEnabled(enabled bool) {
|
||||
v.enabled = enabled
|
||||
}
|
||||
|
||||
// SetPressed does nothing for checkboxes
|
||||
func (v *Checkbox) SetPressed(_ bool) {
|
||||
}
|
||||
|
||||
// SetCheckState sets the check state of the checkbox
|
||||
func (v *Checkbox) SetCheckState(checkState bool) {
|
||||
v.checkState = checkState
|
||||
}
|
||||
|
||||
// GetCheckState returns the check state of the checkbox
|
||||
func (v *Checkbox) GetCheckState() bool {
|
||||
return v.checkState
|
||||
}
|
||||
|
||||
// GetPressed returns the pressed state of the checkbox
|
||||
func (v *Checkbox) GetPressed() bool {
|
||||
return v.checkState
|
||||
}
|
||||
|
||||
// OnACtivated sets the callback function of the click event for the checkbox
|
||||
func (v *Checkbox) OnActivated(callback func()) {
|
||||
v.onClick = callback
|
||||
}
|
||||
|
||||
// Activate activates the checkbox
|
||||
func (v *Checkbox) Activate() {
|
||||
v.checkState = !v.checkState
|
||||
if v.onClick == nil {
|
||||
@ -93,23 +111,28 @@ func (v *Checkbox) Activate() {
|
||||
v.onClick()
|
||||
}
|
||||
|
||||
// GetPosition returns the position of the checkbox
|
||||
func (v *Checkbox) GetPosition() (int, int) {
|
||||
return v.x, v.y
|
||||
}
|
||||
|
||||
// GetSize returns the size of the checkbox
|
||||
func (v *Checkbox) GetSize() (int, int) {
|
||||
return v.width, v.height
|
||||
}
|
||||
|
||||
// GetVisible returns the visibility state of the checkbox
|
||||
func (v *Checkbox) GetVisible() bool {
|
||||
return v.visible
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the checkbox
|
||||
func (v *Checkbox) SetPosition(x int, y int) {
|
||||
v.x = x
|
||||
v.y = y
|
||||
}
|
||||
|
||||
// SetVisible sets the visibility of the checkbox
|
||||
func (v *Checkbox) SetVisible(visible bool) {
|
||||
v.visible = visible
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
// Drawable represents an instance that can be drawn
|
||||
type Drawable interface {
|
||||
Render(target d2interface.Surface)
|
||||
Render(target d2interface.Surface) error
|
||||
Advance(elapsed float64)
|
||||
GetSize() (width, height int)
|
||||
SetPosition(x, y int)
|
||||
|
@ -74,27 +74,46 @@ func (v *Scrollbar) GetLastDirChange() int {
|
||||
return v.lastDirChange
|
||||
}
|
||||
|
||||
func (v *Scrollbar) Render(target d2interface.Surface) {
|
||||
func (v *Scrollbar) Render(target d2interface.Surface) error {
|
||||
if !v.visible || v.maxOffset == 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
offset := 0
|
||||
|
||||
if !v.enabled {
|
||||
offset = 2
|
||||
}
|
||||
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y)
|
||||
v.scrollbarSprite.RenderSegmented(target, 1, 1, 0+offset)
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+v.height-10)
|
||||
v.scrollbarSprite.RenderSegmented(target, 1, 1, 1+offset)
|
||||
if v.maxOffset == 0 || v.currentOffset < 0 || v.currentOffset > v.maxOffset {
|
||||
return
|
||||
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, 0+offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+v.height-10)
|
||||
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, 1+offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.maxOffset == 0 || v.currentOffset < 0 || v.currentOffset > v.maxOffset {
|
||||
return nil
|
||||
}
|
||||
|
||||
v.scrollbarSprite.SetPosition(v.x, v.y+10+v.getBarPosition())
|
||||
|
||||
offset = 0
|
||||
|
||||
if !v.enabled {
|
||||
offset = 1
|
||||
}
|
||||
v.scrollbarSprite.RenderSegmented(target, 1, 1, 4+offset)
|
||||
|
||||
if err := v.scrollbarSprite.RenderSegmented(target, 1, 1, 4+offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Scrollbar) Advance(elapsed float64) {
|
||||
|
@ -5,9 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
)
|
||||
@ -26,7 +24,8 @@ type TextBox struct {
|
||||
isFocused bool
|
||||
}
|
||||
|
||||
func CreateTextbox(renderer d2interface.Renderer) TextBox {
|
||||
// CreateTextbox creates a new instance of a text box
|
||||
func CreateTextbox() TextBox {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits)
|
||||
bgSprite, _ := LoadSprite(animation)
|
||||
tb := TextBox{
|
||||
@ -42,41 +41,59 @@ func CreateTextbox(renderer d2interface.Renderer) TextBox {
|
||||
return tb
|
||||
}
|
||||
|
||||
// SetFilter sets the text box filter
|
||||
func (v *TextBox) SetFilter(filter string) {
|
||||
v.filter = filter
|
||||
}
|
||||
|
||||
func (v *TextBox) Render(target d2interface.Surface) {
|
||||
// Render renders the text box
|
||||
func (v *TextBox) Render(target d2interface.Surface) error {
|
||||
if !v.visible {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
v.bgSprite.Render(target)
|
||||
|
||||
if err := v.bgSprite.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.textLabel.Render(target)
|
||||
|
||||
if (time.Now().UnixNano()/1e6)&(1<<8) > 0 {
|
||||
v.lineBar.Render(target)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnKeyChars handles key character events
|
||||
func (v *TextBox) OnKeyChars(event d2interface.KeyCharsEvent) bool {
|
||||
if !v.isFocused || !v.visible || !v.enabled {
|
||||
return false
|
||||
}
|
||||
|
||||
newText := string(event.Chars())
|
||||
|
||||
if len(newText) > 0 {
|
||||
v.text += newText
|
||||
|
||||
v.SetText(v.text)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// OnKeyRepeat handles key repeat events
|
||||
func (v *TextBox) OnKeyRepeat(event d2interface.KeyEvent) bool {
|
||||
if event.Key() == d2enum.KeyBackspace && debounceEvents(event.Duration()) {
|
||||
if len(v.text) >= 1 {
|
||||
v.text = v.text[:len(v.text)-1]
|
||||
}
|
||||
|
||||
v.SetText(v.text)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@ -85,97 +102,127 @@ func debounceEvents(numFrames int) bool {
|
||||
delay = 30
|
||||
interval = 3
|
||||
)
|
||||
|
||||
if numFrames == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
if numFrames >= delay && (numFrames-delay)%interval == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Advance updates the text box
|
||||
func (v *TextBox) Advance(_ float64) {
|
||||
if !v.visible || !v.enabled {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates the textbox
|
||||
func (v *TextBox) Update() {
|
||||
}
|
||||
|
||||
// GetText returns the text box's text
|
||||
func (v *TextBox) GetText() string {
|
||||
return v.text
|
||||
}
|
||||
|
||||
// SetText sets the text box's text
|
||||
//nolint:gomnd // Built-in values
|
||||
func (v *TextBox) SetText(newText string) {
|
||||
result := ""
|
||||
|
||||
for _, c := range newText {
|
||||
if !strings.Contains(v.filter, string(c)) {
|
||||
continue
|
||||
}
|
||||
|
||||
result += string(c)
|
||||
}
|
||||
|
||||
if len(result) > 15 {
|
||||
result = result[0:15]
|
||||
}
|
||||
|
||||
v.text = result
|
||||
|
||||
for {
|
||||
tw, _ := v.textLabel.GetTextMetrics(result)
|
||||
|
||||
if tw > 150 {
|
||||
result = result[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
v.lineBar.SetPosition(v.x+6+tw, v.y+3)
|
||||
v.textLabel.SetText(result)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// GetSize returns the size of the text box
|
||||
func (v *TextBox) GetSize() (width, height int) {
|
||||
return v.bgSprite.GetCurrentFrameSize()
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the text box
|
||||
//nolint:gomnd // Built-in values
|
||||
func (v *TextBox) SetPosition(x, y int) {
|
||||
lw, _ := v.textLabel.GetSize()
|
||||
|
||||
v.x = x
|
||||
v.y = y
|
||||
|
||||
v.textLabel.SetPosition(v.x+6, v.y+3)
|
||||
v.lineBar.SetPosition(v.x+6+lw, v.y+3)
|
||||
v.bgSprite.SetPosition(v.x, v.y+26)
|
||||
}
|
||||
|
||||
// GetPosition returns the position of the text box
|
||||
func (v *TextBox) GetPosition() (x, y int) {
|
||||
return v.x, v.y
|
||||
}
|
||||
|
||||
// GetVisible returns the visibility of the text box
|
||||
func (v *TextBox) GetVisible() bool {
|
||||
return v.visible
|
||||
}
|
||||
|
||||
// SetVisible sets the visibility of the text box
|
||||
func (v *TextBox) SetVisible(visible bool) {
|
||||
v.visible = visible
|
||||
}
|
||||
|
||||
// GetEnabled returns the enabled state of the text box
|
||||
func (v *TextBox) GetEnabled() bool {
|
||||
return v.enabled
|
||||
}
|
||||
|
||||
// SetEnabled sets the enabled state of the text box
|
||||
func (v *TextBox) SetEnabled(enabled bool) {
|
||||
v.enabled = enabled
|
||||
}
|
||||
|
||||
func (v *TextBox) SetPressed(pressed bool) {
|
||||
// SetPressed does nothing for text boxes
|
||||
func (v *TextBox) SetPressed(_ bool) {
|
||||
// no op
|
||||
}
|
||||
|
||||
// GetPressed does nothing for text boxes
|
||||
func (v *TextBox) GetPressed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *TextBox) OnActivated(callback func()) {
|
||||
// OnActivated handles activation events for the text box
|
||||
func (v *TextBox) OnActivated(_ func()) {
|
||||
// no op
|
||||
}
|
||||
|
||||
// Activate activates the text box
|
||||
func (v *TextBox) Activate() {
|
||||
v.isFocused = true
|
||||
}
|
||||
|
@ -120,7 +120,9 @@ func (v *Game) Render(screen d2interface.Surface) error {
|
||||
v.mapRenderer.Render(screen)
|
||||
|
||||
if v.gameControls != nil {
|
||||
v.gameControls.Render(screen)
|
||||
if err := v.gameControls.Render(screen); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -166,7 +168,9 @@ func (v *Game) Advance(elapsed float64) error {
|
||||
|
||||
// Bind the game controls to the player once it exists
|
||||
if v.gameControls == nil {
|
||||
v.bindGameControls()
|
||||
if err := v.bindGameControls(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update the camera to focus on the player
|
||||
@ -180,14 +184,21 @@ func (v *Game) Advance(elapsed float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Game) bindGameControls() {
|
||||
func (v *Game) bindGameControls() error {
|
||||
for _, player := range v.gameClient.Players {
|
||||
if player.ID != v.gameClient.PlayerID {
|
||||
continue
|
||||
}
|
||||
|
||||
v.localPlayer = player
|
||||
v.gameControls = d2player.NewGameControls(v.renderer, player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal)
|
||||
|
||||
var err error
|
||||
v.gameControls, err = d2player.NewGameControls(v.renderer, player, v.gameClient.MapEngine, v.mapRenderer, v, v.terminal)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.gameControls.Load()
|
||||
|
||||
if err := v.inputManager.BindHandler(v.gameControls); err != nil {
|
||||
@ -196,6 +207,8 @@ func (v *Game) bindGameControls() {
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnPlayerMove sends the player move action to the server
|
||||
|
@ -148,7 +148,7 @@ func (v *MainMenu) OnLoad(loading d2screen.LoadingState) {
|
||||
v.createLogos(loading)
|
||||
v.createButtons(loading)
|
||||
|
||||
v.tcpJoinGameEntry = d2ui.CreateTextbox(v.renderer)
|
||||
v.tcpJoinGameEntry = d2ui.CreateTextbox()
|
||||
v.tcpJoinGameEntry.SetPosition(joinGameDialogX, joinGameDialogY)
|
||||
v.tcpJoinGameEntry.SetFilter(joinGameCharacterFilter)
|
||||
d2ui.AddWidget(&v.tcpJoinGameEntry)
|
||||
|
@ -429,7 +429,7 @@ func (v *SelectHeroClass) createButtons() {
|
||||
}
|
||||
|
||||
func (v *SelectHeroClass) createCheckboxes(renderer d2interface.Renderer) {
|
||||
v.heroNameTextbox = d2ui.CreateTextbox(renderer)
|
||||
v.heroNameTextbox = d2ui.CreateTextbox()
|
||||
v.heroNameTextbox.SetPosition(heroNameTextBoxX, heoNameTextBoxY)
|
||||
v.heroNameTextbox.SetVisible(false)
|
||||
d2ui.AddWidget(&v.heroNameTextbox)
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
|
||||
// Panel represents the panel at the bottom of the game screen
|
||||
type Panel interface {
|
||||
IsOpen() bool
|
||||
Toggle()
|
||||
@ -38,10 +39,7 @@ const (
|
||||
globeWidth = 80
|
||||
)
|
||||
|
||||
var leftMenuRect = d2common.Rectangle{Left: 0, Top: 0, Width: 400, Height: 600}
|
||||
var rightMenuRect = d2common.Rectangle{Left: 400, Top: 0, Width: 400, Height: 600}
|
||||
var bottomMenuRect = d2common.Rectangle{Left: 0, Top: 550, Width: 800, Height: 50}
|
||||
|
||||
// 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
|
||||
@ -69,6 +67,7 @@ type GameControls struct {
|
||||
}
|
||||
|
||||
type ActionableType int
|
||||
|
||||
type ActionableRegion struct {
|
||||
ActionableTypeId ActionableType
|
||||
Rect d2common.Rectangle
|
||||
@ -87,7 +86,7 @@ const (
|
||||
)
|
||||
|
||||
func NewGameControls(renderer d2interface.Renderer, hero *d2mapentity.Player, mapEngine *d2mapengine.MapEngine,
|
||||
mapRenderer *d2maprenderer.MapRenderer, inputListener InputCallbackListener, term d2interface.Terminal) *GameControls {
|
||||
mapRenderer *d2maprenderer.MapRenderer, inputListener InputCallbackListener, term d2interface.Terminal) (*GameControls, error) {
|
||||
missileID := initialMissileID
|
||||
term.BindAction("setmissile", "set missile id to summon on right click", func(id int) {
|
||||
missileID = id
|
||||
@ -104,6 +103,7 @@ func NewGameControls(renderer d2interface.Renderer, hero *d2mapentity.Player, ma
|
||||
|
||||
// TODO make this depend on the hero type to respect inventory.txt
|
||||
var inventoryRecordKey string
|
||||
|
||||
switch hero.Class {
|
||||
case d2enum.HeroAssassin:
|
||||
inventoryRecordKey = "Assassin"
|
||||
@ -148,11 +148,15 @@ func NewGameControls(renderer d2interface.Renderer, hero *d2mapentity.Player, ma
|
||||
},
|
||||
}
|
||||
|
||||
term.BindAction("freecam", "toggle free camera movement", func() {
|
||||
err := term.BindAction("freecam", "toggle free camera movement", func() {
|
||||
gc.FreeCam = !gc.FreeCam
|
||||
})
|
||||
|
||||
return gc
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gc, nil
|
||||
}
|
||||
|
||||
func (g *GameControls) OnKeyRepeat(event d2interface.KeyEvent) bool {
|
||||
@ -238,6 +242,7 @@ func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool {
|
||||
|
||||
if isLeft && shouldDoLeft && inRect {
|
||||
lastLeftBtnActionTime = now
|
||||
|
||||
g.inputListener.OnPlayerMove(px, py)
|
||||
|
||||
if g.FreeCam {
|
||||
@ -259,7 +264,9 @@ func (g *GameControls) OnMouseButtonRepeat(event d2interface.MouseEvent) bool {
|
||||
|
||||
if isRight && shouldDoRight && inRect {
|
||||
lastRightBtnActionTime = now
|
||||
|
||||
g.inputListener.OnPlayerCast(g.missileID, px, py)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -283,6 +290,7 @@ func (g *GameControls) OnMouseMove(event d2interface.MouseMoveEvent) bool {
|
||||
|
||||
func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
||||
mx, my := event.X(), event.Y()
|
||||
|
||||
for i := range g.actionableRegions {
|
||||
// If click is on a game control element
|
||||
if g.actionableRegions[i].Rect.IsInRect(mx, my) {
|
||||
@ -297,13 +305,17 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
|
||||
|
||||
if event.Button() == d2enum.MouseButtonLeft && !g.isInActiveMenusRect(mx, my) {
|
||||
lastLeftBtnActionTime = d2common.Now()
|
||||
|
||||
g.inputListener.OnPlayerMove(px, py)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if event.Button() == d2enum.MouseButtonRight && !g.isInActiveMenusRect(mx, my) {
|
||||
lastRightBtnActionTime = d2common.Now()
|
||||
|
||||
g.inputListener.OnPlayerCast(g.missileID, px, py)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -335,11 +347,14 @@ func (g *GameControls) Load() {
|
||||
func (g *GameControls) loadUIButtons() {
|
||||
// Run button
|
||||
g.runButton = d2ui.CreateButton(g.renderer, d2ui.ButtonTypeRun, "")
|
||||
|
||||
g.runButton.SetPosition(255, 570)
|
||||
g.runButton.OnActivated(func() { g.onToggleRunButton() })
|
||||
|
||||
if g.hero.IsRunToggled() {
|
||||
g.runButton.Toggle()
|
||||
}
|
||||
|
||||
d2ui.AddWidget(&g.runButton)
|
||||
}
|
||||
|
||||
@ -350,7 +365,6 @@ func (g *GameControls) onToggleRunButton() {
|
||||
g.hero.SetIsRunning(g.hero.IsRunToggled())
|
||||
}
|
||||
|
||||
// ScreenAdvanceHandler
|
||||
func (g *GameControls) Advance(elapsed float64) error {
|
||||
g.mapRenderer.Advance(elapsed)
|
||||
return nil
|
||||
@ -380,6 +394,12 @@ func (g *GameControls) isRightPanelOpen() bool {
|
||||
}
|
||||
|
||||
func (g *GameControls) isInActiveMenusRect(px int, 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}
|
||||
|
||||
var rightMenuRect = d2common.Rectangle{Left: 400, Top: 0, Width: 400, Height: 600}
|
||||
|
||||
if bottomMenuRect.IsInRect(px, py) {
|
||||
return true
|
||||
}
|
||||
@ -396,7 +416,7 @@ func (g *GameControls) isInActiveMenusRect(px int, py int) bool {
|
||||
}
|
||||
|
||||
// TODO: consider caching the panels to single image that is reused.
|
||||
func (g *GameControls) Render(target d2interface.Surface) {
|
||||
func (g *GameControls) Render(target d2interface.Surface) error {
|
||||
for entityIdx := range *g.mapEngine.Entities() {
|
||||
entity := (*g.mapEngine.Entities())[entityIdx]
|
||||
if !entity.Selectable() {
|
||||
@ -413,6 +433,7 @@ func (g *GameControls) Render(target d2interface.Surface) {
|
||||
g.nameLabel.SetPosition(entScreenX, entScreenY-100)
|
||||
g.nameLabel.Render(target)
|
||||
entity.Highlight()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -424,110 +445,213 @@ func (g *GameControls) Render(target d2interface.Surface) {
|
||||
offset := 0
|
||||
|
||||
// Left globe holder
|
||||
g.mainPanel.SetCurrentFrame(0)
|
||||
if err := g.mainPanel.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ := g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
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))
|
||||
g.hpManaStatusSprite.SetCurrentFrame(0)
|
||||
|
||||
if err := g.hpManaStatusSprite.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.hpManaStatusSprite.SetPosition(offset+30, height-13)
|
||||
g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight))
|
||||
|
||||
if err := g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-hpBarHeight, globeWidth, globeHeight)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Left globe
|
||||
g.globeSprite.SetCurrentFrame(0)
|
||||
if err := g.globeSprite.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.globeSprite.SetPosition(offset+28, height-5)
|
||||
g.globeSprite.Render(target)
|
||||
|
||||
if err := g.globeSprite.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Left skill
|
||||
g.skillIcon.SetCurrentFrame(2)
|
||||
if err := g.skillIcon.SetCurrentFrame(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.skillIcon.GetCurrentFrameSize()
|
||||
|
||||
g.skillIcon.SetPosition(offset, height)
|
||||
g.skillIcon.Render(target)
|
||||
|
||||
if err := g.skillIcon.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Left skill selector
|
||||
g.mainPanel.SetCurrentFrame(1)
|
||||
if err := g.mainPanel.SetCurrentFrame(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
if err := g.mainPanel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Stamina
|
||||
g.mainPanel.SetCurrentFrame(2)
|
||||
if err := g.mainPanel.SetCurrentFrame(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
if err := g.mainPanel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Stamina status bar
|
||||
target.PushTranslation(273, 572)
|
||||
target.PushEffect(d2enum.DrawEffectModulate)
|
||||
|
||||
staminaPercent := float64(g.hero.Stats.Stamina) / float64(g.hero.Stats.MaxStamina)
|
||||
|
||||
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)
|
||||
|
||||
expPercent := float64(g.hero.Stats.Experience) / float64(g.hero.Stats.NextLevelExp)
|
||||
|
||||
target.DrawRect(int(expPercent*expBarWidth), 2, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
target.Pop()
|
||||
|
||||
// Center menu button
|
||||
g.menuButton.SetCurrentFrame(0)
|
||||
if err := g.menuButton.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.menuButton.SetPosition((width/2)-8, height-16)
|
||||
g.menuButton.Render(target)
|
||||
|
||||
if err := g.menuButton.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Potions
|
||||
g.mainPanel.SetCurrentFrame(3)
|
||||
if err := g.mainPanel.SetCurrentFrame(3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
if err := g.mainPanel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Right skill selector
|
||||
g.mainPanel.SetCurrentFrame(4)
|
||||
if err := g.mainPanel.SetCurrentFrame(4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
if err := g.mainPanel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Right skill
|
||||
g.skillIcon.SetCurrentFrame(2)
|
||||
if err := g.skillIcon.SetCurrentFrame(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.skillIcon.GetCurrentFrameSize()
|
||||
|
||||
g.skillIcon.SetPosition(offset, height)
|
||||
g.skillIcon.Render(target)
|
||||
|
||||
if err := g.skillIcon.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset += w
|
||||
|
||||
// Right globe holder
|
||||
g.mainPanel.SetCurrentFrame(5)
|
||||
if err := g.mainPanel.SetCurrentFrame(5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, _ = g.mainPanel.GetCurrentFrameSize()
|
||||
|
||||
g.mainPanel.SetPosition(offset, height)
|
||||
g.mainPanel.Render(target)
|
||||
|
||||
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))
|
||||
g.hpManaStatusSprite.SetCurrentFrame(1)
|
||||
|
||||
if err := g.hpManaStatusSprite.SetCurrentFrame(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.hpManaStatusSprite.SetPosition(offset+7, height-12)
|
||||
g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight))
|
||||
|
||||
if err := g.hpManaStatusSprite.RenderSection(target, image.Rect(0, globeHeight-manaBarHeight, globeWidth, globeHeight)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Right globe
|
||||
g.globeSprite.SetCurrentFrame(1)
|
||||
if err := g.globeSprite.SetCurrentFrame(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.globeSprite.SetPosition(offset+8, height-8)
|
||||
g.globeSprite.Render(target)
|
||||
g.globeSprite.Render(target)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GameControls) SetZoneChangeText(text string) {
|
||||
|
@ -1,18 +1,18 @@
|
||||
package d2player
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"strconv"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
|
||||
// PanelText represents text on the panel
|
||||
type PanelText struct {
|
||||
X int
|
||||
Y int
|
||||
@ -22,6 +22,7 @@ type PanelText struct {
|
||||
AlignCenter bool
|
||||
}
|
||||
|
||||
// StatsPanelLabels represents the labels in the status panel
|
||||
type StatsPanelLabels struct {
|
||||
Level d2ui.Label
|
||||
Experience d2ui.Label
|
||||
@ -38,36 +39,10 @@ type StatsPanelLabels struct {
|
||||
Stamina d2ui.Label
|
||||
}
|
||||
|
||||
var StaticTextLabels = []PanelText{
|
||||
{X: 110, Y: 100, Text: "Level", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 200, Y: 100, Text: "Experience", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 330, Y: 100, Text: "Next Level", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 100, Y: 150, Text: "Strength", Font: d2resource.Font6},
|
||||
{X: 100, Y: 213, Text: "Dexterity", Font: d2resource.Font6},
|
||||
{X: 100, Y: 300, Text: "Vitality", Font: d2resource.Font6},
|
||||
{X: 100, Y: 360, Text: "Energy", Font: d2resource.Font6},
|
||||
{X: 280, Y: 260, Text: "Defense", Font: d2resource.Font6},
|
||||
{X: 280, Y: 300, Text: "Stamina", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 280, Y: 322, Text: "Life", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 280, Y: 360, Text: "Mana", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
// can't use "Fire\nResistance" because line spacing is too big and breaks the layout
|
||||
{X: 310, Y: 395, Text: "Fire", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 402, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{X: 310, Y: 420, Text: "Cold", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 427, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{X: 310, Y: 445, Text: "Lightning", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 452, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{X: 310, Y: 468, Text: "Poison", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 477, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
}
|
||||
|
||||
// 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 {
|
||||
frame *d2ui.Sprite
|
||||
panel *d2ui.Sprite
|
||||
@ -83,6 +58,7 @@ type HeroStatsPanel struct {
|
||||
isOpen bool
|
||||
}
|
||||
|
||||
// NewHeroStatsPanel creates a new hero status panel
|
||||
func NewHeroStatsPanel(renderer d2interface.Renderer, heroName string, heroClass d2enum.Hero,
|
||||
heroState *d2hero.HeroStatsState) *HeroStatsPanel {
|
||||
originX := 0
|
||||
@ -99,6 +75,7 @@ func NewHeroStatsPanel(renderer d2interface.Renderer, heroName string, heroClass
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads the data for the hero status panel
|
||||
func (s *HeroStatsPanel) Load() {
|
||||
animation, _ := d2asset.LoadAnimation(d2resource.Frame, d2resource.PaletteSky)
|
||||
s.frame, _ = d2ui.LoadSprite(animation)
|
||||
@ -107,25 +84,30 @@ func (s *HeroStatsPanel) Load() {
|
||||
s.initStatValueLabels()
|
||||
}
|
||||
|
||||
// IsOpen returns true if the hero status panel is open
|
||||
func (s *HeroStatsPanel) IsOpen() bool {
|
||||
return s.isOpen
|
||||
}
|
||||
|
||||
// Toggle toggles the visibility of the hero status panel
|
||||
func (s *HeroStatsPanel) Toggle() {
|
||||
s.isOpen = !s.isOpen
|
||||
}
|
||||
|
||||
// Open opens the hero status panel
|
||||
func (s *HeroStatsPanel) Open() {
|
||||
s.isOpen = true
|
||||
}
|
||||
|
||||
// Close closed the hero status panel
|
||||
func (s *HeroStatsPanel) Close() {
|
||||
s.isOpen = false
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) Render(target d2interface.Surface) {
|
||||
// Render renders the hero status panel
|
||||
func (s *HeroStatsPanel) Render(target d2interface.Surface) error {
|
||||
if !s.isOpen {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.staticMenuImageCache == nil {
|
||||
@ -134,53 +116,101 @@ func (s *HeroStatsPanel) Render(target d2interface.Surface) {
|
||||
surface, err := s.renderer.NewSurface(frameWidth*framesCount, frameHeight*framesCount, d2enum.FilterNearest)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
s.staticMenuImageCache = &surface
|
||||
s.renderStaticMenu(*s.staticMenuImageCache)
|
||||
|
||||
if err := s.renderStaticMenu(*s.staticMenuImageCache); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
target.Render(*s.staticMenuImageCache)
|
||||
|
||||
if err := target.Render(*s.staticMenuImageCache); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.renderStatValues(target)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) {
|
||||
func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) error {
|
||||
x, y := s.originX, s.originY
|
||||
|
||||
// Frame
|
||||
// Top left
|
||||
s.frame.SetCurrentFrame(0)
|
||||
if err := s.frame.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h := s.frame.GetCurrentFrameSize()
|
||||
|
||||
s.frame.SetPosition(x, y+h)
|
||||
s.frame.Render(target)
|
||||
|
||||
if err := s.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
y += h
|
||||
|
||||
// Top right
|
||||
s.frame.SetCurrentFrame(1)
|
||||
if err := s.frame.SetCurrentFrame(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.frame.GetCurrentFrameSize()
|
||||
|
||||
s.frame.SetPosition(x, s.originY+h)
|
||||
s.frame.Render(target)
|
||||
|
||||
if err := s.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x = s.originX
|
||||
|
||||
// Right
|
||||
s.frame.SetCurrentFrame(2)
|
||||
if err := s.frame.SetCurrentFrame(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.frame.GetCurrentFrameSize()
|
||||
s.frame.SetPosition(x, y+h)
|
||||
s.frame.Render(target)
|
||||
|
||||
if err := s.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
y += h
|
||||
|
||||
// Bottom left
|
||||
s.frame.SetCurrentFrame(3)
|
||||
if err := s.frame.SetCurrentFrame(3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.frame.GetCurrentFrameSize()
|
||||
|
||||
s.frame.SetPosition(x, y+h)
|
||||
s.frame.Render(target)
|
||||
|
||||
if err := s.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
|
||||
// Bottom right
|
||||
s.frame.SetCurrentFrame(4)
|
||||
if err := s.frame.SetCurrentFrame(4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.frame.GetCurrentFrameSize()
|
||||
|
||||
s.frame.SetPosition(x, y+h)
|
||||
s.frame.Render(target)
|
||||
|
||||
if err := s.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x, y = s.originX, s.originY
|
||||
y += 64
|
||||
@ -188,42 +218,106 @@ func (s *HeroStatsPanel) renderStaticMenu(target d2interface.Surface) {
|
||||
|
||||
// Panel
|
||||
// Top left
|
||||
s.panel.SetCurrentFrame(0)
|
||||
if err := s.panel.SetCurrentFrame(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.panel.GetCurrentFrameSize()
|
||||
|
||||
s.panel.SetPosition(x, y+h)
|
||||
s.panel.Render(target)
|
||||
|
||||
if err := s.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
|
||||
// Top right
|
||||
s.panel.SetCurrentFrame(1)
|
||||
if err := s.panel.SetCurrentFrame(1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.panel.GetCurrentFrameSize()
|
||||
|
||||
s.panel.SetPosition(x, y+h)
|
||||
s.panel.Render(target)
|
||||
|
||||
if err := s.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
y += h
|
||||
|
||||
// Bottom right
|
||||
s.panel.SetCurrentFrame(3)
|
||||
if err := s.panel.SetCurrentFrame(3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.panel.GetCurrentFrameSize()
|
||||
|
||||
s.panel.SetPosition(x, y+h)
|
||||
s.panel.Render(target)
|
||||
|
||||
if err := s.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Bottom left
|
||||
s.panel.SetCurrentFrame(2)
|
||||
if err := s.panel.SetCurrentFrame(2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = s.panel.GetCurrentFrameSize()
|
||||
|
||||
s.panel.SetPosition(x-w, y+h)
|
||||
s.panel.Render(target)
|
||||
|
||||
if err := s.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var label d2ui.Label
|
||||
|
||||
// all static labels are not stored since we use them only once to generate the image cache
|
||||
for _, textElement := range StaticTextLabels {
|
||||
|
||||
//nolint:gomnd
|
||||
var staticTextLabels = []PanelText{
|
||||
{X: 110, Y: 100, Text: "Level", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 200, Y: 100, Text: "Experience", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 330, Y: 100, Text: "Next Level", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 100, Y: 150, Text: "Strength", Font: d2resource.Font6},
|
||||
{X: 100, Y: 213, Text: "Dexterity", Font: d2resource.Font6},
|
||||
{X: 100, Y: 300, Text: "Vitality", Font: d2resource.Font6},
|
||||
{X: 100, Y: 360, Text: "Energy", Font: d2resource.Font6},
|
||||
{X: 280, Y: 260, Text: "Defense", Font: d2resource.Font6},
|
||||
{X: 280, Y: 300, Text: "Stamina", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 280, Y: 322, Text: "Life", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 280, Y: 360, Text: "Mana", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
// can't use "Fire\nResistance" because line spacing is too big and breaks the layout
|
||||
{X: 310, Y: 395, Text: "Fire", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 402, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{X: 310, Y: 420, Text: "Cold", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 427, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{X: 310, Y: 445, Text: "Lightning", Font: d2resource.Font6, AlignCenter: true},
|
||||
{X: 310, Y: 452, Text: "Resistance", Font: d2resource.Font6, AlignCenter: true},
|
||||
|
||||
{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)
|
||||
}
|
||||
// hero name and class are part of the static image cache since they don't change after we enter the world
|
||||
label = s.createTextLabel(PanelText{X: 165, Y: 72, Text: s.heroName, Font: d2resource.Font16, AlignCenter: true})
|
||||
|
||||
label.Render(target)
|
||||
|
||||
label = s.createTextLabel(PanelText{X: 330, Y: 72, Text: s.heroClass.String(), Font: d2resource.Font16, AlignCenter: true})
|
||||
|
||||
label.Render(target)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HeroStatsPanel) initStatValueLabels() {
|
||||
|
@ -72,78 +72,148 @@ func (g *Inventory) Load() {
|
||||
g.grid.Add(items...)
|
||||
}
|
||||
|
||||
func (g *Inventory) Render(target d2interface.Surface) {
|
||||
func (g *Inventory) Render(target d2interface.Surface) error {
|
||||
if !g.isOpen {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
x, y := g.originX, g.originY
|
||||
|
||||
// Frame
|
||||
// Top left
|
||||
g.frame.SetCurrentFrame(5)
|
||||
if err := g.frame.SetCurrentFrame(5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h := g.frame.GetCurrentFrameSize()
|
||||
|
||||
g.frame.SetPosition(x, y+h)
|
||||
g.frame.Render(target)
|
||||
|
||||
if err := g.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
|
||||
// Top right
|
||||
g.frame.SetCurrentFrame(6)
|
||||
if err := g.frame.SetCurrentFrame(6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.frame.GetCurrentFrameSize()
|
||||
|
||||
g.frame.SetPosition(x, y+h)
|
||||
g.frame.Render(target)
|
||||
|
||||
if err := g.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
y += h
|
||||
|
||||
// Right
|
||||
g.frame.SetCurrentFrame(7)
|
||||
if err := g.frame.SetCurrentFrame(7); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.frame.GetCurrentFrameSize()
|
||||
|
||||
g.frame.SetPosition(x-w, y+h)
|
||||
g.frame.Render(target)
|
||||
|
||||
if err := g.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
y += h
|
||||
|
||||
// Bottom right
|
||||
g.frame.SetCurrentFrame(8)
|
||||
if err := g.frame.SetCurrentFrame(8); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.frame.GetCurrentFrameSize()
|
||||
|
||||
g.frame.SetPosition(x-w, y+h)
|
||||
g.frame.Render(target)
|
||||
|
||||
if err := g.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x -= w
|
||||
|
||||
// Bottom left
|
||||
g.frame.SetCurrentFrame(9)
|
||||
if err := g.frame.SetCurrentFrame(9); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.frame.GetCurrentFrameSize()
|
||||
|
||||
g.frame.SetPosition(x-w, y+h)
|
||||
g.frame.Render(target)
|
||||
|
||||
if err := g.frame.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x, y = g.originX, g.originY
|
||||
y += 64
|
||||
|
||||
// Panel
|
||||
// Top left
|
||||
g.panel.SetCurrentFrame(4)
|
||||
if err := g.panel.SetCurrentFrame(4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.panel.GetCurrentFrameSize()
|
||||
|
||||
g.panel.SetPosition(x, y+h)
|
||||
g.panel.Render(target)
|
||||
|
||||
if err := g.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x += w
|
||||
|
||||
// Top right
|
||||
g.panel.SetCurrentFrame(5)
|
||||
if err := g.panel.SetCurrentFrame(5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.panel.GetCurrentFrameSize()
|
||||
|
||||
g.panel.SetPosition(x, y+h)
|
||||
g.panel.Render(target)
|
||||
|
||||
if err := g.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
y += h
|
||||
|
||||
// Bottom right
|
||||
g.panel.SetCurrentFrame(7)
|
||||
if err := g.panel.SetCurrentFrame(7); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.panel.GetCurrentFrameSize()
|
||||
g.panel.SetPosition(x, y+h)
|
||||
g.panel.Render(target)
|
||||
|
||||
if err := g.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Bottom left
|
||||
g.panel.SetCurrentFrame(6)
|
||||
if err := g.panel.SetCurrentFrame(6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, h = g.panel.GetCurrentFrameSize()
|
||||
|
||||
g.panel.SetPosition(x-w, y+h)
|
||||
g.panel.Render(target)
|
||||
|
||||
if err := g.panel.Render(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.grid.Render(target)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user