main.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package main
  2. import (
  3. _ "embed"
  4. "fmt"
  5. "log"
  6. "strings"
  7. "time"
  8. "github.com/gdamore/tcell/v3"
  9. "github.com/gdamore/tcell/v3/color"
  10. )
  11. //go:embed assets/title/title_idle.txt
  12. var title_idle string
  13. //go:embed assets/title/title_blinking.txt
  14. var title_blinking string
  15. func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) {
  16. row := y1
  17. col := x1
  18. var width int
  19. for text != "" {
  20. text, width = s.Put(col, row, text, style)
  21. col += width
  22. if col >= x2 {
  23. row++
  24. col = x1
  25. }
  26. if row > y2 {
  27. break
  28. }
  29. if width == 0 {
  30. // incomplete grapheme at end of string
  31. break
  32. }
  33. }
  34. }
  35. func drawTitle(s tcell.Screen, x, padding_top, frame int, style tcell.Style, titles [][]string) int{
  36. lines := titles[frame]
  37. frame = (frame + 1) % 2
  38. // Draw Title
  39. xmax, ymax := s.Size()
  40. for l:= range lines{
  41. drawText(s,x, l+padding_top, xmax, ymax, style, lines[l])
  42. }
  43. return len(lines)
  44. }
  45. func DrawMainMenu(s tcell.Screen, defStyle tcell.Style, old_session bool) bool{
  46. cats := [][]string{
  47. strings.Split(string(title_idle), "\n"),
  48. strings.Split(string(title_blinking), "\n")}
  49. var first_item_menu string
  50. if (old_session){
  51. first_item_menu = "Continue"
  52. } else {
  53. first_item_menu = "Start New Game"
  54. }
  55. curr_menu_item := 0
  56. menu := true
  57. gap_title_menu := 3
  58. settings, credits := false, false
  59. frame := 0
  60. ticker := time.NewTicker(500 * time.Millisecond)
  61. defer ticker.Stop()
  62. // Event loop
  63. for {
  64. s.Clear()
  65. title_size := drawTitle(s, 10, 2, frame, defStyle, cats)
  66. frame = (frame + 1) % 2
  67. xmax,ymax := s.Size()
  68. // Draw Menu
  69. if menu {
  70. drawText(s, 10, title_size+gap_title_menu, xmax, ymax, defStyle, "Use arrows or WASD to navigate. Press ENTER to select")
  71. switch curr_menu_item {
  72. case 0:
  73. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, "▶ "+first_item_menu)
  74. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Settings")
  75. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  76. case 1:
  77. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " "+first_item_menu)
  78. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, "▶ Settings")
  79. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, " Credits")
  80. case 2:
  81. drawText(s, 10, title_size+gap_title_menu+2, xmax, ymax, defStyle, " "+first_item_menu)
  82. drawText(s, 10, title_size+gap_title_menu+3, xmax, ymax, defStyle, " Settings")
  83. drawText(s, 10, title_size+gap_title_menu+4, xmax, ymax, defStyle, "▶ Credits")
  84. }
  85. }
  86. if credits {
  87. drawText(s, 10, title_size+gap_title_menu, xmax,ymax, defStyle, "Abderrahmane Faiz")
  88. drawText(s, 10, title_size+gap_title_menu+1, xmax,ymax, defStyle, "\u001B]8;;https://afaiz.dev\u001B\\https://afaiz.dev\u001B]8;;\u001B\\")
  89. drawText(s, 10, title_size+gap_title_menu+2, xmax,ymax, defStyle, "ASCII Art found on https://emojicombos.com/cat")
  90. drawText(s, 10, title_size+gap_title_menu+4, xmax,ymax, defStyle, "Press ENTER to go back to the menu")
  91. }
  92. if settings {
  93. drawText(s, 10, title_size+gap_title_menu, xmax,ymax, defStyle, "Nothing to customize for now ^^'")
  94. drawText(s, 10, title_size+gap_title_menu+4, xmax,ymax, defStyle, "Press ENTER to go back to the menu")
  95. }
  96. // Update screen
  97. s.Show()
  98. // Poll event (this can be in a select statement as well)
  99. select{
  100. case <- ticker.C:
  101. continue
  102. case ev := <-s.EventQ():
  103. // Process event
  104. switch ev := ev.(type) {
  105. default:
  106. continue
  107. case *tcell.EventResize:
  108. s.Sync()
  109. case *tcell.EventKey:
  110. if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
  111. return false
  112. } else if (ev.Key() == tcell.KeyUp || ev.Str() == "w" || ev.Str() == "W") && menu {
  113. if curr_menu_item == 0 {
  114. curr_menu_item = 2
  115. } else {
  116. curr_menu_item = curr_menu_item - 1
  117. }
  118. } else if (ev.Key() == tcell.KeyDown || ev.Str() == "s" || ev.Str() == "S") && menu {
  119. curr_menu_item = (curr_menu_item + 1) % 3
  120. } else if ev.Key() == tcell.KeyEnter && credits {
  121. credits = false
  122. menu = true
  123. } else if ev.Key() == tcell.KeyEnter && settings {
  124. settings = false
  125. menu = true
  126. } else if ev.Key() == tcell.KeyEnter && curr_menu_item == 2 {
  127. menu = false
  128. credits = true
  129. } else if ev.Key() == tcell.KeyEnter && curr_menu_item == 1 {
  130. menu = false
  131. settings = true
  132. } else if ev.Key() == tcell.KeyEnter && curr_menu_item == 0 {
  133. return true
  134. }
  135. }
  136. }
  137. }
  138. }
  139. func DrawCat(s tcell.Screen, x1,y1,x2,y2 int, style tcell.Style, direction rune) {
  140. mycat := []string{" /\\_/\\","( o.o )", " > ^ <"}
  141. switch direction{
  142. case 'l':
  143. mycat[len(mycat)-1] = " < ^ <"
  144. case 'r':
  145. mycat[len(mycat)-1] = " > ^ >"
  146. default :
  147. mycat[len(mycat)-1] = " > ^ <"
  148. }
  149. for index,cat := range mycat{
  150. drawText(s, x1, y1 + index, x2,y2, style, cat)
  151. }
  152. }
  153. func main() {
  154. // Initialize screen
  155. defStyle := tcell.StyleDefault.Background(color.Black).Foreground(color.NewHexColor(0xf3e5ab))
  156. s, err := tcell.NewScreen()
  157. if err != nil {
  158. log.Fatalf("%+v", err)
  159. }
  160. if err := s.Init(); err != nil {
  161. log.Fatalf("%+v", err)
  162. }
  163. s.SetStyle(defStyle)
  164. quit := func() {
  165. maybePanic := recover()
  166. s.Fini()
  167. if maybePanic != nil {
  168. panic(maybePanic)
  169. }
  170. }
  171. defer quit()
  172. // Main Menu
  173. start_game := DrawMainMenu(s, defStyle, false)
  174. if (!start_game){
  175. return
  176. }
  177. score := 0
  178. level := 1
  179. direction := 'n'
  180. x_offset := 0
  181. game_loop:
  182. go_to_menu := false
  183. for {
  184. if (go_to_menu){
  185. break
  186. }
  187. s.Clear()
  188. xmax,ymax := s.Size()
  189. length_factor := 5
  190. width_factor := 4
  191. up_right_border := 3*xmax/width_factor
  192. width_map := up_right_border - (xmax/width_factor) + 1
  193. down_right_border := 4*ymax/length_factor
  194. length_map := down_right_border - (ymax/length_factor) + 1
  195. // Draw UI
  196. drawText(s, xmax/width_factor, ymax/length_factor - 5, xmax, ymax, defStyle, "Use arrows or AD to move. Press q to return to Main Menu.")
  197. drawText(s, xmax/width_factor, ymax/length_factor - 3, xmax, ymax, defStyle, fmt.Sprintf("Level: %d", level))
  198. drawText(s, xmax/width_factor, ymax/length_factor - 2, xmax, ymax, defStyle, fmt.Sprintf("Score: %d", score))
  199. // Draw Game Map Borders
  200. // upper border
  201. for i := range(width_map){
  202. drawText(s,xmax/width_factor+i, ymax/length_factor-1, xmax, ymax, defStyle, "=")
  203. }
  204. // bottom border
  205. for i := range(width_map){
  206. drawText(s,xmax/width_factor+i, down_right_border, xmax, ymax, defStyle, "=")
  207. }
  208. // left border
  209. for i := range(length_map){
  210. drawText(s,xmax/width_factor, ymax/length_factor+i, xmax, ymax, defStyle, "|")
  211. }
  212. // right border
  213. for i := range(length_map){
  214. drawText(s,up_right_border, ymax/length_factor+i, xmax, ymax, defStyle, "|")
  215. }
  216. starting_point_x := (up_right_border + xmax/width_factor)/2 + x_offset
  217. DrawCat(s, starting_point_x, down_right_border - 3, xmax,ymax,defStyle, direction)
  218. s.Show()
  219. ev := <- s.EventQ()
  220. // Process event
  221. switch ev := ev.(type) {
  222. case *tcell.EventResize:
  223. s.Sync()
  224. case *tcell.EventKey:
  225. if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
  226. return
  227. } else if (ev.Key() == tcell.KeyRight || ev.Str() == "d" || ev.Str() == "D") {
  228. direction = 'r'
  229. if (starting_point_x + 1 < (up_right_border-6)){
  230. x_offset += 1
  231. }
  232. } else if (ev.Key() == tcell.KeyLeft || ev.Str() == "a" || ev.Str() == "A") {
  233. direction = 'l'
  234. if (starting_point_x + 1 > (xmax/width_factor + 2)){
  235. x_offset -= 1
  236. }
  237. } else if (ev.Key() == tcell.KeyUp || ev.Str() == "w" || ev.Str() == "W") {
  238. direction = 'n'
  239. } else if (ev.Str() == "q" || ev.Str() == "Q"){
  240. go_to_menu = true
  241. }
  242. }
  243. }
  244. start_game = DrawMainMenu(s, defStyle, true)
  245. if (start_game){
  246. goto game_loop
  247. }
  248. }