|
|
|
@ -5,10 +5,10 @@ import (
|
|
|
|
|
"image"
|
|
|
|
|
"image/color"
|
|
|
|
|
"math"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"code.rocketnine.space/tslocum/boxbrawl/component"
|
|
|
|
|
"code.rocketnine.space/tslocum/boxbrawl/world"
|
|
|
|
|
"code.rocketnine.space/tslocum/etk"
|
|
|
|
|
"code.rocketnine.space/tslocum/gohan"
|
|
|
|
|
"github.com/assemblaj/ggpo"
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
@ -20,21 +20,25 @@ const uiStartPrompt = `BOX BRAWL`
|
|
|
|
|
const uiBrowserIntro = `Click anywhere to enable keyboard input.`
|
|
|
|
|
const uiComputerPrompt = `Press <Enter> to play against the computer.`
|
|
|
|
|
const uiHostPrompt = `Press <H> to host a match against a remote player.`
|
|
|
|
|
const uiHostInfoPrompt = `Type your opponent's IP address and port (address:port) to host a match.`
|
|
|
|
|
const uiHostInfoPrompt = `Type your opponent's IP address and port
|
|
|
|
|
(address:port) to host a match.`
|
|
|
|
|
const uiHostStartPrompt = `Press <Enter> to start hosting.`
|
|
|
|
|
const uiRemotePrompt = `Type your opponent's IP address and port (address:port) to join a match.`
|
|
|
|
|
const uiRemotePrompt = `Type your opponent's IP address and port
|
|
|
|
|
(address:port) to join a match.`
|
|
|
|
|
const uiConnectPrompt = `Press <Enter> to connect.`
|
|
|
|
|
const uiBrowserPrompt = `Playing against remote players is unavailable in the browser version.
|
|
|
|
|
|
|
|
|
|
Download Box Brawl for Windows or Linux to play against remote players.`
|
|
|
|
|
const uiHostListeningPrompt = `Waiting for opponent to connect from %s...`
|
|
|
|
|
const uiHostListeningPrompt = `Waiting for opponent to connect
|
|
|
|
|
from %s...`
|
|
|
|
|
const uiClientConnectingPrompt = `Connecting to %s...`
|
|
|
|
|
const controlsInfo = `<H> Punch <J> Kick <K> Block <L>+<Dir> Taunt`
|
|
|
|
|
|
|
|
|
|
type UISystem struct {
|
|
|
|
|
*component.Once
|
|
|
|
|
|
|
|
|
|
initialized bool
|
|
|
|
|
buffer *etk.Text
|
|
|
|
|
buffer string
|
|
|
|
|
updateTicks int
|
|
|
|
|
|
|
|
|
|
tmpImg *ebiten.Image
|
|
|
|
@ -43,21 +47,9 @@ type UISystem struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UISystem) initialize() {
|
|
|
|
|
u.buffer = etk.NewText("")
|
|
|
|
|
u.updateBuffer()
|
|
|
|
|
|
|
|
|
|
inputDemo := etk.NewFlex()
|
|
|
|
|
inputDemo.SetVertical(true)
|
|
|
|
|
|
|
|
|
|
inputDemo.AddChild(u.buffer)
|
|
|
|
|
|
|
|
|
|
etk.SetRoot(inputDemo)
|
|
|
|
|
etk.Layout(world.InternalScreenWidth, world.InternalScreenHeight)
|
|
|
|
|
|
|
|
|
|
u.tmpImg = ebiten.NewImage(250, 100)
|
|
|
|
|
|
|
|
|
|
u.tmpImg = ebiten.NewImage(world.InternalScreenWidth/2, world.InternalScreenHeight/2)
|
|
|
|
|
u.hitboxImg = ebiten.NewImage(32, 32)
|
|
|
|
|
|
|
|
|
|
u.updateBuffer()
|
|
|
|
|
u.initialized = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -68,6 +60,7 @@ func (u *UISystem) updateBuffer() {
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+uiBrowserIntro)...)
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+uiComputerPrompt)...)
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+uiBrowserPrompt)...)
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+controlsInfo)...)
|
|
|
|
|
} else if world.ConnectionActive {
|
|
|
|
|
if world.ConnectPromptHost {
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+fmt.Sprintf(uiHostListeningPrompt, world.ConnectPromptText))...)
|
|
|
|
@ -90,10 +83,10 @@ func (u *UISystem) updateBuffer() {
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+uiHostPrompt)...)
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+uiRemotePrompt)...)
|
|
|
|
|
}
|
|
|
|
|
prompt = append(prompt, []byte("\n\n"+controlsInfo)...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u.buffer.Clear()
|
|
|
|
|
u.buffer.Write(prompt)
|
|
|
|
|
u.buffer = string(prompt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UISystem) Update(e gohan.Entity) error {
|
|
|
|
@ -180,8 +173,7 @@ func (u *UISystem) Update(e gohan.Entity) error {
|
|
|
|
|
u.updateTicks = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return etk.Update()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UISystem) drawBox(screen *ebiten.Image, fillColor color.Color, r image.Rectangle) {
|
|
|
|
@ -208,10 +200,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if world.ConnectPromptVisible {
|
|
|
|
|
err := etk.Draw(screen)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
u.drawCenteredText(screen, u.buffer)
|
|
|
|
|
} else if world.Debug > 1 { // In-game and debug mode is enabled
|
|
|
|
|
var p, o *component.Player
|
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
@ -359,3 +348,35 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UISystem) drawCenteredText(screen *ebiten.Image, text string) {
|
|
|
|
|
const (
|
|
|
|
|
charWidth = 6
|
|
|
|
|
charHeight = 16
|
|
|
|
|
textScale = 2
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
lines := strings.Split(text, "\n")
|
|
|
|
|
var w int
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
l := len(line)
|
|
|
|
|
if l > w {
|
|
|
|
|
w = l
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u.tmpImg.Clear()
|
|
|
|
|
for i, line := range lines {
|
|
|
|
|
x, y := ((w-len(line))/2)*charWidth, i*charHeight
|
|
|
|
|
ebitenutil.DebugPrintAt(u.tmpImg, line, x, y)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width := float64(w) * charWidth * textScale
|
|
|
|
|
height := float64(len(lines) * charHeight * textScale)
|
|
|
|
|
|
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
|
|
|
op.GeoM.Reset()
|
|
|
|
|
op.GeoM.Scale(textScale, textScale)
|
|
|
|
|
op.GeoM.Translate(world.InternalScreenWidth/2-width/2, world.InternalScreenHeight/2-height/2)
|
|
|
|
|
screen.DrawImage(u.tmpImg, op)
|
|
|
|
|
}
|
|
|
|
|