diff --git a/fightme_webapp/assets/images/default-avatar.png b/fightme_webapp/assets/images/default-avatar.png new file mode 100644 index 0000000..77a3fb1 Binary files /dev/null and b/fightme_webapp/assets/images/default-avatar.png differ diff --git a/fightme_webapp/lib/Cosmetics/profile_pictures.dart b/fightme_webapp/lib/Cosmetics/profile_pictures.dart index 822a649..2ba265c 100644 --- a/fightme_webapp/lib/Cosmetics/profile_pictures.dart +++ b/fightme_webapp/lib/Cosmetics/profile_pictures.dart @@ -1,6 +1,15 @@ // Enter in the source link of the image. Later implementation will handle it. List profilePictures = [ + "assets/images/default-avatar.png", "assets/images/dummyKnight.png", + "assets/images/knight-green.png", + "assets/images/fknight-green.png", "assets/images/knight-grey.png", +]; + +// Structure: [element in profilePictures, price] +List> buyableProfilePictures = [ + [2, 4000], + [3, 8000], ]; \ No newline at end of file diff --git a/fightme_webapp/lib/Cosmetics/themes.dart b/fightme_webapp/lib/Cosmetics/themes.dart index 46b8337..d55ff4a 100644 --- a/fightme_webapp/lib/Cosmetics/themes.dart +++ b/fightme_webapp/lib/Cosmetics/themes.dart @@ -15,8 +15,14 @@ List themes = [ const ColorScheme(brightness: Brightness.light, primary: Colors.tealAccent, onPrimary: Colors.black, secondary: Colors.pinkAccent, onSecondary: Colors.black, surface: Colors.deepPurple, onSurface: Colors.black, error: Colors.red, onError: Colors.black), ]; +// TODO: Match the element in themes. List themeNames = [ "Default", "Dark", "Vaporwave", +]; + +// Structure: [element in themes, price] +List> buyableThemes = [ + [2, 6000], ]; \ No newline at end of file diff --git a/fightme_webapp/lib/Models/fight_game_session.dart b/fightme_webapp/lib/Models/fight_game_session.dart index 5c6fe43..040dd0a 100644 --- a/fightme_webapp/lib/Models/fight_game_session.dart +++ b/fightme_webapp/lib/Models/fight_game_session.dart @@ -5,6 +5,7 @@ enum Move {attack, defense, magic, none} class FightGameSession { int id = 0; + int winnerID = 0; User user1 = User(""); int user1hp = 5; Move user1move = Move.none; @@ -15,6 +16,7 @@ class FightGameSession { FightGameSession(User curUser, otherUser) { id = 0; + winnerID = 0; user1 = curUser; user1hp = 5; user1move = Move.none; @@ -26,11 +28,12 @@ class FightGameSession { FightGameSession.practice(User curUser) { id = 0; + winnerID = 0; user1 = curUser; user1hp = 5; user1move = Move.none; user2 = User("Dummy"); - // TODO: Set Dummy's profile picture to a picture of something like a straw man. + user2.pfp = 1; user2.attackScore = curUser.attackScore; user2.defenseScore = curUser.defenseScore; user2.magicScore = curUser.magicScore; diff --git a/fightme_webapp/lib/chat_page_web_socket.dart b/fightme_webapp/lib/chat_page_web_socket.dart index 9ee7376..b92de33 100644 --- a/fightme_webapp/lib/chat_page_web_socket.dart +++ b/fightme_webapp/lib/chat_page_web_socket.dart @@ -152,6 +152,15 @@ class ChatPageState extends State { labelText: "Message", ), onSubmitted: (value) async { + if (value.length > 255) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + duration: Duration(seconds: 3), + content: Text('You exceeded the character limit.'), + ) + ); + } + else { _sendMessage(value); // Send message via WebSocket randomNumber = Random().nextInt(100); print("I received $randomNumber"); @@ -162,6 +171,7 @@ class ChatPageState extends State { statsProvider.updateGamerscore(widget.currentUser.gamerScore + 1); } textEditControl.clear(); + } }, ), ], diff --git a/fightme_webapp/lib/chats_master_page.dart b/fightme_webapp/lib/chats_master_page.dart index c66b37f..994a4ac 100644 --- a/fightme_webapp/lib/chats_master_page.dart +++ b/fightme_webapp/lib/chats_master_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'Models/chatroom.dart'; // import 'chat_page.dart'; import 'chat_page_web_socket.dart'; +import 'Cosmetics/profile_pictures.dart'; import 'Models/user.dart'; import 'package:fightme_webapp/Models/httpservice.dart'; import 'pending_requests.dart'; @@ -59,9 +60,14 @@ class ChatsMasterPageState extends State { ))); }, child: ListTile( - leading: const Icon(Icons.account_circle_sharp), + leading: ClipRRect( + borderRadius: BorderRadius.circular(80.0), + child: Image.asset(profilePictures[user.pfp], fit: BoxFit.cover, width: 60, height: 60), + ), title: user.id != 0 ? Text(user.name) : const Text("Group"), - subtitle: room.messages.isEmpty ? const Text("") : Text(room.messages.last.content), + subtitle: room.messages.isEmpty ? const Text("") : Text(room.messages.last.content, overflow: TextOverflow.fade, + maxLines: 1, + softWrap: false), ) ); } diff --git a/fightme_webapp/lib/gamerscore_shop.dart b/fightme_webapp/lib/gamerscore_shop.dart index 1cfdce5..08d32f8 100644 --- a/fightme_webapp/lib/gamerscore_shop.dart +++ b/fightme_webapp/lib/gamerscore_shop.dart @@ -1,6 +1,8 @@ import 'package:fightme_webapp/Models/user.dart'; import 'package:fightme_webapp/main.dart'; import 'package:flutter/material.dart'; +import 'Cosmetics/profile_pictures.dart'; +import 'Cosmetics/themes.dart'; import 'package:provider/provider.dart'; import 'globals.dart' as globals; import 'Models/httpservice.dart'; @@ -16,28 +18,17 @@ class GamerscoreShop extends StatefulWidget { class _GamerscoreShopState extends State { final HttpService _httpService = HttpService(); - Map currentStats = { - 'attackScore': curUser.attackScore, - 'magicScore': curUser.magicScore, - 'defenseScore': curUser.defenseScore, - }; @override Widget build(BuildContext context) { StatsProvider statsProvider = Provider.of(context, listen: false); return Scaffold( appBar: AppBar( - title: const Text("Shop"), backgroundColor: Theme - .of(context) - .colorScheme - .primary, - centerTitle: true, - leading: IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: const Icon(Icons.arrow_back), - ), + .of(context) + .colorScheme + .primary, + centerTitle: true, + title: const Text("Shop"), ), body: Center( child: Column( @@ -54,81 +45,223 @@ class _GamerscoreShopState extends State { ); } ), - const SizedBox( - height: 200, + const Align( + alignment: Alignment.centerLeft, + child: Text("Profile Pictures", style: TextStyle( + fontSize: 40)), ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - children: [ - Image.asset("assets/images/attack.png", height: 80, width: 80, color: Colors.red), - const SizedBox(height: 10,), - SizedBox( - width: MediaQuery.of(context).size.width / 3 - 60.0, - child: ElevatedButton( - onPressed: () async { - if(statsProvider.gamerscore < 1) { - print("gamerscore is less than 1: ${statsProvider.gamerscore}"); - return; - } - await _httpService.updateUserGamerScore(globals.uid, statsProvider.gamerscore - 1); - currentStats['attackScore'] = statsProvider.attack + 1; - await _httpService.updateUserStats(globals.uid,currentStats); - statsProvider.updateGamerscore(statsProvider.gamerscore - 1); - statsProvider.updateStats(statsProvider.attack + 1, statsProvider.magic, statsProvider.defense); - print("i've updated gamerscore and stats"); - }, - child: const Text("Buy Attack"), - ), - ), - ], + SizedBox( + height: MediaQuery.of(context).size.height / 3, + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 1.25, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, ), - Column( - children: [ - Image.asset("assets/images/defense.png", height: 80, width: 80, color: Colors.green,), - const SizedBox(height: 10,), - SizedBox( - width: MediaQuery.of(context).size.width / 3 - 60.0, - child: ElevatedButton( - onPressed: () async { - if(statsProvider.gamerscore < 1) { - return; - } - await _httpService.updateUserGamerScore(globals.uid, statsProvider.gamerscore - 1); - currentStats['defenseScore'] = statsProvider.defense + 1; - await _httpService.updateUserStats(globals.uid,currentStats); - statsProvider.updateGamerscore(statsProvider.gamerscore - 1); - statsProvider.updateStats(statsProvider.attack, statsProvider.magic, statsProvider.defense + 1); - }, - child: const Text("Buy Defense"), + shrinkWrap: true, + itemCount: buyableProfilePictures.length, + itemBuilder: (BuildContext context, int index) { + bool bought = widget.curUser.unlockedpfps.firstWhere((element) => element == buyableProfilePictures[index][0], orElse: () => -1) != -1; + return TextButton( + onPressed: () { + if (!bought) { + if (buyableProfilePictures[index][1] > widget.curUser.gamerScore) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + duration: Duration(seconds: 3), + content: Text('You do not have enough gamer score for that.'), + ) + ); + } + else { + showDialog( + context: context, + builder: (BuildContext context) => + AlertDialog( + title: const Text( + 'Are you sure you want to buy?'), + content: Image.asset( + profilePictures[buyableProfilePictures[index][0]], + width: 150, height: 150), + actionsAlignment: MainAxisAlignment.center, + actions: [ + TextButton( + onPressed: () => + Navigator.pop(context, 'No'), + child: const Text('No'), + ), + TextButton( + onPressed: () async { + await _httpService.updateUserGamerScore(globals.uid, widget.curUser.gamerScore - buyableProfilePictures[index][1]); + await _httpService.addUserProfilePicture(globals.uid, buyableProfilePictures[index][0]); + setState(() { + widget.curUser.unlockedpfps.add(buyableProfilePictures[index][0]); + widget.curUser.gamerScore = widget.curUser.gamerScore - buyableProfilePictures[index][1]; + }); + Navigator.pop(context, 'Yes'); + }, + child: const Text('Yes'), + ), + ], + ), + ); + } + } + }, + child: GridTile( + child: Column( + children: [ + Image.asset(profilePictures[buyableProfilePictures[index][0]], width: 200, height: 200), + bought ? const Text("Bought", style: TextStyle( + fontSize: 30)) : + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("${buyableProfilePictures[index][1]}", style: const TextStyle( + fontSize: 30)), + const Icon(Icons.monetization_on, color: Colors.yellow, size: 30,), + ] + ), + ] ), ), - ], - ), - Column( - children: [ - Image.asset("assets/images/magic.png", width: 80, height: 80, color: Colors.purple,), - const SizedBox(height: 10,), - SizedBox( - width: MediaQuery.of(context).size.width / 3 - 60.0, - child: ElevatedButton( - onPressed: () async { - if(statsProvider.gamerscore < 1) { - return; + ); + } + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text("Themes", style: TextStyle( + fontSize: 40)), + ), + SizedBox( + height: MediaQuery.of(context).size.height / 3.0, + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 1.25, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, + ), + shrinkWrap: true, + itemCount: buyableThemes.length, + itemBuilder: (BuildContext context, int index) { + bool bought = widget.curUser.unlockedThemes.firstWhere((element) => element == buyableThemes[index][0], orElse: () => -1) != -1; + return TextButton( + onPressed: () { + if (!bought) { + if (buyableThemes[index][1] > widget.curUser.gamerScore) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + duration: Duration(seconds: 3), + content: Text('You do not have enough gamer score for that.'), + ) + ); } - await _httpService.updateUserGamerScore(globals.uid, statsProvider.gamerscore - 1); - currentStats['magicScore'] = statsProvider.magic + 1; - await _httpService.updateUserStats(globals.uid,currentStats); - statsProvider.updateGamerscore(statsProvider.gamerscore - 1); - statsProvider.updateStats(statsProvider.attack, statsProvider.magic + 1, statsProvider.defense); - }, - child: const Text("Buy Magic"), + else { + showDialog( + context: context, + builder: (BuildContext context) => + AlertDialog( + title: const Text( + 'Are you sure you want to buy?'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 45.0, + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].primary, + ) + ), + Container( + height: 90.0, + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].surface, + ), + child: Align( + child: Text(themeNames[buyableThemes[index][0]], style: TextStyle( + color: themes[buyableThemes[index][0]].onSurface)), + ) + ), + Container( + height: 45.0, + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].secondary, + ) + ), + ] + ), + actionsAlignment: MainAxisAlignment.center, + actions: [ + TextButton( + onPressed: () => + Navigator.pop(context, 'No'), + child: const Text('No'), + ), + TextButton( + onPressed: () async { + await _httpService.updateUserGamerScore(globals.uid, widget.curUser.gamerScore - buyableThemes[index][1]); + await _httpService.addUserTheme(globals.uid, buyableThemes[index][0]); + setState(() { + widget.curUser.unlockedThemes.add(buyableThemes[index][0]); + widget.curUser.gamerScore = widget.curUser.gamerScore - buyableThemes[index][1]; + }); + Navigator.pop(context, 'Yes'); + }, + child: const Text('Yes'), + ), + ], + ), + ); + } + } + }, + child: GridTile( + child: Column( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].primary, + ) + ), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].surface, + ), + child: Align( + child: Text(themeNames[buyableThemes[index][0]], style: TextStyle( + color: themes[buyableThemes[index][0]].onSurface)), + ) + ), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: themes[buyableThemes[index][0]].secondary, + ) + ), + ), + bought ? const Text("Bought", style: TextStyle( + fontSize: 30)) : + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("${buyableProfilePictures[index][1]}", style: const TextStyle( + fontSize: 30)), + const Icon(Icons.monetization_on, color: Colors.yellow, size: 30,), + ] + ), + ] + ) ), - ), - ], - ), - ], + ); + } + ), ), ], ), diff --git a/fightme_webapp/lib/pending_requests.dart b/fightme_webapp/lib/pending_requests.dart index 89b3ef7..4b25a27 100644 --- a/fightme_webapp/lib/pending_requests.dart +++ b/fightme_webapp/lib/pending_requests.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:fightme_webapp/Models/httpservice.dart'; import 'profile_page.dart'; +import 'Cosmetics/profile_pictures.dart'; import 'package:fightme_webapp/Models/user.dart'; +import 'Widgets/fightButton.dart'; import 'package:fightme_webapp/Models/friend_request.dart'; +import 'package:fightme_webapp/Models/fight_game_session.dart'; import 'globals.dart' as globals; class PendingRequestsPage extends StatefulWidget { @@ -22,7 +25,7 @@ class PendingRequestsPageState extends State { HttpService http = HttpService(); List list = List.empty(growable: true); List myRequests = await http.getAllFriendRequests(globals.uid); - myRequests.removeWhere((element) => element.status != Status.pending); + myRequests.removeWhere((element) => element.status == Status.rejected); for (var request in myRequests) { User user = await http.getUserByID(request.fromUserID); list.add( @@ -36,9 +39,12 @@ class PendingRequestsPageState extends State { userViewed: user))); }, child: ListTile( - leading: const Icon(Icons.account_circle_sharp), + leading: ClipRRect( + borderRadius: BorderRadius.circular(60.0), + child: Image.asset(profilePictures[user.pfp], fit: BoxFit.cover, width: 60, height: 60), + ), title: user.id != 0 ? Text(user.name) : const Text("Group"), - trailing: Row( + trailing: request.status == Status.pending ? Row( mainAxisSize: MainAxisSize.min, children: [ FilledButton.tonal( @@ -68,7 +74,11 @@ class PendingRequestsPageState extends State { child: const Text("reject"), ) ] - ) + ) : ElevatedButton( + onPressed: () => + buildFightButton(context, FightGameSession(curUser, user)), + child: const Text('Fight!') + ), ) ) ); diff --git a/fightme_webapp/lib/profile_page.dart b/fightme_webapp/lib/profile_page.dart index f7da410..3f5c1d1 100644 --- a/fightme_webapp/lib/profile_page.dart +++ b/fightme_webapp/lib/profile_page.dart @@ -67,7 +67,6 @@ class ProfilePageState extends State { @override Widget build(BuildContext context) { - List friendsList = List.filled(5, widget.userViewed.name); return Scaffold( appBar: AppBar( backgroundColor: Theme @@ -83,7 +82,7 @@ class ProfilePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => GamerscoreShop(curUser: widget.userViewed)), + builder: (context) => GamerscoreShop(curUser: widget.curUser)), ); }, icon: Consumer(