1+ /*
2+ * Copyright (c) 2013, Javier Ayres
3+ *
4+ * This file is part of Lona.
5+ *
6+ * Lona is free software: you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation, either version 3 of the License, or
9+ * (at your option) any later version.
10+ *
11+ * Lona is distributed in the hope that it will
12+ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License along with
17+ * Lona. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+ //Snake constants
21+ var SPEED = 0.003 ; //Rotation speed, in radians/ms.
22+ var DISTANCE = 0.5 ; //Distance between body parts, in radians.
23+ var RADIUS , LINEAR_WIDTH , CIRCULAR_WIDTH ;
24+
25+ //Display constants
26+ var SCREEN_WIDTH , SCREEN_HEIGHT , FOCI_X , FOCUS_1_Y , FOCUS_2_Y , BORDER_WIDTH ;
27+ var MAX_SCORE_KEY = 'hs' ;
28+
29+ //Global snake variables
30+ var centers , item , rotDiff , now , lastRepaint , score , isPaused ;
31+ //Global screen variables
32+ var container , canvas , svg , ellipse , ctx , scoreSpan , pauseSpan , maxSpan , gameOverContainer , gameOverMessage ;
33+
34+ function initScreen ( ) {
35+ container = document . getElementById ( 'container' ) ;
36+ canvas = document . getElementById ( 'c' ) ;
37+ svg = document . getElementById ( 's' ) ;
38+ ellipse = document . getElementById ( 'e' ) ;
39+ scoreSpan = document . getElementById ( 'score' ) ;
40+ pauseSpan = document . getElementById ( 'pause' ) ;
41+ maxSpan = document . getElementById ( 'max' ) ;
42+ gameOverContainer = document . getElementById ( 'game-over-container' ) ;
43+ gameOverMessage = document . getElementById ( 'game-over' ) ;
44+
45+ //Get support for touch events
46+ if ( ! ! ( 'ontouchstart' in window ) ) {
47+ document . body . addEventListener ( 'touchstart' , tap , false ) ;
48+ } else {
49+ document . body . addEventListener ( 'click' , tap , false ) ;
50+ }
51+
52+ //Get support of requestAnimationFrame
53+ if ( typeof requestAnimationFrame == 'undefined' ) {
54+ window . requestAnimationFrame = mozRequestAnimationFrame ;
55+ }
56+
57+ //Get window size
58+ if ( window . innerWidth / window . innerHeight > 2 / 3 ) {
59+ SCREEN_HEIGHT = window . innerHeight ;
60+ SCREEN_WIDTH = Math . round ( window . innerHeight * 2 / 3 ) ;
61+ //canvas.style.left = Math.round((window.innerWidth - SCREEN_WIDTH) / 2) + 'px';
62+ //svg.style.left = Math.round((window.innerWidth - SCREEN_WIDTH) / 2) + 'px';
63+ } else {
64+ SCREEN_WIDTH = window . innerWidth ;
65+ SCREEN_HEIGHT = Math . round ( window . innerWidth * 3 / 2 ) ;
66+ }
67+ BORDER_WIDTH = Math . round ( SCREEN_WIDTH / 64 ) ;
68+ //Set lona's constants based on screen dimentions
69+ RADIUS = Math . round ( SCREEN_WIDTH / 12.8 ) ;
70+ LINEAR_WIDTH = Math . round ( SCREEN_WIDTH / 13.3 ) ;
71+ CIRCULAR_WIDTH = LINEAR_WIDTH / ( RADIUS * 2 ) ;
72+
73+ //Set canvas size
74+ container . style . width = SCREEN_WIDTH + 'px' ;
75+ container . style . height = SCREEN_HEIGHT + 'px' ;
76+ canvas . width = SCREEN_WIDTH ;
77+ canvas . height = SCREEN_HEIGHT ;
78+ svg . setAttribute ( 'width' , SCREEN_WIDTH ) ;
79+ svg . setAttribute ( 'height' , SCREEN_HEIGHT ) ;
80+ ellipse . setAttribute ( 'cx' , SCREEN_WIDTH / 2 ) ;
81+ ellipse . setAttribute ( 'cy' , SCREEN_HEIGHT / 2 ) ;
82+ ellipse . setAttribute ( 'rx' , SCREEN_WIDTH / 2 - BORDER_WIDTH ) ;
83+ ellipse . setAttribute ( 'ry' , SCREEN_HEIGHT / 2 - BORDER_WIDTH ) ;
84+ ellipse . setAttribute ( 'stroke-width' , BORDER_WIDTH * 2 ) ;
85+ document . body . style . fontSize = Math . round ( SCREEN_WIDTH / 8 ) + 'px' ;
86+
87+ //Shrink SCREEN_WIDTH and SCREEN_HEIGHT to take the ellipse's border into account
88+ SCREEN_WIDTH -= BORDER_WIDTH * 4 ;
89+ SCREEN_HEIGHT -= BORDER_WIDTH * 4 ;
90+
91+ //Set ellipse's centers
92+ FOCI_X = Math . round ( SCREEN_WIDTH / 2 ) + BORDER_WIDTH * 2 ;
93+ //f² = a² - b²
94+ FOCUS_1_Y = SCREEN_HEIGHT / 2 - Math . sqrt ( Math . pow ( ( SCREEN_HEIGHT - LINEAR_WIDTH ) / 2 , 2 ) - Math . pow ( ( SCREEN_WIDTH - LINEAR_WIDTH ) / 2 , 2 ) ) + BORDER_WIDTH * 2 ;
95+ FOCUS_2_Y = SCREEN_HEIGHT / 2 + Math . sqrt ( Math . pow ( ( SCREEN_HEIGHT - LINEAR_WIDTH ) / 2 , 2 ) - Math . pow ( ( SCREEN_WIDTH - LINEAR_WIDTH ) / 2 , 2 ) ) + BORDER_WIDTH * 2 ;
96+
97+ isPaused = false ;
98+ if ( typeof ( Storage ) !== "undefined" ) {
99+ max . innerHTML = localStorage . getItem ( MAX_SCORE_KEY ) ;
100+ if ( max . innerHTML == '' ) {
101+ max . innerHTML = '0' ;
102+ }
103+ }
104+
105+ ctx = canvas . getContext ( '2d' ) ;
106+ ctx . lineWidth = LINEAR_WIDTH ;
107+ ctx . strokeStyle = '#FFDB21' ;
108+ ctx . fillStyle = '#FFDB21' ;
109+ ctx . lineCap = 'round' ;
110+
111+ go ( ) ;
112+ }
113+
114+ function go ( ) {
115+ centers = new Array ( new Center ( Math . round ( SCREEN_WIDTH / 2 ) , Math . round ( SCREEN_HEIGHT / 2 ) , 1 , 0 , DISTANCE * 9 ) ) ;
116+ item = new Item ( ) ;
117+ lastRepaint = new Date ( ) . getTime ( ) ;
118+ score = 0 ;
119+ scoreSpan . innerHTML = score + '' ;
120+ requestAnimationFrame ( loop ) ;
121+ }
122+
123+ function loop ( ) {
124+ if ( ! isPaused ) {
125+ now = new Date ( ) . getTime ( ) ;
126+ rotDiff = SPEED * ( now - lastRepaint ) ;
127+ ctx . clearRect ( 0 , 0 , SCREEN_WIDTH + BORDER_WIDTH * 4 , SCREEN_HEIGHT + BORDER_WIDTH * 4 ) ;
128+ centers [ centers . length - 1 ] . moveStart ( rotDiff ) ;
129+ if ( item . isDrawn ( ) && centers [ centers . length - 1 ] . collide ( item . getPosition ( ) ) ) {
130+ item . reset ( ) ;
131+ centers [ 0 ] . grow ( ) ;
132+ score ++ ;
133+ scoreSpan . innerHTML = score + '' ;
134+ if ( parseInt ( max . innerHTML ) < score ) {
135+ max . innerHTML = scoreSpan . innerHTML ;
136+ }
137+ }
138+ item . draw ( ) ;
139+ for ( var i = 0 ; i < centers . length && rotDiff > 0 ; i ++ ) {
140+ rotDiff = centers [ i ] . moveEnd ( rotDiff ) ;
141+ if ( centers [ i ] . mustShift ( ) ) {
142+ centers . shift ( ) ;
143+ }
144+ }
145+ var collision = false ;
146+ var offBorders = false ;
147+ var currentLength = 0 ;
148+ for ( var j = centers . length - 1 ; j >= 0 ; j -- ) {
149+ currentLength += centers [ j ] . getLength ( ) ;
150+ if ( currentLength >= 2 * Math . PI - 2 * CIRCULAR_WIDTH && ! collision ) {
151+ if ( j == centers . length - 1 ) {
152+ collision = centers [ j ] . collideSelf ( ) ;
153+ offBorders = centers [ j ] . isOffBorders ( ) ;
154+ } else {
155+ collision = centers [ j ] . collide ( centers [ centers . length - 1 ] . getEndPosition ( ) ) ;
156+ }
157+ }
158+ if ( j == centers . length - 1 ) {
159+ offBorders = centers [ j ] . isOffBorders ( ) ;
160+ }
161+ centers [ j ] . draw ( ) ;
162+ }
163+ if ( collision || offBorders ) {
164+ gameOver ( ) ;
165+ if ( score > localStorage . getItem ( MAX_SCORE_KEY ) ) {
166+ localStorage . setItem ( MAX_SCORE_KEY , score ) ;
167+ }
168+ } else {
169+ requestAnimationFrame ( loop ) ;
170+ lastRepaint = now ;
171+ }
172+ }
173+ }
174+
175+ function tap ( event ) {
176+ if ( ! isPaused ) {
177+ if ( event . target . id != 'pause' && event . target . id != 'about' ) {
178+ var last = centers [ centers . length - 1 ] ;
179+ centers . push (
180+ new Center (
181+ last . x + Math . cos ( last . direction > 0 ? last . endRot : last . startRot ) * 2 * RADIUS ,
182+ last . y + Math . sin ( last . direction > 0 ? last . endRot : last . startRot ) * 2 * RADIUS ,
183+ - last . direction ,
184+ ( Math . PI + ( last . direction > 0 ? last . endRot : last . startRot ) ) ,
185+ ( Math . PI + ( last . direction > 0 ? last . endRot : last . startRot ) )
186+ )
187+ ) ;
188+ }
189+ }
190+ }
191+
192+ function normalizeAngle ( angle ) {
193+ return angle >= 0 ? angle % ( Math . PI * 2 ) : angle % ( Math . PI * 2 ) + ( Math . PI * 2 ) ;
194+ }
195+
196+ function pause ( ) {
197+ if ( isPaused ) {
198+ isPaused = false ;
199+ lastRepaint = new Date ( ) . getTime ( ) ;
200+ pauseSpan . setAttribute ( 'class' , 'icon-pause' ) ;
201+ loop ( ) ;
202+ } else {
203+ isPaused = true ;
204+ pauseSpan . setAttribute ( 'class' , 'icon-play' ) ;
205+ //⟳
206+ }
207+ }
208+
209+ function showAbout ( ) {
210+ window . location = 'about.html' ;
211+ }
212+
213+ function gameOver ( ) {
214+ gameOverContainer . style . visibility = 'visible' ;
215+ gameOverMessage . style . visibility = 'visible' ;
216+ gameOverContainer . style . opacity = 0.3 ;
217+ gameOverMessage . style . opacity = 1 ;
218+ }
219+
220+ function restart ( ) {
221+ gameOverContainer . style . visibility = 'hidden' ;
222+ gameOverMessage . style . visibility = 'hidden' ;
223+ gameOverContainer . style . opacity = 0 ;
224+ gameOverMessage . style . opacity = 0 ;
225+ go ( ) ;
226+ }
0 commit comments