main.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. package main
  2. import (
  3. "log"
  4. "strings"
  5. "time"
  6. "github.com/gdamore/tcell/v3"
  7. "github.com/gdamore/tcell/v3/color"
  8. )
  9. func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) {
  10. row := y1
  11. col := x1
  12. var width int
  13. for text != "" {
  14. text, width = s.Put(col, row, text, style)
  15. col += width
  16. if col >= x2 {
  17. row++
  18. col = x1
  19. }
  20. if row > y2 {
  21. break
  22. }
  23. if width == 0 {
  24. // incomplete grapheme at end of string
  25. break
  26. }
  27. }
  28. }
  29. var title_idle string = `
  30. ⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀
  31. ⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀
  32. ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀
  33. ⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀
  34. ⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀
  35. ⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣠⣤⣤⣼⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀
  36. ⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀
  37. ⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀
  38. ⠀⠀⠀⠘⣿⣿⣿⣿⠟⠁⠀⠀⠀⠹⣿⣿⣿⣿⣿⠟⠁⠀⠀⠹⣿⣿⡿⠀⠀⠀⠀⠀
  39. ⠀⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⢼⣿⠀⢿⣿⣿⣿⣿⠀⣾⣷⠀⠀⢿⣿⣷⠀⠀⠀⠀⠀
  40. ⠀⠀⠀⢠⣿⣿⣿⣷⡀⠀⠀⠈⠋⢀⣿⣿⣿⣿⣿⡀⠙⠋⠀⢀⣾⣿⣿⠀⠀⠀⠀⠀
  41. ⢀⣀⣀⣀⣿⣿⣿⣿⣿⣶⣶⣶⣶⣿⣿⣿⣿⣾⣿⣷⣦⣤⣴⣿⣿⣿⣿⣤⠤⢤⣤⡄
  42. ⠈⠉⠉⢉⣙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⣀⣀⣀⡀⠀
  43. ⠐⠚⠋⠉⢀⣬⡿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣥⣀⡀⠈⠀⠈⠛
  44. ⠀⠀⠴⠚⠉⠀⠀⠀⠉⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠋⠁⠀⠀⠀⠉⠛⠢⠀⠀
  45. ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ /$$ /$$ /$$ /$$ /$$
  46. ⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ | $$ |__/| $$| $$
  47. ⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ | $$ /$$$$$$ /$$$$$$$ /$$| $$| $$ /$$$$$$
  48. ⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ / $$/|____ $$| $$__ $$| $$| $$| $$ |____ $$
  49. ⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ \ $$ $$/ /$$$$$$$| $$ \ $$| $$| $$| $$ /$$$$$$$
  50. ⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀ \ $$$/ /$$__ $$| $$ | $$| $$| $$| $$ /$$__ $$
  51. ⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀ \ $/ | $$$$$$$| $$ | $$| $$| $$| $$| $$$$$$$
  52. ⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀ \_/ \_______/|__/ |__/|__/|__/|__/ \_______/
  53. /$$$$$$ /$$ /$$
  54. /$$__ $$ | $$ | $$
  55. | $$ \ $$ /$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$$$$$$
  56. | $$$$$$$$ /$$__ $$| $$ /$$//$$__ $$| $$__ $$|_ $$_/ | $$ | $$ /$$__ $$ /$$__ $$ /$$_____/
  57. | $$__ $$| $$ | $$ \ $$/$$/| $$$$$$$$| $$ \ $$ | $$ | $$ | $$| $$ \__/| $$$$$$$$| $$$$$$
  58. | $$ | $$| $$ | $$ \ $$$/ | $$_____/| $$ | $$ | $$ /$$| $$ | $$| $$ | $$_____/ \____ $$
  59. | $$ | $$| $$$$$$$ \ $/ | $$$$$$$| $$ | $$ | $$$$/| $$$$$$/| $$ | $$$$$$$ /$$$$$$$/
  60. |__/ |__/ \_______/ \_/ \_______/|__/ |__/ \___/ \______/ |__/ \_______/|_______/
  61. `
  62. var title_blinking string = `
  63. ⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀
  64. ⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀
  65. ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀
  66. ⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀
  67. ⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀
  68. ⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣠⣤⣤⣼⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀
  69. ⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀
  70. ⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀
  71. ⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀
  72. ⠀⠀⠀⠀⣿⣿⣿⣿⣿⣤⣤⣤⣤⣼⣿⣿⣿⣿⣧⣤⣤⣤⣤⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
  73. ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀
  74. ⢀⣀⣀⣀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣤⠤⢤⣤⡄
  75. ⠈⠉⠉⢉⣙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⣀⣀⣀⡀⠀
  76. ⠐⠚⠋⠉⢀⣬⡿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣥⣀⡀⠈⠀⠈⠛
  77. ⠀⠀⠴⠚⠉⠀⠀⠀⠉⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠋⠁⠀⠀⠀⠉⠛⠢⠀⠀
  78. ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ /$$ /$$ /$$ /$$ /$$
  79. ⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ | $$ |__/| $$| $$
  80. ⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ | $$ /$$$$$$ /$$$$$$$ /$$| $$| $$ /$$$$$$
  81. ⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀| $$ / $$/|____ $$| $$__ $$| $$| $$| $$ |____ $$
  82. ⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ \ $$ $$/ /$$$$$$$| $$ \ $$| $$| $$| $$ /$$$$$$$
  83. ⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀ \ $$$/ /$$__ $$| $$ | $$| $$| $$| $$ /$$__ $$
  84. ⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀ \ $/ | $$$$$$$| $$ | $$| $$| $$| $$| $$$$$$$
  85. ⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀ \_/ \_______/|__/ |__/|__/|__/|__/ \_______/
  86. /$$$$$$ /$$ /$$
  87. /$$__ $$ | $$ | $$
  88. | $$ \ $$ /$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$$$$$$
  89. | $$$$$$$$ /$$__ $$| $$ /$$//$$__ $$| $$__ $$|_ $$_/ | $$ | $$ /$$__ $$ /$$__ $$ /$$_____/
  90. | $$__ $$| $$ | $$ \ $$/$$/| $$$$$$$$| $$ \ $$ | $$ | $$ | $$| $$ \__/| $$$$$$$$| $$$$$$
  91. | $$ | $$| $$ | $$ \ $$$/ | $$_____/| $$ | $$ | $$ /$$| $$ | $$| $$ | $$_____/ \____ $$
  92. | $$ | $$| $$$$$$$ \ $/ | $$$$$$$| $$ | $$ | $$$$/| $$$$$$/| $$ | $$$$$$$ /$$$$$$$/
  93. |__/ |__/ \_______/ \_/ \_______/|__/ |__/ \___/ \______/ |__/ \_______/|_______/
  94. `
  95. var cats = [][]string{
  96. strings.Split(title_idle, "\n"),
  97. strings.Split(title_blinking, "\n")}
  98. func drawTitle(s tcell.Screen, x, frame int, style tcell.Style) int{
  99. lines := cats[frame]
  100. frame = (frame + 1) % 2
  101. // Draw Title
  102. xmax, ymax := s.Size()
  103. for l:= range lines{
  104. drawText(s,x, l, xmax, ymax, style, lines[l])
  105. }
  106. return len(lines)
  107. }
  108. func main() {
  109. defStyle := tcell.StyleDefault.Background(color.Reset).Foreground(color.NewHexColor(0xf3e5ab))
  110. // Initialize screen
  111. s, err := tcell.NewScreen()
  112. if err != nil {
  113. log.Fatalf("%+v", err)
  114. }
  115. if err := s.Init(); err != nil {
  116. log.Fatalf("%+v", err)
  117. }
  118. s.SetStyle(defStyle)
  119. s.Clear()
  120. quit := func() {
  121. // You have to catch panics in a defer, clean up, and
  122. // re-raise them - otherwise your application can
  123. // die without leaving any diagnostic trace.
  124. maybePanic := recover()
  125. s.Fini()
  126. if maybePanic != nil {
  127. panic(maybePanic)
  128. }
  129. }
  130. defer quit()
  131. // Here's how to get the screen size when you need it.
  132. // xmax, ymax := s.Size()
  133. // Here's an example of how to inject a keystroke where it will
  134. // be picked up by a future read of the event queue. Note that
  135. // care should be used to avoid blocking writes to the queue if
  136. // this is done from the same thread that is responsible for reading
  137. // the queue, or else a single-party deadlock might occur.
  138. // s.EventQ() <- tcell.NewEventKey(tcell.KeyRune, rune('a'), 0)
  139. curr_menu_item := 0
  140. menu := true
  141. gap_title_menu := 3
  142. credits := false
  143. frame := 0
  144. ticker := time.NewTicker(500 * time.Millisecond)
  145. defer ticker.Stop()
  146. // Event loop
  147. for {
  148. s.Clear()
  149. title_size := drawTitle(s, 10, frame, defStyle)
  150. frame = (frame + 1) % 2
  151. xmax,ymax := s.Size()
  152. // Draw Menu
  153. if menu {
  154. drawText(s, 10, title_size+gap_title_menu, xmax, ymax, defStyle, "Use arrows to navigate. Press ENTER to select")
  155. switch curr_menu_item {
  156. case 0:
  157. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, "▶ Start New Game")
  158. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Level Selector")
  159. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  160. case 1:
  161. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " Start New Game")
  162. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, "▶ Level Selector")
  163. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  164. case 2:
  165. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " Start New Game")
  166. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Level Selector")
  167. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, "▶ Credits")
  168. }
  169. }
  170. if credits {
  171. drawText(s, 10, title_size+gap_title_menu, xmax,ymax, defStyle, "Abderrahmane Faiz")
  172. drawText(s, 10, title_size+gap_title_menu+1, xmax,ymax, defStyle, "\u001B]8;;https://afaiz.dev\u001B\\https://afaiz.dev\u001B]8;;\u001B\\")
  173. drawText(s, 10, title_size+gap_title_menu+2, xmax,ymax, defStyle, "ASCII Art found on https://emojicombos.com/cat")
  174. drawText(s, 10, title_size+gap_title_menu+4, xmax,ymax, defStyle, "Press ENTER to go back to the menu")
  175. }
  176. // Update screen
  177. s.Show()
  178. // Poll event (this can be in a select statement as well)
  179. select{
  180. case <- ticker.C:
  181. continue
  182. case ev := <-s.EventQ():
  183. // Process event
  184. switch ev := ev.(type) {
  185. default:
  186. continue
  187. case *tcell.EventResize:
  188. s.Sync()
  189. case *tcell.EventKey:
  190. if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
  191. return
  192. } else if ev.Key() == tcell.KeyUp && menu {
  193. if curr_menu_item == 0 {
  194. curr_menu_item = 2
  195. } else {
  196. curr_menu_item = curr_menu_item - 1
  197. }
  198. } else if ev.Key() == tcell.KeyDown && menu {
  199. curr_menu_item = (curr_menu_item + 1) % 3
  200. } else if ev.Key() == tcell.KeyEnter && credits {
  201. credits = false
  202. menu = true
  203. } else if ev.Key() == tcell.KeyEnter && curr_menu_item == 2 {
  204. menu = false
  205. credits = true
  206. }
  207. }
  208. }
  209. }
  210. }