From 7c51c923b93d7bcd81630a966983e487ea250bc2 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 1 Jun 2020 01:21:54 +0200 Subject: [PATCH] a bunch of stuff! yay! --- ThemeParkClicker/Attractions/Attraction.cs | 15 ++- .../Content/Textures/Attractions.aseprite | Bin 1089 -> 1107 bytes .../Content/Textures/Attractions.png | Bin 1580 -> 1627 bytes ThemeParkClicker/GameImpl.cs | 32 +++++- ThemeParkClicker/ParkMap.cs | 50 +++++++--- ThemeParkClicker/RainingTicket.cs | 4 +- ThemeParkClicker/ThemeParkClicker.csproj | 1 + ThemeParkClicker/Ui.cs | 91 +++++++++++++----- 8 files changed, 147 insertions(+), 46 deletions(-) diff --git a/ThemeParkClicker/Attractions/Attraction.cs b/ThemeParkClicker/Attractions/Attraction.cs index e2813cc..11530d6 100644 --- a/ThemeParkClicker/Attractions/Attraction.cs +++ b/ThemeParkClicker/Attractions/Attraction.cs @@ -14,12 +14,12 @@ namespace ThemeParkClicker.Attractions { static Attraction() { Attractions.Add("Carousel", () => new Attraction(new[,] {{true}}, Texture[0, 0, 1, 1], 0.5F, 50)); - Attractions.Add("FoodCourt", () => new Attraction(new[,] {{true, true}}, Texture[1, 0, 2, 1], 0.7F, 300)); + Attractions.Add("FoodCourt", () => new Attraction(new[,] {{true, true}}, Texture[1, 0, 2, 1], 1.1F, 300)); } private readonly bool[,] area; - public int Width => this.area.GetLength(0); - public int Height => this.area.GetLength(1); + public int Width => this.area.GetLength(1); + public int Height => this.area.GetLength(0); public readonly TextureRegion TextureRegion; public readonly float GenerationPerSecond; public readonly long InitialPrice; @@ -32,8 +32,13 @@ namespace ThemeParkClicker.Attractions { this.InitialPrice = initialPrice; } - public bool HasTileAt(int x, int y) { - return this.area[y, x]; + 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 delegate Attraction Constructor(); diff --git a/ThemeParkClicker/Content/Textures/Attractions.aseprite b/ThemeParkClicker/Content/Textures/Attractions.aseprite index d45f86001a62e9681df307e48be7601cd761f8f2..00e2ffe341ad9fd89d515c3ef6d0c8b0b5f5a68a 100644 GIT binary patch delta 584 zcmV-O0=NCa2-64wQ;`9G0n@R8hyejglL`VGf31|yD}+H9$Ndc)T_l9HiJN+w(sAjNO%~Ph+0BapwK|8~=`Z`= zf7;B!jRVqIx(}7kJfZn-cUed7FP19uENApOFBPr%n(ri=Dr#>z&!4`;ob%W39_fx8 z?T#8hUG95V31YJTaJ10Q1m2*~*=V|loR`jDJ=NUleRa4QL*JRgLfTo#oQ{dPy1ePb z8q-<21D?;S`N?iR2F@aLwsIM_Iy)T0fAY=@_QVKz!+}297vLKS-4!%=N_4&d+xzQt zx$MK=KIy9C=rKn-Z?B|dXm@xojHP10JBp%X>0OcY<1KU8vSS0TKPoxP-||H+=EybR zr@i^R$UQ^%TYD4u63e?dGvYl${^+G|qOHYYj$8xl@_iD-Inx_l47Qh@SbxGe&Ifn= zE$a|+z~0(b<}!zQgwAq~TnBrmlbzmUudmhnq32mZ+jO&!qo+IOl<%$BSMDFGF`cvz z*Rjr^W4uG~fDin5XNZnVXEg02GHa~QV*M{hyejOlL`VGf3=j)D@0Kg$LDWgIU5OK%nT`0el#PL z8jK-f!(uQsDT^tQm9inqMo~6O6ANXb>|`w)f6>+V)>r49Id=?k>(lL>d+xoTd)|F7 z7PH&*;hh74;a|1Qv0k@5o^csU(+kN34m9M6b1vp#$`Zr(i2x=&TI4m9f=a;LxS ze;c*@!L=dlo;}~W)BEypD}uf=r9#G8$egZ;*&lh+ zhc%|NbO$`2RrBNB^$0kN%-LSay49JHe+ZU$cBn5x$Qusy(Y^pj-$sM?Yy&;iJ;x#UC5;(Sl)Fx0q^D9pc`1IILqJiMK0#ZHQ=Yc^?$M1 z`%U0WEbnrD)O&>d(M#V%N1MYOy$-)mV(o=iS($Xz(!4`&jbn_2)}Z%bqQ|?ye@Xh& zm+|ybIq1zj=_Q|ue3rR750nsV9hc7X zzS2k5Sf9mu7qYhcJaVpHhx@~aAhoyPxV~r7!SZYL4K`|_yTbbGeEMmAy6=CtPtV`Y Ed`;~wg#Z8m diff --git a/ThemeParkClicker/Content/Textures/Attractions.png b/ThemeParkClicker/Content/Textures/Attractions.png index 876ed7221d06942db25bfff73529c997d348e4c8..0a05d4af2ca5ef773307ba9314e25d80ecc8c04a 100644 GIT binary patch delta 1367 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 diff --git a/ThemeParkClicker/GameImpl.cs b/ThemeParkClicker/GameImpl.cs index 3e683fe..acb90e2 100644 --- a/ThemeParkClicker/GameImpl.cs +++ b/ThemeParkClicker/GameImpl.cs @@ -11,12 +11,25 @@ namespace ThemeParkClicker { public class GameImpl : MlemGame { public static GameImpl Instance { get; private set; } - public BigInteger Tickets = 200; + private BigInteger tickets; + public BigInteger Tickets { + get => this.tickets; + set { + var diff = value - this.tickets; + if (diff > 0) + this.ticketsPerSecondCounter += (long) diff; + this.tickets = value; + } + } public ParkMap Map { get; private set; } public Camera Camera { get; private set; } public Ui Ui { get; private set; } public bool DrawMap; + public float TicketsPerSecond { get; private set; } + private long ticketsPerSecondCounter; + private double secondCounter; + public GameImpl() { Instance = this; } @@ -26,17 +39,20 @@ namespace ThemeParkClicker { this.Ui = new Ui(this.UiSystem); this.Map = new ParkMap(10, 10); this.Camera = new Camera(this.GraphicsDevice) { - Scale = 16, + Scale = 5, AutoScaleWithScreen = true, AutoScaleReferenceSize = new Point(720, 1280), MaxScale = 24, - MinScale = 4 + MinScale = 3 }; + #if DEBUG + this.Tickets = 1000; this.Map.Place(Point.Zero, Attraction.Attractions["Carousel"]()); this.Map.Place(new Point(1, 0), Attraction.Attractions["Carousel"]()); this.Map.Place(new Point(3, 0), Attraction.Attractions["Carousel"]()); this.Map.Place(new Point(1, 2), Attraction.Attractions["FoodCourt"]()); + #endif } public string DisplayTicketCount() { @@ -47,13 +63,21 @@ namespace ThemeParkClicker { base.DoUpdate(gameTime); this.Map.Update(gameTime); this.Ui.Update(gameTime); + + // we average tickets per second over two seconds + this.secondCounter += gameTime.ElapsedGameTime.TotalSeconds; + if (this.secondCounter >= 2) { + this.secondCounter -= 2; + this.TicketsPerSecond = this.ticketsPerSecondCounter / 2F; + this.ticketsPerSecondCounter = 0; + } } 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); + this.Map.Draw(gameTime, this.SpriteBatch, Vector2.Zero, 1, 1); this.SpriteBatch.End(); } base.DoDraw(gameTime); diff --git a/ThemeParkClicker/ParkMap.cs b/ThemeParkClicker/ParkMap.cs index d2d618c..06f83c1 100644 --- a/ThemeParkClicker/ParkMap.cs +++ b/ThemeParkClicker/ParkMap.cs @@ -19,6 +19,10 @@ namespace ThemeParkClicker { private readonly Attraction[,] grid; private readonly Dictionary attractions = new Dictionary(); + public Attraction PlacingAttraction; + public Point PlacingPosition; + private bool draggingAttraction; + public ParkMap(int width, int height) { this.Width = width; this.Height = height; @@ -43,35 +47,55 @@ namespace ThemeParkClicker { camera.Zoom(-pinch.Delta.Length() / camera.Scale); } } else if (MlemGame.Input.GetGesture(GestureType.FreeDrag, out var drag)) { - camera.Position -= drag.Delta / camera.Scale; + if (this.draggingAttraction) { + // move the current placing position + var nextPos = (camera.ToWorldPos(drag.Position + drag.Delta) / Attraction.TileSize).ToPoint(); + if (nextPos.X >= 0 && nextPos.Y >= 0 && nextPos.X < this.Width && nextPos.Y < this.Height) + this.PlacingPosition = nextPos; + } else { + // move the camera + camera.Position -= drag.Delta / camera.Scale; + } + } 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) / Attraction.TileSize).ToPoint(); + this.draggingAttraction = this.PlacingAttraction.GetCoveredTiles() + .Any(p => this.PlacingPosition + p == offset); + } } } } - public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale) { + public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale, float alpha) { var tileSize = Attraction.TileSize * scale; // draw ground - batch.Draw(batch.GetBlankTexture(), new RectangleF(position, new Vector2(this.Width, this.Height) * tileSize), ColorExtensions.FromHex(0xff53a662)); + batch.Draw(batch.GetBlankTexture(), new RectangleF(position, new Vector2(this.Width, this.Height) * tileSize), ColorExtensions.FromHex(0xff53a662) * alpha); // draw attractions foreach (var kv in this.attractions) - batch.Draw(kv.Value.TextureRegion, position + kv.Key.ToVector2() * tileSize, Color.White); + batch.Draw(kv.Value.TextureRegion, position + kv.Key.ToVector2() * tileSize, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0); + // placing attraction + if (this.PlacingAttraction != null) { + var placingPos = position + this.PlacingPosition.ToVector2() * tileSize; + foreach (var pos in this.PlacingAttraction.GetCoveredTiles()) + batch.Draw(batch.GetBlankTexture(), new RectangleF(placingPos + pos.ToVector2() * tileSize, tileSize), Color.Black * 0.15F * alpha); + batch.Draw(this.PlacingAttraction.TextureRegion, placingPos, Color.White * alpha * 0.5F, 0, Vector2.Zero, scale, SpriteEffects.None, 0); + } } public bool CanPlace(Point position, Attraction attraction) { - for (var x = 0; x < attraction.Width; x++) { - for (var y = 0; y < attraction.Height; y++) { - if (this.grid[position.X + x, position.Y + y] != null) - return false; - } + foreach (var (x, y) in attraction.GetCoveredTiles()) { + if (this.grid[position.X + x, position.Y + y] != null) + return false; } return true; } public void Place(Point position, Attraction attraction) { - for (var x = 0; x < attraction.Width; x++) { - for (var y = 0; y < attraction.Height; y++) - this.grid[position.X + x, position.Y + y] = attraction; - } + foreach (var (x, y) in attraction.GetCoveredTiles()) + this.grid[position.X + x, position.Y + y] = attraction; this.attractions[position] = attraction; } diff --git a/ThemeParkClicker/RainingTicket.cs b/ThemeParkClicker/RainingTicket.cs index 7ae81ac..f26403d 100644 --- a/ThemeParkClicker/RainingTicket.cs +++ b/ThemeParkClicker/RainingTicket.cs @@ -26,9 +26,9 @@ namespace ThemeParkClicker { return this.position.Y >= 1.15F; } - public void Draw(SpriteBatch batch, Vector2 viewport, float scale) { + public void Draw(SpriteBatch batch, Vector2 viewport, float scale, Color color) { var tex = Ui.Texture[2, 0]; - batch.Draw(tex, this.position * viewport, Color.White, this.rotation, tex.Size.ToVector2() / 2, scale, SpriteEffects.None, 0); + batch.Draw(tex, this.position * viewport, color, this.rotation, tex.Size.ToVector2() / 2, scale, SpriteEffects.None, 0); } } diff --git a/ThemeParkClicker/ThemeParkClicker.csproj b/ThemeParkClicker/ThemeParkClicker.csproj index b11e499..6342d6a 100644 --- a/ThemeParkClicker/ThemeParkClicker.csproj +++ b/ThemeParkClicker/ThemeParkClicker.csproj @@ -5,6 +5,7 @@ + all diff --git a/ThemeParkClicker/Ui.cs b/ThemeParkClicker/Ui.cs index 8104e1a..4f2ac05 100644 --- a/ThemeParkClicker/Ui.cs +++ b/ThemeParkClicker/Ui.cs @@ -28,7 +28,7 @@ namespace ThemeParkClicker { private bool finishingSwipe; public Ui(UiSystem uiSystem) { - InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.FreeDrag, GestureType.Pinch); + InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.FreeDrag, GestureType.Pinch, GestureType.Tap); this.uiSystem = uiSystem; this.uiSystem.GlobalScale = 4; this.uiSystem.AutoScaleWithScreen = true; @@ -45,19 +45,22 @@ namespace ThemeParkClicker { if (rainingTickets[i].Update()) rainingTickets.RemoveAt(i); } - while (rainingTickets.Count < BigInteger.Min(GameImpl.Instance.Tickets / 100, 500)) + while (rainingTickets.Count < Math.Min(GameImpl.Instance.TicketsPerSecond / 10, 500)) rainingTickets.Add(new RainingTicket()); }, OnDrawn = (e, time, batch, alpha) => { - batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorExtensions.FromHex(0xff86cfcb)); + batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorExtensions.FromHex(0xff86cfcb) * alpha); foreach (var ticket in rainingTickets) - ticket.Draw(batch, e.DisplayArea.Size, e.Scale); + ticket.Draw(batch, e.DisplayArea.Size, e.Scale, Color.White * alpha); } }; 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 }); + ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.TicketsPerSecond.ToString("0.##") + "/s", true) { + PositionOffset = new Vector2(0, -8) + }); BigInteger lastTickets = 0; var storeGroup = main.AddChild(new CustomDrawGroup(Anchor.AutoCenter, new Vector2(1, 0.5F), null, null, false) { OnUpdated = (e, time) => { @@ -80,9 +83,21 @@ namespace ThemeParkClicker { 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); + map.Draw(time, batch, pos, scale, alpha); }, - OnPressed = e => CoroutineHandler.Start(this.SwipeUi(true)) + OnPressed = e => { + var map = GameImpl.Instance.Map; + GameImpl.Instance.Camera.LookingPosition = new Vector2(map.Width, map.Height) / 2 * Attraction.TileSize; + + var backUi = new Group(Anchor.BottomLeft, new Vector2(1)); + backUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(1, 40), "Back") { + OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) + }); + // we want this to render below the main ui while it fades away + this.uiSystem.Add("MapViewBack", backUi).Priority = -100; + + this.FadeUi(true); + } }); this.currentUi = main; this.uiSystem.Add("Main", main); @@ -96,12 +111,36 @@ namespace ThemeParkClicker { 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) + Padding = new Padding(0, 0, 0, 4), + OnPressed = e => { + var map = GameImpl.Instance.Map; + var pos = new Vector2(map.Width, map.Height) / 2; + GameImpl.Instance.Camera.LookingPosition = (pos + new Vector2(0.5F)) * Attraction.TileSize; + map.PlacingAttraction = attraction.Value(); + map.PlacingPosition = pos.ToPoint(); + + var yesNoUi = new Group(Anchor.BottomLeft, new Vector2(1)); + yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 40), "Back") { + OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) + }); + yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 40), "Place") { + OnPressed = e2 => { + GameImpl.Instance.Tickets -= map.PlacingAttraction.GetPrice(); + map.Place(map.PlacingPosition, map.PlacingAttraction); + 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); + } }); 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)); + var center = button.AddChild(new Group(Anchor.Center, new Vector2(0.8F, 1), false) {CanBeMoused = 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) { @@ -167,21 +206,29 @@ namespace ThemeParkClicker { } } - private IEnumerator 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); + 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 WaitEvent(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) + GameImpl.Instance.Map.PlacingAttraction = null; + after?.Invoke(); } - 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); + + CoroutineHandler.Start(Impl()); } private static IEnumerator WobbleElement(CustomDrawGroup element, float intensity = 0.02F) {