diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a3bcd8fe..62c7576e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -565,6 +565,32 @@ "missions": "MISSIONS", "unlimited": "Unlimited", "eventNoSignup": "No signup required", - "eventHasLottery": "Spots decided by lottery" - + "eventHasLottery": "Spots decided by lottery", + + "encloseDay": "Day", + "encloseBonus": "Bonus", + "encloseWalls": "Walls", + "encloseSubmit": "Submit", + "encloseScore": "Score", + "encloseOptimal": "Optimal", + "encloseYourSolution": "Your solution", + "encloseYourBest": "Your best", + "encloseIfEnclosed": "if in enclosure", + "enclosePreviousDays": "Previous days", + "encloseBonusLevels": "Bonus levels", + "encloseHowToPlay": "How to play", + "encloseInstruction": "The objective is to build an enclosure for the moose by placing walls (tapping) on the grid. The moose can not move over the water or diagonally. The more grass tiles in your enclosure, the higher your score. You only have a limited amount of walls so use them wisely", + "enclosePerfect": "PERFECT!!!", + "enclosePrettyGood": "Pretty good!", + "encloseNotBad": "Not bad!", + "encloseNotTheWorst": "I've seen worse!", + "encloseComeOn": "Come on...", + "encloseBetterThan": "You scored as good or better than", + "enclosePercentOfPlayers": "% of players", + "encloseYourScore": "Your score", + "encloseSubmitErrorTitle": "A problem occurred with your submission", + "encloseSubmitErrorText": "Please try again later\n\nThe reason was", + "encloseOptimalScore": "Optimal score", + "encloseOccurrences": "Occurrences", + "encloseOccurrence": "Occurrence" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index c346dbdc..d0c22d1d 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3187,6 +3187,162 @@ abstract class AppLocalizations { /// In sv, this message translates to: /// **'Platser lottas'** String get eventHasLottery; + + /// No description provided for @encloseDay. + /// + /// In sv, this message translates to: + /// **'Dag'** + String get encloseDay; + + /// No description provided for @encloseBonus. + /// + /// In sv, this message translates to: + /// **'Bonus'** + String get encloseBonus; + + /// No description provided for @encloseWalls. + /// + /// In sv, this message translates to: + /// **'Murar'** + String get encloseWalls; + + /// No description provided for @encloseSubmit. + /// + /// In sv, this message translates to: + /// **'Skicka in'** + String get encloseSubmit; + + /// No description provided for @encloseScore. + /// + /// In sv, this message translates to: + /// **'Poäng'** + String get encloseScore; + + /// No description provided for @encloseOptimal. + /// + /// In sv, this message translates to: + /// **'Optimal'** + String get encloseOptimal; + + /// No description provided for @encloseYourSolution. + /// + /// In sv, this message translates to: + /// **'Din lösning'** + String get encloseYourSolution; + + /// No description provided for @encloseYourBest. + /// + /// In sv, this message translates to: + /// **'Ditt bästa'** + String get encloseYourBest; + + /// No description provided for @encloseIfEnclosed. + /// + /// In sv, this message translates to: + /// **'om i hagen'** + String get encloseIfEnclosed; + + /// No description provided for @enclosePreviousDays. + /// + /// In sv, this message translates to: + /// **'Tidigare dagar'** + String get enclosePreviousDays; + + /// No description provided for @encloseBonusLevels. + /// + /// In sv, this message translates to: + /// **'Bonusnivåer'** + String get encloseBonusLevels; + + /// No description provided for @encloseHowToPlay. + /// + /// In sv, this message translates to: + /// **'Hur man spelar'** + String get encloseHowToPlay; + + /// No description provided for @encloseInstruction. + /// + /// In sv, this message translates to: + /// **'Målet är att bygga en så stor hage som möjligt åt älgen genom att placera murar (klicka) i rutnätet. Älgen kan inte röra sig över vatten eller diagonalt. Ju mer gräs i din hage, desto högre din poäng. Du har ett begränsat antal murar så använd dem vist'** + String get encloseInstruction; + + /// No description provided for @enclosePerfect. + /// + /// In sv, this message translates to: + /// **'PERFEKT!!!'** + String get enclosePerfect; + + /// No description provided for @enclosePrettyGood. + /// + /// In sv, this message translates to: + /// **'Ganska bra!'** + String get enclosePrettyGood; + + /// No description provided for @encloseNotBad. + /// + /// In sv, this message translates to: + /// **'Inte illa!'** + String get encloseNotBad; + + /// No description provided for @encloseNotTheWorst. + /// + /// In sv, this message translates to: + /// **'Jag har sett värre!'** + String get encloseNotTheWorst; + + /// No description provided for @encloseComeOn. + /// + /// In sv, this message translates to: + /// **'Kom igen...'** + String get encloseComeOn; + + /// No description provided for @encloseBetterThan. + /// + /// In sv, this message translates to: + /// **'Du fick lika bra eller bättre än'** + String get encloseBetterThan; + + /// No description provided for @enclosePercentOfPlayers. + /// + /// In sv, this message translates to: + /// **'% av spelare'** + String get enclosePercentOfPlayers; + + /// No description provided for @encloseYourScore. + /// + /// In sv, this message translates to: + /// **'Din poäng'** + String get encloseYourScore; + + /// No description provided for @encloseSubmitErrorTitle. + /// + /// In sv, this message translates to: + /// **'Ett problem uppstod med att skicka in ditt svar'** + String get encloseSubmitErrorTitle; + + /// No description provided for @encloseSubmitErrorText. + /// + /// In sv, this message translates to: + /// **'Försök igen senare\n\nAnledningen var'** + String get encloseSubmitErrorText; + + /// No description provided for @encloseOptimalScore. + /// + /// In sv, this message translates to: + /// **'Optimal poäng'** + String get encloseOptimalScore; + + /// No description provided for @encloseOccurrences. + /// + /// In sv, this message translates to: + /// **'Förekomster'** + String get encloseOccurrences; + + /// No description provided for @encloseOccurrence. + /// + /// In sv, this message translates to: + /// **'Förekomst'** + String get encloseOccurrence; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 54b14ccd..0c2126be 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1693,4 +1693,85 @@ class AppLocalizationsEn extends AppLocalizations { @override String get eventHasLottery => 'Spots decided by lottery'; + + @override + String get encloseDay => 'Day'; + + @override + String get encloseBonus => 'Bonus'; + + @override + String get encloseWalls => 'Walls'; + + @override + String get encloseSubmit => 'Submit'; + + @override + String get encloseScore => 'Score'; + + @override + String get encloseOptimal => 'Optimal'; + + @override + String get encloseYourSolution => 'Your solution'; + + @override + String get encloseYourBest => 'Your best'; + + @override + String get encloseIfEnclosed => 'if in enclosure'; + + @override + String get enclosePreviousDays => 'Previous days'; + + @override + String get encloseBonusLevels => 'Bonus levels'; + + @override + String get encloseHowToPlay => 'How to play'; + + @override + String get encloseInstruction => + 'The objective is to build an enclosure for the moose by placing walls (tapping) on the grid. The moose can not move over the water or diagonally. The more grass tiles in your enclosure, the higher your score. You only have a limited amount of walls so use them wisely'; + + @override + String get enclosePerfect => 'PERFECT!!!'; + + @override + String get enclosePrettyGood => 'Pretty good!'; + + @override + String get encloseNotBad => 'Not bad!'; + + @override + String get encloseNotTheWorst => 'I\'ve seen worse!'; + + @override + String get encloseComeOn => 'Come on...'; + + @override + String get encloseBetterThan => 'You scored as good or better than'; + + @override + String get enclosePercentOfPlayers => '% of players'; + + @override + String get encloseYourScore => 'Your score'; + + @override + String get encloseSubmitErrorTitle => + 'A problem occurred with your submission'; + + @override + String get encloseSubmitErrorText => + 'Please try again later\n\nThe reason was'; + + @override + String get encloseOptimalScore => 'Optimal score'; + + @override + String get encloseOccurrences => 'Occurrences'; + + @override + String get encloseOccurrence => 'Occurrence'; } diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 41fc64a0..1716d76e 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1683,4 +1683,84 @@ class AppLocalizationsSv extends AppLocalizations { @override String get eventHasLottery => 'Platser lottas'; + + @override + String get encloseDay => 'Dag'; + + @override + String get encloseBonus => 'Bonus'; + + @override + String get encloseWalls => 'Murar'; + + @override + String get encloseSubmit => 'Skicka in'; + + @override + String get encloseScore => 'Poäng'; + + @override + String get encloseOptimal => 'Optimal'; + + @override + String get encloseYourSolution => 'Din lösning'; + + @override + String get encloseYourBest => 'Ditt bästa'; + + @override + String get encloseIfEnclosed => 'om i hagen'; + + @override + String get enclosePreviousDays => 'Tidigare dagar'; + + @override + String get encloseBonusLevels => 'Bonusnivåer'; + + @override + String get encloseHowToPlay => 'Hur man spelar'; + + @override + String get encloseInstruction => + 'Målet är att bygga en så stor hage som möjligt åt älgen genom att placera murar (klicka) i rutnätet. Älgen kan inte röra sig över vatten eller diagonalt. Ju mer gräs i din hage, desto högre din poäng. Du har ett begränsat antal murar så använd dem vist'; + + @override + String get enclosePerfect => 'PERFEKT!!!'; + + @override + String get enclosePrettyGood => 'Ganska bra!'; + + @override + String get encloseNotBad => 'Inte illa!'; + + @override + String get encloseNotTheWorst => 'Jag har sett värre!'; + + @override + String get encloseComeOn => 'Kom igen...'; + + @override + String get encloseBetterThan => 'Du fick lika bra eller bättre än'; + + @override + String get enclosePercentOfPlayers => '% av spelare'; + + @override + String get encloseYourScore => 'Din poäng'; + + @override + String get encloseSubmitErrorTitle => + 'Ett problem uppstod med att skicka in ditt svar'; + + @override + String get encloseSubmitErrorText => 'Försök igen senare\n\nAnledningen var'; + + @override + String get encloseOptimalScore => 'Optimal poäng'; + + @override + String get encloseOccurrences => 'Förekomster'; + + @override + String get encloseOccurrence => 'Förekomst'; } diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index df351f45..b29d0bba 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -567,6 +567,32 @@ "missions": "UPPDRAG", "unlimited": "Oändligt", "eventNoSignup": "Ingen anmälan behövs", - "eventHasLottery": "Platser lottas" - + "eventHasLottery": "Platser lottas", + + "encloseDay": "Dag", + "encloseBonus": "Bonus", + "encloseWalls": "Murar", + "encloseSubmit": "Skicka in", + "encloseScore": "Poäng", + "encloseOptimal": "Optimal", + "encloseYourSolution": "Din lösning", + "encloseYourBest": "Ditt bästa", + "encloseIfEnclosed": "om i hagen", + "enclosePreviousDays": "Tidigare dagar", + "encloseBonusLevels": "Bonusnivåer", + "encloseHowToPlay": "Hur man spelar", + "encloseInstruction": "Målet är att bygga en så stor hage som möjligt åt älgen genom att placera murar (klicka) i rutnätet. Älgen kan inte röra sig över vatten eller diagonalt. Ju mer gräs i din hage, desto högre din poäng. Du har ett begränsat antal murar så använd dem vist", + "enclosePerfect": "PERFEKT!!!", + "enclosePrettyGood": "Ganska bra!", + "encloseNotBad": "Inte illa!", + "encloseNotTheWorst": "Jag har sett värre!", + "encloseComeOn": "Kom igen...", + "encloseBetterThan": "Du fick lika bra eller bättre än", + "enclosePercentOfPlayers": "% av spelare", + "encloseYourScore": "Din poäng", + "encloseSubmitErrorTitle": "Ett problem uppstod med att skicka in ditt svar", + "encloseSubmitErrorText": "Försök igen senare\n\nAnledningen var", + "encloseOptimalScore": "Optimal poäng", + "encloseOccurrences": "Förekomster", + "encloseOccurrence": "Förekomst" } diff --git a/lib/screens/games/enclose_moose/asset_handler.dart b/lib/screens/games/enclose_moose/asset_handler.dart index c0fe00c1..bc68494e 100644 --- a/lib/screens/games/enclose_moose/asset_handler.dart +++ b/lib/screens/games/enclose_moose/asset_handler.dart @@ -235,7 +235,7 @@ class AssetHandler { ("I'll sneak away like sooo... Moohoho", "${baseAudioPath}sneak_away_like_so.mp3"), ("I'll sneak away like sooo... Moohoho", "${baseAudioPath}sneak_away_like_so.mp3"), ("I will first go THIS way and then I will go THAT way and then I'm free!", "${baseAudioPath}this_way_that_way_2.mp3"), - ("I will go THIS way and then I will go THAT way and then I'm gonna be free!", "${baseAudioPath}this_way_that_way.mp3"), + ("I will go THIS way and then I will go THAT way and then I will be free!", "${baseAudioPath}this_way_that_way.mp3"), ("You can't enclose me, I'm a moose!", "${baseAudioPath}you_cant_enclose_me.mp3") ]; const enclosedVoicelines = [ diff --git a/lib/screens/games/enclose_moose/enclose_moose_level_page.dart b/lib/screens/games/enclose_moose/enclose_moose_level_page.dart index e1a68159..b908c7e3 100644 --- a/lib/screens/games/enclose_moose/enclose_moose_level_page.dart +++ b/lib/screens/games/enclose_moose/enclose_moose_level_page.dart @@ -107,42 +107,29 @@ class _EncloseMooseLevelState extends State with TickerPr crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - WigglingWidget( - controller: _idleController, - child: IconButton( - onPressed: () { - showDialog(context: context, builder: _buildDayChooserDialog()); - }, - icon: const Icon(Icons.calendar_month_rounded), - color: Colors.white - ) - ), - - Visibility( - visible: _hasSubmitted, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: WigglingWidget( - controller: _idleController, - child: IconButton( - onPressed: () { - showDialog(context: context, builder: _buildSubmitDialog()); - }, - icon: const Icon(Icons.leaderboard_outlined), - color: Colors.white + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: WigglingWidget( + controller: _idleController, + child: IconButton( + onPressed: () { + showDialog(context: context, builder: _buildDayChooserDialog()); + }, + icon: const Icon(Icons.calendar_month_rounded), + color: Colors.white + ) ) ) ), - - const Spacer(), - - Row( - children: [ - Visibility( - visible: widget.availableLevels.first != _usedLevel, + + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Visibility( + visible: _hasSubmitted, maintainState: true, maintainAnimation: true, maintainSize: true, @@ -150,66 +137,105 @@ class _EncloseMooseLevelState extends State with TickerPr controller: _idleController, child: IconButton( onPressed: () { - widget.pageController.jumpToPage(widget.pageController.page!.toInt() - 1); + showDialog(context: context, builder: _buildSubmitDialog()); }, - icon: const Icon(Icons.chevron_left), + icon: const Icon(Icons.leaderboard_outlined), color: Colors.white ) ) - ), + ) + ) + ), - WigglingWidget( - controller: _idleController, - child: OutlinedText( - text: _usedLevel.dayIndex != null ? "Day ${_usedLevel.dayIndex}" : "Bonus", - style: const TextStyle( - color: Colors.white, - fontSize: 25, - fontFamily: "Schoolbell" - ) - ) - ), + // const Spacer(), - Visibility( - visible: widget.availableLevels.last != _usedLevel, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: WigglingWidget( - controller: _idleController, - child: IconButton( - onPressed: () { - widget.pageController.jumpToPage(widget.pageController.page!.toInt() + 1); - }, - icon: const Icon(Icons.chevron_right), - color: Colors.white - ) - ) - ), - ] + Flexible( + flex: 4, + child: FittedBox( + fit: BoxFit.scaleDown, + child: Row( + children: [ + Visibility( + visible: widget.availableLevels.first != _usedLevel, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + child: WigglingWidget( + controller: _idleController, + child: IconButton( + onPressed: () { + widget.pageController.jumpToPage(widget.pageController.page!.toInt() - 1); + }, + icon: const Icon(Icons.chevron_left), + color: Colors.white + ) + ) + ), + + WigglingWidget( + controller: _idleController, + child: OutlinedText( + text: _usedLevel.dayIndex != null ? "${t.encloseDay} ${_usedLevel.dayIndex}" : t.encloseBonus, + style: const TextStyle( + color: Colors.white, + fontSize: 25, + fontFamily: "Schoolbell" + ) + ) + ), + + Visibility( + visible: widget.availableLevels.last != _usedLevel, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + child: WigglingWidget( + controller: _idleController, + child: IconButton( + onPressed: () { + widget.pageController.jumpToPage(widget.pageController.page!.toInt() + 1); + }, + icon: const Icon(Icons.chevron_right), + color: Colors.white + ) + ) + ), + ] + ) + ) ), - Spacer(), + // const Spacer(), - WigglingWidget( - controller: _idleController, - child: IconButton( - onPressed: () { - _grid.reset(); - }, - icon: const Icon(Icons.restart_alt_outlined), - color: Colors.white + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: WigglingWidget( + controller: _idleController, + child: IconButton( + onPressed: () { + _grid.reset(); + }, + icon: const Icon(Icons.restart_alt_outlined), + color: Colors.white + ) + ) ) ), - WigglingWidget( - controller: _idleController, - child: IconButton( - onPressed: () { - showDialog(context: context, builder: _buildHelpDialog()); - }, - icon: const Icon(Icons.question_mark_outlined), - color: Colors.white + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: WigglingWidget( + controller: _idleController, + child: IconButton( + onPressed: () { + showDialog(context: context, builder: _buildHelpDialog()); + }, + icon: const Icon(Icons.question_mark_outlined), + color: Colors.white + ) + ) ) ), ] @@ -240,23 +266,25 @@ class _EncloseMooseLevelState extends State with TickerPr ) ), - title: WigglingWidget( - controller: _idleController, - child: Text.rich( - TextSpan( - children: "enclose.moose".characters.map((char) => WidgetSpan( // Could consider making all text like this - child: WigglingWidget( - controller: _idleController, - child: OutlinedText( - text: char, - style: const TextStyle( - color: Colors.white, - fontSize: 45, - fontFamily: "Schoolbell" + title: FittedBox( + child: WigglingWidget( + controller: _idleController, + child: Text.rich( + TextSpan( + children: "enclose.moose".characters.map((char) => WidgetSpan( // Could consider making all text like this + child: WigglingWidget( + controller: _idleController, + child: OutlinedText( + text: char, + style: const TextStyle( + color: Colors.white, + fontSize: 45, + fontFamily: "Schoolbell" + ) ) ) - ) - )).toList() + )).toList() + ) ) ) ) @@ -322,9 +350,9 @@ class _EncloseMooseLevelState extends State with TickerPr return Container( decoration: BoxDecoration( - border: Border.all( - color: Colors.green[900]!, - width: cellSize / 100 + border: tile.isWater ? null : Border.all( + color: const Color.fromARGB(255, 36, 124, 42), + width: 0, // thinnest possible // cellSize / 100 // Would prefer this to depend on cellSize but looks weird for a nonzero value next to water ) ), child: OverflowBox( @@ -444,7 +472,7 @@ class _EncloseMooseLevelState extends State with TickerPr cellSize: cellSize, extraLength: cellSize, extraDirection: _grid.escapeDirection, - lineColor: Colors.white.withAlpha(200), + lineColor: Colors.white.withAlpha(220), idleController: _idleController, frequency: 2 ) @@ -458,7 +486,7 @@ class _EncloseMooseLevelState extends State with TickerPr child: AnimatedThoughtBubble( isVisible: (_openCallCounter.getCount("thinking") ?? 0) != 0, text: _mooseThought, - color: Colors.white.withAlpha(200), + color: Colors.white.withAlpha(220), idleController: _idleController ) ) @@ -496,61 +524,72 @@ class _EncloseMooseLevelState extends State with TickerPr child: Column( children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - WigglingWidget( - controller: _idleController, - child: HighlightedText( - showHighlight: (_openCallCounter.getCount("walls") ?? 0) != 0, - child: OutlinedText( - text: "Walls: ${_grid.wallsLeft}/${_grid.wallBudget}", - style: const TextStyle( - fontSize: 25, - fontFamily: "Schoolbell" + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: WigglingWidget( + controller: _idleController, + child: HighlightedText( + showHighlight: (_openCallCounter.getCount("walls") ?? 0) != 0, + child: OutlinedText( + text: "${t.encloseWalls}: ${_grid.wallsLeft}/${_grid.wallBudget}", + style: const TextStyle( + fontSize: 25, + fontFamily: "Schoolbell" + ) + ) ) ) ) ), - Spacer(), - - Visibility( - visible: !_hasSubmitted && _grid.escapePath == null, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: WigglingWidget( - child: TextButton( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), - ) - ), - onPressed: _submitSolution, - child: const OutlinedText( - text: "Submit", - style: TextStyle( - color: Colors.white, - fontSize: 25, - fontFamily: "Schoolbell" - ) + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Visibility( + visible: !_hasSubmitted && _grid.escapePath == null, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + child: WigglingWidget( + child: TextButton( + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ) + ), + onPressed: _submitSolution, + child: OutlinedText( + text: t.encloseSubmit, + style: const TextStyle( + color: Colors.white, + fontSize: 25, + fontFamily: "Schoolbell" + ) + ) + ), + controller: _idleController ) - ), - controller: _idleController + ) ) ), - Spacer(), - - WigglingWidget( - controller: _idleController, - child: HighlightedText( - showHighlight: (_openCallCounter.getCount("score") ?? 0) != 0, - child: OutlinedText( - text: "Score: ${_grid.score ?? "N/A"}", - style: const TextStyle( - fontSize: 25, - fontFamily: "Schoolbell" + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: WigglingWidget( + controller: _idleController, + child: HighlightedText( + showHighlight: (_openCallCounter.getCount("score") ?? 0) != 0, + child: OutlinedText( + text: "${t.encloseScore}: ${_grid.score ?? "N/A"}", + style: const TextStyle( + fontSize: 25, + fontFamily: "Schoolbell" + ) + ) ) ) ) @@ -561,58 +600,68 @@ class _EncloseMooseLevelState extends State with TickerPr Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Visibility( - visible: _hasSubmitted && _grid.score != _usedLevel.optimalScore, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: WigglingWidget( - controller: _idleController, - child: OutlinedButton.icon( - onPressed: () { - _changeToSolution(_usedLevel.optimalSolution!); - }, - style: OutlinedButton.styleFrom( - backgroundColor: Color(0xFF15542D).withAlpha(200), // Colors.black.withAlpha(55), - visualDensity: VisualDensity.compact - ), - label: OutlinedText( - text: "Optimal: ${_usedLevel.optimalScore}", - style: const TextStyle( - // color: Colors.white, - fontSize: 20, - fontFamily: "Schoolbell" + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Visibility( + visible: _hasSubmitted && _grid.score != _usedLevel.optimalScore, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + child: WigglingWidget( + controller: _idleController, + child: OutlinedButton.icon( + onPressed: () { + _changeToSolution(_usedLevel.optimalSolution!); + }, + style: OutlinedButton.styleFrom( + backgroundColor: Color(0xFF15542D).withAlpha(200), // Colors.black.withAlpha(55), + visualDensity: VisualDensity.compact + ), + label: OutlinedText( + text: "${t.encloseOptimal}: ${_usedLevel.optimalScore}", + style: const TextStyle( + // color: Colors.white, + fontSize: 20, + fontFamily: "Schoolbell" + ) + ), + icon: const Icon(Icons.star_sharp) ) - ), - icon: const Icon(Icons.star_sharp) + ) ) ) ), - Visibility( - visible: isNotShowingYours || !isCurrentBest, - maintainState: true, - maintainAnimation: true, - maintainSize: true, - child: WigglingWidget( - controller: _idleController, - child: OutlinedButton.icon( - onPressed: () { - _changeToSolution(_hasSubmitted ? _usedLevel.playerSubmission!.playerSolution : _bestSolution!); - }, - style: OutlinedButton.styleFrom( - backgroundColor: Color(0xFF15542D).withAlpha(200), // Colors.black.withAlpha(55), - visualDensity: VisualDensity.compact - ), - label: OutlinedText( - text: _hasSubmitted ? "Your solution: ${_usedLevel.playerSubmission!.playerScore}" : "Your best: $_bestSolutionScore", - style: const TextStyle( - // color: Colors.white, - fontSize: 20, - fontFamily: "Schoolbell" + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Visibility( + visible: isNotShowingYours || !isCurrentBest, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + child: WigglingWidget( + controller: _idleController, + child: OutlinedButton.icon( + onPressed: () { + _changeToSolution(_hasSubmitted ? _usedLevel.playerSubmission!.playerSolution : _bestSolution!); + }, + style: OutlinedButton.styleFrom( + backgroundColor: Color(0xFF15542D).withAlpha(200), // Colors.black.withAlpha(55), + visualDensity: VisualDensity.compact + ), + label: OutlinedText( + text: _hasSubmitted ? "${t.encloseYourSolution}: ${_usedLevel.playerSubmission!.playerScore}" : "${t.encloseYourBest}: $_bestSolutionScore", + style: const TextStyle( + // color: Colors.white, + fontSize: 20, + fontFamily: "Schoolbell" + ) + ), + icon: const Icon(Icons.keyboard_return_outlined) ) - ), - icon: const Icon(Icons.keyboard_return_outlined) + ) ) ) ), @@ -677,21 +726,27 @@ class _EncloseMooseLevelState extends State with TickerPr void _tapTile(EncloseGridTile tile) async { /* // Useful for debug! - final builder = EncloseMooseLevelReadBuilder(); - builder.levelId = 10; - builder.nameSv = "debug"; - builder.nameEn = "debug"; - builder.encodedGrid = ""; - builder.wallBudget = 10; + final builder = _usedLevel.toBuilder(); + builder.optimalScore = 45; + builder.optimalSolution = SetBuilder(); + builder.optimalIsUnique = true; + builder.scoreDistribution = MapBuilder({ + "1": 3, + "2": 1, + "17": 2, + "22": 1, + "41": 3, + "45": 4 + }); builder.playerSubmission = EncloseMooseSubmissionReadBuilder(); - builder.playerSubmission.levelId = 10; + builder.playerSubmission.levelId = _usedLevel.levelId; builder.playerSubmission.submissionTime = DateTime.now(); builder.playerSubmission.playerId = 0; - builder.playerSubmission.playerScore = 10; - builder.releaseDate = Date.now(); - builder.optimalScore = 54; + builder.playerSubmission.playerScore = 45; _usedLevel = builder.build(); + + showDialog(context: context, builder: _buildSubmitDialog()); */ if (_grid.wallsLeft == 0 && tile.isGrass) { @@ -720,8 +775,7 @@ class _EncloseMooseLevelState extends State with TickerPr if (tile.isMoose) { final mooseVoiceline = AssetHandler.getMooseVoiceline(_grid.escapePath == null); _mooseThought = mooseVoiceline.$1; - _audioPlayer.release(); // Doesn't otherwise happen if the same voiceline is chosen - _audioPlayer.play(AssetSource(mooseVoiceline.$2)); + _playVoicelineAudio(AssetSource(mooseVoiceline.$2)); final voicelineDuration = await _audioPlayer.onDurationChanged.first; if (_grid.escapePath != null) { @@ -734,11 +788,17 @@ class _EncloseMooseLevelState extends State with TickerPr if (tile.isBonus) { final bonusScore = tile.bonusScore; if (bonusScore != 0) { - _tooltipCallCounter.increment(tile, (bonusScore > 0 ? "+" : "") + "$bonusScore if enclosed"); + final t = AppLocalizations.of(context)!; + _tooltipCallCounter.increment(tile, (bonusScore > 0 ? "+" : "") + "$bonusScore ${t.encloseIfEnclosed}"); } } } + Future _playVoicelineAudio(AssetSource voiceline) async { // for some reason has to be a seperate function to avoid a bug where the audio played but the though bubble didn't show + await _audioPlayer.stop(); // Doesn't otherwise happen if the same voiceline is chosen + await _audioPlayer.play(voiceline); + } + Future _submitSolution() async { if (_bestSolution != null) { _changeToSolution(_bestSolution!); @@ -777,6 +837,8 @@ class _EncloseMooseLevelState extends State with TickerPr } void _changeToSolution(Iterable solution) { + final oldScore = _grid.score; + _grid.reset(doUpdate: false); for (final flatIndex in solution) { @@ -785,6 +847,7 @@ class _EncloseMooseLevelState extends State with TickerPr } _grid.updateEnclosure(); + updateScore(oldScore); } Widget Function(BuildContext) _buildDayChooserDialog() { @@ -814,10 +877,10 @@ class _EncloseMooseLevelState extends State with TickerPr Column( children: [ if (_dailies.isNotEmpty) - const Text( - "Previous days", + Text( + t.enclosePreviousDays, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( fontSize: 25, fontFamily: "Schoolbell" ) @@ -834,12 +897,10 @@ class _EncloseMooseLevelState extends State with TickerPr title: Text.rich( TextSpan( children: [ - TextSpan( - text: "Day ${level.dayIndex}" - ), + TextSpan(text: "${t.encloseDay} ${level.dayIndex}"), TextSpan( - text: " (${Time.format(level.releaseDate.toDateTime(), "%d %M %Y", locale: "en")})", + text: " (${Time.format(level.releaseDate.toDateTime(), "%d %M %Y", locale: t.localeName)})", style: TextStyle( color: Theme.of(context).primaryColor ) @@ -865,10 +926,10 @@ class _EncloseMooseLevelState extends State with TickerPr Column( children: [ if (_nonDailies.isNotEmpty) - const Text( - "Bonus levels", + Text( + t.encloseBonusLevels, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( fontSize: 25, fontFamily: "Schoolbell" ) @@ -906,20 +967,22 @@ class _EncloseMooseLevelState extends State with TickerPr } Widget Function(BuildContext) _buildHelpDialog() { - return (context) => const SimpleDialog( + final t = AppLocalizations.of(context)!; + + return (context) => SimpleDialog( contentPadding: const EdgeInsets.all(16), title: Text( - "How to play", + t.encloseHowToPlay, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( fontSize: 25, fontFamily: "Schoolbell" ) ), children: [ Text( - "The objective is to build an enclosure for the moose by placing walls (tapping) on the grid. The moose can not move over the water. The more grass tiles in your enclosure, the higher your score. You only have a limited amount of walls so use them wisely.", - style: TextStyle( + t.encloseInstruction, + style: const TextStyle( fontSize: 18, fontFamily: "Schoolbell" ) @@ -929,6 +992,8 @@ class _EncloseMooseLevelState extends State with TickerPr } Widget Function(BuildContext) _buildSubmitDialog() { + final t = AppLocalizations.of(context)!; + final Map scoreDistribution = {}; for (final entry in _usedLevel.scoreDistribution!.entries) { scoreDistribution[int.parse(entry.key)] = entry.value; @@ -954,15 +1019,15 @@ class _EncloseMooseLevelState extends State with TickerPr String grade; if (_usedLevel.playerSubmission!.playerScore == _usedLevel.optimalScore) { - grade = "PERFECT!!! 💎"; + grade = "${t.enclosePerfect} 💎"; } else if (percentile >= 0.75) { - grade = "Pretty good! ✅"; + grade = "${t.enclosePrettyGood} ✅"; } else if (percentile >= 0.5) { - grade = "Not bad! ✅"; + grade = "${t.encloseNotBad} ✅"; } else if (percentile >= 0.25) { - grade = "Not the worst! ✅"; + grade = "${t.encloseNotTheWorst} ✅"; } else { - grade = "Come on... ✅"; + grade = "${t.encloseComeOn} ✅"; } return (context) => SimpleDialog( @@ -987,7 +1052,7 @@ class _EncloseMooseLevelState extends State with TickerPr TextSpan( children: [ TextSpan( - text: "You scored as good or better than ", + text: "${t.encloseBetterThan} ", style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" @@ -1004,7 +1069,7 @@ class _EncloseMooseLevelState extends State with TickerPr ), TextSpan( - text: "% of players", + text: t.enclosePercentOfPlayers, style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" @@ -1019,7 +1084,7 @@ class _EncloseMooseLevelState extends State with TickerPr TextSpan( children: [ TextSpan( - text: "Your score: ", + text: "${t.encloseYourScore}: ", style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" @@ -1043,7 +1108,7 @@ class _EncloseMooseLevelState extends State with TickerPr TextSpan( children: [ TextSpan( - text: "Optimal: ", + text: "${t.encloseOptimalScore}: ", style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" @@ -1080,19 +1145,21 @@ class _EncloseMooseLevelState extends State with TickerPr } Widget Function(BuildContext) _buildSubmitErrorDialog(String errorMessage) { + final t = AppLocalizations.of(context)!; + return (context) => SimpleDialog( contentPadding: const EdgeInsets.all(16), - title: const Text( - "There was an error with your submission", + title: Text( + t.encloseSubmitErrorTitle, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( fontSize: 25, fontFamily: "Schoolbell" ) ), children: [ Text( - "Please try again later.\n\nResponse was: \"$errorMessage\"", + "${t.encloseSubmitErrorText} \"$errorMessage\"", style: const TextStyle( fontSize: 18, fontFamily: "Schoolbell" diff --git a/lib/screens/games/enclose_moose/helper_widgets/outlined_text.dart b/lib/screens/games/enclose_moose/helper_widgets/outlined_text.dart index bd33cc0e..54b1baf7 100644 --- a/lib/screens/games/enclose_moose/helper_widgets/outlined_text.dart +++ b/lib/screens/games/enclose_moose/helper_widgets/outlined_text.dart @@ -5,7 +5,7 @@ class OutlinedText extends StatefulWidget { super.key, required this.text, this.style, - this.strokeWidth = 1.5, + this.strokeWidth = 1.5, // could make this default depend on font size this.strokeColor = Colors.black, this.textAlign }); diff --git a/lib/screens/games/enclose_moose/helper_widgets/submissions_bar_chart.dart b/lib/screens/games/enclose_moose/helper_widgets/submissions_bar_chart.dart index 7c4df20b..94b89072 100644 --- a/lib/screens/games/enclose_moose/helper_widgets/submissions_bar_chart.dart +++ b/lib/screens/games/enclose_moose/helper_widgets/submissions_bar_chart.dart @@ -1,6 +1,7 @@ import "dart:math" as math; import "package:flutter/material.dart"; import "package:fl_chart/fl_chart.dart"; +import "package:fsek_mobile/l10n/app_localizations.dart"; class SubmissionsBarChart extends StatefulWidget { const SubmissionsBarChart({ @@ -52,6 +53,8 @@ class _SubmissionsBarChartState extends State with SingleTi @override Widget build(BuildContext context) { + final t = AppLocalizations.of(context)!; + final barColorDict = { widget.playerScore: Theme.of(context).primaryColor, // A bit weird on themeN where primaryColor is green widget.optimalScore: Colors.green[800] @@ -68,10 +71,15 @@ class _SubmissionsBarChartState extends State with SingleTi ]), builder: (context, child) { - final scale = _chartTransformationController.value.getMaxScaleOnAxis(); + final transformationMatrix = _chartTransformationController.value; + final scale = transformationMatrix.getMaxScaleOnAxis(); + final translation = transformationMatrix.getTranslation().x.abs(); + final barWidth = scale * (constraints.maxWidth * 0.7 - 75) / amountBars; final currentAlpha = (255 * _tooltipAnimationController.value).toInt(); + final amountShownBars = amountBars / scale; + return BarChart( BarChartData( borderData: FlBorderData(show: false), @@ -88,7 +96,7 @@ class _SubmissionsBarChartState extends State with SingleTi getTooltipColor: (group) => (barColorDict[group.x] ?? defaultBarColor).withAlpha(currentAlpha), getTooltipItem: (group, groupIndex, rod, rodIndex) { return BarTooltipItem( - "Score: ${entries[groupIndex].key}\nOccurrences: ${rod.toY.toInt()}", + "${t.encloseScore}: ${entries[groupIndex].key}\n${t.encloseOccurrences}: ${rod.toY.toInt()}", TextStyle( color: Colors.white.withAlpha(currentAlpha), fontSize: 18, @@ -117,22 +125,23 @@ class _SubmissionsBarChartState extends State with SingleTi ), bottomTitles: AxisTitles( axisNameSize: 30, - axisNameWidget: const Text( - "Score", - style: TextStyle( + axisNameWidget: Text( + t.encloseScore, + style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" ) ), sideTitles: SideTitles( showTitles: true, - interval: scale == 1 ? 1 : amountBars / scale, + // interval: scale == 1 ? 1 : amountBars / scale, + interval: 1, reservedSize: 28, - getTitlesWidget: (value, meta) { - if (scale == 1 && value != minScore && value != widget.optimalScore) return const SizedBox.shrink(); // Because the axis is categorical we can't use interval and instead have to use this - - final usedInterval = amountBars ~/ scale; // Evenly space labels so that at least one is always showing - if ((value.toInt() - minScore) % usedInterval != 0 && value != minScore && value != widget.optimalScore) return const SizedBox.shrink(); + getTitlesWidget: (value, meta) { // Why is meta.min == 0 and meta.max == 1 always? They have the tools to calculate the max and min (they do it because they only call getTitlesWidget for bars that are in frame...) + final barsTranslated = (amountBars / (meta.parentAxisSize / translation)); // Don't really understand why meta.parentAxisSize != constraints.maxWidth cause then this would just be translation / barWidth + final minShown = (minScore + barsTranslated).roundToDouble(); // This should maybe be a ceil because sometimes this one is out of frame but it's so ugly! + final maxShown = (minScore + barsTranslated + amountShownBars - 1).roundToDouble(); // Same thing here but with floor. + if (value != minShown && value != maxShown) return const SizedBox.shrink(); return SideTitleWidget( space: 5, @@ -150,9 +159,9 @@ class _SubmissionsBarChartState extends State with SingleTi ), leftTitles: AxisTitles( axisNameSize: 30, - axisNameWidget: const Text( - "Occurrence", - style: TextStyle( + axisNameWidget: Text( + t.encloseOccurrence, + style: const TextStyle( fontSize: 20, fontFamily: "Schoolbell" ) diff --git a/lib/screens/games/enclose_moose/tile_widget.dart b/lib/screens/games/enclose_moose/tile_widget.dart index 572e0bbb..d75ab585 100644 --- a/lib/screens/games/enclose_moose/tile_widget.dart +++ b/lib/screens/games/enclose_moose/tile_widget.dart @@ -101,7 +101,8 @@ class TileWidget extends StatelessWidget { vsync: vsync, shouldAnimate: tile.type == EncloseGridCellType.wall, animationFrames: AssetHandler.getAnimationFrames(EncloseGridCellType.wall), - animationReverseFrameDuration: const Duration(milliseconds: 20), + animationFrameDuration: _wheatFrameDuration, + animationReverseFrameDuration: _wheatFrameDuration * 0.5, endIdleFrames: AssetHandler.getIdleFrames(EncloseGridCellType.wall), idleController: idleController ), diff --git a/lib/widgets/bottom_app_bar.dart b/lib/widgets/bottom_app_bar.dart index ac0a0599..308497e1 100644 --- a/lib/widgets/bottom_app_bar.dart +++ b/lib/widgets/bottom_app_bar.dart @@ -182,10 +182,13 @@ class FsekAppBarState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(item.iconData, color: color, size: widget.iconSize), - Text( - item.text!, - style: TextStyle(color: color), - softWrap: false, + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + item.text!, + style: TextStyle(color: color), + softWrap: false, + ) ) ], ),