From f7bc8738d131a8f6b2d4957da68debd715bac6af Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 11 Feb 2023 10:16:42 +0100 Subject: [PATCH] start of update --- Android/.config/dotnet-tools.json | 36 + Android/Activity1.cs | 138 +- Android/Android.csproj | 111 +- Android/{Properties => }/AndroidManifest.xml | 9 +- Android/AndroidPlatform.cs | 250 ++- Android/Properties/AssemblyInfo.cs | 17 - Android/Resources/Resource.Designer.cs | 1037 ------------- Android/Resources/Values/Strings.xml | 2 +- TouchyTickets.sln | 6 - TouchyTickets/Achievement.cs | 92 +- TouchyTickets/Assets.cs | 44 +- TouchyTickets/Attractions/Attraction.cs | 187 ++- TouchyTickets/Attractions/AttractionFlags.cs | 26 +- .../Attractions/AttractionModifier.cs | 131 +- TouchyTickets/Attractions/AttractionType.cs | 151 +- TouchyTickets/Content/Contentless.json | 26 +- TouchyTickets/GameImpl.cs | 266 ++-- TouchyTickets/Localization.cs | 73 +- TouchyTickets/Options.cs | 100 +- TouchyTickets/ParkMap.cs | 514 +++---- TouchyTickets/Platform.cs | 18 +- TouchyTickets/RainingTicket.cs | 47 +- TouchyTickets/SaveHandler.cs | 159 +- TouchyTickets/TouchyTickets.csproj | 10 +- TouchyTickets/Tutorial.cs | 112 +- TouchyTickets/Ui.cs | 1368 ++++++++--------- TouchyTickets/Upgrades/ModifierUpgrade.cs | 30 +- .../Upgrades/NeighborModifierUpgrade.cs | 34 +- TouchyTickets/Upgrades/Upgrade.cs | 105 +- iOS/Default.png | Bin 1553 -> 0 bytes iOS/Entitlements.plist | 6 - iOS/GameThumbnail.png | Bin 729 -> 0 bytes iOS/Info.plist | 37 - iOS/IosPlatform.cs | 32 - iOS/LaunchScreen.storyboard | 27 - iOS/Program.cs | 30 - iOS/Properties/AssemblyInfo.cs | 22 - iOS/iOS.csproj | 146 -- 38 files changed, 2001 insertions(+), 3398 deletions(-) create mode 100644 Android/.config/dotnet-tools.json rename Android/{Properties => }/AndroidManifest.xml (52%) delete mode 100644 Android/Properties/AssemblyInfo.cs delete mode 100644 Android/Resources/Resource.Designer.cs delete mode 100644 iOS/Default.png delete mode 100644 iOS/Entitlements.plist delete mode 100644 iOS/GameThumbnail.png delete mode 100644 iOS/Info.plist delete mode 100644 iOS/IosPlatform.cs delete mode 100644 iOS/LaunchScreen.storyboard delete mode 100644 iOS/Program.cs delete mode 100644 iOS/Properties/AssemblyInfo.cs delete mode 100644 iOS/iOS.csproj diff --git a/Android/.config/dotnet-tools.json b/Android/.config/dotnet-tools.json new file mode 100644 index 0000000..f6d45d3 --- /dev/null +++ b/Android/.config/dotnet-tools.json @@ -0,0 +1,36 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-mgcb": { + "version": "3.8.1.303", + "commands": [ + "mgcb" + ] + }, + "dotnet-mgcb-editor": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor" + ] + }, + "dotnet-mgcb-editor-linux": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-linux" + ] + }, + "dotnet-mgcb-editor-windows": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-windows" + ] + }, + "dotnet-mgcb-editor-mac": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-mac" + ] + } + } +} \ No newline at end of file diff --git a/Android/Activity1.cs b/Android/Activity1.cs index cb97207..11a6641 100644 --- a/Android/Activity1.cs +++ b/Android/Activity1.cs @@ -1,95 +1,95 @@ -using System; using Android.App; using Android.Content; using Android.Content.PM; -using Android.Gms.Common; using Android.Gms.Games; using Android.OS; using Android.Views; using Android.Widget; using GameAnalyticsSDK; +using Java.Lang; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using MLEM.Extensions; using MLEM.Misc; using TouchyTickets; -using static Android.Views.SystemUiFlags; -using static Android.Views.ViewGroup.LayoutParams; +using static Android.Views.ViewGroup; using Uri = Android.Net.Uri; -namespace Android { - [Activity( - Label = "@string/app_name", - MainLauncher = true, - Icon = "@drawable/icon", - AlwaysRetainTaskState = true, - LaunchMode = LaunchMode.SingleInstance, - ScreenOrientation = ScreenOrientation.UserPortrait, - ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize - )] - public class Activity1 : AndroidGameActivity { +namespace Android; - private GameImpl game; - private AndroidPlatform platform; - private LinearLayout mainView; +[Activity( + Label = "@string/app_name", + MainLauncher = true, + Icon = "@drawable/icon", + AlwaysRetainTaskState = true, + LaunchMode = LaunchMode.SingleInstance, + ScreenOrientation = ScreenOrientation.UserPortrait, + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize +)] +public class Activity1 : AndroidGameActivity { - protected override void OnCreate(Bundle bundle) { - base.OnCreate(bundle); + private GameImpl game; + private AndroidPlatform platform; + private LinearLayout mainView; - // ad layout - var adLayout = new LinearLayout(this) {Orientation = Orientation.Vertical}; - adLayout.SetGravity(GravityFlags.Bottom); + protected override void OnCreate(Bundle bundle) { + base.OnCreate(bundle); - // set up the game - MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l)))); - this.platform = new AndroidPlatform(this, adLayout); - this.game = new GameImpl(this.platform); - this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window); - this.game.GraphicsDeviceManager.IsFullScreen = true; - this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false; + // ad layout + var adLayout = new LinearLayout(this) {Orientation = Orientation.Vertical}; + adLayout.SetGravity(GravityFlags.Bottom); - var gameView = this.game.Services.GetService(typeof(View)) as View; - gameView.LayoutChange += (o, args) => { - // force the game size to update when the ad size changes - this.game.GraphicsDeviceManager.PreferredBackBufferWidth = args.Right - args.Left; - this.game.GraphicsDeviceManager.PreferredBackBufferHeight = args.Bottom - args.Top; - this.game.GraphicsDeviceManager.ApplyChanges(); - }; + // set up the game + MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l)))); + this.platform = new AndroidPlatform(this, adLayout); + this.game = new GameImpl(this.platform); + this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window); + this.game.GraphicsDeviceManager.IsFullScreen = true; + this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false; - // don't render under notches - if (Build.VERSION.SdkInt >= BuildVersionCodes.P) - this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Never; + var gameView = this.game.Services.GetService(typeof(View)) as View; + gameView.LayoutChange += (_, args) => { + // force the game size to update when the ad size changes + this.game.GraphicsDeviceManager.PreferredBackBufferWidth = args.Right - args.Left; + this.game.GraphicsDeviceManager.PreferredBackBufferHeight = args.Bottom - args.Top; + this.game.GraphicsDeviceManager.ApplyChanges(); + }; - // total layout that is displayed - this.mainView = new LinearLayout(this) {Orientation = Orientation.Vertical}; - this.mainView.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, MatchParent); - this.mainView.AddView(gameView); - // height of 0 but high weight causes this element so scale based on the ad's height - gameView.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, 0, 1); - this.mainView.AddView(adLayout); - adLayout.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, WrapContent); - this.SetContentView(this.mainView); + // don't render under notches + if (Build.VERSION.SdkInt >= BuildVersionCodes.P) + this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Never; - this.game.Run(); - } - - protected override void OnActivityResult(int requestCode, Result result, Intent data) { - base.OnActivityResult(requestCode, result, data); - // Connect again after logging in to Google Play game services, but only if we haven't tried yet - try { - if (requestCode == AndroidPlatform.GooglePlayLoginRequest && (int) result != GamesActivityResultCodes.ResultSignInFailed) - this.platform.GoogleApi.Connect(); - } catch (Exception e) { - GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "OnActivityResult " + e); - } - } - - public override void OnWindowFocusChanged(bool hasFocus) { - base.OnWindowFocusChanged(hasFocus); - // hide the status bar - if (hasFocus) - this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (ImmersiveSticky | LayoutStable | LayoutHideNavigation | LayoutFullscreen | HideNavigation | Fullscreen); - } + // total layout that is displayed + this.mainView = new LinearLayout(this) {Orientation = Orientation.Vertical}; + this.mainView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent); + this.mainView.AddView(gameView); + // height of 0 but high weight causes this element so scale based on the ad's height + gameView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, 0, 1); + this.mainView.AddView(adLayout); + adLayout.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.WrapContent); + this.SetContentView(this.mainView); + this.game.Run(); } + + protected override void OnActivityResult(int requestCode, Result result, Intent data) { + base.OnActivityResult(requestCode, result, data); + // Connect again after logging in to Google Play game services, but only if we haven't tried yet + try { + if (requestCode == AndroidPlatform.GooglePlayLoginRequest && (int) result != GamesActivityResultCodes.ResultSignInFailed) + this.platform.GoogleApi.Connect(); + } catch (Exception e) { + GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "OnActivityResult " + e); + } + } + + public override void OnWindowFocusChanged(bool hasFocus) { + base.OnWindowFocusChanged(hasFocus); +#pragma warning disable CS0618 + // hide the status bar + if (hasFocus) + this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (SystemUiFlags.ImmersiveSticky | SystemUiFlags.LayoutStable | SystemUiFlags.LayoutHideNavigation | SystemUiFlags.LayoutFullscreen | SystemUiFlags.HideNavigation | SystemUiFlags.Fullscreen); +#pragma warning restore CS0618 + } + } \ No newline at end of file diff --git a/Android/Android.csproj b/Android/Android.csproj index 4bdaa8a..55262c2 100644 --- a/Android/Android.csproj +++ b/Android/Android.csproj @@ -1,94 +1,31 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {410C0262-131C-4D0E-910D-D01B4F7143E0} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - Android - Android - 512 - true - Resources\Resource.Designer.cs - Resource - Off - .m4a - v9.0 - Properties\AndroidManifest.xml - false - Resources - Assets - true - Xamarin.Android.Net.AndroidClientHandler - - - true - full - false - bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ - DEBUG;TRACE;ANDROID - prompt - 4 - True - None - - - pdbonly - true - bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ - TRACE;ANDROID - prompt - 4 - False - SdkOnly - false - false - false - false - - aab - true - false + net6.0-android + 31 + Exe + de.ellpeck.touchytickets + 1.2.1 + - - - - + + + + + + + + - - - - + + + Content/%(RecursiveDir)%(Filename)%(Extension) + - - - - - - - - - Content\Content.mgcb - - - - - - - - - - - - {3df7ae69-f3f0-461a-be98-f31eb576b5e2} - TouchyTickets - - - + + + + + \ No newline at end of file diff --git a/Android/Properties/AndroidManifest.xml b/Android/AndroidManifest.xml similarity index 52% rename from Android/Properties/AndroidManifest.xml rename to Android/AndroidManifest.xml index ebf89f5..7279f63 100644 --- a/Android/Properties/AndroidManifest.xml +++ b/Android/AndroidManifest.xml @@ -1,9 +1,10 @@  - - + + + - + diff --git a/Android/AndroidPlatform.cs b/Android/AndroidPlatform.cs index c56f822..739e6ab 100644 --- a/Android/AndroidPlatform.cs +++ b/Android/AndroidPlatform.cs @@ -1,149 +1,145 @@ -using System; using System.Collections; using System.Collections.Generic; using Android.App; using Android.Content; -using Android.Gms.Common; using Android.Gms.Common.Apis; -using Android.Gms.Extensions; using Android.Gms.Games; -using Android.Gms.Games.Achievement; using Android.Runtime; using Android.Views; using Android.Widget; -using Coroutine; using GameAnalyticsSDK; using GameAnalyticsSDK.Utilities; +using Java.Lang; using TouchyTickets; using Achievement = TouchyTickets.Achievement; using Uri = Android.Net.Uri; -namespace Android { - public class AndroidPlatform : Platform { +namespace Android; - public const int GooglePlayLoginRequest = 9001; - public const int ShowAchievementsRequest = 9002; +public class AndroidPlatform : Platform { - private static readonly Dictionary AchievementIds = new Dictionary { - {"1Stars", "CgkI_Lyp7PcEEAIQAw"}, - {"10Stars", "CgkI_Lyp7PcEEAIQBA"}, - {"100Stars", "CgkI_Lyp7PcEEAIQBQ"}, - {"FullMap", "CgkI_Lyp7PcEEAIQBg"}, - {"OnlySmallRides", "CgkI_Lyp7PcEEAIQBw"}, - {"OnlyRelaxedRides", "CgkI_Lyp7PcEEAIQCA"}, - {"OnlyWalkingRides", "CgkI_Lyp7PcEEAIQCQ"}, - {"OnlyNonTechnologyRides", "CgkI_Lyp7PcEEAIQCg"}, - {"100Modifiers", "CgkI_Lyp7PcEEAIQCw"}, - {"500Modifiers", "CgkI_Lyp7PcEEAIQDA"}, - {"1000Modifiers", "CgkI_Lyp7PcEEAIQDQ"}, - {"5000Modifiers", "CgkI_Lyp7PcEEAIQDg"}, - {"1ExpTickets", "CgkI_Lyp7PcEEAIQDw"}, - {"2ExpTickets", "CgkI_Lyp7PcEEAIQEA"}, - {"3ExpTickets", "CgkI_Lyp7PcEEAIQEQ"}, - {"4ExpTickets", "CgkI_Lyp7PcEEAIQEg"}, - {"5ExpTickets", "CgkI_Lyp7PcEEAIQEw"}, - {"6ExpTickets", "CgkI_Lyp7PcEEAIQFA"}, - {"7ExpTickets", "CgkI_Lyp7PcEEAIQFQ"}, - {"8ExpTickets", "CgkI_Lyp7PcEEAIQFg"}, - {"9ExpTickets", "CgkI_Lyp7PcEEAIQFw"}, - {"10ExpTickets", "CgkI_Lyp7PcEEAIQGA"} - }; + public const int GooglePlayLoginRequest = 9001; + public const int ShowAchievementsRequest = 9002; - private readonly Activity activity; - private readonly LinearLayout adLayout; - public GoogleApiClient GoogleApi { get; private set; } + private static readonly Dictionary AchievementIds = new() { + {"1Stars", "CgkI_Lyp7PcEEAIQAw"}, + {"10Stars", "CgkI_Lyp7PcEEAIQBA"}, + {"100Stars", "CgkI_Lyp7PcEEAIQBQ"}, + {"FullMap", "CgkI_Lyp7PcEEAIQBg"}, + {"OnlySmallRides", "CgkI_Lyp7PcEEAIQBw"}, + {"OnlyRelaxedRides", "CgkI_Lyp7PcEEAIQCA"}, + {"OnlyWalkingRides", "CgkI_Lyp7PcEEAIQCQ"}, + {"OnlyNonTechnologyRides", "CgkI_Lyp7PcEEAIQCg"}, + {"100Modifiers", "CgkI_Lyp7PcEEAIQCw"}, + {"500Modifiers", "CgkI_Lyp7PcEEAIQDA"}, + {"1000Modifiers", "CgkI_Lyp7PcEEAIQDQ"}, + {"5000Modifiers", "CgkI_Lyp7PcEEAIQDg"}, + {"1ExpTickets", "CgkI_Lyp7PcEEAIQDw"}, + {"2ExpTickets", "CgkI_Lyp7PcEEAIQEA"}, + {"3ExpTickets", "CgkI_Lyp7PcEEAIQEQ"}, + {"4ExpTickets", "CgkI_Lyp7PcEEAIQEg"}, + {"5ExpTickets", "CgkI_Lyp7PcEEAIQEw"}, + {"6ExpTickets", "CgkI_Lyp7PcEEAIQFA"}, + {"7ExpTickets", "CgkI_Lyp7PcEEAIQFQ"}, + {"8ExpTickets", "CgkI_Lyp7PcEEAIQFg"}, + {"9ExpTickets", "CgkI_Lyp7PcEEAIQFw"}, + {"10ExpTickets", "CgkI_Lyp7PcEEAIQGA"} + }; - public AndroidPlatform(Activity activity, LinearLayout adLayout) { - this.activity = activity; - this.adLayout = adLayout; - } - - public override void SetupOnlineInteractions(Dictionary analyticsJson) { - // Analytics - GameAnalytics.SetAutoDetectAppVersion(true); - GameAnalytics.Initialize(this.activity, GA_MiniJSON.Serialize(new Hashtable(analyticsJson))); - AndroidEnvironment.UnhandledExceptionRaiser += (o, args) => GameAnalytics.NewErrorEvent(GAErrorSeverity.Critical, args.Exception.ToString()); - - // TODO fix ads - // Ads - /*try { - var ad = new AdView(this.activity) { - AdUnitId = "ca-app-pub-5754829579653773/7841535920", - AdSize = AdSize.SmartBanner - }; - ad.LoadAd(new AdRequest.Builder() - .AddTestDevice("14B965C6457E17D2808061ADF7E34923") - .Build()); - this.adLayout.AddView(ad); - } catch (Exception e) { - GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "Ads " + e); - }*/ - - // TODO fix google play game services - /*// Google Play game services - try { - this.GoogleApi = new GoogleApiClient.Builder(this.activity) - .AddApi(GamesClass.API) - .AddScope(GamesClass.ScopeGames) - .AddOnConnectionFailedListener(res => { - if (res.HasResolution) { - res.StartResolutionForResult(this.activity, GooglePlayLoginRequest); - } else { - throw new GoogleApiClientConnectionException(res); - } - }) - .Build(); - this.GoogleApi.Connect(); - } catch (Exception e) { - GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GoogleApiClient " + e); - }*/ - - #if DEBUG - // Sanity check to ensure that all achievements are mapped - foreach (var achievement in Achievement.Achievements.Values) { - var _ = AchievementIds[achievement.Name]; - } - #endif - } - - public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) { - GameAnalytics.NewResourceEvent(sink ? GAResourceFlowType.Sink : GAResourceFlowType.Source, currency, amount, itemType, itemId); - } - - public override void SetKeepScreenOn(bool keep) { - if (keep) { - this.activity.Window.AddFlags(WindowManagerFlags.KeepScreenOn); - } else { - this.activity.Window.ClearFlags(WindowManagerFlags.KeepScreenOn); - } - } - - public override void OpenRateLink() { - this.activity.StartActivity(new Intent(Intent.ActionView, Uri.Parse("https://play.google.com/store/apps/details?id=de.ellpeck.touchytickets"))); - } - - public override bool GainAchievement(Achievement achievement) { - try { - if (this.GoogleApi != null && this.GoogleApi.IsConnected) { - GamesClass.Achievements.Unlock(this.GoogleApi, AchievementIds[achievement.Name]); - return true; - } - } catch (Exception e) { - GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GainAchievement " + e); - } - return false; - } - - public override void ShowAchievements() { - try { - if (this.GoogleApi == null || !this.GoogleApi.IsConnected) - return; - var intent = GamesClass.Achievements.GetAchievementsIntent(this.GoogleApi); - this.activity.StartActivityForResult(intent, ShowAchievementsRequest); - } catch (Exception e) { - GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "ShowAchievements " + e); - } - } + private readonly Activity activity; + private readonly LinearLayout adLayout; + public GoogleApiClient GoogleApi { get; private set; } + public AndroidPlatform(Activity activity, LinearLayout adLayout) { + this.activity = activity; + this.adLayout = adLayout; } + + public override void SetupOnlineInteractions(Dictionary analyticsJson) { + // Analytics + GameAnalytics.SetAutoDetectAppVersion(true); + GameAnalytics.Initialize(this.activity, GA_MiniJSON.Serialize(new Hashtable(analyticsJson))); + AndroidEnvironment.UnhandledExceptionRaiser += (_, args) => GameAnalytics.NewErrorEvent(GAErrorSeverity.Critical, args.Exception.ToString()); + + // TODO fix ads + // Ads + /*try { + var ad = new AdView(this.activity) { + AdUnitId = "ca-app-pub-5754829579653773/7841535920", + AdSize = AdSize.SmartBanner + }; + ad.LoadAd(new AdRequest.Builder() + .AddTestDevice("14B965C6457E17D2808061ADF7E34923") + .Build()); + this.adLayout.AddView(ad); + } catch (Exception e) { + GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "Ads " + e); + }*/ + + // TODO fix google play game services + /*// Google Play game services + try { + this.GoogleApi = new GoogleApiClient.Builder(this.activity) + .AddApi(GamesClass.API) + .AddScope(GamesClass.ScopeGames) + .AddOnConnectionFailedListener(res => { + if (res.HasResolution) { + res.StartResolutionForResult(this.activity, GooglePlayLoginRequest); + } else { + throw new GoogleApiClientConnectionException(res); + } + }) + .Build(); + this.GoogleApi.Connect(); + } catch (Exception e) { + GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GoogleApiClient " + e); + }*/ + +#if DEBUG + // Sanity check to ensure that all achievements are mapped + foreach (var achievement in Achievement.Achievements.Values) { + var _ = AndroidPlatform.AchievementIds[achievement.Name]; + } +#endif + } + + public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) { + GameAnalytics.NewResourceEvent(sink ? GAResourceFlowType.Sink : GAResourceFlowType.Source, currency, amount, itemType, itemId); + } + + public override void SetKeepScreenOn(bool keep) { + if (keep) { + this.activity.Window.AddFlags(WindowManagerFlags.KeepScreenOn); + } else { + this.activity.Window.ClearFlags(WindowManagerFlags.KeepScreenOn); + } + } + + public override void OpenRateLink() { + this.activity.StartActivity(new Intent(Intent.ActionView, Uri.Parse("https://play.google.com/store/apps/details?id=de.ellpeck.touchytickets"))); + } + + public override bool GainAchievement(Achievement achievement) { + try { + if (this.GoogleApi != null && this.GoogleApi.IsConnected) { + GamesClass.Achievements.Unlock(this.GoogleApi, AndroidPlatform.AchievementIds[achievement.Name]); + return true; + } + } catch (Exception e) { + GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GainAchievement " + e); + } + return false; + } + + public override void ShowAchievements() { + try { + if (this.GoogleApi == null || !this.GoogleApi.IsConnected) + return; + var intent = GamesClass.Achievements.GetAchievementsIntent(this.GoogleApi); + this.activity.StartActivityForResult(intent, AndroidPlatform.ShowAchievementsRequest); + } catch (Exception e) { + GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "ShowAchievements " + e); + } + } + } \ No newline at end of file diff --git a/Android/Properties/AssemblyInfo.cs b/Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 241daad..0000000 --- a/Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Touchy Tickets")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Touchy Tickets")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Android/Resources/Resource.Designer.cs b/Android/Resources/Resource.Designer.cs deleted file mode 100644 index 980f301..0000000 --- a/Android/Resources/Resource.Designer.cs +++ /dev/null @@ -1,1037 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("Android.Resource", IsApplication=true)] - -namespace Android -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - global::GameAnalyticsSDK.Resource.String.app_name = global::Android.Resource.String.app_name; - global::GameAnalyticsSDK.Resource.String.library_name = global::Android.Resource.String.library_name; - } - - public partial class Animation - { - - // aapt resource value: 0x7F010000 - public const int fragment_fast_out_extra_slow_in = 2130771968; - - static Animation() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animation() - { - } - } - - public partial class Animator - { - - // aapt resource value: 0x7F020000 - public const int fragment_close_enter = 2130837504; - - // aapt resource value: 0x7F020001 - public const int fragment_close_exit = 2130837505; - - // aapt resource value: 0x7F020002 - public const int fragment_fade_enter = 2130837506; - - // aapt resource value: 0x7F020003 - public const int fragment_fade_exit = 2130837507; - - // aapt resource value: 0x7F020004 - public const int fragment_open_enter = 2130837508; - - // aapt resource value: 0x7F020005 - public const int fragment_open_exit = 2130837509; - - static Animator() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animator() - { - } - } - - public partial class Attribute - { - - // aapt resource value: 0x7F030000 - public const int alpha = 2130903040; - - // aapt resource value: 0x7F030001 - public const int buttonSize = 2130903041; - - // aapt resource value: 0x7F030002 - public const int circleCrop = 2130903042; - - // aapt resource value: 0x7F030003 - public const int colorScheme = 2130903043; - - // aapt resource value: 0x7F030004 - public const int font = 2130903044; - - // aapt resource value: 0x7F030005 - public const int fontProviderAuthority = 2130903045; - - // aapt resource value: 0x7F030006 - public const int fontProviderCerts = 2130903046; - - // aapt resource value: 0x7F030007 - public const int fontProviderFetchStrategy = 2130903047; - - // aapt resource value: 0x7F030008 - public const int fontProviderFetchTimeout = 2130903048; - - // aapt resource value: 0x7F030009 - public const int fontProviderPackage = 2130903049; - - // aapt resource value: 0x7F03000A - public const int fontProviderQuery = 2130903050; - - // aapt resource value: 0x7F03000B - public const int fontStyle = 2130903051; - - // aapt resource value: 0x7F03000C - public const int fontVariationSettings = 2130903052; - - // aapt resource value: 0x7F03000D - public const int fontWeight = 2130903053; - - // aapt resource value: 0x7F03000E - public const int imageAspectRatio = 2130903054; - - // aapt resource value: 0x7F03000F - public const int imageAspectRatioAdjust = 2130903055; - - // aapt resource value: 0x7F030010 - public const int scopeUris = 2130903056; - - // aapt resource value: 0x7F030011 - public const int ttcIndex = 2130903057; - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Color - { - - // aapt resource value: 0x7F040000 - public const int androidx_core_ripple_material_light = 2130968576; - - // aapt resource value: 0x7F040001 - public const int androidx_core_secondary_text_default_material_light = 2130968577; - - // aapt resource value: 0x7F040002 - public const int common_google_signin_btn_text_dark = 2130968578; - - // aapt resource value: 0x7F040003 - public const int common_google_signin_btn_text_dark_default = 2130968579; - - // aapt resource value: 0x7F040004 - public const int common_google_signin_btn_text_dark_disabled = 2130968580; - - // aapt resource value: 0x7F040005 - public const int common_google_signin_btn_text_dark_focused = 2130968581; - - // aapt resource value: 0x7F040006 - public const int common_google_signin_btn_text_dark_pressed = 2130968582; - - // aapt resource value: 0x7F040007 - public const int common_google_signin_btn_text_light = 2130968583; - - // aapt resource value: 0x7F040008 - public const int common_google_signin_btn_text_light_default = 2130968584; - - // aapt resource value: 0x7F040009 - public const int common_google_signin_btn_text_light_disabled = 2130968585; - - // aapt resource value: 0x7F04000A - public const int common_google_signin_btn_text_light_focused = 2130968586; - - // aapt resource value: 0x7F04000B - public const int common_google_signin_btn_text_light_pressed = 2130968587; - - // aapt resource value: 0x7F04000C - public const int common_google_signin_btn_tint = 2130968588; - - // aapt resource value: 0x7F04000D - public const int notification_action_color_filter = 2130968589; - - // aapt resource value: 0x7F04000E - public const int notification_icon_bg_color = 2130968590; - - static Color() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Color() - { - } - } - - public partial class Dimension - { - - // aapt resource value: 0x7F050000 - public const int compat_button_inset_horizontal_material = 2131034112; - - // aapt resource value: 0x7F050001 - public const int compat_button_inset_vertical_material = 2131034113; - - // aapt resource value: 0x7F050002 - public const int compat_button_padding_horizontal_material = 2131034114; - - // aapt resource value: 0x7F050003 - public const int compat_button_padding_vertical_material = 2131034115; - - // aapt resource value: 0x7F050004 - public const int compat_control_corner_material = 2131034116; - - // aapt resource value: 0x7F050005 - public const int compat_notification_large_icon_max_height = 2131034117; - - // aapt resource value: 0x7F050006 - public const int compat_notification_large_icon_max_width = 2131034118; - - // aapt resource value: 0x7F050007 - public const int notification_action_icon_size = 2131034119; - - // aapt resource value: 0x7F050008 - public const int notification_action_text_size = 2131034120; - - // aapt resource value: 0x7F050009 - public const int notification_big_circle_margin = 2131034121; - - // aapt resource value: 0x7F05000A - public const int notification_content_margin_start = 2131034122; - - // aapt resource value: 0x7F05000B - public const int notification_large_icon_height = 2131034123; - - // aapt resource value: 0x7F05000C - public const int notification_large_icon_width = 2131034124; - - // aapt resource value: 0x7F05000D - public const int notification_main_column_padding_top = 2131034125; - - // aapt resource value: 0x7F05000E - public const int notification_media_narrow_margin = 2131034126; - - // aapt resource value: 0x7F05000F - public const int notification_right_icon_size = 2131034127; - - // aapt resource value: 0x7F050010 - public const int notification_right_side_padding_top = 2131034128; - - // aapt resource value: 0x7F050011 - public const int notification_small_icon_background_padding = 2131034129; - - // aapt resource value: 0x7F050012 - public const int notification_small_icon_size_as_large = 2131034130; - - // aapt resource value: 0x7F050013 - public const int notification_subtext_size = 2131034131; - - // aapt resource value: 0x7F050014 - public const int notification_top_pad = 2131034132; - - // aapt resource value: 0x7F050015 - public const int notification_top_pad_large_text = 2131034133; - - static Dimension() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Dimension() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7F060000 - public const int common_full_open_on_phone = 2131099648; - - // aapt resource value: 0x7F060001 - public const int common_google_signin_btn_icon_dark = 2131099649; - - // aapt resource value: 0x7F060002 - public const int common_google_signin_btn_icon_dark_focused = 2131099650; - - // aapt resource value: 0x7F060003 - public const int common_google_signin_btn_icon_dark_normal = 2131099651; - - // aapt resource value: 0x7F060004 - public const int common_google_signin_btn_icon_dark_normal_background = 2131099652; - - // aapt resource value: 0x7F060005 - public const int common_google_signin_btn_icon_disabled = 2131099653; - - // aapt resource value: 0x7F060006 - public const int common_google_signin_btn_icon_light = 2131099654; - - // aapt resource value: 0x7F060007 - public const int common_google_signin_btn_icon_light_focused = 2131099655; - - // aapt resource value: 0x7F060008 - public const int common_google_signin_btn_icon_light_normal = 2131099656; - - // aapt resource value: 0x7F060009 - public const int common_google_signin_btn_icon_light_normal_background = 2131099657; - - // aapt resource value: 0x7F06000A - public const int common_google_signin_btn_text_dark = 2131099658; - - // aapt resource value: 0x7F06000B - public const int common_google_signin_btn_text_dark_focused = 2131099659; - - // aapt resource value: 0x7F06000C - public const int common_google_signin_btn_text_dark_normal = 2131099660; - - // aapt resource value: 0x7F06000D - public const int common_google_signin_btn_text_dark_normal_background = 2131099661; - - // aapt resource value: 0x7F06000E - public const int common_google_signin_btn_text_disabled = 2131099662; - - // aapt resource value: 0x7F06000F - public const int common_google_signin_btn_text_light = 2131099663; - - // aapt resource value: 0x7F060010 - public const int common_google_signin_btn_text_light_focused = 2131099664; - - // aapt resource value: 0x7F060011 - public const int common_google_signin_btn_text_light_normal = 2131099665; - - // aapt resource value: 0x7F060012 - public const int common_google_signin_btn_text_light_normal_background = 2131099666; - - // aapt resource value: 0x7F060013 - public const int googleg_disabled_color_18 = 2131099667; - - // aapt resource value: 0x7F060014 - public const int googleg_standard_color_18 = 2131099668; - - // aapt resource value: 0x7F060015 - public const int Icon = 2131099669; - - // aapt resource value: 0x7F060016 - public const int notification_action_background = 2131099670; - - // aapt resource value: 0x7F060017 - public const int notification_bg = 2131099671; - - // aapt resource value: 0x7F060018 - public const int notification_bg_low = 2131099672; - - // aapt resource value: 0x7F060019 - public const int notification_bg_low_normal = 2131099673; - - // aapt resource value: 0x7F06001A - public const int notification_bg_low_pressed = 2131099674; - - // aapt resource value: 0x7F06001B - public const int notification_bg_normal = 2131099675; - - // aapt resource value: 0x7F06001C - public const int notification_bg_normal_pressed = 2131099676; - - // aapt resource value: 0x7F06001D - public const int notification_icon_background = 2131099677; - - // aapt resource value: 0x7F06001E - public const int notification_template_icon_bg = 2131099678; - - // aapt resource value: 0x7F06001F - public const int notification_template_icon_low_bg = 2131099679; - - // aapt resource value: 0x7F060020 - public const int notification_tile_bg = 2131099680; - - // aapt resource value: 0x7F060021 - public const int notify_panel_notification_icon_bg = 2131099681; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7F070000 - public const int accessibility_action_clickable_span = 2131165184; - - // aapt resource value: 0x7F070001 - public const int accessibility_custom_action_0 = 2131165185; - - // aapt resource value: 0x7F070002 - public const int accessibility_custom_action_1 = 2131165186; - - // aapt resource value: 0x7F070003 - public const int accessibility_custom_action_10 = 2131165187; - - // aapt resource value: 0x7F070004 - public const int accessibility_custom_action_11 = 2131165188; - - // aapt resource value: 0x7F070005 - public const int accessibility_custom_action_12 = 2131165189; - - // aapt resource value: 0x7F070006 - public const int accessibility_custom_action_13 = 2131165190; - - // aapt resource value: 0x7F070007 - public const int accessibility_custom_action_14 = 2131165191; - - // aapt resource value: 0x7F070008 - public const int accessibility_custom_action_15 = 2131165192; - - // aapt resource value: 0x7F070009 - public const int accessibility_custom_action_16 = 2131165193; - - // aapt resource value: 0x7F07000A - public const int accessibility_custom_action_17 = 2131165194; - - // aapt resource value: 0x7F07000B - public const int accessibility_custom_action_18 = 2131165195; - - // aapt resource value: 0x7F07000C - public const int accessibility_custom_action_19 = 2131165196; - - // aapt resource value: 0x7F07000D - public const int accessibility_custom_action_2 = 2131165197; - - // aapt resource value: 0x7F07000E - public const int accessibility_custom_action_20 = 2131165198; - - // aapt resource value: 0x7F07000F - public const int accessibility_custom_action_21 = 2131165199; - - // aapt resource value: 0x7F070010 - public const int accessibility_custom_action_22 = 2131165200; - - // aapt resource value: 0x7F070011 - public const int accessibility_custom_action_23 = 2131165201; - - // aapt resource value: 0x7F070012 - public const int accessibility_custom_action_24 = 2131165202; - - // aapt resource value: 0x7F070013 - public const int accessibility_custom_action_25 = 2131165203; - - // aapt resource value: 0x7F070014 - public const int accessibility_custom_action_26 = 2131165204; - - // aapt resource value: 0x7F070015 - public const int accessibility_custom_action_27 = 2131165205; - - // aapt resource value: 0x7F070016 - public const int accessibility_custom_action_28 = 2131165206; - - // aapt resource value: 0x7F070017 - public const int accessibility_custom_action_29 = 2131165207; - - // aapt resource value: 0x7F070018 - public const int accessibility_custom_action_3 = 2131165208; - - // aapt resource value: 0x7F070019 - public const int accessibility_custom_action_30 = 2131165209; - - // aapt resource value: 0x7F07001A - public const int accessibility_custom_action_31 = 2131165210; - - // aapt resource value: 0x7F07001B - public const int accessibility_custom_action_4 = 2131165211; - - // aapt resource value: 0x7F07001C - public const int accessibility_custom_action_5 = 2131165212; - - // aapt resource value: 0x7F07001D - public const int accessibility_custom_action_6 = 2131165213; - - // aapt resource value: 0x7F07001E - public const int accessibility_custom_action_7 = 2131165214; - - // aapt resource value: 0x7F07001F - public const int accessibility_custom_action_8 = 2131165215; - - // aapt resource value: 0x7F070020 - public const int accessibility_custom_action_9 = 2131165216; - - // aapt resource value: 0x7F070025 - public const int actions = 2131165221; - - // aapt resource value: 0x7F070021 - public const int action_container = 2131165217; - - // aapt resource value: 0x7F070022 - public const int action_divider = 2131165218; - - // aapt resource value: 0x7F070023 - public const int action_image = 2131165219; - - // aapt resource value: 0x7F070024 - public const int action_text = 2131165220; - - // aapt resource value: 0x7F070026 - public const int adjust_height = 2131165222; - - // aapt resource value: 0x7F070027 - public const int adjust_width = 2131165223; - - // aapt resource value: 0x7F070028 - public const int async = 2131165224; - - // aapt resource value: 0x7F070029 - public const int auto = 2131165225; - - // aapt resource value: 0x7F07002A - public const int blocking = 2131165226; - - // aapt resource value: 0x7F07002B - public const int chronometer = 2131165227; - - // aapt resource value: 0x7F07002C - public const int dark = 2131165228; - - // aapt resource value: 0x7F07002D - public const int dialog_button = 2131165229; - - // aapt resource value: 0x7F07002E - public const int forever = 2131165230; - - // aapt resource value: 0x7F07002F - public const int fragment_container_view_tag = 2131165231; - - // aapt resource value: 0x7F070030 - public const int icon = 2131165232; - - // aapt resource value: 0x7F070031 - public const int icon_group = 2131165233; - - // aapt resource value: 0x7F070032 - public const int icon_only = 2131165234; - - // aapt resource value: 0x7F070033 - public const int info = 2131165235; - - // aapt resource value: 0x7F070034 - public const int italic = 2131165236; - - // aapt resource value: 0x7F070035 - public const int light = 2131165237; - - // aapt resource value: 0x7F070036 - public const int line1 = 2131165238; - - // aapt resource value: 0x7F070037 - public const int line3 = 2131165239; - - // aapt resource value: 0x7F070038 - public const int none = 2131165240; - - // aapt resource value: 0x7F070039 - public const int normal = 2131165241; - - // aapt resource value: 0x7F07003A - public const int notification_background = 2131165242; - - // aapt resource value: 0x7F07003B - public const int notification_main_column = 2131165243; - - // aapt resource value: 0x7F07003C - public const int notification_main_column_container = 2131165244; - - // aapt resource value: 0x7F07003D - public const int right_icon = 2131165245; - - // aapt resource value: 0x7F07003E - public const int right_side = 2131165246; - - // aapt resource value: 0x7F07003F - public const int special_effects_controller_view_tag = 2131165247; - - // aapt resource value: 0x7F070040 - public const int standard = 2131165248; - - // aapt resource value: 0x7F070041 - public const int tag_accessibility_actions = 2131165249; - - // aapt resource value: 0x7F070042 - public const int tag_accessibility_clickable_spans = 2131165250; - - // aapt resource value: 0x7F070043 - public const int tag_accessibility_heading = 2131165251; - - // aapt resource value: 0x7F070044 - public const int tag_accessibility_pane_title = 2131165252; - - // aapt resource value: 0x7F070045 - public const int tag_screen_reader_focusable = 2131165253; - - // aapt resource value: 0x7F070046 - public const int tag_transition_group = 2131165254; - - // aapt resource value: 0x7F070047 - public const int tag_unhandled_key_event_manager = 2131165255; - - // aapt resource value: 0x7F070048 - public const int tag_unhandled_key_listeners = 2131165256; - - // aapt resource value: 0x7F070049 - public const int text = 2131165257; - - // aapt resource value: 0x7F07004A - public const int text2 = 2131165258; - - // aapt resource value: 0x7F07004B - public const int time = 2131165259; - - // aapt resource value: 0x7F07004C - public const int title = 2131165260; - - // aapt resource value: 0x7F07004D - public const int view_tree_lifecycle_owner = 2131165261; - - // aapt resource value: 0x7F07004E - public const int view_tree_saved_state_registry_owner = 2131165262; - - // aapt resource value: 0x7F07004F - public const int view_tree_view_model_store_owner = 2131165263; - - // aapt resource value: 0x7F070050 - public const int visible_removing_fragment_view_tag = 2131165264; - - // aapt resource value: 0x7F070051 - public const int wide = 2131165265; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Integer - { - - // aapt resource value: 0x7F080000 - public const int google_play_services_version = 2131230720; - - // aapt resource value: 0x7F080001 - public const int status_bar_notification_info_maxnum = 2131230721; - - static Integer() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Integer() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7F090000 - public const int custom_dialog = 2131296256; - - // aapt resource value: 0x7F090001 - public const int notification_action = 2131296257; - - // aapt resource value: 0x7F090002 - public const int notification_action_tombstone = 2131296258; - - // aapt resource value: 0x7F090003 - public const int notification_template_custom_big = 2131296259; - - // aapt resource value: 0x7F090004 - public const int notification_template_icon_group = 2131296260; - - // aapt resource value: 0x7F090005 - public const int notification_template_part_chronometer = 2131296261; - - // aapt resource value: 0x7F090006 - public const int notification_template_part_time = 2131296262; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7F0A0000 - public const int app_name = 2131361792; - - // aapt resource value: 0x7F0A0001 - public const int common_google_play_services_enable_button = 2131361793; - - // aapt resource value: 0x7F0A0002 - public const int common_google_play_services_enable_text = 2131361794; - - // aapt resource value: 0x7F0A0003 - public const int common_google_play_services_enable_title = 2131361795; - - // aapt resource value: 0x7F0A0004 - public const int common_google_play_services_install_button = 2131361796; - - // aapt resource value: 0x7F0A0005 - public const int common_google_play_services_install_text = 2131361797; - - // aapt resource value: 0x7F0A0006 - public const int common_google_play_services_install_title = 2131361798; - - // aapt resource value: 0x7F0A0007 - public const int common_google_play_services_notification_channel_name = 2131361799; - - // aapt resource value: 0x7F0A0008 - public const int common_google_play_services_notification_ticker = 2131361800; - - // aapt resource value: 0x7F0A0009 - public const int common_google_play_services_unknown_issue = 2131361801; - - // aapt resource value: 0x7F0A000A - public const int common_google_play_services_unsupported_text = 2131361802; - - // aapt resource value: 0x7F0A000B - public const int common_google_play_services_update_button = 2131361803; - - // aapt resource value: 0x7F0A000C - public const int common_google_play_services_update_text = 2131361804; - - // aapt resource value: 0x7F0A000D - public const int common_google_play_services_update_title = 2131361805; - - // aapt resource value: 0x7F0A000E - public const int common_google_play_services_updating_text = 2131361806; - - // aapt resource value: 0x7F0A000F - public const int common_google_play_services_wear_update_text = 2131361807; - - // aapt resource value: 0x7F0A0010 - public const int common_open_on_phone = 2131361808; - - // aapt resource value: 0x7F0A0011 - public const int common_signin_button_text = 2131361809; - - // aapt resource value: 0x7F0A0012 - public const int common_signin_button_text_long = 2131361810; - - // aapt resource value: 0x7F0A0013 - public const int library_name = 2131361811; - - // aapt resource value: 0x7F0A0014 - public const int status_bar_notification_info_overflow = 2131361812; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - - public partial class Style - { - - // aapt resource value: 0x7F0B0000 - public const int TextAppearance_Compat_Notification = 2131427328; - - // aapt resource value: 0x7F0B0001 - public const int TextAppearance_Compat_Notification_Info = 2131427329; - - // aapt resource value: 0x7F0B0002 - public const int TextAppearance_Compat_Notification_Line2 = 2131427330; - - // aapt resource value: 0x7F0B0003 - public const int TextAppearance_Compat_Notification_Time = 2131427331; - - // aapt resource value: 0x7F0B0004 - public const int TextAppearance_Compat_Notification_Title = 2131427332; - - // aapt resource value: 0x7F0B0005 - public const int Widget_Compat_NotificationActionContainer = 2131427333; - - // aapt resource value: 0x7F0B0006 - public const int Widget_Compat_NotificationActionText = 2131427334; - - static Style() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Style() - { - } - } - - public partial class Styleable - { - - // aapt resource value: { 0x10101A5,0x101031F,0x7F030000 } - public static int[] ColorStateListItem = new int[] { - 16843173, - 16843551, - 2130903040}; - - // aapt resource value: 2 - public const int ColorStateListItem_alpha = 2; - - // aapt resource value: 1 - public const int ColorStateListItem_android_alpha = 1; - - // aapt resource value: 0 - public const int ColorStateListItem_android_color = 0; - - // aapt resource value: { 0x7F030005,0x7F030006,0x7F030007,0x7F030008,0x7F030009,0x7F03000A } - public static int[] FontFamily = new int[] { - 2130903045, - 2130903046, - 2130903047, - 2130903048, - 2130903049, - 2130903050}; - - // aapt resource value: { 0x1010532,0x1010533,0x101053F,0x101056F,0x1010570,0x7F030004,0x7F03000B,0x7F03000C,0x7F03000D,0x7F030011 } - public static int[] FontFamilyFont = new int[] { - 16844082, - 16844083, - 16844095, - 16844143, - 16844144, - 2130903044, - 2130903051, - 2130903052, - 2130903053, - 2130903057}; - - // aapt resource value: 0 - public const int FontFamilyFont_android_font = 0; - - // aapt resource value: 2 - public const int FontFamilyFont_android_fontStyle = 2; - - // aapt resource value: 4 - public const int FontFamilyFont_android_fontVariationSettings = 4; - - // aapt resource value: 1 - public const int FontFamilyFont_android_fontWeight = 1; - - // aapt resource value: 3 - public const int FontFamilyFont_android_ttcIndex = 3; - - // aapt resource value: 5 - public const int FontFamilyFont_font = 5; - - // aapt resource value: 6 - public const int FontFamilyFont_fontStyle = 6; - - // aapt resource value: 7 - public const int FontFamilyFont_fontVariationSettings = 7; - - // aapt resource value: 8 - public const int FontFamilyFont_fontWeight = 8; - - // aapt resource value: 9 - public const int FontFamilyFont_ttcIndex = 9; - - // aapt resource value: 0 - public const int FontFamily_fontProviderAuthority = 0; - - // aapt resource value: 1 - public const int FontFamily_fontProviderCerts = 1; - - // aapt resource value: 2 - public const int FontFamily_fontProviderFetchStrategy = 2; - - // aapt resource value: 3 - public const int FontFamily_fontProviderFetchTimeout = 3; - - // aapt resource value: 4 - public const int FontFamily_fontProviderPackage = 4; - - // aapt resource value: 5 - public const int FontFamily_fontProviderQuery = 5; - - // aapt resource value: { 0x1010003,0x10100D0,0x10100D1 } - public static int[] Fragment = new int[] { - 16842755, - 16842960, - 16842961}; - - // aapt resource value: { 0x1010003,0x10100D1 } - public static int[] FragmentContainerView = new int[] { - 16842755, - 16842961}; - - // aapt resource value: 0 - public const int FragmentContainerView_android_name = 0; - - // aapt resource value: 1 - public const int FragmentContainerView_android_tag = 1; - - // aapt resource value: 1 - public const int Fragment_android_id = 1; - - // aapt resource value: 0 - public const int Fragment_android_name = 0; - - // aapt resource value: 2 - public const int Fragment_android_tag = 2; - - // aapt resource value: { 0x101019D,0x101019E,0x10101A1,0x10101A2,0x10101A3,0x10101A4,0x1010201,0x101020B,0x1010510,0x1010511,0x1010512,0x1010513 } - public static int[] GradientColor = new int[] { - 16843165, - 16843166, - 16843169, - 16843170, - 16843171, - 16843172, - 16843265, - 16843275, - 16844048, - 16844049, - 16844050, - 16844051}; - - // aapt resource value: { 0x10101A5,0x1010514 } - public static int[] GradientColorItem = new int[] { - 16843173, - 16844052}; - - // aapt resource value: 0 - public const int GradientColorItem_android_color = 0; - - // aapt resource value: 1 - public const int GradientColorItem_android_offset = 1; - - // aapt resource value: 7 - public const int GradientColor_android_centerColor = 7; - - // aapt resource value: 3 - public const int GradientColor_android_centerX = 3; - - // aapt resource value: 4 - public const int GradientColor_android_centerY = 4; - - // aapt resource value: 1 - public const int GradientColor_android_endColor = 1; - - // aapt resource value: 10 - public const int GradientColor_android_endX = 10; - - // aapt resource value: 11 - public const int GradientColor_android_endY = 11; - - // aapt resource value: 5 - public const int GradientColor_android_gradientRadius = 5; - - // aapt resource value: 0 - public const int GradientColor_android_startColor = 0; - - // aapt resource value: 8 - public const int GradientColor_android_startX = 8; - - // aapt resource value: 9 - public const int GradientColor_android_startY = 9; - - // aapt resource value: 6 - public const int GradientColor_android_tileMode = 6; - - // aapt resource value: 2 - public const int GradientColor_android_type = 2; - - // aapt resource value: { 0x7F030002,0x7F03000E,0x7F03000F } - public static int[] LoadingImageView = new int[] { - 2130903042, - 2130903054, - 2130903055}; - - // aapt resource value: 0 - public const int LoadingImageView_circleCrop = 0; - - // aapt resource value: 1 - public const int LoadingImageView_imageAspectRatio = 1; - - // aapt resource value: 2 - public const int LoadingImageView_imageAspectRatioAdjust = 2; - - // aapt resource value: { 0x7F030001,0x7F030003,0x7F030010 } - public static int[] SignInButton = new int[] { - 2130903041, - 2130903043, - 2130903056}; - - // aapt resource value: 0 - public const int SignInButton_buttonSize = 0; - - // aapt resource value: 1 - public const int SignInButton_colorScheme = 1; - - // aapt resource value: 2 - public const int SignInButton_scopeUris = 2; - - static Styleable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Styleable() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/Android/Resources/Values/Strings.xml b/Android/Resources/Values/Strings.xml index 3a2c2df..c82b833 100644 --- a/Android/Resources/Values/Strings.xml +++ b/Android/Resources/Values/Strings.xml @@ -1,4 +1,4 @@ - Touchy Tickets + Touchy Tickets diff --git a/TouchyTickets.sln b/TouchyTickets.sln index 7bb4601..05793b1 100644 --- a/TouchyTickets.sln +++ b/TouchyTickets.sln @@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchyTickets", "TouchyTick EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "Android\Android.csproj", "{410C0262-131C-4D0E-910D-D01B4F7143E0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "iOS\iOS.csproj", "{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,9 +18,5 @@ Global {410C0262-131C-4D0E-910D-D01B4F7143E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {410C0262-131C-4D0E-910D-D01B4F7143E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {410C0262-131C-4D0E-910D-D01B4F7143E0}.Release|Any CPU.Build.0 = Release|Any CPU - {CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator - {CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator - {CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator - {CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Release|Any CPU.Build.0 = Release|iPhoneSimulator EndGlobalSection EndGlobal diff --git a/TouchyTickets/Achievement.cs b/TouchyTickets/Achievement.cs index c97c85c..afd443e 100644 --- a/TouchyTickets/Achievement.cs +++ b/TouchyTickets/Achievement.cs @@ -5,56 +5,56 @@ using System.Numerics; using Microsoft.Xna.Framework; using TouchyTickets.Attractions; -namespace TouchyTickets { - public class Achievement { +namespace TouchyTickets; - public static readonly Dictionary Achievements = new Dictionary(); +public class Achievement { - static Achievement() { - foreach (var amount in new[] {1, 10, 100}) - Register(new Achievement($"{amount}Stars", g => g.Stars >= amount)); - Register(new Achievement("FullMap", g => { - for (var x = 0; x < g.Map.Width; x++) { - for (var y = 0; y < g.Map.Height; y++) { - if (g.Map.GetAttractionAt(new Point(x, y)) == null) - return false; - } + public static readonly Dictionary Achievements = new(); + + static Achievement() { + foreach (var amount in new[] {1, 10, 100}) + Achievement.Register(new Achievement($"{amount}Stars", g => g.Stars >= amount)); + Achievement.Register(new Achievement("FullMap", g => { + for (var x = 0; x < g.Map.Width; x++) { + for (var y = 0; y < g.Map.Height; y++) { + if (g.Map.GetAttractionAt(new Point(x, y)) == null) + return false; } - return true; - })); - foreach (var flag in new[] {AttractionFlags.Small, AttractionFlags.Relaxed, AttractionFlags.Walking, AttractionFlags.NonTechnology}) - Register(new Achievement($"Only{flag}Rides", g => g.Map.GetAttractionAmount(null) >= 100 && g.Map.GetAttractions().All(a => a.Item2.Type.Flags.HasFlag(flag)))); - foreach (var amount in new[] {100, 500, 1000, 5000}) - Register(new Achievement($"{amount}Modifiers", g => g.Map.GetAttractionAmount(null) > 0 && g.Map.GetAttractions().All(a => a.Item2.GetModifierAmount(null) >= amount))); - for (var i = 1; i <= 10; i++) { - var amount = BigInteger.Pow(1000, i + 1); - Register(new Achievement($"{i}ExpTickets", g => g.Tickets >= amount)); } + return true; + })); + foreach (var flag in new[] {AttractionFlags.Small, AttractionFlags.Relaxed, AttractionFlags.Walking, AttractionFlags.NonTechnology}) + Achievement.Register(new Achievement($"Only{flag}Rides", g => g.Map.GetAttractionAmount(null) >= 100 && g.Map.GetAttractions().All(a => a.Item2.Type.Flags.HasFlag(flag)))); + foreach (var amount in new[] {100, 500, 1000, 5000}) + Achievement.Register(new Achievement($"{amount}Modifiers", g => g.Map.GetAttractionAmount(null) > 0 && g.Map.GetAttractions().All(a => a.Item2.GetModifierAmount(null) >= amount))); + for (var i = 1; i <= 10; i++) { + var amount = BigInteger.Pow(1000, i + 1); + Achievement.Register(new Achievement($"{i}ExpTickets", g => g.Tickets >= amount)); } - - public readonly string Name; - private readonly Func condition; - // this value doesn't save between game runs, since achievements - // are only displayed inside of the respective stores anyway - private bool unlocked; - - public Achievement(string name, Func condition) { - this.Name = name; - this.condition = condition; - } - - public void Update() { - if (this.unlocked) - return; - if (!this.condition.Invoke(GameImpl.Instance)) - return; - if (GameImpl.Instance.Platform.GainAchievement(this)) - this.unlocked = true; - } - - public static void Register(Achievement achievement) { - Achievements.Add(achievement.Name, achievement); - } - } + + public readonly string Name; + private readonly Func condition; + // this value doesn't save between game runs, since achievements + // are only displayed inside of the respective stores anyway + private bool unlocked; + + public Achievement(string name, Func condition) { + this.Name = name; + this.condition = condition; + } + + public void Update() { + if (this.unlocked) + return; + if (!this.condition.Invoke(GameImpl.Instance)) + return; + if (GameImpl.Instance.Platform.GainAchievement(this)) + this.unlocked = true; + } + + public static void Register(Achievement achievement) { + Achievement.Achievements.Add(achievement.Name, achievement); + } + } \ No newline at end of file diff --git a/TouchyTickets/Assets.cs b/TouchyTickets/Assets.cs index b87322f..b175e3e 100644 --- a/TouchyTickets/Assets.cs +++ b/TouchyTickets/Assets.cs @@ -5,34 +5,34 @@ using MLEM.Font; using MLEM.Startup; using MLEM.Textures; -namespace TouchyTickets { - public static class Assets { +namespace TouchyTickets; - public static UniformTextureAtlas TilesTexture { get; private set; } - public static UniformTextureAtlas AttractionTexture { get; private set; } - public static UniformTextureAtlas UiTexture { get; private set; } +public static class Assets { - public static SoundEffect ClickSound { get; private set; } - public static SoundEffect PlaceSound { get; private set; } - public static SoundEffect BuySound { get; private set; } + public static UniformTextureAtlas TilesTexture { get; private set; } + public static UniformTextureAtlas AttractionTexture { get; private set; } + public static UniformTextureAtlas UiTexture { get; private set; } - public static Vector2 TileSize { get; private set; } - public static GenericFont Font { get; private set; } - public static GenericFont MonospacedFont { get; private set; } + public static SoundEffect ClickSound { get; private set; } + public static SoundEffect PlaceSound { get; private set; } + public static SoundEffect BuySound { get; private set; } - public static void Load() { - TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Tiles"), 16, 16); - AttractionTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Attractions"), 16, 16); - UiTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Ui"), 16, 16); + public static Vector2 TileSize { get; private set; } + public static GenericFont Font { get; private set; } + public static GenericFont MonospacedFont { get; private set; } - ClickSound = MlemGame.LoadContent("Sounds/Click"); - PlaceSound = MlemGame.LoadContent("Sounds/Place"); - BuySound = MlemGame.LoadContent("Sounds/StarBuy"); + public static void Load() { + Assets.TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Tiles"), 16, 16); + Assets.AttractionTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Attractions"), 16, 16); + Assets.UiTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Ui"), 16, 16); - TileSize = new Vector2(AttractionTexture.RegionWidth, AttractionTexture.RegionHeight); - Font = new GenericSpriteFont(MlemGame.LoadContent("Fonts/" + Localization.Get("Font"))); - MonospacedFont = new GenericSpriteFont(MlemGame.LoadContent("Fonts/Monospaced")); - } + Assets.ClickSound = MlemGame.LoadContent("Sounds/Click"); + Assets.PlaceSound = MlemGame.LoadContent("Sounds/Place"); + Assets.BuySound = MlemGame.LoadContent("Sounds/StarBuy"); + Assets.TileSize = new Vector2(Assets.AttractionTexture.RegionWidth, Assets.AttractionTexture.RegionHeight); + Assets.Font = new GenericSpriteFont(MlemGame.LoadContent("Fonts/" + Localization.Get("Font"))); + Assets.MonospacedFont = new GenericSpriteFont(MlemGame.LoadContent("Fonts/Monospaced")); } + } \ No newline at end of file diff --git a/TouchyTickets/Attractions/Attraction.cs b/TouchyTickets/Attractions/Attraction.cs index ddfe7fb..fbc36d2 100644 --- a/TouchyTickets/Attractions/Attraction.cs +++ b/TouchyTickets/Attractions/Attraction.cs @@ -5,107 +5,104 @@ using System.Numerics; using System.Runtime.Serialization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MLEM.Extensions; using MLEM.Misc; -using MLEM.Startup; using MLEM.Textures; using TouchyTickets.Upgrades; -using static TouchyTickets.Attractions.AttractionFlags; -using static TouchyTickets.Attractions.AttractionType; +using Vector2 = Microsoft.Xna.Framework.Vector2; -namespace TouchyTickets.Attractions { - [DataContract] - public class Attraction { +namespace TouchyTickets.Attractions; - [DataMember] - public readonly List Modifiers = new List(); - [DataMember] - public readonly AttractionType Type; - [DataMember] - private double ticketPercentage; - private float animationSizeModifier; +[DataContract] +public class Attraction { - public Attraction(AttractionType type) { - this.Type = type; - } - - public double Update(TimeSpan passed, ParkMap map, Point position) { - var genRate = this.GetGenerationRate(map, position); - // apply generation rate to ticket amount - this.ticketPercentage += genRate * passed.TotalSeconds; - var total = (BigInteger) this.ticketPercentage; - if (total > 0) { - GameImpl.Instance.Tickets += total; - this.ticketPercentage -= (double) total; - } - - // animation stuff - if (this.animationSizeModifier > 0) - this.animationSizeModifier = Math.Max(this.animationSizeModifier - 0.2F, 0); - - // return the generation rate per second - return genRate; - } - - public void Draw(SpriteBatch batch, Vector2 position, float alpha, float scale) { - var drawScale = scale; - if (this.animationSizeModifier > 0) - drawScale += (float) Math.Sin(this.animationSizeModifier) * 0.05F; - var tex = Assets.AttractionTexture[this.Type.TextureRegion]; - var center = tex.Size.ToVector2() / 2; - batch.Draw(tex, position + center * scale, Color.White * alpha, 0, center, drawScale, SpriteEffects.None, 0); - } - - public double GetGenerationRate(ParkMap map, Point position) { - var genRate = this.Type.GetGenerationRate(); - - // apply attraction modifiers - var mod = 1D; - foreach (var modifier in this.Modifiers) - mod += Math.Pow(modifier.Modifier.Multiplier, modifier.Amount) - 1; - genRate *= mod; - - // apply star upgrades - foreach (var upgrade in Upgrade.Upgrades.Values.OfType()) - genRate *= upgrade.GetCurrentMultiplier(this, map, position); - - return genRate; - } - - public void ApplyModifier(AttractionModifier modifier) { - // increase the amount of existing modifiers - foreach (var mod in this.Modifiers) { - if (mod.Modifier == modifier) { - mod.Amount++; - return; - } - } - // or add a new modifier - this.Modifiers.Add(new ActiveModifier(modifier, 1)); - } - - public int GetModifierAmount(AttractionModifier modifier) { - return this.Modifiers.Where(m => modifier == null || m.Modifier == modifier).Sum(m => m.Amount); - } - - public BigInteger GetModifierPrice(AttractionModifier modifier) { - var amount = this.GetModifierAmount(modifier); - return (BigInteger) Math.Ceiling(modifier.InitialPrice * Math.Pow(1 + 0.45F, amount)); - } - - public void Wobble() { - this.animationSizeModifier = MathHelper.Pi; - } - - public IEnumerable GetSurrounding(ParkMap map, Point position, AttractionType type) { - foreach (var tile in this.Type.GetCoveredTiles()) { - foreach (var dir in Direction2Helper.Adjacent) { - var other = map.GetAttractionAt(position + tile + dir.Offset()); - if (other != null && other != this && other.Type == type) - yield return other; - } - } - } + [DataMember] + public readonly List Modifiers = new(); + [DataMember] + public readonly AttractionType Type; + [DataMember] + private double ticketPercentage; + private float animationSizeModifier; + public Attraction(AttractionType type) { + this.Type = type; } + + public double Update(TimeSpan passed, ParkMap map, Point position) { + var genRate = this.GetGenerationRate(map, position); + // apply generation rate to ticket amount + this.ticketPercentage += genRate * passed.TotalSeconds; + var total = (BigInteger) this.ticketPercentage; + if (total > 0) { + GameImpl.Instance.Tickets += total; + this.ticketPercentage -= (double) total; + } + + // animation stuff + if (this.animationSizeModifier > 0) + this.animationSizeModifier = Math.Max(this.animationSizeModifier - 0.2F, 0); + + // return the generation rate per second + return genRate; + } + + public void Draw(SpriteBatch batch, Vector2 position, float alpha, float scale) { + var drawScale = scale; + if (this.animationSizeModifier > 0) + drawScale += (float) Math.Sin(this.animationSizeModifier) * 0.05F; + var tex = Assets.AttractionTexture[this.Type.TextureRegion]; + var center = tex.Size.ToVector2() / 2; + batch.Draw(tex, position + center * scale, Color.White * alpha, 0, center, drawScale, SpriteEffects.None, 0); + } + + public double GetGenerationRate(ParkMap map, Point position) { + var genRate = this.Type.GetGenerationRate(); + + // apply attraction modifiers + var mod = 1D; + foreach (var modifier in this.Modifiers) + mod += Math.Pow(modifier.Modifier.Multiplier, modifier.Amount) - 1; + genRate *= mod; + + // apply star upgrades + foreach (var upgrade in Upgrade.Upgrades.Values.OfType()) + genRate *= upgrade.GetCurrentMultiplier(this, map, position); + + return genRate; + } + + public void ApplyModifier(AttractionModifier modifier) { + // increase the amount of existing modifiers + foreach (var mod in this.Modifiers) { + if (mod.Modifier == modifier) { + mod.Amount++; + return; + } + } + // or add a new modifier + this.Modifiers.Add(new ActiveModifier(modifier, 1)); + } + + public int GetModifierAmount(AttractionModifier modifier) { + return this.Modifiers.Where(m => modifier == null || m.Modifier == modifier).Sum(m => m.Amount); + } + + public BigInteger GetModifierPrice(AttractionModifier modifier) { + var amount = this.GetModifierAmount(modifier); + return (BigInteger) Math.Ceiling(modifier.InitialPrice * Math.Pow(1 + 0.45F, amount)); + } + + public void Wobble() { + this.animationSizeModifier = MathHelper.Pi; + } + + public IEnumerable GetSurrounding(ParkMap map, Point position, AttractionType type) { + foreach (var tile in this.Type.GetCoveredTiles()) { + foreach (var dir in Direction2Helper.Adjacent) { + var other = map.GetAttractionAt(position + tile + dir.Offset()); + if (other != null && other != this && other.Type == type) + yield return other; + } + } + } + } \ No newline at end of file diff --git a/TouchyTickets/Attractions/AttractionFlags.cs b/TouchyTickets/Attractions/AttractionFlags.cs index 926fef8..33b81ce 100644 --- a/TouchyTickets/Attractions/AttractionFlags.cs +++ b/TouchyTickets/Attractions/AttractionFlags.cs @@ -1,18 +1,18 @@ using System; -namespace TouchyTickets.Attractions { - [Flags] - public enum AttractionFlags { +namespace TouchyTickets.Attractions; - // base flags - None = 0, - Relaxed = 1, - Cars = 2, - Walking = 4, - FastCars = 8, - NonTechnology = 16, - Small = 32, - All = ~0 +[Flags] +public enum AttractionFlags { + + // base flags + None = 0, + Relaxed = 1, + Cars = 2, + Walking = 4, + FastCars = 8, + NonTechnology = 16, + Small = 32, + All = ~0 - } } \ No newline at end of file diff --git a/TouchyTickets/Attractions/AttractionModifier.cs b/TouchyTickets/Attractions/AttractionModifier.cs index 738776e..1c751e3 100644 --- a/TouchyTickets/Attractions/AttractionModifier.cs +++ b/TouchyTickets/Attractions/AttractionModifier.cs @@ -2,85 +2,84 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; using Microsoft.Xna.Framework; -using MLEM.Textures; using Newtonsoft.Json; using static TouchyTickets.Attractions.AttractionFlags; -namespace TouchyTickets.Attractions { - [JsonConverter(typeof(Converter))] - public class AttractionModifier { +namespace TouchyTickets.Attractions; - public static readonly Dictionary Modifiers = new Dictionary(); +[JsonConverter(typeof(Converter))] +public class AttractionModifier { - static AttractionModifier() { - Register(new AttractionModifier("Lubricant", 200, Cars | FastCars, 1.02F, new Point(0, 4))); - Register(new AttractionModifier("LouderMusic", 500, Relaxed, 1.03F, new Point(2, 4))); - Register(new AttractionModifier("SmallAds", 800, Small, 1.35F, new Point(5, 4))); - Register(new AttractionModifier("LongerQueue", 1000, All, 1.06F, new Point(1, 4))); - Register(new AttractionModifier("Bouncer", 1500, Walking, 1.2F, new Point(3, 4))); - Register(new AttractionModifier("OnRideCameras", 2500, FastCars, 1.1F, new Point(4, 4))); + public static readonly Dictionary Modifiers = new(); + + static AttractionModifier() { + AttractionModifier.Register(new AttractionModifier("Lubricant", 200, AttractionFlags.Cars | AttractionFlags.FastCars, 1.02F, new Point(0, 4))); + AttractionModifier.Register(new AttractionModifier("LouderMusic", 500, AttractionFlags.Relaxed, 1.03F, new Point(2, 4))); + AttractionModifier.Register(new AttractionModifier("SmallAds", 800, AttractionFlags.Small, 1.35F, new Point(5, 4))); + AttractionModifier.Register(new AttractionModifier("LongerQueue", 1000, AttractionFlags.All, 1.06F, new Point(1, 4))); + AttractionModifier.Register(new AttractionModifier("Bouncer", 1500, AttractionFlags.Walking, 1.2F, new Point(3, 4))); + AttractionModifier.Register(new AttractionModifier("OnRideCameras", 2500, AttractionFlags.FastCars, 1.1F, new Point(4, 4))); + } + + public readonly string Name; + public readonly long InitialPrice; + public readonly Point Texture; + public readonly float Multiplier; + private readonly AttractionFlags affectedFlags; + + public AttractionModifier(string name, long initialPrice, AttractionFlags affectedFlags, float multiplier, Point texture) { + this.Name = name; + this.InitialPrice = initialPrice; + this.affectedFlags = affectedFlags; + this.Texture = texture; + this.Multiplier = multiplier; + } + + public bool IsAffected(Attraction attraction) { + return (attraction.Type.Flags & this.affectedFlags) != 0; + } + + public bool Buy(Attraction attraction) { + var price = attraction.GetModifierPrice(this); + if (GameImpl.Instance.Tickets < price) + return false; + GameImpl.Instance.Tickets -= price; + GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Modifier", this.Name); + attraction.ApplyModifier(this); + return true; + } + + private static AttractionModifier Register(AttractionModifier type) { + AttractionModifier.Modifiers.Add(type.Name, type); + return type; + } + + public class Converter : JsonConverter { + + public override void WriteJson(JsonWriter writer, AttractionModifier value, JsonSerializer serializer) { + if (value != null) + writer.WriteValue(value.Name); } - public readonly string Name; - public readonly long InitialPrice; - public readonly Point Texture; - public readonly float Multiplier; - private readonly AttractionFlags affectedFlags; - - public AttractionModifier(string name, long initialPrice, AttractionFlags affectedFlags, float multiplier, Point texture) { - this.Name = name; - this.InitialPrice = initialPrice; - this.affectedFlags = affectedFlags; - this.Texture = texture; - this.Multiplier = multiplier; - } - - public bool IsAffected(Attraction attraction) { - return (attraction.Type.Flags & this.affectedFlags) != 0; - } - - public bool Buy(Attraction attraction) { - var price = attraction.GetModifierPrice(this); - if (GameImpl.Instance.Tickets < price) - return false; - GameImpl.Instance.Tickets -= price; - GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Modifier", this.Name); - attraction.ApplyModifier(this); - return true; - } - - private static AttractionModifier Register(AttractionModifier type) { - Modifiers.Add(type.Name, type); - return type; - } - - public class Converter : JsonConverter { - - public override void WriteJson(JsonWriter writer, AttractionModifier value, JsonSerializer serializer) { - if (value != null) - writer.WriteValue(value.Name); - } - - public override AttractionModifier ReadJson(JsonReader reader, Type objectType, AttractionModifier existingValue, bool hasExistingValue, JsonSerializer serializer) { - return reader.Value != null ? Modifiers[reader.Value.ToString()] : null; - } - + public override AttractionModifier ReadJson(JsonReader reader, Type objectType, AttractionModifier existingValue, bool hasExistingValue, JsonSerializer serializer) { + return reader.Value != null ? AttractionModifier.Modifiers[reader.Value.ToString()] : null; } } - [DataContract] - public class ActiveModifier { +} - [DataMember] - public readonly AttractionModifier Modifier; - [DataMember] - public int Amount; +[DataContract] +public class ActiveModifier { - public ActiveModifier(AttractionModifier modifier, int amount) { - this.Modifier = modifier; - this.Amount = amount; - } + [DataMember] + public readonly AttractionModifier Modifier; + [DataMember] + public int Amount; + public ActiveModifier(AttractionModifier modifier, int amount) { + this.Modifier = modifier; + this.Amount = amount; } + } \ No newline at end of file diff --git a/TouchyTickets/Attractions/AttractionType.cs b/TouchyTickets/Attractions/AttractionType.cs index aabc0fa..a45c1bd 100644 --- a/TouchyTickets/Attractions/AttractionType.cs +++ b/TouchyTickets/Attractions/AttractionType.cs @@ -2,98 +2,97 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; -using MLEM.Textures; using Newtonsoft.Json; using TouchyTickets.Upgrades; using static TouchyTickets.Attractions.AttractionFlags; -namespace TouchyTickets.Attractions { - [JsonConverter(typeof(Converter))] - public class AttractionType { +namespace TouchyTickets.Attractions; - public static readonly Dictionary Attractions = new Dictionary(); - public static readonly AttractionType Carousel = Register(new AttractionType("Carousel", RectArea(1, 1), new Rectangle(0, 0, 1, 1), 0.5F, 50, Relaxed | Cars | Small)); - public static readonly AttractionType MirrorHouse = Register(new AttractionType("MirrorHouse", RectArea(1, 1), new Rectangle(3, 0, 1, 1), 1, 150, Relaxed | Walking | NonTechnology | Small)); - public static readonly AttractionType FoodCourt = Register(new AttractionType("FoodCourt", RectArea(2, 1), new Rectangle(1, 0, 2, 1), 2F, 300, None)); - public static readonly AttractionType SpiralSlide = Register(new AttractionType("SpiralSlide", RectArea(1, 2), new Rectangle(5, 0, 1, 2), 4, 1200, Relaxed | Walking)); - public static readonly AttractionType HedgeMaze = Register(new AttractionType("HedgeMaze", RectArea(2, 2), new Rectangle(3, 3, 2, 2), 8, 2500, Relaxed | Walking | NonTechnology)); - public static readonly AttractionType FerrisWheel = Register(new AttractionType("FerrisWheel", RectArea(2, 2), new Rectangle(0, 1, 2, 2), 12, 4000, Relaxed | Cars)); - public static readonly AttractionType FreefallCoaster = Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, new Rectangle(6, 0, 3, 2), 24, 8000, FastCars)); - public static readonly AttractionType HauntedHouse = Register(new AttractionType("HauntedHouse", RectArea(2, 2), new Rectangle(3, 5, 2, 2), 30, 12000, FastCars)); - public static readonly AttractionType GoKarts = Register(new AttractionType("GoKarts", RectArea(2, 2), new Rectangle(5, 2, 2, 2), 50, 24000, Cars | Relaxed)); - public static readonly AttractionType MiniGolf = Register(new AttractionType("MiniGolf", RectArea(2, 3), new Rectangle(9, 0, 2, 3), 75, 35000, Relaxed | Walking | NonTechnology)); - public static readonly AttractionType WildMouse = Register(new AttractionType("WildMouse", RectArea(3, 2), new Rectangle(2, 1, 3, 2), 100, 60000, FastCars)); - public static readonly AttractionType LogFlume = Register(new AttractionType("LogFlume", new[,] {{true, true, false}, {true, true, true}}, new Rectangle(0, 3, 3, 2), 160, 90000, FastCars)); - public static readonly AttractionType HeartlineTwister = Register(new AttractionType("HeartlineTwister", RectArea(4, 2), new Rectangle(5, 4, 4, 2), 250, 150000, FastCars)); - public static readonly AttractionType WoodCoaster = Register(new AttractionType("WoodCoaster", RectArea(3, 3), new Rectangle(0, 5, 3, 3), 300, 215000, FastCars)); - public static readonly AttractionType SafariZone = Register(new AttractionType("SafariZone", RectArea(5, 3), new Rectangle(11, 0, 5, 3), 600, 750000, Relaxed | Walking | NonTechnology)); +[JsonConverter(typeof(Converter))] +public class AttractionType { - public readonly string Name; - public readonly bool[,] Area; - public int Width => this.Area.GetLength(1); - public int Height => this.Area.GetLength(0); - public readonly Rectangle TextureRegion; - private readonly float generationPerSecond; - public readonly long InitialPrice; - public readonly AttractionFlags Flags; + public static readonly Dictionary Attractions = new(); + public static readonly AttractionType Carousel = AttractionType.Register(new AttractionType("Carousel", AttractionType.RectArea(1, 1), new Rectangle(0, 0, 1, 1), 0.5F, 50, AttractionFlags.Relaxed | AttractionFlags.Cars | AttractionFlags.Small)); + public static readonly AttractionType MirrorHouse = AttractionType.Register(new AttractionType("MirrorHouse", AttractionType.RectArea(1, 1), new Rectangle(3, 0, 1, 1), 1, 150, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology | AttractionFlags.Small)); + public static readonly AttractionType FoodCourt = AttractionType.Register(new AttractionType("FoodCourt", AttractionType.RectArea(2, 1), new Rectangle(1, 0, 2, 1), 2F, 300, AttractionFlags.None)); + public static readonly AttractionType SpiralSlide = AttractionType.Register(new AttractionType("SpiralSlide", AttractionType.RectArea(1, 2), new Rectangle(5, 0, 1, 2), 4, 1200, AttractionFlags.Relaxed | AttractionFlags.Walking)); + public static readonly AttractionType HedgeMaze = AttractionType.Register(new AttractionType("HedgeMaze", AttractionType.RectArea(2, 2), new Rectangle(3, 3, 2, 2), 8, 2500, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology)); + public static readonly AttractionType FerrisWheel = AttractionType.Register(new AttractionType("FerrisWheel", AttractionType.RectArea(2, 2), new Rectangle(0, 1, 2, 2), 12, 4000, AttractionFlags.Relaxed | AttractionFlags.Cars)); + public static readonly AttractionType FreefallCoaster = AttractionType.Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, new Rectangle(6, 0, 3, 2), 24, 8000, AttractionFlags.FastCars)); + public static readonly AttractionType HauntedHouse = AttractionType.Register(new AttractionType("HauntedHouse", AttractionType.RectArea(2, 2), new Rectangle(3, 5, 2, 2), 30, 12000, AttractionFlags.FastCars)); + public static readonly AttractionType GoKarts = AttractionType.Register(new AttractionType("GoKarts", AttractionType.RectArea(2, 2), new Rectangle(5, 2, 2, 2), 50, 24000, AttractionFlags.Cars | AttractionFlags.Relaxed)); + public static readonly AttractionType MiniGolf = AttractionType.Register(new AttractionType("MiniGolf", AttractionType.RectArea(2, 3), new Rectangle(9, 0, 2, 3), 75, 35000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology)); + public static readonly AttractionType WildMouse = AttractionType.Register(new AttractionType("WildMouse", AttractionType.RectArea(3, 2), new Rectangle(2, 1, 3, 2), 100, 60000, AttractionFlags.FastCars)); + public static readonly AttractionType LogFlume = AttractionType.Register(new AttractionType("LogFlume", new[,] {{true, true, false}, {true, true, true}}, new Rectangle(0, 3, 3, 2), 160, 90000, AttractionFlags.FastCars)); + public static readonly AttractionType HeartlineTwister = AttractionType.Register(new AttractionType("HeartlineTwister", AttractionType.RectArea(4, 2), new Rectangle(5, 4, 4, 2), 250, 150000, AttractionFlags.FastCars)); + public static readonly AttractionType WoodCoaster = AttractionType.Register(new AttractionType("WoodCoaster", AttractionType.RectArea(3, 3), new Rectangle(0, 5, 3, 3), 300, 215000, AttractionFlags.FastCars)); + public static readonly AttractionType SafariZone = AttractionType.Register(new AttractionType("SafariZone", AttractionType.RectArea(5, 3), new Rectangle(11, 0, 5, 3), 600, 750000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology)); - public AttractionType(string name, bool[,] area, Rectangle textureRegion, float generationPerSecond, long initialPrice, AttractionFlags flags) { - this.Name = name; - this.Area = area; - this.TextureRegion = textureRegion; - this.generationPerSecond = generationPerSecond; - this.InitialPrice = initialPrice; - this.Flags = flags; - } + public readonly string Name; + public readonly bool[,] Area; + public int Width => this.Area.GetLength(1); + public int Height => this.Area.GetLength(0); + public readonly Rectangle TextureRegion; + private readonly float generationPerSecond; + public readonly long InitialPrice; + public readonly AttractionFlags Flags; - public Attraction Create() { - return new Attraction(this); - } + public AttractionType(string name, bool[,] area, Rectangle textureRegion, float generationPerSecond, long initialPrice, AttractionFlags flags) { + this.Name = name; + this.Area = area; + this.TextureRegion = textureRegion; + this.generationPerSecond = generationPerSecond; + this.InitialPrice = initialPrice; + this.Flags = flags; + } - public double GetGenerationRate() { - var genRate = this.generationPerSecond; - foreach (var upgrade in Upgrade.Upgrades.Values.OfType()) - genRate *= upgrade.GetCurrentMultiplier(this); - return genRate; - } + public Attraction Create() { + return new Attraction(this); + } - public IEnumerable GetCoveredTiles() { - for (var x = 0; x < this.Width; x++) { - for (var y = 0; y < this.Height; y++) { - if (this.Area[y, x]) - yield return new Point(x, y); - } + public double GetGenerationRate() { + var genRate = this.generationPerSecond; + foreach (var upgrade in Upgrade.Upgrades.Values.OfType()) + genRate *= upgrade.GetCurrentMultiplier(this); + return genRate; + } + + public IEnumerable GetCoveredTiles() { + for (var x = 0; x < this.Width; x++) { + for (var y = 0; y < this.Height; y++) { + if (this.Area[y, x]) + yield return new Point(x, y); } } + } - private static AttractionType Register(AttractionType type) { - Attractions.Add(type.Name, type); - return type; + private static AttractionType Register(AttractionType type) { + AttractionType.Attractions.Add(type.Name, type); + return type; + } + + private static bool[,] RectArea(int width, int height) { + var ret = new bool[height, width]; + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) + ret[y, x] = true; + } + return ret; + } + + public delegate Attraction Constructor(AttractionType type); + + public class Converter : JsonConverter { + + public override void WriteJson(JsonWriter writer, AttractionType value, JsonSerializer serializer) { + if (value != null) + writer.WriteValue(value.Name); } - private static bool[,] RectArea(int width, int height) { - var ret = new bool[height, width]; - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) - ret[y, x] = true; - } - return ret; - } - - public delegate Attraction Constructor(AttractionType type); - - public class Converter : JsonConverter { - - public override void WriteJson(JsonWriter writer, AttractionType value, JsonSerializer serializer) { - if (value != null) - writer.WriteValue(value.Name); - } - - public override AttractionType ReadJson(JsonReader reader, Type objectType, AttractionType existingValue, bool hasExistingValue, JsonSerializer serializer) { - return reader.Value != null ? Attractions[reader.Value.ToString()] : null; - } - + public override AttractionType ReadJson(JsonReader reader, Type objectType, AttractionType existingValue, bool hasExistingValue, JsonSerializer serializer) { + return reader.Value != null ? AttractionType.Attractions[reader.Value.ToString()] : null; } } + } \ No newline at end of file diff --git a/TouchyTickets/Content/Contentless.json b/TouchyTickets/Content/Contentless.json index e18b17e..0014489 100644 --- a/TouchyTickets/Content/Contentless.json +++ b/TouchyTickets/Content/Contentless.json @@ -1,16 +1,16 @@ { - "exclude": [ - "obj/", - "bin/", - "LICENSE", - "README" - ], - "overrides": { - ".json": { - "copy": true - }, - "Sounds/": { - "processor": "SoundEffectProcessor" - } + "exclude": [ + "obj/", + "bin/", + "LICENSE", + "README" + ], + "overrides": { + ".json": { + "copy": true + }, + "Sounds/": { + "processor": "SoundEffectProcessor" } + } } \ No newline at end of file diff --git a/TouchyTickets/GameImpl.cs b/TouchyTickets/GameImpl.cs index 0577e80..93606bf 100644 --- a/TouchyTickets/GameImpl.cs +++ b/TouchyTickets/GameImpl.cs @@ -7,141 +7,143 @@ using Microsoft.Xna.Framework.Graphics; using MLEM.Cameras; using MLEM.Startup; using TouchyTickets.Upgrades; +using Vector2 = Microsoft.Xna.Framework.Vector2; -namespace TouchyTickets { - public class GameImpl : MlemGame { +namespace TouchyTickets; - public static GameImpl Instance { get; private set; } - public readonly ISet AppliedUpgrades = new HashSet(); - public readonly Platform Platform; - public BigInteger Tickets; - public int TimesRestarted; - public int Stars; - public ParkMap Map; - public Tutorial Tutorial { get; private set; } - public Camera Camera { get; private set; } - public Ui Ui { get; private set; } - public bool DrawMap; - public DateTime LastUpdate; - public TimeSpan PlayTime; - private double saveCounter; - private double achievementCounter; +public class GameImpl : MlemGame { - public GameImpl(Platform platform) { - this.Platform = platform; - Instance = this; - } - - protected override void LoadContent() { - base.LoadContent(); - Assets.Load(); - Options.Load(); - - // start the load sequence - Ui.SetupUiSystem(this.UiSystem); - CoroutineHandler.Start(Ui.DisplaySplash(this.LoadGame)); - } - - private void LoadGame() { - // set up online stuff - var analytics = new Dictionary(); - analytics["InfoLog"] = true; - analytics["VerboseLog"] = true; - analytics["ResourceCurrencies"] = new[] {"Tickets", "Stars"}; - analytics["ResourceItemTypes"] = new[] {"Attraction", "Restart", "Upgrade", "Modifier"}; - // ios comes first, then android. For now they're the same - analytics["GameKey"] = new[] {"cc18de06eebbc5d5e987c384fcd28000", "cc18de06eebbc5d5e987c384fcd28000"}; - analytics["SecretKey"] = new[] {"82ca1a930ee38e2383ffb02db7631e16033b511d", "82ca1a930ee38e2383ffb02db7631e16033b511d"}; - this.Platform.SetupOnlineInteractions(analytics); - - this.Tutorial = new Tutorial(); - - if (!SaveHandler.Load(this)) - this.Map = new ParkMap(20, 20); - - // load other stuff - this.Ui = new Ui(this.UiSystem); - this.Camera = new Camera(this.GraphicsDevice) { - Scale = 4, - AutoScaleWithScreen = true, - AutoScaleReferenceSize = new Point(720, 1280), - MaxScale = 24, - MinScale = 2 - }; - - // update the map once to make sure that catching up happens during loading - this.UpdateMapOnce(); - } - - protected override void DoUpdate(GameTime gameTime) { - base.DoUpdate(gameTime); - - if (this.Map != null) { - // update the map - this.UpdateMapOnce(); - - // achievements - this.achievementCounter += gameTime.ElapsedGameTime.TotalSeconds; - if (this.achievementCounter >= 5) { - this.achievementCounter = 0; - foreach (var achievement in Achievement.Achievements.Values) - achievement.Update(); - } - - // save every 3 seconds - this.saveCounter += gameTime.ElapsedGameTime.TotalSeconds; - if (this.saveCounter >= 3) { - this.saveCounter = 0; - SaveHandler.Save(this); - } - } - - // play time stuff - var lastTime = this.PlayTime; - this.PlayTime += gameTime.ElapsedGameTime; - if (lastTime.TotalHours >= 1 != this.PlayTime.TotalHours >= 1) - Ui.DisplayRatePlease(); - - this.Ui?.Update(gameTime); - this.Tutorial?.Update(this); - } - - protected override void DoDraw(GameTime gameTime) { - this.GraphicsDevice.Clear(Color.Black); - if (this.DrawMap) { - this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: this.Camera.ViewMatrix); - this.Map.Draw(gameTime, this.SpriteBatch, Vector2.Zero, 1, 1, true, this.Camera.GetVisibleRectangle()); - this.SpriteBatch.End(); - } - base.DoDraw(gameTime); - } - - public BigInteger GetStarPrice() { - return 1000000000 * BigInteger.Pow(100, this.TimesRestarted); - } - - public int GetBuyableStars() { - #if DEBUG - return 3; - #endif - return (int) BigInteger.Min(3, this.Tickets / this.GetStarPrice()); - } - - private void UpdateMapOnce() { - var now = DateTime.Now; - if (this.LastUpdate != default) { - var lastTickets = this.Tickets; - var lastTps = this.Map.TicketsPerSecond; - - var passed = now - this.LastUpdate; - this.Map.Update(passed, passed.TotalSeconds >= 1); - - // if 10 or more seconds passed, we display a message - if (Options.Instance.WhileYouWereAwayMessage && passed.TotalSeconds >= 10) - Ui.DisplayWhileYouWereAway(passed, this.Tickets - lastTickets, this.Map.TicketsPerSecond - lastTps); - } - this.LastUpdate = now; - } + public static GameImpl Instance { get; private set; } + public readonly ISet AppliedUpgrades = new HashSet(); + public readonly Platform Platform; + public BigInteger Tickets; + public int TimesRestarted; + public int Stars; + public ParkMap Map; + public Tutorial Tutorial { get; private set; } + public Camera Camera { get; private set; } + public Ui Ui { get; private set; } + public bool DrawMap; + public DateTime LastUpdate; + public TimeSpan PlayTime; + private double saveCounter; + private double achievementCounter; + public GameImpl(Platform platform) { + this.Platform = platform; + GameImpl.Instance = this; } + + protected override void LoadContent() { + base.LoadContent(); + Assets.Load(); + Options.Load(); + + // start the load sequence + Ui.SetupUiSystem(this.UiSystem); + CoroutineHandler.Start(Ui.DisplaySplash(this.LoadGame)); + } + + private void LoadGame() { + // set up online stuff + var analytics = new Dictionary(); + analytics["InfoLog"] = true; + analytics["VerboseLog"] = true; + analytics["ResourceCurrencies"] = new[] {"Tickets", "Stars"}; + analytics["ResourceItemTypes"] = new[] {"Attraction", "Restart", "Upgrade", "Modifier"}; + // ios comes first, then android. For now they're the same + analytics["GameKey"] = new[] {"cc18de06eebbc5d5e987c384fcd28000", "cc18de06eebbc5d5e987c384fcd28000"}; + analytics["SecretKey"] = new[] {"82ca1a930ee38e2383ffb02db7631e16033b511d", "82ca1a930ee38e2383ffb02db7631e16033b511d"}; + this.Platform.SetupOnlineInteractions(analytics); + + this.Tutorial = new Tutorial(); + + if (!SaveHandler.Load(this)) + this.Map = new ParkMap(20, 20); + + // load other stuff + this.Ui = new Ui(this.UiSystem); + this.Camera = new Camera(this.GraphicsDevice) { + Scale = 4, + AutoScaleWithScreen = true, + AutoScaleReferenceSize = new Point(720, 1280), + MaxScale = 24, + MinScale = 2 + }; + + // update the map once to make sure that catching up happens during loading + this.UpdateMapOnce(); + } + + protected override void DoUpdate(GameTime gameTime) { + base.DoUpdate(gameTime); + + if (this.Map != null) { + // update the map + this.UpdateMapOnce(); + + // achievements + this.achievementCounter += gameTime.ElapsedGameTime.TotalSeconds; + if (this.achievementCounter >= 5) { + this.achievementCounter = 0; + foreach (var achievement in Achievement.Achievements.Values) + achievement.Update(); + } + + // save every 3 seconds + this.saveCounter += gameTime.ElapsedGameTime.TotalSeconds; + if (this.saveCounter >= 3) { + this.saveCounter = 0; + SaveHandler.Save(this); + } + } + + // play time stuff + var lastTime = this.PlayTime; + this.PlayTime += gameTime.ElapsedGameTime; + if (lastTime.TotalHours >= 1 != this.PlayTime.TotalHours >= 1) + Ui.DisplayRatePlease(); + + this.Ui?.Update(gameTime); + this.Tutorial?.Update(this); + } + + protected override void DoDraw(GameTime gameTime) { + this.GraphicsDevice.Clear(Color.Black); + if (this.DrawMap) { + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: this.Camera.ViewMatrix); + this.Map.Draw(gameTime, this.SpriteBatch, Vector2.Zero, 1, 1, true, this.Camera.GetVisibleRectangle()); + this.SpriteBatch.End(); + } + base.DoDraw(gameTime); + } + + public BigInteger GetStarPrice() { + return 1000000000 * BigInteger.Pow(100, this.TimesRestarted); + } + + public int GetBuyableStars() { +#if DEBUG + return 3; +#else + return (int) BigInteger.Min(3, this.Tickets / this.GetStarPrice()); +#endif + } + + private void UpdateMapOnce() { + var now = DateTime.Now; + if (this.LastUpdate != default) { + var lastTickets = this.Tickets; + var lastTps = this.Map.TicketsPerSecond; + + var passed = now - this.LastUpdate; + this.Map.Update(passed, passed.TotalSeconds >= 1); + + // if 10 or more seconds passed, we display a message + if (Options.Instance.WhileYouWereAwayMessage && passed.TotalSeconds >= 10) + Ui.DisplayWhileYouWereAway(passed, this.Tickets - lastTickets, this.Map.TicketsPerSecond - lastTps); + } + this.LastUpdate = now; + } + } \ No newline at end of file diff --git a/TouchyTickets/Localization.cs b/TouchyTickets/Localization.cs index 3bac890..5184e71 100644 --- a/TouchyTickets/Localization.cs +++ b/TouchyTickets/Localization.cs @@ -4,47 +4,46 @@ using System.Globalization; using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; -using MLEM.Startup; using Newtonsoft.Json; -namespace TouchyTickets { - public static class Localization { +namespace TouchyTickets; - public static readonly List NumberFormat = LoadLocalized>("NumberFormat", "json5"); - private static readonly Dictionary Strings = LoadLocalized>("Localization"); - private static readonly Dictionary FallbackStrings = Load>("Localization"); - private static readonly List News = LoadLocalized>("News"); - private static readonly Random Random = new Random(); +public static class Localization { - public static string Get(string key) { - if (Strings.TryGetValue(key, out var val)) - return val; - if (FallbackStrings.TryGetValue(key, out var fallback)) - return fallback; - return $"?{key}?"; - } - - public static string GetRandomNews() { - return News[Random.Next(News.Count)]; - } - - private static T LoadLocalized(string name, string extension = "json") { - var culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName; - foreach (var path in new[] {$"{name}.{culture}", name}) { - try { - return Load(path, extension); - } catch (Exception) { - // move on to the next path - } - } - throw new ContentLoadException(); - } - - private static T Load(string name, string extension = "json") { - var path = $"{GameImpl.Instance.Content.RootDirectory}/Localization/{name}.{extension}"; - using (var reader = new JsonTextReader(new StreamReader(TitleContainer.OpenStream(path)))) - return SaveHandler.Serializer.Deserialize(reader); - } + public static readonly List NumberFormat = Localization.LoadLocalized>("NumberFormat", "json5"); + private static readonly Dictionary Strings = Localization.LoadLocalized>("Localization"); + private static readonly Dictionary FallbackStrings = Localization.Load>("Localization"); + private static readonly List News = Localization.LoadLocalized>("News"); + private static readonly Random Random = new(); + public static string Get(string key) { + if (Localization.Strings.TryGetValue(key, out var val)) + return val; + if (Localization.FallbackStrings.TryGetValue(key, out var fallback)) + return fallback; + return $"?{key}?"; } + + public static string GetRandomNews() { + return Localization.News[Localization.Random.Next(Localization.News.Count)]; + } + + private static T LoadLocalized(string name, string extension = "json") { + var culture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName; + foreach (var path in new[] {$"{name}.{culture}", name}) { + try { + return Localization.Load(path, extension); + } catch (Exception) { + // move on to the next path + } + } + throw new ContentLoadException(); + } + + private static T Load(string name, string extension = "json") { + var path = $"{GameImpl.Instance.Content.RootDirectory}/Localization/{name}.{extension}"; + using var reader = new JsonTextReader(new StreamReader(TitleContainer.OpenStream(path))); + return SaveHandler.Serializer.Deserialize(reader); + } + } \ No newline at end of file diff --git a/TouchyTickets/Options.cs b/TouchyTickets/Options.cs index 8100065..afc2cc5 100644 --- a/TouchyTickets/Options.cs +++ b/TouchyTickets/Options.cs @@ -3,58 +3,58 @@ using System.Runtime.Serialization; using Microsoft.Xna.Framework.Audio; using Newtonsoft.Json; -namespace TouchyTickets { - [DataContract] - public class Options { +namespace TouchyTickets; - public static Options Instance { get; private set; } +[DataContract] +public class Options { - [DataMember] - public int RainingTicketLimit = 300; - [DataMember] - public float SoundVolume { - get => this.soundVolume; - set { - this.soundVolume = value; - SoundEffect.MasterVolume = value; - } + public static Options Instance { get; private set; } + + [DataMember] + public int RainingTicketLimit = 300; + [DataMember] + public float SoundVolume { + get => this.soundVolume; + set { + this.soundVolume = value; + SoundEffect.MasterVolume = value; } - private float soundVolume = 1; - [DataMember] - public bool WhileYouWereAwayMessage = true; - [DataMember] - public bool KeepScreenOn { - get => this.keepScreenOn; - set { - this.keepScreenOn = value; - GameImpl.Instance.Platform.SetKeepScreenOn(value); - } - } - private bool keepScreenOn; - [DataMember] - public bool AutoBuyEnabled = true; - [DataMember] - public int MinTicketsForAutoBuy = 50000; - - public static void Save() { - var file = GetOptionsFile(true); - using (var stream = new JsonTextWriter(file.CreateText())) - SaveHandler.Serializer.Serialize(stream, Instance); - } - - public static void Load() { - var file = GetOptionsFile(false); - if (file.Exists) { - using (var stream = new JsonTextReader(file.OpenText())) - Instance = SaveHandler.Serializer.Deserialize(stream); - } else { - Instance = new Options(); - } - } - - private static FileInfo GetOptionsFile(bool create) { - return new FileInfo(Path.Combine(SaveHandler.GetGameDirectory(create).FullName, "Options")); - } - } + private float soundVolume = 1; + [DataMember] + public bool WhileYouWereAwayMessage = true; + [DataMember] + public bool KeepScreenOn { + get => this.keepScreenOn; + set { + this.keepScreenOn = value; + GameImpl.Instance.Platform.SetKeepScreenOn(value); + } + } + private bool keepScreenOn; + [DataMember] + public bool AutoBuyEnabled = true; + [DataMember] + public int MinTicketsForAutoBuy = 50000; + + public static void Save() { + var file = Options.GetOptionsFile(true); + using var stream = new JsonTextWriter(file.CreateText()); + SaveHandler.Serializer.Serialize(stream, Options.Instance); + } + + public static void Load() { + var file = Options.GetOptionsFile(false); + if (file.Exists) { + using var stream = new JsonTextReader(file.OpenText()); + Options.Instance = SaveHandler.Serializer.Deserialize(stream); + } else { + Options.Instance = new Options(); + } + } + + private static FileInfo GetOptionsFile(bool create) { + return new FileInfo(Path.Combine(SaveHandler.GetGameDirectory(create).FullName, "Options")); + } + } \ No newline at end of file diff --git a/TouchyTickets/ParkMap.cs b/TouchyTickets/ParkMap.cs index 87f228e..dcb68f2 100644 --- a/TouchyTickets/ParkMap.cs +++ b/TouchyTickets/ParkMap.cs @@ -12,273 +12,273 @@ using MLEM.Textures; using TouchyTickets.Attractions; using TouchyTickets.Upgrades; -namespace TouchyTickets { - [DataContract] - public class ParkMap { +namespace TouchyTickets; - private const int AdditionalRadius = 15; - private const int AutoBuyIntervalSecs = 30; +[DataContract] +public class ParkMap { - [DataMember] - public readonly int Width; - [DataMember] - public readonly int Height; - [DataMember] - private readonly List<(Point, Attraction)> attractions = new List<(Point, Attraction)>(); - private readonly Dictionary treePositions = new Dictionary(); - private readonly Dictionary fencePositions = new Dictionary(); - private readonly Attraction[,] attractionGrid; + private const int AdditionalRadius = 15; + private const int AutoBuyIntervalSecs = 30; - [DataMember] - public double TicketsPerSecond { get; private set; } - public Attraction PlacingAttraction; - public AttractionModifier PlacingModifier; - public Point PlacingPosition; - public Point? SelectedPosition; - private bool draggingAttraction; - private double autoBuyCounter; + [DataMember] + public readonly int Width; + [DataMember] + public readonly int Height; + [DataMember] + private readonly List<(Point, Attraction)> attractions = new(); + private readonly Dictionary treePositions = new(); + private readonly Dictionary fencePositions = new(); + private readonly Attraction[,] attractionGrid; - public ParkMap(int width, int height) { - this.Width = width; - this.Height = height; - this.attractionGrid = new Attraction[width, height]; + [DataMember] + public double TicketsPerSecond { get; private set; } + public Attraction PlacingAttraction; + public AttractionModifier PlacingModifier; + public Point PlacingPosition; + public Point? SelectedPosition; + private bool draggingAttraction; + private double autoBuyCounter; - // set up trees - var random = new Random(); - for (var x = -AdditionalRadius; x < this.Width + AdditionalRadius; x++) { - for (var y = -AdditionalRadius; y < this.Height + AdditionalRadius; y++) { - var pos = new Point(x, y); - if (this.IsInBounds(pos)) - continue; - if (random.Next(15) != 0) - continue; - var type = random.Next(3); - this.treePositions[pos] = type; - } - } + public ParkMap(int width, int height) { + this.Width = width; + this.Height = height; + this.attractionGrid = new Attraction[width, height]; - // set up fences - this.fencePositions[new Point(-1, -1)] = 2; - this.fencePositions[new Point(this.Width, -1)] = 3; - this.fencePositions[new Point(-1, this.Height)] = 4; - this.fencePositions[new Point(this.Width, this.Height)] = 5; - for (var x = 0; x < this.Width; x++) { - this.fencePositions[new Point(x, -1)] = 0; - this.fencePositions[new Point(x, this.Height)] = 0; - } - for (var y = 0; y < this.Height; y++) { - this.fencePositions[new Point(-1, y)] = 1; - this.fencePositions[new Point(this.Width, y)] = 1; + // set up trees + var random = new Random(); + for (var x = -ParkMap.AdditionalRadius; x < this.Width + ParkMap.AdditionalRadius; x++) { + for (var y = -ParkMap.AdditionalRadius; y < this.Height + ParkMap.AdditionalRadius; y++) { + var pos = new Point(x, y); + if (this.IsInBounds(pos)) + continue; + if (random.Next(15) != 0) + continue; + var type = random.Next(3); + this.treePositions[pos] = type; } } - public void Update(TimeSpan passed, bool wasAway) { - var toSimulate = wasAway ? new TimeSpan(passed.Ticks / 2) : passed; - - // handle auto-buying - this.autoBuyCounter += toSimulate.TotalSeconds; - this.TryAutoBuy(); - var autoBuysPerAttraction = ((float) this.autoBuyCounter / AutoBuyIntervalSecs / this.attractions.Count).Ceil(); - - // update tickets - this.TicketsPerSecond = 0; - foreach (var (pos, attraction) in this.attractions) { - var genPerSecond = attraction.Update(toSimulate, this, pos); - this.TicketsPerSecond += genPerSecond; - - // if we were away, we have to catch up with auto-buys while also taking into account - // the amount of tickets that each ride generates. The easiest way we can do this is - // to progress, between updating each ride, by a percentage of the total update amount - if (wasAway) { - for (var i = autoBuysPerAttraction; i > 0; i--) { - if (!this.TryAutoBuy()) - break; - } - } - } - - // map movement - if (GameImpl.Instance.DrawMap && GameImpl.Instance.UiSystem.Controls.HandleTouch) { - var camera = GameImpl.Instance.Camera; - if (MlemGame.Input.GetGesture(GestureType.Pinch, out var pinch)) { - // pinch zoom - var center = (pinch.Position + pinch.Position2) / 2; - var newDist = Vector2.Distance(pinch.Position + pinch.Delta, pinch.Position2 + pinch.Delta2); - var oldDist = Vector2.Distance(pinch.Position, pinch.Position2); - var newScale = newDist / oldDist * camera.Scale; - camera.Zoom(newScale - camera.Scale, center); - } else if (MlemGame.Input.GetGesture(GestureType.FreeDrag, out var drag)) { - if (this.draggingAttraction) { - // move the current placing position - var nextPos = (camera.ToWorldPos(drag.Position + drag.Delta) / Assets.TileSize).ToPoint(); - // drag the center of the attraction - nextPos -= new Point(this.PlacingAttraction.Type.Width / 2, this.PlacingAttraction.Type.Height / 2); - if (this.PlacingAttraction.Type.GetCoveredTiles().Select(p => nextPos + p).All(this.IsInBounds)) - this.PlacingPosition = nextPos; - } else { - // move the camera - camera.Position -= drag.Delta / camera.ActualScale; - } - } else if (this.PlacingAttraction != null) { - foreach (var touch in MlemGame.Input.TouchState) { - if (touch.State != TouchLocationState.Pressed) - continue; - // when first pressing down, go into attraction drag mode if we're touching the place location - var offset = (camera.ToWorldPos(touch.Position) / Assets.TileSize).ToPoint(); - this.draggingAttraction = this.PlacingAttraction.Type.GetCoveredTiles() - .Any(p => this.PlacingPosition + p == offset); - } - } else { - // we're not placing an attraction, so we're in remove and move mode - if (MlemGame.Input.GetGesture(GestureType.Tap, out var tap) && GameImpl.Instance.UiSystem.Controls.GetElementUnderPos(tap.Position) == null) { - var pos = (camera.ToWorldPos(tap.Position) / Assets.TileSize).ToPoint(); - var attraction = this.GetAttractionAt(pos); - if (attraction != null && (this.PlacingModifier == null || this.PlacingModifier.IsAffected(attraction))) { - // actually select the top left for easy usage later - this.SelectedPosition = this.attractions.First(kv => kv.Item2 == attraction).Item1; - } else { - this.SelectedPosition = null; - } - } - } - camera.ConstrainWorldBounds(new Vector2(-AdditionalRadius) * Assets.TileSize, new Vector2(this.Width + AdditionalRadius, this.Height + AdditionalRadius) * Assets.TileSize); - } + // set up fences + this.fencePositions[new Point(-1, -1)] = 2; + this.fencePositions[new Point(this.Width, -1)] = 3; + this.fencePositions[new Point(-1, this.Height)] = 4; + this.fencePositions[new Point(this.Width, this.Height)] = 5; + for (var x = 0; x < this.Width; x++) { + this.fencePositions[new Point(x, -1)] = 0; + this.fencePositions[new Point(x, this.Height)] = 0; } - - public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale, float alpha, bool showSurroundings, RectangleF visibleArea) { - var tileSize = Assets.TileSize * scale; - // draw ground - var additionalRadius = showSurroundings ? AdditionalRadius : 0; - var minX = Math.Max(-additionalRadius, visibleArea.Left / tileSize.X).Floor(); - var minY = Math.Max(-additionalRadius, visibleArea.Top / tileSize.Y).Floor(); - var maxX = Math.Min(this.Width + additionalRadius, visibleArea.Right / tileSize.X).Ceil(); - var maxY = Math.Min(this.Height + additionalRadius, visibleArea.Bottom / tileSize.Y).Ceil(); - for (var x = minX; x < maxX; x++) { - for (var y = minY; y < maxY; y++) { - var pos = new Vector2(x, y); - var drawPos = position + pos * tileSize; - batch.Draw(Assets.TilesTexture[0, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); - - if (this.fencePositions.TryGetValue(pos.ToPoint(), out var fenceType)) { - batch.Draw(Assets.TilesTexture[fenceType, 1], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); - } else if (this.treePositions.TryGetValue(pos.ToPoint(), out var treeType)) { - batch.Draw(Assets.TilesTexture[1 + treeType, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); - } - } - } - // selected attraction - if (this.SelectedPosition != null) { - var selected = this.SelectedPosition.Value; - var attr = this.GetAttractionAt(selected); - foreach (var pos in attr.Type.GetCoveredTiles()) - batch.Draw(batch.GetBlankTexture(), new RectangleF(position + (selected + pos).ToVector2() * tileSize, tileSize), Color.Black * 0.25F * alpha); - } - // draw attractions - foreach (var (pos, attraction) in this.attractions) { - if (this.PlacingModifier != null && this.PlacingModifier.IsAffected(attraction)) { - var color = GameImpl.Instance.Tickets >= attraction.GetModifierPrice(this.PlacingModifier) ? Color.Yellow : Color.Red; - foreach (var offset in attraction.Type.GetCoveredTiles()) - batch.Draw(batch.GetBlankTexture(), new RectangleF(position + (pos + offset).ToVector2() * tileSize, tileSize), color * 0.25F * alpha); - } - attraction.Draw(batch, position + pos.ToVector2() * tileSize, alpha, scale); - } - // placing attraction - if (this.PlacingAttraction != null) { - var placingPos = position + this.PlacingPosition.ToVector2() * tileSize; - var color = this.CanPlace(this.PlacingPosition, this.PlacingAttraction) ? Color.Yellow : Color.Red; - foreach (var pos in this.PlacingAttraction.Type.GetCoveredTiles()) - batch.Draw(batch.GetBlankTexture(), new RectangleF(placingPos + pos.ToVector2() * tileSize, tileSize), color * 0.25F * alpha); - this.PlacingAttraction.Draw(batch, placingPos, alpha * 0.5F, scale); - } + for (var y = 0; y < this.Height; y++) { + this.fencePositions[new Point(-1, y)] = 1; + this.fencePositions[new Point(this.Width, y)] = 1; } - - public bool CanPlace(Point position, Attraction attraction) { - foreach (var offset in attraction.Type.GetCoveredTiles()) { - if (!this.IsInBounds(position + offset)) - return false; - if (this.GetAttractionAt(position + offset) != null) - return false; - } - return true; - } - - public void Place(Point position, Attraction attraction) { - foreach (var (x, y) in attraction.Type.GetCoveredTiles()) - this.attractionGrid[position.X + x, position.Y + y] = attraction; - this.attractions.Add((position, attraction)); - } - - public Attraction Remove(Point position) { - var attraction = this.GetAttractionAt(position); - if (attraction != null) { - foreach (var (x, y) in attraction.Type.GetCoveredTiles()) - this.attractionGrid[position.X + x, position.Y + y] = null; - this.attractions.Remove((position, attraction)); - } - return attraction; - } - - public Attraction GetAttractionAt(Point position) { - return !this.IsInBounds(position) ? null : this.attractionGrid[position.X, position.Y]; - } - - public int GetAttractionAmount(AttractionType type) { - return this.attractions.Count(a => type == null || a.Item2.Type == type); - } - - public int GetModifierAmount(AttractionModifier modifier) { - return this.attractions.Sum(a => a.Item2.GetModifierAmount(modifier)); - } - - public bool IsAnyAttractionAffected(AttractionModifier modifier) { - return this.attractions.Any(a => modifier.IsAffected(a.Item2)); - } - - public IEnumerable<(Point, Attraction)> GetAttractions() { - foreach (var attraction in this.attractions) - yield return attraction; - } - - public bool IsInBounds(Point pos) { - return pos.X >= 0 && pos.Y >= 0 && pos.X < this.Width && pos.Y < this.Height; - } - - public ParkMap Copy(int? newWidth = null, int? newHeight = null) { - var newMap = new ParkMap(newWidth ?? this.Width, newHeight ?? this.Height); - foreach (var (pos, attraction) in this.attractions) { - if (newMap.CanPlace(pos, attraction)) - newMap.Place(pos, attraction); - } - newMap.TicketsPerSecond = this.TicketsPerSecond; - return newMap; - } - - private bool TryAutoBuy() { - if (!Options.Instance.AutoBuyEnabled) - return false; - if (GameImpl.Instance.Tickets < Options.Instance.MinTicketsForAutoBuy) - return false; - if (this.autoBuyCounter < AutoBuyIntervalSecs) - return false; - this.autoBuyCounter -= AutoBuyIntervalSecs; - - var success = false; - // auto-buy modifiers - if (Upgrade.AutoPlaceModifiers[0].IsActive()) { - // loop through all attractions, but look at attractions with fewer applied modifiers first - foreach (var attraction in this.attractions.Select(kv => kv.Item2).OrderBy(a => a.GetModifierAmount(null))) { - var match = AttractionModifier.Modifiers.Values.Where(m => m.IsAffected(attraction)); - // if we don't have level 2, we only want to increase existing modifiers - if (!Upgrade.AutoPlaceModifiers[1].IsActive()) - match = match.Where(m => attraction.GetModifierAmount(m) > 0); - // we want to apply the least applied modifier on this attraction - var modifier = match.OrderBy(m => attraction.GetModifierAmount(m)).FirstOrDefault(); - if (modifier != null && modifier.Buy(attraction)) - success = true; - } - } - return success; - } - } + + public void Update(TimeSpan passed, bool wasAway) { + var toSimulate = wasAway ? new TimeSpan(passed.Ticks / 2) : passed; + + // handle auto-buying + this.autoBuyCounter += toSimulate.TotalSeconds; + this.TryAutoBuy(); + var autoBuysPerAttraction = ((float) this.autoBuyCounter / ParkMap.AutoBuyIntervalSecs / this.attractions.Count).Ceil(); + + // update tickets + this.TicketsPerSecond = 0; + foreach (var (pos, attraction) in this.attractions) { + var genPerSecond = attraction.Update(toSimulate, this, pos); + this.TicketsPerSecond += genPerSecond; + + // if we were away, we have to catch up with auto-buys while also taking into account + // the amount of tickets that each ride generates. The easiest way we can do this is + // to progress, between updating each ride, by a percentage of the total update amount + if (wasAway) { + for (var i = autoBuysPerAttraction; i > 0; i--) { + if (!this.TryAutoBuy()) + break; + } + } + } + + // map movement + if (GameImpl.Instance.DrawMap && GameImpl.Instance.UiSystem.Controls.HandleTouch) { + var camera = GameImpl.Instance.Camera; + if (MlemGame.Input.GetGesture(GestureType.Pinch, out var pinch)) { + // pinch zoom + var center = (pinch.Position + pinch.Position2) / 2; + var newDist = Vector2.Distance(pinch.Position + pinch.Delta, pinch.Position2 + pinch.Delta2); + var oldDist = Vector2.Distance(pinch.Position, pinch.Position2); + var newScale = newDist / oldDist * camera.Scale; + camera.Zoom(newScale - camera.Scale, center); + } else if (MlemGame.Input.GetGesture(GestureType.FreeDrag, out var drag)) { + if (this.draggingAttraction) { + // move the current placing position + var nextPos = (camera.ToWorldPos(drag.Position + drag.Delta) / Assets.TileSize).ToPoint(); + // drag the center of the attraction + nextPos -= new Point(this.PlacingAttraction.Type.Width / 2, this.PlacingAttraction.Type.Height / 2); + if (this.PlacingAttraction.Type.GetCoveredTiles().Select(p => nextPos + p).All(this.IsInBounds)) + this.PlacingPosition = nextPos; + } else { + // move the camera + camera.Position -= drag.Delta / camera.ActualScale; + } + } else if (this.PlacingAttraction != null) { + foreach (var touch in MlemGame.Input.TouchState) { + if (touch.State != TouchLocationState.Pressed) + continue; + // when first pressing down, go into attraction drag mode if we're touching the place location + var offset = (camera.ToWorldPos(touch.Position) / Assets.TileSize).ToPoint(); + this.draggingAttraction = this.PlacingAttraction.Type.GetCoveredTiles() + .Any(p => this.PlacingPosition + p == offset); + } + } else { + // we're not placing an attraction, so we're in remove and move mode + if (MlemGame.Input.GetGesture(GestureType.Tap, out var tap) && GameImpl.Instance.UiSystem.Controls.GetElementUnderPos(tap.Position) == null) { + var pos = (camera.ToWorldPos(tap.Position) / Assets.TileSize).ToPoint(); + var attraction = this.GetAttractionAt(pos); + if (attraction != null && (this.PlacingModifier == null || this.PlacingModifier.IsAffected(attraction))) { + // actually select the top left for easy usage later + this.SelectedPosition = this.attractions.First(kv => kv.Item2 == attraction).Item1; + } else { + this.SelectedPosition = null; + } + } + } + camera.ConstrainWorldBounds(new Vector2(-ParkMap.AdditionalRadius) * Assets.TileSize, new Vector2(this.Width + ParkMap.AdditionalRadius, this.Height + ParkMap.AdditionalRadius) * Assets.TileSize); + } + } + + public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale, float alpha, bool showSurroundings, RectangleF visibleArea) { + var tileSize = Assets.TileSize * scale; + // draw ground + var additionalRadius = showSurroundings ? ParkMap.AdditionalRadius : 0; + var minX = Math.Max(-additionalRadius, visibleArea.Left / tileSize.X).Floor(); + var minY = Math.Max(-additionalRadius, visibleArea.Top / tileSize.Y).Floor(); + var maxX = Math.Min(this.Width + additionalRadius, visibleArea.Right / tileSize.X).Ceil(); + var maxY = Math.Min(this.Height + additionalRadius, visibleArea.Bottom / tileSize.Y).Ceil(); + for (var x = minX; x < maxX; x++) { + for (var y = minY; y < maxY; y++) { + var pos = new Vector2(x, y); + var drawPos = position + pos * tileSize; + batch.Draw(Assets.TilesTexture[0, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); + + if (this.fencePositions.TryGetValue(pos.ToPoint(), out var fenceType)) { + batch.Draw(Assets.TilesTexture[fenceType, 1], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); + } else if (this.treePositions.TryGetValue(pos.ToPoint(), out var treeType)) { + batch.Draw(Assets.TilesTexture[1 + treeType, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); + } + } + } + // selected attraction + if (this.SelectedPosition != null) { + var selected = this.SelectedPosition.Value; + var attr = this.GetAttractionAt(selected); + foreach (var pos in attr.Type.GetCoveredTiles()) + batch.Draw(batch.GetBlankTexture(), new RectangleF(position + (selected + pos).ToVector2() * tileSize, tileSize), Color.Black * 0.25F * alpha); + } + // draw attractions + foreach (var (pos, attraction) in this.attractions) { + if (this.PlacingModifier != null && this.PlacingModifier.IsAffected(attraction)) { + var color = GameImpl.Instance.Tickets >= attraction.GetModifierPrice(this.PlacingModifier) ? Color.Yellow : Color.Red; + foreach (var offset in attraction.Type.GetCoveredTiles()) + batch.Draw(batch.GetBlankTexture(), new RectangleF(position + (pos + offset).ToVector2() * tileSize, tileSize), color * 0.25F * alpha); + } + attraction.Draw(batch, position + pos.ToVector2() * tileSize, alpha, scale); + } + // placing attraction + if (this.PlacingAttraction != null) { + var placingPos = position + this.PlacingPosition.ToVector2() * tileSize; + var color = this.CanPlace(this.PlacingPosition, this.PlacingAttraction) ? Color.Yellow : Color.Red; + foreach (var pos in this.PlacingAttraction.Type.GetCoveredTiles()) + batch.Draw(batch.GetBlankTexture(), new RectangleF(placingPos + pos.ToVector2() * tileSize, tileSize), color * 0.25F * alpha); + this.PlacingAttraction.Draw(batch, placingPos, alpha * 0.5F, scale); + } + } + + public bool CanPlace(Point position, Attraction attraction) { + foreach (var offset in attraction.Type.GetCoveredTiles()) { + if (!this.IsInBounds(position + offset)) + return false; + if (this.GetAttractionAt(position + offset) != null) + return false; + } + return true; + } + + public void Place(Point position, Attraction attraction) { + foreach (var (x, y) in attraction.Type.GetCoveredTiles()) + this.attractionGrid[position.X + x, position.Y + y] = attraction; + this.attractions.Add((position, attraction)); + } + + public Attraction Remove(Point position) { + var attraction = this.GetAttractionAt(position); + if (attraction != null) { + foreach (var (x, y) in attraction.Type.GetCoveredTiles()) + this.attractionGrid[position.X + x, position.Y + y] = null; + this.attractions.Remove((position, attraction)); + } + return attraction; + } + + public Attraction GetAttractionAt(Point position) { + return !this.IsInBounds(position) ? null : this.attractionGrid[position.X, position.Y]; + } + + public int GetAttractionAmount(AttractionType type) { + return this.attractions.Count(a => type == null || a.Item2.Type == type); + } + + public int GetModifierAmount(AttractionModifier modifier) { + return this.attractions.Sum(a => a.Item2.GetModifierAmount(modifier)); + } + + public bool IsAnyAttractionAffected(AttractionModifier modifier) { + return this.attractions.Any(a => modifier.IsAffected(a.Item2)); + } + + public IEnumerable<(Point, Attraction)> GetAttractions() { + foreach (var attraction in this.attractions) + yield return attraction; + } + + public bool IsInBounds(Point pos) { + return pos.X >= 0 && pos.Y >= 0 && pos.X < this.Width && pos.Y < this.Height; + } + + public ParkMap Copy(int? newWidth = null, int? newHeight = null) { + var newMap = new ParkMap(newWidth ?? this.Width, newHeight ?? this.Height); + foreach (var (pos, attraction) in this.attractions) { + if (newMap.CanPlace(pos, attraction)) + newMap.Place(pos, attraction); + } + newMap.TicketsPerSecond = this.TicketsPerSecond; + return newMap; + } + + private bool TryAutoBuy() { + if (!Options.Instance.AutoBuyEnabled) + return false; + if (GameImpl.Instance.Tickets < Options.Instance.MinTicketsForAutoBuy) + return false; + if (this.autoBuyCounter < ParkMap.AutoBuyIntervalSecs) + return false; + this.autoBuyCounter -= ParkMap.AutoBuyIntervalSecs; + + var success = false; + // auto-buy modifiers + if (Upgrade.AutoPlaceModifiers[0].IsActive()) { + // loop through all attractions, but look at attractions with fewer applied modifiers first + foreach (var attraction in this.attractions.Select(kv => kv.Item2).OrderBy(a => a.GetModifierAmount(null))) { + var match = AttractionModifier.Modifiers.Values.Where(m => m.IsAffected(attraction)); + // if we don't have level 2, we only want to increase existing modifiers + if (!Upgrade.AutoPlaceModifiers[1].IsActive()) + match = match.Where(m => attraction.GetModifierAmount(m) > 0); + // we want to apply the least applied modifier on this attraction + var modifier = match.OrderBy(m => attraction.GetModifierAmount(m)).FirstOrDefault(); + if (modifier != null && modifier.Buy(attraction)) + success = true; + } + } + return success; + } + } \ No newline at end of file diff --git a/TouchyTickets/Platform.cs b/TouchyTickets/Platform.cs index 5501323..0b759fb 100644 --- a/TouchyTickets/Platform.cs +++ b/TouchyTickets/Platform.cs @@ -1,19 +1,19 @@ using System.Collections.Generic; -namespace TouchyTickets { - public abstract class Platform { +namespace TouchyTickets; - public abstract void SetupOnlineInteractions(Dictionary analyticsJson); +public abstract class Platform { - public abstract void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId); + public abstract void SetupOnlineInteractions(Dictionary analyticsJson); - public abstract void SetKeepScreenOn(bool keep); + public abstract void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId); - public abstract void OpenRateLink(); + public abstract void SetKeepScreenOn(bool keep); - public abstract bool GainAchievement(Achievement achievement); + public abstract void OpenRateLink(); - public abstract void ShowAchievements(); + public abstract bool GainAchievement(Achievement achievement); + + public abstract void ShowAchievements(); - } } \ No newline at end of file diff --git a/TouchyTickets/RainingTicket.cs b/TouchyTickets/RainingTicket.cs index 05b64be..bfdde4a 100644 --- a/TouchyTickets/RainingTicket.cs +++ b/TouchyTickets/RainingTicket.cs @@ -2,34 +2,33 @@ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; -using MLEM.Misc; using MLEM.Textures; -namespace TouchyTickets { - public class RainingTicket { +namespace TouchyTickets; - private static readonly Random Random = new Random(); - private readonly Vector2 speed; - private readonly float rotationSpeed; - private float rotation; - private Vector2 position; +public class RainingTicket { - public RainingTicket() { - this.position = new Vector2((float) Random.NextDouble(), -0.15F); - this.speed = new Vector2((float) Random.NextDouble() * 0.0005F - 0.00025F, 0.005F + (float) Random.NextDouble() * 0.01F); - this.rotationSpeed = MathHelper.ToRadians((float) Random.NextDouble() * 5); - } - - public bool Update() { - this.rotation += this.rotationSpeed; - this.position += this.speed; - return this.position.Y >= 1.15F; - } - - public void Draw(SpriteBatch batch, Vector2 viewport, float scale, Color color) { - var tex = Assets.UiTexture[2, 0]; - batch.Draw(tex, this.position * viewport, color, this.rotation, tex.Size.ToVector2() / 2, scale, SpriteEffects.None, 0); - } + private static readonly Random Random = new(); + private readonly Vector2 speed; + private readonly float rotationSpeed; + private float rotation; + private Vector2 position; + public RainingTicket() { + this.position = new Vector2(RainingTicket.Random.NextSingle(), -0.15F); + this.speed = new Vector2(RainingTicket.Random.NextSingle() * 0.0005F - 0.00025F, 0.005F + RainingTicket.Random.NextSingle() * 0.01F); + this.rotationSpeed = MathHelper.ToRadians(RainingTicket.Random.NextSingle() * 5); } + + public bool Update() { + this.rotation += this.rotationSpeed; + this.position += this.speed; + return this.position.Y >= 1.15F; + } + + public void Draw(SpriteBatch batch, Vector2 viewport, float scale, Color color) { + var tex = Assets.UiTexture[2, 0]; + batch.Draw(tex, this.position * viewport, color, this.rotation, tex.Size.ToVector2() / 2, scale, SpriteEffects.None, 0); + } + } \ No newline at end of file diff --git a/TouchyTickets/SaveHandler.cs b/TouchyTickets/SaveHandler.cs index 376cae9..7f9e6cd 100644 --- a/TouchyTickets/SaveHandler.cs +++ b/TouchyTickets/SaveHandler.cs @@ -6,92 +6,91 @@ using System.Numerics; using Newtonsoft.Json; using TouchyTickets.Upgrades; -namespace TouchyTickets { - public static class SaveHandler { +namespace TouchyTickets; - public static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings { - TypeNameHandling = TypeNameHandling.Auto, - Formatting = Formatting.Indented - }); - private const int SaveVersion = 3; +public static class SaveHandler { - public static void Save(GameImpl game) { - var file = GetSaveFile(true); - using (var stream = new JsonTextWriter(file.CreateText())) { - var data = new SaveData { - SaveVersion = SaveVersion, - Tickets = game.Tickets, - LastUpdate = game.LastUpdate, - Map = game.Map, - Stars = game.Stars, - TimesRestarted = game.TimesRestarted, - Upgrades = game.AppliedUpgrades.Select(u => u.Name).ToList(), - TutorialStep = game.Tutorial.CurrentStep, - PlayTime = game.PlayTime - }; - Serializer.Serialize(stream, data); - } - } - - public static bool Load(GameImpl game) { - var file = GetSaveFile(false); - if (!file.Exists) - return false; - SaveData data; - using (var stream = new JsonTextReader(file.OpenText())) - data = Serializer.Deserialize(stream); - - game.Tickets = data.Tickets; - game.LastUpdate = data.LastUpdate; - game.Map = data.Map.Copy(); - game.Stars = data.Stars; - game.TimesRestarted = data.TimesRestarted; - game.AppliedUpgrades.Clear(); - foreach (var name in data.Upgrades) - game.AppliedUpgrades.Add(Upgrade.Upgrades[name]); - game.Tutorial.CurrentStep = data.TutorialStep; - game.PlayTime = data.PlayTime; - - // version 1 had smaller maps - if (data.SaveVersion <= 1) { - game.Map = game.Map.Copy(20, 20); - foreach (var upgrade in game.AppliedUpgrades.Intersect(Upgrade.MapSize)) - upgrade.OnApplied(); - } - // new tutorial additions need to be forced - if (data.SaveVersion <= 2) { - if (game.Tutorial.CurrentStep > 4) - game.Tutorial.CurrentStep = 4; - } - - return true; - } - - public static DirectoryInfo GetGameDirectory(bool create) { - var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - var dir = new DirectoryInfo(Path.Combine(path, "TouchyTickets")); - if (!dir.Exists && create) - dir.Create(); - return dir; - } - - private static FileInfo GetSaveFile(bool create) { - return new FileInfo(Path.Combine(GetGameDirectory(create).FullName, "Save")); - } + public static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings { + TypeNameHandling = TypeNameHandling.Auto, + Formatting = Formatting.Indented + }); + private const int SaveVersion = 3; + public static void Save(GameImpl game) { + var file = SaveHandler.GetSaveFile(true); + using var stream = new JsonTextWriter(file.CreateText()); + var data = new SaveData { + SaveVersion = SaveHandler.SaveVersion, + Tickets = game.Tickets, + LastUpdate = game.LastUpdate, + Map = game.Map, + Stars = game.Stars, + TimesRestarted = game.TimesRestarted, + Upgrades = game.AppliedUpgrades.Select(u => u.Name).ToList(), + TutorialStep = game.Tutorial.CurrentStep, + PlayTime = game.PlayTime + }; + SaveHandler.Serializer.Serialize(stream, data); } - public class SaveData { + public static bool Load(GameImpl game) { + var file = SaveHandler.GetSaveFile(false); + if (!file.Exists) + return false; + SaveData data; + using (var stream = new JsonTextReader(file.OpenText())) + data = SaveHandler.Serializer.Deserialize(stream); - public int SaveVersion; - public BigInteger Tickets; - public DateTime LastUpdate; - public ParkMap Map; - public int Stars; - public int TimesRestarted; - public List Upgrades; - public int TutorialStep; - public TimeSpan PlayTime; + game.Tickets = data.Tickets; + game.LastUpdate = data.LastUpdate; + game.Map = data.Map.Copy(); + game.Stars = data.Stars; + game.TimesRestarted = data.TimesRestarted; + game.AppliedUpgrades.Clear(); + foreach (var name in data.Upgrades) + game.AppliedUpgrades.Add(Upgrade.Upgrades[name]); + game.Tutorial.CurrentStep = data.TutorialStep; + game.PlayTime = data.PlayTime; + // version 1 had smaller maps + if (data.SaveVersion <= 1) { + game.Map = game.Map.Copy(20, 20); + foreach (var upgrade in game.AppliedUpgrades.Intersect(Upgrade.MapSize)) + upgrade.OnApplied(); + } + // new tutorial additions need to be forced + if (data.SaveVersion <= 2) { + if (game.Tutorial.CurrentStep > 4) + game.Tutorial.CurrentStep = 4; + } + + return true; } + + public static DirectoryInfo GetGameDirectory(bool create) { + var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + var dir = new DirectoryInfo(Path.Combine(path, "TouchyTickets")); + if (!dir.Exists && create) + dir.Create(); + return dir; + } + + private static FileInfo GetSaveFile(bool create) { + return new FileInfo(Path.Combine(SaveHandler.GetGameDirectory(create).FullName, "Save")); + } + +} + +public class SaveData { + + public int SaveVersion; + public BigInteger Tickets; + public DateTime LastUpdate; + public ParkMap Map; + public int Stars; + public int TimesRestarted; + public List Upgrades; + public int TutorialStep; + public TimeSpan PlayTime; + } \ No newline at end of file diff --git a/TouchyTickets/TouchyTickets.csproj b/TouchyTickets/TouchyTickets.csproj index 13e56b6..8e972e9 100644 --- a/TouchyTickets/TouchyTickets.csproj +++ b/TouchyTickets/TouchyTickets.csproj @@ -1,16 +1,16 @@  - netstandard2.0 + net6.0 - - - + + + + all - \ No newline at end of file diff --git a/TouchyTickets/Tutorial.cs b/TouchyTickets/Tutorial.cs index 9cfb154..d1f3680 100644 --- a/TouchyTickets/Tutorial.cs +++ b/TouchyTickets/Tutorial.cs @@ -4,68 +4,68 @@ using MLEM.Ui; using MLEM.Ui.Elements; using TouchyTickets.Attractions; -namespace TouchyTickets { - public class Tutorial { +namespace TouchyTickets; - private static readonly Step[] Steps = { - // introduction - new Step(g => true, "Tutorial1"), - new Step(g => g.Tickets >= AttractionType.Carousel.InitialPrice, "Tutorial2"), - new Step(g => g.DrawMap && g.Map.PlacingAttraction?.Type == AttractionType.Carousel, "Tutorial3"), - new Step(g => g.Map.GetAttractionAmount(AttractionType.Carousel) > 0, "Tutorial4", "Tutorial5", "Tutorial6"), - // modifier tutorial - new Step(g => g.Map.GetAttractionAmount(null) >= 3, "Tutorial11"), - new Step(g => g.Map.PlacingModifier != null, "Tutorial12"), - new Step(g => g.Map.GetModifierAmount(null) > 0, "Tutorial13"), - // star tutorial - new Step(g => g.Tickets >= g.GetStarPrice(), "Tutorial7", "Tutorial8"), - new Step(g => g.Stars > 0, "Tutorial9", "Tutorial10") - }; - public int CurrentStep; - private int currentStepMessage; +public class Tutorial { - public void Update(GameImpl game) { - // stop if the tutorial is finished - if (this.CurrentStep >= Steps.Length) - return; - // don't do anything while a tutorial box is already displaying - if (game.UiSystem.Get("TutorialBox") != null) - return; + private static readonly Step[] Steps = { + // introduction + new(_ => true, "Tutorial1"), + new(g => g.Tickets >= AttractionType.Carousel.InitialPrice, "Tutorial2"), + new(g => g.DrawMap && g.Map.PlacingAttraction?.Type == AttractionType.Carousel, "Tutorial3"), + new(g => g.Map.GetAttractionAmount(AttractionType.Carousel) > 0, "Tutorial4", "Tutorial5", "Tutorial6"), + // modifier tutorial + new(g => g.Map.GetAttractionAmount(null) >= 3, "Tutorial11"), + new(g => g.Map.PlacingModifier != null, "Tutorial12"), + new(g => g.Map.GetModifierAmount(null) > 0, "Tutorial13"), + // star tutorial + new(g => g.Tickets >= g.GetStarPrice(), "Tutorial7", "Tutorial8"), + new(g => g.Stars > 0, "Tutorial9", "Tutorial10") + }; + public int CurrentStep; + private int currentStepMessage; - var step = Steps[this.CurrentStep]; - if (step.ShouldContinue(game)) { - var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { - OnDrawn = (e2, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) - }; - var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); - panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(step.Content[this.currentStepMessage]))); - panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { - OnPressed = e2 => { - game.UiSystem.Remove(e2.Root.Name); - this.currentStepMessage++; - if (this.currentStepMessage >= step.Content.Length) { - this.currentStepMessage = 0; - this.CurrentStep++; - } + public void Update(GameImpl game) { + // stop if the tutorial is finished + if (this.CurrentStep >= Tutorial.Steps.Length) + return; + // don't do anything while a tutorial box is already displaying + if (game.UiSystem.Get("TutorialBox") != null) + return; + + var step = Tutorial.Steps[this.CurrentStep]; + if (step.ShouldContinue(game)) { + var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { + OnDrawn = (e2, _, batch, _) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) + }; + var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); + panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(step.Content[this.currentStepMessage]))); + panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { + OnPressed = e2 => { + game.UiSystem.Remove(e2.Root.Name); + this.currentStepMessage++; + if (this.currentStepMessage >= step.Content.Length) { + this.currentStepMessage = 0; + this.CurrentStep++; } - }); - game.UiSystem.Add("TutorialBox", infoBox); - } + } + }); + game.UiSystem.Add("TutorialBox", infoBox); + } + } + + private class Step { + + public readonly ContinueDelegate ShouldContinue; + public readonly string[] Content; + + public Step(ContinueDelegate shouldContinue, params string[] content) { + this.ShouldContinue = shouldContinue; + this.Content = content; } - private class Step { - - public readonly ContinueDelegate ShouldContinue; - public readonly string[] Content; - - public Step(ContinueDelegate shouldContinue, params string[] content) { - this.ShouldContinue = shouldContinue; - this.Content = content; - } - - public delegate bool ContinueDelegate(GameImpl game); - - } + public delegate bool ContinueDelegate(GameImpl game); } + } \ No newline at end of file diff --git a/TouchyTickets/Ui.cs b/TouchyTickets/Ui.cs index 8123676..0f1beaf 100644 --- a/TouchyTickets/Ui.cs +++ b/TouchyTickets/Ui.cs @@ -5,11 +5,8 @@ using System.Linq; using System.Numerics; using Coroutine; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; -using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input.Touch; using MLEM.Extensions; -using MLEM.Font; using MLEM.Formatting.Codes; using MLEM.Input; using MLEM.Misc; @@ -20,731 +17,732 @@ using MLEM.Ui; using MLEM.Ui.Elements; using TouchyTickets.Attractions; using TouchyTickets.Upgrades; +using Vector2 = Microsoft.Xna.Framework.Vector2; -namespace TouchyTickets { - public class Ui { +namespace TouchyTickets; - private static readonly BigInteger[] ExpoNums = Enumerable.Range(0, Localization.NumberFormat.Count).Select(i => BigInteger.Pow(1000, i + 1)).ToArray(); - private readonly UiSystem uiSystem; - private readonly Element[] swipeRelations; - private Element currentUi; - private float swipeProgress; - private bool finishingSwipe; +public class Ui { - public Ui(UiSystem uiSystem) { - this.uiSystem = uiSystem; - foreach (var modifier in AttractionModifier.Modifiers.Values) - this.uiSystem.TextFormatter.AddImage(modifier.Name, Assets.UiTexture[modifier.Texture]); - foreach (var upgrade in Upgrade.Upgrades.Values) - this.uiSystem.TextFormatter.AddImage(upgrade.Name, Assets.UiTexture[upgrade.Texture]); + private static readonly BigInteger[] ExpoNums = Enumerable.Range(0, Localization.NumberFormat.Count).Select(i => BigInteger.Pow(1000, i + 1)).ToArray(); + private readonly UiSystem uiSystem; + private readonly Element[] swipeRelations; + private Element currentUi; + private float swipeProgress; + private bool finishingSwipe; - // main ticket store ui - var rainingTickets = new List(); - var main = new Group(Anchor.TopLeft, Vector2.One, false) { - OnUpdated = (e, time) => { - if (e.IsHidden) - return; - for (var i = rainingTickets.Count - 1; i >= 0; i--) { - if (rainingTickets[i].Update()) - rainingTickets.RemoveAt(i); - } - while (rainingTickets.Count < Math.Min(GameImpl.Instance.Map.TicketsPerSecond / 30, Options.Instance.RainingTicketLimit)) - rainingTickets.Add(new RainingTicket()); - }, - OnDrawn = (e, time, batch, alpha) => { - batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha); - foreach (var ticket in rainingTickets) - ticket.Draw(batch, e.DisplayArea.Size, e.Scale, Color.White * alpha); + public Ui(UiSystem uiSystem) { + this.uiSystem = uiSystem; + foreach (var modifier in AttractionModifier.Modifiers.Values) + this.uiSystem.TextFormatter.AddImage(modifier.Name, Assets.UiTexture[modifier.Texture]); + foreach (var upgrade in Upgrade.Upgrades.Values) + this.uiSystem.TextFormatter.AddImage(upgrade.Name, Assets.UiTexture[upgrade.Texture]); + + // main ticket store ui + var rainingTickets = new List(); + var main = new Group(Anchor.TopLeft, Vector2.One, false) { + OnUpdated = (e, _) => { + if (e.IsHidden) + return; + for (var i = rainingTickets.Count - 1; i >= 0; i--) { + if (rainingTickets[i].Update()) + rainingTickets.RemoveAt(i); } - }; - var currentNews = Localization.GetRandomNews(); - var newsTicker = main.AddChild(new Panel(Anchor.AutoCenter, new Vector2(1, 0.05F), Vector2.Zero)); - newsTicker.AddChild(new Paragraph(Anchor.CenterLeft, float.MaxValue, p => currentNews, true) { - OnUpdated = (e, time) => { - e.PositionOffset -= new Vector2(1, 0); - if (e.PositionOffset.X <= -e.DisplayArea.Width / e.Scale - 20) { - e.PositionOffset = new Vector2(e.Parent.DisplayArea.Width / e.Scale + 20, 0); - currentNews = Localization.GetRandomNews(); - } + while (rainingTickets.Count < Math.Min(GameImpl.Instance.Map.TicketsPerSecond / 30, Options.Instance.RainingTicketLimit)) + rainingTickets.Add(new RainingTicket()); + }, + OnDrawn = (e, _, batch, alpha) => { + batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha); + foreach (var ticket in rainingTickets) + ticket.Draw(batch, e.DisplayArea.Size, e.Scale, Color.White * alpha); + } + }; + var currentNews = Localization.GetRandomNews(); + var newsTicker = main.AddChild(new Panel(Anchor.AutoCenter, new Vector2(1, 0.05F), Vector2.Zero)); + newsTicker.AddChild(new Paragraph(Anchor.CenterLeft, float.MaxValue, _ => currentNews, true) { + OnUpdated = (e, _) => { + e.PositionOffset -= new Vector2(1, 0); + if (e.PositionOffset.X <= -e.DisplayArea.Width / e.Scale - 20) { + e.PositionOffset = new Vector2(e.Parent.DisplayArea.Width / e.Scale + 20, 0); + currentNews = Localization.GetRandomNews(); } - }); - var ticketGroup = main.AddChild(new Group(Anchor.AutoCenter, new Vector2(1, 0.175F), false)); - ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 10000, p => PrettyPrintNumber(GameImpl.Instance.Tickets) + "", true) { - RegularFont = Assets.MonospacedFont, - TextScale = 0.3F - }); - ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.Map.TicketsPerSecond.ToString("#,0.##") + "/s", true)); - BigInteger lastTickets = 0; - ActiveCoroutine storeWobble = null; - var storeGroup = main.AddChild(new Group(Anchor.AutoCenter, new Vector2(1, 0.425F), false) { - OnUpdated = (e, time) => { - if (lastTickets != GameImpl.Instance.Tickets) { - lastTickets = GameImpl.Instance.Tickets; - // only wobble if we're not already wobbling - if (storeWobble == null || storeWobble.IsFinished) - storeWobble = CoroutineHandler.Start(WobbleElement(e)); - } + } + }); + var ticketGroup = main.AddChild(new Group(Anchor.AutoCenter, new Vector2(1, 0.175F), false)); + ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 10000, _ => Ui.PrettyPrintNumber(GameImpl.Instance.Tickets) + "", true) { + RegularFont = Assets.MonospacedFont, + TextScale = 0.3F + }); + ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => GameImpl.Instance.Map.TicketsPerSecond.ToString("#,0.##") + "/s", true)); + BigInteger lastTickets = 0; + ActiveCoroutine storeWobble = null; + var storeGroup = main.AddChild(new Group(Anchor.AutoCenter, new Vector2(1, 0.425F), false) { + OnUpdated = (e, _) => { + if (lastTickets != GameImpl.Instance.Tickets) { + lastTickets = GameImpl.Instance.Tickets; + // only wobble if we're not already wobbling + if (storeWobble == null || storeWobble.IsFinished) + storeWobble = CoroutineHandler.Start(Ui.WobbleElement(e)); } - }); - storeGroup.AddChild(new Image(Anchor.TopLeft, Vector2.One, Assets.UiTexture[0, 0, 2, 3]) { - OnPressed = e => { - var rate = 1; - if (Upgrade.TapIncrease[2].IsActive()) { - rate = 50; - } else if (Upgrade.TapIncrease[1].IsActive()) { - rate = 10; - } else if (Upgrade.TapIncrease[0].IsActive()) { - rate = 5; - } - #if DEBUG - rate = 500000; - #endif - GameImpl.Instance.Tickets += rate; - }, - CanBeSelected = true, - CanBeMoused = true - }); - main.AddChild(new Group(Anchor.AutoLeft, new Vector2(1, 0.35F), false) { - Padding = new Padding(6, 6, 12, 6), - OnDrawn = (e, time, batch, alpha) => { - var map = GameImpl.Instance.Map; - var mapSize = new Vector2(map.Width, map.Height) * Assets.TileSize; - var (scaleX, scaleY) = e.DisplayArea.Size / mapSize; - var scale = Math.Min(scaleX, scaleY); - var pos = e.DisplayArea.Location + (e.DisplayArea.Size - mapSize * scale) / 2; - batch.Draw(this.uiSystem.Style.PanelTexture, new RectangleF(pos - new Vector2(2) * e.Scale, mapSize * scale + new Vector2(4) * e.Scale), Color.White * alpha, e.Scale); - map.Draw(time, batch, pos, scale, alpha, false, new RectangleF(Vector2.Zero, mapSize * scale)); - }, - OnPressed = e => { + } + }); + storeGroup.AddChild(new Image(Anchor.TopLeft, Vector2.One, Assets.UiTexture[0, 0, 2, 3]) { + OnPressed = _ => { + var rate = 1; + if (Upgrade.TapIncrease[2].IsActive()) { + rate = 50; + } else if (Upgrade.TapIncrease[1].IsActive()) { + rate = 10; + } else if (Upgrade.TapIncrease[0].IsActive()) { + rate = 5; + } +#if DEBUG + rate = 500000; +#endif + GameImpl.Instance.Tickets += rate; + }, + CanBeSelected = true, + CanBeMoused = true + }); + main.AddChild(new Group(Anchor.AutoLeft, new Vector2(1, 0.35F), false) { + Padding = new Padding(6, 6, 12, 6), + OnDrawn = (e, time, batch, alpha) => { + var map = GameImpl.Instance.Map; + var mapSize = new Vector2(map.Width, map.Height) * Assets.TileSize; + var (scaleX, scaleY) = e.DisplayArea.Size / mapSize; + var scale = Math.Min(scaleX, scaleY); + var pos = e.DisplayArea.Location + (e.DisplayArea.Size - mapSize * scale) / 2; + batch.Draw(this.uiSystem.Style.PanelTexture, new RectangleF(pos - new Vector2(2) * e.Scale, mapSize * scale + new Vector2(4) * e.Scale), Color.White * alpha, e.Scale); + map.Draw(time, batch, pos, scale, alpha, false, new RectangleF(Vector2.Zero, mapSize * scale)); + }, + OnPressed = _ => { + if (this.swipeProgress != 0) + return; + var map = GameImpl.Instance.Map; + var infoUi = new Group(Anchor.BottomLeft, new Vector2(1)) {CanBeMoused = false}; + Ui.AddSelectedAttractionInfo(infoUi); + infoUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { + OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) + }); + infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Remove")) { + ActionSound = new SoundEffectInfo(Assets.PlaceSound), + OnPressed = _ => { + if (map.SelectedPosition == null) + return; + map.Remove(map.SelectedPosition.Value); + map.SelectedPosition = null; + }, + OnUpdated = (e2, _) => ((Button) e2).IsDisabled = map.SelectedPosition == null + }); + // we want this to render below the main ui while it fades away + this.uiSystem.Add("MapViewInfo", infoUi).Priority = -100; + + this.FadeUi(true); + } + }); + this.currentUi = main; + this.uiSystem.Add("Main", main); + + // buy ui + var buyUi = new Group(Anchor.TopLeft, Vector2.One, false) { + IsHidden = true, + OnDrawn = (e, _, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) + }; + buyUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Attractions"), true) {TextScale = 0.15F}); + var buyList = buyUi.AddChild(new Panel(Anchor.AutoLeft, Vector2.One, Vector2.Zero, false, true, false) { + ChildPadding = new Padding(5, 15, 5, 5), + PreventParentSpill = true + }); + foreach (var attraction in AttractionType.Attractions) { + BigInteger price = 0; + var attractionAmount = 0; + var button = buyList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) { + ChildPadding = new Padding(4), + PositionOffset = new Vector2(0, 1), + OnPressed = _ => { if (this.swipeProgress != 0) return; var map = GameImpl.Instance.Map; + map.PlacingAttraction = attraction.Value.Create(); + // set placing position to center of camera's view + var (posX, posY) = (GameImpl.Instance.Camera.LookingPosition / Assets.TileSize).ToPoint(); + map.PlacingPosition = new Point(MathHelper.Clamp(posX, 0, map.Width - attraction.Value.Width), MathHelper.Clamp(posY, 0, map.Height - attraction.Value.Height)); + + var yesNoUi = new Group(Anchor.BottomLeft, new Vector2(1)); + yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Back")) { + OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) + }); + yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Place")) { + ActionSound = new SoundEffectInfo(Assets.PlaceSound), + OnPressed = e2 => { + GameImpl.Instance.Tickets -= price; + GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Attraction", attraction.Key); + + map.Place(map.PlacingPosition, map.PlacingAttraction); + map.PlacingAttraction.Wobble(); + this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)); + }, + OnUpdated = (e2, _) => ((Button) e2).IsDisabled = !map.CanPlace(map.PlacingPosition, map.PlacingAttraction) + }); + // we want this to render below the main ui while it fades away + this.uiSystem.Add("PlacingYesNo", yesNoUi).Priority = -100; + + this.FadeUi(true); + }, + OnAreaUpdated = _ => { + // only update the price when the area updates, since it won't change while we're in the buy ui + attractionAmount = GameImpl.Instance.Map.GetAttractionAmount(attraction.Value); + // yay compound interest + price = (BigInteger) Math.Ceiling(attraction.Value.InitialPrice * Math.Pow(1 + 0.1F, attractionAmount)); + } + }); + var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.AttractionTexture[attraction.Value.TextureRegion]) { + Padding = new Padding(4) + }); + var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1), false) {CanBeMoused = false}); + var name = right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(attraction.Key), true)); + var hiddenName = right.AddChild(new Paragraph(Anchor.TopLeft, 1, "?????", true) {TextColor = Color.Gray}); + var genRate = right.AddChild(new Paragraph(Anchor.BottomLeft, 1, _ => attraction.Value.GetGenerationRate() + "/s", true) {TextScale = 0.08F}); + right.AddChild(new Paragraph(Anchor.BottomRight, 1, _ => Ui.PrettyPrintNumber(price) + "", true)); + button.OnUpdated += (_, _) => { + button.IsDisabled = GameImpl.Instance.Tickets < price; + // we only want to show the info if we have enough tickets or we've placed the thing before + var shouldShow = !button.IsDisabled || attractionAmount > 0; + image.Color = shouldShow ? Color.White : Color.Black; + name.IsHidden = !shouldShow; + genRate.IsHidden = !shouldShow; + hiddenName.IsHidden = shouldShow; + }; + } + this.uiSystem.Add("Buy", buyUi); + + // modifier ui + var modifierUi = new Group(Anchor.TopLeft, Vector2.One, false) { + IsHidden = true, + OnDrawn = (e, _, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) + }; + modifierUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Modifiers"), true) {TextScale = 0.15F}); + var modifierList = modifierUi.AddChild(new Panel(Anchor.AutoLeft, Vector2.One, Vector2.Zero, false, true, false) { + ChildPadding = new Padding(5, 15, 5, 5), + PreventParentSpill = true + }); + foreach (var modifier in AttractionModifier.Modifiers.Values) { + var button = modifierList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) { + SetHeightBasedOnChildren = true, + PositionOffset = new Vector2(0, 1), + ChildPadding = new Padding(4), + OnPressed = _ => { + if (this.swipeProgress != 0) + return; + var map = GameImpl.Instance.Map; + map.PlacingModifier = modifier; var infoUi = new Group(Anchor.BottomLeft, new Vector2(1)) {CanBeMoused = false}; - AddSelectedAttractionInfo(infoUi); + Ui.AddSelectedAttractionInfo(infoUi); infoUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) }); - infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Remove")) { + var addButton = infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), string.Empty) { ActionSound = new SoundEffectInfo(Assets.PlaceSound), - OnPressed = e2 => { + OnPressed = _ => { if (map.SelectedPosition == null) return; - map.Remove(map.SelectedPosition.Value); - map.SelectedPosition = null; + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + + var placeAmount = 1; + if (Upgrade.ModifierIncrease[2].IsActive()) { + placeAmount = 10; + } else if (Upgrade.ModifierIncrease[1].IsActive()) { + placeAmount = 5; + } else if (Upgrade.ModifierIncrease[0].IsActive()) { + placeAmount = 3; + } + + for (var i = 0; i < placeAmount; i++) { + if (!map.PlacingModifier.Buy(attraction)) + break; + } + attraction.Wobble(); }, - OnUpdated = (e2, time) => ((Button) e2).IsDisabled = map.SelectedPosition == null + OnUpdated = (e2, _) => { + var disabled = map.SelectedPosition == null; + if (!disabled) { + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + disabled = attraction == null || !map.PlacingModifier.IsAffected(attraction) || GameImpl.Instance.Tickets < attraction.GetModifierPrice(map.PlacingModifier); + } + ((Button) e2).IsDisabled = disabled; + } }); + addButton.Text.GetTextCallback = _ => { + BigInteger price = map.PlacingModifier.InitialPrice; + if (map.SelectedPosition != null) { + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + if (attraction != null && map.PlacingModifier.IsAffected(attraction)) + price = attraction.GetModifierPrice(map.PlacingModifier); + } + return Ui.PrettyPrintNumber(price) + ""; + }; // we want this to render below the main ui while it fades away - this.uiSystem.Add("MapViewInfo", infoUi).Priority = -100; + this.uiSystem.Add("ModifierInfo", infoUi).Priority = -100; this.FadeUi(true); } }); - this.currentUi = main; - this.uiSystem.Add("Main", main); - - // buy ui - var buyUi = new Group(Anchor.TopLeft, Vector2.One, false) { - IsHidden = true, - OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) - }; - buyUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Attractions"), true) {TextScale = 0.15F}); - var buyList = buyUi.AddChild(new Panel(Anchor.AutoLeft, Vector2.One, Vector2.Zero, false, true, false) { - ChildPadding = new Padding(5, 15, 5, 5), - PreventParentSpill = true + var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.UiTexture[modifier.Texture]) { + Padding = new Padding(4) }); - foreach (var attraction in AttractionType.Attractions) { - BigInteger price = 0; - var attractionAmount = 0; - var button = buyList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) { - ChildPadding = new Padding(4), - PositionOffset = new Vector2(0, 1), - OnPressed = e => { - if (this.swipeProgress != 0) - return; - var map = GameImpl.Instance.Map; - map.PlacingAttraction = attraction.Value.Create(); - // set placing position to center of camera's view - var (posX, posY) = (GameImpl.Instance.Camera.LookingPosition / Assets.TileSize).ToPoint(); - map.PlacingPosition = new Point(MathHelper.Clamp(posX, 0, map.Width - attraction.Value.Width), MathHelper.Clamp(posY, 0, map.Height - attraction.Value.Height)); + var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1)) {CanBeMoused = false}); + var name = right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(modifier.Name), true)); + var hiddenName = right.AddChild(new Paragraph(Anchor.TopLeft, 1, "?????", true) {TextColor = Color.Gray}); + var desc = right.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(modifier.Name + "Description"), true) {TextScale = 0.08F}); + right.AddChild(new Paragraph(Anchor.AutoRight, 1, _ => Ui.PrettyPrintNumber(modifier.InitialPrice) + "", true)); + var genRate = right.AddChild(new Paragraph(Anchor.BottomLeft, 1, $"x{modifier.Multiplier}", true) {TextScale = 0.08F}); - var yesNoUi = new Group(Anchor.BottomLeft, new Vector2(1)); - yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Back")) { - OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) - }); - yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Place")) { - ActionSound = new SoundEffectInfo(Assets.PlaceSound), - OnPressed = e2 => { - GameImpl.Instance.Tickets -= price; - GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Attraction", attraction.Key); + var shouldShow = false; + button.OnAreaUpdated += e => { + // this is a pretty expensive operation, so only do it when the area changes + shouldShow = GameImpl.Instance.Map.IsAnyAttractionAffected(modifier); + e.SetData("Show", shouldShow); + }; + button.OnUpdated += (_, _) => { + button.IsDisabled = !shouldShow || GameImpl.Instance.Tickets < modifier.InitialPrice; + image.Color = shouldShow ? Color.White : Color.Black; + name.IsHidden = !shouldShow; + hiddenName.IsHidden = shouldShow; + genRate.IsHidden = !shouldShow; + desc.IsHidden = !shouldShow; + }; + } + modifierList.OnAreaUpdated += _ => { + // sort children by whether they should be shown or not + modifierList.ReorderChildren((e1, e2) => Comparer.Default.Compare(e2.GetData("Show"), e1.GetData("Show"))); + }; + this.uiSystem.Add("Modifiers", modifierUi); - map.Place(map.PlacingPosition, map.PlacingAttraction); - map.PlacingAttraction.Wobble(); - this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)); - }, - OnUpdated = (e2, time) => ((Button) e2).IsDisabled = !map.CanPlace(map.PlacingPosition, map.PlacingAttraction) - }); - // we want this to render below the main ui while it fades away - this.uiSystem.Add("PlacingYesNo", yesNoUi).Priority = -100; - - this.FadeUi(true); - }, - OnAreaUpdated = e => { - // only update the price when the area updates, since it won't change while we're in the buy ui - attractionAmount = GameImpl.Instance.Map.GetAttractionAmount(attraction.Value); - // yay compound interest - price = (BigInteger) Math.Ceiling(attraction.Value.InitialPrice * Math.Pow(1 + 0.1F, attractionAmount)); - } - }); - var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.AttractionTexture[attraction.Value.TextureRegion]) { - Padding = new Padding(4) - }); - var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1), false) {CanBeMoused = false}); - var name = right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(attraction.Key), true)); - var hiddenName = right.AddChild(new Paragraph(Anchor.TopLeft, 1, "?????", true) {TextColor = Color.Gray}); - var genRate = right.AddChild(new Paragraph(Anchor.BottomLeft, 1, p => attraction.Value.GetGenerationRate() + "/s", true) {TextScale = 0.08F}); - right.AddChild(new Paragraph(Anchor.BottomRight, 1, p => PrettyPrintNumber(price) + "", true)); - button.OnUpdated += (e, time) => { - button.IsDisabled = GameImpl.Instance.Tickets < price; - // we only want to show the info if we have enough tickets or we've placed the thing before - var shouldShow = !button.IsDisabled || attractionAmount > 0; - image.Color = shouldShow ? Color.White : Color.Black; - name.IsHidden = !shouldShow; - genRate.IsHidden = !shouldShow; - hiddenName.IsHidden = shouldShow; + // upgrade ui + var upgradeUi = new Group(Anchor.TopLeft, Vector2.One, false) { + IsHidden = true, + OnDrawn = (e, _, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) + }; + var upgradeHeader = upgradeUi.AddChild(new Group(Anchor.AutoLeft, new Vector2(1))); + upgradeHeader.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => GameImpl.Instance.Stars + "", true) {TextScale = 0.3F}); + upgradeHeader.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.8F, 30), Localization.Get("EarnStar")) { + PositionOffset = new Vector2(0, 4), + OnUpdated = (e, _) => ((Button) e).IsDisabled = GameImpl.Instance.GetBuyableStars() <= 0, + OnPressed = _ => { + var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { + OnDrawn = (e2, _, batch, _) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) }; - } - this.uiSystem.Add("Buy", buyUi); - - // modifier ui - var modifierUi = new Group(Anchor.TopLeft, Vector2.One, false) { - IsHidden = true, - OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) - }; - modifierUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Modifiers"), true) {TextScale = 0.15F}); - var modifierList = modifierUi.AddChild(new Panel(Anchor.AutoLeft, Vector2.One, Vector2.Zero, false, true, false) { - ChildPadding = new Padding(5, 15, 5, 5), - PreventParentSpill = true - }); - foreach (var modifier in AttractionModifier.Modifiers.Values) { - var button = modifierList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) { - SetHeightBasedOnChildren = true, - PositionOffset = new Vector2(0, 1), - ChildPadding = new Padding(4), - OnPressed = e => { - if (this.swipeProgress != 0) - return; - var map = GameImpl.Instance.Map; - map.PlacingModifier = modifier; - var infoUi = new Group(Anchor.BottomLeft, new Vector2(1)) {CanBeMoused = false}; - AddSelectedAttractionInfo(infoUi); - infoUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { - OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) - }); - var addButton = infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), string.Empty) { - ActionSound = new SoundEffectInfo(Assets.PlaceSound), - OnPressed = e2 => { - if (map.SelectedPosition == null) - return; - var attraction = map.GetAttractionAt(map.SelectedPosition.Value); - - var placeAmount = 1; - if (Upgrade.ModifierIncrease[2].IsActive()) { - placeAmount = 10; - } else if (Upgrade.ModifierIncrease[1].IsActive()) { - placeAmount = 5; - } else if (Upgrade.ModifierIncrease[0].IsActive()) { - placeAmount = 3; - } - - for (var i = 0; i < placeAmount; i++) { - if (!map.PlacingModifier.Buy(attraction)) - break; - } - attraction.Wobble(); - }, - OnUpdated = (e2, time) => { - var disabled = map.SelectedPosition == null; - if (!disabled) { - var attraction = map.GetAttractionAt(map.SelectedPosition.Value); - disabled = attraction == null || !map.PlacingModifier.IsAffected(attraction) || GameImpl.Instance.Tickets < attraction.GetModifierPrice(map.PlacingModifier); - } - ((Button) e2).IsDisabled = disabled; - } - }); - addButton.Text.GetTextCallback = p => { - BigInteger price = map.PlacingModifier.InitialPrice; - if (map.SelectedPosition != null) { - var attraction = map.GetAttractionAt(map.SelectedPosition.Value); - if (attraction != null && map.PlacingModifier.IsAffected(attraction)) - price = attraction.GetModifierPrice(map.PlacingModifier); - } - return PrettyPrintNumber(price) + ""; - }; - // we want this to render below the main ui while it fades away - this.uiSystem.Add("ModifierInfo", infoUi).Priority = -100; - - this.FadeUi(true); - } + var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); + panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, string.Format(Localization.Get("ReallyEarnStar"), GameImpl.Instance.GetBuyableStars()))); + panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { + OnPressed = e2 => this.uiSystem.Remove(e2.Root.Name) }); - var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.UiTexture[modifier.Texture]) { - Padding = new Padding(4) - }); - var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1)) {CanBeMoused = false}); - var name = right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(modifier.Name), true)); - var hiddenName = right.AddChild(new Paragraph(Anchor.TopLeft, 1, "?????", true) {TextColor = Color.Gray}); - var desc = right.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(modifier.Name + "Description"), true) {TextScale = 0.08F}); - right.AddChild(new Paragraph(Anchor.AutoRight, 1, p => PrettyPrintNumber(modifier.InitialPrice) + "", true)); - var genRate = right.AddChild(new Paragraph(Anchor.BottomLeft, 1, $"x{modifier.Multiplier}", true) {TextScale = 0.08F}); - - var shouldShow = false; - button.OnAreaUpdated += e => { - // this is a pretty expensive operation, so only do it when the area changes - shouldShow = GameImpl.Instance.Map.IsAnyAttractionAffected(modifier); - e.SetData("Show", shouldShow); - }; - button.OnUpdated += (e, time) => { - button.IsDisabled = !shouldShow || GameImpl.Instance.Tickets < modifier.InitialPrice; - image.Color = shouldShow ? Color.White : Color.Black; - name.IsHidden = !shouldShow; - hiddenName.IsHidden = shouldShow; - genRate.IsHidden = !shouldShow; - desc.IsHidden = !shouldShow; - }; - } - modifierList.OnAreaUpdated += e => { - // sort children by whether they should be shown or not - modifierList.ReorderChildren((e1, e2) => Comparer.Default.Compare(e2.GetData("Show"), e1.GetData("Show"))); - }; - this.uiSystem.Add("Modifiers", modifierUi); - - // upgrade ui - var upgradeUi = new Group(Anchor.TopLeft, Vector2.One, false) { - IsHidden = true, - OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgb(0x86bccf) * alpha) - }; - var upgradeHeader = upgradeUi.AddChild(new Group(Anchor.AutoLeft, new Vector2(1))); - upgradeHeader.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.Stars + "", true) {TextScale = 0.3F}); - upgradeHeader.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.8F, 30), Localization.Get("EarnStar")) { - PositionOffset = new Vector2(0, 4), - OnUpdated = (e, time) => ((Button) e).IsDisabled = GameImpl.Instance.GetBuyableStars() <= 0, - OnPressed = e => { - var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { - OnDrawn = (e2, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) - }; - var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); - panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, string.Format(Localization.Get("ReallyEarnStar"), GameImpl.Instance.GetBuyableStars()))); - panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { - OnPressed = e2 => this.uiSystem.Remove(e2.Root.Name) - }); - panel.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Yes")) { - ActionSound = new SoundEffectInfo(Assets.BuySound), - OnPressed = e2 => { - this.uiSystem.Remove(e2.Root.Name); - - var game = GameImpl.Instance; - game.Platform.AddResourceEvent(true, "Tickets", (float) game.Tickets, "Restart", "Restart" + game.TimesRestarted); - game.Platform.AddResourceEvent(false, "Stars", game.GetBuyableStars(), "Restart", "Restart" + game.TimesRestarted); - - game.Stars += game.GetBuyableStars(); - game.TimesRestarted++; - game.Tickets = 0; - game.Map = new ParkMap(game.Map.Width, game.Map.Height); - } - }); - this.uiSystem.Add("ReallyEarnStarBox", infoBox); - } - }); - upgradeHeader.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => string.Format(Localization.Get("RequiresTickets"), PrettyPrintNumber(GameImpl.Instance.GetStarPrice())), true) { - PositionOffset = new Vector2(0, 2) - }); - var upgradeList = upgradeUi.AddChild(new Panel(Anchor.AutoLeft, new Vector2(1), Vector2.Zero, false, true, false) { - ChildPadding = new Padding(5, 15, 5, 5) - }); - upgradeHeader.OnAreaUpdated += e => upgradeList.Size = new Vector2(1, (upgradeUi.DisplayArea.Height - upgradeHeader.DisplayArea.Height) / e.Scale); - PopulateUpgradeList(upgradeList); - this.uiSystem.Add("Upgrade", upgradeUi); - - // options ui - var optionsUi = new Group(Anchor.TopLeft, Vector2.One, false) { - IsHidden = true, - OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgba(0x86bccf) * alpha) - }; - optionsUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Options"), true) {TextScale = 0.15F}); - var optionList = optionsUi.AddChild(new Panel(Anchor.AutoLeft, new Vector2(1), Vector2.Zero, false, true, false) { - ChildPadding = new Padding(5, 15, 5, 5), - PreventParentSpill = true - }); - - optionList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("GameplayOptions"), true) { - TextScale = 0.12F - }); - optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("AutoBuyEnabled"), Options.Instance.AutoBuyEnabled) { - OnCheckStateChange = (e, value) => { - Options.Instance.AutoBuyEnabled = value; - Options.Save(); - }, - PositionOffset = new Vector2(0, 1) - }); - optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get("MinTicketsForAutoBuy") + ":")); - var num = optionList.AddChild(ElementHelper.NumberField(Anchor.AutoLeft, new Vector2(1, 20), Options.Instance.MinTicketsForAutoBuy, 1000, null, (t, value) => { - if (int.TryParse(value, out var ret)) { - Options.Instance.MinTicketsForAutoBuy = ret; - Options.Save(); - } - })); - num.PositionOffset = new Vector2(0, 1); - optionList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Achievements")) { - PositionOffset = new Vector2(0, 1), - OnPressed = e => GameImpl.Instance.Platform.ShowAchievements() - }); - - optionList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("OtherOptions"), true) { - PositionOffset = new Vector2(0, 10), - TextScale = 0.12F - }); - optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, p => Localization.Get("RainingTicketLimit") + ": " + Options.Instance.RainingTicketLimit)); - optionList.AddChild(new Slider(Anchor.AutoLeft, new Vector2(1, 20), 10, 500) { - PositionOffset = new Vector2(0, 1), - CurrentValue = Options.Instance.RainingTicketLimit, - OnValueChanged = (s, v) => { - Options.Instance.RainingTicketLimit = (int) v; - Options.Save(); - } - }); - optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, p => Localization.Get("SoundVolume") + ": " + (int) (Options.Instance.SoundVolume * 100))); - optionList.AddChild(new Slider(Anchor.AutoLeft, new Vector2(1, 20), 10, 100) { - PositionOffset = new Vector2(0, 1), - CurrentValue = Options.Instance.SoundVolume * 100, - OnValueChanged = (s, v) => { - Options.Instance.SoundVolume = v / 100; - Options.Save(); - } - }); - optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("WhileYouWereAwayMessage"), Options.Instance.WhileYouWereAwayMessage) { - PositionOffset = new Vector2(0, 1), - OnCheckStateChange = (b, value) => { - Options.Instance.WhileYouWereAwayMessage = value; - Options.Save(); - } - }); - optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("KeepScreenOn"), Options.Instance.KeepScreenOn) { - PositionOffset = new Vector2(0, 1), - OnCheckStateChange = (b, value) => { - Options.Instance.KeepScreenOn = value; - Options.Save(); - } - }); - this.uiSystem.Add("Options", optionsUi); - - this.swipeRelations = new Element[] {optionsUi, upgradeUi, main, buyUi, modifierUi}; - - var swipeTimer = 0D; - var swipeInfo = new Group(Anchor.BottomCenter, Vector2.One) { - PositionOffset = new Vector2(0, 10), - SetWidthBasedOnChildren = true, - CanBeMoused = false, - OnUpdated = (e, time) => { - if (this.swipeProgress == 0) { - if (swipeTimer > 0) { - swipeTimer -= 0.04F; - } else if (e.DrawAlpha > 0) { - e.DrawAlpha -= 0.05F; - } - } else { - swipeTimer = 1; - if (e.DrawAlpha < 1) - e.DrawAlpha += 0.05F; - } - } - }; - foreach (var ui in this.swipeRelations) { - var tex = new TextureRegion(GameImpl.Instance.SpriteBatch.GetBlankTexture()); - swipeInfo.AddChild(new Image(Anchor.AutoInlineIgnoreOverflow, new Vector2(12, 6), tex) { - MaintainImageAspect = false, - Padding = new Padding(1), - OnUpdated = (e, time) => ((Image) e).DrawAlpha = this.currentUi == ui ? 1 : 0.35F - }); - } - this.uiSystem.Add("SwipeInfo", swipeInfo).Priority = 200; - } - - public void Update(GameTime time) { - // swiping between tabs - if (!this.currentUi.IsHidden && this.uiSystem.Controls.HandleTouch && !this.currentUi.GetChildren(c => c.Horizontal && c.IsBeingScrolled, true).Any()) { - if (MlemGame.Input.GetGesture(GestureType.HorizontalDrag, out var gesture)) { - this.swipeProgress -= gesture.Delta.X / this.currentUi.DisplayArea.Width; - } else if (!this.finishingSwipe && this.swipeProgress != 0 && !MlemGame.Input.TouchState.Any()) { - // if we're not dragging or holding, we need to move back to our current ui - this.swipeProgress -= Math.Sign(this.swipeProgress) * 0.03F; - if (this.swipeProgress.Equals(0, 0.03F)) { - this.currentUi.Root.Transform = Matrix.Identity; - this.finishingSwipe = false; - this.swipeProgress = 0; - } - } - - var next = -1; - if (this.swipeProgress != 0) { - // actual swipe reaction logic - var curr = Array.IndexOf(this.swipeRelations, this.currentUi); - next = curr + Math.Sign(this.swipeProgress); - if (next >= 0 && next < this.swipeRelations.Length) { - this.swipeRelations[next].IsHidden = false; - // if we've swiped a bit, we count this as a success and move to the next ui - this.finishingSwipe = Math.Abs(this.swipeProgress) >= 0.15F; - // if we're in the process of finishing a swipe, move without requiring input - if (this.finishingSwipe) { - if (!MlemGame.Input.TouchState.Any()) - this.swipeProgress += Math.Sign(this.swipeProgress) * 0.05F; - // we're done with the swipe, so switch the active ui - if (this.swipeProgress.Equals(Math.Sign(this.swipeProgress), 0.05F)) { - this.currentUi = this.swipeRelations[next]; - this.currentUi.Root.Transform = Matrix.Identity; - this.finishingSwipe = false; - this.swipeProgress = 0; - } - } - } else { - // when we're swiping into the void, we want to stop at the max - this.swipeProgress = MathHelper.Clamp(this.swipeProgress, -1, 1); - } - - // update element positions - this.currentUi.Root.Transform = Matrix.CreateTranslation(-this.swipeProgress * this.currentUi.DisplayArea.Width, 0, 0); - if (next >= 0 && next < this.swipeRelations.Length) - this.swipeRelations[next].Root.Transform = Matrix.CreateTranslation((Math.Sign(this.swipeProgress) - this.swipeProgress) * this.swipeRelations[next].DisplayArea.Width, 0, 0); - } - for (var i = 0; i < this.swipeRelations.Length; i++) - this.swipeRelations[i].IsHidden = i != next && this.swipeRelations[i] != this.currentUi; - } - } - - public static void SetupUiSystem(UiSystem uiSystem) { - InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.FreeDrag, GestureType.Pinch, GestureType.Tap); - uiSystem.GlobalScale = 4; - uiSystem.AutoScaleWithScreen = true; - uiSystem.AutoScaleReferenceSize = new Point(720, 1280); - uiSystem.Style.Font = Assets.Font; - uiSystem.Style.PanelTexture = uiSystem.Style.TextFieldTexture = uiSystem.Style.ScrollBarBackground = uiSystem.Style.CheckboxTexture = new NinePatch(Assets.UiTexture[2, 1], 4); - uiSystem.Style.ButtonTexture = uiSystem.Style.ScrollBarScrollerTexture = new NinePatch(Assets.UiTexture[3, 1], 4); - uiSystem.Style.CheckboxCheckmark = Assets.UiTexture[4, 1]; - uiSystem.Style.TextScale = 0.1F; - uiSystem.Style.ActionSound = new SoundEffectInfo(Assets.ClickSound, 0.5F); - uiSystem.TextFormatter.AddImage("ticket", Assets.UiTexture[2, 0]); - uiSystem.TextFormatter.AddImage("star", Assets.UiTexture[3, 0]); - } - - public static IEnumerator DisplaySplash(Action loadGame) { - var splash = new Group(Anchor.TopLeft, Vector2.One, false) { - OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, Color.Black * alpha) - }; - var content = splash.AddChild(new Group(Anchor.TopLeft, Vector2.One, false) {DrawAlpha = 0}); - var center = content.AddChild(new Group(Anchor.Center, new Vector2(0.5F, 0.5F), false)); - center.AddChild(new Image(Anchor.AutoCenter, new Vector2(1, -1), Assets.UiTexture[4, 0])); - center.AddChild(new Paragraph(Anchor.AutoCenter, 10000, Localization.Get("AGameByEllpeck"), true)); - content.AddChild(new Paragraph(Anchor.BottomCenter, 1, Localization.Get("TranslationBy"), true) { - PositionOffset = new Vector2(0, 15), - TextScale = 0.08F - }); - GameImpl.Instance.UiSystem.Add("Splash", splash).Priority = 100000; - while (content.DrawAlpha < 1) { - content.DrawAlpha += 0.015F; - yield return new Wait(CoroutineEvents.Update); - } - yield return new Wait(0.5); - - var analyticsFlag = new FileInfo(Path.Combine(SaveHandler.GetGameDirectory(true).FullName, "_ReadGdpr")); - if (!analyticsFlag.Exists) { - var evt = new Event(); - var panel = splash.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); - panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get("GDPRInfo"))); - panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { - OnPressed = e2 => { - // create the (empty) flag file - using (analyticsFlag.Create()) {} - splash.RemoveChild(panel); - CoroutineHandler.RaiseEvent(evt); - } - }); - yield return new Wait(evt); - } - - yield return new Wait(0.25); - loadGame(); - yield return new Wait(0.25); - while (content.DrawAlpha > 0) { - content.DrawAlpha -= 0.015F; - yield return new Wait(CoroutineEvents.Update); - } - while (splash.DrawAlpha > 0) { - splash.DrawAlpha -= 0.015F; - yield return new Wait(CoroutineEvents.Update); - } - splash.System.Remove(splash.Root.Name); - } - - public static void DisplayWhileYouWereAway(TimeSpan passed, BigInteger ticketGen, double ticketPerSecondGen) { - if (ticketGen <= 0 && ticketPerSecondGen <= 0) - return; - var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { - OnDrawn = (e2, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) - }; - var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); - var text = string.Format(Localization.Get("WhileYouWereAway"), passed.TotalMinutes.ToString("0.#")); - if (ticketGen > 0) - text += " " + string.Format(Localization.Get("WhileYouWereAwayTickets"), PrettyPrintNumber(ticketGen)); - if (ticketPerSecondGen > 0) - text += " " + string.Format(Localization.Get("WhileYouWereAwayTps"), ticketPerSecondGen.ToString("#,0.##")); - panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, text)); - panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { - OnPressed = e2 => e2.System.Remove(e2.Root.Name) - }); - GameImpl.Instance.UiSystem.Add("WhileYouWereAway", infoBox); - } - - public static void DisplayRatePlease() { - var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { - OnDrawn = (e2, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) - }; - var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); - panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, string.Format(Localization.Get("RateInfo")))); - panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { - OnPressed = e2 => e2.System.Remove(e2.Root.Name) - }); - panel.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Rate")) { - OnPressed = e2 => { - GameImpl.Instance.Platform.OpenRateLink(); - e2.System.Remove(e2.Root.Name); - } - }); - GameImpl.Instance.UiSystem.Add("RatePlease", infoBox); - } - - private void FadeUi(bool fadeOut, Action after = null) { - IEnumerator Impl() { - // disable input handling during fade - this.uiSystem.Controls.HandleTouch = false; - GameImpl.Instance.DrawMap = true; - this.currentUi.IsHidden = false; - var alpha = 1F; - while (alpha > 0) { - alpha -= 0.03F; - this.currentUi.DrawAlpha = !fadeOut ? 1 - alpha : alpha; - yield return new Wait(CoroutineEvents.Update); - } - this.uiSystem.Controls.HandleTouch = true; - GameImpl.Instance.DrawMap = fadeOut; - this.currentUi.IsHidden = fadeOut; - // disable horizontal and vertical drag on map view to allow free drag to take priority - InputHandler.SetGesturesEnabled(!fadeOut, GestureType.HorizontalDrag, GestureType.VerticalDrag); - if (!fadeOut) { - var map = GameImpl.Instance.Map; - map.PlacingAttraction = null; - map.SelectedPosition = null; - map.PlacingModifier = null; - } - after?.Invoke(); - } - - CoroutineHandler.Start(Impl()); - } - - private static void AddSelectedAttractionInfo(Element element) { - var map = GameImpl.Instance.Map; - element.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => { - if (map.SelectedPosition == null) - return string.Empty; - var attraction = map.GetAttractionAt(map.SelectedPosition.Value); - return string.Join(" ", attraction.Modifiers.Select(m => $"{m.Amount}")); - }, true)); - element.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => { - if (map.SelectedPosition == null) - return string.Empty; - var pos = map.SelectedPosition.Value; - var attraction = map.GetAttractionAt(pos); - // beautiful - return string.Join(" ", Upgrade.Upgrades.Values - .Select(u => { - var multiplier = - u is ModifierUpgrade mod ? mod.GetCurrentMultiplier(attraction.Type) : - u is NeighborModifierUpgrade neighbor ? neighbor.GetCurrentMultiplier(attraction, map, pos) : 1; - return multiplier > 1 ? $" x{multiplier}" : null; - }).Where(s => s != null)); - }, true)); - element.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => { - if (map.SelectedPosition == null) - return string.Empty; - var pos = map.SelectedPosition.Value; - var attraction = map.GetAttractionAt(pos); - return attraction.GetGenerationRate(map, pos).ToString("#,0.##") + "/s"; - }, true)); - } - - private static void PopulateUpgradeList(Element upgradeList) { - upgradeList.RemoveChildren(c => !(c is ScrollBar)); - var reachedActive = false; - foreach (var upgrade in Upgrade.Upgrades.Values.OrderBy(u => u.IsActive())) { - // the first active upgrade should be preceded by a section separator paragraph - if (!reachedActive && upgrade.IsActive()) { - reachedActive = true; - upgradeList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("AppliedUpgrades"), true) { - PositionOffset = new Vector2(0, 4), - TextScale = 0.12F - }); - } - var button = upgradeList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) { - SetHeightBasedOnChildren = true, - PositionOffset = new Vector2(0, 1), - ChildPadding = new Padding(4), + panel.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Yes")) { ActionSound = new SoundEffectInfo(Assets.BuySound), - OnPressed = e => { - GameImpl.Instance.Stars -= upgrade.Price; - GameImpl.Instance.Platform.AddResourceEvent(true, "Stars", upgrade.Price, "Upgrade", upgrade.Name); + OnPressed = e2 => { + this.uiSystem.Remove(e2.Root.Name); - GameImpl.Instance.AppliedUpgrades.Add(upgrade); - upgrade.OnApplied(); - PopulateUpgradeList(upgradeList); + var game = GameImpl.Instance; + game.Platform.AddResourceEvent(true, "Tickets", (float) game.Tickets, "Restart", "Restart" + game.TimesRestarted); + game.Platform.AddResourceEvent(false, "Stars", game.GetBuyableStars(), "Restart", "Restart" + game.TimesRestarted); + + game.Stars += game.GetBuyableStars(); + game.TimesRestarted++; + game.Tickets = 0; + game.Map = new ParkMap(game.Map.Width, game.Map.Height); } }); + this.uiSystem.Add("ReallyEarnStarBox", infoBox); + } + }); + upgradeHeader.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => string.Format(Localization.Get("RequiresTickets"), Ui.PrettyPrintNumber(GameImpl.Instance.GetStarPrice())), true) { + PositionOffset = new Vector2(0, 2) + }); + var upgradeList = upgradeUi.AddChild(new Panel(Anchor.AutoLeft, new Vector2(1), Vector2.Zero, false, true, false) { + ChildPadding = new Padding(5, 15, 5, 5) + }); + upgradeHeader.OnAreaUpdated += e => upgradeList.Size = new Vector2(1, (upgradeUi.DisplayArea.Height - upgradeHeader.DisplayArea.Height) / e.Scale); + Ui.PopulateUpgradeList(upgradeList); + this.uiSystem.Add("Upgrade", upgradeUi); - void HideAndDisable() { - button.IsHidden = upgrade.Dependencies.Any(u => !u.IsActive()); - button.IsDisabled = upgrade.IsActive() || GameImpl.Instance.Stars < upgrade.Price; + // options ui + var optionsUi = new Group(Anchor.TopLeft, Vector2.One, false) { + IsHidden = true, + OnDrawn = (e, _, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorHelper.FromHexRgba(0x86bccf) * alpha) + }; + optionsUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("Options"), true) {TextScale = 0.15F}); + var optionList = optionsUi.AddChild(new Panel(Anchor.AutoLeft, new Vector2(1), Vector2.Zero, false, true, false) { + ChildPadding = new Padding(5, 15, 5, 5), + PreventParentSpill = true + }); + + optionList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("GameplayOptions"), true) { + TextScale = 0.12F + }); + optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("AutoBuyEnabled"), Options.Instance.AutoBuyEnabled) { + OnCheckStateChange = (_, value) => { + Options.Instance.AutoBuyEnabled = value; + Options.Save(); + }, + PositionOffset = new Vector2(0, 1) + }); + optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get("MinTicketsForAutoBuy") + ":")); + var num = optionList.AddChild(ElementHelper.NumberField(Anchor.AutoLeft, new Vector2(1, 20), Options.Instance.MinTicketsForAutoBuy, 1000, null, (_, value) => { + if (int.TryParse(value, out var ret)) { + Options.Instance.MinTicketsForAutoBuy = ret; + Options.Save(); + } + })); + num.PositionOffset = new Vector2(0, 1); + optionList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Achievements")) { + PositionOffset = new Vector2(0, 1), + OnPressed = _ => GameImpl.Instance.Platform.ShowAchievements() + }); + + optionList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("OtherOptions"), true) { + PositionOffset = new Vector2(0, 10), + TextScale = 0.12F + }); + optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, _ => Localization.Get("RainingTicketLimit") + ": " + Options.Instance.RainingTicketLimit)); + optionList.AddChild(new Slider(Anchor.AutoLeft, new Vector2(1, 20), 10, 500) { + PositionOffset = new Vector2(0, 1), + CurrentValue = Options.Instance.RainingTicketLimit, + OnValueChanged = (_, v) => { + Options.Instance.RainingTicketLimit = (int) v; + Options.Save(); + } + }); + optionList.AddChild(new Paragraph(Anchor.AutoLeft, 1, _ => Localization.Get("SoundVolume") + ": " + (int) (Options.Instance.SoundVolume * 100))); + optionList.AddChild(new Slider(Anchor.AutoLeft, new Vector2(1, 20), 10, 100) { + PositionOffset = new Vector2(0, 1), + CurrentValue = Options.Instance.SoundVolume * 100, + OnValueChanged = (_, v) => { + Options.Instance.SoundVolume = v / 100; + Options.Save(); + } + }); + optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("WhileYouWereAwayMessage"), Options.Instance.WhileYouWereAwayMessage) { + PositionOffset = new Vector2(0, 1), + OnCheckStateChange = (_, value) => { + Options.Instance.WhileYouWereAwayMessage = value; + Options.Save(); + } + }); + optionList.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 20), Localization.Get("KeepScreenOn"), Options.Instance.KeepScreenOn) { + PositionOffset = new Vector2(0, 1), + OnCheckStateChange = (_, value) => { + Options.Instance.KeepScreenOn = value; + Options.Save(); + } + }); + this.uiSystem.Add("Options", optionsUi); + + this.swipeRelations = new Element[] {optionsUi, upgradeUi, main, buyUi, modifierUi}; + + var swipeTimer = 0D; + var swipeInfo = new Group(Anchor.BottomCenter, Vector2.One) { + PositionOffset = new Vector2(0, 10), + SetWidthBasedOnChildren = true, + CanBeMoused = false, + OnUpdated = (e, _) => { + if (this.swipeProgress == 0) { + if (swipeTimer > 0) { + swipeTimer -= 0.04F; + } else if (e.DrawAlpha > 0) { + e.DrawAlpha -= 0.05F; + } + } else { + swipeTimer = 1; + if (e.DrawAlpha < 1) + e.DrawAlpha += 0.05F; + } + } + }; + foreach (var ui in this.swipeRelations) { + var tex = new TextureRegion(GameImpl.Instance.SpriteBatch.GetBlankTexture()); + swipeInfo.AddChild(new Image(Anchor.AutoInlineIgnoreOverflow, new Vector2(12, 6), tex) { + MaintainImageAspect = false, + Padding = new Padding(1), + OnUpdated = (e, _) => ((Image) e).DrawAlpha = this.currentUi == ui ? 1 : 0.35F + }); + } + this.uiSystem.Add("SwipeInfo", swipeInfo).Priority = 200; + } + + public void Update(GameTime time) { + // swiping between tabs + if (!this.currentUi.IsHidden && this.uiSystem.Controls.HandleTouch && !this.currentUi.GetChildren(c => c.Horizontal && c.IsBeingScrolled, true).Any()) { + if (MlemGame.Input.GetGesture(GestureType.HorizontalDrag, out var gesture)) { + this.swipeProgress -= gesture.Delta.X / this.currentUi.DisplayArea.Width; + } else if (!this.finishingSwipe && this.swipeProgress != 0 && !MlemGame.Input.TouchState.Any()) { + // if we're not dragging or holding, we need to move back to our current ui + this.swipeProgress -= Math.Sign(this.swipeProgress) * 0.03F; + if (this.swipeProgress.Equals(0, 0.03F)) { + this.currentUi.Root.Transform = Matrix.Identity; + this.finishingSwipe = false; + this.swipeProgress = 0; + } + } + + var next = -1; + if (this.swipeProgress != 0) { + // actual swipe reaction logic + var curr = Array.IndexOf(this.swipeRelations, this.currentUi); + next = curr + Math.Sign(this.swipeProgress); + if (next >= 0 && next < this.swipeRelations.Length) { + this.swipeRelations[next].IsHidden = false; + // if we've swiped a bit, we count this as a success and move to the next ui + this.finishingSwipe = Math.Abs(this.swipeProgress) >= 0.15F; + // if we're in the process of finishing a swipe, move without requiring input + if (this.finishingSwipe) { + if (!MlemGame.Input.TouchState.Any()) + this.swipeProgress += Math.Sign(this.swipeProgress) * 0.05F; + // we're done with the swipe, so switch the active ui + if (this.swipeProgress.Equals(Math.Sign(this.swipeProgress), 0.05F)) { + this.currentUi = this.swipeRelations[next]; + this.currentUi.Root.Transform = Matrix.Identity; + this.finishingSwipe = false; + this.swipeProgress = 0; + } + } + } else { + // when we're swiping into the void, we want to stop at the max + this.swipeProgress = MathHelper.Clamp(this.swipeProgress, -1, 1); } - button.OnUpdated += (e, time) => HideAndDisable(); - HideAndDisable(); - - button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.UiTexture[upgrade.Texture]) { - Padding = new Padding(4) - }); - var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1)) {CanBeMoused = false}); - right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(upgrade.Name), true)); - if (!reachedActive) - right.AddChild(new Paragraph(Anchor.TopRight, 1, p => upgrade.Price + "", true)); - right.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(upgrade.Name + "Description"), true) {TextScale = 0.08F}); + // update element positions + this.currentUi.Root.Transform = Matrix.CreateTranslation(-this.swipeProgress * this.currentUi.DisplayArea.Width, 0, 0); + if (next >= 0 && next < this.swipeRelations.Length) + this.swipeRelations[next].Root.Transform = Matrix.CreateTranslation((Math.Sign(this.swipeProgress) - this.swipeProgress) * this.swipeRelations[next].DisplayArea.Width, 0, 0); } + for (var i = 0; i < this.swipeRelations.Length; i++) + this.swipeRelations[i].IsHidden = i != next && this.swipeRelations[i] != this.currentUi; + } + } + + public static void SetupUiSystem(UiSystem uiSystem) { + InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.FreeDrag, GestureType.Pinch, GestureType.Tap); + uiSystem.GlobalScale = 4; + uiSystem.AutoScaleWithScreen = true; + uiSystem.AutoScaleReferenceSize = new Point(720, 1280); + uiSystem.Style.Font = Assets.Font; + uiSystem.Style.PanelTexture = uiSystem.Style.TextFieldTexture = uiSystem.Style.ScrollBarBackground = uiSystem.Style.CheckboxTexture = new NinePatch(Assets.UiTexture[2, 1], 4); + uiSystem.Style.ButtonTexture = uiSystem.Style.ScrollBarScrollerTexture = new NinePatch(Assets.UiTexture[3, 1], 4); + uiSystem.Style.CheckboxCheckmark = Assets.UiTexture[4, 1]; + uiSystem.Style.TextScale = 0.1F; + uiSystem.Style.ActionSound = new SoundEffectInfo(Assets.ClickSound, 0.5F); + uiSystem.TextFormatter.AddImage("ticket", Assets.UiTexture[2, 0]); + uiSystem.TextFormatter.AddImage("star", Assets.UiTexture[3, 0]); + } + + public static IEnumerator DisplaySplash(Action loadGame) { + var splash = new Group(Anchor.TopLeft, Vector2.One, false) { + OnDrawn = (e, _, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, Color.Black * alpha) + }; + var content = splash.AddChild(new Group(Anchor.TopLeft, Vector2.One, false) {DrawAlpha = 0}); + var center = content.AddChild(new Group(Anchor.Center, new Vector2(0.5F, 0.5F), false)); + center.AddChild(new Image(Anchor.AutoCenter, new Vector2(1, -1), Assets.UiTexture[4, 0])); + center.AddChild(new Paragraph(Anchor.AutoCenter, 10000, Localization.Get("AGameByEllpeck"), true)); + content.AddChild(new Paragraph(Anchor.BottomCenter, 1, Localization.Get("TranslationBy"), true) { + PositionOffset = new Vector2(0, 15), + TextScale = 0.08F + }); + GameImpl.Instance.UiSystem.Add("Splash", splash).Priority = 100000; + while (content.DrawAlpha < 1) { + content.DrawAlpha += 0.015F; + yield return new Wait(CoroutineEvents.Update); + } + yield return new Wait(0.5); + + var analyticsFlag = new FileInfo(Path.Combine(SaveHandler.GetGameDirectory(true).FullName, "_ReadGdpr")); + if (!analyticsFlag.Exists) { + var evt = new Event(); + var panel = splash.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); + panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get("GDPRInfo"))); + panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { + OnPressed = _ => { + // create the (empty) flag file + using (analyticsFlag.Create()) {} + splash.RemoveChild(panel); + CoroutineHandler.RaiseEvent(evt); + } + }); + yield return new Wait(evt); } - private static IEnumerator WobbleElement(Element element, float intensity = 0.02F) { - var sin = 0F; - while (sin < MathHelper.Pi) { - element.ScaleTransform(1 + (float) Math.Sin(sin) * intensity); - sin += 0.2F; + yield return new Wait(0.25); + loadGame(); + yield return new Wait(0.25); + while (content.DrawAlpha > 0) { + content.DrawAlpha -= 0.015F; + yield return new Wait(CoroutineEvents.Update); + } + while (splash.DrawAlpha > 0) { + splash.DrawAlpha -= 0.015F; + yield return new Wait(CoroutineEvents.Update); + } + splash.System.Remove(splash.Root.Name); + } + + public static void DisplayWhileYouWereAway(TimeSpan passed, BigInteger ticketGen, double ticketPerSecondGen) { + if (ticketGen <= 0 && ticketPerSecondGen <= 0) + return; + var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { + OnDrawn = (e2, _, batch, _) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) + }; + var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); + var text = string.Format(Localization.Get("WhileYouWereAway"), passed.TotalMinutes.ToString("0.#")); + if (ticketGen > 0) + text += " " + string.Format(Localization.Get("WhileYouWereAwayTickets"), Ui.PrettyPrintNumber(ticketGen)); + if (ticketPerSecondGen > 0) + text += " " + string.Format(Localization.Get("WhileYouWereAwayTps"), ticketPerSecondGen.ToString("#,0.##")); + panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, text)); + panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 30), Localization.Get("Okay")) { + OnPressed = e2 => e2.System.Remove(e2.Root.Name) + }); + GameImpl.Instance.UiSystem.Add("WhileYouWereAway", infoBox); + } + + public static void DisplayRatePlease() { + var infoBox = new Group(Anchor.TopLeft, Vector2.One, false) { + OnDrawn = (e2, _, batch, _) => batch.Draw(batch.GetBlankTexture(), e2.DisplayArea, Color.Black * 0.35F) + }; + var panel = infoBox.AddChild(new Panel(Anchor.Center, new Vector2(0.8F), Vector2.Zero, true)); + panel.AddChild(new Paragraph(Anchor.AutoLeft, 1, string.Format(Localization.Get("RateInfo")))); + panel.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { + OnPressed = e2 => e2.System.Remove(e2.Root.Name) + }); + panel.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Rate")) { + OnPressed = e2 => { + GameImpl.Instance.Platform.OpenRateLink(); + e2.System.Remove(e2.Root.Name); + } + }); + GameImpl.Instance.UiSystem.Add("RatePlease", infoBox); + } + + private void FadeUi(bool fadeOut, Action after = null) { + IEnumerator Impl() { + // disable input handling during fade + this.uiSystem.Controls.HandleTouch = false; + GameImpl.Instance.DrawMap = true; + this.currentUi.IsHidden = false; + var alpha = 1F; + while (alpha > 0) { + alpha -= 0.03F; + this.currentUi.DrawAlpha = !fadeOut ? 1 - alpha : alpha; yield return new Wait(CoroutineEvents.Update); } - element.Transform = Matrix.Identity; - } - - public static string PrettyPrintNumber(BigInteger number) { - for (var i = 0; i < Localization.NumberFormat.Count; i++) { - if (number < ExpoNums[i]) - return number.ToString(Localization.NumberFormat[i]); + this.uiSystem.Controls.HandleTouch = true; + GameImpl.Instance.DrawMap = fadeOut; + this.currentUi.IsHidden = fadeOut; + // disable horizontal and vertical drag on map view to allow free drag to take priority + InputHandler.SetGesturesEnabled(!fadeOut, GestureType.HorizontalDrag, GestureType.VerticalDrag); + if (!fadeOut) { + var map = GameImpl.Instance.Map; + map.PlacingAttraction = null; + map.SelectedPosition = null; + map.PlacingModifier = null; } - // if the number is too large, just return the highest possible - return number.ToString(Localization.NumberFormat.Last()); + after?.Invoke(); } + CoroutineHandler.Start(Impl()); } + + private static void AddSelectedAttractionInfo(Element element) { + var map = GameImpl.Instance.Map; + element.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => { + if (map.SelectedPosition == null) + return string.Empty; + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + return string.Join(" ", attraction.Modifiers.Select(m => $"{m.Amount}")); + }, true)); + element.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => { + if (map.SelectedPosition == null) + return string.Empty; + var pos = map.SelectedPosition.Value; + var attraction = map.GetAttractionAt(pos); + // beautiful + return string.Join(" ", Upgrade.Upgrades.Values + .Select(u => { + var multiplier = + u is ModifierUpgrade mod ? mod.GetCurrentMultiplier(attraction.Type) : + u is NeighborModifierUpgrade neighbor ? neighbor.GetCurrentMultiplier(attraction, map, pos) : 1; + return multiplier > 1 ? $" x{multiplier}" : null; + }).Where(s => s != null)); + }, true)); + element.AddChild(new Paragraph(Anchor.AutoCenter, 1, _ => { + if (map.SelectedPosition == null) + return string.Empty; + var pos = map.SelectedPosition.Value; + var attraction = map.GetAttractionAt(pos); + return attraction.GetGenerationRate(map, pos).ToString("#,0.##") + "/s"; + }, true)); + } + + private static void PopulateUpgradeList(Element upgradeList) { + upgradeList.RemoveChildren(c => !(c is ScrollBar)); + var reachedActive = false; + foreach (var upgrade in Upgrade.Upgrades.Values.OrderBy(u => u.IsActive())) { + // the first active upgrade should be preceded by a section separator paragraph + if (!reachedActive && upgrade.IsActive()) { + reachedActive = true; + upgradeList.AddChild(new Paragraph(Anchor.AutoCenter, 1, Localization.Get("AppliedUpgrades"), true) { + PositionOffset = new Vector2(0, 4), + TextScale = 0.12F + }); + } + var button = upgradeList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) { + SetHeightBasedOnChildren = true, + PositionOffset = new Vector2(0, 1), + ChildPadding = new Padding(4), + ActionSound = new SoundEffectInfo(Assets.BuySound), + OnPressed = _ => { + GameImpl.Instance.Stars -= upgrade.Price; + GameImpl.Instance.Platform.AddResourceEvent(true, "Stars", upgrade.Price, "Upgrade", upgrade.Name); + + GameImpl.Instance.AppliedUpgrades.Add(upgrade); + upgrade.OnApplied(); + Ui.PopulateUpgradeList(upgradeList); + } + }); + + void HideAndDisable() { + button.IsHidden = upgrade.Dependencies.Any(u => !u.IsActive()); + button.IsDisabled = upgrade.IsActive() || GameImpl.Instance.Stars < upgrade.Price; + } + + button.OnUpdated += (_, _) => HideAndDisable(); + HideAndDisable(); + + button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), Assets.UiTexture[upgrade.Texture]) { + Padding = new Padding(4) + }); + var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1)) {CanBeMoused = false}); + right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(upgrade.Name), true)); + if (!reachedActive) + right.AddChild(new Paragraph(Anchor.TopRight, 1, _ => upgrade.Price + "", true)); + right.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(upgrade.Name + "Description"), true) {TextScale = 0.08F}); + } + } + + private static IEnumerator WobbleElement(Element element, float intensity = 0.02F) { + var sin = 0F; + while (sin < MathHelper.Pi) { + element.ScaleTransform(1 + (float) Math.Sin(sin) * intensity); + sin += 0.2F; + yield return new Wait(CoroutineEvents.Update); + } + element.Transform = Matrix.Identity; + } + + public static string PrettyPrintNumber(BigInteger number) { + for (var i = 0; i < Localization.NumberFormat.Count; i++) { + if (number < Ui.ExpoNums[i]) + return number.ToString(Localization.NumberFormat[i]); + } + // if the number is too large, just return the highest possible + return number.ToString(Localization.NumberFormat.Last()); + } + } \ No newline at end of file diff --git a/TouchyTickets/Upgrades/ModifierUpgrade.cs b/TouchyTickets/Upgrades/ModifierUpgrade.cs index 3262490..a461681 100644 --- a/TouchyTickets/Upgrades/ModifierUpgrade.cs +++ b/TouchyTickets/Upgrades/ModifierUpgrade.cs @@ -1,23 +1,23 @@ using Microsoft.Xna.Framework; using TouchyTickets.Attractions; -namespace TouchyTickets.Upgrades { - public class ModifierUpgrade : Upgrade { +namespace TouchyTickets.Upgrades; - private readonly AttractionFlags requiredFlag; - private readonly float modifier; +public class ModifierUpgrade : Upgrade { - public ModifierUpgrade(string name, int price, Point texture, AttractionFlags requiredFlag, float modifier, params Upgrade[] dependencies) : - base(name, price, texture, dependencies) { - this.requiredFlag = requiredFlag; - this.modifier = modifier; - } - - public float GetCurrentMultiplier(AttractionType attraction) { - if (this.IsActive() && attraction.Flags.HasFlag(this.requiredFlag)) - return this.modifier; - return 1; - } + private readonly AttractionFlags requiredFlag; + private readonly float modifier; + public ModifierUpgrade(string name, int price, Point texture, AttractionFlags requiredFlag, float modifier, params Upgrade[] dependencies) : + base(name, price, texture, dependencies) { + this.requiredFlag = requiredFlag; + this.modifier = modifier; } + + public float GetCurrentMultiplier(AttractionType attraction) { + if (this.IsActive() && attraction.Flags.HasFlag(this.requiredFlag)) + return this.modifier; + return 1; + } + } \ No newline at end of file diff --git a/TouchyTickets/Upgrades/NeighborModifierUpgrade.cs b/TouchyTickets/Upgrades/NeighborModifierUpgrade.cs index 7c438c7..fe0ea44 100644 --- a/TouchyTickets/Upgrades/NeighborModifierUpgrade.cs +++ b/TouchyTickets/Upgrades/NeighborModifierUpgrade.cs @@ -2,25 +2,25 @@ using System.Linq; using Microsoft.Xna.Framework; using TouchyTickets.Attractions; -namespace TouchyTickets.Upgrades { - public class NeighborModifierUpgrade : Upgrade { +namespace TouchyTickets.Upgrades; - private readonly AttractionType requiredNeighbor; - private readonly AttractionFlags requiredFlag; - private readonly float modifier; +public class NeighborModifierUpgrade : Upgrade { - public NeighborModifierUpgrade(string name, int price, Point texture, AttractionType requiredNeighbor, float modifier, AttractionFlags requiredFlag = AttractionFlags.None, params Upgrade[] dependencies) : - base(name, price, texture, dependencies) { - this.requiredNeighbor = requiredNeighbor; - this.requiredFlag = requiredFlag; - this.modifier = modifier; - } - - public float GetCurrentMultiplier(Attraction attraction, ParkMap map, Point position) { - if (this.IsActive() && attraction.Type.Flags.HasFlag(this.requiredFlag) && attraction.GetSurrounding(map, position, this.requiredNeighbor).Any()) - return this.modifier; - return 1; - } + private readonly AttractionType requiredNeighbor; + private readonly AttractionFlags requiredFlag; + private readonly float modifier; + public NeighborModifierUpgrade(string name, int price, Point texture, AttractionType requiredNeighbor, float modifier, AttractionFlags requiredFlag = AttractionFlags.None, params Upgrade[] dependencies) : + base(name, price, texture, dependencies) { + this.requiredNeighbor = requiredNeighbor; + this.requiredFlag = requiredFlag; + this.modifier = modifier; } + + public float GetCurrentMultiplier(Attraction attraction, ParkMap map, Point position) { + if (this.IsActive() && attraction.Type.Flags.HasFlag(this.requiredFlag) && attraction.GetSurrounding(map, position, this.requiredNeighbor).Any()) + return this.modifier; + return 1; + } + } \ No newline at end of file diff --git a/TouchyTickets/Upgrades/Upgrade.cs b/TouchyTickets/Upgrades/Upgrade.cs index 3537e44..2a58d7a 100644 --- a/TouchyTickets/Upgrades/Upgrade.cs +++ b/TouchyTickets/Upgrades/Upgrade.cs @@ -3,64 +3,65 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using MLEM.Extensions; +using TouchyTickets.Attractions; using static TouchyTickets.Attractions.AttractionFlags; using static TouchyTickets.Attractions.AttractionType; -namespace TouchyTickets.Upgrades { - public class Upgrade { +namespace TouchyTickets.Upgrades; - public static readonly Dictionary Upgrades = new Dictionary(); - public static readonly Upgrade[] MapSize = RegisterTiers("MapSize", 5, 1, 1, new Point(0, 3)); - public static readonly Upgrade[] TapIncrease = RegisterTiers("TapIncrease", 3, 1, 0.5F, new Point(6, 3)); - public static readonly Upgrade[] ModifierIncrease = RegisterTiers("ModifierIncrease", 3, 1, 1.5F, new Point(9, 3)); - public static readonly Upgrade[] AutoPlaceModifiers = RegisterTiers("AutoPlaceModifiers", 2, 6, 1, new Point(10, 3)); - public static readonly Upgrade FerrisWheelModifier = Register(new NeighborModifierUpgrade("FerrisWheelModifier", 1, new Point(2, 3), FerrisWheel, 2)); - public static readonly Upgrade NatureModifier = Register(new ModifierUpgrade("NatureModifier", 1, new Point(8, 3), NonTechnology, 2)); - public static readonly Upgrade FoodCourtModifier = Register(new NeighborModifierUpgrade("FoodCourtModifier", 2, new Point(1, 3), FoodCourt, 2)); - public static readonly Upgrade RollerCoasterModifier = Register(new ModifierUpgrade("RollerCoasterModifier", 2, new Point(3, 3), FastCars, 2)); - public static readonly Upgrade ManualRideModifier = Register(new ModifierUpgrade("ManualRideModifier", 2, new Point(4, 3), Walking, 3)); - public static readonly Upgrade SpiralSlideModifier = Register(new NeighborModifierUpgrade("SpiralSlideModifier", 2, new Point(5, 3), SpiralSlide, 2)); - public static readonly Upgrade HauntedHouseModifier = Register(new NeighborModifierUpgrade("HauntedHouseModifier", 2, new Point(7, 3), HauntedHouse, 3, Relaxed)); +public class Upgrade { - public readonly string Name; - public readonly int Price; - public readonly Point Texture; - public readonly Upgrade[] Dependencies; + public static readonly Dictionary Upgrades = new(); + public static readonly Upgrade[] MapSize = Upgrade.RegisterTiers("MapSize", 5, 1, 1, new Point(0, 3)); + public static readonly Upgrade[] TapIncrease = Upgrade.RegisterTiers("TapIncrease", 3, 1, 0.5F, new Point(6, 3)); + public static readonly Upgrade[] ModifierIncrease = Upgrade.RegisterTiers("ModifierIncrease", 3, 1, 1.5F, new Point(9, 3)); + public static readonly Upgrade[] AutoPlaceModifiers = Upgrade.RegisterTiers("AutoPlaceModifiers", 2, 6, 1, new Point(10, 3)); + public static readonly Upgrade FerrisWheelModifier = Upgrade.Register(new NeighborModifierUpgrade("FerrisWheelModifier", 1, new Point(2, 3), AttractionType.FerrisWheel, 2)); + public static readonly Upgrade NatureModifier = Upgrade.Register(new ModifierUpgrade("NatureModifier", 1, new Point(8, 3), AttractionFlags.NonTechnology, 2)); + public static readonly Upgrade FoodCourtModifier = Upgrade.Register(new NeighborModifierUpgrade("FoodCourtModifier", 2, new Point(1, 3), AttractionType.FoodCourt, 2)); + public static readonly Upgrade RollerCoasterModifier = Upgrade.Register(new ModifierUpgrade("RollerCoasterModifier", 2, new Point(3, 3), AttractionFlags.FastCars, 2)); + public static readonly Upgrade ManualRideModifier = Upgrade.Register(new ModifierUpgrade("ManualRideModifier", 2, new Point(4, 3), AttractionFlags.Walking, 3)); + public static readonly Upgrade SpiralSlideModifier = Upgrade.Register(new NeighborModifierUpgrade("SpiralSlideModifier", 2, new Point(5, 3), AttractionType.SpiralSlide, 2)); + public static readonly Upgrade HauntedHouseModifier = Upgrade.Register(new NeighborModifierUpgrade("HauntedHouseModifier", 2, new Point(7, 3), AttractionType.HauntedHouse, 3, AttractionFlags.Relaxed)); - public Upgrade(string name, int price, Point texture, params Upgrade[] dependencies) { - this.Name = name; - this.Price = price; - this.Texture = texture; - this.Dependencies = dependencies; - } - - public void OnApplied() { - // map size upgrades - if (MapSize.Contains(this)) { - var oldMap = GameImpl.Instance.Map; - GameImpl.Instance.Map = oldMap.Copy(oldMap.Width + 10, oldMap.Height + 10); - } - } - - public bool IsActive() { - return GameImpl.Instance.AppliedUpgrades.Contains(this); - } - - private static Upgrade Register(Upgrade upgrade) { - Upgrades.Add(upgrade.Name, upgrade); - return upgrade; - } - - private static Upgrade[] RegisterTiers(string name, int amount, int startPrice, float priceIncrease, Point texture) { - var ret = new Upgrade[amount]; - for (var i = 0; i < amount; i++) { - // every tier except first depends on last tier - var deps = i == 0 ? Array.Empty() : new[] {ret[i - 1]}; - var price = (startPrice * (1 + i * priceIncrease)).Floor(); - Register(ret[i] = new Upgrade(name + (i + 1), price, texture, deps)); - } - return ret; - } + public readonly string Name; + public readonly int Price; + public readonly Point Texture; + public readonly Upgrade[] Dependencies; + public Upgrade(string name, int price, Point texture, params Upgrade[] dependencies) { + this.Name = name; + this.Price = price; + this.Texture = texture; + this.Dependencies = dependencies; } + + public void OnApplied() { + // map size upgrades + if (Upgrade.MapSize.Contains(this)) { + var oldMap = GameImpl.Instance.Map; + GameImpl.Instance.Map = oldMap.Copy(oldMap.Width + 10, oldMap.Height + 10); + } + } + + public bool IsActive() { + return GameImpl.Instance.AppliedUpgrades.Contains(this); + } + + private static Upgrade Register(Upgrade upgrade) { + Upgrade.Upgrades.Add(upgrade.Name, upgrade); + return upgrade; + } + + private static Upgrade[] RegisterTiers(string name, int amount, int startPrice, float priceIncrease, Point texture) { + var ret = new Upgrade[amount]; + for (var i = 0; i < amount; i++) { + // every tier except first depends on last tier + var deps = i == 0 ? Array.Empty() : new[] {ret[i - 1]}; + var price = (startPrice * (1 + i * priceIncrease)).Floor(); + Upgrade.Register(ret[i] = new Upgrade(name + (i + 1), price, texture, deps)); + } + return ret; + } + } \ No newline at end of file diff --git a/iOS/Default.png b/iOS/Default.png deleted file mode 100644 index a85d8dea78b62a9691712df740483350cbf0c64b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1553 zcmeAS@N?(olHy`uVBq!ia0y~yU~~ZD2OMlbkt;o0T!0i~age(c!@6@aFBuqEPkXvJ zhE&XXd-r@+Y^unyhnLlId#7#5a=fVE&K|mOQFBwVsE7>rqZJjrjZJ(mEiq24f^U5C z7bFP^y6CXBK9WD-V3Hx?+`UoJMD=UL`-Iw-ewE)(YUjN?b?@dA75jY$lhRF?fOZZV z&~P!s#A?fZ{sRZUo!UMB&fA^;HkZeJw?4Jba?gE!h7Iv70v)Oi4myl1Nt_A--VBWq zOdLlxCA&_)Ht+tSAD!!duV42k=-gY*ONTkJC=_x~m?pc5`Q6p+_HU}>?>~9*vFP6J z=hs(nRo}63+pW_7`Fc9Pq%9B3ffx%?&A_w~VdKrhSw(AY_sz1CfG+-J;<2 z>;F?e2S48ToiXpsnKwUXe!G-nr1|`{`R{LEs=jN?%=1GJN2vX882Rq}&X3c4{#;*o z%I9lxx34`rD{Ft*@7?~}Rrjx7|7~~R+VjtET4n8ps!!nZqV0jk=%^UY=h^%OEWzd!Wc`967_=eO_IN1dB4 z_WE`8caG?&>YDtTfcG!wMy{A&|8wc)ugo!fV$RRZ!W}MtJAujU^7YrYd)7;TzyJ2T z;I=t(SKq(>y|J+N_0M$sb>Ai2pU16x9$uGa!|41BM~t49dvGB3_ggp1`ga?HpZ;0@ z*7N%J{r~&zU+Y(VnYixv_8P;VTj$72%f8(8@94+JPygILHt)gK!{w(f592lQZ{p8@ zzoPzAyEFd(D_`?bcH60!&&yxEmi`~Q{dVX5=cj+ZJ@$F~I{Dt$A9Iq=umAhgAO)vC y4P*<>+Xuhr4BGs>d;^XYHfRcrA>xn?)BmyaKR#>M6eBSa diff --git a/iOS/Entitlements.plist b/iOS/Entitlements.plist deleted file mode 100644 index 9ae5993..0000000 --- a/iOS/Entitlements.plist +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/iOS/GameThumbnail.png b/iOS/GameThumbnail.png deleted file mode 100644 index e30ec8726166ba535c7eb16e938b198936698a63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 729 zcmV;~0w(>5P)Px%lSxEDRA_Q4k*8RkK7Z1*Mj_^2EwxIElDRCctljGEoHKKo@vgKYch2`Thi^RR4D)@?-M!le zhk)z%pZ(>yg?l&KT77Y92m(>G+|M!kk|B_HnX{$ja~W5-#J zj+f4E>-()z5pj}7RdiBCk+BxL5_RPXfF;_DJgc=jo7>ydjn+x!>q-{@%a=-5MJIic zX+c&Z>6mbRAdLmdgZt(73c2q2^llPVbW%l;F}ZGU|1|MHz>kn>Ci7n|5JeF_e}|W$ zdp;c@5>#~37nv5sJo)hUl8I;LbBJ?&P_ONOPY3TZS*BgDP;m0A3EDmv+lObcS3 z3=1U_S5Ch{+|xT16VJ@wK-@texhSN49*jRR@pL$dxbSreIH>5PiXvljJ$b@@QXl@Y zjJU91X{pW*z6E{|$BQ7jhPVy@S z_1&#U)@!4=e#8~8<(*AmuzOOd=%k7wMY+221OT|SX=S%aa+8i8iebP{Dj%3V0RXad zjt!y%EuLgH!7pc5(Jh;tRZrzMyp%bgbmWFZV2$hvew{hTnedI{Q)% z0vl$DJn_9TRCLl8nHDtlv$Lz0NBIBKhYZPs6e>EYqDT)9kE4Zuw5ZIsE>O;b00000 LNkvXXu0mjfEVEb6 diff --git a/iOS/Info.plist b/iOS/Info.plist deleted file mode 100644 index b9a8f19..0000000 --- a/iOS/Info.plist +++ /dev/null @@ -1,37 +0,0 @@ - - - - - CFBundleVersion - 0.1.0 - CFBundleDisplayName - Touchy Tickets - CFBundleIconFiles - - GameThumbnail.png - - CFBundleIdentifier - Ellpeck.TouchyTickets - MinimumOSVersion - 7.0 - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - CFBundleName - TouchyTickets - UIRequiresFullScreen - - UIStatusBarHidden - - UILaunchStoryboardName - LaunchScreen - - UIDeviceFamily - - 1 - 2 - - - diff --git a/iOS/IosPlatform.cs b/iOS/IosPlatform.cs deleted file mode 100644 index 828cb16..0000000 --- a/iOS/IosPlatform.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; -using TouchyTickets; - -namespace iOS { - public class IosPlatform : Platform { - - public override void SetupOnlineInteractions(Dictionary analyticsJson) { - throw new System.NotImplementedException(); - } - - public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) { - throw new System.NotImplementedException(); - } - - public override void SetKeepScreenOn(bool keep) { - throw new System.NotImplementedException(); - } - - public override void OpenRateLink() { - throw new System.NotImplementedException(); - } - - public override bool GainAchievement(Achievement achievement) { - throw new System.NotImplementedException(); - } - - public override void ShowAchievements() { - throw new System.NotImplementedException(); - } - - } -} \ No newline at end of file diff --git a/iOS/LaunchScreen.storyboard b/iOS/LaunchScreen.storyboard deleted file mode 100644 index 5d2e905..0000000 --- a/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iOS/Program.cs b/iOS/Program.cs deleted file mode 100644 index b2fd6ac..0000000 --- a/iOS/Program.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Foundation; -using MLEM.Misc; -using TouchyTickets; -using UIKit; - -namespace iOS { - [Register("AppDelegate")] - internal class Program : UIApplicationDelegate { - - private static GameImpl game; - - private static void RunGame() { - TextInputWrapper.Current = new TextInputWrapper.Mobile(); - game = new GameImpl(new IosPlatform()); - game.Run(); - } - - /// - /// The main entry point for the application. - /// - private static void Main(string[] args) { - UIApplication.Main(args, null, "AppDelegate"); - } - - public override void FinishedLaunching(UIApplication app) { - RunGame(); - } - - } -} \ No newline at end of file diff --git a/iOS/Properties/AssemblyInfo.cs b/iOS/Properties/AssemblyInfo.cs deleted file mode 100644 index a20aa1e..0000000 --- a/iOS/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Touchy Tickets")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Touchy Tickets")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] \ No newline at end of file diff --git a/iOS/iOS.csproj b/iOS/iOS.csproj deleted file mode 100644 index 1194a58..0000000 --- a/iOS/iOS.csproj +++ /dev/null @@ -1,146 +0,0 @@ - - - - - Debug - iPhoneSimulator - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F} - Exe - iOS - Resources - iOS - - - true - full - false - bin\$(Platform)\$(Configuration) - DEBUG; - prompt - 4 - false - i386, x86_64 - None - true - true - true - true - true - - - none - true - bin\$(Platform)\$(Configuration) - prompt - 4 - false - i386, x86_64 - None - true - true - - - true - full - false - bin\$(Platform)\$(Configuration) - DEBUG; - prompt - 4 - false - ARMv7, ARMv7s, ARM64 - true - true - true - true - true - Entitlements.plist - iPhone Developer - None - - - none - true - bin\$(Platform)\$(Configuration) - prompt - 4 - false - ARMv7, ARMv7s, ARM64 - true - true - Entitlements.plist - iPhone Developer - None - - - none - True - bin\$(Platform)\$(Configuration) - prompt - 4 - False - ARMv7, ARMv7s, ARM64 - true - true - Entitlements.plist - True - Automatic:AdHoc - iPhone Distribution - None - - - none - True - bin\$(Platform)\$(Configuration) - prompt - 4 - False - ARMv7, ARMv7s, ARM64 - true - true - Entitlements.plist - Automatic:AppStore - iPhone Distribution - None - - - - - - - - - - - - - - - - - - Content\Content.mgcb - - - - - - - - - - - - - - - - - - {3df7ae69-f3f0-461a-be98-f31eb576b5e2} - TouchyTickets - - - - \ No newline at end of file