Add mirror and block AI types

This commit is contained in:
Trevor Slocum 2023-01-27 15:18:59 -08:00
parent cf1be8e89d
commit aba1cb30f5
8 changed files with 105 additions and 39 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -101,10 +101,6 @@ var AllPlayerFrames = [][][]FrameData{
S: asset.FrameAt(asset.ImgPlayer, 0, 2),
},
},
}, { // ActionBlock
{
// TODO
},
}, { // ActionPunch
{
{
@ -153,6 +149,16 @@ var AllPlayerFrames = [][][]FrameData{
{
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 1)),
},
}, { // ActionKick
{
// TODO
},
}, { // ActionBlock
{
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 11)),
}, {
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 11)),
},
}, { // ActionTaunt1
{
stdHit(asset.FrameAt(asset.ImgPlayer, 28, 4)),

View File

@ -15,8 +15,9 @@ type PlayerAction int
const (
ActionIdle PlayerAction = iota
ActionStunned
ActionBlock
ActionPunch
ActionKick
ActionBlock
ActionTaunt1
ActionTaunt2
ActionTaunt3

View File

@ -209,6 +209,14 @@ func (g *Game) ReadInputs() InputBits {
in.setButtonOn(ButtonPunch)
}
if ebiten.IsKeyPressed(ebiten.KeyJ) {
in.setButtonOn(ButtonKick)
}
if ebiten.IsKeyPressed(ebiten.KeyK) {
in.setButtonOn(ButtonBlock)
}
if ebiten.IsKeyPressed(ebiten.KeyL) {
in.setButtonOn(ButtonTaunt)
}
@ -345,6 +353,12 @@ 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(ButtonBlock) {
g.Players[i].Action = component.ActionBlock
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionBlock])
continue
}
if input.isButtonOn(ButtonTaunt) {
var tauntAction component.PlayerAction
switch {
@ -421,16 +435,23 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
if frame.T == component.HitboxHurt {
// Hit opponent.
if oppRect.Overlaps(component.TranslateRect(frameRect, int(player.X), int(player.Y))) {
hitStrength := 4.0
if g.Players[opp].Action == component.ActionBlock {
hitStrength = 1.0
}
// Send the opponent flying in some direction.
if opponent.X <= player.X { // Opponent is to the left of the player.
opponent.VX = -4
opponent.VX = -hitStrength
} else { // Opponent is to the right of the player.
opponent.VX = 4
opponent.VX = hitStrength
}
// Stun the opponent.
opponent.Action = component.ActionStunned
opponent.ActionTicksLeft = 14
if g.Players[opp].Action != component.ActionBlock {
opponent.Action = component.ActionStunned
opponent.ActionTicksLeft = 14
}
}
}
}
@ -446,6 +467,12 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
g.Players[i].ActionTicksLeft--
}
if g.Players[i].ActionTicksLeft == 0 {
// Hold block.
if input.isButtonOn(ButtonBlock) {
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionBlock])
continue
}
// Return to the idle action.
g.Players[i].Action = component.ActionIdle
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionIdle]) // TODO
@ -480,11 +507,15 @@ func (g *Game) playerStateUpdated() {
func (g *Game) RunLocalFrame() {
inputs := make([]InputBits, 2)
inputs[0] = g.ReadInputs()
if world.Mirror || true {
inputs[1] = mirrorInput(inputs[0])
}
g.UpdateByInputs(inputs)
if world.AI == world.AIMirror {
inputs[1] = mirrorInput(inputs[0])
} else if world.AI == world.AIBlock {
inputs[1] = inputs[0]
inputs[1].setButtonOn(ButtonBlock)
}
g.UpdateByInputs(inputs)
g.playerStateUpdated()
}
@ -544,6 +575,17 @@ func (g *Game) Update() error {
return nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyI) && ebiten.IsKeyPressed(ebiten.KeyControl) {
switch world.AI {
case world.AIMirror:
world.AI = world.AIBlock
case world.AIBlock:
world.AI = world.AINone
default:
world.AI = world.AIMirror
}
}
if inpututil.IsKeyJustPressed(ebiten.KeyV) && ebiten.IsKeyPressed(ebiten.KeyControl) {
world.Debug++
if world.Debug > world.MaxDebug {

View File

@ -21,6 +21,8 @@ const (
ButtonDown
ButtonUp
ButtonPunch
ButtonKick
ButtonBlock
ButtonTaunt
ButtonStart
)
@ -105,31 +107,15 @@ func encodeInputsGob(inputs Input) []byte {
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

@ -38,7 +38,7 @@ type UISystem struct {
buffer *etk.Text
updateTicks int
gameOverImg *ebiten.Image
tmpImg *ebiten.Image
hitboxImg *ebiten.Image
}
@ -55,7 +55,7 @@ func (u *UISystem) initialize() {
etk.SetRoot(inputDemo)
etk.Layout(world.InternalScreenWidth, world.InternalScreenHeight)
u.gameOverImg = ebiten.NewImage(250, 100)
u.tmpImg = ebiten.NewImage(250, 100)
u.hitboxImg = ebiten.NewImage(32, 32)
@ -250,14 +250,37 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
}
}
if world.Local {
u.tmpImg.Clear()
aiLabel := "NONE"
switch world.AI {
case world.AIMirror:
aiLabel = "MIRROR"
case world.AIBlock:
aiLabel = "BLOCK"
}
label := fmt.Sprintf("AI TYPE: %s (CONTROL+I)", aiLabel)
ebitenutil.DebugPrint(u.tmpImg, label)
width := float64(len(label) * 12)
height := float64(32)
op := &ebiten.DrawImageOptions{}
op.GeoM.Reset()
op.GeoM.Scale(2, 2)
op.GeoM.Translate(world.InternalScreenWidth/2-width/2, world.InternalScreenHeight-height)
screen.DrawImage(u.tmpImg, op)
}
if world.Winner != 0 {
u.gameOverImg.Clear()
u.tmpImg.Clear()
label := "YOU LOSE!"
if world.Winner == world.CurrentPlayer {
label = "YOU WIN!"
}
ebitenutil.DebugPrint(u.gameOverImg, label)
ebitenutil.DebugPrint(u.tmpImg, label)
width := float64(len(label) * 24)
height := float64(64) * 2
@ -265,9 +288,9 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
op := &ebiten.DrawImageOptions{}
op.GeoM.Scale(4, 4)
op.GeoM.Translate(world.InternalScreenWidth/2-width/2, world.InternalScreenHeight/2-height/2)
screen.DrawImage(u.gameOverImg, op)
screen.DrawImage(u.tmpImg, op)
u.gameOverImg.Clear()
u.tmpImg.Clear()
label = "PRESS ENTER OR START TO PLAY AGAIN"
p := world.Player1
@ -277,7 +300,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
if p.PlayAgain {
label = "WAITING FOR OPPONENT..."
}
ebitenutil.DebugPrint(u.gameOverImg, label)
ebitenutil.DebugPrint(u.tmpImg, label)
width = float64(len(label) * 12)
height = float64(32)
@ -285,7 +308,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
op.GeoM.Reset()
op.GeoM.Scale(2, 2)
op.GeoM.Translate(world.InternalScreenWidth/2-width/2, world.InternalScreenHeight/2+height/2)
screen.DrawImage(u.gameOverImg, op)
screen.DrawImage(u.tmpImg, op)
}
if world.Debug != 0 {

View File

@ -27,6 +27,14 @@ const (
GroundHeight = 100
)
type AIType int
const (
AINone AIType = iota
AIMirror
AIBlock
)
var (
TPS = DefaultTPS
@ -50,8 +58,8 @@ var (
CurrentPlayer = 1
Local bool // Playing against computer
Mirror bool // AI should mirror player movements
Local bool // Playing against computer
AI AIType // AI configuration
Backend ggpo.Backend