Add chat
This commit is contained in:
174
game/chat.go
Normal file
174
game/chat.go
Normal file
@ -0,0 +1,174 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
const (
|
||||
maxMessages = 50
|
||||
chatWindowWidth = 400
|
||||
chatHeight = 200
|
||||
messageHeight = 20
|
||||
inputHeight = 30
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
messages []types.ChatMessage
|
||||
inputBuffer []rune
|
||||
isTyping bool
|
||||
cursorPos int
|
||||
scrollOffset int
|
||||
}
|
||||
|
||||
func NewChat() *Chat {
|
||||
return &Chat{
|
||||
messages: make([]types.ChatMessage, 0, maxMessages),
|
||||
inputBuffer: make([]rune, 0, 256),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chat) AddMessage(playerID int32, content string) {
|
||||
msg := types.ChatMessage{
|
||||
PlayerID: playerID,
|
||||
Content: content,
|
||||
Time: time.Now(),
|
||||
}
|
||||
|
||||
if len(c.messages) >= maxMessages {
|
||||
c.messages = c.messages[1:]
|
||||
}
|
||||
c.messages = append(c.messages, msg)
|
||||
c.scrollOffset = 0 // Reset scroll position for new messages
|
||||
}
|
||||
|
||||
func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||
// Convert protobuf messages to our local type
|
||||
for _, msg := range messages {
|
||||
localMsg := types.ChatMessage{
|
||||
PlayerID: msg.PlayerId,
|
||||
Content: msg.Content,
|
||||
Time: time.Unix(0, msg.Timestamp),
|
||||
}
|
||||
|
||||
// Only add if it's not already in our history
|
||||
if len(c.messages) == 0 || c.messages[len(c.messages)-1].Time.UnixNano() < msg.Timestamp {
|
||||
if len(c.messages) >= maxMessages {
|
||||
c.messages = c.messages[1:]
|
||||
}
|
||||
c.messages = append(c.messages, localMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chat) Draw(screenWidth, screenHeight int32) {
|
||||
// Draw chat window background
|
||||
chatX := float32(10)
|
||||
chatY := float32(screenHeight - chatHeight - 10)
|
||||
rl.DrawRectangle(int32(chatX), int32(chatY), chatWindowWidth, chatHeight, rl.ColorAlpha(rl.Black, 0.5))
|
||||
|
||||
// Draw messages
|
||||
messageY := chatY + 5
|
||||
startIdx := len(c.messages) - 1 - c.scrollOffset
|
||||
endIdx := max(0, startIdx-int((chatHeight-inputHeight)/messageHeight))
|
||||
|
||||
for i := startIdx; i >= endIdx && i >= 0; i-- {
|
||||
msg := c.messages[i]
|
||||
text := fmt.Sprintf("[%d]: %s", msg.PlayerID, msg.Content)
|
||||
rl.DrawText(text, int32(chatX)+5, int32(messageY), 20, rl.White)
|
||||
messageY += messageHeight
|
||||
}
|
||||
|
||||
// Draw input field
|
||||
inputY := chatY + float32(chatHeight-inputHeight)
|
||||
rl.DrawRectangle(int32(chatX), int32(inputY), chatWindowWidth, inputHeight, rl.ColorAlpha(rl.White, 0.3))
|
||||
if c.isTyping {
|
||||
inputText := string(c.inputBuffer)
|
||||
rl.DrawText(inputText, int32(chatX)+5, int32(inputY)+5, 20, rl.White)
|
||||
|
||||
// Draw cursor
|
||||
cursorX := rl.MeasureText(inputText[:c.cursorPos], 20)
|
||||
rl.DrawRectangle(int32(chatX)+5+cursorX, int32(inputY)+5, 2, 20, rl.White)
|
||||
} else {
|
||||
rl.DrawText("Press T to chat", int32(chatX)+5, int32(inputY)+5, 20, rl.Gray)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chat) Update() (string, bool) {
|
||||
// Handle scrolling with mouse wheel when not typing
|
||||
if !c.isTyping {
|
||||
wheelMove := rl.GetMouseWheelMove()
|
||||
if wheelMove != 0 {
|
||||
maxScroll := max(0, len(c.messages)-int((chatHeight-inputHeight)/messageHeight))
|
||||
c.scrollOffset = clamp(c.scrollOffset-int(wheelMove), 0, maxScroll)
|
||||
}
|
||||
|
||||
if rl.IsKeyPressed(rl.KeyT) {
|
||||
c.isTyping = true
|
||||
return "", false
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
key := rl.GetCharPressed()
|
||||
for key > 0 {
|
||||
if len(c.inputBuffer) < 256 {
|
||||
c.inputBuffer = append(c.inputBuffer[:c.cursorPos], append([]rune{key}, c.inputBuffer[c.cursorPos:]...)...)
|
||||
c.cursorPos++
|
||||
}
|
||||
key = rl.GetCharPressed()
|
||||
}
|
||||
|
||||
if rl.IsKeyPressed(rl.KeyEnter) {
|
||||
if len(c.inputBuffer) > 0 {
|
||||
message := string(c.inputBuffer)
|
||||
c.inputBuffer = c.inputBuffer[:0]
|
||||
c.cursorPos = 0
|
||||
c.isTyping = false
|
||||
return message, true
|
||||
}
|
||||
c.isTyping = false
|
||||
}
|
||||
|
||||
if rl.IsKeyPressed(rl.KeyEscape) {
|
||||
c.inputBuffer = c.inputBuffer[:0]
|
||||
c.cursorPos = 0
|
||||
c.isTyping = false
|
||||
}
|
||||
|
||||
if rl.IsKeyPressed(rl.KeyBackspace) && c.cursorPos > 0 {
|
||||
c.inputBuffer = append(c.inputBuffer[:c.cursorPos-1], c.inputBuffer[c.cursorPos:]...)
|
||||
c.cursorPos--
|
||||
}
|
||||
|
||||
if rl.IsKeyPressed(rl.KeyLeft) && c.cursorPos > 0 {
|
||||
c.cursorPos--
|
||||
}
|
||||
if rl.IsKeyPressed(rl.KeyRight) && c.cursorPos < len(c.inputBuffer) {
|
||||
c.cursorPos++
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Add helper functions
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func clamp(value, min, max int) int {
|
||||
if value < min {
|
||||
return min
|
||||
}
|
||||
if value > max {
|
||||
return max
|
||||
}
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user