TouchyTickets/ThemeParkClicker/Ui.cs
2020-05-31 21:16:50 +02:00

198 lines
No EOL
9.9 KiB
C#

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.Input;
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<Texture2D>("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) {
InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.FreeDrag, GestureType.Pinch);
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<SpriteFont>("Fonts/Regular"));
this.uiSystem.Style.TextScale = 0.1F;
this.uiSystem.TextFormatter.AddImage("ticket", Texture[2, 0]);
// main ticket store ui
var rainingTickets = new List<RainingTicket>();
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) => {
batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorExtensions.FromHex(0xff86cfcb));
foreach (var ticket in rainingTickets)
ticket.Draw(batch, e.DisplayArea.Size, e.Scale);
}
};
var ticketGroup = main.AddChild(new Group(Anchor.AutoCenter, new Vector2(1, 0.15F), false));
ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.DisplayTicketCount(), true) {
TextScale = 0.3F
});
BigInteger lastTickets = 0;
var storeGroup = main.AddChild(new CustomDrawGroup(Anchor.AutoCenter, new Vector2(1, 0.5F), null, null, false) {
OnUpdated = (e, time) => {
if (lastTickets != GameImpl.Instance.Tickets) {
lastTickets = GameImpl.Instance.Tickets;
CoroutineHandler.Start(WobbleElement((CustomDrawGroup) e));
}
}
});
storeGroup.AddChild(new Image(Anchor.TopLeft, Vector2.One, Texture[0, 0, 2, 3]) {
OnPressed = e => GameImpl.Instance.Tickets++,
CanBeSelected = true,
CanBeMoused = true
});
main.AddChild(new Group(Anchor.AutoLeft, new Vector2(1, 0.35F), false) {
Padding = new Padding(4, 4, 12, 4),
OnDrawn = (e, time, batch, alpha) => {
var map = GameImpl.Instance.Map;
var mapSize = new Vector2(map.Width, map.Height) * Attraction.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;
map.Draw(time, batch, pos, scale);
},
OnPressed = e => CoroutineHandler.Start(this.SwipeUi(true))
});
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 + "<i ticket>/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() + "<i ticket>", true));
}
this.uiSystem.Add("Buy", buyUi);
this.swipeRelations = new Element[] {main, buyUi};
}
public void Update(GameTime time) {
// swiping between tabs
if (!this.currentUi.IsHidden) {
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 IEnumerator<IWait> SwipeUi(bool swipeOut) {
GameImpl.Instance.DrawMap = true;
this.currentUi.IsHidden = false;
var offset = 0F;
while (offset < 1) {
offset += 0.025F;
var trans = (!swipeOut ? 1 - offset : offset) * this.currentUi.DisplayArea.Height;
this.currentUi.Root.Transform = Matrix.CreateTranslation(0, trans, 0);
yield return new WaitEvent(CoroutineEvents.Update);
}
this.currentUi.Root.Transform = Matrix.Identity;
GameImpl.Instance.DrawMap = swipeOut;
this.currentUi.IsHidden = swipeOut;
// disable horizontal and vertical drag on map view to allow free drag to take priority
InputHandler.SetGesturesEnabled(!swipeOut, GestureType.HorizontalDrag, GestureType.VerticalDrag);
}
private static IEnumerator<IWait> 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;
}
}
}