1
0
Fork 0
tetris-go/game/state/handlers/gameplay.go

149 lines
3.8 KiB
Go

package handlers
import (
"fmt"
"image/color"
"tetris/assets"
"tetris/engine/audio"
"tetris/engine/core"
"tetris/engine/render"
"tetris/game"
"tetris/game/draw"
"tetris/game/state"
rl "github.com/gen2brain/raylib-go/raylib"
)
type GamePlay struct {
shape game.Shape
shape_pos core.Vec2i8
score game.Score
dropTimer core.IntervalTimer
moveTimer core.IntervalTimer
lineClearTimer core.IntervalTimer
grid game.Grid
nextShape game.Shape
shapeQueue *game.ShapeQueue
r draw.Renderer
lineClearAnimation game.LineClearAnimation
}
func NewGamePlay() *GamePlay {
return &GamePlay{
dropTimer: core.NewIntervalTimer(0.3),
moveTimer: core.NewIntervalTimer(0.1),
lineClearTimer: core.NewIntervalTimer(0.05),
r: draw.Renderer{
Theme: &draw.Theme{
FrameBG: color.RGBA{R: 30, G: 30, B: 46, A: 255},
FrameBorder: color.RGBA{R: 242, G: 205, B: 205, A: 255},
TextHeader: color.RGBA{R: 242, G: 205, B: 205, A: 255},
Text: color.RGBA{R: 205, G: 214, B: 244, A: 255},
GridBackground: color.RGBA{R: 17, G: 17, B: 27, A: 255},
},
},
}
}
func (gp *GamePlay) Enter() {
gp.grid = game.Grid{}
gp.score = 0
gp.dropTimer.SetInterval(0.3)
gp.shapeQueue = game.NewShapeQueue()
gp.nextShape = gp.shapeQueue.Next()
gp.SpawnShape()
gp.lineClearAnimation.Reset()
}
func (GamePlay) Exit() {
}
func (gp *GamePlay) SpawnShape() {
gp.shape = gp.nextShape
gp.nextShape = gp.shapeQueue.Next()
gp.shape_pos = core.Vec2i8{X: 4, Y: 0}
}
func (gp *GamePlay) LockShape() {
audio.Play(assets.SFX_SHAPE_LOCKED)
for _, block := range gp.shape.Coordinates() {
block = gp.shape_pos.Add(block)
// Check bounds
if block.X < 0 || block.X > int8(gp.grid.Width()) || block.Y < 0 || block.Y > int8(gp.grid.Height()) {
continue
}
gp.grid.Set(byte(block.X), byte(block.Y), gp.shape.GetBlock())
}
}
func (gp *GamePlay) Update(fsm state.Transitioner, delta float32) {
if rl.IsKeyPressed(rl.KeyDown) {
gp.dropTimer.SetInterval(0.05)
} else if rl.IsKeyReleased(rl.KeyDown) {
gp.dropTimer.SetInterval(0.3)
}
if !gp.lineClearAnimation.Completed() {
if gp.lineClearTimer.UpdateReset(delta) {
gp.lineClearAnimation.Update(&gp.grid)
}
return
}
if rl.IsKeyPressed(rl.KeyUp) {
rotated := gp.shape.RotateCW()
if !game.CheckShapeCollision(gp.shape_pos, &rotated, &gp.grid) {
gp.shape = rotated
}
}
if gp.moveTimer.UpdateReset(delta) && (rl.IsKeyDown(rl.KeyLeft) || rl.IsKeyDown(rl.KeyRight)) {
new_pos := gp.shape_pos
if rl.IsKeyDown(rl.KeyLeft) {
new_pos.X -= 1
} else {
new_pos.X += 1
}
if !game.CheckShapeCollision(new_pos, &gp.shape, &gp.grid) {
gp.shape_pos.X = new_pos.X
}
}
if gp.dropTimer.UpdateReset(delta) {
new_pos := gp.shape_pos
new_pos.Y += 1
// Update position if it does not collide
if game.CheckShapeCollision(new_pos, &gp.shape, &gp.grid) {
gp.LockShape()
gp.SpawnShape()
lines := gp.grid.FullRows()
if len(lines) > 0 {
gp.lineClearAnimation.SetLines(lines)
gp.score.Lines(byte(len(lines)))
audio.Play(assets.SFX_ROW_CLEARED)
} else {
if game.CheckShapeCollision(gp.shape_pos, &gp.shape, &gp.grid) {
fsm.Switch("gameover")
}
}
} else {
gp.shape_pos = new_pos
}
}
}
func (gp GamePlay) Render() {
render.Begin(gp.r.Theme.GridBackground)
gp.r.DrawGrid(rl.NewVector2(25, 25), gp.grid)
draw.DrawShape(rl.NewVector2(25, 25), gp.shape_pos, gp.shape)
gp.r.DrawFrame(rl.RectangleInt32{X: 400, Y: 25, Width: 250, Height: 100})
gp.r.DrawHeaderText(410, 30, "Score")
gp.r.DrawText(410, 65, fmt.Sprintf("%.7d", gp.score))
gp.r.DrawFrame(rl.RectangleInt32{X: 400, Y: 150, Width: 250, Height: 200})
gp.r.DrawHeaderText(410, 155, "Next")
draw.DrawShape(rl.NewVector2(450, 150), core.NewVec2[int8](1, 3), gp.nextShape)
render.End()
}