1
+
2
+ const SCREENSHOT_UPDATE_DELAY = 60 ; // send a screenshot every 60 frames
3
+ const COLOR_AMBER = '#FFBF00'
4
+ const COLOR_GREEN = '#33ff33'
5
+ const COLOR_DURATION = 150
6
+ var Foods = [
7
+ { x :0.25 , y : 0.25 , color : '#3c6e6f' } ,
8
+ { x :0.5 , y : 0.25 , color : '#007727' } ,
9
+ { x :0.75 , y : 0.25 , color : '#b8aa01' } ,
10
+
11
+ { x :0.25 , y : 0.5 , color : '#0350a0' } ,
12
+ { x :0.75 , y : 0.5 , color : '#966401' } ,
13
+
14
+ { x :0.25 , y : 0.75 , color : '#48019d' } ,
15
+ { x :0.5 , y : 0.75 , color : '#730075' } ,
16
+ { x :0.75 , y : 0.75 , color : '#9c0e3e' }
17
+ ]
18
+ kontra . init ( ) ;
19
+
20
+ var sprites = [ ]
21
+
22
+ var degreesToRadians = function ( deg ) {
23
+ return deg * Math . PI / 180 ;
24
+ }
25
+
26
+ function lerp ( min , max , t ) {
27
+ return min * ( 1 - t ) + max * t
28
+ }
29
+
30
+ function damp ( a , b , lambda , dt ) {
31
+ return lerp ( a , b , 1 - Math . exp ( - lambda * dt ) )
32
+ }
33
+
34
+ function shuffle ( array ) {
35
+ var m = array . length , t , i ;
36
+
37
+ // While there remain elements to shuffle…
38
+ while ( m ) {
39
+
40
+ // Pick a remaining element…
41
+ i = Math . floor ( Math . random ( ) * m -- ) ;
42
+
43
+ // And swap it with the current element.
44
+ t = array [ m ] ;
45
+ array [ m ] = array [ i ] ;
46
+ array [ i ] = t ;
47
+ }
48
+
49
+ return array ;
50
+ }
51
+
52
+ var score = kontra . sprite ( {
53
+ x :0 ,
54
+ y :480 ,
55
+ hunger :0 ,
56
+ color : COLOR_AMBER ,
57
+ render : function ( ) {
58
+ // Make a progress bar
59
+ this . context . fillStyle = '#333' ;
60
+ this . context . fillRect ( this . x , this . y , kontra . canvas . width , kontra . canvas . height - this . y )
61
+ this . context . fillStyle = '#666' ;
62
+ let percent = this . hunger / 40.0 ;
63
+ let percentWidth = percent * kontra . canvas . width
64
+ this . context . fillRect ( this . x , this . y , percentWidth , kontra . canvas . height - this . y )
65
+
66
+ // Write the hunger
67
+ this . context . fillStyle = this . color ;
68
+ this . context . font = "48px Courier New"
69
+ this . context . textBaseline = 'top'
70
+ this . context . fillText ( this . hunger + '/40' , this . x , this . y )
71
+ }
72
+ } )
73
+ sprites . unshift ( score )
74
+
75
+ var critter = {
76
+ anchor : {
77
+ x : 0.5 ,
78
+ y : 0.5
79
+ } ,
80
+ x : kontra . canvas . width * 0.5 , // remove - 48 when anchor works with pointers
81
+ y : kontra . canvas . width * 0.5 , // Width bc we don't want to count the bar at the bottom
82
+ width :96 ,
83
+ height :96 ,
84
+ color : COLOR_GREEN ,
85
+ selected : false ,
86
+ colorFrames : 0 ,
87
+ sick : false ,
88
+ desiredRotation : 0 ,
89
+ collidesWith ( object ) {
90
+ // We're ok with the collision being a rough estimate, ignore rotation
91
+ // if (this.rotation || object.rotation) return null;
92
+
93
+ // take into account sprite anchors
94
+ let x = this . x - this . width * this . anchor . x ;
95
+ let y = this . y - this . height * this . anchor . y ;
96
+
97
+ let objX = object . x ;
98
+ let objY = object . y ;
99
+ if ( object . anchor ) {
100
+ objX -= object . width * object . anchor . x ;
101
+ objY -= object . height * object . anchor . y ;
102
+ }
103
+
104
+ return x < objX + object . width &&
105
+ x + this . width > objX &&
106
+ y < objY + object . height &&
107
+ y + this . height > objY ;
108
+ } ,
109
+ update : function ( dt ) {
110
+ if ( ! this . colorFrames || this . colorFrames <= 0 ) {
111
+ this . newColor ( )
112
+ this . sick = false
113
+ }
114
+ this . colorFrames --
115
+ if ( this . colorFrames < 15 ) {
116
+ this . desiredRotation = degreesToRadians ( - 45 )
117
+ }
118
+ this . advance ( )
119
+ this . rotation = damp ( this . rotation , this . desiredRotation , 8 , dt )
120
+
121
+ if ( this . sick && this . colorFrames > 25 ) {
122
+ if ( Math . floor ( Math . random ( ) * 4 ) == 0 ) { // One in 4 chance
123
+ let b = kontra . sprite ( {
124
+ x :this . x ,
125
+ y :this . y + this . height * 0.25 ,
126
+ color : '#ffffff' ,
127
+ dx : Math . random ( ) * 9 - 2 ,
128
+ dy :1 ,
129
+ ddy : 1.0 ,
130
+ width :12 ,
131
+ height :12 ,
132
+ ttl : 4 * 60 ,
133
+ update : function ( dt ) { // override to get desired behavior
134
+ this . advance ( )
135
+ }
136
+ } )
137
+ sprites . push ( b )
138
+ // b.x = this.x
139
+ // b.y = this.y
140
+ // b.radius = b.width = 8
141
+ // b.dx = {'up':0, 'down':0, 'left':5, 'right':-5}[this.direction]
142
+ // b.dy = {'up':5, 'down':-5, 'left':0, 'right':0}[this.direction]
143
+ // sprites.push(b)
144
+ }
145
+ }
146
+ } ,
147
+ feed : function ( color ) {
148
+ if ( this . sickFrames > 0 ) return
149
+ if ( this . color !== color ) { // Fail
150
+ this . sick = true
151
+ this . color = "#ff00ff"
152
+ this . colorFrames = 4 * 60
153
+
154
+ } else { // Success
155
+ score . hunger ++
156
+ this . colorFrames = 0
157
+
158
+ // Throw some particles
159
+ for ( let i = 0 ; i < 18 ; i ++ ) {
160
+ let particle = kontra . sprite ( {
161
+ type :'particle' ,
162
+ x : this . x ,
163
+ y : this . y ,
164
+ dx : 24 * Math . cos ( degreesToRadians ( i * 20 ) ) ,
165
+ dy : 24 * Math . sin ( degreesToRadians ( i * 20 ) ) ,
166
+ ttl : 20 ,
167
+ width :16 ,
168
+ height :16 ,
169
+ color :'#fff' ,
170
+ update : function ( dt ) {
171
+ // this.color = '#' + (this.ttl*15).toString(16) + (this.ttl*15).toString(16) + '00'
172
+ this . advance ( )
173
+ } ,
174
+ render : function ( ) {
175
+ kontra . context . save ( )
176
+ kontra . context . fillStyle = this . color
177
+ kontra . context . beginPath ( )
178
+ kontra . context . arc ( this . x , this . y , 16 , 0 , 2 * Math . PI )
179
+ kontra . context . fill ( )
180
+ kontra . context . restore ( )
181
+ }
182
+ } )
183
+ sprites . push ( particle )
184
+ }
185
+ // Shuffle the foods
186
+ shuffle ( Foods )
187
+ let foodIndex = 0 ;
188
+ sprites . forEach ( sprite => {
189
+ if ( sprite . type == 'food' ) {
190
+ sprite . desiredX = Foods [ foodIndex ] . x * kontra . canvas . width
191
+ sprite . desiredY = Foods [ foodIndex ] . y * kontra . canvas . width
192
+ sprite . dx = Math . floor ( Math . random ( ) * 2 ) == 0 ? - 1 : 1
193
+ sprite . dy = Math . floor ( Math . random ( ) * 2 ) == 0 ? - 1 : 1
194
+ foodIndex ++
195
+ }
196
+ } )
197
+ }
198
+ } ,
199
+ newColor : function ( dt ) {
200
+ this . color = Foods [ Math . floor ( Math . random ( ) * Foods . length ) ] . color
201
+ this . colorFrames = COLOR_DURATION
202
+ this . rotation -= degreesToRadians ( 180 )
203
+ this . desiredRotation = 0
204
+ }
205
+ }
206
+
207
+ let food = {
208
+ type : 'food' ,
209
+ anchor : {
210
+ x : 0.5 ,
211
+ y : 0.5
212
+ } ,
213
+ x : kontra . canvas . width / 2 , // remove - 48 when anchor works with pointers
214
+ y : kontra . canvas . width / 2 , // Width bc we don't want to count the bar at the bottom
215
+ desiredX : 240 ,
216
+ desiredY : 480 ,
217
+ width :48 ,
218
+ height :48 ,
219
+ color : COLOR_GREEN ,
220
+ selected : false ,
221
+ onDown : function ( ) {
222
+ this . selected = true ;
223
+ this . lastPosition = {
224
+ x : kontra . pointer . x ,
225
+ y : kontra . pointer . y
226
+ }
227
+ } ,
228
+ onUp : function ( ) {
229
+ this . selected = false ;
230
+ if ( this . critter ) {
231
+ if ( this . critter . collidesWith ( this ) ) {
232
+ this . critter . feed ( this . color )
233
+ }
234
+ }
235
+ } ,
236
+ update : function ( dt ) {
237
+ if ( ! kontra . pointer . pressed ( 'left' ) ) this . selected = false
238
+ if ( ! this . selected ) {
239
+ // damp it back to desiredX
240
+ this . desiredX += this . dx
241
+ this . desiredY += this . dy
242
+ if ( this . desiredX < 0 ) this . dx = Math . abs ( this . dx )
243
+ if ( this . desiredX > kontra . canvas . width ) this . dx = - 1 * Math . abs ( this . dx )
244
+ if ( this . desiredY < 0 ) this . dy = Math . abs ( this . dx )
245
+ if ( this . desiredY > kontra . canvas . width ) this . dy = - 1 * Math . abs ( this . dy )
246
+
247
+ this . x = damp ( this . x , this . desiredX , 8 , dt )
248
+ this . y = damp ( this . y , this . desiredY , 8 , dt )
249
+ this . advance ( )
250
+ } else {
251
+ let dx = kontra . pointer . x - this . lastPosition . x
252
+ let dy = kontra . pointer . y - this . lastPosition . y
253
+
254
+ this . x += dx
255
+ this . y += dy
256
+
257
+ // // Flinging
258
+ // this.dx = dx
259
+ // this.dy = dy
260
+
261
+ this . lastPosition = {
262
+ x : kontra . pointer . x ,
263
+ y : kontra . pointer . y
264
+ }
265
+
266
+ // Collision
267
+
268
+ }
269
+ } ,
270
+ }
271
+
272
+ var debugSprite = kontra . sprite ( {
273
+ render : function ( ) {
274
+ kontra . context . save ( )
275
+ this . context . fillStyle = "#ff00ff" ;
276
+ this . context . font = "24px Courier New"
277
+ this . context . textBaseline = 'top'
278
+ // this.context.fillText("pointer: " + kontra.pointer.pressed('left'), 0, 0)
279
+
280
+ kontra . context . restore ( )
281
+ }
282
+ } )
283
+ sprites . push ( debugSprite )
284
+
285
+ let reset = function ( ) {
286
+ // Place the critter in the middle
287
+ let critterSprite = kontra . sprite ( critter )
288
+ sprites . push ( critterSprite )
289
+ kontra . pointer . track ( critterSprite )
290
+ // Position foods
291
+ for ( let i = 0 ; i < Foods . length ; i ++ ) {
292
+ let s = kontra . sprite ( food )
293
+ s . critter = critterSprite
294
+ s . color = Foods [ i ] . color
295
+ s . x = s . desiredX = Foods [ i ] . x * kontra . canvas . width // remove - 24 when anchor works
296
+ s . y = s . desiredY = Foods [ i ] . y * kontra . canvas . width // Squared screen
297
+ kontra . pointer . track ( s )
298
+ sprites . push ( s )
299
+ }
300
+ }
301
+
302
+ var loop = kontra . gameLoop ( { // create the main game loop
303
+ fps : 60 ,
304
+ update ( dt ) { // update the game state
305
+ sprites . forEach ( sprite => {
306
+ sprite . update ( dt )
307
+ } )
308
+ sprites = sprites . filter ( sprite => sprite . isAlive ( ) ) ;
309
+ } ,
310
+ render ( ) { // render the game state
311
+ sprites . forEach ( sprite => {
312
+ sprite . render ( ) ;
313
+ } )
314
+ }
315
+ } ) ;
316
+
317
+ reset ( )
318
+ loop . start ( ) ;
319
+ this . loop = loop
0 commit comments