@@ -25,6 +25,9 @@ export default class Runner {
25
25
playing = false
26
26
crashed = false
27
27
paused = false
28
+ inverted = false // Night mode on
29
+ inverTimer = 0 // Night mode start time
30
+ invertTrigger = false
28
31
updatePending = false
29
32
resizeTimerId_ : NodeJS . Timer | null = null
30
33
@@ -40,7 +43,7 @@ export default class Runner {
40
43
41
44
constructor ( outerContainerId : string , optConfig ?: ConfigDict ) {
42
45
this . outerContainerEl = document . querySelector ( outerContainerId ) !
43
- this . config = optConfig || Runner . config
46
+ this . config = optConfig || Object . assign ( Runner . config , Runner . normalConfig )
44
47
45
48
this . loadImages ( )
46
49
}
@@ -85,7 +88,7 @@ export default class Runner {
85
88
this . outerContainerEl . appendChild ( this . containerEl )
86
89
87
90
// Load background class Horizon
88
- this . horizon = new Horizon ( this . canvas , this . spriteDef , this . dimensions , Runner . config . GAP_COEFFICIENT )
91
+ this . horizon = new Horizon ( this . canvas , this . spriteDef , this . dimensions , this . config . GAP_COEFFICIENT )
89
92
90
93
this . distanceMeter = new DistanceMeter ( this . canvas , this . spriteDef . TEXT_SPRITE , this . dimensions . WIDTH )
91
94
@@ -159,19 +162,45 @@ export default class Runner {
159
162
this . playIntro ( )
160
163
}
161
164
165
+ // The horizon doesn't move until the intro is over.
162
166
if ( this . playingIntro ) {
163
167
this . horizon . update ( 0 , this . currentSpeed , hasObstacles )
164
- } else {
168
+ } else if ( ! this . crashed ) {
165
169
deltaTime = ! this . activated ? 0 : deltaTime
166
- this . horizon . update ( deltaTime , this . currentSpeed , hasObstacles )
170
+ this . horizon . update ( deltaTime , this . currentSpeed , hasObstacles , this . inverted )
167
171
}
168
172
169
- if ( this . currentSpeed < Runner . config . MAX_SPEED ) {
170
- this . currentSpeed += Runner . config . ACCELERATION
173
+ if ( this . currentSpeed < this . config . MAX_SPEED ) {
174
+ this . currentSpeed += this . config . ACCELERATION
171
175
}
172
176
173
177
this . distanceRan += ( this . currentSpeed * deltaTime ) / this . msPerFrame
174
- let playAchievementSound = this . distanceMeter ?. update ( deltaTime , Math . ceil ( this . distanceRan ) )
178
+ let playAchievementSound = this . distanceMeter . update ( deltaTime , Math . ceil ( this . distanceRan ) )
179
+ }
180
+
181
+ // Night mode.
182
+ if ( this . inverTimer > this . config . INVERT_FADE_DURATION ) {
183
+ // 夜晚模式结束
184
+ this . inverTimer = 0
185
+ this . invertTrigger = false
186
+ this . invert ( false )
187
+ } else if ( this . inverTimer ) {
188
+ // 处于夜晚模式,更新其时间
189
+ this . inverTimer += deltaTime
190
+ } else {
191
+ // 还没进入夜晚模式
192
+ // 游戏移动的距离
193
+ const actualDistance = this . distanceMeter . getActualDistance ( Math . ceil ( this . distanceRan ) )
194
+
195
+ if ( actualDistance > 0 ) {
196
+ // 每移动指定距离就触发一次夜晚模式
197
+ this . invertTrigger = ! ( actualDistance % this . config . INVERT_DISTANCE )
198
+
199
+ if ( this . invertTrigger && this . inverTimer === 0 ) {
200
+ this . inverTimer += deltaTime
201
+ this . invert ( false )
202
+ }
203
+ }
175
204
}
176
205
177
206
if ( this . playing ) {
@@ -285,11 +314,26 @@ export default class Runner {
285
314
this . containerEl . style . transform = "scale(" + scale + ") translateY(" + translateY + "px)"
286
315
}
287
316
317
+ /**
318
+ * Inverts the current page / canvas colors.
319
+ */
320
+ invert ( reset : boolean ) {
321
+ const htmlEl = document . firstElementChild
322
+
323
+ if ( reset ) {
324
+ htmlEl ?. classList . toggle ( Runner . classes . INVERTED , false )
325
+ this . inverTimer = 0
326
+ this . inverted = false
327
+ } else {
328
+ this . inverted = htmlEl ?. classList . toggle ( Runner . classes . INVERTED , this . invertTrigger ) !
329
+ }
330
+ }
331
+
288
332
onVisibilityChange ( e : Event ) {
289
333
console . log ( e . type )
290
334
if ( document . hidden || e . type === Runner . events . BLUR || document . visibilityState != "visible" ) {
291
335
this . stop ( )
292
-
336
+
293
337
this . gameOver ( )
294
338
} else if ( ! this . crashed ) {
295
339
this . play ( )
@@ -442,29 +486,47 @@ export default class Runner {
442
486
RESTART : { Enter : 1 } as any // Enter
443
487
}
444
488
489
+ /**
490
+ * Default game configuration.
491
+ * Shared config for all versions of the game. Additional parameters are
492
+ * defined in Runner.normalConfig and Runner.slowConfig.
493
+ */
445
494
static config = {
446
- SPEED : 6 ,
495
+ AUDIOCUE_PROXIMITY_THRESHOLD : 190 ,
496
+ AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y : 250 ,
447
497
BG_CLOUD_SPEED : 0.2 ,
498
+ BOTTOM_PAD : 10 ,
499
+ // Scroll Y threshold at which the game can be activated.
500
+ CANVAS_IN_VIEW_OFFSET : - 10 ,
501
+ CLEAR_TIME : 3000 ,
448
502
CLOUD_FREQUENCY : 0.5 ,
449
- GAP_COEFFICIENT : 0.6 ,
503
+ FADE_DURATION : 1 ,
504
+ FLASH_DURATION : 1000 ,
505
+ GAMEOVER_CLEAR_TIME : 1200 ,
506
+ INITIAL_JUMP_VELOCITY : 12 ,
507
+ INVERT_FADE_DURATION : 12000 ,
508
+ MAX_BLINK_COUNT : 3 ,
450
509
MAX_CLOUDS : 6 ,
451
- MAX_SPEED : 12 ,
452
510
MAX_OBSTACLE_LENGTH : 3 ,
453
511
MAX_OBSTACLE_DUPLICATION : 2 ,
454
- CLEAR_TIME : 3000 ,
455
- ACCELERATION : 0.001 ,
456
- BOTTOM_PAD : 10 ,
457
- GAMEOVER_CLEAR_TIME : 750 ,
458
- GRAVITY : 0.6 ,
459
- INITIAL_JUMP_VELOCITY : 12 ,
460
- MIN_JUMP_HEIGHT : 35 ,
461
- MOBILE_SPEED_COEFFICIENT : 1.2 ,
462
512
RESOURCE_TEMPLATE_ID : "audio-resources" ,
513
+ SPEED : 6 ,
463
514
SPEED_DROP_COEFFICIENT : 3 ,
464
515
ARCADE_MODE_INITIAL_TOP_POSITION : 35 ,
465
516
ARCADE_MODE_TOP_POSITION_PERCENT : 0.1
466
517
}
467
518
519
+ static normalConfig = {
520
+ ACCELERATION : 0.001 ,
521
+ AUDIOCUE_PROXIMITY_THRESHOLD : 190 ,
522
+ AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y : 250 ,
523
+ GAP_COEFFICIENT : 0.6 ,
524
+ INVERT_DISTANCE : 100 ,
525
+ MAX_SPEED : 13 ,
526
+ MOBILE_SPEED_COEFFICIENT : 1.2 ,
527
+ SPEED : 6
528
+ }
529
+
468
530
static imageSprite : HTMLImageElement
469
531
470
532
static spriteDefinition = {
0 commit comments