Add kick action
parent
361529de18
commit
0860c30cfd
30
README.md
30
README.md
|
@ -32,6 +32,36 @@ are bypassed, and port forwarding is usually not required.
|
|||
Only multiplayer over a local network (whether truly local or virtual) has been
|
||||
tested successfully.
|
||||
|
||||
#### Multiplayer guide
|
||||
|
||||
You and your opponent will need to choose a port on which you will accept UDP
|
||||
traffic from the other person. This is referred to as your local port,
|
||||
which can specified when playing Box Brawl using the `--local` argument.
|
||||
|
||||
In the following examples, user A has the IP address `1.1.1.1` and user B has
|
||||
the IP address `2.2.2.2`. User A will listen for a connection on port `17000`.
|
||||
User B will listen for a connection on port `19000`.
|
||||
|
||||
Simply replace the example IP addresses and ports with actual IP addresses and
|
||||
ports, and run the command specified (depending on whether you are the host or
|
||||
the guest).
|
||||
|
||||
User A (who has IP `1.1.1.1`) should run the following command:
|
||||
|
||||
```
|
||||
boxbrawl --local 17000 --host 2.2.2.2:19000
|
||||
```
|
||||
|
||||
User B (who has IP `2.2.2.2`) should run the following command:
|
||||
|
||||
```
|
||||
boxbrawl --local 19000 --connect 1.1.1.1:17000
|
||||
```
|
||||
|
||||
In the above commands, each user first specifies which port to listen for
|
||||
connections from the opponent, then specifies the IP address and port where
|
||||
their opponent is listening for connections.
|
||||
|
||||
### Compile
|
||||
|
||||
Install the dependencies listed for [your platform](https://github.com/hajimehoshi/ebiten/blob/main/README.md#platforms),
|
||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
@ -102,6 +102,39 @@ var AllPlayerFrames = [][][]FrameData{
|
|||
},
|
||||
},
|
||||
}, { // ActionPunch
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 1, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 2, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 3, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 4, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 5, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 6, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 7, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 8, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 9, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 10, 1)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 11, 1)),
|
||||
},
|
||||
{
|
||||
{
|
||||
T: HitboxNormal,
|
||||
|
@ -151,7 +184,92 @@ var AllPlayerFrames = [][][]FrameData{
|
|||
},
|
||||
}, { // ActionKick
|
||||
{
|
||||
// TODO
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 1, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 2, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 3, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 4, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 5, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 6, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 7, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 8, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 9, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 10, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 11, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 12, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 13, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 14, 12)),
|
||||
{
|
||||
T: HitboxHurt,
|
||||
R: image.Rect(-5, -5, PlayerSize+10, PlayerSize+10),
|
||||
},
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 13, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 12, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 11, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 10, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 9, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 8, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 7, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 6, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 5, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 4, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 3, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 2, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 1, 12)),
|
||||
},
|
||||
{
|
||||
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 12)),
|
||||
},
|
||||
}, { // ActionBlock
|
||||
{
|
||||
|
|
|
@ -103,6 +103,7 @@ type Player struct {
|
|||
ActionTicksLeft int
|
||||
|
||||
NoPunch bool
|
||||
NoKick bool
|
||||
|
||||
PlayAgain bool
|
||||
}
|
||||
|
|
49
game/game.go
49
game/game.go
|
@ -328,7 +328,12 @@ func (g *Game) applyPhysics() {
|
|||
}
|
||||
|
||||
func (g *Game) UpdateByInputs(inputs []InputBits) {
|
||||
const punchStunTicks = 15
|
||||
const (
|
||||
punchHitStrength = 4.0
|
||||
punchStunTicks = 15
|
||||
kickHitStrength = 7.0
|
||||
KickStunTicks = 19
|
||||
)
|
||||
|
||||
var player, opponent *component.Player
|
||||
var playerFlipped, oppFlipped bool
|
||||
|
@ -381,18 +386,27 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if g.Players[i].NoPunch && !input.isButtonOn(ButtonPunch) {
|
||||
g.Players[i].NoPunch = false
|
||||
}
|
||||
if g.Players[i].NoKick && !input.isButtonOn(ButtonKick) {
|
||||
g.Players[i].NoKick = false
|
||||
}
|
||||
|
||||
if input.isButtonOn(ButtonPunch) && !g.Players[i].NoPunch {
|
||||
g.Players[i].Action = component.ActionPunch
|
||||
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionPunch])
|
||||
|
||||
g.Players[i].NoPunch = true
|
||||
continue
|
||||
}
|
||||
if input.isButtonOn(ButtonKick) && !g.Players[i].NoKick {
|
||||
g.Players[i].Action = component.ActionKick
|
||||
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionKick])
|
||||
|
||||
g.Players[i].NoKick = true
|
||||
continue
|
||||
}
|
||||
|
||||
if input.isButtonOn(ButtonUp) && !component.TranslateRect(playerRect, 0, -1).Overlaps(oppRect) {
|
||||
var grounded bool
|
||||
|
@ -438,9 +452,16 @@ 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
|
||||
hitStrength := punchHitStrength
|
||||
stunTicks := punchStunTicks
|
||||
if player.Action == component.ActionKick {
|
||||
hitStrength = kickHitStrength
|
||||
stunTicks = KickStunTicks
|
||||
}
|
||||
|
||||
// Apply blocking.
|
||||
if g.Players[opp].Action == component.ActionBlock {
|
||||
hitStrength = 1.0
|
||||
hitStrength /= 4.0
|
||||
}
|
||||
|
||||
// Send the opponent flying in some direction.
|
||||
|
@ -453,7 +474,7 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
|
|||
// Stun the opponent.
|
||||
if g.Players[opp].Action != component.ActionBlock {
|
||||
opponent.Action = component.ActionStunned
|
||||
opponent.ActionTicksLeft = punchStunTicks
|
||||
opponent.ActionTicksLeft = stunTicks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ var (
|
|||
botTicks int
|
||||
botWait bool
|
||||
botLastPunch int
|
||||
botLastKick int
|
||||
botBlockTicks int
|
||||
botTaunt component.PlayerAction
|
||||
botTauntTicks int
|
||||
|
@ -131,9 +132,11 @@ func botInput() InputBits {
|
|||
botMaxActionTime = 20
|
||||
botWaitChance = 3
|
||||
botPunchDistance = 25
|
||||
botBlockDistance = 25
|
||||
botBlockTime = 17
|
||||
botBlockChance = 4
|
||||
botKickDistance = 30
|
||||
botKickChance = 7
|
||||
botBlockDistance = 30
|
||||
botBlockTime = 25
|
||||
botBlockChance = 3
|
||||
botTauntMinTime = 60
|
||||
botTauntTime = 200
|
||||
botTauntMaxTime = 550
|
||||
|
@ -146,6 +149,7 @@ func botInput() InputBits {
|
|||
botTicks--
|
||||
}
|
||||
if !botWait {
|
||||
// Handle taunting.
|
||||
if (botTauntTicks > 0 || world.Winner == 2) && botTauntTotalTicks < botTauntMaxTime {
|
||||
if botTauntTicks == 0 {
|
||||
if p.Action == component.ActionIdle {
|
||||
|
@ -194,24 +198,32 @@ func botInput() InputBits {
|
|||
} else {
|
||||
input.setButtonOn(ButtonLeft)
|
||||
}
|
||||
}
|
||||
|
||||
if botBlockTicks > 0 || (math.Abs(p.X-o.X) < botBlockDistance && o.Action == component.ActionPunch && rand.Intn(botBlockChance) != 0) {
|
||||
input.setButtonOn(ButtonBlock)
|
||||
if botBlockTicks > 0 {
|
||||
botBlockTicks--
|
||||
} else {
|
||||
botBlockTicks = botBlockTime
|
||||
opponentAttacking := o.Action == component.ActionPunch || o.Action == component.ActionKick
|
||||
|
||||
if botBlockTicks > 0 || (math.Abs(p.X-o.X) < botBlockDistance && opponentAttacking && rand.Intn(botBlockChance) == 0) {
|
||||
input.setButtonOn(ButtonBlock)
|
||||
if botBlockTicks > 0 {
|
||||
botBlockTicks--
|
||||
} else {
|
||||
botBlockTicks = botBlockTime
|
||||
}
|
||||
} else if p.Action == component.ActionIdle && math.Abs(p.X-o.X) < botKickDistance && rand.Intn(botKickChance) == 0 && botLastKick > 1 {
|
||||
input.setButtonOn(ButtonKick)
|
||||
botLastKick = 0
|
||||
} else if p.Action == component.ActionIdle && math.Abs(p.X-o.X) < botPunchDistance && botLastPunch > 1 {
|
||||
input.setButtonOn(ButtonPunch)
|
||||
botLastPunch = 0
|
||||
}
|
||||
} else if math.Abs(p.X-o.X) < botPunchDistance && botLastPunch > 1 {
|
||||
input.setButtonOn(ButtonPunch)
|
||||
botLastPunch = 0
|
||||
}
|
||||
}
|
||||
|
||||
if !input.isButtonOn(ButtonPunch) {
|
||||
botLastPunch++
|
||||
}
|
||||
if !input.isButtonOn(ButtonKick) {
|
||||
botLastKick++
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue