//! Central plugin that groups all gameplay plugins. //! //! Register [`CoreGamePlugin`] once in the app instead of the individual //! plugins. Plugin registration lives here rather than directly in the app //! entry point. use std::sync::Mutex; use bevy::prelude::*; use crate::platform::{ ClipboardBackendResource, StorageBackendResource, default_clipboard_backend, default_storage_backend, }; use crate::{ AchievementPlugin, AnimationPlugin, AssetSourcesPlugin, AutoCompletePlugin, CardAnimationPlugin, CardPlugin, ChallengePlugin, CursorPlugin, DailyChallengePlugin, DiagnosticsHudPlugin, DifficultyPlugin, FeedbackAnimPlugin, FontPlugin, GamePlugin, HelpPlugin, HomePlugin, HudPlugin, InputPlugin, OnboardingPlugin, PausePlugin, PlayBySeedPlugin, ProfilePlugin, ProgressPlugin, RadialMenuPlugin, ReplayOverlayPlugin, ReplayPlaybackPlugin, SafeAreaInsetsPlugin, SelectionPlugin, SettingsPlugin, SplashPlugin, StatsPlugin, SyncProvider, TablePlugin, ThemePlugin, ThemeRegistryPlugin, TimeAttackPlugin, TouchSelectionPlugin, UiFocusPlugin, UiModalPlugin, UiTooltipPlugin, WeeklyGoalsPlugin, WinSummaryPlugin, }; #[cfg(not(target_arch = "wasm32"))] use crate::{ AnalyticsPlugin, AudioPlugin, AvatarPlugin, LeaderboardPlugin, SyncPlugin, SyncSetupPlugin, }; /// Groups all Ferrous Solitaire gameplay plugins. pub struct CoreGamePlugin { sync_provider: Mutex>>, } impl CoreGamePlugin { /// Create a new [`CoreGamePlugin`] with the sync provider used by [`SyncPlugin`]. pub fn new(sync_provider: Box) -> Self { Self { sync_provider: Mutex::new(Some(sync_provider)), } } } impl Plugin for CoreGamePlugin { fn build(&self, app: &mut App) { let mut sync_provider = match self.sync_provider.lock() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), }; #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] let sync_provider = sync_provider .take() .expect("CoreGamePlugin::build called twice"); match default_storage_backend() { Ok(storage) => { app.insert_resource(StorageBackendResource(storage)); } Err(err) => { warn!("storage: failed to initialize platform backend: {err}"); } } match default_clipboard_backend() { Ok(clipboard) => { app.insert_resource(ClipboardBackendResource(clipboard)); } Err(err) => { warn!("clipboard: failed to initialize platform backend: {err}"); } } app.add_plugins(AssetSourcesPlugin) .add_plugins(ThemePlugin) .add_plugins(ThemeRegistryPlugin) .add_plugins(FontPlugin) .add_plugins(GamePlugin) .add_plugins(TablePlugin) .add_plugins(CardPlugin) // Cursor-icon feedback is desktop-only; Android has no pointer cursor. // The drop-target highlight systems (update_drop_highlights, // update_drop_target_overlays) live in CursorPlugin but ARE useful // on Android — they've been left running because their Bevy system // params compile and function on Android; only the CursorIcon insert // is inert. Gate the whole plugin if the cursor APIs ever cause // Android linker issues; for now it's harmless to leave it registered. .add_plugins(CursorPlugin) .add_plugins(InputPlugin) .add_plugins(RadialMenuPlugin) .add_plugins(SelectionPlugin) .add_plugins(TouchSelectionPlugin) .add_plugins(AnimationPlugin) .add_plugins(FeedbackAnimPlugin) .add_plugins(CardAnimationPlugin) .add_plugins(AutoCompletePlugin) .add_plugins(ReplayPlaybackPlugin) .add_plugins(ReplayOverlayPlugin) .add_plugins(StatsPlugin::default()) .add_plugins(ProgressPlugin::default()) .add_plugins(AchievementPlugin::default()) .add_plugins(DailyChallengePlugin) .add_plugins(WeeklyGoalsPlugin) .add_plugins(ChallengePlugin) .add_plugins(PlayBySeedPlugin) .add_plugins(DifficultyPlugin) .add_plugins(TimeAttackPlugin) .add_plugins(SafeAreaInsetsPlugin) .add_plugins(HudPlugin) .add_plugins(HelpPlugin) .add_plugins(HomePlugin::default()) .add_plugins(ProfilePlugin) .add_plugins(PausePlugin) .add_plugins(SettingsPlugin::default()) .add_plugins(OnboardingPlugin) .add_plugins(WinSummaryPlugin) .add_plugins(UiModalPlugin) .add_plugins(UiFocusPlugin) .add_plugins(UiTooltipPlugin) .add_plugins(SplashPlugin) .add_plugins(DiagnosticsHudPlugin); // Plugins that use kira/cpal audio or multi-threaded Tokio are not // compatible with the single-threaded wasm32 runtime. Gate them out // so the browser build boots silently and without a sync backend. #[cfg(not(target_arch = "wasm32"))] app.add_plugins(AvatarPlugin) .add_plugins(AudioPlugin) .add_plugins(SyncPlugin::new(sync_provider)) .add_plugins(SyncSetupPlugin) .add_plugins(AnalyticsPlugin) .add_plugins(LeaderboardPlugin); } }