1 ! Copyright (C) 2015 Sankaranarayanan Viswanathan.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs calendar combinators
4 combinators.short-circuit destructors formatting kernel math
5 math.vectors namespaces opengl opengl.textures sequences sets
6 snake-game.game snake-game.sprites timers ui ui.gadgets
7 ui.gadgets.worlds ui.gestures ui.render ;
13 TUPLE: snake-gadget < gadget
14 snake-game timer textures ;
16 : start-new-game ( snake-gadget -- )
17 <snake-game> >>snake-game drop ;
19 : <snake-gadget> ( -- snake-gadget )
20 snake-gadget new [ start-new-game ] keep ;
22 CONSTANT: snake-game-cell-size 20
24 : game-loc>screen-loc ( loc -- loc )
25 [ snake-game-cell-size * ] map ;
27 : lookup-texture ( key -- texture )
28 game-textures get at ;
30 : draw-sprite* ( key screen-loc -- )
31 [ lookup-texture draw-texture ] with-translation ;
33 : draw-sprite ( grid-loc key -- )
34 swap game-loc>screen-loc draw-sprite* ;
36 : draw-food ( loc -- )
39 : draw-background ( -- )
40 { 0 0 } "background" draw-sprite ;
42 : draw-snake-head ( loc facing-dir -- )
43 dup name>> rest "head-" prepend [
44 [ game-loc>screen-loc ] dip {
45 { :right [ { -20 -10 } ] }
46 { :down [ { -10 -20 } ] }
48 { :left [ { 0 -10 } ] }
50 ] dip swap draw-sprite* ;
52 : draw-snake-body ( loc from-dir to-dir -- )
53 [ name>> rest ] bi@ "body-%s-%s" sprintf draw-sprite ;
55 : draw-snake-tail ( loc facing-dir -- )
56 name>> rest "tail-" prepend draw-sprite ;
58 : draw-snake-part ( loc from-dir snake-part -- )
60 { :head [ drop opposite-dir draw-snake-head ] }
61 { :body [ dir>> draw-snake-body ] }
62 { :tail [ drop draw-snake-tail ] }
65 : next-snake-loc-from-dir ( loc from-dir snake-part -- new-loc new-from-dir )
66 nip dir>> [ move-loc ] keep ;
68 : draw-snake ( loc from-dir snake -- )
71 [ next-snake-loc-from-dir ] 3bi
73 ! make sure to draw the head again
74 first draw-snake-part ;
76 : game-status ( snake-game -- str )
80 { [ dup game-over?>> ] [ drop "Game Over" ] }
81 { [ dup paused?>> ] [ drop "Game Paused" ] }
82 [ drop "Game In Progress" ]
84 ] bi "Score: %d -- %s" sprintf ;
86 : update-status ( gadget -- )
87 [ snake-game>> game-status ] keep show-status ;
89 : do-updates ( gadget -- )
90 [ snake-game>> do-game-step ]
95 : toggle-game-pause ( snake-gadget -- )
96 snake-game>> [ not ] change-paused? drop ;
98 M: snake-gadget graft*
99 dup '[ _ do-updates ] 200 milliseconds every >>timer
100 snake-textures >>textures
103 M: snake-gadget ungraft*
104 [ stop-timer f ] change-timer
105 dup find-gl-context ! so texture disposing works properly
106 [ values dispose-each f ] change-textures
109 M: snake-gadget pref-dim*
110 drop snake-game-dim [ snake-game-cell-size * 20 + ] map ;
112 M: snake-gadget draw-gadget*
113 [ textures>> game-textures ] keep '[
117 [ food-loc>> [ draw-food ] when* ]
120 [ snake-dir>> opposite-dir ]
127 : key-dir ( key -- dir )
135 : quit-key? ( key -- ? )
136 HS{ "ESC" "q" "Q" } in? ;
138 : pause-key? ( key -- ? )
139 HS{ " " "SPACE" "p" "P" } in? ;
141 : new-game-key? ( key -- ? )
142 HS{ "ENTER" "RET" "n" "N" } in? ;
144 M: snake-gadget handle-gesture
147 { [ dup quit-key? ] [ drop close-window ] }
148 { [ dup pause-key? ] [ drop toggle-game-pause ] }
149 { [ dup new-game-key? ] [ drop start-new-game ] }
152 swap snake-game>> dup {
153 [ game-in-progress? ]
154 [ snake-dir>> opposite-dir pick = not ]
155 } 1&& [ next-turn-dir<< ] [ 2drop ] if