Merge pull request #122 from flixcoo/enhance/104-rework-kamikaze-features
Rework kamikaze feature
This commit is contained in:
@@ -4,7 +4,9 @@ class CustomTheme {
|
|||||||
static Color white = CupertinoColors.white;
|
static Color white = CupertinoColors.white;
|
||||||
static Color primaryColor = CupertinoColors.systemGreen;
|
static Color primaryColor = CupertinoColors.systemGreen;
|
||||||
static Color backgroundColor = const Color(0xFF101010);
|
static Color backgroundColor = const Color(0xFF101010);
|
||||||
static Color backgroundTintColor = CupertinoColors.darkBackgroundGray;
|
static Color mainElementBackgroundColor = CupertinoColors.darkBackgroundGray;
|
||||||
|
static Color playerTileColor = CupertinoColors.secondaryLabel;
|
||||||
|
static Color buttonBackgroundColor = const Color(0xFF202020);
|
||||||
|
|
||||||
// Line Colors for GraphView
|
// Line Colors for GraphView
|
||||||
static const Color graphColor1 = Color(0xFFF44336);
|
static const Color graphColor1 = Color(0xFFF44336);
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"results": "Ergebnisse",
|
"results": "Ergebnisse",
|
||||||
"who_said_cabo": "Wer hat CABO gesagt?",
|
"who_said_cabo": "Wer hat CABO gesagt?",
|
||||||
"kamikaze": "Kamikaze",
|
"kamikaze": "Kamikaze",
|
||||||
|
"who_has_kamikaze": "Wer hat Kamikaze?",
|
||||||
"done": "Fertig",
|
"done": "Fertig",
|
||||||
"next_round": "Nächste Runde",
|
"next_round": "Nächste Runde",
|
||||||
"bonus_points_title": "Bonus-Punkte!",
|
"bonus_points_title": "Bonus-Punkte!",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"results": "Results",
|
"results": "Results",
|
||||||
"who_said_cabo": "Who called Cabo?",
|
"who_said_cabo": "Who called Cabo?",
|
||||||
"kamikaze": "Kamikaze",
|
"kamikaze": "Kamikaze",
|
||||||
|
"who_has_kamikaze": "Who has Kamikaze?",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"next_round": "Next Round",
|
"next_round": "Next Round",
|
||||||
"bonus_points_title": "Bonus-Points!",
|
"bonus_points_title": "Bonus-Points!",
|
||||||
|
|||||||
@@ -404,6 +404,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Kamikaze'**
|
/// **'Kamikaze'**
|
||||||
String get kamikaze;
|
String get kamikaze;
|
||||||
|
|
||||||
|
/// No description provided for @who_has_kamikaze.
|
||||||
|
///
|
||||||
|
/// In de, this message translates to:
|
||||||
|
/// **'Wer hat Kamikaze?'**
|
||||||
|
String get who_has_kamikaze;
|
||||||
|
|
||||||
/// No description provided for @done.
|
/// No description provided for @done.
|
||||||
///
|
///
|
||||||
/// In de, this message translates to:
|
/// In de, this message translates to:
|
||||||
|
|||||||
@@ -172,6 +172,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get kamikaze => 'Kamikaze';
|
String get kamikaze => 'Kamikaze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get who_has_kamikaze => 'Wer hat Kamikaze?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get done => 'Fertig';
|
String get done => 'Fertig';
|
||||||
|
|
||||||
|
|||||||
@@ -170,6 +170,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get kamikaze => 'Kamikaze';
|
String get kamikaze => 'Kamikaze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get who_has_kamikaze => 'Who has Kamikaze?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get done => 'Done';
|
String get done => 'Done';
|
||||||
|
|
||||||
|
|||||||
@@ -74,21 +74,22 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
return CupertinoPageScaffold(
|
return CupertinoPageScaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
navigationBar: CupertinoNavigationBar(
|
navigationBar: CupertinoNavigationBar(
|
||||||
transitionBetweenRoutes: true,
|
transitionBetweenRoutes: true,
|
||||||
leading: CupertinoButton(
|
leading: CupertinoButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
onPressed: () =>
|
onPressed: () => {
|
||||||
{LocalStorageService.saveGameSessions(), Navigator.pop(context)},
|
LocalStorageService.saveGameSessions(),
|
||||||
child: Text(AppLocalizations.of(context).cancel),
|
Navigator.pop(context)
|
||||||
),
|
},
|
||||||
middle: Text(AppLocalizations.of(context).results),
|
child: Text(AppLocalizations.of(context).cancel),
|
||||||
trailing: widget.gameSession.isGameFinished
|
),
|
||||||
? const Icon(
|
middle: Text(AppLocalizations.of(context).results),
|
||||||
|
trailing: Visibility(
|
||||||
|
visible: widget.gameSession.isGameFinished,
|
||||||
|
child: const Icon(
|
||||||
CupertinoIcons.lock,
|
CupertinoIcons.lock,
|
||||||
size: 25,
|
size: 25,
|
||||||
)
|
))),
|
||||||
: null,
|
|
||||||
),
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@@ -114,9 +115,10 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
vertical: 10,
|
vertical: 10,
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 60,
|
||||||
child: CupertinoSegmentedControl<int>(
|
child: CupertinoSegmentedControl<int>(
|
||||||
unselectedColor: CustomTheme.backgroundTintColor,
|
unselectedColor:
|
||||||
|
CustomTheme.mainElementBackgroundColor,
|
||||||
selectedColor: CustomTheme.primaryColor,
|
selectedColor: CustomTheme.primaryColor,
|
||||||
groupValue: _caboPlayerIndex,
|
groupValue: _caboPlayerIndex,
|
||||||
children: Map.fromEntries(widget.gameSession.players
|
children: Map.fromEntries(widget.gameSession.players
|
||||||
@@ -130,7 +132,7 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 6,
|
horizontal: 6,
|
||||||
vertical: 6,
|
vertical: 8,
|
||||||
),
|
),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
@@ -154,27 +156,6 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
||||||
child: CupertinoListTile(
|
|
||||||
title: Text(AppLocalizations.of(context).player),
|
|
||||||
trailing: Row(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 100,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context).points))),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
SizedBox(
|
|
||||||
width: 80,
|
|
||||||
child: Center(
|
|
||||||
child: Text(AppLocalizations.of(context)
|
|
||||||
.kamikaze))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
@@ -190,7 +171,7 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: CupertinoListTile(
|
child: CupertinoListTile(
|
||||||
backgroundColor: CupertinoColors.secondaryLabel,
|
backgroundColor: CustomTheme.playerTileColor,
|
||||||
title: Row(children: [
|
title: Row(children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
@@ -204,90 +185,68 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: shouldShowMedal,
|
visible: shouldShowMedal,
|
||||||
child: const Icon(FontAwesomeIcons.medal,
|
child: const Icon(FontAwesomeIcons.crown,
|
||||||
size: 15))
|
size: 15))
|
||||||
]))
|
]))
|
||||||
]),
|
]),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'${widget.gameSession.playerScores[originalIndex]}'
|
'${widget.gameSession.playerScores[originalIndex]}'
|
||||||
' ${AppLocalizations.of(context).points}'),
|
' ${AppLocalizations.of(context).points}'),
|
||||||
trailing: Row(
|
trailing: SizedBox(
|
||||||
children: [
|
width: 100,
|
||||||
SizedBox(
|
child: CupertinoTextField(
|
||||||
width: 100,
|
maxLength: 3,
|
||||||
child: CupertinoTextField(
|
focusNode: _focusNodeList[originalIndex],
|
||||||
maxLength: 3,
|
keyboardType:
|
||||||
focusNode: _focusNodeList[originalIndex],
|
const TextInputType.numberWithOptions(
|
||||||
keyboardType:
|
signed: true,
|
||||||
const TextInputType.numberWithOptions(
|
decimal: false,
|
||||||
signed: true,
|
|
||||||
decimal: false,
|
|
||||||
),
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.digitsOnly,
|
|
||||||
],
|
|
||||||
textInputAction: index ==
|
|
||||||
widget.gameSession.players
|
|
||||||
.length -
|
|
||||||
1
|
|
||||||
? TextInputAction.done
|
|
||||||
: TextInputAction.next,
|
|
||||||
controller:
|
|
||||||
_scoreControllerList[originalIndex],
|
|
||||||
placeholder:
|
|
||||||
AppLocalizations.of(context).points,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
onSubmitted: (_) =>
|
|
||||||
_focusNextTextfield(originalIndex),
|
|
||||||
onChanged: (_) => setState(() {}),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 50),
|
inputFormatters: [
|
||||||
GestureDetector(
|
FilteringTextInputFormatter.digitsOnly,
|
||||||
onTap: () {
|
],
|
||||||
setState(() {
|
textInputAction: index ==
|
||||||
_kamikazePlayerIndex =
|
widget.gameSession.players.length - 1
|
||||||
(_kamikazePlayerIndex ==
|
? TextInputAction.done
|
||||||
originalIndex)
|
: TextInputAction.next,
|
||||||
? null
|
controller:
|
||||||
: originalIndex;
|
_scoreControllerList[originalIndex],
|
||||||
});
|
placeholder:
|
||||||
},
|
AppLocalizations.of(context).points,
|
||||||
child: Container(
|
textAlign: TextAlign.center,
|
||||||
width: 24,
|
onSubmitted: (_) =>
|
||||||
height: 24,
|
_focusNextTextfield(originalIndex),
|
||||||
decoration: BoxDecoration(
|
onChanged: (_) => setState(() {}),
|
||||||
shape: BoxShape.circle,
|
),
|
||||||
color: _kamikazePlayerIndex ==
|
|
||||||
originalIndex
|
|
||||||
? CupertinoColors.systemRed
|
|
||||||
: CupertinoColors
|
|
||||||
.tertiarySystemFill,
|
|
||||||
border: Border.all(
|
|
||||||
color: _kamikazePlayerIndex ==
|
|
||||||
originalIndex
|
|
||||||
? CupertinoColors.systemRed
|
|
||||||
: CupertinoColors.systemGrey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: _kamikazePlayerIndex ==
|
|
||||||
originalIndex
|
|
||||||
? const Icon(
|
|
||||||
CupertinoIcons.exclamationmark,
|
|
||||||
size: 16,
|
|
||||||
color: CupertinoColors.white,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 22),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||||
|
child: Center(
|
||||||
|
heightFactor: 1,
|
||||||
|
child: CupertinoButton(
|
||||||
|
sizeStyle: CupertinoButtonSize.medium,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: CustomTheme.buttonBackgroundColor,
|
||||||
|
onPressed: () async {
|
||||||
|
if (await _showKamikazeSheet(context)) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
_endOfRoundNavigation(context, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context).kamikaze,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: CupertinoColors.destructiveRed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -302,21 +261,14 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
return Container(
|
return Container(
|
||||||
height: 80,
|
height: 80,
|
||||||
padding: const EdgeInsets.only(bottom: 20),
|
padding: const EdgeInsets.only(bottom: 20),
|
||||||
color: CustomTheme.backgroundTintColor,
|
color: CustomTheme.mainElementBackgroundColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
CupertinoButton(
|
CupertinoButton(
|
||||||
onPressed: _areRoundInputsValid()
|
onPressed: _areRoundInputsValid()
|
||||||
? () async {
|
? () {
|
||||||
List<int> bonusPlayersIndices = _finishRound();
|
_endOfRoundNavigation(context, false);
|
||||||
if (bonusPlayersIndices.isNotEmpty) {
|
|
||||||
await _showBonusPopup(
|
|
||||||
context, bonusPlayersIndices);
|
|
||||||
}
|
|
||||||
LocalStorageService.saveGameSessions();
|
|
||||||
if (!context.mounted) return;
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: Text(AppLocalizations.of(context).done),
|
child: Text(AppLocalizations.of(context).done),
|
||||||
@@ -324,21 +276,8 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
if (!widget.gameSession.isGameFinished)
|
if (!widget.gameSession.isGameFinished)
|
||||||
CupertinoButton(
|
CupertinoButton(
|
||||||
onPressed: _areRoundInputsValid()
|
onPressed: _areRoundInputsValid()
|
||||||
? () async {
|
? () {
|
||||||
List<int> bonusPlayersIndices =
|
_endOfRoundNavigation(context, true);
|
||||||
_finishRound();
|
|
||||||
if (bonusPlayersIndices.isNotEmpty) {
|
|
||||||
await _showBonusPopup(
|
|
||||||
context, bonusPlayersIndices);
|
|
||||||
}
|
|
||||||
LocalStorageService.saveGameSessions();
|
|
||||||
if (widget.gameSession.isGameFinished &&
|
|
||||||
context.mounted) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
} else if (context.mounted) {
|
|
||||||
Navigator.pop(
|
|
||||||
context, widget.roundNumber + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: Text(AppLocalizations.of(context).next_round),
|
child: Text(AppLocalizations.of(context).next_round),
|
||||||
@@ -401,6 +340,37 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows a Cupertino action sheet to select the player who has Kamikaze.
|
||||||
|
/// It returns true if a player was selected, false if the action was cancelled.
|
||||||
|
Future<bool> _showKamikazeSheet(BuildContext context) async {
|
||||||
|
return await showCupertinoModalPopup<bool?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return CupertinoActionSheet(
|
||||||
|
title: Text(AppLocalizations.of(context).kamikaze),
|
||||||
|
message: Text(AppLocalizations.of(context).who_has_kamikaze),
|
||||||
|
actions: widget.gameSession.players.asMap().entries.map((entry) {
|
||||||
|
final index = entry.key;
|
||||||
|
final name = entry.value;
|
||||||
|
return CupertinoActionSheetAction(
|
||||||
|
onPressed: () {
|
||||||
|
_kamikazePlayerIndex = index;
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
},
|
||||||
|
child: Text(name),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
cancelButton: CupertinoActionSheetAction(
|
||||||
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
isDestructiveAction: true,
|
||||||
|
child: Text(AppLocalizations.of(context).cancel),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
) ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Focuses the next text field in the list of text fields.
|
/// Focuses the next text field in the list of text fields.
|
||||||
/// [index] is the index of the current text field.
|
/// [index] is the index of the current text field.
|
||||||
void _focusNextTextfield(int index) {
|
void _focusNextTextfield(int index) {
|
||||||
@@ -471,10 +441,9 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
return bonusPlayers;
|
return bonusPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup dialog with the bonus information.
|
/// Shows a popup dialog with the information which player received the bonus points.
|
||||||
Future<void> _showBonusPopup(
|
Future<void> _showBonusPopup(
|
||||||
BuildContext context, List<int> bonusPlayers) async {
|
BuildContext context, List<int> bonusPlayers) async {
|
||||||
print('Bonus Popup wird angezeigt');
|
|
||||||
int pointLimit = widget.gameSession.pointLimit;
|
int pointLimit = widget.gameSession.pointLimit;
|
||||||
int bonusPoints = (pointLimit / 2).round();
|
int bonusPoints = (pointLimit / 2).round();
|
||||||
|
|
||||||
@@ -521,6 +490,37 @@ class _RoundViewState extends State<RoundView> {
|
|||||||
return resultText;
|
return resultText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles the navigation for the end of the round.
|
||||||
|
/// It checks for bonus players and shows a popup, saves the game session,
|
||||||
|
/// and navigates to the next round or back to the previous screen.
|
||||||
|
/// It takes the BuildContext [context] and a boolean [navigateToNextRound] to determine
|
||||||
|
/// if it should navigate to the next round or not.
|
||||||
|
Future<void> _endOfRoundNavigation(
|
||||||
|
BuildContext context, bool navigateToNextRound) async {
|
||||||
|
List<int> bonusPlayersIndices = _finishRound();
|
||||||
|
if (bonusPlayersIndices.isNotEmpty) {
|
||||||
|
await _showBonusPopup(context, bonusPlayersIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalStorageService.saveGameSessions();
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
// If the game is finished, pop the context and return to the previous screen.
|
||||||
|
if (widget.gameSession.isGameFinished) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If navigateToNextRound is false, pop the context and return to the previous screen.
|
||||||
|
if (!navigateToNextRound) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If navigateToNextRound is true and the game isn't finished yet,
|
||||||
|
// pop the context and navigate to the next round.
|
||||||
|
Navigator.pop(context, widget.roundNumber + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (final controller in _scoreControllerList) {
|
for (final controller in _scoreControllerList) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class _TabViewState extends State<TabView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CupertinoTabScaffold(
|
return CupertinoTabScaffold(
|
||||||
tabBar: CupertinoTabBar(
|
tabBar: CupertinoTabBar(
|
||||||
backgroundColor: CustomTheme.backgroundTintColor,
|
backgroundColor: CustomTheme.mainElementBackgroundColor,
|
||||||
iconSize: 27,
|
iconSize: 27,
|
||||||
height: 55,
|
height: 55,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: cabo_counter
|
|||||||
description: "Mobile app for the card game Cabo"
|
description: "Mobile app for the card game Cabo"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 0.4.8+525
|
version: 0.4.9+533
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
|||||||
Reference in New Issue
Block a user