boxbrawl/component/player.go

256 lines
4.8 KiB
Go

package component
import (
"fmt"
"image"
"image/color"
"code.rocketnine.space/tslocum/boxbrawl/asset"
"github.com/hajimehoshi/ebiten/v2"
)
type PlayerAction int
const (
ActionIdle PlayerAction = iota
ActionStunned
ActionBlock
ActionPunch
)
type HitboxType int
const (
HitboxNone HitboxType = iota
HitboxNormal
HitboxHurt
)
type FrameData struct {
T HitboxType
R image.Rectangle
S *ebiten.Image
}
const PlayerSize = 16
const (
PlayerHeight = 48
PlayerWidth = 16
)
func RectTouches(r1 image.Rectangle, r2 image.Rectangle) bool {
return !r1.Empty() && !r2.Empty() &&
r1.Min.X <= r2.Max.X && r2.Min.X <= r1.Max.X &&
r1.Min.Y <= r2.Max.Y && r2.Min.Y <= r1.Max.Y
}
// stdHit returns FrameData using a standard hitbox and the provided sprite.
func stdHit(sprite *ebiten.Image) FrameData {
return FrameData{
T: HitboxNormal,
R: image.Rect(0, 0, PlayerWidth, PlayerHeight),
S: sprite,
}
}
// AllPlayerFrames defines all frame data for the game. Frames are defined in reverse order.
var AllPlayerFrames = [][][]FrameData{
{ // ActionIdle
{
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 1, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 2, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 3, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 4, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 5, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 6, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 7, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 8, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 9, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 10, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 11, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 12, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 13, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 14, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 15, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 16, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 17, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 18, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 19, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 20, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 21, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 22, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 23, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 24, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 25, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 26, 0)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 27, 0)),
},
}, { // ActionStunned
{
FrameData{
T: HitboxNone,
R: image.Rect(0, 0, 0, 0),
S: asset.FrameAt(asset.ImgPlayer, 0, 2),
},
},
}, { // ActionBlock
{
// TODO
},
}, { // ActionPunch
{
{
T: HitboxNormal,
R: image.Rect(0, 0, PlayerSize, PlayerSize),
S: asset.FrameAt(asset.ImgPlayer, 12, 1),
},
{
T: HitboxHurt,
R: image.Rect(-5, -5, PlayerSize+10, PlayerSize+10),
},
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 11, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 10, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 9, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 8, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 7, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 6, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 5, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 4, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 3, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 2, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 1, 1)),
},
{
stdHit(asset.FrameAt(asset.ImgPlayer, 0, 1)),
},
},
}
func FrameDataForActionTick(a PlayerAction, tick int) []FrameData {
actionFrames := AllPlayerFrames[a]
if tick-1 >= len(actionFrames) {
// Handle
if a == ActionStunned {
return AllPlayerFrames[ActionStunned][0]
}
return nil
}
return actionFrames[tick-1]
}
func FlipRect(r image.Rectangle, flip bool) image.Rectangle {
if !flip {
return r
}
return image.Rect(-r.Min.X+PlayerSize, r.Min.Y, -r.Max.X+PlayerSize, r.Max.Y)
}
func PlayerOnRightSide(p Player, o Player) bool {
return p.X > o.X
}
type Player struct {
X, Y float64
VX, VY float64
Color color.Color
PlayerNum int
Action PlayerAction
ActionTicksLeft int
}
func (p *Player) String() string {
return fmt.Sprintf("Player %d: X:%f Y:%f Action: %d", p.PlayerNum, p.X, p.Y, p.Action)
}
func (p *Player) Clone() Player {
result := Player{}
result = *p
return result
}
func TranslateRect(r image.Rectangle, x int, y int) image.Rectangle {
r.Min.X, r.Min.Y = r.Min.X+x, r.Min.Y+y
r.Max.X, r.Max.Y = r.Max.X+x, r.Max.Y+y
return r
}