├── ios
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ └── project.pbxproj
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
└── .gitignore
├── assets
├── bonfire.gif
└── images
│ ├── gun_blue.png
│ ├── gun_red.png
│ ├── gun_green.png
│ ├── player_red.png
│ ├── bullet_blue.png
│ ├── maps
│ ├── tileset.png
│ └── map1.tmj
│ ├── player_blue.png
│ └── player_green.png
├── imgs
├── game_screen_shot.png
├── home_screen_shot.png
└── login_screen_shot.png
├── lib
├── server_conection
│ ├── messages
│ │ ├── base
│ │ │ ├── message_code.dart
│ │ │ └── message.dart
│ │ ├── die_message.dart
│ │ ├── receive_damage_message.dart
│ │ ├── attack_message.dart
│ │ └── move_message.dart
│ ├── modules
│ │ ├── nakama_leaderboard.dart
│ │ ├── nakama_auth.dart
│ │ └── nakama_websocket.dart
│ └── nakama_service.dart
├── shared
│ ├── theme
│ │ └── game_colors.dart
│ ├── widgets
│ │ ├── change_notifier_builder.dart
│ │ ├── game_container.dart
│ │ ├── game_color_selector.dart
│ │ ├── game_textfield.dart
│ │ ├── game_button.dart
│ │ └── game_dialog.dart
│ ├── bootstrap.dart
│ └── util
│ │ └── event_queue.dart
├── game
│ ├── decorations
│ │ └── poison.dart
│ ├── game_route.dart
│ ├── util
│ │ ├── player_customization.dart
│ │ └── player_spritesheet.dart
│ ├── game_win_controller.dart
│ ├── player
│ │ ├── weapons
│ │ │ ├── bullet_capsule.dart
│ │ │ └── breaker_cannon.dart
│ │ └── breaker.dart
│ ├── remote_player
│ │ ├── remote_breaker.dart
│ │ └── remote_breaker_controlller.dart
│ ├── interface
│ │ └── bar_life.dart
│ └── game.dart
├── pages
│ ├── home
│ │ ├── home_route.dart
│ │ └── home_page.dart
│ ├── login
│ │ ├── login_route.dart
│ │ ├── bloc
│ │ │ ├── login_event.dart
│ │ │ ├── login_state.dart
│ │ │ └── login_bloc.dart
│ │ └── login_page.dart
│ └── room_match
│ │ ├── bloc
│ │ ├── room_match_event.dart
│ │ ├── room_match_state.dart
│ │ └── room_match_bloc.dart
│ │ ├── room_match_route.dart
│ │ └── room_match_page.dart
└── main.dart
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── light_shooter
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── .vscode
└── launch.json
├── README.md
├── .gitignore
├── .metadata
├── test
└── widget_test.dart
├── analysis_options.yaml
├── pubspec.yaml
└── pubspec.lock
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/assets/bonfire.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/bonfire.gif
--------------------------------------------------------------------------------
/assets/images/gun_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/gun_blue.png
--------------------------------------------------------------------------------
/assets/images/gun_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/gun_red.png
--------------------------------------------------------------------------------
/imgs/game_screen_shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/imgs/game_screen_shot.png
--------------------------------------------------------------------------------
/imgs/home_screen_shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/imgs/home_screen_shot.png
--------------------------------------------------------------------------------
/imgs/login_screen_shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/imgs/login_screen_shot.png
--------------------------------------------------------------------------------
/lib/server_conection/messages/base/message_code.dart:
--------------------------------------------------------------------------------
1 | enum MessageCodeEnum { movement, attack, die, receiveDamage }
2 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/images/gun_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/gun_green.png
--------------------------------------------------------------------------------
/assets/images/player_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/player_red.png
--------------------------------------------------------------------------------
/assets/images/bullet_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/bullet_blue.png
--------------------------------------------------------------------------------
/assets/images/maps/tileset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/maps/tileset.png
--------------------------------------------------------------------------------
/assets/images/player_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/player_blue.png
--------------------------------------------------------------------------------
/assets/images/player_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/assets/images/player_green.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelBarbosatec/light_shooter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/light_shooter/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.light_shooter
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/shared/theme/game_colors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | class GameColors {
4 | static const Color background = Color(0xFF1a1d2d);
5 | static const Color primary = Color(0xFF3b5dc9);
6 | static const Color secondary = Color(0xFF333c57);
7 | }
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/server_conection/messages/die_message.dart:
--------------------------------------------------------------------------------
1 | import 'package:light_shooter/server_conection/messages/base/message.dart';
2 | import 'package:light_shooter/server_conection/messages/base/message_code.dart';
3 |
4 | class DieMessage extends Message {
5 | DieMessage({DateTime? date})
6 | : super(
7 | op: MessageCodeEnum.die.index,
8 | data: {},
9 | date: date,
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/game/decorations/poison.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:light_shooter/game/player/breaker.dart';
3 |
4 | class Poison extends GameComponent with Sensor {
5 | static const name = 'poison';
6 | Poison(Vector2 position, Vector2 size) {
7 | this.position = position;
8 | this.size = size;
9 | }
10 |
11 | @override
12 | void onContact(GameComponent component) {
13 | if (component is Breaker) {
14 | component.removeLife(10);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/home/home_route.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/pages/home/home_page.dart';
3 |
4 | class HomeRoute {
5 | static const name = '/home';
6 |
7 | static Map get builder => {
8 | name: (context) => const HomePage(),
9 | };
10 |
11 | static Future open(BuildContext context) {
12 | return Navigator.of(context).pushNamedAndRemoveUntil(
13 | name,
14 | (route) => false,
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/login/login_route.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/pages/login/login_page.dart';
3 |
4 | class LoginRoute {
5 | static const name = '/';
6 |
7 | static Map get builder => {
8 | name: (context) => const LoginPage(),
9 | };
10 |
11 | static Future open(BuildContext context) {
12 | return Navigator.of(context).pushNamedAndRemoveUntil(
13 | name,
14 | (route) => false,
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/game/game_route.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/game/game.dart';
3 |
4 | class GameRoute {
5 | static const name = '/game';
6 |
7 | static Map get builder => {
8 | name: (context) => Game(
9 | properties:
10 | ModalRoute.of(context)?.settings.arguments as GameProperties,
11 | ),
12 | };
13 |
14 | static Future open(BuildContext context, GameProperties properties) {
15 | return Navigator.of(context).pushNamed(name, arguments: properties);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/room_match/bloc/room_match_event.dart:
--------------------------------------------------------------------------------
1 | part of 'room_match_bloc.dart';
2 |
3 | @immutable
4 | abstract class RoomMatchEvent {}
5 |
6 | class InitScreenEvent extends RoomMatchEvent {
7 | final PlayerCustomization custom;
8 |
9 | InitScreenEvent(this.custom);
10 | }
11 |
12 | class MatchedEvent extends RoomMatchEvent {
13 | final MatchmakerMatched matched;
14 | MatchedEvent(this.matched);
15 | }
16 |
17 | class CancelMatchMakerEvent extends RoomMatchEvent {
18 | final bool withPop;
19 |
20 | CancelMatchMakerEvent({this.withPop = true});
21 | }
22 |
23 | class DisposeEvent extends RoomMatchEvent {}
24 |
--------------------------------------------------------------------------------
/lib/pages/login/bloc/login_event.dart:
--------------------------------------------------------------------------------
1 | part of 'login_bloc.dart';
2 |
3 | @immutable
4 | abstract class LoginEvent {}
5 |
6 | class SignInEvent extends LoginEvent {
7 | final String email;
8 | final String password;
9 |
10 | SignInEvent(this.email, this.password);
11 | }
12 |
13 | class SignUpEvent extends LoginEvent {
14 | final String username;
15 | final String email;
16 | final String password;
17 |
18 | SignUpEvent(this.username, this.email, this.password);
19 | }
20 |
21 | class ClickSignUpEvent extends LoginEvent {
22 | final bool goSignUp;
23 |
24 | ClickSignUpEvent({this.goSignUp = true});
25 | }
26 |
--------------------------------------------------------------------------------
/lib/server_conection/modules/nakama_leaderboard.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: public_member_api_docs, sort_constructors_first
2 | import 'package:light_shooter/server_conection/modules/nakama_auth.dart';
3 | import 'package:nakama/nakama.dart';
4 |
5 | class NakamaLeaderboard {
6 | final NakamaBaseClient nakamaClient;
7 | final NakamaAuth auth;
8 | NakamaLeaderboard(this.nakamaClient, this.auth);
9 |
10 | Future addLeaderboardScore(int score) async {
11 | return nakamaClient.writeLeaderboardRecord(
12 | session: auth.getSession(),
13 | leaderboardName: 'wins',
14 | score: score,
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/shared/widgets/change_notifier_builder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ChangeNotifierBuilder extends StatelessWidget {
4 | const ChangeNotifierBuilder({
5 | Key? key,
6 | required this.value,
7 | required this.builder,
8 | }) : super(key: key);
9 |
10 | final T value;
11 | final Widget Function(BuildContext context, T value) builder;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return ListenableBuilder(
16 | listenable: value,
17 | builder: (context, child) {
18 | return builder(context, value);
19 | },
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/shared/bootstrap.dart:
--------------------------------------------------------------------------------
1 | // ignore: depend_on_referenced_packages
2 |
3 | import 'package:get_it/get_it.dart';
4 | import 'package:light_shooter/pages/login/bloc/login_bloc.dart';
5 | import 'package:light_shooter/pages/room_match/bloc/room_match_bloc.dart';
6 | import 'package:light_shooter/server_conection/nakama_service.dart';
7 |
8 | final getIt = GetIt.instance;
9 | T inject() => getIt.get();
10 |
11 | class Bootstrap {
12 | static run() {
13 | getIt.registerLazySingleton(() => NakamaService(host: '192.168.0.11'));
14 | getIt.registerFactory(() => LoginBloc(getIt.get()));
15 | getIt.registerFactory(() => RoomMatchBloc(getIt.get()));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/pages/room_match/room_match_route.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/game/util/player_customization.dart';
3 | import 'package:light_shooter/pages/room_match/room_match_page.dart';
4 |
5 | class RoomMatchRoute {
6 | static const name = '/roomMatch';
7 |
8 | static Map get builder => {
9 | name: (context) => RoomMatchPage(
10 | custom: ModalRoute.of(context)?.settings.arguments
11 | as PlayerCustomization,
12 | ),
13 | };
14 |
15 | static Future open(BuildContext context, PlayerCustomization custom) {
16 | return Navigator.of(context).pushNamed(name, arguments: custom);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/server_conection/messages/base/message.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | class Message {
4 | DateTime date;
5 | int op;
6 | Map data;
7 | Message({
8 | DateTime? date,
9 | required this.op,
10 | required this.data,
11 | }) : date = date ?? DateTime.now();
12 |
13 | Map toJson() {
14 | return {
15 | "1": op,
16 | "2": date.millisecondsSinceEpoch,
17 | "3": jsonEncode(data),
18 | };
19 | }
20 |
21 | factory Message.fromJson(Map json) {
22 | return Message(
23 | op: json['1'],
24 | date: DateTime.fromMillisecondsSinceEpoch(json['2']),
25 | data: jsonDecode(json['3']),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/lib/pages/room_match/bloc/room_match_state.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: public_member_api_docs, sort_constructors_first
2 | part of 'room_match_bloc.dart';
3 |
4 | @immutable
5 | class RoomMatchState {
6 | final GameProperties? gameProperties;
7 | final String ticket;
8 | final bool goBack;
9 |
10 | const RoomMatchState({
11 | this.gameProperties,
12 | this.ticket = '',
13 | this.goBack = false,
14 | });
15 |
16 | RoomMatchState copyWith({
17 | GameProperties? gameProperties,
18 | String? ticket,
19 | bool? goBack,
20 | }) {
21 | return RoomMatchState(
22 | gameProperties: gameProperties,
23 | ticket: ticket ?? this.ticket,
24 | goBack: goBack ?? false,
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/shared/widgets/game_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/shared/theme/game_colors.dart';
3 |
4 | class GameContainer extends StatelessWidget {
5 | final Widget child;
6 | final BoxConstraints? constraints;
7 | final EdgeInsetsGeometry? margin;
8 | const GameContainer(
9 | {super.key, required this.child, this.constraints, this.margin});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | margin: margin,
15 | constraints: constraints,
16 | padding: const EdgeInsets.all(16),
17 | decoration: BoxDecoration(
18 | color: GameColors.secondary,
19 | borderRadius: BorderRadius.circular(24),
20 | ),
21 | child: child,
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/pages/login/bloc/login_state.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: public_member_api_docs, sort_constructors_first
2 | part of 'login_bloc.dart';
3 |
4 | @immutable
5 | class LoginState {
6 | final bool loading;
7 | final bool authorized;
8 | final String error;
9 | final bool signUpMode;
10 |
11 | const LoginState({
12 | this.loading = false,
13 | this.authorized = false,
14 | this.error = '',
15 | this.signUpMode = false,
16 | });
17 |
18 | LoginState copyWith({
19 | bool? loading,
20 | bool? authorized,
21 | String? error,
22 | bool? signUpMode,
23 | }) {
24 | return LoginState(
25 | loading: loading ?? this.loading,
26 | signUpMode: signUpMode ?? this.signUpMode,
27 | authorized: authorized ?? false,
28 | error: error ?? '',
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "light_shooter",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "light_shooter (profile mode)",
14 | "request": "launch",
15 | "type": "dart",
16 | "flutterMode": "profile"
17 | },
18 | {
19 | "name": "light_shooter (release mode)",
20 | "request": "launch",
21 | "type": "dart",
22 | "flutterMode": "release"
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/lib/server_conection/messages/receive_damage_message.dart:
--------------------------------------------------------------------------------
1 | import 'package:light_shooter/server_conection/messages/base/message.dart';
2 | import 'package:light_shooter/server_conection/messages/base/message_code.dart';
3 |
4 | class ReceiveDamageMessage extends Message {
5 | final double damage;
6 | ReceiveDamageMessage(this.damage, {DateTime? date})
7 | : super(
8 | op: MessageCodeEnum.receiveDamage.index,
9 | data: {'1': damage},
10 | date: date,
11 | );
12 |
13 | factory ReceiveDamageMessage.fromJson(Map json) {
14 | return ReceiveDamageMessage.fromMessage(Message.fromJson(json));
15 | }
16 |
17 | factory ReceiveDamageMessage.fromMessage(Message msg) {
18 | return ReceiveDamageMessage(
19 | double.tryParse(msg.data['1'].toString()) ?? 0.0,
20 | date: msg.date,
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/game/util/player_customization.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: public_member_api_docs, sort_constructors_first
2 | import 'package:light_shooter/game/util/player_spritesheet.dart';
3 |
4 | class PlayerCustomization {
5 | final PlayerColor color;
6 | final String skin;
7 |
8 | const PlayerCustomization({this.color = PlayerColor.blue, this.skin = ''});
9 |
10 | factory PlayerCustomization.fromMap(Map map) {
11 | return PlayerCustomization(
12 | skin: map['skin'] ?? '',
13 | color: PlayerColor.fromName(map['color']),
14 | );
15 | }
16 | Map toMap() {
17 | return {
18 | 'color': color.name,
19 | 'skin': skin,
20 | };
21 | }
22 |
23 | PlayerCustomization copyWith({
24 | PlayerColor? color,
25 | String? skin,
26 | }) {
27 | return PlayerCustomization(
28 | color: color ?? this.color,
29 | skin: skin ?? this.skin,
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/game/game_route.dart';
3 | import 'package:light_shooter/pages/login/login_route.dart';
4 | import 'package:light_shooter/pages/room_match/room_match_route.dart';
5 | import 'package:light_shooter/shared/bootstrap.dart';
6 |
7 | import 'pages/home/home_route.dart';
8 |
9 | void main() {
10 | Bootstrap.run();
11 | runApp(const MyApp());
12 | }
13 |
14 | class MyApp extends StatelessWidget {
15 | const MyApp({Key? key}) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return MaterialApp(
20 | title: 'Light Shooter',
21 | theme: ThemeData(
22 | primarySwatch: Colors.blue,
23 | ),
24 | routes: {
25 | ...LoginRoute.builder,
26 | ...HomeRoute.builder,
27 | ...RoomMatchRoute.builder,
28 | ...GameRoute.builder,
29 | },
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Light Shooter
2 |
3 |
4 |
5 |
6 | | Login | Home | Game |
7 | | ------------- | ------------- | ------------- |
8 | |  |  |  |
9 |
10 | Game online built with [Bonfire](https://bonfire-engine.github.io/) integrated with [Nakama Game Server](https://flutter-nakama.gitbook.io/)
11 |
12 | ## Getting Started
13 |
14 | ### Flutter
15 |
16 | For help getting started with Flutter development, view the
17 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
18 | samples, guidance on mobile development, and a full API reference.
19 |
20 | ### Nakama server
21 |
22 | [Install Nakama with Docker Compose](https://heroiclabs.com/docs/nakama/getting-started/install/docker/)
23 |
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Symbolication related
39 | app.*.symbols
40 |
41 | # Obfuscation related
42 | app.*.map.json
43 |
44 | # Android Studio will place build artifacts here
45 | /android/app/debug
46 | /android/app/profile
47 | /android/app/release
48 |
49 | web
50 | linux
51 | windows
52 | macos
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: f72efea43c3013323d1b95cff571f3c1caa37583
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
17 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
18 | - platform: web
19 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
20 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/lib/server_conection/messages/attack_message.dart:
--------------------------------------------------------------------------------
1 | import 'package:light_shooter/server_conection/messages/base/message.dart';
2 | import 'package:light_shooter/server_conection/messages/base/message_code.dart';
3 |
4 | class AttackMessage extends Message {
5 | // ignore: non_constant_identifier_names
6 | static final CODE = MessageCodeEnum.attack.index;
7 | final double damage;
8 | final String type;
9 | final double angle;
10 |
11 | AttackMessage(this.damage, this.type, this.angle, {DateTime? date})
12 | : super(
13 | op: CODE,
14 | date: date,
15 | data: {'1': damage, '2': type, '3': angle},
16 | );
17 |
18 | factory AttackMessage.fromJson(Map json) {
19 | return AttackMessage.fromMessage(Message.fromJson(json));
20 | }
21 |
22 | factory AttackMessage.fromMessage(Message msg) {
23 | return AttackMessage(
24 | double.tryParse(msg.data['1'].toString()) ?? 0.0,
25 | msg.data['2'],
26 | double.tryParse(msg.data['3'].toString()) ?? 0.0,
27 | date: msg.date,
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/server_conection/modules/nakama_auth.dart:
--------------------------------------------------------------------------------
1 | import 'package:nakama/nakama.dart';
2 |
3 | class NakamaAuth {
4 | final NakamaBaseClient _nakamaClient;
5 | Session? _session;
6 |
7 | NakamaAuth(this._nakamaClient);
8 |
9 | Future authenticateEmail({
10 | String? username,
11 | required String email,
12 | required String password,
13 | bool create = false,
14 | }) {
15 | return _nakamaClient
16 | .authenticateEmail(
17 | email: email,
18 | password: password,
19 | create: create,
20 | username: username,
21 | )
22 | .then((value) {
23 | _session = value;
24 | return value;
25 | });
26 | }
27 |
28 | Future getAccount() {
29 | return _nakamaClient.getAccount(getSession());
30 | }
31 |
32 | Session getSession() {
33 | if (_session == null) {
34 | throw Exception('Session not found. Please authenticate!');
35 | }
36 | return _session!;
37 | }
38 |
39 | Future logout() {
40 | return _nakamaClient.sessionLogout(session: getSession()).then((value) {
41 | _session = null;
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:light_shooter/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/lib/shared/widgets/game_color_selector.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/game/util/player_spritesheet.dart';
3 | import 'package:light_shooter/shared/theme/game_colors.dart';
4 |
5 | class GameColorSelector extends StatelessWidget {
6 | final PlayerColor? colorSelected;
7 | final ValueChanged? onChanged;
8 | const GameColorSelector({super.key, this.colorSelected, this.onChanged});
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Row(
13 | mainAxisSize: MainAxisSize.min,
14 | children: PlayerColor.values.map(_buildItem).toList(),
15 | );
16 | }
17 |
18 | Widget _buildItem(PlayerColor e) {
19 | return InkWell(
20 | onTap: () => onChanged?.call(e),
21 | child: Container(
22 | width: 40,
23 | height: 40,
24 | margin: const EdgeInsets.all(5),
25 | decoration: BoxDecoration(
26 | color: e.getColor(),
27 | borderRadius: BorderRadius.circular(10),
28 | border: Border.all(
29 | color: e == colorSelected ? Colors.white : GameColors.background,
30 | width: 4,
31 | ),
32 | ),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/server_conection/nakama_service.dart:
--------------------------------------------------------------------------------
1 | // ignore: depend_on_referenced_packages
2 | import 'package:light_shooter/server_conection/modules/nakama_auth.dart';
3 | import 'package:light_shooter/server_conection/modules/nakama_leaderboard.dart';
4 | import 'package:light_shooter/server_conection/modules/nakama_websocket.dart';
5 | import 'package:nakama/nakama.dart';
6 |
7 | class NakamaService {
8 | final String host;
9 | late final NakamaBaseClient _nakamaClient;
10 | NakamaAuth? _auth;
11 | NakamaWebsocket? _websocket;
12 | NakamaLeaderboard? _leaderboard;
13 |
14 | NakamaService({
15 | String? host,
16 | bool ssl = false,
17 | }) : host = host ?? '127.0.0.1' {
18 | _nakamaClient = getNakamaClient(
19 | host: host,
20 | ssl: ssl,
21 | serverKey: 'defaultkey',
22 | );
23 | }
24 |
25 | NakamaAuth auth() {
26 | _auth ??= NakamaAuth(_nakamaClient);
27 | return _auth!;
28 | }
29 |
30 | NakamaWebsocket websocket() {
31 | _websocket ??= NakamaWebsocket(host: host)..init(auth().getSession());
32 | return _websocket!;
33 | }
34 |
35 | NakamaLeaderboard leaderboard() {
36 | _leaderboard ??= NakamaLeaderboard(_nakamaClient, auth());
37 | return _leaderboard!;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/server_conection/messages/move_message.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:light_shooter/server_conection/messages/base/message.dart';
3 | import 'package:light_shooter/server_conection/messages/base/message_code.dart';
4 |
5 | class MoveMessage extends Message {
6 | // ignore: non_constant_identifier_names
7 | static final CODE = MessageCodeEnum.movement.index;
8 | final String direction;
9 | final double speed;
10 | final Vector2 position;
11 |
12 | MoveMessage(this.direction, this.position, this.speed, {DateTime? date})
13 | : super(
14 | op: CODE,
15 | date: date,
16 | data: {
17 | '1': direction,
18 | '2': {'x': position.x, 'y': position.y},
19 | '3': speed,
20 | },
21 | );
22 |
23 | factory MoveMessage.fromJson(Map json) {
24 | return MoveMessage.fromMessage(Message.fromJson(json));
25 | }
26 |
27 | factory MoveMessage.fromMessage(Message msg) {
28 | var pos = msg.data['2'] as Map;
29 | return MoveMessage(
30 | msg.data['1'],
31 | Vector2(
32 | double.tryParse(pos['x'].toString()) ?? 0.0,
33 | double.tryParse(pos['y'].toString()) ?? 0.0,
34 | ),
35 | double.tryParse(msg.data['3'].toString()) ?? 0.0,
36 | date: msg.date,
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/shared/widgets/game_textfield.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/shared/theme/game_colors.dart';
3 |
4 | class GameTextField extends StatelessWidget {
5 | final String? hint;
6 | final bool enabled;
7 | final FormFieldValidator? validator;
8 | final TextEditingController? controller;
9 | final ValueChanged? onFieldSubmitted;
10 | const GameTextField({
11 | super.key,
12 | this.controller,
13 | this.hint,
14 | this.enabled = true,
15 | this.validator,
16 | this.onFieldSubmitted,
17 | });
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return TextFormField(
22 | controller: controller,
23 | enabled: enabled,
24 | validator: validator,
25 | onFieldSubmitted: onFieldSubmitted,
26 | decoration: InputDecoration(
27 | filled: true,
28 | fillColor: Colors.white,
29 | hintText: hint,
30 | contentPadding: const EdgeInsets.only(left: 16),
31 | border: OutlineInputBorder(
32 | borderSide: const BorderSide(color: Colors.white),
33 | borderRadius: BorderRadius.circular(20),
34 | ),
35 | focusedBorder: OutlineInputBorder(
36 | borderSide: const BorderSide(color: GameColors.primary, width: 2),
37 | borderRadius: BorderRadius.circular(20),
38 | ),
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/game/game_win_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:light_shooter/pages/home/home_route.dart';
4 | import 'package:light_shooter/shared/widgets/game_dialog.dart';
5 |
6 | class GameWinController extends GameComponent {
7 | bool finishedGame = false;
8 | bool checking = false;
9 |
10 | @override
11 | void onMount() {
12 | Future.delayed(const Duration(seconds: 2), () {
13 | checking = true;
14 | });
15 | super.onMount();
16 | }
17 |
18 | @override
19 | void update(double dt) {
20 | if (checkInterval('CHECK_WIN', 250, dt) && !finishedGame && checking) {
21 | _verifyWinOrLose();
22 | }
23 | super.update(dt);
24 | }
25 |
26 | void _verifyWinOrLose() {
27 | if (gameRef.player?.isDead == true) {
28 | _showDialog(gameRef.context, GameDialogTypeEnum.gameOver);
29 | finishedGame = true;
30 | }
31 | if (gameRef.livingEnemies().isEmpty) {
32 | _showDialog(gameRef.context, GameDialogTypeEnum.win);
33 | finishedGame = true;
34 | }
35 | }
36 |
37 | void _showDialog(BuildContext context, GameDialogTypeEnum type) {
38 | gameRef.pauseEngine();
39 | GameDialog.show(
40 | context,
41 | type,
42 | onTapOk: () {
43 | Navigator.popUntil(
44 | context,
45 | (route) => route.settings.name == HomeRoute.name,
46 | );
47 | },
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/shared/widgets/game_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/shared/theme/game_colors.dart';
3 |
4 | class GameButton extends StatelessWidget {
5 | final VoidCallback? onPressed;
6 | final String text;
7 | final bool expanded;
8 | final bool loading;
9 | const GameButton({
10 | super.key,
11 | this.onPressed,
12 | required this.text,
13 | this.expanded = false,
14 | this.loading = false,
15 | });
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return SizedBox(
20 | width: (expanded ? double.maxFinite : null),
21 | height: 40,
22 | child: ElevatedButton(
23 | onPressed: loading ? null : onPressed,
24 | style: const ButtonStyle(
25 | backgroundColor: MaterialStatePropertyAll(GameColors.primary),
26 | shape: MaterialStatePropertyAll(StadiumBorder()),
27 | ),
28 | child: AnimatedSwitcher(
29 | duration: const Duration(milliseconds: 300),
30 | child: loading
31 | ? const SizedBox(
32 | width: 20,
33 | height: 20,
34 | child: CircularProgressIndicator(
35 | valueColor: AlwaysStoppedAnimation(Colors.white),
36 | ),
37 | )
38 | : Text(
39 | text,
40 | style: const TextStyle(color: Colors.white),
41 | ),
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Light Shooter
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | light_shooter
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/lib/pages/login/bloc/login_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter_bloc/flutter_bloc.dart';
5 | import 'package:light_shooter/server_conection/nakama_service.dart';
6 |
7 | part 'login_event.dart';
8 | part 'login_state.dart';
9 |
10 | class LoginBloc extends Bloc {
11 | final NakamaService _client;
12 | LoginBloc(this._client) : super(const LoginState()) {
13 | on(_onSignInEvent);
14 | on(_onSignUpEvent);
15 | on(_onClickSignUpEvent);
16 | }
17 |
18 | FutureOr _onSignInEvent(
19 | SignInEvent event,
20 | Emitter emit,
21 | ) async {
22 | emit(state.copyWith(loading: true));
23 | await _client
24 | .auth()
25 | .authenticateEmail(email: event.email, password: event.password)
26 | .then((value) async {
27 | emit(state.copyWith(authorized: true, loading: false));
28 | }).catchError((e) {
29 | emit(state.copyWith(error: e.message, loading: false));
30 | });
31 | }
32 |
33 | FutureOr _onClickSignUpEvent(
34 | ClickSignUpEvent event,
35 | Emitter emit,
36 | ) {
37 | emit(state.copyWith(signUpMode: event.goSignUp));
38 | }
39 |
40 | FutureOr _onSignUpEvent(
41 | SignUpEvent event,
42 | Emitter emit,
43 | ) async {
44 | emit(state.copyWith(loading: true));
45 | await _client
46 | .auth()
47 | .authenticateEmail(
48 | username: event.username,
49 | email: event.email,
50 | password: event.password,
51 | create: true,
52 | )
53 | .then((value) async {
54 | emit(state.copyWith(authorized: true, loading: false));
55 | }).catchError((e) {
56 | emit(state.copyWith(error: e.message, loading: false));
57 | });
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
10 |
18 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/game/player/weapons/bullet_capsule.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:light_shooter/game/player/breaker.dart';
5 | import 'package:light_shooter/game/remote_player/remote_breaker.dart';
6 | import 'package:light_shooter/game/util/player_spritesheet.dart';
7 |
8 | class BulletCapsule extends GameDecoration
9 | with Movement, BlockMovementCollision, HandleForces {
10 | bool removing = false;
11 |
12 | BulletCapsule(Vector2 p, double angle)
13 | : super.withSprite(
14 | sprite: PlayerSpriteSheet.bulletCapsule,
15 | position: p,
16 | size: Vector2.all(16),
17 | ) {
18 | this.angle = angle;
19 | speed = 100;
20 | movementOnlyVisible = false;
21 | addForce(
22 | ResistanceForce2D(
23 | id: 'id',
24 | value: Vector2.all(Random().nextDouble() * 3 + 2),
25 | ),
26 | );
27 | }
28 |
29 | @override
30 | Future onLoad() async {
31 | await add(
32 | RectangleHitbox(
33 | size: Vector2(5, 6),
34 | position: Vector2.all(6),
35 | ),
36 | );
37 | moveFromAngle(angle);
38 | return super.onLoad();
39 | }
40 |
41 | @override
42 | void update(double dt) {
43 | if (!removing && isStopped()) {
44 | removing = true;
45 | _removeCapsule();
46 | }
47 | super.update(dt);
48 | }
49 |
50 | @override
51 | bool onBlockMovement(Set intersectionPoints, GameComponent other) {
52 | if (other is Breaker || other is RemoteBreaker || other is BulletCapsule) {
53 | return false;
54 | }
55 | return super.onBlockMovement(intersectionPoints, other);
56 | }
57 |
58 | @override
59 | int get priority => LayerPriority.MAP + 1;
60 |
61 | void _removeCapsule() {
62 | removing = true;
63 | add(
64 | OpacityEffect.fadeOut(
65 | EffectController(
66 | duration: 0.1,
67 | alternate: true,
68 | repeatCount: 3,
69 | startDelay: 1,
70 | ),
71 | onComplete: removeFromParent,
72 | ),
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/game/remote_player/remote_breaker.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:light_shooter/game/player/weapons/breaker_cannon.dart';
4 | import 'package:light_shooter/game/remote_player/remote_breaker_controlller.dart';
5 | import 'package:light_shooter/game/util/player_spritesheet.dart';
6 | import 'package:light_shooter/server_conection/modules/nakama_websocket.dart';
7 |
8 | class RemoteBreaker extends SimpleEnemy
9 | with BlockMovementCollision, UseLifeBar, RemoteBreakerControlller {
10 | BreakerCannon? gun;
11 | final String id;
12 | final PlayerColor color;
13 | final String name;
14 | final NakamaWebsocket websocket;
15 |
16 | RemoteBreaker({
17 | required this.id,
18 | required super.position,
19 | required this.color,
20 | required this.websocket,
21 | this.name = '',
22 | }) : super(
23 | size: Vector2.all(64),
24 | animation: PlayerSpriteSheet.animation(color),
25 | speed: 60,
26 | ) {
27 | setupLifeBar(
28 | size: Vector2(width / 2, 6),
29 | borderRadius: BorderRadius.circular(10),
30 | barLifeDrawPosition: BarLifeDrawPorition.bottom,
31 | position: Vector2(width / 8, 0),
32 | );
33 | movementOnlyVisible = false;
34 | }
35 |
36 | @override
37 | Future onLoad() async {
38 | await add(
39 | RectangleHitbox(
40 | size: size / 4,
41 | position: Vector2(size.y * 0.35, size.x * 0.70),
42 | ),
43 | );
44 | return super.onLoad();
45 | }
46 |
47 | @override
48 | void onMount() {
49 | add(
50 | gun = BreakerCannon(
51 | Vector2(32, 44),
52 | color,
53 | withScreenEffect: false,
54 | blockShootWithoutBullet: false,
55 | attackFrom: AttackFromEnum.ENEMY,
56 | ),
57 | );
58 | super.onMount();
59 | }
60 |
61 | @override
62 | void receiveDamage(AttackFromEnum attacker, double damage, identify) {}
63 |
64 | @override
65 | void die() {
66 | animation?.playOnce(
67 | PlayerSpriteSheet.die(color),
68 | onFinish: removeFromParent,
69 | );
70 | super.die();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.example.light_shooter"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/shared/widgets/game_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:light_shooter/shared/widgets/game_button.dart';
3 | import 'package:light_shooter/shared/widgets/game_container.dart';
4 |
5 | enum GameDialogTypeEnum { win, gameOver }
6 |
7 | class GameDialog extends StatelessWidget {
8 | final GameDialogTypeEnum type;
9 | final VoidCallback? onTapOk;
10 | const GameDialog({super.key, required this.type, this.onTapOk});
11 |
12 | static Future show(BuildContext context, GameDialogTypeEnum type,
13 | {VoidCallback? onTapOk}) {
14 | return showDialog(
15 | context: context,
16 | barrierDismissible: false,
17 | builder: (context) {
18 | return GameDialog(
19 | type: type,
20 | onTapOk: onTapOk,
21 | );
22 | },
23 | );
24 | }
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | return Material(
29 | type: MaterialType.transparency,
30 | child: Center(
31 | child: GameContainer(
32 | margin: const EdgeInsets.all(24),
33 | constraints: const BoxConstraints(
34 | maxWidth: 400,
35 | ),
36 | child: Column(
37 | mainAxisSize: MainAxisSize.min,
38 | children: [
39 | const SizedBox(height: 16),
40 | Icon(
41 | _getIcon(type),
42 | size: 100,
43 | color: _getIconColor(type),
44 | ),
45 | const SizedBox(height: 8),
46 | Text(
47 | _getTile(type),
48 | style: const TextStyle(
49 | color: Colors.white,
50 | fontSize: 30,
51 | fontWeight: FontWeight.bold,
52 | ),
53 | ),
54 | const SizedBox(
55 | height: 32,
56 | ),
57 | GameButton(
58 | expanded: true,
59 | text: 'OK',
60 | onPressed: () {
61 | Navigator.pop(context);
62 | onTapOk?.call();
63 | },
64 | ),
65 | ],
66 | ),
67 | ),
68 | ),
69 | );
70 | }
71 |
72 | String _getTile(GameDialogTypeEnum type) {
73 | switch (type) {
74 | case GameDialogTypeEnum.win:
75 | return 'Congratulations';
76 | case GameDialogTypeEnum.gameOver:
77 | return 'Game Over';
78 | }
79 | }
80 |
81 | IconData? _getIcon(GameDialogTypeEnum type) {
82 | switch (type) {
83 | case GameDialogTypeEnum.win:
84 | return Icons.emoji_events_sharp;
85 | case GameDialogTypeEnum.gameOver:
86 | return Icons.sentiment_dissatisfied_rounded;
87 | }
88 | }
89 |
90 | Color? _getIconColor(GameDialogTypeEnum type) {
91 | switch (type) {
92 | case GameDialogTypeEnum.win:
93 | return Colors.yellow[700];
94 | case GameDialogTypeEnum.gameOver:
95 | return Colors.red;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/shared/util/event_queue.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | abstract class Timeline {}
5 |
6 | class Empty extends Timeline {}
7 |
8 | class Delay extends Timeline {
9 | final int time;
10 | late Timer _timer;
11 |
12 | Delay(this.time) {
13 | _timer = Timer(time / 1000);
14 | }
15 | bool update(double dt) {
16 | _timer.update(dt);
17 | return _timer.finished;
18 | }
19 | }
20 |
21 | class Frame extends Timeline {
22 | final T value;
23 | final DateTime time;
24 | DateTime? timeRun;
25 |
26 | Frame(this.value, this.time);
27 |
28 | int get differenceTimeRun {
29 | if (timeRun != null) {
30 | return DateTime.now().difference(timeRun!).inMilliseconds;
31 | } else {
32 | return 0;
33 | }
34 | }
35 | }
36 |
37 | class EventQueue {
38 | final int bufferSize;
39 | final int delay;
40 | late List> _timeLine;
41 |
42 | ValueChanged? listen;
43 |
44 | int _currentIndex = 0;
45 | int _headIndex = 0;
46 |
47 | Timeline? _current;
48 |
49 | EventQueue(this.delay, {this.bufferSize = 40}) {
50 | _timeLine = List.filled(bufferSize, Empty(), growable: false);
51 | }
52 |
53 | void add(T value, DateTime time) {
54 | if (isEmpty()) {
55 | _add(Delay(delay));
56 | _add(Frame(value, time));
57 | _current = _timeLine.first;
58 | } else {
59 | Frame lastFrame = _timeLine[_getLastHeadIndex()] as Frame;
60 | int delayLastFrame = time.difference(lastFrame.time).inMilliseconds;
61 | if (lastFrame.timeRun == null) {
62 | _add(Delay(delayLastFrame));
63 | } else {
64 | int timeExecuted = lastFrame.differenceTimeRun;
65 | int delay = delayLastFrame - timeExecuted;
66 | if (delay > 0) {
67 | _add(Delay(delay <= this.delay ? delay : 0));
68 | }
69 | }
70 | _add(Frame(value, time));
71 | }
72 | }
73 |
74 | void run(double dt) async {
75 | if (_current == null) return;
76 | if (_current is Delay) {
77 | if ((_current as Delay).update(dt)) {
78 | _next();
79 | }
80 | } else if (_current is Frame) {
81 | if ((_current as Frame).timeRun == null) {
82 | (_current as Frame).timeRun = DateTime.now();
83 | listen?.call((_current as Frame).value);
84 | }
85 | _next();
86 | }
87 | }
88 |
89 | void _next() {
90 | if (haveNext()) {
91 | _currentIndex++;
92 | _current = _timeLine[_getIndex()];
93 | }
94 | }
95 |
96 | int _getIndex() {
97 | return _currentIndex % bufferSize;
98 | }
99 |
100 | int _getHeadIndex() {
101 | return _headIndex % bufferSize;
102 | }
103 |
104 | int _getLastHeadIndex() {
105 | return (_headIndex - 1) % bufferSize;
106 | }
107 |
108 | bool haveNext() {
109 | int nextIndex = (_currentIndex + 1) % bufferSize;
110 | return _timeLine[nextIndex] is! Empty;
111 | }
112 |
113 | bool isEmpty() {
114 | return _timeLine.where((element) => element is! Empty).isEmpty;
115 | }
116 |
117 | void _add(Timeline timeline) {
118 | _timeLine[_getHeadIndex()] = timeline;
119 | _headIndex++;
120 | _timeLine[_getHeadIndex()] = Empty();
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/lib/game/remote_player/remote_breaker_controlller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:light_shooter/game/remote_player/remote_breaker.dart';
6 | import 'package:light_shooter/server_conection/messages/attack_message.dart';
7 | import 'package:light_shooter/server_conection/messages/base/message.dart';
8 | import 'package:light_shooter/server_conection/messages/base/message_code.dart';
9 | import 'package:light_shooter/server_conection/messages/move_message.dart';
10 | import 'package:light_shooter/server_conection/messages/receive_damage_message.dart';
11 | import 'package:light_shooter/shared/util/event_queue.dart';
12 | import 'package:nakama/nakama.dart';
13 |
14 | mixin RemoteBreakerControlller on SimpleEnemy {
15 | EventQueue buffer = EventQueue(80);
16 |
17 | RemoteBreaker get remote => this as RemoteBreaker;
18 |
19 | @override
20 | void onMount() {
21 | buffer.listen = _listenEventBuffer;
22 | remote.websocket.addOnMatchDataObserser(_onDataObserver);
23 | super.onMount();
24 | }
25 |
26 | @override
27 | void onRemove() {
28 | buffer.listen = null;
29 | remote.websocket.removeOnMatchDataObserser(_onDataObserver);
30 | super.onRemove();
31 | }
32 |
33 | void _onDataObserver(MatchData data) {
34 | if (data.presence.userId == remote.id) {
35 | String dataString = String.fromCharCodes(data.data);
36 | final json = jsonDecode(dataString);
37 | Message m = Message.fromJson(json);
38 | buffer.add(m, m.date);
39 | }
40 | }
41 |
42 | @override
43 | void update(double dt) {
44 | buffer.run(dt);
45 | super.update(dt);
46 | }
47 |
48 | void _listenEventBuffer(Message value) {
49 | switch (MessageCodeEnum.values[value.op]) {
50 | case MessageCodeEnum.movement:
51 | _doMove(value);
52 | break;
53 | case MessageCodeEnum.attack:
54 | _doAttack(value);
55 | break;
56 | case MessageCodeEnum.die:
57 | if (!(isDead == true)) {
58 | die();
59 | }
60 | break;
61 | case MessageCodeEnum.receiveDamage:
62 | _doReceiveDamage(value);
63 | break;
64 | }
65 | }
66 |
67 | void _doMove(Message value) {
68 | final move = MoveMessage.fromMessage(value);
69 | Direction? remoteDirection;
70 | try {
71 | remoteDirection = Direction.values.firstWhere(
72 | (element) => element.name == move.direction,
73 | );
74 | speed = move.speed;
75 | } catch (e) {
76 | //idle
77 | stopMove(forceIdle: true);
78 | }
79 | _execDirection(remoteDirection);
80 |
81 | if (position.distanceTo(move.position) > width / 6) {
82 | add(MoveEffect.to(move.position, EffectController(duration: 0.2)));
83 | }
84 | }
85 |
86 | void _doAttack(Message value) {
87 | final attack = AttackMessage.fromMessage(value);
88 | remote.gun?.changeAngle(attack.angle);
89 | if (attack.damage > 0) {
90 | remote.gun?.changeAngle(attack.angle);
91 | remote.gun?.execShoot(attack.damage);
92 | }
93 | remote.gun?.changeAngle(0);
94 | }
95 |
96 | void _doReceiveDamage(Message value) {
97 | final msg = ReceiveDamageMessage.fromMessage(value);
98 | showDamage(
99 | msg.damage,
100 | config: const TextStyle(fontSize: 14, color: Colors.red),
101 | );
102 | removeLife(msg.damage);
103 | }
104 |
105 | void _execDirection(Direction? remoteDirection) {
106 | if (remoteDirection != null) {
107 | moveFromDirection(remoteDirection);
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/lib/game/util/player_spritesheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 |
3 | enum PlayerColor {
4 | blue,
5 | green,
6 | red;
7 |
8 | factory PlayerColor.fromName(String? name) {
9 | if (name == null) {
10 | return PlayerColor.blue;
11 | }
12 | return PlayerColor.values.firstWhere((element) => element.name == name);
13 | }
14 |
15 | Color getColor() {
16 | switch (this) {
17 | case PlayerColor.blue:
18 | return const Color(0xFF3b5dc9);
19 | case PlayerColor.green:
20 | return const Color(0xFF257179);
21 | case PlayerColor.red:
22 | return const Color(0xFFb13e53);
23 | }
24 | }
25 | }
26 |
27 | class PlayerSpriteSheet {
28 | static Future idle(PlayerColor color) =>
29 | SpriteAnimation.load(
30 | 'player_${color.name}.png',
31 | SpriteAnimationData.sequenced(
32 | amount: 1,
33 | stepTime: 0.1,
34 | textureSize: Vector2.all(32),
35 | ),
36 | );
37 |
38 | static Future run(PlayerColor color) => SpriteAnimation.load(
39 | 'player_${color.name}.png',
40 | SpriteAnimationData.sequenced(
41 | amount: 4,
42 | stepTime: 0.2,
43 | textureSize: Vector2.all(32),
44 | texturePosition: Vector2(0, 64),
45 | ),
46 | );
47 |
48 | static Future die(PlayerColor color) => SpriteAnimation.load(
49 | 'player_${color.name}.png',
50 | SpriteAnimationData.sequenced(
51 | amount: 6,
52 | stepTime: 0.1,
53 | textureSize: Vector2.all(32),
54 | texturePosition: Vector2(0, 96),
55 | ),
56 | );
57 |
58 | static Future talk(PlayerColor color) =>
59 | SpriteAnimation.load(
60 | 'player_${color.name}.png',
61 | SpriteAnimationData.sequenced(
62 | amount: 2,
63 | stepTime: 0.1,
64 | textureSize: Vector2.all(32),
65 | texturePosition: Vector2(0, 32),
66 | ),
67 | );
68 |
69 | static Future gun(PlayerColor color) => SpriteAnimation.load(
70 | 'gun_${color.name}.png',
71 | SpriteAnimationData.sequenced(
72 | amount: 1,
73 | stepTime: 0.1,
74 | textureSize: Vector2.all(32),
75 | ),
76 | );
77 |
78 | static Future gunShot(PlayerColor color) =>
79 | SpriteAnimation.load(
80 | 'gun_${color.name}.png',
81 | SpriteAnimationData.sequenced(
82 | amount: 4,
83 | stepTime: 0.1,
84 | textureSize: Vector2.all(32),
85 | texturePosition: Vector2(0, 64),
86 | ),
87 | );
88 |
89 | static Future gunReload(PlayerColor color) =>
90 | SpriteAnimation.load(
91 | 'gun_${color.name}.png',
92 | SpriteAnimationData.sequenced(
93 | amount: 5,
94 | stepTime: 0.1,
95 | textureSize: Vector2.all(32),
96 | texturePosition: Vector2(0, 32),
97 | ),
98 | );
99 |
100 | static Future get bullet => SpriteAnimation.load(
101 | 'bullet_blue.png',
102 | SpriteAnimationData.sequenced(
103 | amount: 4,
104 | stepTime: 0.1,
105 | textureSize: Vector2.all(16),
106 | ),
107 | );
108 |
109 |
110 |
111 | static Future get bulletCapsule => Sprite.load(
112 | 'bullet_blue.png',
113 | srcSize: Vector2.all(16),
114 | srcPosition: Vector2(0, 16),
115 | );
116 |
117 | static SimpleDirectionAnimation animation(PlayerColor color) =>
118 | SimpleDirectionAnimation(
119 | idleRight: idle(color),
120 | runRight: run(color),
121 | others: {
122 | 'talk': talk(color),
123 | },
124 | );
125 | }
126 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: light_shooter
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.17.6 <3.0.0"
22 |
23 | # Dependencies specify other packages that your package needs in order to work.
24 | # To automatically upgrade your package dependencies to the latest versions
25 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
26 | # dependencies can be manually updated by changing the version numbers below to
27 | # the latest version available on pub.dev. To see which dependencies have newer
28 | # versions available, run `flutter pub outdated`.
29 | dependencies:
30 | flutter:
31 | sdk: flutter
32 |
33 | bonfire: ^3.0.5
34 | flutter_bloc: ^8.1.2
35 | gif_view: ^0.4.0
36 | nakama:
37 | git:
38 | url: https://github.com/RafaelBarbosatec/flutter_nakama.git
39 | ref: cc832b06427f9897ce6fea254248426ee9b53790
40 | get_it: ^7.6.4
41 |
42 | dev_dependencies:
43 | flutter_test:
44 | sdk: flutter
45 |
46 | # The "flutter_lints" package below contains a set of recommended lints to
47 | # encourage good coding practices. The lint set provided by the package is
48 | # activated in the `analysis_options.yaml` file located at the root of your
49 | # package. See that file for information about deactivating specific lint
50 | # rules and activating additional ones.
51 | flutter_lints: ^2.0.0
52 |
53 | # For information on the generic Dart part of this file, see the
54 | # following page: https://dart.dev/tools/pub/pubspec
55 |
56 | # The following section is specific to Flutter packages.
57 | flutter:
58 |
59 | # The following line ensures that the Material Icons font is
60 | # included with your application, so that you can use the icons in
61 | # the material Icons class.
62 | uses-material-design: true
63 |
64 | # To add assets to your application, add an assets section, like this:
65 | assets:
66 | - assets/
67 | - assets/images/
68 | - assets/images/maps/
69 |
70 | # An image asset can refer to one or more resolution-specific "variants", see
71 | # https://flutter.dev/assets-and-images/#resolution-aware
72 |
73 | # For details regarding adding assets from package dependencies, see
74 | # https://flutter.dev/assets-and-images/#from-packages
75 |
76 | # To add custom fonts to your application, add a fonts section here,
77 | # in this "flutter" section. Each entry in this list should have a
78 | # "family" key with the font family name, and a "fonts" key with a
79 | # list giving the asset and other descriptors for the font. For
80 | # example:
81 | # fonts:
82 | # - family: Schyler
83 | # fonts:
84 | # - asset: fonts/Schyler-Regular.ttf
85 | # - asset: fonts/Schyler-Italic.ttf
86 | # style: italic
87 | # - family: Trajan Pro
88 | # fonts:
89 | # - asset: fonts/TrajanPro.ttf
90 | # - asset: fonts/TrajanPro_Bold.ttf
91 | # weight: 700
92 | #
93 | # For details regarding fonts from package dependencies,
94 | # see https://flutter.dev/custom-fonts/#from-packages
95 |
--------------------------------------------------------------------------------
/lib/pages/room_match/bloc/room_match_bloc.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter_bloc/flutter_bloc.dart';
6 | import 'package:light_shooter/game/game.dart';
7 | import 'package:light_shooter/game/util/player_customization.dart';
8 | import 'package:light_shooter/server_conection/modules/nakama_websocket.dart';
9 | import 'package:light_shooter/server_conection/nakama_service.dart';
10 | // ignore: implementation_imports
11 | import 'package:nakama/src/models/matchmaker.dart';
12 |
13 | part 'room_match_event.dart';
14 | part 'room_match_state.dart';
15 |
16 | class RoomMatchBloc extends Bloc {
17 | final NakamaService _serverClient;
18 |
19 | List positionsToBorn = [
20 | Vector2(3, 3),
21 | Vector2(15, 15),
22 | ];
23 |
24 | StreamSubscription? subscription;
25 | RoomMatchBloc(this._serverClient) : super(const RoomMatchState()) {
26 | on(_onInitScreenEvent);
27 | on(_onMatchedEvent);
28 | on(_onCancelMatchMaker);
29 | on(_onDisposeEvent);
30 | }
31 |
32 | FutureOr _onInitScreenEvent(
33 | InitScreenEvent event,
34 | Emitter emit,
35 | ) async {
36 | subscription =
37 | _serverClient.websocket().listenMatchmaker().listen((matched) {
38 | add(MatchedEvent(matched));
39 | });
40 | await _serverClient
41 | .websocket()
42 | .createMatchMaker(propertiers: event.custom.toMap())
43 | .then((value) {
44 | emit(state.copyWith(ticket: value.ticket));
45 | });
46 | }
47 |
48 | FutureOr _onMatchedEvent(
49 | MatchedEvent event,
50 | Emitter emit,
51 | ) async {
52 | GameProperties properties = _getGameProperties(event.matched);
53 | await _serverClient.websocket().joinMatch(event.matched).then((value) {
54 | add(CancelMatchMakerEvent(withPop: false));
55 | emit(state.copyWith(gameProperties: properties));
56 | });
57 | }
58 |
59 | FutureOr _onCancelMatchMaker(
60 | CancelMatchMakerEvent event,
61 | Emitter emit,
62 | ) async {
63 | await _serverClient.websocket().exitMatchmaker().catchError((e) {});
64 | emit(state.copyWith(goBack: event.withPop));
65 | }
66 |
67 | FutureOr _onDisposeEvent(
68 | DisposeEvent event,
69 | Emitter emit,
70 | ) {
71 | subscription?.cancel();
72 | }
73 |
74 | GameProperties _getGameProperties(MatchmakerMatched event) {
75 | String userId = _serverClient.auth().getSession().userId;
76 | List users = event.users.toList();
77 | PlayerPropertie myProperties = PlayerPropertie(
78 | position: Vector2.zero(),
79 | userId: '',
80 | );
81 | List opponentPositions = [];
82 |
83 | users.sort(
84 | (a, b) {
85 | double firstNumber =
86 | a.numericProperties[NakamaWebsocket.PARAM_NUMBER_POSITION] ?? 0.0;
87 | double secondNumber =
88 | b.numericProperties[NakamaWebsocket.PARAM_NUMBER_POSITION] ?? 0.0;
89 | return firstNumber.compareTo(secondNumber);
90 | },
91 | );
92 |
93 | int index = 0;
94 | for (var u in users) {
95 | if (u.presence.userId == userId) {
96 | myProperties = PlayerPropertie(
97 | userId: u.presence.userId,
98 | name: u.presence.username,
99 | position: positionsToBorn[index],
100 | customization: PlayerCustomization.fromMap(u.stringProperties),
101 | );
102 | } else {
103 | opponentPositions.add(
104 | PlayerPropertie(
105 | userId: u.presence.userId,
106 | name: u.presence.username,
107 | position: positionsToBorn[index],
108 | customization: PlayerCustomization.fromMap(u.stringProperties),
109 | ),
110 | );
111 | }
112 | index++;
113 | }
114 |
115 | return GameProperties(
116 | myProperties: myProperties,
117 | opponentPositions: opponentPositions,
118 | );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/lib/game/player/weapons/breaker_cannon.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:light_shooter/game/player/weapons/bullet_capsule.dart';
6 | import 'package:light_shooter/game/util/player_spritesheet.dart';
7 |
8 | class BreakerCannon extends GameDecoration with ChangeNotifier {
9 | double dt = 0;
10 | final double timeToReload = 5;
11 | final Color flash = const Color(0xFF73eff7).withOpacity(0.5);
12 | final bool withScreenEffect;
13 | final AttackFromEnum attackFrom;
14 | final PlayerColor color;
15 | final bool blockShootWithoutBullet;
16 | SpriteAnimation? _normalAnimation;
17 | SpriteAnimation? _reloadAnimation;
18 | int _countBullet = 5;
19 | bool reloading = false;
20 | double currentTimeReload = 0;
21 | BreakerCannon(
22 | Vector2 position,
23 | this.color, {
24 | this.withScreenEffect = true,
25 | this.blockShootWithoutBullet = true,
26 | this.attackFrom = AttackFromEnum.PLAYER_OR_ALLY,
27 | }) : super.withAnimation(
28 | animation: PlayerSpriteSheet.gun(color),
29 | position: position,
30 | size: Vector2.all(64),
31 | );
32 |
33 | @override
34 | void update(double dt) {
35 | this.dt = dt;
36 |
37 | if ((parent as Movement).lastDirectionHorizontal != Direction.right) {
38 | if (!isFlippedHorizontally) {
39 | flipHorizontally();
40 | }
41 | } else {
42 | if (isFlippedHorizontally) {
43 | flipHorizontally();
44 | }
45 | }
46 |
47 | if (reloading) {
48 | currentTimeReload += dt;
49 | if (currentTimeReload >= timeToReload) {
50 | reloading = false;
51 | _countBullet = 5;
52 | setAnimation(_normalAnimation);
53 | }
54 | notifyListeners();
55 | }
56 | super.update(dt);
57 | }
58 |
59 | @override
60 | Future onLoad() async {
61 | _reloadAnimation = await PlayerSpriteSheet.gunReload(color);
62 | _normalAnimation = await PlayerSpriteSheet.gun(color);
63 | anchor = Anchor.center;
64 | return super.onLoad();
65 | }
66 |
67 | void execShoot(double damage) {
68 | if (_countBullet <= 0 && blockShootWithoutBullet) {
69 | return;
70 | }
71 | playSpriteAnimationOnce(
72 | PlayerSpriteSheet.gunShot(color),
73 | );
74 | simpleAttackRangeByAngle(
75 | animation: PlayerSpriteSheet.bullet,
76 | size: Vector2.all(32),
77 | angle: radAngle,
78 | damage: damage,
79 | speed: 300,
80 | collision: RectangleHitbox(
81 | size: Vector2.all(16),
82 | position: Vector2.all(16) / 2,
83 | ),
84 | marginFromOrigin: -3,
85 | attackFrom: attackFrom,
86 | );
87 | if (withScreenEffect) {
88 | gameRef.camera.shake(
89 | intensity: 1,
90 | duration: const Duration(milliseconds: 200),
91 | );
92 | gameRef.colorFilter?.config.color = flash;
93 | gameRef.colorFilter?.animateTo(Colors.transparent);
94 | }
95 |
96 | gameRef.add(
97 | BulletCapsule(
98 | absoluteCenter,
99 | _getAnglecapsule(radAngle),
100 | ),
101 | );
102 | _countBullet--;
103 | if (_countBullet == 0) {
104 | currentTimeReload = 0;
105 | reloading = true;
106 | setAnimation(_reloadAnimation);
107 | }
108 | notifyListeners();
109 | }
110 |
111 | double radAngle = 0;
112 |
113 | void changeAngle(double radAngle) {
114 | this.radAngle = radAngle;
115 | angle = calculeNewAngle(radAngle);
116 | }
117 |
118 | double calculeNewAngle(double radAngle) {
119 | return radAngle + ((isFlippedHorizontally && radAngle != 0) ? pi : 0);
120 | }
121 |
122 | double _getAnglecapsule(double radAngle) {
123 | double angle = radAngle + pi / 2;
124 | Direction angleDirection = BonfireUtil.getDirectionFromAngle(angle);
125 | if (angleDirection == Direction.down ||
126 | angleDirection == Direction.downLeft ||
127 | angleDirection == Direction.downRight) {
128 | angle += pi;
129 | }
130 | return angle;
131 | }
132 |
133 | void reload() {
134 | playSpriteAnimationOnce(
135 | PlayerSpriteSheet.gunReload(color),
136 | );
137 | }
138 |
139 | void addBullet(int count) {
140 | _countBullet = count;
141 | }
142 |
143 | int get countBullet => _countBullet;
144 | }
145 |
--------------------------------------------------------------------------------
/lib/pages/room_match/room_match_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:light_shooter/game/game_route.dart';
4 | import 'package:light_shooter/game/util/player_customization.dart';
5 | import 'package:light_shooter/pages/room_match/bloc/room_match_bloc.dart';
6 | import 'package:light_shooter/shared/bootstrap.dart';
7 | import 'package:light_shooter/shared/theme/game_colors.dart';
8 | import 'package:light_shooter/shared/widgets/game_button.dart';
9 | import 'package:light_shooter/shared/widgets/game_container.dart';
10 | // ignore: depend_on_referenced_packages
11 |
12 | class RoomMatchPage extends StatefulWidget {
13 | final PlayerCustomization custom;
14 | const RoomMatchPage({super.key, required this.custom});
15 |
16 | @override
17 | State createState() => _RoomMatchPageState();
18 | }
19 |
20 | class _RoomMatchPageState extends State {
21 | late RoomMatchBloc _bloc;
22 |
23 | @override
24 | void initState() {
25 | _bloc = inject();
26 | _bloc.add(InitScreenEvent(widget.custom));
27 | super.initState();
28 | }
29 |
30 | @override
31 | void dispose() {
32 | _bloc.add(DisposeEvent());
33 | super.dispose();
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | return WillPopScope(
39 | onWillPop: () => Future.value(false),
40 | child: Scaffold(
41 | backgroundColor: GameColors.background,
42 | body: BlocConsumer(
43 | bloc: _bloc,
44 | listener: (context, state) {
45 | if (state.goBack) {
46 | Navigator.pop(context);
47 | }
48 | if (state.gameProperties != null) {
49 | GameRoute.open(context, state.gameProperties!);
50 | }
51 | },
52 | builder: (context, state) {
53 | return Center(
54 | child: ListView(
55 | shrinkWrap: true,
56 | children: [
57 | const Center(
58 | child: Text(
59 | 'Looking for player',
60 | style: TextStyle(
61 | color: Colors.white,
62 | fontSize: 20,
63 | ),
64 | ),
65 | ),
66 | const SizedBox(height: 32),
67 | Center(
68 | child: GameContainer(
69 | constraints: const BoxConstraints(maxWidth: 300),
70 | child: Column(
71 | mainAxisSize: MainAxisSize.min,
72 | children: [
73 | if (state.ticket.isNotEmpty) ...[
74 | const SizedBox(height: 16),
75 | const CircularProgressIndicator(
76 | valueColor: AlwaysStoppedAnimation(
77 | GameColors.primary,
78 | ),
79 | ),
80 | const SizedBox(height: 16),
81 | const Text(
82 | 'Ticket:',
83 | style: TextStyle(
84 | fontSize: 10,
85 | color: Colors.white,
86 | fontWeight: FontWeight.bold,
87 | ),
88 | ),
89 | const SizedBox(width: 10),
90 | Text(
91 | state.ticket,
92 | style: const TextStyle(
93 | fontSize: 10,
94 | color: Colors.white,
95 | ),
96 | ),
97 | const SizedBox(height: 32),
98 | GameButton(
99 | expanded: true,
100 | onPressed: () =>
101 | _bloc.add(CancelMatchMakerEvent()),
102 | text: 'Cancel',
103 | )
104 | ]
105 | ],
106 | ),
107 | ),
108 | ),
109 | ],
110 | ),
111 | );
112 | },
113 | ),
114 | ),
115 | );
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/lib/game/interface/bar_life.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: non_constant_identifier_names
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:light_shooter/game/player/breaker.dart';
6 | import 'package:light_shooter/game/player/weapons/breaker_cannon.dart';
7 | import 'package:light_shooter/shared/theme/game_colors.dart';
8 | import 'package:light_shooter/shared/widgets/change_notifier_builder.dart';
9 |
10 | class BarLife extends StatefulWidget {
11 | static String name = 'Barlife';
12 | final BonfireGame game;
13 | const BarLife({super.key, required this.game});
14 |
15 | @override
16 | State createState() => _BarLifeState();
17 | }
18 |
19 | class _BarLifeState extends State {
20 | final double BAR_WIDTH = 100;
21 | final double BAR_HEIGHT = 20;
22 | final double BORDER_RADIUS = 5;
23 |
24 | @override
25 | void initState() {
26 | Future.delayed(const Duration(milliseconds: 500), () {
27 | setState(() {});
28 | });
29 | super.initState();
30 | }
31 |
32 | Breaker? get player {
33 | return (widget.game.player as Breaker?);
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | return Material(
39 | type: MaterialType.transparency,
40 | child: Container(
41 | decoration: BoxDecoration(
42 | color: GameColors.background,
43 | borderRadius: BorderRadius.circular(BORDER_RADIUS),
44 | ),
45 | margin: const EdgeInsets.all(16),
46 | padding: const EdgeInsets.all(8),
47 | child: Column(
48 | crossAxisAlignment: CrossAxisAlignment.start,
49 | mainAxisSize: MainAxisSize.min,
50 | children: [
51 | if (player?.gun != null) ...[
52 | ChangeNotifierBuilder(
53 | value: player!,
54 | builder: _buildLife,
55 | ),
56 | const SizedBox(height: 10),
57 | ChangeNotifierBuilder(
58 | value: player!.gun!,
59 | builder: _buildCountBullet,
60 | ),
61 | ]
62 | ],
63 | ),
64 | ),
65 | );
66 | }
67 |
68 | Widget _buildCountBullet(BuildContext context, BreakerCannon value) {
69 | double height = BAR_HEIGHT / 1.5;
70 | if (value.reloading) {
71 | double percent = value.currentTimeReload / value.timeToReload;
72 | return Container(
73 | width: BAR_WIDTH,
74 | height: height,
75 | decoration: BoxDecoration(
76 | color: Colors.black,
77 | borderRadius: BorderRadius.circular(BORDER_RADIUS),
78 | ),
79 | child: Container(
80 | decoration: BoxDecoration(
81 | color: Colors.yellow,
82 | borderRadius: BorderRadius.circular(BORDER_RADIUS),
83 | ),
84 | margin: EdgeInsets.only(right: BAR_WIDTH * (1 - percent)),
85 | ),
86 | );
87 | }
88 | return Row(
89 | mainAxisSize: MainAxisSize.min,
90 | children: List.generate(value.countBullet, (index) {
91 | return Container(
92 | width: BAR_HEIGHT / 4,
93 | height: height,
94 | margin: const EdgeInsets.only(right: 5),
95 | color: Colors.blue,
96 | );
97 | }),
98 | );
99 | }
100 |
101 | Widget _buildLife(BuildContext context, Breaker value) {
102 | double percent = value.life / Breaker.maxLive;
103 | return Column(
104 | crossAxisAlignment: CrossAxisAlignment.start,
105 | mainAxisSize: MainAxisSize.min,
106 | children: [
107 | Text(
108 | value.name,
109 | style: const TextStyle(
110 | color: Colors.white,
111 | fontWeight: FontWeight.bold,
112 | ),
113 | ),
114 | const SizedBox(height: 5),
115 | Container(
116 | height: BAR_HEIGHT,
117 | width: BAR_WIDTH,
118 | decoration: BoxDecoration(
119 | color: Colors.black,
120 | borderRadius: BorderRadius.circular(BORDER_RADIUS),
121 | border: Border.all(color: Colors.white),
122 | ),
123 | child: AnimatedContainer(
124 | duration: const Duration(milliseconds: 300),
125 | decoration: BoxDecoration(
126 | borderRadius: BorderRadius.circular(BORDER_RADIUS),
127 | color: _getColor(percent),
128 | ),
129 | height: BAR_HEIGHT,
130 | margin: EdgeInsets.only(right: BAR_WIDTH * (1 - percent)),
131 | ),
132 | ),
133 | ],
134 | );
135 | }
136 |
137 | Color _getColor(double percent) {
138 | if (percent > 0.8) {
139 | return Colors.green;
140 | }
141 | if (percent > 0.3) {
142 | return Colors.yellow;
143 | }
144 |
145 | return Colors.red;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/game/game.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: public_member_api_docs, sort_constructors_first
2 | import 'package:bonfire/base/bonfire_game_interface.dart';
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:light_shooter/game/decorations/poison.dart';
6 | import 'package:light_shooter/game/game_win_controller.dart';
7 | import 'package:light_shooter/game/interface/bar_life.dart';
8 | import 'package:light_shooter/game/player/breaker.dart';
9 | import 'package:light_shooter/game/remote_player/remote_breaker.dart';
10 | import 'package:light_shooter/game/util/player_customization.dart';
11 | import 'package:light_shooter/server_conection/modules/nakama_websocket.dart';
12 | import 'package:light_shooter/server_conection/nakama_service.dart';
13 | import 'package:light_shooter/shared/bootstrap.dart';
14 | // ignore: depend_on_referenced_packages
15 | import 'package:nakama/nakama.dart';
16 |
17 | class PlayerPropertie {
18 | String userId;
19 | String name;
20 | Vector2 position;
21 | final PlayerCustomization customization;
22 | PlayerPropertie({
23 | required this.userId,
24 | required this.position,
25 | this.name = '',
26 | this.customization = const PlayerCustomization(),
27 | });
28 | }
29 |
30 | class GameProperties {
31 | final PlayerPropertie myProperties;
32 | final List opponentPositions;
33 | GameProperties({
34 | required this.myProperties,
35 | required this.opponentPositions,
36 | });
37 | }
38 |
39 | class Game extends StatefulWidget {
40 | static const tileSize = 32.0;
41 | final GameProperties properties;
42 | final bool enabledMouse;
43 | const Game({
44 | Key? key,
45 | required this.properties,
46 | this.enabledMouse = false,
47 | }) : super(key: key);
48 |
49 | @override
50 | State createState() => _GameState();
51 | }
52 |
53 | class _GameState extends State {
54 | late NakamaWebsocket _websocketClient;
55 | late NakamaService _serverClient;
56 | BonfireGameInterface? game;
57 | String userId = '';
58 | @override
59 | void initState() {
60 | _serverClient = inject();
61 | _websocketClient = _serverClient.websocket();
62 | userId = _serverClient.auth().getSession().userId;
63 | _websocketClient.addOnMatchPresenceObserser(_onMatchPresence);
64 | super.initState();
65 | }
66 |
67 | @override
68 | Widget build(BuildContext context) {
69 | return WillPopScope(
70 | onWillPop: () => Future.value(false),
71 | child: BonfireWidget(
72 | joystick: Joystick(
73 | keyboardConfig: KeyboardConfig(),
74 | directional: widget.enabledMouse
75 | ? null
76 | : JoystickDirectional(
77 | isFixed: false,
78 | size: 100,
79 | ),
80 | actions: widget.enabledMouse
81 | ? []
82 | : [
83 | JoystickAction(
84 | actionId: 1,
85 | enableDirection: true,
86 | margin: const EdgeInsets.all(80),
87 | size: 60,
88 | ),
89 | ],
90 | ),
91 | overlayBuilderMap: {
92 | BarLife.name: (context, game) => BarLife(game: game),
93 | },
94 | initialActiveOverlays: [
95 | BarLife.name,
96 | ],
97 | map: WorldMapByTiled(
98 | 'maps/map1.tmj',
99 | objectsBuilder: {
100 | Poison.name: (prop) => Poison(prop.position, prop.size)
101 | },
102 | ),
103 | player: Breaker(
104 | position: widget.properties.myProperties.position * Game.tileSize,
105 | color: widget.properties.myProperties.customization.color,
106 | name: widget.properties.myProperties.name,
107 | websocketClient: _websocketClient,
108 | enabledMouse: widget.enabledMouse,
109 | ),
110 | cameraConfig: CameraConfig(
111 | zoom: getZoomFromMaxVisibleTile(context, 20, 30),
112 | ),
113 | onReady: _onReady,
114 | onDispose: () => _websocketClient.leaveMatch(),
115 | components: [
116 | GameWinController(),
117 | ],
118 | ),
119 | );
120 | }
121 |
122 | void _onMatchPresence(MatchPresenceEvent data) {
123 | for (var leave in data.leaves) {
124 | game
125 | ?.query()
126 | .where((element) => element.id == leave.userId)
127 | .forEach((remote) => remote.removeFromParent());
128 | }
129 | }
130 |
131 | void _onReady(BonfireGameInterface game) {
132 | this.game = game;
133 | for (var e in widget.properties.opponentPositions) {
134 | game.add(
135 | RemoteBreaker(
136 | id: e.userId,
137 | position: e.position * Game.tileSize,
138 | color: e.customization.color,
139 | name: e.name,
140 | websocket: _websocketClient,
141 | ),
142 | );
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/lib/server_conection/modules/nakama_websocket.dart:
--------------------------------------------------------------------------------
1 | // ignore: depend_on_referenced_packages
2 | // ignore_for_file: constant_identifier_names
3 |
4 | import 'dart:async';
5 | import 'dart:math';
6 |
7 | // ignore: depend_on_referenced_packages
8 | import 'package:nakama/nakama.dart';
9 |
10 | class NakamaWebsocket {
11 | static const PARAM_NUMBER_POSITION = 'numberPosition';
12 | final String host;
13 | NakamaWebsocketClient? _websocketClient;
14 | MatchmakerTicket? matchmakerTicket;
15 | Match? match;
16 | MatchmakerMatched? matched;
17 | final List _onMatchDataObservers = [];
18 | final List _onMatchPresenceObservers = [];
19 |
20 | StreamSubscription? onMatchDataSubscription;
21 | StreamSubscription? onMatchPresenceSubscription;
22 |
23 | NakamaWebsocket({this.host = '127.0.0.1'});
24 |
25 | void init(Session session) {
26 | _websocketClient = NakamaWebsocketClient.init(
27 | host: host,
28 | ssl: false,
29 | token: session.token,
30 | );
31 | }
32 |
33 | Future createMatchMaker({
34 | int minCount = 2,
35 | int maxCount = 2,
36 | Map? propertiers,
37 | }) {
38 | if (_websocketClient == null) {
39 | throw Exception('WebsocketClient not initializaed');
40 | }
41 |
42 | if (matchmakerTicket != null) {
43 | throw Exception('Already in a match');
44 | }
45 | return _websocketClient!
46 | .addMatchmaker(
47 | minCount: minCount,
48 | maxCount: maxCount,
49 | query: '*',
50 | numericProperties: {
51 | PARAM_NUMBER_POSITION: Random().nextInt(90000).toDouble(),
52 | },
53 | stringProperties: propertiers,
54 | )
55 | .then(
56 | (value) {
57 | return matchmakerTicket = value;
58 | },
59 | );
60 | }
61 |
62 | bool get inMatch => matchmakerTicket != null;
63 |
64 | Future exitMatchmaker() async {
65 | if (_websocketClient == null) {
66 | throw Exception('WebsocketClient not initializaed');
67 | }
68 | if (matchmakerTicket != null) {
69 | await _websocketClient!.removeMatchmaker(matchmakerTicket!.ticket);
70 | matchmakerTicket = null;
71 | }
72 | }
73 |
74 | Stream listenMatchmaker() {
75 | if (_websocketClient == null) {
76 | throw Exception('WebsocketClient not initializaed');
77 | }
78 | return _websocketClient!.onMatchmakerMatched;
79 | }
80 |
81 | Future joinMatch(MatchmakerMatched matched) {
82 | if (_websocketClient == null) {
83 | throw Exception('WebsocketClient not initializaed');
84 | }
85 | this.matched = matched;
86 | return _websocketClient!
87 | .joinMatch(matched.matchId ?? '', token: matched.token)
88 | .then(
89 | (value) {
90 | _startListens();
91 | return match = value;
92 | },
93 | );
94 | }
95 |
96 | Future leaveMatch() async {
97 | if (_websocketClient == null) {
98 | throw Exception('WebsocketClient not initializaed');
99 | }
100 | if (match?.matchId == null) {
101 | throw Exception('There is not matched');
102 | }
103 | await _websocketClient!.leaveMatch(match?.matchId ?? '');
104 | _stopListens();
105 | }
106 |
107 | Future sendMatchData(int code, String data) {
108 | if (_websocketClient == null) {
109 | throw Exception('WebsocketClient not initializaed');
110 | }
111 | if (match?.matchId == null) {
112 | throw Exception('There is not matched');
113 | }
114 |
115 | return _websocketClient!.sendMatchData(
116 | matchId: match!.matchId,
117 | opCode: Int64(code),
118 | data: data.codeUnits,
119 | );
120 | }
121 |
122 | void _startListens() {
123 | onMatchDataSubscription =
124 | _websocketClient!.onMatchData.listen(_onMatchObserver);
125 | onMatchPresenceSubscription =
126 | _websocketClient!.onMatchPresence.listen(_onMatchPresence);
127 | }
128 |
129 | void _stopListens() {
130 | onMatchDataSubscription?.cancel();
131 | onMatchPresenceSubscription?.cancel();
132 | _onMatchDataObservers.clear();
133 | _onMatchPresenceObservers.clear();
134 | }
135 |
136 | void _onMatchObserver(MatchData event) {
137 | for (var observer in _onMatchDataObservers) {
138 | observer(event);
139 | }
140 | }
141 |
142 | void _onMatchPresence(MatchPresenceEvent event) {
143 | for (var observer in _onMatchPresenceObservers) {
144 | observer(event);
145 | }
146 | }
147 |
148 | void addOnMatchDataObserser(Function(MatchData data) observer) {
149 | if (!_onMatchDataObservers.any((element) => element == observer)) {
150 | _onMatchDataObservers.add(observer);
151 | }
152 | }
153 |
154 | void removeOnMatchDataObserser(Function(MatchData data) observer) {
155 | _onMatchDataObservers.remove(observer);
156 | }
157 |
158 | void addOnMatchPresenceObserser(Function(MatchPresenceEvent data) observer) {
159 | if (!_onMatchPresenceObservers.any((element) => element == observer)) {
160 | _onMatchPresenceObservers.add(observer);
161 | }
162 | }
163 |
164 | void removeOnMatchPresenceObserser(
165 | Function(MatchPresenceEvent data) observer) {
166 | _onMatchPresenceObservers.remove(observer);
167 | }
168 |
169 | Match getMatch() {
170 | if (match?.matchId == null) {
171 | throw Exception('There is not matched');
172 | }
173 | return match!;
174 | }
175 |
176 | MatchmakerMatched getMatched() {
177 | if (matched?.matchId == null) {
178 | throw Exception('There is not matched');
179 | }
180 | return matched!;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/lib/game/player/breaker.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:bonfire/bonfire.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:light_shooter/game/player/weapons/breaker_cannon.dart';
6 | import 'package:light_shooter/game/remote_player/remote_breaker.dart';
7 | import 'package:light_shooter/game/util/player_spritesheet.dart';
8 | import 'package:light_shooter/server_conection/messages/attack_message.dart';
9 | import 'package:light_shooter/server_conection/messages/base/message.dart';
10 | import 'package:light_shooter/server_conection/messages/die_message.dart';
11 | import 'package:light_shooter/server_conection/messages/move_message.dart';
12 | import 'package:light_shooter/server_conection/messages/receive_damage_message.dart';
13 | import 'package:light_shooter/server_conection/modules/nakama_websocket.dart';
14 |
15 | class Breaker extends SimplePlayer
16 | with BlockMovementCollision, MouseEventListener, Lighting, ChangeNotifier {
17 | static const double maxLive = 100;
18 | BreakerCannon? gun;
19 | final Color flashDamage = Colors.red;
20 | final bool enabledMouse;
21 | double gunDamage = 25;
22 | final NakamaWebsocket websocketClient;
23 | Direction? lastSocketDirection;
24 | final PlayerColor color;
25 | final String name;
26 | final lineGunPaint = Paint()
27 | ..color = Colors.blue.withOpacity(0.2)
28 | ..strokeWidth = 3;
29 | Breaker({
30 | required super.position,
31 | required this.websocketClient,
32 | required this.color,
33 | this.name = '',
34 | this.enabledMouse = false,
35 | }) : super(
36 | size: Vector2.all(64),
37 | animation: PlayerSpriteSheet.animation(color),
38 | speed: 60,
39 | life: maxLive,
40 | ) {
41 | enableMouseGesture = enabledMouse;
42 | }
43 |
44 | @override
45 | Future onLoad() async {
46 | await add(
47 | RectangleHitbox(
48 | size: size / 4,
49 | position: Vector2(size.y * 0.35, size.x * 0.70),
50 | ),
51 | );
52 | return super.onLoad();
53 | }
54 |
55 | @override
56 | void update(double dt) {
57 | if (!isIdle) {
58 | if (lastDirection != lastSocketDirection) {
59 | lastSocketDirection = lastDirection;
60 | sendMessage(MoveMessage(lastDirection.name, position, speed));
61 | } else if (checkInterval('sendDirection', 300, dt)) {
62 | sendMessage(MoveMessage(lastDirection.name, position, speed));
63 | }
64 | }
65 | super.update(dt);
66 | }
67 |
68 | @override
69 | void idle() {
70 | sendMessage(MoveMessage('idle', position, speed));
71 | super.idle();
72 | }
73 |
74 | @override
75 | void render(Canvas canvas) {
76 | if (gun?.radAngle != 0) {
77 | final p1 = gun!.center.toOffset();
78 | final p2 = BonfireUtil.movePointByAngle(gun!.center, 800, gun!.radAngle)
79 | .toOffset();
80 |
81 | canvas.drawLine(p1, p2, lineGunPaint);
82 | }
83 | super.render(canvas);
84 | }
85 |
86 | @override
87 | void onJoystickAction(JoystickActionEvent event) {
88 | if (event.id == 1) {
89 | if (event.event == ActionEvent.MOVE) {
90 | gun?.changeAngle(event.radAngle);
91 | }
92 | if (event.event == ActionEvent.UP) {
93 | if (gun?.reloading == false) {
94 | gun?.execShoot(gunDamage);
95 | _sendShootMessage(gun!.radAngle, gunDamage);
96 | }
97 | gun?.changeAngle(0);
98 | }
99 | }
100 | if (event.id == 2 && event.event == ActionEvent.DOWN) {
101 | gun?.reload();
102 | }
103 | super.onJoystickAction(event);
104 | }
105 |
106 | @override
107 | void onMount() {
108 | add(gun = BreakerCannon(Vector2(32, 44), color));
109 | super.onMount();
110 | }
111 |
112 | @override
113 | void removeLife(double life) {
114 | gameRef.colorFilter?.config.color = flashDamage;
115 | gameRef.colorFilter?.animateTo(Colors.transparent);
116 | sendMessage(ReceiveDamageMessage(life));
117 | showDamage(
118 | life,
119 | config: const TextStyle(fontSize: 14, color: Colors.red),
120 | );
121 | super.removeLife(life);
122 | notifyListeners();
123 | }
124 |
125 | @override
126 | void onMouseTap(MouseButton button) {}
127 |
128 | @override
129 | void onMouseScreenTapDown(int pointer, Vector2 position, MouseButton button) {
130 | var angle = BonfireUtil.angleBetweenPoints(
131 | gun?.absoluteCenter ?? absoluteCenter,
132 | gameRef.screenToWorld(position),
133 | );
134 | if (gun?.reloading == false) {
135 | gun?.execShoot(gunDamage);
136 | _sendShootMessage(angle, gunDamage);
137 | }
138 | super.onMouseScreenTapDown(pointer, position, button);
139 | }
140 |
141 | @override
142 | void onMouseHoverScreen(int pointer, Vector2 position) {
143 | double angle = BonfireUtil.angleBetweenPoints(
144 | gun?.absoluteCenter ?? absoluteCenter,
145 | gameRef.screenToWorld(position),
146 | );
147 | gun?.changeAngle(angle);
148 | super.onMouseHoverScreen(pointer, position);
149 | }
150 |
151 | @override
152 | bool onBlockMovement(Set intersectionPoints, GameComponent other) {
153 | if (other is RemoteBreaker) {
154 | return false;
155 | }
156 | return super.onBlockMovement(intersectionPoints, other);
157 | }
158 |
159 | void _sendShootMessage(double angle, damage) {
160 | sendMessage(AttackMessage(damage, 'cannon', angle));
161 | }
162 |
163 | void sendMessage(Message m) {
164 | websocketClient.sendMatchData(
165 | m.op,
166 | jsonEncode(m.toJson()),
167 | );
168 | }
169 |
170 | @override
171 | void die() {
172 | sendMessage(DieMessage());
173 | animation?.playOnce(
174 | PlayerSpriteSheet.die(color),
175 | onFinish: removeFromParent,
176 | );
177 | super.die();
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/lib/pages/home/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:bonfire/bonfire.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:light_shooter/game/util/player_customization.dart';
4 | import 'package:light_shooter/game/util/player_spritesheet.dart';
5 | import 'package:light_shooter/pages/login/login_route.dart';
6 | import 'package:light_shooter/pages/room_match/room_match_route.dart';
7 | import 'package:light_shooter/server_conection/nakama_service.dart';
8 | import 'package:light_shooter/shared/bootstrap.dart';
9 | import 'package:light_shooter/shared/theme/game_colors.dart';
10 | import 'package:light_shooter/shared/widgets/game_button.dart';
11 | import 'package:light_shooter/shared/widgets/game_color_selector.dart';
12 | import 'package:light_shooter/shared/widgets/game_container.dart';
13 | // ignore: depend_on_referenced_packages
14 | import 'package:nakama/nakama.dart';
15 |
16 | class HomePage extends StatefulWidget {
17 | const HomePage({super.key});
18 |
19 | @override
20 | State createState() => _HomePageState();
21 | }
22 |
23 | class _HomePageState extends State {
24 | late NakamaService _serverClient;
25 |
26 | PlayerCustomization customization = const PlayerCustomization();
27 |
28 | @override
29 | void initState() {
30 | _serverClient = inject();
31 | super.initState();
32 | }
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | return Scaffold(
37 | backgroundColor: GameColors.background,
38 | body: Stack(
39 | children: [
40 | Center(
41 | child: ListView(
42 | shrinkWrap: true,
43 | children: [
44 | Center(
45 | child: FutureBuilder(
46 | future: _serverClient.auth().getAccount(),
47 | builder: (context, snapshot) {
48 | if (snapshot.hasData) {
49 | return Text(
50 | 'Hello ${snapshot.data?.user.username}!',
51 | style: const TextStyle(
52 | color: Colors.white,
53 | fontSize: 22,
54 | ),
55 | );
56 | }
57 | return const SizedBox.shrink();
58 | },
59 | ),
60 | ),
61 | const SizedBox(height: 32),
62 | Center(
63 | child: Padding(
64 | padding: EdgeInsets.symmetric(
65 | horizontal: MediaQuery.of(context).size.width / 4,
66 | ),
67 | child: GameContainer(
68 | child: Padding(
69 | padding: const EdgeInsets.only(top: 16),
70 | child: Column(
71 | mainAxisSize: MainAxisSize.min,
72 | children: [
73 | const Text(
74 | "Select your character's color",
75 | style: TextStyle(
76 | color: Colors.white,
77 | ),
78 | ),
79 | const SizedBox(height: 32),
80 | Padding(
81 | padding: const EdgeInsets.all(20),
82 | child: Transform.scale(
83 | scale: 2,
84 | child: Container(
85 | transform:
86 | Matrix4.translationValues(0, -25, 0),
87 | height: 100,
88 | width: 100,
89 | child: PlayerSpriteSheet.talk(
90 | customization.color)
91 | .asWidget(),
92 | ),
93 | ),
94 | ),
95 | GameColorSelector(
96 | colorSelected: customization.color,
97 | onChanged: (color) {
98 | setState(() {
99 | customization = customization.copyWith(
100 | color: color,
101 | );
102 | });
103 | },
104 | ),
105 | const SizedBox(height: 32),
106 | GameButton(
107 | expanded: true,
108 | onPressed: _createMatchMaker,
109 | text: 'Play',
110 | ),
111 | ],
112 | ),
113 | ),
114 | ),
115 | ),
116 | ),
117 | ],
118 | ),
119 | ),
120 | Align(
121 | alignment: Alignment.topRight,
122 | child: Padding(
123 | padding: const EdgeInsets.all(16.0),
124 | child: IconButton(
125 | onPressed: _logout,
126 | icon: const Icon(
127 | Icons.exit_to_app,
128 | color: Colors.white,
129 | ),
130 | ),
131 | ),
132 | ),
133 | ],
134 | ),
135 | );
136 | }
137 |
138 | void _createMatchMaker() {
139 | RoomMatchRoute.open(context, customization);
140 | }
141 |
142 | void _logout() async {
143 | await _serverClient.auth().logout().catchError((e) {});
144 | if (mounted) {
145 | LoginRoute.open(context);
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/lib/pages/login/login_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:gif_view/gif_view.dart';
4 | import 'package:light_shooter/pages/home/home_route.dart';
5 | import 'package:light_shooter/pages/login/bloc/login_bloc.dart';
6 | import 'package:light_shooter/shared/bootstrap.dart';
7 | import 'package:light_shooter/shared/theme/game_colors.dart';
8 | import 'package:light_shooter/shared/widgets/game_button.dart';
9 | import 'package:light_shooter/shared/widgets/game_container.dart';
10 | import 'package:light_shooter/shared/widgets/game_textfield.dart';
11 |
12 | class LoginPage extends StatefulWidget {
13 | const LoginPage({super.key});
14 |
15 | @override
16 | State createState() => _LoginPageState();
17 | }
18 |
19 | class _LoginPageState extends State {
20 | final GlobalKey _form = GlobalKey();
21 | final TextEditingController _userName = TextEditingController();
22 | final TextEditingController _email = TextEditingController();
23 | final TextEditingController _password = TextEditingController();
24 | final TextEditingController _confirmpassowrd = TextEditingController();
25 | late LoginBloc _bloc;
26 | @override
27 | void initState() {
28 | _bloc = inject();
29 | super.initState();
30 | }
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | return Scaffold(
35 | backgroundColor: GameColors.background,
36 | body: BlocConsumer(
37 | bloc: _bloc,
38 | listener: (context, state) {
39 | if (state.authorized) {
40 | HomeRoute.open(context);
41 | }
42 | if (state.error.isNotEmpty) {
43 | _showErrorSnackbar(context, state.error);
44 | }
45 | },
46 | builder: (context, state) {
47 | return Stack(
48 | children: [
49 | Center(
50 | child: Form(
51 | key: _form,
52 | child: Column(
53 | mainAxisSize: MainAxisSize.min,
54 | children: [
55 | const Text(
56 | 'Light Shooter',
57 | style: TextStyle(
58 | color: Colors.white,
59 | fontSize: 30,
60 | fontWeight: FontWeight.bold,
61 | ),
62 | ),
63 | const SizedBox(
64 | height: 32,
65 | ),
66 | GameContainer(
67 | constraints: const BoxConstraints(maxWidth: 300),
68 | child: AnimatedSize(
69 | duration: const Duration(milliseconds: 300),
70 | child: Column(
71 | mainAxisSize: MainAxisSize.min,
72 | children: [
73 | const SizedBox(height: 16),
74 | if (state.signUpMode) ...[
75 | GameTextField(
76 | controller: _userName,
77 | hint: 'User name',
78 | enabled: !state.loading,
79 | validator: (value) {
80 | String text = value ?? '';
81 | if (text.isEmpty) {
82 | return 'Field required';
83 | }
84 | return null;
85 | },
86 | ),
87 | const SizedBox(height: 16),
88 | ],
89 | GameTextField(
90 | controller: _email,
91 | hint: 'E-mail',
92 | enabled: !state.loading,
93 | validator: (value) {
94 | String text = value ?? '';
95 | if (text.isEmpty) {
96 | return 'Field required';
97 | }
98 | return null;
99 | },
100 | ),
101 | const SizedBox(height: 16),
102 | GameTextField(
103 | controller: _password,
104 | hint: 'Password',
105 | enabled: !state.loading,
106 | onFieldSubmitted: (_) {
107 | _doSignIn();
108 | },
109 | validator: (value) {
110 | String text = value ?? '';
111 | if (text.isEmpty) {
112 | return 'Field required';
113 | }
114 | return null;
115 | },
116 | ),
117 | if (state.signUpMode) ...[
118 | const SizedBox(height: 16),
119 | GameTextField(
120 | controller: _confirmpassowrd,
121 | hint: 'Confirm password',
122 | enabled: !state.loading,
123 | validator: (value) {
124 | String text = value ?? '';
125 | if (text.isEmpty) {
126 | return 'Field required';
127 | }
128 | if (_password.text != text) {
129 | return 'The passawords is not the same';
130 | }
131 | return null;
132 | },
133 | ),
134 | ],
135 | const SizedBox(height: 32),
136 | GameButton(
137 | expanded: true,
138 | onPressed: () {
139 | if (state.signUpMode) {
140 | _doSignUp();
141 | } else {
142 | _doSignIn();
143 | }
144 | },
145 | text: state.signUpMode ? 'Sign up' : 'Sign in',
146 | loading: state.loading,
147 | ),
148 | const SizedBox(height: 16),
149 | SizedBox(
150 | height: 40,
151 | width: double.maxFinite,
152 | child: TextButton(
153 | onPressed: () {
154 | _bloc.add(
155 | ClickSignUpEvent(
156 | goSignUp: !state.signUpMode,
157 | ),
158 | );
159 | },
160 | style: const ButtonStyle(
161 | shape: MaterialStatePropertyAll(
162 | StadiumBorder(),
163 | ),
164 | ),
165 | child: Text(
166 | state.signUpMode ? 'Voltar' : 'Sign up',
167 | style: const TextStyle(color: Colors.white),
168 | ),
169 | ),
170 | )
171 | ],
172 | ),
173 | ),
174 | ),
175 | ],
176 | ),
177 | ),
178 | ),
179 | Align(
180 | alignment: Alignment.bottomRight,
181 | child: Padding(
182 | padding: const EdgeInsets.all(16),
183 | child: Column(
184 | mainAxisSize: MainAxisSize.min,
185 | children: [
186 | GifView.asset(
187 | 'assets/bonfire.gif',
188 | height: 50,
189 | width: 50,
190 | ),
191 | const SizedBox(
192 | height: 10,
193 | ),
194 | const Text(
195 | 'Buit with Bonfire',
196 | style: TextStyle(
197 | color: Colors.white,
198 | fontSize: 12,
199 | fontWeight: FontWeight.w300,
200 | ),
201 | ),
202 | ],
203 | ),
204 | ),
205 | ),
206 | ],
207 | );
208 | },
209 | ),
210 | );
211 | }
212 |
213 | void _doSignUp() {
214 | if (_form.currentState?.validate() == true) {
215 | _bloc.add(SignUpEvent(_userName.text, _email.text, _password.text));
216 | }
217 | }
218 |
219 | void _doSignIn() {
220 | if (_form.currentState?.validate() == true) {
221 | _bloc.add(SignInEvent(_email.text, _password.text));
222 | }
223 | }
224 |
225 | void _showErrorSnackbar(BuildContext context, String error) {
226 | var snackBar = SnackBar(
227 | backgroundColor: Colors.red,
228 | content: Text(error),
229 | );
230 | ScaffoldMessenger.of(context).showSnackBar(snackBar);
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/assets/images/maps/map1.tmj:
--------------------------------------------------------------------------------
1 | { "compressionlevel":-1,
2 | "height":20,
3 | "infinite":false,
4 | "layers":[
5 | {
6 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
9 | 0, 72, 72, 107, 72, 72, 72, 72, 72, 70, 72, 72, 72, 72, 72, 72, 70, 72, 72, 0,
10 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
11 | 0, 72, 72, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
12 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 72, 0,
13 | 0, 72, 72, 72, 72, 72, 72, 70, 72, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 0,
14 | 0, 0, 0, 0, 0, 72, 72, 72, 72, 71, 72, 72, 72, 72, 71, 71, 72, 72, 72, 0,
15 | 0, 0, 0, 0, 0, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 107, 72, 0,
16 | 0, 0, 0, 0, 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
17 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 107, 72, 72, 72, 72, 72, 72, 72, 71, 0,
18 | 0, 72, 70, 72, 72, 72, 72, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 71, 0,
19 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 0,
20 | 0, 72, 72, 72, 71, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 0,
21 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
22 | 0, 72, 72, 72, 72, 72, 72, 72, 72, 72, 107, 72, 72, 72, 72, 72, 72, 107, 72, 0,
23 | 0, 72, 72, 72, 72, 72, 72, 72, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 0,
24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
26 | "height":20,
27 | "id":2,
28 | "name":"ground",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":20,
33 | "x":0,
34 | "y":0
35 | },
36 | {
37 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38 | 0, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 0,
39 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
40 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
41 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
42 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
43 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
44 | 0, 335, 6, 6, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
45 | 0, 0, 0, 0, 84, 0, 0, 0, 0, 74, 74, 74, 74, 0, 0, 0, 0, 0, 84, 0,
46 | 0, 0, 0, 0, 84, 0, 0, 0, 0, 74, 147, 147, 74, 0, 0, 0, 0, 0, 84, 0,
47 | 0, 3, 6, 6, 81, 0, 0, 0, 0, 74, 147, 147, 74, 0, 0, 0, 0, 0, 84, 0,
48 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 74, 74, 74, 74, 0, 0, 0, 0, 0, 84, 0,
49 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
50 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
51 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
52 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
53 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
54 | 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
55 | 0, 79, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 379, 0,
56 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
57 | "height":20,
58 | "id":1,
59 | "name":"wall",
60 | "opacity":1,
61 | "type":"tilelayer",
62 | "visible":true,
63 | "width":20,
64 | "x":0,
65 | "y":0
66 | },
67 | {
68 | "draworder":"topdown",
69 | "id":4,
70 | "name":"poison",
71 | "objects":[
72 | {
73 | "class":"",
74 | "height":64,
75 | "id":1,
76 | "name":"poison",
77 | "rotation":0,
78 | "visible":true,
79 | "width":64,
80 | "x":319.75,
81 | "y":288
82 | }],
83 | "opacity":1,
84 | "type":"objectgroup",
85 | "visible":true,
86 | "x":0,
87 | "y":0
88 | }],
89 | "nextlayerid":5,
90 | "nextobjectid":4,
91 | "orientation":"orthogonal",
92 | "renderorder":"right-down",
93 | "tiledversion":"1.9.0",
94 | "tileheight":32,
95 | "tilesets":[
96 | {
97 | "columns":37,
98 | "firstgid":1,
99 | "image":"tileset.png",
100 | "imageheight":736,
101 | "imagewidth":1184,
102 | "margin":0,
103 | "name":"tileset",
104 | "spacing":0,
105 | "tilecount":851,
106 | "tileheight":32,
107 | "tiles":[
108 | {
109 | "id":2,
110 | "objectgroup":
111 | {
112 | "draworder":"index",
113 | "name":"",
114 | "objects":[
115 | {
116 | "class":"",
117 | "height":32,
118 | "id":1,
119 | "name":"",
120 | "rotation":0,
121 | "visible":true,
122 | "width":32,
123 | "x":0,
124 | "y":0
125 | }],
126 | "opacity":1,
127 | "type":"objectgroup",
128 | "visible":true,
129 | "x":0,
130 | "y":0
131 | }
132 | },
133 | {
134 | "id":5,
135 | "objectgroup":
136 | {
137 | "draworder":"index",
138 | "name":"",
139 | "objects":[
140 | {
141 | "class":"",
142 | "height":32,
143 | "id":1,
144 | "name":"",
145 | "rotation":0,
146 | "visible":true,
147 | "width":32,
148 | "x":0,
149 | "y":0
150 | }],
151 | "opacity":1,
152 | "type":"objectgroup",
153 | "visible":true,
154 | "x":0,
155 | "y":0
156 | }
157 | },
158 | {
159 | "id":8,
160 | "objectgroup":
161 | {
162 | "draworder":"index",
163 | "name":"",
164 | "objects":[
165 | {
166 | "class":"",
167 | "height":32,
168 | "id":1,
169 | "name":"",
170 | "rotation":0,
171 | "visible":true,
172 | "width":32,
173 | "x":0,
174 | "y":0
175 | }],
176 | "opacity":1,
177 | "type":"objectgroup",
178 | "visible":true,
179 | "x":0,
180 | "y":0
181 | }
182 | },
183 | {
184 | "id":75,
185 | "objectgroup":
186 | {
187 | "draworder":"index",
188 | "name":"",
189 | "objects":[
190 | {
191 | "class":"",
192 | "height":32,
193 | "id":1,
194 | "name":"",
195 | "rotation":0,
196 | "visible":true,
197 | "width":8.07655502392345,
198 | "x":0,
199 | "y":0
200 | }],
201 | "opacity":1,
202 | "type":"objectgroup",
203 | "visible":true,
204 | "x":0,
205 | "y":0
206 | }
207 | },
208 | {
209 | "id":78,
210 | "objectgroup":
211 | {
212 | "draworder":"index",
213 | "name":"",
214 | "objects":[
215 | {
216 | "class":"",
217 | "height":32,
218 | "id":1,
219 | "name":"",
220 | "rotation":0,
221 | "visible":true,
222 | "width":32,
223 | "x":0,
224 | "y":0
225 | }],
226 | "opacity":1,
227 | "type":"objectgroup",
228 | "visible":true,
229 | "x":0,
230 | "y":0
231 | }
232 | },
233 | {
234 | "id":80,
235 | "objectgroup":
236 | {
237 | "draworder":"index",
238 | "name":"",
239 | "objects":[
240 | {
241 | "class":"",
242 | "height":32,
243 | "id":1,
244 | "name":"",
245 | "rotation":0,
246 | "visible":true,
247 | "width":32,
248 | "x":0,
249 | "y":0
250 | }],
251 | "opacity":1,
252 | "type":"objectgroup",
253 | "visible":true,
254 | "x":0,
255 | "y":0
256 | }
257 | },
258 | {
259 | "id":83,
260 | "objectgroup":
261 | {
262 | "draworder":"index",
263 | "name":"",
264 | "objects":[
265 | {
266 | "class":"",
267 | "height":32,
268 | "id":1,
269 | "name":"",
270 | "rotation":0,
271 | "visible":true,
272 | "width":8.0127591706539,
273 | "x":23.9872408293461,
274 | "y":0
275 | }],
276 | "opacity":1,
277 | "type":"objectgroup",
278 | "visible":true,
279 | "x":0,
280 | "y":0
281 | }
282 | },
283 | {
284 | "id":378,
285 | "objectgroup":
286 | {
287 | "draworder":"index",
288 | "name":"",
289 | "objects":[
290 | {
291 | "class":"",
292 | "height":1,
293 | "id":1,
294 | "name":"",
295 | "rotation":0,
296 | "visible":true,
297 | "width":0,
298 | "x":32,
299 | "y":2
300 | },
301 | {
302 | "class":"",
303 | "height":32,
304 | "id":3,
305 | "name":"",
306 | "rotation":0,
307 | "visible":true,
308 | "width":32,
309 | "x":0,
310 | "y":0
311 | }],
312 | "opacity":1,
313 | "type":"objectgroup",
314 | "visible":true,
315 | "x":0,
316 | "y":0
317 | }
318 | }],
319 | "tilewidth":32
320 | }],
321 | "tilewidth":32,
322 | "type":"map",
323 | "version":"1.9",
324 | "width":20
325 | }
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | a_star_algorithm:
5 | dependency: transitive
6 | description:
7 | name: a_star_algorithm
8 | sha256: "7129494d8a494dafd43fb473c412bc8ab6c9ae130be100211c9102a70f30f848"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "0.3.1"
12 | archive:
13 | dependency: transitive
14 | description:
15 | name: archive
16 | sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "3.3.7"
20 | args:
21 | dependency: transitive
22 | description:
23 | name: args
24 | sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "2.4.1"
28 | async:
29 | dependency: transitive
30 | description:
31 | name: async
32 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "2.11.0"
36 | bloc:
37 | dependency: transitive
38 | description:
39 | name: bloc
40 | sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "8.1.1"
44 | bonfire:
45 | dependency: "direct main"
46 | description:
47 | name: bonfire
48 | sha256: "89dc33098294fde0c2dd4cc8cbc94b84cd99551e0ce8c7b3677d429809ad8108"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "3.0.5"
52 | boolean_selector:
53 | dependency: transitive
54 | description:
55 | name: boolean_selector
56 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "2.1.1"
60 | characters:
61 | dependency: transitive
62 | description:
63 | name: characters
64 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "1.3.0"
68 | chopper:
69 | dependency: transitive
70 | description:
71 | name: chopper
72 | sha256: "23aac2db54f6a7854ed8984fba4b33222e53123c71a0ccf01d2b1087a1b5aab7"
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "6.1.4"
76 | clock:
77 | dependency: transitive
78 | description:
79 | name: clock
80 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "1.1.1"
84 | collection:
85 | dependency: transitive
86 | description:
87 | name: collection
88 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
89 | url: "https://pub.dev"
90 | source: hosted
91 | version: "1.17.2"
92 | convert:
93 | dependency: transitive
94 | description:
95 | name: convert
96 | sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
97 | url: "https://pub.dev"
98 | source: hosted
99 | version: "3.1.1"
100 | crypto:
101 | dependency: transitive
102 | description:
103 | name: crypto
104 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
105 | url: "https://pub.dev"
106 | source: hosted
107 | version: "3.0.3"
108 | equatable:
109 | dependency: transitive
110 | description:
111 | name: equatable
112 | sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
113 | url: "https://pub.dev"
114 | source: hosted
115 | version: "2.0.5"
116 | fake_async:
117 | dependency: transitive
118 | description:
119 | name: fake_async
120 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
121 | url: "https://pub.dev"
122 | source: hosted
123 | version: "1.3.1"
124 | fixnum:
125 | dependency: transitive
126 | description:
127 | name: fixnum
128 | sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
129 | url: "https://pub.dev"
130 | source: hosted
131 | version: "1.1.0"
132 | flame:
133 | dependency: transitive
134 | description:
135 | name: flame
136 | sha256: "042533edc7ded96c2a439b2eae1a905f2611447d0d106b33686b841371c8d8c5"
137 | url: "https://pub.dev"
138 | source: hosted
139 | version: "1.10.0"
140 | flutter:
141 | dependency: "direct main"
142 | description: flutter
143 | source: sdk
144 | version: "0.0.0"
145 | flutter_bloc:
146 | dependency: "direct main"
147 | description:
148 | name: flutter_bloc
149 | sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775"
150 | url: "https://pub.dev"
151 | source: hosted
152 | version: "8.1.2"
153 | flutter_lints:
154 | dependency: "direct dev"
155 | description:
156 | name: flutter_lints
157 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
158 | url: "https://pub.dev"
159 | source: hosted
160 | version: "2.0.1"
161 | flutter_test:
162 | dependency: "direct dev"
163 | description: flutter
164 | source: sdk
165 | version: "0.0.0"
166 | freezed_annotation:
167 | dependency: transitive
168 | description:
169 | name: freezed_annotation
170 | sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338
171 | url: "https://pub.dev"
172 | source: hosted
173 | version: "2.2.0"
174 | get_it:
175 | dependency: "direct main"
176 | description:
177 | name: get_it
178 | sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3
179 | url: "https://pub.dev"
180 | source: hosted
181 | version: "7.6.4"
182 | gif_view:
183 | dependency: "direct main"
184 | description:
185 | name: gif_view
186 | sha256: "73a089a046fd1a5e63e3856cf30ef7bac1e64b78d79becde1449e955dddfb645"
187 | url: "https://pub.dev"
188 | source: hosted
189 | version: "0.4.0"
190 | googleapis_auth:
191 | dependency: transitive
192 | description:
193 | name: googleapis_auth
194 | sha256: af7c3a3edf9d0de2e1e0a77e994fae0a581c525fa7012af4fa0d4a52ed9484da
195 | url: "https://pub.dev"
196 | source: hosted
197 | version: "1.4.1"
198 | grpc:
199 | dependency: transitive
200 | description:
201 | name: grpc
202 | sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
203 | url: "https://pub.dev"
204 | source: hosted
205 | version: "3.2.4"
206 | http:
207 | dependency: transitive
208 | description:
209 | name: http
210 | sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
211 | url: "https://pub.dev"
212 | source: hosted
213 | version: "1.1.0"
214 | http2:
215 | dependency: transitive
216 | description:
217 | name: http2
218 | sha256: "38db0c4aa9f1cd238a5d2e86aa0cc7cc91c77e0c6c94ba64bbe85e4ff732a952"
219 | url: "https://pub.dev"
220 | source: hosted
221 | version: "2.2.0"
222 | http_parser:
223 | dependency: transitive
224 | description:
225 | name: http_parser
226 | sha256: db3060f22889f3d9d55f6a217565486737037eec3609f7f3eca4d0c67ee0d8a0
227 | url: "https://pub.dev"
228 | source: hosted
229 | version: "4.0.1"
230 | js:
231 | dependency: transitive
232 | description:
233 | name: js
234 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
235 | url: "https://pub.dev"
236 | source: hosted
237 | version: "0.6.7"
238 | json_annotation:
239 | dependency: transitive
240 | description:
241 | name: json_annotation
242 | sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
243 | url: "https://pub.dev"
244 | source: hosted
245 | version: "4.8.1"
246 | jwt_decoder:
247 | dependency: transitive
248 | description:
249 | name: jwt_decoder
250 | sha256: "54774aebf83f2923b99e6416b4ea915d47af3bde56884eb622de85feabbc559f"
251 | url: "https://pub.dev"
252 | source: hosted
253 | version: "2.0.1"
254 | lints:
255 | dependency: transitive
256 | description:
257 | name: lints
258 | sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
259 | url: "https://pub.dev"
260 | source: hosted
261 | version: "2.0.0"
262 | logging:
263 | dependency: transitive
264 | description:
265 | name: logging
266 | sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
267 | url: "https://pub.dev"
268 | source: hosted
269 | version: "1.1.1"
270 | matcher:
271 | dependency: transitive
272 | description:
273 | name: matcher
274 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
275 | url: "https://pub.dev"
276 | source: hosted
277 | version: "0.12.16"
278 | material_color_utilities:
279 | dependency: transitive
280 | description:
281 | name: material_color_utilities
282 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
283 | url: "https://pub.dev"
284 | source: hosted
285 | version: "0.5.0"
286 | meta:
287 | dependency: transitive
288 | description:
289 | name: meta
290 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
291 | url: "https://pub.dev"
292 | source: hosted
293 | version: "1.9.1"
294 | nakama:
295 | dependency: "direct main"
296 | description:
297 | path: "."
298 | ref: cc832b06427f9897ce6fea254248426ee9b53790
299 | resolved-ref: cc832b06427f9897ce6fea254248426ee9b53790
300 | url: "https://github.com/RafaelBarbosatec/flutter_nakama.git"
301 | source: git
302 | version: "1.0.1"
303 | nested:
304 | dependency: transitive
305 | description:
306 | name: nested
307 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
308 | url: "https://pub.dev"
309 | source: hosted
310 | version: "1.0.0"
311 | ordered_set:
312 | dependency: transitive
313 | description:
314 | name: ordered_set
315 | sha256: "3fedcc9121b3ba24c0a84f32da2989c42e36c159b73feadbc2f402dc55966b81"
316 | url: "https://pub.dev"
317 | source: hosted
318 | version: "5.0.1"
319 | path:
320 | dependency: transitive
321 | description:
322 | name: path
323 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
324 | url: "https://pub.dev"
325 | source: hosted
326 | version: "1.8.3"
327 | pointycastle:
328 | dependency: transitive
329 | description:
330 | name: pointycastle
331 | sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
332 | url: "https://pub.dev"
333 | source: hosted
334 | version: "3.7.3"
335 | protobuf:
336 | dependency: transitive
337 | description:
338 | name: protobuf
339 | sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
340 | url: "https://pub.dev"
341 | source: hosted
342 | version: "2.1.0"
343 | provider:
344 | dependency: transitive
345 | description:
346 | name: provider
347 | sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
348 | url: "https://pub.dev"
349 | source: hosted
350 | version: "6.0.5"
351 | sky_engine:
352 | dependency: transitive
353 | description: flutter
354 | source: sdk
355 | version: "0.0.99"
356 | source_span:
357 | dependency: transitive
358 | description:
359 | name: source_span
360 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
361 | url: "https://pub.dev"
362 | source: hosted
363 | version: "1.10.0"
364 | stack_trace:
365 | dependency: transitive
366 | description:
367 | name: stack_trace
368 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
369 | url: "https://pub.dev"
370 | source: hosted
371 | version: "1.11.0"
372 | stream_channel:
373 | dependency: transitive
374 | description:
375 | name: stream_channel
376 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
377 | url: "https://pub.dev"
378 | source: hosted
379 | version: "2.1.1"
380 | string_scanner:
381 | dependency: transitive
382 | description:
383 | name: string_scanner
384 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
385 | url: "https://pub.dev"
386 | source: hosted
387 | version: "1.2.0"
388 | term_glyph:
389 | dependency: transitive
390 | description:
391 | name: term_glyph
392 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
393 | url: "https://pub.dev"
394 | source: hosted
395 | version: "1.2.1"
396 | test_api:
397 | dependency: transitive
398 | description:
399 | name: test_api
400 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
401 | url: "https://pub.dev"
402 | source: hosted
403 | version: "0.6.0"
404 | tiledjsonreader:
405 | dependency: transitive
406 | description:
407 | name: tiledjsonreader
408 | sha256: "2fe265c0a47b81dff2ea900d6c3c915046c0d7a7347cfc812f3ddff1da914773"
409 | url: "https://pub.dev"
410 | source: hosted
411 | version: "1.3.3"
412 | typed_data:
413 | dependency: transitive
414 | description:
415 | name: typed_data
416 | sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
417 | url: "https://pub.dev"
418 | source: hosted
419 | version: "1.3.1"
420 | vector_math:
421 | dependency: transitive
422 | description:
423 | name: vector_math
424 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
425 | url: "https://pub.dev"
426 | source: hosted
427 | version: "2.1.4"
428 | web:
429 | dependency: transitive
430 | description:
431 | name: web
432 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
433 | url: "https://pub.dev"
434 | source: hosted
435 | version: "0.1.4-beta"
436 | web_socket_channel:
437 | dependency: transitive
438 | description:
439 | name: web_socket_channel
440 | sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
441 | url: "https://pub.dev"
442 | source: hosted
443 | version: "2.4.0"
444 | sdks:
445 | dart: ">=3.1.0-185.0.dev <4.0.0"
446 | flutter: ">=3.13.0"
447 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
97 | );
98 | path = Runner;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | 97C146ED1CF9000F007C117D /* Runner */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
107 | buildPhases = (
108 | 9740EEB61CF901F6004384FC /* Run Script */,
109 | 97C146EA1CF9000F007C117D /* Sources */,
110 | 97C146EB1CF9000F007C117D /* Frameworks */,
111 | 97C146EC1CF9000F007C117D /* Resources */,
112 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
114 | );
115 | buildRules = (
116 | );
117 | dependencies = (
118 | );
119 | name = Runner;
120 | productName = Runner;
121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
122 | productType = "com.apple.product-type.application";
123 | };
124 | /* End PBXNativeTarget section */
125 |
126 | /* Begin PBXProject section */
127 | 97C146E61CF9000F007C117D /* Project object */ = {
128 | isa = PBXProject;
129 | attributes = {
130 | LastUpgradeCheck = 1300;
131 | ORGANIZATIONNAME = "";
132 | TargetAttributes = {
133 | 97C146ED1CF9000F007C117D = {
134 | CreatedOnToolsVersion = 7.3.1;
135 | LastSwiftMigration = 1100;
136 | };
137 | };
138 | };
139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
140 | compatibilityVersion = "Xcode 9.3";
141 | developmentRegion = en;
142 | hasScannedForEncodings = 0;
143 | knownRegions = (
144 | en,
145 | Base,
146 | );
147 | mainGroup = 97C146E51CF9000F007C117D;
148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
149 | projectDirPath = "";
150 | projectRoot = "";
151 | targets = (
152 | 97C146ED1CF9000F007C117D /* Runner */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 97C146EC1CF9000F007C117D /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXShellScriptBuildPhase section */
172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
173 | isa = PBXShellScriptBuildPhase;
174 | buildActionMask = 2147483647;
175 | files = (
176 | );
177 | inputPaths = (
178 | );
179 | name = "Thin Binary";
180 | outputPaths = (
181 | );
182 | runOnlyForDeploymentPostprocessing = 0;
183 | shellPath = /bin/sh;
184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
185 | };
186 | 9740EEB61CF901F6004384FC /* Run Script */ = {
187 | isa = PBXShellScriptBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | );
191 | inputPaths = (
192 | );
193 | name = "Run Script";
194 | outputPaths = (
195 | );
196 | runOnlyForDeploymentPostprocessing = 0;
197 | shellPath = /bin/sh;
198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
199 | };
200 | /* End PBXShellScriptBuildPhase section */
201 |
202 | /* Begin PBXSourcesBuildPhase section */
203 | 97C146EA1CF9000F007C117D /* Sources */ = {
204 | isa = PBXSourcesBuildPhase;
205 | buildActionMask = 2147483647;
206 | files = (
207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | };
212 | /* End PBXSourcesBuildPhase section */
213 |
214 | /* Begin PBXVariantGroup section */
215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
216 | isa = PBXVariantGroup;
217 | children = (
218 | 97C146FB1CF9000F007C117D /* Base */,
219 | );
220 | name = Main.storyboard;
221 | sourceTree = "";
222 | };
223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
224 | isa = PBXVariantGroup;
225 | children = (
226 | 97C147001CF9000F007C117D /* Base */,
227 | );
228 | name = LaunchScreen.storyboard;
229 | sourceTree = "";
230 | };
231 | /* End PBXVariantGroup section */
232 |
233 | /* Begin XCBuildConfiguration section */
234 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
235 | isa = XCBuildConfiguration;
236 | buildSettings = {
237 | ALWAYS_SEARCH_USER_PATHS = NO;
238 | CLANG_ANALYZER_NONNULL = YES;
239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
240 | CLANG_CXX_LIBRARY = "libc++";
241 | CLANG_ENABLE_MODULES = YES;
242 | CLANG_ENABLE_OBJC_ARC = YES;
243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
244 | CLANG_WARN_BOOL_CONVERSION = YES;
245 | CLANG_WARN_COMMA = YES;
246 | CLANG_WARN_CONSTANT_CONVERSION = YES;
247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
249 | CLANG_WARN_EMPTY_BODY = YES;
250 | CLANG_WARN_ENUM_CONVERSION = YES;
251 | CLANG_WARN_INFINITE_RECURSION = YES;
252 | CLANG_WARN_INT_CONVERSION = YES;
253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
258 | CLANG_WARN_STRICT_PROTOTYPES = YES;
259 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
260 | CLANG_WARN_UNREACHABLE_CODE = YES;
261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
263 | COPY_PHASE_STRIP = NO;
264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
265 | ENABLE_NS_ASSERTIONS = NO;
266 | ENABLE_STRICT_OBJC_MSGSEND = YES;
267 | GCC_C_LANGUAGE_STANDARD = gnu99;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
271 | GCC_WARN_UNDECLARED_SELECTOR = YES;
272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
273 | GCC_WARN_UNUSED_FUNCTION = YES;
274 | GCC_WARN_UNUSED_VARIABLE = YES;
275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
276 | MTL_ENABLE_DEBUG_INFO = NO;
277 | SDKROOT = iphoneos;
278 | SUPPORTED_PLATFORMS = iphoneos;
279 | TARGETED_DEVICE_FAMILY = "1,2";
280 | VALIDATE_PRODUCT = YES;
281 | };
282 | name = Profile;
283 | };
284 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
285 | isa = XCBuildConfiguration;
286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | CLANG_ENABLE_MODULES = YES;
290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
291 | DEVELOPMENT_TEAM = 2S2LBSQSMB;
292 | ENABLE_BITCODE = NO;
293 | INFOPLIST_FILE = Runner/Info.plist;
294 | LD_RUNPATH_SEARCH_PATHS = (
295 | "$(inherited)",
296 | "@executable_path/Frameworks",
297 | );
298 | PRODUCT_BUNDLE_IDENTIFIER = com.example.lightShooter;
299 | PRODUCT_NAME = "$(TARGET_NAME)";
300 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
301 | SWIFT_VERSION = 5.0;
302 | VERSIONING_SYSTEM = "apple-generic";
303 | };
304 | name = Profile;
305 | };
306 | 97C147031CF9000F007C117D /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
312 | CLANG_CXX_LIBRARY = "libc++";
313 | CLANG_ENABLE_MODULES = YES;
314 | CLANG_ENABLE_OBJC_ARC = YES;
315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
316 | CLANG_WARN_BOOL_CONVERSION = YES;
317 | CLANG_WARN_COMMA = YES;
318 | CLANG_WARN_CONSTANT_CONVERSION = YES;
319 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
321 | CLANG_WARN_EMPTY_BODY = YES;
322 | CLANG_WARN_ENUM_CONVERSION = YES;
323 | CLANG_WARN_INFINITE_RECURSION = YES;
324 | CLANG_WARN_INT_CONVERSION = YES;
325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
330 | CLANG_WARN_STRICT_PROTOTYPES = YES;
331 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
332 | CLANG_WARN_UNREACHABLE_CODE = YES;
333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
334 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
335 | COPY_PHASE_STRIP = NO;
336 | DEBUG_INFORMATION_FORMAT = dwarf;
337 | ENABLE_STRICT_OBJC_MSGSEND = YES;
338 | ENABLE_TESTABILITY = YES;
339 | GCC_C_LANGUAGE_STANDARD = gnu99;
340 | GCC_DYNAMIC_NO_PIC = NO;
341 | GCC_NO_COMMON_BLOCKS = YES;
342 | GCC_OPTIMIZATION_LEVEL = 0;
343 | GCC_PREPROCESSOR_DEFINITIONS = (
344 | "DEBUG=1",
345 | "$(inherited)",
346 | );
347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
349 | GCC_WARN_UNDECLARED_SELECTOR = YES;
350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
351 | GCC_WARN_UNUSED_FUNCTION = YES;
352 | GCC_WARN_UNUSED_VARIABLE = YES;
353 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
354 | MTL_ENABLE_DEBUG_INFO = YES;
355 | ONLY_ACTIVE_ARCH = YES;
356 | SDKROOT = iphoneos;
357 | TARGETED_DEVICE_FAMILY = "1,2";
358 | };
359 | name = Debug;
360 | };
361 | 97C147041CF9000F007C117D /* Release */ = {
362 | isa = XCBuildConfiguration;
363 | buildSettings = {
364 | ALWAYS_SEARCH_USER_PATHS = NO;
365 | CLANG_ANALYZER_NONNULL = YES;
366 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
367 | CLANG_CXX_LIBRARY = "libc++";
368 | CLANG_ENABLE_MODULES = YES;
369 | CLANG_ENABLE_OBJC_ARC = YES;
370 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
371 | CLANG_WARN_BOOL_CONVERSION = YES;
372 | CLANG_WARN_COMMA = YES;
373 | CLANG_WARN_CONSTANT_CONVERSION = YES;
374 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
375 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
376 | CLANG_WARN_EMPTY_BODY = YES;
377 | CLANG_WARN_ENUM_CONVERSION = YES;
378 | CLANG_WARN_INFINITE_RECURSION = YES;
379 | CLANG_WARN_INT_CONVERSION = YES;
380 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
381 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
382 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
383 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
384 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
385 | CLANG_WARN_STRICT_PROTOTYPES = YES;
386 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
387 | CLANG_WARN_UNREACHABLE_CODE = YES;
388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
389 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
390 | COPY_PHASE_STRIP = NO;
391 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
392 | ENABLE_NS_ASSERTIONS = NO;
393 | ENABLE_STRICT_OBJC_MSGSEND = YES;
394 | GCC_C_LANGUAGE_STANDARD = gnu99;
395 | GCC_NO_COMMON_BLOCKS = YES;
396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
398 | GCC_WARN_UNDECLARED_SELECTOR = YES;
399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
400 | GCC_WARN_UNUSED_FUNCTION = YES;
401 | GCC_WARN_UNUSED_VARIABLE = YES;
402 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
403 | MTL_ENABLE_DEBUG_INFO = NO;
404 | SDKROOT = iphoneos;
405 | SUPPORTED_PLATFORMS = iphoneos;
406 | SWIFT_COMPILATION_MODE = wholemodule;
407 | SWIFT_OPTIMIZATION_LEVEL = "-O";
408 | TARGETED_DEVICE_FAMILY = "1,2";
409 | VALIDATE_PRODUCT = YES;
410 | };
411 | name = Release;
412 | };
413 | 97C147061CF9000F007C117D /* Debug */ = {
414 | isa = XCBuildConfiguration;
415 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
416 | buildSettings = {
417 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
418 | CLANG_ENABLE_MODULES = YES;
419 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
420 | DEVELOPMENT_TEAM = 2S2LBSQSMB;
421 | ENABLE_BITCODE = NO;
422 | INFOPLIST_FILE = Runner/Info.plist;
423 | LD_RUNPATH_SEARCH_PATHS = (
424 | "$(inherited)",
425 | "@executable_path/Frameworks",
426 | );
427 | PRODUCT_BUNDLE_IDENTIFIER = com.example.lightShooter;
428 | PRODUCT_NAME = "$(TARGET_NAME)";
429 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
430 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
431 | SWIFT_VERSION = 5.0;
432 | VERSIONING_SYSTEM = "apple-generic";
433 | };
434 | name = Debug;
435 | };
436 | 97C147071CF9000F007C117D /* Release */ = {
437 | isa = XCBuildConfiguration;
438 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
439 | buildSettings = {
440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
441 | CLANG_ENABLE_MODULES = YES;
442 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
443 | DEVELOPMENT_TEAM = 2S2LBSQSMB;
444 | ENABLE_BITCODE = NO;
445 | INFOPLIST_FILE = Runner/Info.plist;
446 | LD_RUNPATH_SEARCH_PATHS = (
447 | "$(inherited)",
448 | "@executable_path/Frameworks",
449 | );
450 | PRODUCT_BUNDLE_IDENTIFIER = com.example.lightShooter;
451 | PRODUCT_NAME = "$(TARGET_NAME)";
452 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
453 | SWIFT_VERSION = 5.0;
454 | VERSIONING_SYSTEM = "apple-generic";
455 | };
456 | name = Release;
457 | };
458 | /* End XCBuildConfiguration section */
459 |
460 | /* Begin XCConfigurationList section */
461 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
462 | isa = XCConfigurationList;
463 | buildConfigurations = (
464 | 97C147031CF9000F007C117D /* Debug */,
465 | 97C147041CF9000F007C117D /* Release */,
466 | 249021D3217E4FDB00AE95B9 /* Profile */,
467 | );
468 | defaultConfigurationIsVisible = 0;
469 | defaultConfigurationName = Release;
470 | };
471 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
472 | isa = XCConfigurationList;
473 | buildConfigurations = (
474 | 97C147061CF9000F007C117D /* Debug */,
475 | 97C147071CF9000F007C117D /* Release */,
476 | 249021D4217E4FDB00AE95B9 /* Profile */,
477 | );
478 | defaultConfigurationIsVisible = 0;
479 | defaultConfigurationName = Release;
480 | };
481 | /* End XCConfigurationList section */
482 | };
483 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
484 | }
485 |
--------------------------------------------------------------------------------