package main import ( "git.kiefte.eu/lapingvino/clitris/tris" // "image/color" "fmt" "gioui.org/app" "gioui.org/font/gofont" "gioui.org/io/key" "gioui.org/io/system" "gioui.org/layout" "gioui.org/op" "gioui.org/unit" "log" "os" "time" // "gioui.org/text" "gioui.org/widget/material" "math/rand" ) func main() { go func() { w := app.NewWindow(app.Title("Gioco - another block stacking game"), app.Size(unit.Dp(500), unit.Dp(500))) if err := loop(w); err != nil { log.Fatal(err) } os.Exit(0) }() app.Main() } func loop(w *app.Window) error { th := material.NewTheme(gofont.Collection()) rand.Seed(time.Now().UnixNano()) // to start with truly random pieces f := tris.NewField(20, 10) // the playing field (10x20) var floor, topout, harddrop bool // state machine variables to check special situations // define outside of game loop to avoid accidental resets of position // x, y and rot are the values calculated to feed to Move // which then checks collisions and does the wall kicks var x, y int var rot tris.Rotation var level, linescleared, score, dropfrom int b := tris.NewBag() b, p := b.Pick() lev := time.NewTicker(time.Second) level = 1 var debug string var ops op.Ops for { x, y, rot = p.X, p.Y, p.Rot select { case e := <-w.Events(): switch e := e.(type) { case system.DestroyEvent: return e.Err case key.Event: if e.State == key.Press { switch e.Name { case key.NameUpArrow: // Up debug = "up" rot = (p.Rot + 1) % 4 case key.NameDownArrow: // Down debug = "down" y = p.Y + 1 score += 1 case key.NameRightArrow: // Right debug = "right" x = p.X + 1 case key.NameLeftArrow: // Left debug = "left" x = p.X - 1 case key.NameEscape, "q", "Q": os.Exit(0) case "x": rot = (p.Rot + 1) % 4 case "z": rot = (p.Rot + 3) % 4 case "c": b, p = b.Swap(p) continue case " ": if !harddrop { dropfrom = p.Y } harddrop = true } } case system.FrameEvent: gtx := layout.NewContext(&ops, e) level = linescleared/10 + 1 slevel := fmt.Sprintf("level %d", level) sscore := fmt.Sprintf("score %d", score) slines := fmt.Sprintf("lines %d", linescleared) /* ppos(0, 0, "Hold (c)") npos(3, 0, tris.HoldBox) fpos(0, 10, f.Add(p)) var next tris.Field b, next = b.Next(5) npos(0, 34, next) ppos(1, 42, sscore) ppos(3, 42, slevel) ppos(5, 42, slines) */ inset := layout.UniformInset(unit.Dp(10)) inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { return material.H5(th, sscore).Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return material.H5(th, slevel).Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return material.H5(th, slines).Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return material.H5(th, debug).Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return material.H5(th, f.Add(p).String()).Layout(gtx) }), ) }) op.InvalidateOp{}.Add(gtx.Ops) e.Frame(gtx.Ops) } case <-lev.C: y = p.Y + 1 lev.Reset(time.Second * 100 / (100 * time.Duration(level))) } debug = "move" if harddrop { y = p.Y + 1 } p, floor, topout = p.Move(f, rot, x, y) // Check if the piece can move, then do it and communicate back for housekeeping // Give some time before actually locking in to enable tucks // This code runs when the time is over if floor && p.Lock.Add(tris.LockDelay).Before(time.Now()) { if harddrop { score += 2 * (p.Y - dropfrom) harddrop = false } var l int f = f.Add(p) l, f = f.Lines() // count and remove full lines if l > 0 { linescleared += l score += 40 * level * l * l } b, p = b.Pick() // ... and pick a new piece from the bag } // when not touching a piece or floor below yet, reset lock delay if !floor { p.Lock = time.Now() } if topout { return fmt.Errorf("GAME OVER") } } return nil }