diff --git a/assets/images/button.png b/assets/images/button.png new file mode 100644 index 0000000..a4ad799 Binary files /dev/null and b/assets/images/button.png differ diff --git a/lib/game_state.dart b/lib/game_state.dart index 6051951..46f77cd 100644 --- a/lib/game_state.dart +++ b/lib/game_state.dart @@ -91,6 +91,14 @@ class GameState extends Component { return distance ~/ 10 + numCoins * 1000000; } + int getPlayerScore() { + return getScore() ~/ 10000; + } + + int getPlayerDistance() { + return distance ~/ 1000000; + } + double getVelocity() { if (!isPaused) { switch (getLevel()) { diff --git a/lib/lose_menu_overlay.dart b/lib/lose_menu_overlay.dart new file mode 100644 index 0000000..e8120ea --- /dev/null +++ b/lib/lose_menu_overlay.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +import 'main.dart'; + +class LoseMenuOverlay extends StatelessWidget { + const LoseMenuOverlay({ + Key? key, + required this.game, + }) : super(key: key); + + final MyGame game; + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + height: game.viewport.canvasSize.y, + width: game.viewport.canvasSize.x, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/overlay100.png'), + fit: BoxFit.fill, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Score: ' + game.gameState.getPlayerScore().toString(), + style: overlayText, + ), + const SizedBox(height: 32.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MaterialButton( + padding: const EdgeInsets.all(8.0), + textColor: Colors.white, + splashColor: Colors.greenAccent, + elevation: 8.0, + child: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/button.png'), + fit: BoxFit.fill), + ), + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Main Menu"), + ), + ), + // ), + onPressed: () { + // Go to the Main Menu + }, + ), + const SizedBox( + width: 32.0, + ), + MaterialButton( + padding: const EdgeInsets.all(8.0), + textColor: Colors.white, + splashColor: Colors.greenAccent, + elevation: 8.0, + child: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/button.png'), + fit: BoxFit.fill), + ), + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Replay"), + ), + ), + // ), + onPressed: () { + game.reset(); + }, + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 7014e2b..bd24b56 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,6 +24,8 @@ import 'package:flutter/services.dart'; import 'package:firo_runner/runner.dart'; import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:firo_runner/lose_menu_overlay.dart'; + const COLOR = Color(0xFFDDC0A3); const LEVEL2 = 10000000; @@ -33,6 +35,7 @@ const LEVEL5 = 40000000; const LEVEL6 = 50000000; const LEVEL7 = 60000000; +const OVERLAY_PRIORITY = 110; const RUNNER_PRIORITY = 100; const BUG_PRIORITY = 75; const COIN_PRIORITY = 70; @@ -43,12 +46,24 @@ const WIRE_PRIORITY = 25; const FIREWORK_PRIORITY = 15; const WINDOW_PRIORITY = 10; +const overlayText = TextStyle( + fontSize: 30, + color: Colors.white, +); + void main() async { WidgetsFlutterBinding.ensureInitialized(); await Flame.device.fullScreen(); await Flame.device.setLandscape(); final myGame = MyGame(); - runApp(GameWidget(game: myGame)); + runApp(GameWidget( + game: myGame, + overlayBuilderMap: { + 'gameOver': (_, myGame) { + return LoseMenuOverlay(game: myGame); + }, + }, + )); } int getNearestPlatform(int level) { @@ -67,6 +82,10 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { fontSize: 48.0, fontFamily: 'Codystar', color: COLOR), ); + TextPaint scoresPaint = TextPaint( + config: const TextPaintConfig(fontSize: 16.0, color: COLOR), + ); + late CircuitBackground circuitBackground; late PlatformHolder platformHolder; late CoinHolder coinHolder; @@ -83,15 +102,15 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { late double blockSize; bool loaded = false; + bool firstDeath = true; late Wire wire; + late TextComponent _distance; + late TextComponent _coins; MyGame() : super() { viewport.resize(Vector2(1920, 1080)); } - // @override - // flame.Viewport viewport = FixedResolutionViewport(Vector2(1920, 1080)); - @override Future onLoad() async { // debugMode = true; @@ -123,6 +142,14 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { playMusic(); } loaded = true; + _distance = TextComponent("Distance: 0", + position: Vector2(size.x - 100, 10), textRenderer: scoresPaint) + ..anchor = Anchor.topRight; + _distance.changePriorityWithoutResorting(OVERLAY_PRIORITY); + _coins = TextComponent("Coins: 0", + position: Vector2(size.x - 10, 10), textRenderer: scoresPaint) + ..anchor = Anchor.topRight; + _coins.changePriorityWithoutResorting(OVERLAY_PRIORITY); setUp(); } @@ -217,12 +244,19 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { bool shouldReset = false; - void reset() { + void displayLoss() { if (!(runner.sprite.animation?.done() ?? false) && - runner.sprite.animation!.loop == false) { + runner.sprite.animation!.loop == false && + firstDeath) { return; } + firstDeath = false; + overlays.add('gameOver'); + } + + void reset() { runner.sprite.animation!.reset(); + overlays.remove('gameOver'); shouldReset = false; components.clear(); setUp(); @@ -249,6 +283,8 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { gameState.setUp(this); runner.setUp(); + add(_coins); + add(_distance); fillScreen(); platformHolder.objects[2][0].sprite.current = PlatformState.left; @@ -287,8 +323,11 @@ class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents { bugHolder.update(dt); debrisHolder.update(dt); wallHolder.update(dt); - if (shouldReset) { - reset(); + + _distance.text = "Distance: ${gameState.getPlayerDistance()}"; + _coins.text = "Coins: ${gameState.numCoins}"; + if (shouldReset && !overlays.isActive('gameOver')) { + displayLoss(); } } diff --git a/lib/runner.dart b/lib/runner.dart index 8a9fd97..d07fc6b 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -164,7 +164,13 @@ class Runner extends Component with HasGameRef { return; } sprite.clearEffects(); - updateLevel(); + level = 11; + sprite.addEffect(MoveEffect( + path: [Vector2(sprite.position.x, gameRef.blockSize * 11)], + duration: 2, + curve: Curves.bounceOut, + onComplete: () {}, + )); runnerState = event; sprite.current = RunnerState.die; gameRef.die(); @@ -174,7 +180,13 @@ class Runner extends Component with HasGameRef { return; } sprite.clearEffects(); - updateLevel(); + level = 11; + sprite.addEffect(MoveEffect( + path: [Vector2(sprite.position.x, gameRef.blockSize * 11)], + duration: 1, + curve: Curves.bounceOut, + onComplete: () {}, + )); runnerState = event; sprite.current = RunnerState.electrocute; gameRef.die(); @@ -184,7 +196,13 @@ class Runner extends Component with HasGameRef { return; } sprite.clearEffects(); - updateLevel(); + level = 11; + sprite.addEffect(MoveEffect( + path: [Vector2(sprite.position.x, gameRef.blockSize * 11)], + duration: 1, + curve: Curves.bounceOut, + onComplete: () {}, + )); runnerState = event; sprite.current = RunnerState.glitch; gameRef.die();