using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using Coroutine; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input.Touch; using MLEM.Extensions; using MLEM.Font; using MLEM.Formatting.Codes; using MLEM.Misc; using MLEM.Startup; using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; using ThemeParkClicker.Attractions; namespace ThemeParkClicker { public class Ui { public static readonly UniformTextureAtlas Texture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Ui"), 16, 16); private readonly UiSystem uiSystem; private readonly Element[] swipeRelations; private Element currentUi; private float swipeProgress; private bool finishingSwipe; public Ui(UiSystem uiSystem) { this.uiSystem = uiSystem; this.uiSystem.GlobalScale = 4; this.uiSystem.AutoScaleWithScreen = true; this.uiSystem.AutoScaleReferenceSize = new Point(720, 1280); this.uiSystem.Style.Font = new GenericSpriteFont(MlemGame.LoadContent("Fonts/Regular")); this.uiSystem.Style.TextScale = 0.1F; this.uiSystem.TextFormatter.AddImage("ticket", Texture[2, 0]); // main ticket store ui var rainingTickets = new List(); var main = new Group(Anchor.TopLeft, Vector2.One, false) { OnUpdated = (e, time) => { for (var i = rainingTickets.Count - 1; i >= 0; i--) { if (rainingTickets[i].Update()) rainingTickets.RemoveAt(i); } while (rainingTickets.Count < BigInteger.Min(GameImpl.Instance.Tickets / 100, 500)) rainingTickets.Add(new RainingTicket()); }, OnDrawn = (e, time, batch, alpha) => { foreach (var ticket in rainingTickets) ticket.Draw(batch, e.DisplayArea.Size, e.Scale); } }; main.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.DisplayTicketCount(), true) { TextScale = 0.3F }); var storeGroup = main.AddChild(new CustomDrawGroup(Anchor.AutoLeft, Vector2.One)); storeGroup.AddChild(new Image(Anchor.TopLeft, Vector2.One, Texture[0, 0, 2, 3]) { OnPressed = e => { GameImpl.Instance.Tickets++; CoroutineHandler.Start(WobbleElement(storeGroup)); }, CanBeSelected = true, CanBeMoused = true }); main.OnAreaUpdated += e => storeGroup.Size = new Vector2(e.DisplayArea.Width / e.Scale); this.currentUi = main; this.uiSystem.Add("Main", main); // buy ui var buyUi = new Panel(Anchor.TopLeft, Vector2.One, Vector2.Zero, false, true, new Point(10, 30), false) { ChildPadding = new Padding(5, 15, 5, 5), IsHidden = true }; foreach (var attraction in Attraction.Attractions) { var instance = attraction.Value(); var button = buyUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) { ChildPadding = new Vector2(4), Padding = new Padding(0, 0, 0, 4) }); button.OnUpdated += (e, time) => { button.IsDisabled = GameImpl.Instance.Tickets < instance.GetPrice(); }; var center = button.AddChild(new Group(Anchor.Center, new Vector2(0.8F, 1), false)); center.AddChild(new Paragraph(Anchor.AutoCenter, 1, attraction.Key, true)); center.AddChild(new Paragraph(Anchor.AutoCenter, 1, instance.GenerationPerSecond + "/s", true) {TextScale = 0.08F}); var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(1), instance.TextureRegion) { Padding = new Vector2(4) }); button.OnAreaUpdated += e => image.Size = new Vector2(e.DisplayArea.Height / e.Scale); button.AddChild(new Paragraph(Anchor.CenterRight, 1, p => instance.GetPrice() + "", true)); } this.uiSystem.Add("Buy", buyUi); this.swipeRelations = new Element[] {main, buyUi}; } public void Update(GameTime time) { // swiping between tabs 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.ResetSwipe(); } if (this.swipeProgress != 0) { // actual swipe reaction logic var curr = Array.IndexOf(this.swipeRelations, this.currentUi); var 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.ResetSwipe(); } } } 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); } } private void ResetSwipe() { this.finishingSwipe = false; this.swipeProgress = 0; foreach (var element in this.swipeRelations) { element.IsHidden = element != this.currentUi; element.Root.Transform = Matrix.Identity; } } private static IEnumerator WobbleElement(CustomDrawGroup element, float intensity = 0.02F) { var sin = 0F; while (sin < MathHelper.Pi) { element.ScaleOrigin(1 + (float) Math.Sin(sin) * intensity); sin += 0.2F; yield return new WaitEvent(CoroutineEvents.Update); } element.Transform = Matrix.Identity; } } }