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