package main import ( "fmt" "image/color" "tetris/assets" "tetris/engine/audio" "tetris/engine/core" "tetris/engine/graphics" "tetris/engine/render" "tetris/game" "tetris/game/draw" rl "github.com/gen2brain/raylib-go/raylib" ) var ( shape game.Shape shape_pos core.Vec2i8 score game.Score dropTimer = core.NewIntervalTimer(0.3) moveTimer = core.NewIntervalTimer(0.1) grid = game.Grid{} nextShape = game.NewShape(game.SHAPE_O) shapeQueue = game.NewShapeQueue() 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 SpawnShape() { shape = nextShape nextShape = shapeQueue.Next() shape_pos = core.Vec2i8{X: 4, Y: 0} } func LockShape() { audio.Play(assets.SFX_SHAPE_LOCKED) for _, block := range shape.Coordinates() { block = shape_pos.Add(block) // Check bounds if block.X < 0 || block.X > int8(grid.Width()) || block.Y < 0 || block.Y > int8(grid.Height()) { continue } grid.Set(byte(block.X), byte(block.Y), shape.GetBlock()) } } func Update(delta float32) { if rl.IsKeyPressed(rl.KeyDown) { dropTimer.SetInterval(0.05) } else if rl.IsKeyReleased(rl.KeyDown) { dropTimer.SetInterval(0.3) } if rl.IsKeyPressed(rl.KeyUp) { rotated := shape.RotateCW() if !game.CheckShapeCollision(shape_pos, &rotated, &grid) { shape = rotated } } if moveTimer.UpdateReset(delta) && (rl.IsKeyDown(rl.KeyLeft) || rl.IsKeyDown(rl.KeyRight)) { new_pos := shape_pos if rl.IsKeyDown(rl.KeyLeft) { new_pos.X -= 1 } else { new_pos.X += 1 } if !game.CheckShapeCollision(new_pos, &shape, &grid) { shape_pos.X = new_pos.X } } if dropTimer.UpdateReset(delta) { new_pos := shape_pos new_pos.Y += 1 // Update position if it does not collide if game.CheckShapeCollision(new_pos, &shape, &grid) { LockShape() num_rows := grid.ClearFullRows() if num_rows > 0 { audio.Play(assets.SFX_ROW_CLEARED) score.Lines(num_rows) } SpawnShape() } else { shape_pos = new_pos } } } func Render() { render.Begin(r.Theme.GridBackground) r.DrawGrid(rl.NewVector2(25, 25), grid) draw.DrawShape(rl.NewVector2(25, 25), shape_pos, shape) r.DrawFrame(rl.RectangleInt32{X: 400, Y: 25, Width: 250, Height: 100}) r.DrawHeaderText(410, 30, "Score") r.DrawText(410, 65, fmt.Sprintf("%.7d", score)) r.DrawFrame(rl.RectangleInt32{X: 400, Y: 150, Width: 250, Height: 200}) r.DrawHeaderText(410, 155, "Next") draw.DrawShape(rl.NewVector2(450, 150), core.NewVec2[int8](1, 3), nextShape) render.End() } func main() { // Set random blocks to test render.Init(render.Config{ Title: "Tetris", WindowWidth: 685, WindowHeight: 600, RenderWidth: 685, RenderHeight: 600, ScaleFlags: render.SCALE_INTEGER, }) defer render.Exit() audio.Init() defer audio.Exit() audio.LoadLibrary(assets.LoadSound()) // Load texture texture := graphics.LoadTextureFromMemory(".png", assets.Sprite) defer rl.UnloadTexture(texture) render.SetTexture(texture) render.SetFont(&assets.Font) nextShape = shapeQueue.Next() SpawnShape() for !rl.WindowShouldClose() { audio.Update() Update(rl.GetFrameTime()) Render() } }