diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json index d2e1da0b..8f20c7ea 100644 --- a/.fvm/fvm_config.json +++ b/.fvm/fvm_config.json @@ -1,3 +1,3 @@ { - "flutterSdkVersion": "master" + "flutterSdkVersion": "3.22.0" } \ No newline at end of file diff --git a/.fvmrc b/.fvmrc index 1c299f8d..7c3d0b51 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "master" + "flutter": "3.22.0" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 14896bae..f019bd71 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,16 +4,16 @@ "[dart]": { "editor.formatOnSave": true, "editor.formatOnType": true, - "editor.rulers": [ - 80 - ] + "editor.rulers": [80] }, - "dart.flutterSdkPath": ".fvm/versions/master", + "dart.flutterSdkPath": ".fvm/versions/3.22.0", "search.exclude": { "**/.fvm": true }, "files.watcherExclude": { "**/.fvm": true }, - "dart.runPubGetOnPubspecChanges": "never" -} \ No newline at end of file + "dart.runPubGetOnPubspecChanges": "never", + "IDX.aI.enableInlineCompletion": true, + "IDX.aI.enableCodebaseIndexing": true +} diff --git a/lib/pack_core/global_states/global_game/global_game_bloc.dart b/lib/pack_core/global_states/global_game/global_game_bloc.dart index 321cf853..38079713 100644 --- a/lib/pack_core/global_states/global_game/global_game_bloc.dart +++ b/lib/pack_core/global_states/global_game/global_game_bloc.dart @@ -207,6 +207,17 @@ class GlobalGameBloc extends Cubit { level = level.copyWith(canvasDataId: newCanvasData.id); } if (event.isNewStart) { + if (level.featuresSettings.isTechnologiesEnabled) { + level = level.copyWith.characters.playerCharacter( + balloonParams: BalloonLiftParamsModel.initial, + ); + } else { + level = level.copyWith.characters.playerCharacter( + balloonParams: BalloonLiftParamsModel.initial.copyWith( + powerUsage: dto.mechanics.engine.maxPowerUsage, + ), + ); + } level = level.copyWith.characters.playerCharacter( balloonPowers: BalloonLiftPowersModel.initial, ); diff --git a/lib/pack_core/global_states/global_states_initializer.dart b/lib/pack_core/global_states/global_states_initializer.dart index 07c95d5e..b9739025 100644 --- a/lib/pack_core/global_states/global_states_initializer.dart +++ b/lib/pack_core/global_states/global_states_initializer.dart @@ -28,7 +28,12 @@ class GlobalStatesInitializer implements StateInitializer { final analyticsService = read(); final canvasCubit = read(); final appSettingsNotifier = read(); - await appSettingsNotifier.onLoad(); + final onlineStatusService = read(); + await Future.wait([ + onlineStatusService.onLoad(), + appSettingsNotifier.onLoad(), + ]); + final wordsType = await services.userWordsRepository.loadUserWords(); await dictionariesBloc.onLoad(wordsType: wordsType); await canvasCubit.loadInitialData(); diff --git a/lib/pack_core/global_states/level/level_bloc.dart b/lib/pack_core/global_states/level/level_bloc.dart index 9a1d18ff..16b9f3f2 100644 --- a/lib/pack_core/global_states/level/level_bloc.dart +++ b/lib/pack_core/global_states/level/level_bloc.dart @@ -57,6 +57,7 @@ class LevelBloc extends Cubit { ); } + Languages get wordsLanguage => state.wordsLanguage; void onChangeWordsLanguage(final Languages language) { final updatedState = state.copyWith( wordsLanguage: language, @@ -193,6 +194,14 @@ class LevelBloc extends Cubit { phaseType: GamePhaseType.entryWord, ); emit(updatedState); + _onApplyMultiplier(energyApplicationType); + dto.levelPlayersCubit.onSwitchToNextPlayer(const SwitchToNextPlayerEvent()); + } + + void _onApplyMultiplier( + final EnergyApplicationType energyApplicationType, + ) { + final liveState = state; final levelPlayersCubit = dto.levelPlayersCubit; final technologiesCubit = dto.technologiesCubit; @@ -211,18 +220,16 @@ class LevelBloc extends Cubit { technologiesCubit.onResearchTechnology( ResearchTechnologyEvent(score: appliedScore), ); - case EnergyApplicationType.noop: + case EnergyApplicationType.crystalMove || EnergyApplicationType.noop: } final playerId = levelPlayersCubit.state.currentPlayerId; - levelPlayersCubit - ..onUpdatePlayerHighscore( - UpdatePlayerHighscoreEvent( - score: appliedScore * -1, - playerId: playerId, - ), - ) - ..onSwitchToNextPlayer(const SwitchToNextPlayerEvent()); + levelPlayersCubit.onUpdatePlayerHighscore( + UpdatePlayerHighscoreEvent( + score: appliedScore * -1, + playerId: playerId, + ), + ); } String getWordSuggestion() { diff --git a/lib/pack_core/global_states/level/level_bloc_events.dart b/lib/pack_core/global_states/level/level_bloc_events.dart index 500fb061..3c4d4bac 100644 --- a/lib/pack_core/global_states/level/level_bloc_events.dart +++ b/lib/pack_core/global_states/level/level_bloc_events.dart @@ -3,6 +3,7 @@ part of 'level_bloc.dart'; enum EnergyApplicationType { refueling, researchingTechnology, + crystalMove, noop, } diff --git a/lib/pack_core/global_states/level_players/level_players_bloc.dart b/lib/pack_core/global_states/level_players/level_players_bloc.dart index bdda9119..c4035c10 100644 --- a/lib/pack_core/global_states/level_players/level_players_bloc.dart +++ b/lib/pack_core/global_states/level_players/level_players_bloc.dart @@ -26,6 +26,7 @@ class LevelPlayersBloc extends Cubit { super(LevelPlayersBlocState.empty); final LevelPlayersBlocDiDto diDto; final _log = Logger(); + PlayerCharacterModel get playerCharacter => state.playerCharacter; @override Future close() { @@ -83,15 +84,20 @@ class LevelPlayersBloc extends Cubit { ) => emit(state.copyWith(playerCharacter: value)); + double get powerUsage => playerCharacter.balloonParams.powerUsage; + void onPowerUsageChange(final String? value) => onChangeCharacter( + playerCharacter.copyWith.balloonParams( + powerUsage: double.tryParse(value ?? '') ?? 0, + ), + ); + void onChangeCharacterPosition({ required final Vector2 distanceToOrigin, required final LiftForceModel liftForce, }) { - final updatedState = state.copyWith( - playerCharacter: state.playerCharacter.copyWith( - distanceToOrigin: distanceToOrigin.toSerializedVector2(), - balloonPowers: liftForce.updatedPowers, - ), + final updatedState = state.copyWith.playerCharacter( + distanceToOrigin: distanceToOrigin.toSerializedVector2(), + balloonPowers: liftForce.updatedPowers, ); emit(updatedState); @@ -104,11 +110,9 @@ class LevelPlayersBloc extends Cubit { final power = scoreMechanics.convertScoreToPower( score: event.score, ); - final updatedState = state.copyWith( - playerCharacter: state.playerCharacter.copyWith( - balloonPowers: state.playerCharacter.balloonPowers.copyWith( - power: state.playerCharacter.balloonPowers.power + power, - ), + final updatedState = state.copyWith.playerCharacter( + balloonPowers: playerCharacter.balloonPowers.copyWith( + power: playerCharacter.balloonPowers.power + power, ), ); emit(updatedState); diff --git a/lib/pack_core/global_states/technologies/technologies_cubit.dart b/lib/pack_core/global_states/technologies/technologies_cubit.dart index d8650e6d..3a0e714c 100644 --- a/lib/pack_core/global_states/technologies/technologies_cubit.dart +++ b/lib/pack_core/global_states/technologies/technologies_cubit.dart @@ -39,6 +39,44 @@ class TechnologiesCubit extends Cubit final TechnologiesCubitDto dto; @override void onConsumeTickEvent() {} + + /// since ascending and descending are most primitive technologies + /// if they are unlocked, we can make visible actions tab + bool checkIsActionsViewUnblocked({final Languages? language}) => [ + TechnologyType.ascending, + TechnologyType.descending, + ].every( + (final type) => checkIsTechnologyResearchedByType( + type: type, + language: language, + ), + ); + + bool checkIsTechnologyResearchedByType({ + required final TechnologyType type, + final Languages? language, + }) => + checkIsTechnologyResearched( + id: TechnologyModelId(type), + language: language, + ); + bool checkIsTechnologyResearched({ + required final TechnologyModelId id, + final Languages? language, + }) { + final techProgress = _getTechnologyProgress( + technologyId: id, + progressTree: state.progress, + ); + if (techProgress == null) return false; + return dto.mechanics.technology + .checkIsUnlockedForLanguage( + unlockCondition: techProgress.unlockCondition, + language: language, + ) + .isUnlocked; + } + void onResearchingTechnologyChanged( final TechnologyModelId id, // ignore: avoid_positional_boolean_parameters diff --git a/lib/subgames/long_game/long_game.dart b/lib/subgames/long_game/long_game.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/subgames/quick_game/debug_side_bar/debug_side_bar.dart b/lib/subgames/quick_game/debug_side_bar/debug_side_bar.dart index 3ec6bb81..297a61db 100644 --- a/lib/subgames/quick_game/debug_side_bar/debug_side_bar.dart +++ b/lib/subgames/quick_game/debug_side_bar/debug_side_bar.dart @@ -46,7 +46,7 @@ class UiDebugSideBarBody extends StatelessWidget { @override Widget build(final BuildContext context) { final screenCubit = context.watch(); - context.watch(); + final levelPlayerBloc = context.watch(); final debugCubit = context.watch(); final screenSize = MediaQuery.sizeOf(context); final theme = Theme.of(context); @@ -144,8 +144,8 @@ class UiDebugSideBarBody extends StatelessWidget { labelText: 'maxPower', ), UiTextField.underlined( - value: screenCubit.powerUsage.toString(), - onChanged: screenCubit.onPowerUsageChange, + value: levelPlayerBloc.powerUsage.toString(), + onChanged: levelPlayerBloc.onPowerUsageChange, keyboardType: TextInputType.number, labelText: 'powerUsage', ), diff --git a/lib/subgames/quick_game/debug_side_bar/debug_side_bar_vm.dart b/lib/subgames/quick_game/debug_side_bar/debug_side_bar_vm.dart index 843f6441..00353986 100644 --- a/lib/subgames/quick_game/debug_side_bar/debug_side_bar_vm.dart +++ b/lib/subgames/quick_game/debug_side_bar/debug_side_bar_vm.dart @@ -57,15 +57,6 @@ class UiDebugSideBarCubit extends Cubit { ); } - double get powerUsage => _character.balloonParams.powerUsage; - void onPowerUsageChange(final String? value) { - changeParams( - _character.balloonParams.copyWith( - powerUsage: double.tryParse(value ?? '') ?? 0, - ), - ); - } - double get gravityForce => _gameConstants.forces.gravityForce; void onGravityForceChange(final String? value) { changeForces( diff --git a/lib/subgames/quick_game/dialogs/level_start/start_options/level_options_screen.dart b/lib/subgames/quick_game/dialogs/level_start/start_options/level_options_screen.dart index afb3f87f..d57faad1 100644 --- a/lib/subgames/quick_game/dialogs/level_start/start_options/level_options_screen.dart +++ b/lib/subgames/quick_game/dialogs/level_start/start_options/level_options_screen.dart @@ -29,60 +29,63 @@ class LevelOptionsScreen extends HookWidget { useListenable(uxState.isDictionariesLoading); final statusCubit = context.watch(); - return SingleChildScrollView( - child: Column( - children: [ - uiTheme.verticalBoxes.medium, - GestureDetector( - onTap: () => unblockerNotifier.value++, - child: Text( - S.of(context).selectPlayers.toUpperCase(), - style: context.textThemeBold.bodyLarge, - textAlign: TextAlign.center, + return Scrollbar( + thumbVisibility: true, + child: SingleChildScrollView( + child: Column( + children: [ + uiTheme.verticalBoxes.medium, + GestureDetector( + onTap: () => unblockerNotifier.value++, + child: Text( + S.of(context).selectPlayers.toUpperCase(), + style: context.textThemeBold.bodyLarge, + textAlign: TextAlign.center, + ), ), - ), - uiTheme.verticalBoxes.medium, - ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 140), - child: PlayerProfileRow( - checkIsPlayerSelected: uxState.checkIsPlayerSelected, - onSelected: uxState.onPlayerSelected, + uiTheme.verticalBoxes.medium, + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 140), + child: PlayerProfileRow( + checkIsPlayerSelected: uxState.checkIsPlayerSelected, + onSelected: uxState.onPlayerSelected, + ), ), - ), - uiTheme.verticalBoxes.medium, - CheckboxListTile( - value: uxState.shouldStartTutorial, - onChanged: uxState.changeShouldStartTutorial, - title: Text(S.of(context).enableTutorial), - ), - uiTheme.verticalBoxes.medium, - _ExperimentsListView(unblockerNotifier: unblockerNotifier), - uiTheme.verticalBoxes.medium, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Tooltip( - message: S.of(context).createNewPlayerTooltip, - child: TextButton.icon( - onPressed: onCreatePlayer, - icon: const Icon(Icons.add), - label: Text(S.of(context).createPlayer), + uiTheme.verticalBoxes.medium, + CheckboxListTile( + value: uxState.shouldStartTutorial, + onChanged: uxState.changeShouldStartTutorial, + title: Text(S.of(context).enableTutorial), + ), + uiTheme.verticalBoxes.medium, + _ExperimentsListView(unblockerNotifier: unblockerNotifier), + uiTheme.verticalBoxes.medium, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Tooltip( + message: S.of(context).createNewPlayerTooltip, + child: TextButton.icon( + onPressed: onCreatePlayer, + icon: const Icon(Icons.add), + label: Text(S.of(context).createPlayer), + ), ), - ), - UiTextButton.text( - text: S.of(context).play, - isLoading: uxState.isDictionariesLoading.value || - statusCubit.isLoading, - isLongButton: true, - mainAlignment: MainAxisAlignment.center, - onPressed: uxState.playersIds.isEmpty - ? null - : () async => uxState.onPlay(context), - ), - ], - ), - uiTheme.verticalBoxes.medium, - ], + UiTextButton.text( + text: S.of(context).play, + isLoading: uxState.isDictionariesLoading.value || + statusCubit.isLoading, + isLongButton: true, + mainAlignment: MainAxisAlignment.center, + onPressed: uxState.playersIds.isEmpty + ? null + : () async => uxState.onPlay(context), + ), + ], + ), + uiTheme.verticalBoxes.medium, + ], + ), ), ); } @@ -147,7 +150,7 @@ class _ExperimentsListView extends HookWidget { value: { Languages.en: // ignore: lines_longer_than_80_chars - 'Notice: this feature requires to preload a massive amount of dictionaries (about 30 mb), so first level loading will take several minutes.', + 'Notice: this feature requires to preload a massive amount of dictionaries (about 30 mb), so first level loading may take a few minutes.', Languages.ru: // ignore: lines_longer_than_80_chars 'Примечание: данная функция требует предварительного большой загрузки дополнительных словарей (около 30 мб), поэтому первая загрузка уровня займет несколько минут.', diff --git a/lib/subgames/quick_game/dialogs/level_word_suggestion.dart b/lib/subgames/quick_game/dialogs/level_word_suggestion.dart index d42008bb..82bd6270 100644 --- a/lib/subgames/quick_game/dialogs/level_word_suggestion.dart +++ b/lib/subgames/quick_game/dialogs/level_word_suggestion.dart @@ -95,6 +95,9 @@ class LevelWordSuggestionDialog extends HookWidget { final state = useStateBuilder( () => _DialogState(dto: _DialogStateDiDto.use(context.read)), ); + final currentWord = context.select( + (final c) => c.state.currentWord, + ); final fadeColor = context.colorScheme.onPrimaryContainer.withOpacity(0.6); final wordMeaning = AnimatedSize( duration: 350.milliseconds, @@ -195,16 +198,54 @@ class LevelWordSuggestionDialog extends HookWidget { textAlign: TextAlign.center, ), uiTheme.verticalBoxes.large, - Text( - List.generate( - state._suggestedWord.length, - (final index) => '*', - ).join(), + DefaultTextStyle.merge( style: theme.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), - textAlign: TextAlign.center, + child: Builder( + builder: (final context) { + final children = []; + final parts = state._suggestedWord + .split(currentWord.middlePart) + .map( + (final e) => Padding( + padding: const EdgeInsets.only(top: 5), + child: Text( + e.characters.map((final e) => '*').join(), + ), + ), + ); + children + ..add(parts.first) + ..add( + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 2), + child: Text(currentWord.middlePart), + ), + ); + if (parts.length > 1) { + children.add(parts.last); + } + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ); + }, + ), ), + // Text( + + // , + // // List.generate( + // // state._suggestedWord.length, + // // (final index) => '*', + // // ).join(), + + // textAlign: TextAlign.center, + // ), wordMeaning, uiTheme.verticalBoxes.medium, _UsePointsButton( diff --git a/lib/subgames/quick_game/dialogs/technologies/technologies_tree_dialog.dart b/lib/subgames/quick_game/dialogs/technologies/technologies_tree_dialog.dart index 441ec5af..4a931d47 100644 --- a/lib/subgames/quick_game/dialogs/technologies/technologies_tree_dialog.dart +++ b/lib/subgames/quick_game/dialogs/technologies/technologies_tree_dialog.dart @@ -57,8 +57,8 @@ class TechnologiesTreeDialog extends HookWidget { final locale = useLocale(context); final levelCubit = context.read(); - final wordsLanguage = context - .select((final c) => c.state.wordsLanguage); + final wordsLanguage = + context.select((final c) => c.wordsLanguage); /// to save initial state, and compare with final state final initialTechnologyState = @@ -78,11 +78,12 @@ class TechnologiesTreeDialog extends HookWidget { return DialogScaffold( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), - side: _TechnologyPanelView( + bottom: _TechnologyPanelView( technologyWord: technologyForInfoNotifier.value, ), - children: [ - Row( + top: Padding( + padding: const EdgeInsets.only(top: 16, left: 16, right: 16), + child: Row( children: [ Text( const LocalizedMap( @@ -113,6 +114,8 @@ class TechnologiesTreeDialog extends HookWidget { ), ], ), + ), + children: [ const Gap(16), Card.outlined( child: Padding( @@ -188,20 +191,23 @@ class TechnologiesTreeDialog extends HookWidget { ), ), const Gap(32), - ...technologiesCubit.technologies.values.map( - (final e) => _TechnologyTile( - key: ValueKey(e.id), - onHover: (final technologyWord) { - technologyForInfoNotifier.value = technologyWord; - }, - language: wordsLanguage, - isSelectionAllowed: isSelectionAllowed, - selectedId: technologiesCubit.researchingTechnology?.id, - onSelectedChanged: technologiesCubit.onResearchingTechnologyChanged, - value: e, - progress: technologiesCubit.progress.technologies[e.id], - ), - ), + ...technologiesCubit.technologies.values + .where((final e) => TechnologyType.checkIsActive(e.type)) + .map( + (final e) => _TechnologyTile( + key: ValueKey(e.id), + onHover: (final technologyWord) { + technologyForInfoNotifier.value = technologyWord; + }, + language: wordsLanguage, + isSelectionAllowed: isSelectionAllowed, + selectedId: technologiesCubit.researchingTechnology?.id, + onSelectedChanged: + technologiesCubit.onResearchingTechnologyChanged, + value: e, + progress: technologiesCubit.progress.technologies[e.id], + ), + ), ], ); } diff --git a/lib/subgames/quick_game/dialogs/widgets/dialog_scaffold.dart b/lib/subgames/quick_game/dialogs/widgets/dialog_scaffold.dart index f80f0f36..2e948bb5 100644 --- a/lib/subgames/quick_game/dialogs/widgets/dialog_scaffold.dart +++ b/lib/subgames/quick_game/dialogs/widgets/dialog_scaffold.dart @@ -6,7 +6,8 @@ class DialogScaffold extends StatelessWidget { const DialogScaffold({ this.children, this.builder, - this.side, + this.bottom, + this.top, this.padding, super.key, }) : assert( @@ -14,13 +15,15 @@ class DialogScaffold extends StatelessWidget { 'Children or builder should be provided', ); final List? children; - final Widget? side; + final Widget? bottom; + final Widget? top; final WidgetBuilder? builder; final EdgeInsets? padding; @override Widget build(final BuildContext context) { - final side = this.side; final uiTheme = context.uiTheme; + final bottom = this.bottom; + final top = this.top; final padding = this.padding ?? EdgeInsets.all(uiTheme.spacing.extraLarge); Widget child; if (builder != null) { @@ -33,7 +36,13 @@ class DialogScaffold extends StatelessWidget { ); child = Column( mainAxisSize: MainAxisSize.min, - children: side != null ? [Expanded(child: body), side] : [body], + children: top != null || bottom != null + ? [ + if (top != null) top, + Expanded(child: body), + if (bottom != null) bottom, + ] + : [body], ); } return AnimatedContainer( diff --git a/lib/subgames/quick_game/game_renderer/canvas_renderer.dart b/lib/subgames/quick_game/game_renderer/canvas_renderer.dart index dda7f2a8..2d70361e 100644 --- a/lib/subgames/quick_game/game_renderer/canvas_renderer.dart +++ b/lib/subgames/quick_game/game_renderer/canvas_renderer.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flame/events.dart'; @@ -110,9 +111,12 @@ class CanvasRenderer extends Component final player = canvasObjectsDrawer.player; if (player != null) { final screenSize = game.size; - Offset offset = - ((player.position.toVector2() / 2.2) - (screenSize / 11)) - .toOffset(); + Offset offset = (player.position.toVector2() - + Vector2( + screenSize.x / 3, + math.max(200, screenSize.y / 2 - 60), + )) + .toOffset(); offset = origin.toOffset() - offset; origin = offset.toVector2(); canvasObjectsDrawer.onOriginUpdate(); diff --git a/lib/subgames/quick_game/overlays/gui_widgets/power_bar.dart b/lib/subgames/quick_game/overlays/gui_widgets/power_bar.dart index d5f3d6c6..0965f4c3 100644 --- a/lib/subgames/quick_game/overlays/gui_widgets/power_bar.dart +++ b/lib/subgames/quick_game/overlays/gui_widgets/power_bar.dart @@ -1,12 +1,12 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:wbw_core/wbw_core.dart'; import 'package:wbw_design_core/wbw_design_core.dart'; import 'package:word_by_word_game/pack_core/global_states/debug/debug.dart'; import 'package:word_by_word_game/pack_core/global_states/global_states.dart'; +import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/level_actions_frame/heat_engine_view.dart'; class UIPowerBar extends StatelessWidget { const UIPowerBar({super.key}); @@ -60,31 +60,14 @@ class UIPowerBar extends StatelessWidget { uiKey: TutorialUiItem.baloonPower, child: Stack( children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.elliptical(52, 52), - ), - color: context.colorScheme.error.withOpacity(0.3), - border: Border.all(color: context.colorScheme.error), - ), + AnimatedProgressBar( height: 24, width: maxWidth, - ), - Positioned( - top: 0, - left: 0, - bottom: 0, - child: AnimatedContainer( - duration: 20.milliseconds, - width: powerRatio * maxWidth, - decoration: BoxDecoration( - color: context.colorScheme.error.withOpacity(0.6), - borderRadius: const BorderRadius.all( - Radius.elliptical(52, 52), - ), - ), - ), + value: powerRatio, + backgroundColor: context.colorScheme.error.withOpacity(0.3), + color: context.colorScheme.error.withOpacity(0.6), + borderRadiusValue: 52, + border: Border.all(color: context.colorScheme.error), ), Positioned( top: 0, diff --git a/lib/subgames/quick_game/overlays/weather_overlay.dart b/lib/subgames/quick_game/overlays/weather_overlay.dart index 2a7cec6f..e8f87dc4 100644 --- a/lib/subgames/quick_game/overlays/weather_overlay.dart +++ b/lib/subgames/quick_game/overlays/weather_overlay.dart @@ -99,20 +99,22 @@ class _WeatherOverlayBodyState extends State<_WeatherOverlayBody> { Widget build(final BuildContext context) { final color = const Color.fromARGB(255, 1, 20, 17).withOpacity(_maxTargetOpacity); - return Stack( - fit: StackFit.expand, - children: [ - AnimatedContainer( - duration: 1800.milliseconds, - decoration: BoxDecoration( - color: Colors.amber[200]!.withOpacity(0.1), + return IgnorePointer( + child: Stack( + fit: StackFit.expand, + children: [ + AnimatedContainer( + duration: 1800.milliseconds, + decoration: BoxDecoration( + color: Colors.amber[200]!.withOpacity(0.1), + ), ), - ), - AnimatedContainer( - duration: 1500.milliseconds, - decoration: BoxDecoration(color: color), - ), - ], + AnimatedContainer( + duration: 1500.milliseconds, + decoration: BoxDecoration(color: color), + ), + ], + ), ); } } diff --git a/lib/subgames/quick_game/player_controls/elements/game_bottom_bar/game_bottom_bar.dart b/lib/subgames/quick_game/player_controls/elements/game_bottom_bar/game_bottom_bar.dart index 0e0fd57d..bd13adeb 100644 --- a/lib/subgames/quick_game/player_controls/elements/game_bottom_bar/game_bottom_bar.dart +++ b/lib/subgames/quick_game/player_controls/elements/game_bottom_bar/game_bottom_bar.dart @@ -17,7 +17,7 @@ class GameBottomBar extends StatelessWidget { @override Widget build(final BuildContext context) => BlocProvider( create: (final context) => WordCompositionCubit( - diDto: WordCompositionStateDiDto.use(context.read), + dto: WordCompositionStateDiDto.use(context.read), ), child: Builder( builder: (final context) => _Card( diff --git a/lib/subgames/quick_game/player_controls/elements/level_actions_frame/actions_advanced_frame.dart b/lib/subgames/quick_game/player_controls/elements/level_actions_frame/actions_advanced_frame.dart index 75685e41..3ae7cc50 100644 --- a/lib/subgames/quick_game/player_controls/elements/level_actions_frame/actions_advanced_frame.dart +++ b/lib/subgames/quick_game/player_controls/elements/level_actions_frame/actions_advanced_frame.dart @@ -8,6 +8,7 @@ import 'package:wbw_locale/wbw_locale.dart'; import 'package:word_by_word_game/pack_core/global_states/global_states.dart'; import 'package:word_by_word_game/subgames/quick_game/dialogs/dialogs.dart'; import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/level_actions_frame/actions_simple_frame.dart'; +import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/level_actions_frame/heat_engine_view.dart'; import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/level_actions_frame/level_actions_row.dart'; import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar.dart'; @@ -20,12 +21,14 @@ class UIActionFrameAdvanced extends StatelessWidget { final locale = useLocale(context); final textTheme = context.textTheme; return DefaultTabController( - length: 2, + length: 3, child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TabBar( + tabAlignment: TabAlignment.center, padding: EdgeInsets.zero, - labelPadding: EdgeInsets.zero, + isScrollable: true, tabs: [ ( title: const LocalizedMap( @@ -34,7 +37,7 @@ class UIActionFrameAdvanced extends StatelessWidget { Languages.ru: 'Энергия', Languages.it: 'Energia', }, - ).getValue(locale), + ), iconChildren: [ Image.asset( UiAssetHelper.useImagePath(UiIcons.fire.path), @@ -43,6 +46,19 @@ class UIActionFrameAdvanced extends StatelessWidget { ), ], ), + ( + title: const LocalizedMap( + value: { + Languages.en: 'Actions', + Languages.ru: 'Действия', + Languages.it: 'Azioni', + }, + ), + iconChildren: [ + const Icon(CupertinoIcons.book, size: 18), + const Gap(2), + ] + ), ( title: const LocalizedMap( value: { @@ -50,7 +66,7 @@ class UIActionFrameAdvanced extends StatelessWidget { Languages.ru: 'Технология', Languages.it: 'Tecnologia', }, - ).getValue(locale), + ), iconChildren: [ const Icon(CupertinoIcons.lab_flask, size: 18), const Gap(2), @@ -65,7 +81,7 @@ class UIActionFrameAdvanced extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ...e.iconChildren, - Text(e.title), + Text(e.title.getValue(locale)), ], ), ), @@ -88,6 +104,7 @@ class UIActionFrameAdvanced extends StatelessWidget { const UiEnergyCards(), ], ), + const _ActionsTabView(), const _TechnologyTabView(), ], ), @@ -229,11 +246,14 @@ class _ChangeResearchingTechnology extends StatelessWidget { ) { final mechanics = context.read(); final technologiesCubit = context.watch(); + final wordsLanguage = + context.select((final c) => c.wordsLanguage); final technologyProgress = technologiesCubit.researchingTechnologyProgress; final unlockCondition = technologyProgress?.unlockCondition; if (unlockCondition == null) return (isUnlocked: false, percentage: 0.0); return mechanics.technology.checkIsUnlockedForLanguage( unlockCondition: unlockCondition, + language: wordsLanguage, ); } @@ -391,3 +411,12 @@ class _TechnologyMultiplierCard extends StatelessWidget { ); } } + +class _ActionsTabView extends StatelessWidget { + const _ActionsTabView({super.key}); + + @override + Widget build(final BuildContext context) => const Row( + children: [Flexible(child: HeatEngineView())], + ); +} diff --git a/lib/subgames/quick_game/player_controls/elements/level_actions_frame/heat_engine_view.dart b/lib/subgames/quick_game/player_controls/elements/level_actions_frame/heat_engine_view.dart new file mode 100644 index 00000000..d227f76d --- /dev/null +++ b/lib/subgames/quick_game/player_controls/elements/level_actions_frame/heat_engine_view.dart @@ -0,0 +1,540 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:wbw_core/wbw_core.dart'; +import 'package:wbw_design_core/wbw_design_core.dart'; +import 'package:word_by_word_game/pack_core/global_states/global_states.dart'; +import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/level_actions_frame/actions_simple_frame.dart'; +import 'package:word_by_word_game/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar.dart'; + +// TODO(arenukvern): add as freezed +class EngineCrystalModel { + EngineCrystalModel({ + required this.id, + }); + final String id; +} + +class HeatEngineView extends StatelessWidget { + const HeatEngineView({super.key}); + + @override + Widget build(final BuildContext context) { + final technologiesCubit = context.watch(); + final wordsLanguage = + context.select((final c) => c.wordsLanguage); + final isAscendingResearched = + technologiesCubit.checkIsTechnologyResearchedByType( + type: TechnologyType.ascending, + language: wordsLanguage, + ); + final isDescendingResearched = + technologiesCubit.checkIsTechnologyResearchedByType( + type: TechnologyType.descending, + language: wordsLanguage, + ); + return Stack( + children: [ + const HeatEngineViewBody(), + TechnologyLockedCard( + isLocked: !(isDescendingResearched && isAscendingResearched), + children: [ + Wrap( + spacing: 8, + children: [ + _CompletableText( + text: 'Ascending', + isCompleted: isAscendingResearched, + ), + const Text('|'), + _CompletableText( + text: 'Descending', + isCompleted: isDescendingResearched, + ), + ], + ), + ], + ), + ], + ); + } +} + +class _CompletableText extends StatelessWidget { + const _CompletableText({ + required this.text, + required this.isCompleted, + }); + final bool isCompleted; + final String text; + @override + Widget build(final BuildContext context) => Text( + text, + style: DefaultTextStyle.of(context).style.copyWith( + decoration: isCompleted ? TextDecoration.lineThrough : null, + ), + ); +} + +class TechnologyLockedCard extends StatelessWidget { + const TechnologyLockedCard({ + required this.children, + required this.isLocked, + super.key, + }); + final List children; + final bool isLocked; + @override + Widget build(final BuildContext context) => Visibility( + visible: isLocked, + child: Stack( + children: [ + Positioned.fill( + child: const SizedBox().blurred( + blurColor: context.colorScheme.tertiary, + colorOpacity: 0.7, + blur: 2, + ), + ), + DefaultTextStyle.merge( + style: context.textThemeBold.bodyLarge?.copyWith( + color: context.colorScheme.onTertiary, + decorationColor: context.colorScheme.tertiary, + decorationThickness: 5, + ), + child: Container( + alignment: Alignment.center, + // color: context.colorScheme.secondaryContainer.wit, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.lock_outline_rounded, + color: context.colorScheme.onTertiary, + ), + const Gap(8), + Text( + 'Requires technologies', + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onTertiary, + ), + ), + const Gap(4), + ...children, + ], + ), + ), + ), + ], + ), + ); +} + +class HeatEngineViewBody extends StatefulHookWidget { + const HeatEngineViewBody({super.key}); + + @override + State createState() => _HeatEngineViewBodyState(); +} + +class _HeatEngineViewBodyState extends State { + static const _crystalCellDimension = 34.0; + // TODO(arenukvern): save engine crystal + final List _cellsCrystals = []; + final List _engineCrystals = []; + EngineMechanics get _engineMechanics => + context.read().engine; + + LevelPlayersBloc get _levelPlayerBloc => context.read(); + WordCompositionCubit get _wordCompositionCubit => + context.read(); + + @override + void initState() { + super.initState(); + final engineCrystalsCount = _engineMechanics + .convertPowerUsageToCrystalCount(_levelPlayerBloc.powerUsage); + final totalCrystalsCount = _engineMechanics.crystalsCount; + _cellsCrystals.length = _engineMechanics.crystalsCount; + _engineCrystals.length = _engineMechanics.crystalsCount; + final crystals = List.generate( + totalCrystalsCount, + (final i) => EngineCrystalModel(id: '$i'), + ); + for (var i = 0; i < totalCrystalsCount; i++) { + final crystal = crystals[i]; + if (i < engineCrystalsCount) { + _engineCrystals[i] = crystal; + } else { + _cellsCrystals[i] = crystal; + } + } + } + + void _onCrystalPlacedToEngine({ + required final int index, + required final EngineCrystalModel crystal, + }) { + _engineCrystals[index] = crystal; + _cellsCrystals[index] = null; + setState(() {}); + _changePower(); + } + + void _onCrystalPlacedToStorage({ + required final int index, + required final EngineCrystalModel crystal, + }) { + _cellsCrystals[index] = crystal; + _engineCrystals[index] = null; + setState(() {}); + _changePower(); + } + + void _changePower() { + final powerUsage = _engineMechanics + .convertCrystalCountToPowerUsage(_engineCrystals.nonNulls.length); + _levelPlayerBloc.onPowerUsageChange('$powerUsage'); + WidgetsBinding.instance.addPostFrameCallback((final _) async { + await Future.delayed(500.milliseconds); + _wordCompositionCubit.onCrystalMoved(_kMovementMultiplier); + }); + } + + static const _kMovementMultiplier = EnergyMultiplierType.m1; + @override + Widget build(final BuildContext context) { + final composable = useApplyingScoreComposable( + type: _kMovementMultiplier, + context: context, + ); + final movementScore = composable.applyingScore; + + return Column( + children: [ + const Spacer(), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Gap(8), + Card.outlined( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Column( + children: [ + const Gap(4), + ...List.generate( + _cellsCrystals.length, + (final index) { + final crystal = _cellsCrystals[index]; + return _EngineCrystalCell( + key: ValueKey(index), + crystal: crystal, + index: index, + movementScore: movementScore, + cellDimension: _crystalCellDimension, + onCrystalPlaced: (final crystal) => + _onCrystalPlacedToStorage( + index: index, + crystal: crystal, + ), + ); + }, + ), + const Gap(4), + ], + ), + ), + ), + Card( + child: Padding( + padding: const EdgeInsets.all(4), + child: Row( + children: [ + Column( + children: [ + ...List.generate( + _engineCrystals.length, + (final index) { + final crystal = _engineCrystals[index]; + return _EngineWaveRow( + key: ValueKey(index), + cellDimension: _crystalCellDimension, + movementScore: movementScore, + crystal: crystal, + index: index, + onCrystalPlaced: (final crystal) => + _onCrystalPlacedToEngine( + index: index, + crystal: crystal, + ), + ); + }, + ), + ], + ), + const Gap(8), + const Text('Heat\npower'), + const Gap(8), + AnimatedProgressBar( + backgroundColor: Colors.green[200], + color: Colors.green[600], + value: _engineCrystals.nonNulls.length / + _cellsCrystals.length, + height: 70, + width: 12, + ), + const Gap(4), + ], + ), + ), + ), + const Gap(8), + ], + ), + const Spacer(), + const Text('Heat generator'), + const Gap(8), + ], + ); + } +} + +class _EngineCrystalCell extends StatelessWidget { + const _EngineCrystalCell({ + required this.index, + required this.crystal, + required this.onCrystalPlaced, + required this.cellDimension, + required this.movementScore, + super.key, + }); + final int index; + final int movementScore; + final EngineCrystalModel? crystal; + final ValueChanged onCrystalPlaced; + final double cellDimension; + @override + Widget build(final BuildContext context) { + final isFilled = crystal != null; + return DragTarget( + builder: (final context, final candidateData, final rejectedData) { + final isActive = candidateData.isNotEmpty || isFilled; + return _CrystalCell( + cellDimension: cellDimension, + crystal: crystal, + isActive: isActive, + isFilled: isFilled, + movementScore: movementScore, + isForEngine: false, + ); + }, + onAcceptWithDetails: (final details) { + if (isFilled) return; + final newCrystal = details.data; + onCrystalPlaced(newCrystal); + }, + ); + } +} + +class _CrystalCell extends StatelessWidget { + const _CrystalCell({ + required this.isActive, + required this.isFilled, + required this.cellDimension, + required this.crystal, + required this.isForEngine, + required this.movementScore, + super.key, + }); + final bool isActive; + final bool isFilled; + final bool isForEngine; + final EngineCrystalModel? crystal; + final int movementScore; + final double cellDimension; + @override + Widget build(final BuildContext context) { + final Color activeColor; + final Color color; + if (isForEngine) { + activeColor = Colors.green.shade600; + color = Colors.green.shade100; + } else { + activeColor = Colors.lightBlue.shade600; + color = Colors.lightBlue.shade100; + } + return Card.outlined( + shape: BeveledRectangleBorder( + borderRadius: + isActive ? BorderRadius.circular(4) : BorderRadius.circular(16), + side: BorderSide( + color: isActive ? activeColor : color, + ), + ), + margin: const EdgeInsets.all(2), + child: SizedBox.square( + dimension: cellDimension, + child: isFilled + ? _EngineCrystalWidget( + crystal: crystal!, + isActive: isForEngine, + movementScore: movementScore, + ) + : null, + ), + ); + } +} + +class _EngineCrystalWidget extends StatelessWidget { + const _EngineCrystalWidget({ + required this.crystal, + required this.isActive, + required this.movementScore, + this.dimension = 32, + super.key, + }); + final double dimension; + final EngineCrystalModel crystal; + final int movementScore; + final bool isActive; + + @override + Widget build(final BuildContext context) { + const borderRadius = BorderRadius.all(Radius.elliptical(8, 8)); + final crystalWidget = MouseRegion( + cursor: SystemMouseCursors.grab, + child: AnimatedContainer( + duration: 250.milliseconds, + width: dimension, + height: dimension, + decoration: BoxDecoration( + borderRadius: borderRadius, + color: isActive ? Colors.green.shade400 : Colors.lightBlue.shade200, + ), + alignment: Alignment.center, + child: Text( + '${movementScore.formattedScore}', + textAlign: TextAlign.center, + style: context.textThemeBold.titleSmall, + ), + ), + ); + return Draggable( + feedback: Material( + borderRadius: borderRadius, + elevation: 8, + child: crystalWidget, + ), + data: crystal, + axis: Axis.horizontal, + childWhenDragging: const SizedBox(), + child: crystalWidget, + ); + } +} + +class _EngineWaveRow extends StatelessWidget { + const _EngineWaveRow({ + required this.index, + required this.crystal, + required this.onCrystalPlaced, + required this.cellDimension, + required this.movementScore, + super.key, + }); + final int index; + final double cellDimension; + final EngineCrystalModel? crystal; + final ValueChanged onCrystalPlaced; + final int movementScore; + + @override + Widget build(final BuildContext context) { + final isFilled = crystal != null; + return DragTarget( + builder: (final context, final candidateData, final rejectedData) { + final isActive = candidateData.isNotEmpty || isFilled; + return _CrystalCell( + cellDimension: cellDimension, + crystal: crystal, + isActive: isActive, + isFilled: isFilled, + movementScore: movementScore, + isForEngine: true, + ); + }, + onAcceptWithDetails: (final details) { + if (isFilled) return; + final newCrystal = details.data; + onCrystalPlaced(newCrystal); + }, + ); + } +} + +class AnimatedProgressBar extends StatelessWidget { + const AnimatedProgressBar({ + required this.height, + required this.width, + required this.value, + required this.backgroundColor, + required this.color, + this.border, + this.borderRadiusValue = 24, + super.key, + }); + final double height; + final double width; + final Border? border; + final double borderRadiusValue; + + /// from 0 to 1 + final double value; + final Color? backgroundColor; + final Color? color; + + @override + Widget build(final BuildContext context) { + final borderRadius = BorderRadius.all( + Radius.elliptical(borderRadiusValue, borderRadiusValue), + ); + final isVertical = height > width; + return SizedBox( + width: width, + height: height, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: borderRadius, + border: border, + ), + ), + Positioned( + left: isVertical ? 0 : 0, + top: isVertical ? null : 0, + right: isVertical ? 0 : null, + bottom: isVertical ? 0 : 0, + child: AnimatedContainer( + duration: 250.milliseconds, + curve: Curves.easeInOut, + height: isVertical ? height * value : null, + width: isVertical ? null : width * value, + decoration: BoxDecoration( + color: color, + borderRadius: borderRadius, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar_state.dart b/lib/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar_state.dart index 95aba033..ceb16144 100644 --- a/lib/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar_state.dart +++ b/lib/subgames/quick_game/player_controls/elements/word_composition_bar/word_composition_bar_state.dart @@ -64,26 +64,26 @@ class WordCompositionCubitState with _$WordCompositionCubitState { class WordCompositionCubit extends Cubit { WordCompositionCubit({ - required this.diDto, + required this.dto, }) : wordController = WordFieldController( - currentWord: diDto.levelBloc.state.currentWord, + currentWord: dto.levelBloc.state.currentWord, ), super(const WordCompositionCubitState()) { onLoad(); } late final _tutorialEventsListener = WordCompositionTutorialEventListener( - read: diDto.read, + read: dto.read, onRequestWordFieldFocus: onRequestTextFocus, ); String _latestWord = ''; StreamSubscription? _levelBlocSubscription; final _wordUpdatesController = StreamController(); - final WordCompositionStateDiDto diDto; + final WordCompositionStateDiDto dto; void onLoad() { - _latestWord = diDto.levelBloc.state.latestWord; + _latestWord = dto.levelBloc.state.latestWord; _levelBlocSubscription = - diDto.levelBloc.stream.distinct().listen((final newState) { + dto.levelBloc.stream.distinct().listen((final newState) { if (_latestWord != newState.latestWord) { _latestWord = newState.latestWord; wordController.currentWord = newState.currentWord; @@ -95,7 +95,7 @@ class WordCompositionCubit extends Cubit { } }); wordController.addListener(_onPartChanged); - diDto.tutorialBloc.notifier.addListener(_tutorialEventsListener); + dto.tutorialBloc.notifier.addListener(_tutorialEventsListener); unawaited( _wordUpdatesController.stream .sampleTime( @@ -103,38 +103,42 @@ class WordCompositionCubit extends Cubit { ) .forEach(_changeFullWord), ); + onRequestTextFocus(); } final wordFocusNode = FocusNode(); final WordFieldController wordController; + void _selectMultiplier(final EnergyMultiplierType multiplier) => + dto.levelBloc.onLevelPlayerSelectActionMultiplier( + LevelBlocEventSelectActionMultiplier(multiplier: multiplier), + ); void onInvestToResearchSelected(final EnergyMultiplierType multiplier) { _selectMultiplier(multiplier); _onToEndTurn(EnergyApplicationType.researchingTechnology); } - void _selectMultiplier(final EnergyMultiplierType multiplier) => - diDto.levelBloc.onLevelPlayerSelectActionMultiplier( - LevelBlocEventSelectActionMultiplier(multiplier: multiplier), - ); + void onCrystalMoved(final EnergyMultiplierType multiplier) { + _selectMultiplier(multiplier); + _onToEndTurn(EnergyApplicationType.crystalMove); + } void onPowerSelected(final EnergyMultiplierType multiplier) { _selectMultiplier(multiplier); _onToEndTurn(EnergyApplicationType.refueling); } - Future onToSelectActionPhase() async => - diDto.levelBloc.onAcceptNewWord(); + Future onToSelectActionPhase() async => dto.levelBloc.onAcceptNewWord(); void changeCardVisibility() { emit(state.copyWith(isCardVisible: !state.isCardVisible)); } void _onToEndTurn(final EnergyApplicationType energyApplicationType) { - diDto.levelBloc.onLevelPlayerEndTurnAction(energyApplicationType); + dto.levelBloc.onLevelPlayerEndTurnAction(energyApplicationType); onRequestTextFocus(); } - Future onAddWordToDictionary() async => diDto.levelBloc + Future onAddWordToDictionary() async => dto.levelBloc .onAddNewWordToDictionary(const LevelBlocEventAddNewWordToDictionary()); void onRequestTextFocus() { @@ -148,22 +152,22 @@ class WordCompositionCubit extends Cubit { void _changeFullWord(final CurrentWordModel word) { final event = LevelBlocEventChangeCurrentWord(word: word); - diDto.levelBloc.onChangeCurrentWord(event); + dto.levelBloc.onChangeCurrentWord(event); final tutorialEvent = TutorialUiActionEvent( action: TutorialCompleteAction.onEdit, stringValue: event.word.fullWord, key: TutorialUiItem.wordField, ); - diDto.tutorialBloc.onTutorialUiAction(tutorialEvent); + dto.tutorialBloc.onTutorialUiAction(tutorialEvent); } void onUnblockIndex(final int index) { - diDto.levelBloc.onUnblockIndex(index: index); + dto.levelBloc.onUnblockIndex(index: index); } @override Future close() async { - diDto.tutorialBloc.notifier.removeListener(_tutorialEventsListener); + dto.tutorialBloc.notifier.removeListener(_tutorialEventsListener); wordController ..removeListener(_onPartChanged) ..dispose(); diff --git a/lib/subgames/subgames.dart b/lib/subgames/subgames.dart index 7d7858d5..effe7ee2 100644 --- a/lib/subgames/subgames.dart +++ b/lib/subgames/subgames.dart @@ -1,2 +1 @@ -export 'long_game/long_game.dart'; export 'quick_game/quick_game.dart'; diff --git a/packages/map_editor/lib/ui/sandbox/tech_tree_dialog.dart b/packages/map_editor/lib/ui/sandbox/tech_tree_dialog.dart index 04103e74..e874eed0 100644 --- a/packages/map_editor/lib/ui/sandbox/tech_tree_dialog.dart +++ b/packages/map_editor/lib/ui/sandbox/tech_tree_dialog.dart @@ -44,7 +44,7 @@ class TechnologyTreeDialog extends StatefulWidget { } class _TechnologyTreeDialogState extends State { - Languages _language = LocalizedMap.getCurrentLanugage(); + late Languages _language = context.read(); @override Widget build(final BuildContext context) { final drawerCubit = context.watch(); diff --git a/packages/map_editor/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/map_editor/macos/Flutter/GeneratedPluginRegistrant.swift index 7431936a..50d0c737 100644 --- a/packages/map_editor/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/packages/map_editor/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import connectivity_plus import firebase_analytics import firebase_core import firebase_crashlytics @@ -15,6 +16,7 @@ import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) diff --git a/packages/map_editor/pubspec.yaml b/packages/map_editor/pubspec.yaml index 7631acf0..84360402 100644 --- a/packages/map_editor/pubspec.yaml +++ b/packages/map_editor/pubspec.yaml @@ -6,8 +6,8 @@ publish_to: none # publish_to: "none" # homepage: environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: animate_do: ^3.0.2 diff --git a/packages/map_editor/pubspec_overrides.yaml b/packages/map_editor/pubspec_overrides.yaml index 345a0db0..946dd4d9 100644 --- a/packages/map_editor/pubspec_overrides.yaml +++ b/packages/map_editor/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: wbw_core,wbw_design_core,wbw_locale,http,collection,intl +# melos_managed_dependency_overrides: wbw_core,wbw_design_core,wbw_locale,http,collection,intl,wbw_client,wbw_dictionaries dependency_overrides: wbw_core: path: ../wbw_core @@ -9,3 +9,7 @@ dependency_overrides: http: 1.0.0 collection: 1.18.0 intl: ^0.19.0 + wbw_client: + path: ../wbw_client + wbw_dictionaries: + path: ../wbw_dictionaries diff --git a/packages/wbw_core/lib/src/data_models/technology_model.dart b/packages/wbw_core/lib/src/data_models/technology_model.dart index 4a9281ec..8e03615e 100644 --- a/packages/wbw_core/lib/src/data_models/technology_model.dart +++ b/packages/wbw_core/lib/src/data_models/technology_model.dart @@ -127,5 +127,12 @@ enum TechnologyType { safeLanding, emergencyLanding, ascending, - descending, + descending; + + static const Set _active = { + TechnologyType.ascending, + TechnologyType.descending, + }; + static bool checkIsActive(final TechnologyType type) => + _active.contains(type); } diff --git a/packages/wbw_core/lib/src/mechanics/engine_mechanics.dart b/packages/wbw_core/lib/src/mechanics/engine_mechanics.dart new file mode 100644 index 00000000..d57978c8 --- /dev/null +++ b/packages/wbw_core/lib/src/mechanics/engine_mechanics.dart @@ -0,0 +1,59 @@ +/// Some foundation science ideas: +/// +/// Turn waste heat into sound and sound into electricity +/// https://www.americanscientist.org/article/a-sound-use-for-heat +/// +/// Piezoelectricity and the Piezoelectric Effect +/// https://sciencenotes.org/piezoelectricity-and-the-piezoelectric-effect/ +/// +/// Articles on the topic of converting heat into sound +/// and sound into electricity: +/// +/// - "Heat-to-sound converter" - +/// - https://www.researchgate.net/publication/305722594_Thermoacoustic_Effect_the_Power_of_Conversion_of_Sound_Energy_Heat_Energy_Review +/// +/// - "Harvesting heat energy with sound" +/// - https://www.sciencedirect.com/science/article/abs/pii/S2211285518308449 +/// +/// - "Acoustic Harvesting of Heat from Wind Turbines" +/// - https://www.acentech.com/resources/acoustic-energy-harvesting/ +/// +/// - "Piezoelectric Materials" +/// - https://en.wikipedia.org/wiki/List_of_piezoelectric_materials +/// - https://en.wikipedia.org/w/index.php?search=Piezoelectric+materials&title=Special:Search&ns0=1&searchToken=7oltsamgon379lhg4e7lqy43o +/// +/// - "Feroelectricity" +/// - https://en.wikipedia.org/wiki/Ferroelectricity +/// +/// - "Piezoelectric Materials Market Analysis and Forecast to 2025" +/// - https://www.industryarc.com/Research/Composites-Piezoelectric-Material-Market-Research-501454 +/// - https://teletype.in/@maria-blog/IngTWsw5 +class EngineMechanics { + EngineMechanics(); + static const _crystalCountToPowerUsageMap = { + 0: 0, + 1: 0.4, + 2: 0.75, + }; + double get maxPowerUsage => _crystalCountToPowerUsageMap.values.last; + final _powerUsageToCrystalCount = Map.fromEntries( + _crystalCountToPowerUsageMap.entries.map( + (final e) => MapEntry(e.value, e.key), + ), + ); + int get crystalsCount => _crystalCountToPowerUsageMap.length - 1; + + /// above 0.75 or volumeIncreaseRatio or maxVolume + /// should be increased + /// + /// 0.75, max + /// 0.4, flight min + /// 0, min + double convertCrystalCountToPowerUsage(final int count) => switch (count) { + <= 2 => _crystalCountToPowerUsageMap[count] ?? 0, + _ => _crystalCountToPowerUsageMap.values.last, + }; + + int convertPowerUsageToCrystalCount(final double value) => + _powerUsageToCrystalCount[value] ?? 0; +} diff --git a/packages/wbw_core/lib/src/mechanics/mechanics.dart b/packages/wbw_core/lib/src/mechanics/mechanics.dart index 582ca0b3..b4900a63 100644 --- a/packages/wbw_core/lib/src/mechanics/mechanics.dart +++ b/packages/wbw_core/lib/src/mechanics/mechanics.dart @@ -7,10 +7,12 @@ export './dictionary_mechanics.dart'; export './score_mechanics.dart'; export './word_composition_mechanics.dart'; export './world_time_mechanics.dart'; +export 'engine_mechanics.dart'; export 'objects/objects.dart'; class MechanicsCollection { MechanicsCollection._({ + required this.engine, required this.wordComposition, required this.worldTime, required this.score, @@ -24,6 +26,7 @@ class MechanicsCollection { factory MechanicsCollection.getV1(final BuildContext context) { final score = ScoreMechanics(); return MechanicsCollection._( + engine: EngineMechanics(), wordComposition: WordCompositionMechanics(), worldTime: WorldTimeMechanics(), score: ScoreMechanics(), @@ -34,6 +37,7 @@ class MechanicsCollection { technology: TechnologyMechanics(scoreMechanics: score), ); } + final EngineMechanics engine; final TechnologyMechanics technology; final WeatherMechanics weather; final HotAirBalloonMechanics hotAirBalloon; diff --git a/packages/wbw_core/lib/src/mechanics/objects/character_mechanics.dart b/packages/wbw_core/lib/src/mechanics/objects/character_mechanics.dart index b5a21d4e..1e8f2d98 100644 --- a/packages/wbw_core/lib/src/mechanics/objects/character_mechanics.dart +++ b/packages/wbw_core/lib/src/mechanics/objects/character_mechanics.dart @@ -122,7 +122,7 @@ class BalloonLiftParamsModel with _$BalloonLiftParamsModel { static const initial = BalloonLiftParamsModel( maxVolume: 250, maxPower: 10000, - powerUsage: 0.75, + powerUsage: 0, ); } diff --git a/packages/wbw_core/lib/src/mechanics/technology_mechanics.dart b/packages/wbw_core/lib/src/mechanics/technology_mechanics.dart index 14eae7db..18b26f30 100644 --- a/packages/wbw_core/lib/src/mechanics/technology_mechanics.dart +++ b/packages/wbw_core/lib/src/mechanics/technology_mechanics.dart @@ -11,11 +11,11 @@ class TechnologyMechanics { required final TechnologyUnlockConditionModel unlockCondition, }) { for (final language in Languages.values) { - final (:isUnlocked, :percentage) = checkIsUnlockedForLanguage( + final result = checkIsUnlockedForLanguage( unlockCondition: unlockCondition, language: language, ); - if (isUnlocked) return true; + if (result.isUnlocked) return true; } return false; } diff --git a/packages/wbw_core/lib/src/services/online_status_service_io.dart b/packages/wbw_core/lib/src/services/online_status_service_io.dart index 11094ea0..3bff8c04 100644 --- a/packages/wbw_core/lib/src/services/online_status_service_io.dart +++ b/packages/wbw_core/lib/src/services/online_status_service_io.dart @@ -6,13 +6,14 @@ import 'package:universal_io/io.dart'; import 'online_status_service_i.dart'; class OnlineStatusService extends BaseOnlineStatusService { - OnlineStatusService(super.context) { - unawaited(_checkConnection()); + OnlineStatusService(super.context); + Future onLoad() async { + await _checkConnection(); // Schedule a new timer every 5 seconds to check the internet connection. - _timer = Timer.periodic( - const Duration(seconds: 5), - (final _) async => _checkConnection(), - ); + // _timer = Timer.periodic( + // const Duration(seconds: 5), + // (final _) async => _checkConnection(), + // ); } Timer? _timer; diff --git a/packages/wbw_core/pubspec.yaml b/packages/wbw_core/pubspec.yaml index 6ebe6bd6..d2f95571 100644 --- a/packages/wbw_core/pubspec.yaml +++ b/packages/wbw_core/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: bloc: ^8.1.2 diff --git a/packages/wbw_core/pubspec_overrides.yaml b/packages/wbw_core/pubspec_overrides.yaml index 17097b52..dfccd3fb 100644 --- a/packages/wbw_core/pubspec_overrides.yaml +++ b/packages/wbw_core/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: map_editor,wbw_design_core,wbw_locale,http,collection,intl +# melos_managed_dependency_overrides: map_editor,wbw_design_core,wbw_locale,http,collection,intl,wbw_client,wbw_dictionaries dependency_overrides: map_editor: path: ../map_editor @@ -9,3 +9,7 @@ dependency_overrides: http: 1.0.0 collection: 1.18.0 intl: ^0.19.0 + wbw_client: + path: ../wbw_client + wbw_dictionaries: + path: ../wbw_dictionaries diff --git a/packages/wbw_design_core/lib/src/theme/data/ui_form_factor.dart b/packages/wbw_design_core/lib/src/theme/data/ui_form_factor.dart index a308ada2..e4393b96 100644 --- a/packages/wbw_design_core/lib/src/theme/data/ui_form_factor.dart +++ b/packages/wbw_design_core/lib/src/theme/data/ui_form_factor.dart @@ -79,6 +79,8 @@ enum WidthFormFactor { }); final double max; static bool checkIsXs(final Size size) => size.width <= xs.max; + static bool checkIsXsByContext(final BuildContext context) => + checkIsXs(MediaQuery.sizeOf(context)); static const double mobileTutorialMaxWidth = 700; } diff --git a/packages/wbw_design_core/pubspec.yaml b/packages/wbw_design_core/pubspec.yaml index 7f8b87ec..cfe7646b 100644 --- a/packages/wbw_design_core/pubspec.yaml +++ b/packages/wbw_design_core/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: animate_do: ^3.0.2 diff --git a/packages/wbw_design_core/pubspec_overrides.yaml b/packages/wbw_design_core/pubspec_overrides.yaml index 7ddd1fcd..a50664c9 100644 --- a/packages/wbw_design_core/pubspec_overrides.yaml +++ b/packages/wbw_design_core/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: map_editor,wbw_core,wbw_locale,http,collection,intl +# melos_managed_dependency_overrides: map_editor,wbw_core,wbw_locale,http,collection,intl,wbw_client,wbw_dictionaries dependency_overrides: map_editor: path: ../map_editor @@ -9,3 +9,7 @@ dependency_overrides: http: 1.0.0 collection: 1.18.0 intl: ^0.19.0 + wbw_client: + path: ../wbw_client + wbw_dictionaries: + path: ../wbw_dictionaries diff --git a/packages/wbw_dictionaries/README.md b/packages/wbw_dictionaries/README.md index 3131bc7f..e69de29b 100644 --- a/packages/wbw_dictionaries/README.md +++ b/packages/wbw_dictionaries/README.md @@ -1,3 +0,0 @@ -sources: - -https://github.com/CloudBytes-Academy/English-Dictionary-Open-Source/tree/main diff --git a/packages/wbw_dictionaries/assets/src/README.md b/packages/wbw_dictionaries/assets/src/README.md index 57ce8199..da4f38e1 100644 --- a/packages/wbw_dictionaries/assets/src/README.md +++ b/packages/wbw_dictionaries/assets/src/README.md @@ -11,6 +11,11 @@ https://github.com/Layerex/ozhegov-dict/tree/master - remove words: Admin, <=, ==, =>, Spec, Maxime, Colloq, Lib +For researching: +https://norvig.com/ngrams/ +https://norvig.com/ngrams/sowpods.txt +https://codelabs.developers.google.com/codelabs/flutter-word-puzzle#2 + # How to save csv correctly: - download csv diff --git a/packages/wbw_dictionaries/lib/src/wbw_dictionary.dart b/packages/wbw_dictionaries/lib/src/wbw_dictionary.dart index 2ac3ea56..fe883304 100644 --- a/packages/wbw_dictionaries/lib/src/wbw_dictionary.dart +++ b/packages/wbw_dictionaries/lib/src/wbw_dictionary.dart @@ -133,7 +133,8 @@ class WbwDictionary extends ValueNotifier { if (!isAllowedToBeLoaded) return; /// do not load if user is online - if (repository.onlineStatusService.isConnected) return; + if (repository.onlineStatusService.isConnected && + repository.isAllowedToUseRemote) return; value = WbwDictionariesLoadingStatus.loading; _startStopwatch(); diff --git a/packages/wbw_dictionaries/pubspec.yaml b/packages/wbw_dictionaries/pubspec.yaml index 3019da8c..57122c61 100644 --- a/packages/wbw_dictionaries/pubspec.yaml +++ b/packages/wbw_dictionaries/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.0.1 publish_to: "none" # homepage: environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: archive: ^3.4.10 diff --git a/packages/wbw_dictionaries/pubspec_overrides.yaml b/packages/wbw_dictionaries/pubspec_overrides.yaml index df07e132..3dd9c5c6 100644 --- a/packages/wbw_dictionaries/pubspec_overrides.yaml +++ b/packages/wbw_dictionaries/pubspec_overrides.yaml @@ -1,5 +1,15 @@ -# melos_managed_dependency_overrides: http,collection,intl +# melos_managed_dependency_overrides: http,collection,intl,map_editor,wbw_client,wbw_core,wbw_design_core,wbw_locale dependency_overrides: http: 1.0.0 collection: 1.18.0 intl: ^0.19.0 + map_editor: + path: ../map_editor + wbw_client: + path: ../wbw_client + wbw_core: + path: ../wbw_core + wbw_design_core: + path: ../wbw_design_core + wbw_locale: + path: ../wbw_locale diff --git a/packages/wbw_locale/pubspec.yaml b/packages/wbw_locale/pubspec.yaml index bb2d1615..866f3df8 100644 --- a/packages/wbw_locale/pubspec.yaml +++ b/packages/wbw_locale/pubspec.yaml @@ -4,8 +4,8 @@ version: 0.0.1 publish_to: "none" # homepage: environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: flutter: diff --git a/packages/wbw_server/pubspec.yaml b/packages/wbw_server/pubspec.yaml index b6426120..dcbe3d27 100644 --- a/packages/wbw_server/pubspec.yaml +++ b/packages/wbw_server/pubspec.yaml @@ -3,7 +3,7 @@ description: Starting point for a Serverpod server. # version: 1.0.0 # homepage: https://www.example.com environment: - sdk: ">=3.3.1 <4.0.0" + sdk: ">=3.4.0 <4.0.0" dependencies: csv: ^6.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index f3ab3edc..0be00f96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,11 +19,11 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 3.2.11+56 +version: 3.2.12+57 environment: - sdk: ">=3.3.1 <4.0.0" - flutter: ">=3.19.3" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" dependencies: animate_do: ^3.0.2 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 3abd8050..f47de071 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: word-by-word-game -version: 3.2.11+56 +version: 3.2.12+57 summary: Word Adventure-> write word based on 3 letters of last word & pass Landscape description: | Please note: the game is in active development. @@ -43,7 +43,7 @@ slots: apps: word-by-word-game: command: word_by_word_game - extensions: [flutter-master] # Where "master" defines which Flutter channel to use for the build + extensions: [flutter-stable] # Where "master" defines which Flutter channel to use for the build # using dev as it is the one channel that supports linux plugs: - home