main.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package main
  2. import (
  3. "log"
  4. "strings"
  5. "time"
  6. _ "embed"
  7. "github.com/gdamore/tcell/v3"
  8. "github.com/gdamore/tcell/v3/color"
  9. )
  10. //go:embed assets/title/title_idle.txt
  11. var title_idle string
  12. //go:embed assets/title/title_blinking.txt
  13. var title_blinking string
  14. func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) {
  15. row := y1
  16. col := x1
  17. var width int
  18. for text != "" {
  19. text, width = s.Put(col, row, text, style)
  20. col += width
  21. if col >= x2 {
  22. row++
  23. col = x1
  24. }
  25. if row > y2 {
  26. break
  27. }
  28. if width == 0 {
  29. // incomplete grapheme at end of string
  30. break
  31. }
  32. }
  33. }
  34. func drawTitle(s tcell.Screen, x, padding_top, frame int, style tcell.Style, titles [][]string) int{
  35. lines := titles[frame]
  36. frame = (frame + 1) % 2
  37. // Draw Title
  38. xmax, ymax := s.Size()
  39. for l:= range lines{
  40. drawText(s,x, l+padding_top, xmax, ymax, style, lines[l])
  41. }
  42. return len(lines)
  43. }
  44. func main() {
  45. cats := [][]string{
  46. strings.Split(string(title_idle), "\n"),
  47. strings.Split(string(title_blinking), "\n")}
  48. defStyle := tcell.StyleDefault.Background(color.Black).Foreground(color.NewHexColor(0xf3e5ab))
  49. // Initialize screen
  50. s, err := tcell.NewScreen()
  51. if err != nil {
  52. log.Fatalf("%+v", err)
  53. }
  54. if err := s.Init(); err != nil {
  55. log.Fatalf("%+v", err)
  56. }
  57. s.SetStyle(defStyle)
  58. s.Clear()
  59. quit := func() {
  60. // You have to catch panics in a defer, clean up, and
  61. // re-raise them - otherwise your application can
  62. // die without leaving any diagnostic trace.
  63. maybePanic := recover()
  64. s.Fini()
  65. if maybePanic != nil {
  66. panic(maybePanic)
  67. }
  68. }
  69. defer quit()
  70. // Here's how to get the screen size when you need it.
  71. // xmax, ymax := s.Size()
  72. // Here's an example of how to inject a keystroke where it will
  73. // be picked up by a future read of the event queue. Note that
  74. // care should be used to avoid blocking writes to the queue if
  75. // this is done from the same thread that is responsible for reading
  76. // the queue, or else a single-party deadlock might occur.
  77. // s.EventQ() <- tcell.NewEventKey(tcell.KeyRune, rune('a'), 0)
  78. curr_menu_item := 0
  79. menu := true
  80. gap_title_menu := 3
  81. credits := false
  82. frame := 0
  83. ticker := time.NewTicker(500 * time.Millisecond)
  84. defer ticker.Stop()
  85. // Event loop
  86. for {
  87. s.Clear()
  88. title_size := drawTitle(s, 10, 2, frame, defStyle, cats)
  89. frame = (frame + 1) % 2
  90. xmax,ymax := s.Size()
  91. // Draw Menu
  92. if menu {
  93. drawText(s, 10, title_size+gap_title_menu, xmax, ymax, defStyle, "Use arrows to navigate. Press ENTER to select")
  94. switch curr_menu_item {
  95. case 0:
  96. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, "▶ Start New Game")
  97. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Level Selector")
  98. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  99. case 1:
  100. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " Start New Game")
  101. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, "▶ Level Selector")
  102. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  103. case 2:
  104. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " Start New Game")
  105. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Level Selector")
  106. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, "▶ Credits")
  107. }
  108. }
  109. if credits {
  110. drawText(s, 10, title_size+gap_title_menu, xmax,ymax, defStyle, "Abderrahmane Faiz")
  111. drawText(s, 10, title_size+gap_title_menu+1, xmax,ymax, defStyle, "\u001B]8;;https://afaiz.dev\u001B\\https://afaiz.dev\u001B]8;;\u001B\\")
  112. drawText(s, 10, title_size+gap_title_menu+2, xmax,ymax, defStyle, "ASCII Art found on https://emojicombos.com/cat")
  113. drawText(s, 10, title_size+gap_title_menu+4, xmax,ymax, defStyle, "Press ENTER to go back to the menu")
  114. }
  115. // Update screen
  116. s.Show()
  117. // Poll event (this can be in a select statement as well)
  118. select{
  119. case <- ticker.C:
  120. continue
  121. case ev := <-s.EventQ():
  122. // Process event
  123. switch ev := ev.(type) {
  124. default:
  125. continue
  126. case *tcell.EventResize:
  127. s.Sync()
  128. case *tcell.EventKey:
  129. if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
  130. return
  131. } else if ev.Key() == tcell.KeyUp && menu {
  132. if curr_menu_item == 0 {
  133. curr_menu_item = 2
  134. } else {
  135. curr_menu_item = curr_menu_item - 1
  136. }
  137. } else if ev.Key() == tcell.KeyDown && menu {
  138. curr_menu_item = (curr_menu_item + 1) % 3
  139. } else if ev.Key() == tcell.KeyEnter && credits {
  140. credits = false
  141. menu = true
  142. } else if ev.Key() == tcell.KeyEnter && curr_menu_item == 2 {
  143. menu = false
  144. credits = true
  145. }
  146. }
  147. }
  148. }
  149. }