Add local match support

This commit is contained in:
Trevor Slocum 2023-01-27 14:06:20 -08:00
parent 0cd961dd83
commit cf1be8e89d
4 changed files with 105 additions and 44 deletions

View File

@ -95,7 +95,9 @@ func (g *Game) Layout(_, _ int) (screenWidth, screenHeight int) {
func (g *Game) startLocalGame() {
log.Println("Playing against the computer")
// TODO
world.Local = true
world.ConnectionActive = true
world.ConnectPromptVisible = false
}
func (g *Game) startNetworkGame() {
@ -191,28 +193,28 @@ func (g *Game) ReadInputs() InputBits {
var in InputBits
if ebiten.IsKeyPressed(ebiten.KeyArrowUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
in.setButton(ButtonUp)
in.setButtonOn(ButtonUp)
}
if ebiten.IsKeyPressed(ebiten.KeyArrowDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
in.setButton(ButtonDown)
in.setButtonOn(ButtonDown)
}
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
in.setButton(ButtonLeft)
in.setButtonOn(ButtonLeft)
}
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
in.setButton(ButtonRight)
in.setButtonOn(ButtonRight)
}
if ebiten.IsKeyPressed(ebiten.KeyH) {
in.setButton(ButtonPunch)
in.setButtonOn(ButtonPunch)
}
if ebiten.IsKeyPressed(ebiten.KeyL) {
in.setButton(ButtonTaunt)
in.setButtonOn(ButtonTaunt)
}
if ebiten.IsKeyPressed(ebiten.KeyEnter) {
in.setButton(ButtonStart)
in.setButtonOn(ButtonStart)
}
return in
@ -306,6 +308,8 @@ func (g *Game) applyPhysics() {
} else {
p.X = o.X + component.PlayerSize
}
p.VX, o.VX = o.VX, p.VX // TODO
}
if g.Winner == 0 && p.Y < -world.GroundHeight {
@ -341,6 +345,38 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
g.Players[i].VY = g.Players[i].VY * 0.8
if g.Players[i].Action == component.ActionIdle {
if input.isButtonOn(ButtonTaunt) {
var tauntAction component.PlayerAction
switch {
case input.isButtonOn(ButtonUp):
tauntAction = component.ActionTaunt1
case input.isButtonOn(ButtonRight):
tauntAction = component.ActionTaunt2
case input.isButtonOn(ButtonDown):
tauntAction = component.ActionTaunt3
case input.isButtonOn(ButtonLeft):
tauntAction = component.ActionTaunt4
}
if tauntAction != 0 {
g.Players[i].Action = tauntAction
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[tauntAction])
continue
}
}
if input.isButtonOn(ButtonPunch) {
if !g.Players[i].NoPunch {
g.Players[i].Action = component.ActionPunch
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionPunch]) // TODO
g.Players[i].NoPunch = true
continue
}
} else if g.Players[i].NoPunch {
g.Players[i].NoPunch = false
}
if input.isButtonOn(ButtonUp) && !component.TranslateRect(playerRect, 0, -1).Overlaps(oppRect) {
var grounded bool
for _, physRect := range world.PhysicsRects {
@ -363,34 +399,6 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
if input.isButtonOn(ButtonRight) && !component.TranslateRect(playerRect, 1, 0).Overlaps(oppRect) {
g.Players[i].VX = 1
}
if input.isButtonOn(ButtonPunch) {
if !g.Players[i].NoPunch {
g.Players[i].Action = component.ActionPunch
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionPunch]) // TODO
g.Players[i].NoPunch = true
continue
}
} else if g.Players[i].NoPunch {
g.Players[i].NoPunch = false
}
if input.isButtonOn(ButtonTaunt) {
switch {
case input.isButtonOn(ButtonRight):
g.Players[i].Action = component.ActionTaunt2
case input.isButtonOn(ButtonDown):
g.Players[i].Action = component.ActionTaunt3
case input.isButtonOn(ButtonLeft):
g.Players[i].Action = component.ActionTaunt4
default: // ButtonUp or no direction specified
g.Players[i].Action = component.ActionTaunt1
}
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[g.Players[i].Action])
continue
}
}
// TODO player starts in idle action?
@ -469,6 +477,17 @@ func (g *Game) playerStateUpdated() {
world.Winner = g.Winner
}
func (g *Game) RunLocalFrame() {
inputs := make([]InputBits, 2)
inputs[0] = g.ReadInputs()
if world.Mirror || true {
inputs[1] = mirrorInput(inputs[0])
}
g.UpdateByInputs(inputs)
g.playerStateUpdated()
}
func (g *Game) RunFrame() {
input := g.ReadInputs()
buffer := encodeInputs(input)
@ -568,12 +587,16 @@ func (g *Game) Update() error {
}
if world.ConnectionActive {
err := world.Backend.Idle(0)
if err != nil {
panic(err)
}
if !world.Local {
err := world.Backend.Idle(0)
if err != nil {
panic(err)
}
g.RunFrame()
g.RunFrame()
} else {
g.RunLocalFrame()
}
}
err := gohan.Update()

View File

@ -29,10 +29,14 @@ func (i *InputBits) isButtonOn(button InputButton) bool {
return *i&(1<<button) > 0
}
func (i *InputBits) setButton(button InputButton) {
func (i *InputBits) setButtonOn(button InputButton) {
*i |= 1 << button
}
func (i *InputBits) setButtonOff(button InputButton) {
*i &^= 1 << button
}
func readI32(b []byte) int32 {
if len(b) < 4 {
return 0
@ -98,3 +102,34 @@ func encodeInputsGob(inputs Input) []byte {
}
return buf.Bytes()
}
func mirrorInput(inputs InputBits) InputBits {
left, right := inputs.isButtonOn(ButtonRight), inputs.isButtonOn(ButtonLeft)
down, up := inputs.isButtonOn(ButtonUp), inputs.isButtonOn(ButtonDown)
if left {
inputs.setButtonOn(ButtonLeft)
} else {
inputs.setButtonOff(ButtonLeft)
}
if right {
inputs.setButtonOn(ButtonRight)
} else {
inputs.setButtonOff(ButtonRight)
}
if down {
inputs.setButtonOn(ButtonDown)
} else {
inputs.setButtonOff(ButtonDown)
}
if up {
inputs.setButtonOn(ButtonUp)
} else {
inputs.setButtonOff(ButtonUp)
}
return inputs
}

View File

@ -226,7 +226,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
if p.ActionTicksLeft != 0 {
// Draw a rect over stunned players.
if p.Action == component.ActionStunned {
playerRect := world.FloatRect(p.X, p.Y, p.X+float64(component.PlayerSize), p.Y+float64(component.PlayerSize))
playerRect := world.FloatRect(p.X, p.Y, p.X+float64(component.PlayerWidth), p.Y+float64(component.PlayerHeight))
fillColor := color.RGBA{123, 30, 255, 255}
u.drawBox(screen, fillColor, playerRect)
@ -291,7 +291,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
if world.Debug != 0 {
var ping int64
var framesBehind float64
if !world.ConnectPromptVisible {
if !world.ConnectPromptVisible && !world.Local {
p1Stats, err := world.Backend.GetNetworkStats(ggpo.PlayerHandle(1))
if err != nil {
return fmt.Errorf("failed to get network stats for player 1: %s", err)

View File

@ -50,6 +50,9 @@ var (
CurrentPlayer = 1
Local bool // Playing against computer
Mirror bool // AI should mirror player movements
Backend ggpo.Backend
// These variables are cached to prevent race conditions.