Compare commits
3 Commits
0e509ad752
...
testing
Author | SHA1 | Date | |
---|---|---|---|
75eff6c5ad | |||
84d63ba4c1 | |||
49b84c8540 |
@ -34,8 +34,8 @@ func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error
|
|||||||
|
|
||||||
func LoadModels() ([]types.ModelAsset, error) {
|
func LoadModels() ([]types.ModelAsset, error) {
|
||||||
// Goonion model and animations
|
// Goonion model and animations
|
||||||
goonerModel := rl.LoadModel("resources/models/biped/Animation_Unsteady_Walk_withSkin.glb")
|
goonerModel := rl.LoadModel("resources/models/gooner/walk_no_y_transform.glb")
|
||||||
goonerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/biped/Animation_Idle_withSkin.glb", "walk": "resources/models/biped/Animation_Unsteady_Walk_withSkin.glb"})
|
goonerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/gooner/idle_no_y_transform.glb", "walk": "resources/models/gooner/walk_no_y_transform.glb"})
|
||||||
|
|
||||||
// Apply transformations
|
// Apply transformations
|
||||||
transform := rl.MatrixIdentity()
|
transform := rl.MatrixIdentity()
|
||||||
@ -45,15 +45,12 @@ func LoadModels() ([]types.ModelAsset, error) {
|
|||||||
goonerModel.Transform = transform
|
goonerModel.Transform = transform
|
||||||
|
|
||||||
// Coomer model (ready for animations)
|
// Coomer model (ready for animations)
|
||||||
coomerModel := rl.LoadModel("resources/models/coomer.obj")
|
coomerModel := rl.LoadModel("resources/models/coomer/idle_notransy.glb")
|
||||||
coomerTexture := rl.LoadTexture("resources/models/coomer.png")
|
// coomerTexture := rl.LoadTexture("resources/models/coomer.png")
|
||||||
rl.SetMaterialTexture(coomerModel.Materials, rl.MapDiffuse, coomerTexture)
|
// rl.SetMaterialTexture(coomerModel.Materials, rl.MapDiffuse, coomerTexture)
|
||||||
// When you have animations, add them like:
|
// When you have animations, add them like:
|
||||||
// coomerAnims, _ := loadModelAnimations("resources/models/coomer.glb",
|
coomerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/coomer/idle_notransy.glb", "walk": "resources/models/coomer/unsteadywalk_notransy.glb"})
|
||||||
// map[string]string{
|
coomerModel.Transform = transform
|
||||||
// "idle": "resources/models/coomer_idle.glb",
|
|
||||||
// "walk": "resources/models/coomer_walk.glb",
|
|
||||||
// })
|
|
||||||
|
|
||||||
// Shreke model (ready for animations)
|
// Shreke model (ready for animations)
|
||||||
shrekeModel := rl.LoadModel("resources/models/shreke.obj")
|
shrekeModel := rl.LoadModel("resources/models/shreke.obj")
|
||||||
@ -69,11 +66,18 @@ func LoadModels() ([]types.ModelAsset, error) {
|
|||||||
return []types.ModelAsset{
|
return []types.ModelAsset{
|
||||||
{
|
{
|
||||||
Model: goonerModel,
|
Model: goonerModel,
|
||||||
Animation: append(goonerAnims.Idle, goonerAnims.Walk...), // For compatibility
|
Animation: append(goonerAnims.Idle, goonerAnims.Walk...),
|
||||||
AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
|
AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
|
||||||
Animations: goonerAnims,
|
Animations: goonerAnims,
|
||||||
|
YOffset: 0.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Model: coomerModel,
|
||||||
|
Animation: append(coomerAnims.Idle, coomerAnims.Walk...),
|
||||||
|
AnimFrames: int32(len(coomerAnims.Idle) + len(coomerAnims.Walk)),
|
||||||
|
Animations: coomerAnims,
|
||||||
|
YOffset: -4.0,
|
||||||
},
|
},
|
||||||
{Model: coomerModel, Texture: coomerTexture},
|
|
||||||
{Model: shrekeModel, Texture: shrekeTexture},
|
{Model: shrekeModel, Texture: shrekeTexture},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
2287
frames.txt
2287
frames.txt
File diff suppressed because it is too large
Load Diff
18
game/chat.go
18
game/chat.go
@ -25,12 +25,14 @@ type Chat struct {
|
|||||||
cursorPos int
|
cursorPos int
|
||||||
scrollOffset int
|
scrollOffset int
|
||||||
userData interface{}
|
userData interface{}
|
||||||
|
input InputHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChat() *Chat {
|
func NewChat() *Chat {
|
||||||
return &Chat{
|
return &Chat{
|
||||||
messages: make([]types.ChatMessage, 0, maxMessages),
|
messages: make([]types.ChatMessage, 0, maxMessages),
|
||||||
inputBuffer: make([]rune, 0, runeLimit),
|
inputBuffer: make([]rune, 0, runeLimit),
|
||||||
|
input: &RaylibInput{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,23 +155,23 @@ func (c *Chat) Update() (string, bool) {
|
|||||||
c.scrollOffset = clamp(c.scrollOffset-int(wheelMove), 0, maxScroll)
|
c.scrollOffset = clamp(c.scrollOffset-int(wheelMove), 0, maxScroll)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.IsKeyPressed(rl.KeyT) {
|
if c.input.IsKeyPressed(rl.KeyT) {
|
||||||
c.isTyping = true
|
c.isTyping = true
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
key := rl.GetCharPressed()
|
key := c.input.GetCharPressed()
|
||||||
for key > 0 {
|
for key > 0 {
|
||||||
if len(c.inputBuffer) < runeLimit {
|
if len(c.inputBuffer) < runeLimit {
|
||||||
c.inputBuffer = append(c.inputBuffer[:c.cursorPos], append([]rune{key}, c.inputBuffer[c.cursorPos:]...)...)
|
c.inputBuffer = append(c.inputBuffer[:c.cursorPos], append([]rune{key}, c.inputBuffer[c.cursorPos:]...)...)
|
||||||
c.cursorPos++
|
c.cursorPos++
|
||||||
}
|
}
|
||||||
key = rl.GetCharPressed()
|
key = c.input.GetCharPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter) {
|
if c.input.IsKeyPressed(rl.KeyEnter) || c.input.IsKeyPressed(rl.KeyKpEnter) {
|
||||||
if len(c.inputBuffer) > 0 {
|
if len(c.inputBuffer) > 0 {
|
||||||
message := string(c.inputBuffer)
|
message := string(c.inputBuffer)
|
||||||
c.inputBuffer = c.inputBuffer[:0]
|
c.inputBuffer = c.inputBuffer[:0]
|
||||||
@ -180,21 +182,21 @@ func (c *Chat) Update() (string, bool) {
|
|||||||
c.isTyping = false
|
c.isTyping = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.IsKeyPressed(rl.KeyEscape) && c.isTyping {
|
if c.input.IsKeyPressed(rl.KeyEscape) && c.isTyping {
|
||||||
c.inputBuffer = c.inputBuffer[:0]
|
c.inputBuffer = c.inputBuffer[:0]
|
||||||
c.cursorPos = 0
|
c.cursorPos = 0
|
||||||
c.isTyping = false
|
c.isTyping = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.IsKeyPressed(rl.KeyBackspace) && c.cursorPos > 0 {
|
if c.input.IsKeyPressed(rl.KeyBackspace) && c.cursorPos > 0 {
|
||||||
c.inputBuffer = append(c.inputBuffer[:c.cursorPos-1], c.inputBuffer[c.cursorPos:]...)
|
c.inputBuffer = append(c.inputBuffer[:c.cursorPos-1], c.inputBuffer[c.cursorPos:]...)
|
||||||
c.cursorPos--
|
c.cursorPos--
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.IsKeyPressed(rl.KeyLeft) && c.cursorPos > 0 {
|
if c.input.IsKeyPressed(rl.KeyLeft) && c.cursorPos > 0 {
|
||||||
c.cursorPos--
|
c.cursorPos--
|
||||||
}
|
}
|
||||||
if rl.IsKeyPressed(rl.KeyRight) && c.cursorPos < len(c.inputBuffer) {
|
if c.input.IsKeyPressed(rl.KeyRight) && c.cursorPos < len(c.inputBuffer) {
|
||||||
c.cursorPos++
|
c.cursorPos++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
game/chat_test.go
Normal file
106
game/chat_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/testutils"
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/types"
|
||||||
|
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChat_AddMessage(t *testing.T) {
|
||||||
|
chat := NewChat()
|
||||||
|
|
||||||
|
// Test adding single message
|
||||||
|
chat.AddMessage(1, "Hello")
|
||||||
|
assert.Equal(t, 1, len(chat.messages))
|
||||||
|
assert.Equal(t, int32(1), chat.messages[0].PlayerID)
|
||||||
|
assert.Equal(t, "Hello", chat.messages[0].Content)
|
||||||
|
|
||||||
|
// Test message limit
|
||||||
|
for i := 0; i < maxMessages+10; i++ {
|
||||||
|
chat.AddMessage(1, "spam")
|
||||||
|
}
|
||||||
|
assert.Equal(t, maxMessages, len(chat.messages))
|
||||||
|
assert.Equal(t, "spam", chat.messages[len(chat.messages)-1].Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChat_HandleServerMessages(t *testing.T) {
|
||||||
|
chat := NewChat()
|
||||||
|
mockGame := &Game{
|
||||||
|
Player: &types.Player{ID: 1},
|
||||||
|
OtherPlayers: map[int32]*types.Player{
|
||||||
|
2: {ID: 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
chat.userData = mockGame
|
||||||
|
|
||||||
|
messages := []*pb.ChatMessage{
|
||||||
|
{
|
||||||
|
PlayerId: 1,
|
||||||
|
Username: "player1",
|
||||||
|
Content: "test1",
|
||||||
|
Timestamp: time.Now().UnixNano(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PlayerId: 2,
|
||||||
|
Username: "player2",
|
||||||
|
Content: "test2",
|
||||||
|
Timestamp: time.Now().UnixNano(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.HandleServerMessages(messages)
|
||||||
|
assert.Equal(t, 2, len(chat.messages))
|
||||||
|
assert.Equal(t, "test1", chat.messages[0].Content)
|
||||||
|
assert.Equal(t, "test2", chat.messages[1].Content)
|
||||||
|
|
||||||
|
// Test duplicate message prevention
|
||||||
|
chat.HandleServerMessages(messages)
|
||||||
|
assert.Equal(t, 2, len(chat.messages))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChat_Update(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
done := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
game, cleanup := setupTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
chat := game.Chat
|
||||||
|
|
||||||
|
// Test starting chat
|
||||||
|
testutils.SimulateKeyPress(rl.KeyT)
|
||||||
|
msg, sent := chat.Update()
|
||||||
|
assert.True(t, chat.isTyping)
|
||||||
|
assert.False(t, sent)
|
||||||
|
assert.Empty(t, msg)
|
||||||
|
|
||||||
|
// Test typing message
|
||||||
|
testutils.SimulateCharInput('h')
|
||||||
|
msg, sent = chat.Update()
|
||||||
|
testutils.SimulateCharInput('i')
|
||||||
|
msg, sent = chat.Update()
|
||||||
|
assert.Equal(t, 2, len(chat.inputBuffer))
|
||||||
|
assert.False(t, sent)
|
||||||
|
assert.Empty(t, msg)
|
||||||
|
|
||||||
|
// Test sending message
|
||||||
|
testutils.SimulateKeyPress(rl.KeyEnter)
|
||||||
|
msg, sent = chat.Update()
|
||||||
|
assert.True(t, sent)
|
||||||
|
assert.Equal(t, "hi", msg)
|
||||||
|
assert.False(t, chat.isTyping)
|
||||||
|
done <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
// Test completed successfully
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Test timed out")
|
||||||
|
}
|
||||||
|
}
|
18
game/game.go
18
game/game.go
@ -22,6 +22,7 @@ type Game struct {
|
|||||||
QuitChan chan struct{} // Channel to signal shutdown
|
QuitChan chan struct{} // Channel to signal shutdown
|
||||||
loginScreen *LoginScreen
|
loginScreen *LoginScreen
|
||||||
isLoggedIn bool
|
isLoggedIn bool
|
||||||
|
input InputHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Game {
|
func New() *Game {
|
||||||
@ -38,6 +39,7 @@ func New() *Game {
|
|||||||
Chat: NewChat(),
|
Chat: NewChat(),
|
||||||
QuitChan: make(chan struct{}),
|
QuitChan: make(chan struct{}),
|
||||||
loginScreen: NewLoginScreen(),
|
loginScreen: NewLoginScreen(),
|
||||||
|
input: &RaylibInput{},
|
||||||
}
|
}
|
||||||
game.Chat.userData = game
|
game.Chat.userData = game
|
||||||
return game
|
return game
|
||||||
@ -86,7 +88,7 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle ESC for menu
|
// Handle ESC for menu
|
||||||
if rl.IsKeyPressed(rl.KeyEscape) {
|
if g.input.IsKeyPressed(rl.KeyEscape) {
|
||||||
g.MenuOpen = !g.MenuOpen
|
g.MenuOpen = !g.MenuOpen
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -154,15 +156,16 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
|
|||||||
defer player.Unlock()
|
defer player.Unlock()
|
||||||
|
|
||||||
grid := GetMapGrid()
|
grid := GetMapGrid()
|
||||||
|
modelIndex := int(player.ID) % len(g.Models)
|
||||||
|
modelAsset := g.Models[modelIndex]
|
||||||
|
|
||||||
|
const defaultHeight = 8.0 // Default height above tile, fine tune per model in types.ModelAsset
|
||||||
playerPos := rl.Vector3{
|
playerPos := rl.Vector3{
|
||||||
X: player.PosActual.X,
|
X: player.PosActual.X,
|
||||||
Y: grid[player.PosTile.X][player.PosTile.Y].Height*types.TileHeight + 16.0,
|
Y: grid[player.PosTile.X][player.PosTile.Y].Height*types.TileHeight + defaultHeight + modelAsset.YOffset,
|
||||||
Z: player.PosActual.Z,
|
Z: player.PosActual.Z,
|
||||||
}
|
}
|
||||||
|
|
||||||
if player.ID%int32(len(g.Models)) == 0 {
|
|
||||||
modelAsset := g.Models[0]
|
|
||||||
|
|
||||||
// Check if model has animations
|
// Check if model has animations
|
||||||
if modelAsset.Animations.Idle != nil || modelAsset.Animations.Walk != nil {
|
if modelAsset.Animations.Idle != nil || modelAsset.Animations.Walk != nil {
|
||||||
if player.IsMoving && len(modelAsset.Animations.Walk) > 0 {
|
if player.IsMoving && len(modelAsset.Animations.Walk) > 0 {
|
||||||
@ -175,7 +178,6 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
|
|||||||
rl.UpdateModelAnimation(model, anim, player.AnimationFrame)
|
rl.UpdateModelAnimation(model, anim, player.AnimationFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
rl.DrawModel(model, playerPos, 16, rl.White)
|
rl.DrawModel(model, playerPos, 16, rl.White)
|
||||||
|
|
||||||
@ -325,13 +327,13 @@ func (g *Game) DrawMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check mouse hover
|
// Check mouse hover
|
||||||
mousePoint := rl.GetMousePosition()
|
mousePoint := g.input.GetMousePosition()
|
||||||
mouseHover := rl.CheckCollisionPointRec(mousePoint, buttonRect)
|
mouseHover := rl.CheckCollisionPointRec(mousePoint, buttonRect)
|
||||||
|
|
||||||
// Draw button
|
// Draw button
|
||||||
if mouseHover {
|
if mouseHover {
|
||||||
rl.DrawRectangleRec(buttonRect, rl.ColorAlpha(rl.White, 0.3))
|
rl.DrawRectangleRec(buttonRect, rl.ColorAlpha(rl.White, 0.3))
|
||||||
if rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
if g.input.IsMouseButtonPressed(toInt32(rl.MouseLeftButton)) {
|
||||||
switch item {
|
switch item {
|
||||||
case "Resume":
|
case "Resume":
|
||||||
g.MenuOpen = false
|
g.MenuOpen = false
|
||||||
|
106
game/game_test.go
Normal file
106
game/game_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/testutils"
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/types"
|
||||||
|
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGame_HandleInput(t *testing.T) {
|
||||||
|
game := New()
|
||||||
|
game.Player = &types.Player{
|
||||||
|
ID: 1,
|
||||||
|
Speed: 50.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test valid click
|
||||||
|
simulateMouseRay(rl.Ray{
|
||||||
|
Position: rl.Vector3{X: 0, Y: 10, Z: 0},
|
||||||
|
Direction: rl.Vector3{X: 0, Y: -1, Z: 0},
|
||||||
|
})
|
||||||
|
simulateMouseButton(toInt32(rl.MouseLeftButton), true)
|
||||||
|
game.HandleInput()
|
||||||
|
assert.NotEmpty(t, game.Player.TargetPath)
|
||||||
|
|
||||||
|
// Test invalid click (outside map)
|
||||||
|
simulateMouseRay(rl.Ray{
|
||||||
|
Position: rl.Vector3{X: 1000, Y: 10, Z: 1000},
|
||||||
|
Direction: rl.Vector3{X: 0, Y: -1, Z: 0},
|
||||||
|
})
|
||||||
|
simulateMouseButton(toInt32(rl.MouseLeftButton), true)
|
||||||
|
game.HandleInput()
|
||||||
|
assert.Empty(t, game.Player.TargetPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGame_UpdateCamera(t *testing.T) {
|
||||||
|
game := New()
|
||||||
|
|
||||||
|
// Test zoom limits
|
||||||
|
testutils.SimulateMouseWheel(1.0) // Zoom in
|
||||||
|
testutils.SimulateMouseWheel(1.0)
|
||||||
|
assert.GreaterOrEqual(t, cameraDistance, float32(10.0))
|
||||||
|
|
||||||
|
testutils.SimulateMouseWheel(-1.0) // Zoom out
|
||||||
|
testutils.SimulateMouseWheel(-1.0)
|
||||||
|
assert.LessOrEqual(t, cameraDistance, float32(250.0))
|
||||||
|
|
||||||
|
// Test camera rotation
|
||||||
|
originalYaw := cameraYaw
|
||||||
|
testutils.SimulateKeyDown(rl.KeyRight, true)
|
||||||
|
game.Update(0.1)
|
||||||
|
assert.Greater(t, cameraYaw, originalYaw)
|
||||||
|
|
||||||
|
// Test pitch limits
|
||||||
|
simulateKeyDown(rl.KeyUp, true)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
game.Update(0.1)
|
||||||
|
}
|
||||||
|
assert.GreaterOrEqual(t, cameraPitch, float32(20.0))
|
||||||
|
assert.LessOrEqual(t, cameraPitch, float32(85.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGame_ChatIntegration(t *testing.T) {
|
||||||
|
game := New()
|
||||||
|
game.Player = &types.Player{ID: 1}
|
||||||
|
|
||||||
|
// Test chat message to action queue
|
||||||
|
testutils.SimulateKeyPress(rl.KeyT)
|
||||||
|
game.Update(0.1)
|
||||||
|
assert.True(t, game.Chat.isTyping)
|
||||||
|
|
||||||
|
testutils.SimulateCharInput('h')
|
||||||
|
testutils.SimulateCharInput('i')
|
||||||
|
testutils.SimulateKeyPress(rl.KeyEnter)
|
||||||
|
game.Update(0.1)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(game.Player.ActionQueue))
|
||||||
|
assert.Equal(t, pb.Action_CHAT, game.Player.ActionQueue[0].Type)
|
||||||
|
assert.Equal(t, "hi", game.Player.ActionQueue[0].ChatMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGame_MenuHandling(t *testing.T) {
|
||||||
|
game := New()
|
||||||
|
|
||||||
|
// Test menu toggle
|
||||||
|
assert.False(t, game.MenuOpen)
|
||||||
|
testutils.SimulateKeyPress(rl.KeyEscape)
|
||||||
|
game.Update(0.1)
|
||||||
|
assert.True(t, game.MenuOpen)
|
||||||
|
|
||||||
|
// Test input blocking when menu is open
|
||||||
|
game.Player = &types.Player{ID: 1}
|
||||||
|
testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true)
|
||||||
|
game.Update(0.1)
|
||||||
|
assert.Empty(t, game.Player.TargetPath)
|
||||||
|
|
||||||
|
// Test menu close
|
||||||
|
testutils.SimulateKeyPress(rl.KeyEscape)
|
||||||
|
game.Update(0.1)
|
||||||
|
assert.False(t, game.MenuOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more test helpers as needed...
|
@ -3,16 +3,95 @@ package game
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/mock"
|
||||||
"gitea.boner.be/bdnugget/goonscape/types"
|
"gitea.boner.be/bdnugget/goonscape/types"
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// InputHandler abstracts raylib input functions for testing
|
||||||
|
type InputHandler interface {
|
||||||
|
IsKeyPressed(key int32) bool
|
||||||
|
IsKeyDown(key int32) bool
|
||||||
|
IsMouseButtonPressed(button int32) bool
|
||||||
|
GetMousePosition() rl.Vector2
|
||||||
|
GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray
|
||||||
|
GetMouseWheelMove() float32
|
||||||
|
GetCharPressed() rune
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaylibInput implements InputHandler using actual raylib functions
|
||||||
|
type RaylibInput struct{}
|
||||||
|
|
||||||
|
func (r *RaylibInput) IsKeyPressed(key int32) bool { return rl.IsKeyPressed(key) }
|
||||||
|
func (r *RaylibInput) IsKeyDown(key int32) bool { return rl.IsKeyDown(key) }
|
||||||
|
func (r *RaylibInput) IsMouseButtonPressed(button int32) bool {
|
||||||
|
return rl.IsMouseButtonPressed(rl.MouseButton(button))
|
||||||
|
}
|
||||||
|
func (r *RaylibInput) GetMousePosition() rl.Vector2 { return rl.GetMousePosition() }
|
||||||
|
func (r *RaylibInput) GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray {
|
||||||
|
return rl.GetMouseRay(mousePos, camera)
|
||||||
|
}
|
||||||
|
func (r *RaylibInput) GetMouseWheelMove() float32 { return rl.GetMouseWheelMove() }
|
||||||
|
func (r *RaylibInput) GetCharPressed() rune { return rl.GetCharPressed() }
|
||||||
|
|
||||||
|
// MockInput implements InputHandler using our mock functions
|
||||||
|
type MockInput struct{}
|
||||||
|
|
||||||
|
func (m *MockInput) IsKeyPressed(key int32) bool {
|
||||||
|
if mock.IsKeyPressed == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return mock.IsKeyPressed(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) IsKeyDown(key int32) bool {
|
||||||
|
if mock.IsKeyDown == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return mock.IsKeyDown(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) IsMouseButtonPressed(button int32) bool {
|
||||||
|
if mock.IsMouseButtonPressed == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return mock.IsMouseButtonPressed(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) GetMousePosition() rl.Vector2 {
|
||||||
|
if mock.GetMousePosition == nil {
|
||||||
|
return rl.Vector2{}
|
||||||
|
}
|
||||||
|
return mock.GetMousePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) GetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray {
|
||||||
|
if mock.GetMouseRay == nil {
|
||||||
|
return rl.Ray{}
|
||||||
|
}
|
||||||
|
return mock.GetMouseRay(mousePos, camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) GetMouseWheelMove() float32 {
|
||||||
|
if mock.GetMouseWheelMove == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return mock.GetMouseWheelMove()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockInput) GetCharPressed() rune {
|
||||||
|
if mock.GetCharPressed == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return mock.GetCharPressed()
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) GetTileAtMouse() (types.Tile, bool) {
|
func (g *Game) GetTileAtMouse() (types.Tile, bool) {
|
||||||
if !rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
if !g.input.IsMouseButtonPressed(toInt32(rl.MouseLeftButton)) {
|
||||||
return types.Tile{}, false
|
return types.Tile{}, false
|
||||||
}
|
}
|
||||||
mouse := rl.GetMousePosition()
|
mouse := g.input.GetMousePosition()
|
||||||
ray := rl.GetMouseRay(mouse, g.Camera)
|
ray := g.input.GetMouseRay(mouse, g.Camera)
|
||||||
|
|
||||||
for x := 0; x < types.MapWidth; x++ {
|
for x := 0; x < types.MapWidth; x++ {
|
||||||
for y := 0; y < types.MapHeight; y++ {
|
for y := 0; y < types.MapHeight; y++ {
|
||||||
|
187
game/input_test.go
Normal file
187
game/input_test.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/testutils"
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/types"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMouseInput_EdgeCases(t *testing.T) {
|
||||||
|
game, cleanup := setupTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
game.Player = &types.Player{ID: 1}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ray rl.Ray
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Click outside map bounds",
|
||||||
|
ray: rl.Ray{
|
||||||
|
Position: rl.Vector3{X: 1000, Y: 10, Z: 1000},
|
||||||
|
Direction: rl.Vector3{X: 0, Y: -1, Z: 0},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Click at map edge",
|
||||||
|
ray: rl.Ray{
|
||||||
|
Position: rl.Vector3{X: float32(types.MapWidth * types.TileSize), Y: 10, Z: 0},
|
||||||
|
Direction: rl.Vector3{X: 0, Y: -1, Z: 0},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Click on valid tile",
|
||||||
|
ray: rl.Ray{
|
||||||
|
Position: rl.Vector3{X: 32, Y: 10, Z: 32},
|
||||||
|
Direction: rl.Vector3{X: 0, Y: -1, Z: 0},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
testutils.ResetMockInput()
|
||||||
|
testutils.SimulateMouseRay(tt.ray)
|
||||||
|
testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true)
|
||||||
|
tile, clicked := game.GetTileAtMouse()
|
||||||
|
assert.Equal(t, tt.expected, clicked)
|
||||||
|
if tt.expected {
|
||||||
|
assert.NotEmpty(t, tile)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChat_InputValidation(t *testing.T) {
|
||||||
|
game, cleanup := setupTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
game.Player = &types.Player{ID: 1}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []rune
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty message",
|
||||||
|
input: []rune{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Message with only spaces",
|
||||||
|
input: []rune(" "),
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Very long message",
|
||||||
|
input: []rune(string(make([]rune, runeLimit))),
|
||||||
|
expected: string(make([]rune, runeLimit)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unicode characters",
|
||||||
|
input: []rune("Hello 世界"),
|
||||||
|
expected: "Hello 世界",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
testutils.ResetMockInput()
|
||||||
|
testutils.SimulateKeyPress(rl.KeyT)
|
||||||
|
game.Update(0.1)
|
||||||
|
|
||||||
|
for _, r := range tt.input {
|
||||||
|
testutils.SimulateCharInput(r)
|
||||||
|
game.Update(0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
testutils.SimulateKeyPress(rl.KeyEnter)
|
||||||
|
game.Update(0.1)
|
||||||
|
|
||||||
|
if tt.expected != "" {
|
||||||
|
assert.Equal(t, 1, len(game.Player.ActionQueue))
|
||||||
|
assert.Equal(t, tt.expected, game.Player.ActionQueue[0].ChatMessage)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, game.Player.ActionQueue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogin_InputValidation(t *testing.T) {
|
||||||
|
_, cleanup := setupTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
expectSuccess bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid credentials",
|
||||||
|
username: "validuser",
|
||||||
|
password: "validpass",
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty username",
|
||||||
|
username: "",
|
||||||
|
password: "password",
|
||||||
|
expectSuccess: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty password",
|
||||||
|
username: "username",
|
||||||
|
password: "",
|
||||||
|
expectSuccess: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Username too long",
|
||||||
|
username: "verylongusername123",
|
||||||
|
password: "password",
|
||||||
|
expectSuccess: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Special characters in username",
|
||||||
|
username: "user@name",
|
||||||
|
password: "password",
|
||||||
|
expectSuccess: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
login := NewLoginScreen()
|
||||||
|
testutils.ResetMockInput()
|
||||||
|
|
||||||
|
// Simulate typing username
|
||||||
|
login.focusedField = 0
|
||||||
|
for _, r := range tt.username {
|
||||||
|
testutils.SimulateCharInput(r)
|
||||||
|
login.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate typing password
|
||||||
|
login.focusedField = 1
|
||||||
|
for _, r := range tt.password {
|
||||||
|
testutils.SimulateCharInput(r)
|
||||||
|
login.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate clicking login button
|
||||||
|
testutils.SimulateMouseClick(400, 365)
|
||||||
|
_, _, _, submitted := login.Update()
|
||||||
|
assert.Equal(t, tt.expectSuccess, submitted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
80
game/login_test.go
Normal file
80
game/login_test.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/testutils"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoginScreen_Update(t *testing.T) {
|
||||||
|
login := NewLoginScreen()
|
||||||
|
|
||||||
|
// Test field focus switching
|
||||||
|
simulateMouseClick(400, 215) // Click username field
|
||||||
|
assert.Equal(t, 0, login.focusedField)
|
||||||
|
|
||||||
|
simulateMouseClick(400, 265) // Click password field
|
||||||
|
assert.Equal(t, 1, login.focusedField)
|
||||||
|
|
||||||
|
// Test input length limits
|
||||||
|
login.focusedField = 0
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
simulateCharInput('x')
|
||||||
|
}
|
||||||
|
assert.LessOrEqual(t, len(login.username), 12)
|
||||||
|
|
||||||
|
login.focusedField = 1
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
simulateCharInput('x')
|
||||||
|
}
|
||||||
|
assert.LessOrEqual(t, len(login.password), 20)
|
||||||
|
|
||||||
|
// Test mode switching
|
||||||
|
simulateMouseClick(600, 365) // Click switch mode button
|
||||||
|
assert.True(t, login.isRegistering)
|
||||||
|
simulateMouseClick(600, 365) // Click again
|
||||||
|
assert.False(t, login.isRegistering)
|
||||||
|
|
||||||
|
// Test submission
|
||||||
|
login.username = "test"
|
||||||
|
login.password = "password"
|
||||||
|
testutils.SimulateMousePosition(400, 365)
|
||||||
|
testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true)
|
||||||
|
username, password, isRegistering, submitted := login.Update()
|
||||||
|
assert.True(t, submitted)
|
||||||
|
assert.Equal(t, "test", username)
|
||||||
|
assert.Equal(t, "password", password)
|
||||||
|
assert.False(t, isRegistering)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginScreen_ErrorHandling(t *testing.T) {
|
||||||
|
login := NewLoginScreen()
|
||||||
|
|
||||||
|
// Test empty fields
|
||||||
|
login.username = ""
|
||||||
|
login.password = "test"
|
||||||
|
testutils.SimulateMousePosition(400, 365)
|
||||||
|
testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true)
|
||||||
|
_, _, _, submitted := login.Update()
|
||||||
|
assert.False(t, submitted)
|
||||||
|
assert.Contains(t, login.errorMessage, "username")
|
||||||
|
|
||||||
|
// Test special characters
|
||||||
|
login.username = "test!@#"
|
||||||
|
login.password = "password"
|
||||||
|
testutils.SimulateMousePosition(400, 365)
|
||||||
|
testutils.SimulateMouseButton(testutils.ToInt32(rl.MouseLeftButton), true)
|
||||||
|
_, _, _, submitted = login.Update()
|
||||||
|
assert.False(t, submitted)
|
||||||
|
assert.Contains(t, login.errorMessage, "invalid characters")
|
||||||
|
|
||||||
|
// Test error message display
|
||||||
|
login.SetError("Test error")
|
||||||
|
assert.Equal(t, "Test error", login.errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateMouseClick(x, y float32) {
|
||||||
|
// Implementation would depend on how raylib is mocked
|
||||||
|
}
|
15
game/mock/raylib.go
Normal file
15
game/mock/raylib.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
IsKeyPressed func(key int32) bool
|
||||||
|
IsKeyDown func(key int32) bool
|
||||||
|
IsMouseButtonPressed func(button int32) bool
|
||||||
|
GetMousePosition func() rl.Vector2
|
||||||
|
GetMouseRay func(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray
|
||||||
|
GetMouseWheelMove func() float32
|
||||||
|
GetCharPressed func() rune
|
||||||
|
)
|
141
game/test_helpers.go
Normal file
141
game/test_helpers.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/mock"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mockInput struct {
|
||||||
|
sync.Mutex
|
||||||
|
keyPressed map[int32]bool
|
||||||
|
keyDown map[int32]bool
|
||||||
|
mousePressed map[int32]bool
|
||||||
|
mousePosition rl.Vector2
|
||||||
|
mouseRay rl.Ray
|
||||||
|
mouseWheel float32
|
||||||
|
charPressed rune
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
resetMockInput()
|
||||||
|
setupMockFunctions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupMockFunctions() {
|
||||||
|
mock.IsKeyPressed = mockIsKeyPressed
|
||||||
|
mock.IsKeyDown = mockIsKeyDown
|
||||||
|
mock.IsMouseButtonPressed = mockIsMouseButtonPressed
|
||||||
|
mock.GetMousePosition = mockGetMousePosition
|
||||||
|
mock.GetMouseRay = mockGetMouseRay
|
||||||
|
mock.GetMouseWheelMove = mockGetMouseWheelMove
|
||||||
|
mock.GetCharPressed = mockGetCharPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetMockInput() {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
mockInput.keyPressed = make(map[int32]bool)
|
||||||
|
mockInput.keyDown = make(map[int32]bool)
|
||||||
|
mockInput.mousePressed = make(map[int32]bool)
|
||||||
|
mockInput.mousePosition = rl.Vector2{}
|
||||||
|
mockInput.mouseRay = rl.Ray{}
|
||||||
|
mockInput.mouseWheel = 0
|
||||||
|
mockInput.charPressed = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock input simulation functions
|
||||||
|
func simulateKeyPress(key int32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.keyPressed[key] = true
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateKeyDown(key int32, isDown bool) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.keyDown[key] = isDown
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateMouseButton(button int32, isPressed bool) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mousePressed[button] = isPressed
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateMousePosition(x, y float32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mousePosition = rl.Vector2{X: x, Y: y}
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateMouseRay(ray rl.Ray) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mouseRay = ray
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateMouseWheel(move float32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mouseWheel = move
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateCharInput(char rune) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.charPressed = char
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock raylib functions
|
||||||
|
func mockIsKeyPressed(key int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.keyPressed[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockIsKeyDown(key int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.keyDown[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockIsMouseButtonPressed(button int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mousePressed[button]
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockGetMousePosition() rl.Vector2 {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mousePosition
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockGetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mouseRay
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockGetMouseWheelMove() float32 {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mouseWheel
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockGetCharPressed() rune {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.charPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more mock implementations...
|
||||||
|
|
||||||
|
// Add this helper function
|
||||||
|
func toInt32(button rl.MouseButton) int32 {
|
||||||
|
return int32(button)
|
||||||
|
}
|
26
game/test_setup.go
Normal file
26
game/test_setup.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/mock"
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/testutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupTestEnvironment(t *testing.T) (*Game, func()) {
|
||||||
|
testutils.ResetMockInput()
|
||||||
|
testutils.SetupMockFunctions()
|
||||||
|
|
||||||
|
game := New()
|
||||||
|
game.input = &MockInput{}
|
||||||
|
game.Chat.input = &MockInput{} // Also inject mock input into chat
|
||||||
|
|
||||||
|
// Verify mock setup
|
||||||
|
if mock.IsKeyPressed == nil || mock.GetCharPressed == nil {
|
||||||
|
t.Fatal("Mock functions not properly initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return game, func() {
|
||||||
|
testutils.ResetMockInput()
|
||||||
|
}
|
||||||
|
}
|
153
game/testutils/helpers.go
Normal file
153
game/testutils/helpers.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package testutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.boner.be/bdnugget/goonscape/game/mock"
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mockInput struct {
|
||||||
|
sync.Mutex
|
||||||
|
keyPressed map[int32]bool
|
||||||
|
keyDown map[int32]bool
|
||||||
|
mousePressed map[int32]bool
|
||||||
|
mousePosition rl.Vector2
|
||||||
|
mouseRay rl.Ray
|
||||||
|
mouseWheel float32
|
||||||
|
charPressed rune
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ResetMockInput()
|
||||||
|
SetupMockFunctions()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupMockFunctions initializes mock functions
|
||||||
|
func SetupMockFunctions() {
|
||||||
|
mock.IsKeyPressed = MockIsKeyPressed
|
||||||
|
mock.IsKeyDown = MockIsKeyDown
|
||||||
|
mock.IsMouseButtonPressed = MockIsMouseButtonPressed
|
||||||
|
mock.GetMousePosition = MockGetMousePosition
|
||||||
|
mock.GetMouseRay = MockGetMouseRay
|
||||||
|
mock.GetMouseWheelMove = MockGetMouseWheelMove
|
||||||
|
mock.GetCharPressed = MockGetCharPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMockInput resets all mock input states
|
||||||
|
func ResetMockInput() {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
mockInput.keyPressed = make(map[int32]bool)
|
||||||
|
mockInput.keyDown = make(map[int32]bool)
|
||||||
|
mockInput.mousePressed = make(map[int32]bool)
|
||||||
|
mockInput.mousePosition = rl.Vector2{}
|
||||||
|
mockInput.mouseRay = rl.Ray{}
|
||||||
|
mockInput.mouseWheel = 0
|
||||||
|
mockInput.charPressed = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateKeyPress simulates a key press
|
||||||
|
func SimulateKeyPress(key int32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.keyPressed[key] = true
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateKeyDown simulates holding a key down
|
||||||
|
func SimulateKeyDown(key int32, isDown bool) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.keyDown[key] = isDown
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateMouseButton simulates a mouse button press
|
||||||
|
func SimulateMouseButton(button int32, isPressed bool) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mousePressed[button] = isPressed
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateMousePosition simulates mouse movement
|
||||||
|
func SimulateMousePosition(x, y float32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mousePosition = rl.Vector2{X: x, Y: y}
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateMouseClick simulates a mouse click at the given position
|
||||||
|
func SimulateMouseClick(x, y float32) {
|
||||||
|
SimulateMousePosition(x, y)
|
||||||
|
SimulateMouseButton(ToInt32(rl.MouseLeftButton), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateMouseRay simulates a mouse ray
|
||||||
|
func SimulateMouseRay(ray rl.Ray) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mouseRay = ray
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateMouseWheel simulates mouse wheel movement
|
||||||
|
func SimulateMouseWheel(move float32) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.mouseWheel = move
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimulateCharInput simulates character input
|
||||||
|
func SimulateCharInput(char rune) {
|
||||||
|
mockInput.Lock()
|
||||||
|
mockInput.charPressed = char
|
||||||
|
mockInput.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock raylib functions
|
||||||
|
func MockIsKeyPressed(key int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.keyPressed[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockIsKeyDown(key int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.keyDown[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockIsMouseButtonPressed(button int32) bool {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mousePressed[button]
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockGetMousePosition() rl.Vector2 {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mousePosition
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockGetMouseRay(mousePos rl.Vector2, camera rl.Camera3D) rl.Ray {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mouseRay
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockGetMouseWheelMove() float32 {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.mouseWheel
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockGetCharPressed() rune {
|
||||||
|
mockInput.Lock()
|
||||||
|
defer mockInput.Unlock()
|
||||||
|
return mockInput.charPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt32 converts MouseButton to int32
|
||||||
|
func ToInt32(button rl.MouseButton) int32 {
|
||||||
|
return int32(button)
|
||||||
|
}
|
7
go.mod
7
go.mod
@ -8,8 +8,15 @@ require (
|
|||||||
google.golang.org/protobuf v1.36.3
|
google.golang.org/protobuf v1.36.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
)
|
)
|
||||||
|
9
go.sum
9
go.sum
@ -1,12 +1,21 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw=
|
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw=
|
||||||
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
|
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
Binary file not shown.
Binary file not shown.
BIN
resources/models/coomer/Animation_Confused_Scratch_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Confused_Scratch_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Idle_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Idle_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Running_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Running_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Unsteady_Walk_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Unsteady_Walk_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/Animation_Walking_withSkin.glb
Normal file
BIN
resources/models/coomer/Animation_Walking_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/idle_notransy.glb
Normal file
BIN
resources/models/coomer/idle_notransy.glb
Normal file
Binary file not shown.
BIN
resources/models/coomer/unsteadywalk_notransy.glb
Normal file
BIN
resources/models/coomer/unsteadywalk_notransy.glb
Normal file
Binary file not shown.
BIN
resources/models/coomerAnim.zip
Normal file
BIN
resources/models/coomerAnim.zip
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
245
segfault.txt
245
segfault.txt
@ -1,245 +0,0 @@
|
|||||||
INFO: Initializing raylib 5.5
|
|
||||||
INFO: Platform backend: DESKTOP (GLFW)
|
|
||||||
INFO: Supported raylib modules:
|
|
||||||
INFO: > rcore:..... loaded (mandatory)
|
|
||||||
INFO: > rlgl:...... loaded (mandatory)
|
|
||||||
INFO: > rshapes:... loaded (optional)
|
|
||||||
INFO: > rtextures:. loaded (optional)
|
|
||||||
INFO: > rtext:..... loaded (optional)
|
|
||||||
INFO: > rmodels:... loaded (optional)
|
|
||||||
INFO: > raudio:.... loaded (optional)
|
|
||||||
INFO: DISPLAY: Device initialized successfully
|
|
||||||
INFO: > Display size: 1920 x 1080
|
|
||||||
INFO: > Screen size: 1024 x 768
|
|
||||||
INFO: > Render size: 1024 x 768
|
|
||||||
INFO: > Viewport offsets: 0, 0
|
|
||||||
INFO: GLAD: OpenGL extensions loaded successfully
|
|
||||||
INFO: GL: Supported extensions count: 390
|
|
||||||
INFO: GL: OpenGL device information:
|
|
||||||
INFO: > Vendor: NVIDIA Corporation
|
|
||||||
INFO: > Renderer: NVIDIA GeForce GTX 1070/PCIe/SSE2
|
|
||||||
INFO: > Version: 3.3.0 NVIDIA 565.77
|
|
||||||
INFO: > GLSL: 3.30 NVIDIA via Cg compiler
|
|
||||||
INFO: GL: VAO extension detected, VAO functions loaded successfully
|
|
||||||
INFO: GL: NPOT textures extension detected, full NPOT textures supported
|
|
||||||
INFO: GL: DXT compressed textures supported
|
|
||||||
INFO: GL: ETC2/EAC compressed textures supported
|
|
||||||
INFO: PLATFORM: DESKTOP (GLFW - X11): Initialized successfully
|
|
||||||
INFO: TEXTURE: [ID 1] Texture loaded successfully (1x1 | R8G8B8A8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 1] Default texture loaded successfully
|
|
||||||
INFO: SHADER: [ID 1] Vertex shader compiled successfully
|
|
||||||
INFO: SHADER: [ID 2] Fragment shader compiled successfully
|
|
||||||
INFO: SHADER: [ID 3] Program shader loaded successfully
|
|
||||||
INFO: SHADER: [ID 3] Default shader loaded successfully
|
|
||||||
INFO: RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)
|
|
||||||
INFO: RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)
|
|
||||||
INFO: RLGL: Default OpenGL state initialized successfully
|
|
||||||
INFO: TEXTURE: [ID 2] Texture loaded successfully (128x128 | GRAY_ALPHA | 1 mipmaps)
|
|
||||||
INFO: FONT: Default font loaded successfully (224 glyphs)
|
|
||||||
INFO: SYSTEM: Working Directory: /home/bd/Projects/go/goonscape
|
|
||||||
INFO: AUDIO: Device initialized successfully
|
|
||||||
INFO: > Backend: miniaudio | PulseAudio
|
|
||||||
INFO: > Format: 32-bit IEEE Floating Point -> 32-bit Signed Integer
|
|
||||||
INFO: > Channels: 2 -> 2
|
|
||||||
INFO: > Sample rate: 48000 -> 48000
|
|
||||||
INFO: > Periods size: 3600
|
|
||||||
INFO: TIMER: Target time per frame: 16.667 milliseconds
|
|
||||||
INFO: FILEIO: [resources/models/goonion.obj] Text file loaded successfully
|
|
||||||
INFO: FILEIO: [goonion.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 3] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: VAO: [ID 2] Mesh uploaded successfully to VRAM (GPU)
|
|
||||||
WARNING: VAO: [ID 2] Trying to re-load an already loaded mesh
|
|
||||||
INFO: FILEIO: [resources/models/goonion.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 4] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: FILEIO: [resources/models/biped/Animation_Unsteady_Walk_withSkin.glb] File loaded successfully
|
|
||||||
INFO: MODEL: [resources/models/biped/Animation_Unsteady_Walk_withSkin.glb] Loaded animation: Armature|Unsteady_Walk|baselayer (177 frames, 3.000000s)
|
|
||||||
INFO: FILEIO: [resources/models/coomer.obj] Text file loaded successfully
|
|
||||||
INFO: FILEIO: [coomer.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 5] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: VAO: [ID 3] Mesh uploaded successfully to VRAM (GPU)
|
|
||||||
WARNING: VAO: [ID 3] Trying to re-load an already loaded mesh
|
|
||||||
INFO: FILEIO: [resources/models/coomer.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 6] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: FILEIO: [resources/models/shreke.obj] Text file loaded successfully
|
|
||||||
INFO: FILEIO: [shreke.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 7] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: VAO: [ID 4] Mesh uploaded successfully to VRAM (GPU)
|
|
||||||
WARNING: VAO: [ID 4] Trying to re-load an already loaded mesh
|
|
||||||
INFO: FILEIO: [resources/models/shreke.png] File loaded successfully
|
|
||||||
INFO: IMAGE: Data loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: TEXTURE: [ID 8] Texture loaded successfully (2048x2048 | R8G8B8 | 1 mipmaps)
|
|
||||||
INFO: STREAM: Initialized successfully (48000 Hz, 32 bit, Stereo)
|
|
||||||
INFO: FILEIO: [resources/audio/GoonScape2.mp3] Music file loaded successfully
|
|
||||||
INFO: > Sample rate: 48000 Hz
|
|
||||||
INFO: > Sample size: 32 bits
|
|
||||||
INFO: > Channels: 2 (Stereo)
|
|
||||||
INFO: > Total frames: 3487104
|
|
||||||
2025/01/20 12:28:12 Connected to server. Authenticating...
|
|
||||||
2025/01/20 12:28:12 Successfully authenticated with player ID: 6
|
|
||||||
Clicked: 2, 1
|
|
||||||
Path found: [{5 1 1 true} {4 1 5 true} {3 1 4 true} {2 1 3 true}]
|
|
||||||
SIGSEGV: segmentation violation
|
|
||||||
PC=0x69da90 m=0 sigcode=1 addr=0x0
|
|
||||||
signal arrived during cgo execution
|
|
||||||
|
|
||||||
goroutine 1 gp=0xc0000061c0 m=0 mp=0x991ca0 [syscall, locked to thread]:
|
|
||||||
runtime.cgocall(0x5ef7f0, 0xc00002b858)
|
|
||||||
/usr/lib/go/src/runtime/cgocall.go:167 +0x4b fp=0xc00002b830 sp=0xc00002b7f8 pc=0x46a0ab
|
|
||||||
github.com/gen2brain/raylib-go/raylib._Cfunc_UpdateModelAnimation({{0x3f800000, 0x0, 0x0, 0x0, 0x0, 0x3f800000, 0x0, 0x0, 0x0, 0x0, ...}, ...}, ...)
|
|
||||||
_cgo_gotypes.go:7298 +0x45 fp=0xc00002b858 sp=0xc00002b830 pc=0x4db8c5
|
|
||||||
github.com/gen2brain/raylib-go/raylib.UpdateModelAnimation.func1(0x47731a?, 0x1000000?, 0x0)
|
|
||||||
/home/bd/.cache/go/mod/github.com/gen2brain/raylib-go/raylib@v0.0.0-20250109172833-6dbba4f81a9b/rmodels.go:613 +0x1a5 fp=0xc00002ba80 sp=0xc00002b858 pc=0x4dd265
|
|
||||||
github.com/gen2brain/raylib-go/raylib.UpdateModelAnimation({{0x3f800000, 0x0, 0x0, 0x0, 0x0, 0x3f800000, 0x0, 0x0, 0x0, 0x0, ...}, ...}, ...)
|
|
||||||
/home/bd/.cache/go/mod/github.com/gen2brain/raylib-go/raylib@v0.0.0-20250109172833-6dbba4f81a9b/rmodels.go:613 +0x25 fp=0xc00002baa8 sp=0xc00002ba80 pc=0x4dd085
|
|
||||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).DrawPlayer(0xc0001a4000, 0xc0001de160, {{0x3f800000, 0x0, 0x0, 0x0, 0x0, 0x3f800000, 0x0, 0x0, ...}, ...})
|
|
||||||
/home/bd/Projects/go/goonscape/game/game.go:172 +0x2d5 fp=0xc00002bc58 sp=0xc00002baa8 pc=0x5c5c95
|
|
||||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).Render(0xc0001a4000)
|
|
||||||
/home/bd/Projects/go/goonscape/game/game.go:221 +0x147 fp=0xc00002be00 sp=0xc00002bc58 pc=0x5c61e7
|
|
||||||
main.main()
|
|
||||||
/home/bd/Projects/go/goonscape/main.go:55 +0x4ac fp=0xc00002bf50 sp=0xc00002be00 pc=0x5c924c
|
|
||||||
runtime.main()
|
|
||||||
/usr/lib/go/src/runtime/proc.go:272 +0x28b fp=0xc00002bfe0 sp=0xc00002bf50 pc=0x43c42b
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc00002bfe8 sp=0xc00002bfe0 pc=0x4776c1
|
|
||||||
|
|
||||||
goroutine 2 gp=0xc000006c40 m=nil [force gc (idle)]:
|
|
||||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000054fa8 sp=0xc000054f88 pc=0x46ff0e
|
|
||||||
runtime.goparkunlock(...)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:430
|
|
||||||
runtime.forcegchelper()
|
|
||||||
/usr/lib/go/src/runtime/proc.go:337 +0xb3 fp=0xc000054fe0 sp=0xc000054fa8 pc=0x43c773
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc000054fe8 sp=0xc000054fe0 pc=0x4776c1
|
|
||||||
created by runtime.init.7 in goroutine 1
|
|
||||||
/usr/lib/go/src/runtime/proc.go:325 +0x1a
|
|
||||||
|
|
||||||
goroutine 3 gp=0xc000007180 m=nil [GC sweep wait]:
|
|
||||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000055780 sp=0xc000055760 pc=0x46ff0e
|
|
||||||
runtime.goparkunlock(...)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:430
|
|
||||||
runtime.bgsweep(0xc00007e000)
|
|
||||||
/usr/lib/go/src/runtime/mgcsweep.go:277 +0x94 fp=0xc0000557c8 sp=0xc000055780 pc=0x427394
|
|
||||||
runtime.gcenable.gowrap1()
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:204 +0x25 fp=0xc0000557e0 sp=0xc0000557c8 pc=0x41baa5
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000557e8 sp=0xc0000557e0 pc=0x4776c1
|
|
||||||
created by runtime.gcenable in goroutine 1
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:204 +0x66
|
|
||||||
|
|
||||||
goroutine 4 gp=0xc000007340 m=nil [GC scavenge wait]:
|
|
||||||
runtime.gopark(0xc00007e000?, 0x7b0098?, 0x1?, 0x0?, 0xc000007340?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000055f78 sp=0xc000055f58 pc=0x46ff0e
|
|
||||||
runtime.goparkunlock(...)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:430
|
|
||||||
runtime.(*scavengerState).park(0x990ee0)
|
|
||||||
/usr/lib/go/src/runtime/mgcscavenge.go:425 +0x49 fp=0xc000055fa8 sp=0xc000055f78 pc=0x424dc9
|
|
||||||
runtime.bgscavenge(0xc00007e000)
|
|
||||||
/usr/lib/go/src/runtime/mgcscavenge.go:653 +0x3c fp=0xc000055fc8 sp=0xc000055fa8 pc=0x42533c
|
|
||||||
runtime.gcenable.gowrap2()
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:205 +0x25 fp=0xc000055fe0 sp=0xc000055fc8 pc=0x41ba45
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc000055fe8 sp=0xc000055fe0 pc=0x4776c1
|
|
||||||
created by runtime.gcenable in goroutine 1
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:205 +0xa5
|
|
||||||
|
|
||||||
goroutine 5 gp=0xc000007c00 m=nil [finalizer wait]:
|
|
||||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000056620 sp=0xc000056600 pc=0x46ff0e
|
|
||||||
runtime.runfinq()
|
|
||||||
/usr/lib/go/src/runtime/mfinal.go:193 +0x107 fp=0xc0000567e0 sp=0xc000056620 pc=0x41ab27
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000567e8 sp=0xc0000567e0 pc=0x4776c1
|
|
||||||
created by runtime.createfing in goroutine 1
|
|
||||||
/usr/lib/go/src/runtime/mfinal.go:163 +0x3d
|
|
||||||
|
|
||||||
goroutine 6 gp=0xc000007dc0 m=nil [chan receive]:
|
|
||||||
runtime.gopark(0x6f63a0?, 0x0?, 0x48?, 0x69?, 0x986948?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000054718 sp=0xc0000546f8 pc=0x46ff0e
|
|
||||||
runtime.chanrecv(0xc00008a0e0, 0x0, 0x1)
|
|
||||||
/usr/lib/go/src/runtime/chan.go:639 +0x41c fp=0xc000054790 sp=0xc000054718 pc=0x40bb1c
|
|
||||||
runtime.chanrecv1(0x43c5c0?, 0xc000054776?)
|
|
||||||
/usr/lib/go/src/runtime/chan.go:489 +0x12 fp=0xc0000547b8 sp=0xc000054790 pc=0x40b6f2
|
|
||||||
runtime.unique_runtime_registerUniqueMapCleanup.func1(...)
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:1781
|
|
||||||
runtime.unique_runtime_registerUniqueMapCleanup.gowrap1()
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:1784 +0x2f fp=0xc0000547e0 sp=0xc0000547b8 pc=0x41eacf
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000547e8 sp=0xc0000547e0 pc=0x4776c1
|
|
||||||
created by unique.runtime_registerUniqueMapCleanup in goroutine 1
|
|
||||||
/usr/lib/go/src/runtime/mgc.go:1779 +0x96
|
|
||||||
|
|
||||||
goroutine 20 gp=0xc0001b8000 m=nil [IO wait]:
|
|
||||||
runtime.gopark(0xc0000bdad0?, 0x55fcb8?, 0x8?, 0xaa?, 0xb?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc0000bda68 sp=0xc0000bda48 pc=0x46ff0e
|
|
||||||
runtime.netpollblock(0x4b0778?, 0x408f46?, 0x0?)
|
|
||||||
/usr/lib/go/src/runtime/netpoll.go:575 +0xf7 fp=0xc0000bdaa0 sp=0xc0000bda68 pc=0x434fd7
|
|
||||||
internal/poll.runtime_pollWait(0x7094ad4a5680, 0x72)
|
|
||||||
/usr/lib/go/src/runtime/netpoll.go:351 +0x85 fp=0xc0000bdac0 sp=0xc0000bdaa0 pc=0x46f205
|
|
||||||
internal/poll.(*pollDesc).wait(0xc0001e2080?, 0xc0000d2000?, 0x0)
|
|
||||||
/usr/lib/go/src/internal/poll/fd_poll_runtime.go:84 +0x27 fp=0xc0000bdae8 sp=0xc0000bdac0 pc=0x4bf127
|
|
||||||
internal/poll.(*pollDesc).waitRead(...)
|
|
||||||
/usr/lib/go/src/internal/poll/fd_poll_runtime.go:89
|
|
||||||
internal/poll.(*FD).Read(0xc0001e2080, {0xc0000d2000, 0x1000, 0x1000})
|
|
||||||
/usr/lib/go/src/internal/poll/fd_unix.go:165 +0x27a fp=0xc0000bdb80 sp=0xc0000bdae8 pc=0x4bfa5a
|
|
||||||
net.(*netFD).Read(0xc0001e2080, {0xc0000d2000?, 0xc0000961e0?, 0x2710?})
|
|
||||||
/usr/lib/go/src/net/fd_posix.go:55 +0x25 fp=0xc0000bdbc8 sp=0xc0000bdb80 pc=0x5ae0c5
|
|
||||||
net.(*conn).Read(0xc0001b4030, {0xc0000d2000?, 0x10000000006?, 0xc0001a7b00?})
|
|
||||||
/usr/lib/go/src/net/net.go:189 +0x45 fp=0xc0000bdc10 sp=0xc0000bdbc8 pc=0x5b6345
|
|
||||||
net.(*TCPConn).Read(0x7094f4718a68?, {0xc0000d2000?, 0xedf202a1b?, 0x990e60?})
|
|
||||||
<autogenerated>:1 +0x25 fp=0xc0000bdc40 sp=0xc0000bdc10 pc=0x5c0c05
|
|
||||||
bufio.(*Reader).Read(0xc0000802a0, {0xc0001a7b0c, 0x4, 0x471809?})
|
|
||||||
/usr/lib/go/src/bufio/bufio.go:241 +0x197 fp=0xc0000bdc78 sp=0xc0000bdc40 pc=0x4d93b7
|
|
||||||
io.ReadAtLeast({0x7b2640, 0xc0000802a0}, {0xc0001a7b0c, 0x4, 0x4}, 0x4)
|
|
||||||
/usr/lib/go/src/io/io.go:335 +0x90 fp=0xc0000bdcc0 sp=0xc0000bdc78 pc=0x4aaa10
|
|
||||||
io.ReadFull(...)
|
|
||||||
/usr/lib/go/src/io/io.go:354
|
|
||||||
gitea.boner.be/bdnugget/goonscape/network.HandleServerCommunication({0x7b4bb8, 0xc0001b4030}, 0x6, 0xc0001de160, 0xc000194000, 0xc0001a0000)
|
|
||||||
/home/bd/Projects/go/goonscape/network/network.go:166 +0x412 fp=0xc0000bdfa0 sp=0xc0000bdcc0 pc=0x5c27b2
|
|
||||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).Update.gowrap1()
|
|
||||||
/home/bd/Projects/go/goonscape/game/game.go:80 +0x33 fp=0xc0000bdfe0 sp=0xc0000bdfa0 pc=0x5c57d3
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000bdfe8 sp=0xc0000bdfe0 pc=0x4776c1
|
|
||||||
created by gitea.boner.be/bdnugget/goonscape/game.(*Game).Update in goroutine 1
|
|
||||||
/home/bd/Projects/go/goonscape/game/game.go:80 +0x45e
|
|
||||||
|
|
||||||
goroutine 7 gp=0xc0000c6380 m=nil [select]:
|
|
||||||
runtime.gopark(0xc000050f80?, 0x2?, 0x0?, 0x0?, 0xc000050f10?)
|
|
||||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000050db0 sp=0xc000050d90 pc=0x46ff0e
|
|
||||||
runtime.selectgo(0xc000050f80, 0xc000050f0c, 0x0?, 0x0, 0x0?, 0x1)
|
|
||||||
/usr/lib/go/src/runtime/select.go:335 +0x7a5 fp=0xc000050ed8 sp=0xc000050db0 pc=0x44e2a5
|
|
||||||
gitea.boner.be/bdnugget/goonscape/network.HandleServerCommunication.func1()
|
|
||||||
/home/bd/Projects/go/goonscape/network/network.go:108 +0xb2 fp=0xc000050fe0 sp=0xc000050ed8 pc=0x5c3492
|
|
||||||
runtime.goexit({})
|
|
||||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc000050fe8 sp=0xc000050fe0 pc=0x4776c1
|
|
||||||
created by gitea.boner.be/bdnugget/goonscape/network.HandleServerCommunication in goroutine 20
|
|
||||||
/home/bd/Projects/go/goonscape/network/network.go:106 +0x3bd
|
|
||||||
|
|
||||||
rax 0x0
|
|
||||||
rbx 0x0
|
|
||||||
rcx 0x0
|
|
||||||
rdx 0x0
|
|
||||||
rdi 0x13ee9800
|
|
||||||
rsi 0xb1
|
|
||||||
rbp 0x13bd6580
|
|
||||||
rsp 0x7ffc9d661890
|
|
||||||
r8 0x140baba0
|
|
||||||
r9 0x0
|
|
||||||
r10 0x0
|
|
||||||
r11 0x0
|
|
||||||
r12 0x13bd1f20
|
|
||||||
r13 0x744e4
|
|
||||||
r14 0x4d898
|
|
||||||
r15 0x26c4c
|
|
||||||
rip 0x69da90
|
|
||||||
rflags 0x10246
|
|
||||||
cs 0x33
|
|
||||||
fs 0x0
|
|
||||||
gs 0x0
|
|
||||||
exit status 2
|
|
@ -49,6 +49,7 @@ type ModelAsset struct {
|
|||||||
Animation []rl.ModelAnimation // Keep this for compatibility
|
Animation []rl.ModelAnimation // Keep this for compatibility
|
||||||
AnimFrames int32 // Keep this for compatibility
|
AnimFrames int32 // Keep this for compatibility
|
||||||
Animations AnimationSet // New field for organized animations
|
Animations AnimationSet // New field for organized animations
|
||||||
|
YOffset float32 // Additional height offset (added to default 8.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatMessage struct {
|
type ChatMessage struct {
|
||||||
|
Reference in New Issue
Block a user