diff --git a/Android/AndroidPlatform.cs b/Android/AndroidPlatform.cs index ab876bd..6ec3779 100644 --- a/Android/AndroidPlatform.cs +++ b/Android/AndroidPlatform.cs @@ -8,6 +8,7 @@ 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; @@ -15,12 +16,40 @@ using Coroutine; using GameAnalyticsSDK; using GameAnalyticsSDK.Utilities; using TouchyTickets; +using Achievement = TouchyTickets.Achievement; using Uri = Android.Net.Uri; namespace Android { public class AndroidPlatform : Platform { public const int GooglePlayLoginRequest = 9001; + public const int ShowAchievementsRequest = 9002; + + 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"}, + {"1.00MTickets", "CgkI_Lyp7PcEEAIQDw"}, + {"1.00BTickets", "CgkI_Lyp7PcEEAIQEA"}, + {"1.00TTickets", "CgkI_Lyp7PcEEAIQEQ"}, + {"1.00QTickets", "CgkI_Lyp7PcEEAIQEg"}, + {"1.00QiTickets", "CgkI_Lyp7PcEEAIQEw"}, + {"1.00STickets", "CgkI_Lyp7PcEEAIQFA"}, + {"1.00SpTickets", "CgkI_Lyp7PcEEAIQFQ"}, + {"1.00OTickets", "CgkI_Lyp7PcEEAIQFg"}, + {"1.00NTickets", "CgkI_Lyp7PcEEAIQFw"}, + {"1.00DTickets", "CgkI_Lyp7PcEEAIQGA"} + }; + private readonly Activity activity; private readonly LinearLayout adLayout; public GoogleApiClient GoogleApi { get; private set; } @@ -59,6 +88,11 @@ namespace Android { }) .Build(); this.GoogleApi.Connect(); + + // Sanity check to ensure that all achievements are mapped + foreach (var achievement in Achievement.Achievements.Values) { + var _ = AchievementIds[achievement.Name]; + } } public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) { @@ -77,5 +111,17 @@ namespace Android { this.activity.StartActivity(new Intent(Intent.ActionView, Uri.Parse("https://play.google.com/store/apps/details?id=de.ellpeck.touchytickets"))); } + public override void GainAchievement(Achievement achievement) { + if (this.GoogleApi != null) + GamesClass.Achievements.Unlock(this.GoogleApi, AchievementIds[achievement.Name]); + } + + public override void ShowAchievements() { + if (this.GoogleApi == null) + return; + var intent = GamesClass.Achievements.GetAchievementsIntent(this.GoogleApi); + this.activity.StartActivityForResult(intent, ShowAchievementsRequest); + } + } } \ No newline at end of file diff --git a/Ideas.md b/Ideas.md index 149118f..797277d 100644 --- a/Ideas.md +++ b/Ideas.md @@ -8,10 +8,6 @@ # Achievements - Each award has a "gotten for the first time" achievement -- Getting the first star -- Filling each space on the map -- Only attractions of a certain type -- High amount of modifiers on each ride # More star upgrades - Upgrade that allows you to set park placement templates diff --git a/Media/Achievements/FullMap.png b/Media/Achievements/FullMap.png new file mode 100644 index 0000000..da54adb Binary files /dev/null and b/Media/Achievements/FullMap.png differ diff --git a/Media/Achievements/Modifiers.png b/Media/Achievements/Modifiers.png new file mode 100644 index 0000000..82a47e4 Binary files /dev/null and b/Media/Achievements/Modifiers.png differ diff --git a/Media/Achievements/OnlyNonTechnologyRides.png b/Media/Achievements/OnlyNonTechnologyRides.png new file mode 100644 index 0000000..73bd778 Binary files /dev/null and b/Media/Achievements/OnlyNonTechnologyRides.png differ diff --git a/Media/Achievements/OnlyRelaxingRides.png b/Media/Achievements/OnlyRelaxingRides.png new file mode 100644 index 0000000..8b82efb Binary files /dev/null and b/Media/Achievements/OnlyRelaxingRides.png differ diff --git a/Media/Achievements/OnlySmallRides.png b/Media/Achievements/OnlySmallRides.png new file mode 100644 index 0000000..8fb6c50 Binary files /dev/null and b/Media/Achievements/OnlySmallRides.png differ diff --git a/Media/Achievements/OnlyWalkingRides.png b/Media/Achievements/OnlyWalkingRides.png new file mode 100644 index 0000000..93e8385 Binary files /dev/null and b/Media/Achievements/OnlyWalkingRides.png differ diff --git a/Media/Achievements/Star.png b/Media/Achievements/Star.png new file mode 100644 index 0000000..77461d2 Binary files /dev/null and b/Media/Achievements/Star.png differ diff --git a/Media/Achievements/Tickets.png b/Media/Achievements/Tickets.png new file mode 100644 index 0000000..f1316ea Binary files /dev/null and b/Media/Achievements/Tickets.png differ diff --git a/TouchyTickets/Achievement.cs b/TouchyTickets/Achievement.cs new file mode 100644 index 0000000..e49d116 --- /dev/null +++ b/TouchyTickets/Achievement.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Microsoft.Xna.Framework; +using TouchyTickets.Attractions; + +namespace TouchyTickets { + public class Achievement { + + public static readonly Dictionary Achievements = new Dictionary(); + + 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; + } + } + 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.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($"{Ui.PrettyPrintNumber(amount).Replace(" ", "")}Tickets", 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; + GameImpl.Instance.Platform.GainAchievement(this); + this.unlocked = true; + } + + public static void Register(Achievement achievement) { + Achievements.Add(achievement.Name, achievement); + } + + } +} \ No newline at end of file diff --git a/TouchyTickets/GameImpl.cs b/TouchyTickets/GameImpl.cs index 875ab75..0577e80 100644 --- a/TouchyTickets/GameImpl.cs +++ b/TouchyTickets/GameImpl.cs @@ -25,6 +25,7 @@ namespace TouchyTickets { public DateTime LastUpdate; public TimeSpan PlayTime; private double saveCounter; + private double achievementCounter; public GameImpl(Platform platform) { this.Platform = platform; @@ -78,6 +79,15 @@ namespace TouchyTickets { 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) { @@ -122,7 +132,7 @@ namespace TouchyTickets { 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); diff --git a/TouchyTickets/ParkMap.cs b/TouchyTickets/ParkMap.cs index 7bf4daf..87f228e 100644 --- a/TouchyTickets/ParkMap.cs +++ b/TouchyTickets/ParkMap.cs @@ -234,6 +234,11 @@ namespace TouchyTickets { 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; } diff --git a/TouchyTickets/Platform.cs b/TouchyTickets/Platform.cs index 00db7d3..9abe5bc 100644 --- a/TouchyTickets/Platform.cs +++ b/TouchyTickets/Platform.cs @@ -11,5 +11,9 @@ namespace TouchyTickets { public abstract void OpenRateLink(); + public abstract void GainAchievement(Achievement achievement); + + public abstract void ShowAchievements(); + } } \ No newline at end of file diff --git a/TouchyTickets/Ui.cs b/TouchyTickets/Ui.cs index 1596e3b..32ab019 100644 --- a/TouchyTickets/Ui.cs +++ b/TouchyTickets/Ui.cs @@ -393,6 +393,10 @@ namespace TouchyTickets { } })); 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), @@ -733,7 +737,7 @@ namespace TouchyTickets { element.Transform = Matrix.Identity; } - private static string PrettyPrintNumber(BigInteger number) { + 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]); diff --git a/iOS/IosPlatform.cs b/iOS/IosPlatform.cs index 7bb03ed..b3b4374 100644 --- a/iOS/IosPlatform.cs +++ b/iOS/IosPlatform.cs @@ -20,5 +20,13 @@ namespace iOS { throw new System.NotImplementedException(); } + public override void GainAchievement(Achievement achievement) { + throw new System.NotImplementedException(); + } + + public override void ShowAchievements() { + throw new System.NotImplementedException(); + } + } } \ No newline at end of file