From 3feb5f86dae937ec6b0fd10d91ce4c544cbc6faa Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 1 Jun 2020 23:02:47 +0200 Subject: [PATCH] the start of upgrades! --- TouchyTickets/Attractions/AttractionType.cs | 2 +- TouchyTickets/Content/Textures/Ui.aseprite | Bin 1126 -> 1402 bytes TouchyTickets/Content/Textures/Ui.png | Bin 1587 -> 2033 bytes TouchyTickets/GameImpl.cs | 27 ++---- TouchyTickets/ParkMap.cs | 20 +++-- TouchyTickets/SaveHandler.cs | 16 +++- TouchyTickets/TouchyTickets.csproj | 2 +- TouchyTickets/Ui.cs | 91 +++++++++++++++++--- TouchyTickets/Upgrade.cs | 56 ++++++++++++ 9 files changed, 177 insertions(+), 37 deletions(-) create mode 100644 TouchyTickets/Upgrade.cs diff --git a/TouchyTickets/Attractions/AttractionType.cs b/TouchyTickets/Attractions/AttractionType.cs index 0d56fcb..642c378 100644 --- a/TouchyTickets/Attractions/AttractionType.cs +++ b/TouchyTickets/Attractions/AttractionType.cs @@ -38,7 +38,7 @@ namespace TouchyTickets.Attractions { } private static void Register(AttractionType type) { - Attractions[type.Name] = type; + Attractions.Add(type.Name, type); } public delegate Attraction Constructor(AttractionType type); diff --git a/TouchyTickets/Content/Textures/Ui.aseprite b/TouchyTickets/Content/Textures/Ui.aseprite index 9038f767be0ed7d6bb082e727eaa85f3ce225457..e8386441131fffd5c5fc27bf25924a958882420a 100644 GIT binary patch delta 905 zcmV;419tr82>J>FdIgaJeggUgv4V#Ie{ll<00kfb000010RI92000000000!06zeD zoaLCmYZO5g$Ag7UDg_%A16o`Hi6(a>htU{W>^=ChyR&n*@3ZrLZvt&-Kte7xW9cm7JN-GA6)B?gtfx)QOA7j!@-8BvJGMW_4T+3WtZOM>s7~?jrqst z2d2ulPHxTLSi8E$eC(uWk9QCEZE~@L8S=49pT>~J4d#CDlKJv++wfzI@qHUJ@qzj8 z?-wI+c8#BVanf228PnK_iLW;ue@kiW&3VE6=lh9~__}NSF>2X1Cb@miX5Ah3I;+d3vnQU40vja;w!hJkL2@ zeH|X8y!N)wL=|ysHD1T_TQW-zNOt%f-2Z;|4yENze<#ZO8+w-X zGuSn5_?&VhCKp>Mw>BpxH}6>?=Oj1JTU$4cNsgu9t{Pf>BX5mG-V<#c!tXKHR^O?( zd41bhID>Y~@0C1{{oK~u<#QW{+}eE3=ak0n@;CQ3ey`2TalK`I^R=>#$#b%gY4dWd zZ_23&<+R@9bIPYF<;2~je{$fai1iJ%gm0_8xl?9$vpc4(#q;X8@4Pln*GSDNgL0x= zX&XcM@6?`S@M`R3%x9r8`h_r1+ECs*!i_jy%sht@@|+)XGor!U@j zyM~lHk0t}m)5QL!chfN>FylCNUQNX`@$TI28=Hn(%dNWysA1x}|L&upF00Z0xYyIea`*PeO@{<0vsWItu4$vh|Q(c f`85%x_pYkJdska}SZK7VZ&!^rzn}jHRe-x22%zJD delta 627 zcmV-(0*w9o3g!p`W(1J}egftLv4V#Ie_a9q00kfb009620RI92000000000k051S| zoYj}XN&`U*hI{uWcu}#S2Q65&UD{%`7O{v@s2=oC1Q9%V^a1(+zKRc^&!F$1PvT`I zg_Jl=cD6N1+l8Om$<9u`X8)b-6bgTj=dF#@LIvXka=cw%s>+%hnO{#DLt(A3e?M(3 z#@_9;(n(s5n~K&;BPKC_ecYy#v|O#Juy$Ip7(3|kzCSvbMA^gVY$1*tnH$M|`uW~V zX&+X+rrCIEaZ;W8-#t1S=-A}Ok&TfH}r* zXs?d>JZs%R#;~Q{hIt3QHB$jMf9LUO)^GF)a{qiMBXPMQ{?u8|_v2W}-_vqae@iTsf$P)X8eYe9 zPoGE}VJrEBj`8Ns@(1+>-p`Hm@HefM6br9~?;$tN!rw5bhqQeH^La^=f7?@9`%SAo zyvN=?DXygbc6CgryPIr0cAbku4ozm{hFs8E2>uhjNG%Bg-Yw3sdbmvH}6G}w{bQ(D|t z^^&VJy|QS`l-)y`%y8g4V=2q^8w5G7kB835qdj)SqirnLd#h5%Jl!GT(eoI~3K zhe#k*<(eZBmmH{+K+;n$6g6CWBB4YoQhTWsDHpCeQHetjoIt4z2T*@Piir{S0`{7X z`P&|rSdGoS@qgCt(sDfP?7zRwd>*hm zdKz6gG`hd754t#hy{-3a#kEV_rJud~T%!XGbk`P~cx!j*(CGdshBQbcG7Af*xv>GAn5cmAkS z;qr^rz1?NCS-|0g2TCVD9*V8Qy_F1LIzM0cgSQU%MnrW6kPiHai0yB_AH@e>MnpuR zFjw{cLZO^lXb7Mt18CcLf044<6@f+>K+@|+M11l2-Z=8X&G_Q;y>;pTmGQoahdT)evBO;F~DS&cvxRBb5q& z`0Zrfmrk7-i4Tw6jcxsn_t=~|Gm`n;iHD`wIXxJKVzKR8gOlFBHq}oWfe4d-kXG3V zn15cX`|#M^s^d~kJzCjVyE5LFInFADdSg<`Rd{{>*FDh)uR zxBt>XogVj}JHMl|O+YkJOAXr_MjX;M+_s50X|4dyf#l*w(F{`ZlT12GI8`Ue%0hkDc)qhvD z6?kWUa5aAmu)?a|1{@v!;fg)@@`MZkR$J!Z16T>h|FHV1g7BR^+e>?YysNI#;>bDH$VyHLj{w0!oC(i;_S64(iAb;}4jz#zTx678n!T8UeyKyV;&ve6T zs+t4vvJPx4l?Gr-e+%n)z<;_@Nx}CF50u{i>FyPI=-2=HR{9SepehJgYI08SLuaU( z1K2j$zwGk>GqbaCD?hia&j4DJ{Wf4k%+Eh=xNhI<+M;#54G0}0b1QH}#N_qKQ0;KJBgDH-RNj_l8_{g3&I-L9Z?SI+TJ|r_0zq7Lh!@-P#469-+jC3xZW}# zY!KcBfc356R^VhCa6;M->rcagPB#LAA2uNG0ssI2000000Fzk-w-~Ti{2#Py&Da-q RTf6`O002ovPDHLkV1h!CoP+=X delta 1358 zcmV-U1+n_^53>xAFnb0H7y#h6hz#^gJH;?cphgTE(!|7#F4#e0 z!e8(QU?M?Xxj`aqT=5Y|k4-+Sea-V781Wu3PYp=v;(yujEizFcV_N<(3y5d zd7h-Xz2}}ece;4beb2{j1n}G%$tyg%XLoHqZg%#UKXUH}41dPvRu>M=?(6G~F3w%< zYkNTNHZ>L;-Lt!PaCTo*E0w5LDsge{a$KCdoSv_>?BMLawwcAD1N&>was+ypn*n5> z)=4JNbJ_fjmS5t~f&H~JCrc60{qdV`zcZYG9@Pa=^7PNDRw@w@FGZpwQp%SwX({SU9i*%LESzFv-q)$@pm zvnOWa=x0~c-%p;tE%>1i(jdx?&)?noYFgpalhnQ6WsO0%+x02V4M zO<#DrScnK20Q5s$2GF*#? z*({(j$dk??84{ekup@5WA4$uT^K9LSi0blkAAkP@V6D;)pw-)d*`Usj`_En2QP(+~ zyRakuJv)ThK3YpZn;74=t-pT-uqL^40Nuzw@ssl;;2S3g8|wnYA$JZSPh${ggFLy` zm<1%~Nw&~#)d1Ld=`vtMMCqNU`v7j;ABpXwwe*-*)_5(VzGKL%H-EPlFI`*Reqb38 z27fEv2y}3EUwnP-$IPW#%-_u(^UCV4MMUPjRlU_Y026^Qcy(KWcUC3_`CWhwR`)jG zZ1Lp{yYZ!Y82}7g?*9YW2*&>~cy&Se_RK4_y|3?Ts#IRM6Q$|a*+Hx8dF5Ar|1-Y7 z`j_B`A<**LmwdbgJ!HMI>4UhWVeYx(cLTYh-k)~yk-8vGCc zdDQV{jco&i)p7`swJB%H?s3DK!J{qX@xp^@Jlc|zH9V}gY#SJyrgs6R#z)ufW`FD1 zHt_5^4gsFU3Ty=!PKx441S6m?@6cB6^#tpPU$vsX{dU zB$Bq1djW$r60sVPZ@jjn<9`2snSY#^h+nVYjH{J_ty0u zKzH)r28@W6l?N@??b}`JSjXFdFf?+v0!KuYS01$76VUEjr#iL*!|-Uk6(+dd*4=Kl z_1gLi!>#2Ipi!r^zw04`Api`GrVgNOiry)B+g%s AppliedUpgrades = new HashSet(); public BigInteger Tickets; + public int TimesRestarted; + public int Stars; public ParkMap Map; public Camera Camera { get; private set; } public Ui Ui { get; private set; } @@ -28,7 +33,7 @@ namespace TouchyTickets { base.LoadContent(); if (!SaveHandler.Load(this)) - this.Map = new ParkMap(10, 10); + this.Map = new ParkMap(12, 12); this.Ui = new Ui(this.UiSystem); this.Camera = new Camera(this.GraphicsDevice) { @@ -36,7 +41,7 @@ namespace TouchyTickets { AutoScaleWithScreen = true, AutoScaleReferenceSize = new Point(720, 1280), MaxScale = 24, - MinScale = 3 + MinScale = 2 }; } @@ -73,22 +78,8 @@ namespace TouchyTickets { base.DoDraw(gameTime); } - public string DisplayTicketCount() { - if (this.Tickets < 1000) - return this.Tickets.ToString(); - // thousands - if (this.Tickets < 1000000) - return this.Tickets.ToString("0,.##K"); - // millions - if (this.Tickets < 1000000000) - return this.Tickets.ToString("0,,.##M"); - // billions - if (this.Tickets < 1000000000000) - return this.Tickets.ToString("0,,,.##B"); - // trillions - if (this.Tickets < 1000000000000000) - return this.Tickets.ToString("0,,,,.##T"); - return this.Tickets.ToString("0,,,,,.##Q"); + public BigInteger GetStarPrice() { + return BigInteger.Pow(1000000, this.TimesRestarted / 2 + 1); } } diff --git a/TouchyTickets/ParkMap.cs b/TouchyTickets/ParkMap.cs index 2a2934d..659d442 100644 --- a/TouchyTickets/ParkMap.cs +++ b/TouchyTickets/ParkMap.cs @@ -16,7 +16,7 @@ namespace TouchyTickets { public class ParkMap { private static readonly UniformTextureAtlas TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Tiles"), 16, 16); - private const int AdditionalRadius = 10; + private const int AdditionalRadius = 15; [DataMember] public readonly int Width; [DataMember] @@ -37,12 +37,15 @@ namespace TouchyTickets { // set up trees var random = new Random(); - for (var i = 0; i < 100; i++) { - var type = random.Next(3); - var x = random.Next(-AdditionalRadius, this.Width + AdditionalRadius); - var y = random.Next(-AdditionalRadius, this.Height + AdditionalRadius); - if (x < 0 || y < 0 || x >= this.Width || y >= this.Width) + for (var x = -AdditionalRadius; x < this.Width + AdditionalRadius; x++) { + for (var y = -AdditionalRadius; y < this.Height + AdditionalRadius; y++) { + if (x >= 0 && y >= 0 && x < this.Width && y < this.Height) + continue; + if (random.Next(15) != 0) + continue; + var type = random.Next(3); this.treePositions[new Point(x, y)] = type; + } } // set up fences @@ -163,5 +166,10 @@ namespace TouchyTickets { return this.attractions.Count(a => a.Item2.Type == type); } + public void CopyAttractionsFrom(ParkMap other) { + foreach (var (pos, attraction) in other.attractions) + this.Place(pos, attraction); + } + } } \ No newline at end of file diff --git a/TouchyTickets/SaveHandler.cs b/TouchyTickets/SaveHandler.cs index f3e2c17..3e3a633 100644 --- a/TouchyTickets/SaveHandler.cs +++ b/TouchyTickets/SaveHandler.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Numerics; using Newtonsoft.Json; +using TouchyTickets.Upgrades; namespace TouchyTickets { public static class SaveHandler { @@ -20,7 +23,10 @@ namespace TouchyTickets { SaveVersion = SaveVersion, Tickets = game.Tickets, LastUpdate = game.LastUpdate, - Map = game.Map + Map = game.Map, + Stars = game.Stars, + TimesRestarted = game.TimesRestarted, + Upgrades = game.AppliedUpgrades.Select(u => u.Name).ToList() }; Serializer.Serialize(stream, data); } @@ -35,6 +41,11 @@ namespace TouchyTickets { game.Tickets = data.Tickets; game.LastUpdate = data.LastUpdate; game.Map = data.Map; + game.Stars = data.Stars; + game.TimesRestarted = data.TimesRestarted; + game.AppliedUpgrades.Clear(); + foreach (var name in data.Upgrades) + game.AppliedUpgrades.Add(Upgrade.Upgrades[name]); } return true; } @@ -59,6 +70,9 @@ namespace TouchyTickets { public BigInteger Tickets; public DateTime LastUpdate; public ParkMap Map; + public int Stars; + public int TimesRestarted; + public List Upgrades; } } \ No newline at end of file diff --git a/TouchyTickets/TouchyTickets.csproj b/TouchyTickets/TouchyTickets.csproj index 00997ad..6a59fe0 100644 --- a/TouchyTickets/TouchyTickets.csproj +++ b/TouchyTickets/TouchyTickets.csproj @@ -6,7 +6,7 @@ - + all diff --git a/TouchyTickets/Ui.cs b/TouchyTickets/Ui.cs index 0dcbf48..6ea83e3 100644 --- a/TouchyTickets/Ui.cs +++ b/TouchyTickets/Ui.cs @@ -16,6 +16,7 @@ using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; using TouchyTickets.Attractions; +using TouchyTickets.Upgrades; namespace TouchyTickets { public class Ui { @@ -36,6 +37,7 @@ namespace TouchyTickets { this.uiSystem.Style.Font = new GenericSpriteFont(MlemGame.LoadContent("Fonts/Regular")); this.uiSystem.Style.TextScale = 0.1F; this.uiSystem.TextFormatter.AddImage("ticket", Texture[2, 0]); + this.uiSystem.TextFormatter.AddImage("star", Texture[3, 0]); // main ticket store ui var rainingTickets = new List(); @@ -55,7 +57,7 @@ namespace TouchyTickets { } }; 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) { + ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 10000, p => PrettyPrintNumber(GameImpl.Instance.Tickets) + "", true) { TextScale = 0.3F }); ticketGroup.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => GameImpl.Instance.Map.TicketsPerSecond.ToString("0.##") + "/s", true) { @@ -71,7 +73,13 @@ namespace TouchyTickets { } }); storeGroup.AddChild(new Image(Anchor.TopLeft, Vector2.One, Texture[0, 0, 2, 3]) { - OnPressed = e => GameImpl.Instance.Tickets++, + OnPressed = e => { + #if DEBUG + GameImpl.Instance.Tickets += 500000; + #else + GameImpl.Instance.Tickets++; + #endif + }, CanBeSelected = true, CanBeMoused = true }); @@ -86,9 +94,6 @@ namespace TouchyTickets { map.Draw(time, batch, pos, scale, alpha, false, new RectangleF(Vector2.Zero, mapSize * scale)); }, 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)) @@ -111,13 +116,12 @@ namespace TouchyTickets { BigInteger price = 0; var button = buyUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) { ChildPadding = new Vector2(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.Create(); - map.PlacingPosition = pos.ToPoint(); + // set placing position to center of camera's view + var (posX, posY) = (GameImpl.Instance.Camera.LookingPosition / Attraction.TileSize).ToPoint(); + map.PlacingPosition = new Point(MathHelper.Clamp(posX, 0, map.Width), MathHelper.Clamp(posY, 0, map.Height)); var yesNoUi = new Group(Anchor.BottomLeft, new Vector2(1)); yesNoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 40), "Back") { @@ -157,7 +161,56 @@ namespace TouchyTickets { } this.uiSystem.Add("Buy", buyUi); - this.swipeRelations = new Element[] {main, buyUi}; + // upgrade ui + var upgradeUi = new Group(Anchor.TopLeft, Vector2.One, false) { + IsHidden = true, + OnDrawn = (e, time, batch, alpha) => batch.Draw(batch.GetBlankTexture(), e.DisplayArea, ColorExtensions.FromHex(0xff86bccf) * alpha) + }; + var upgradeHeader = upgradeUi.AddChild(new Group(Anchor.AutoLeft, new Vector2(1, 0.25F), false)); + 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, 20), "Earn ") { + PositionOffset = new Vector2(0, 4), + OnUpdated = (e, time) => ((Button) e).IsDisabled = GameImpl.Instance.Tickets < GameImpl.Instance.GetStarPrice(), + OnPressed = e => { + var game = GameImpl.Instance; + game.TimesRestarted++; + game.Stars++; + game.Map = new ParkMap(game.Map.Width, game.Map.Height); + game.Tickets = 0; + } + }); + upgradeHeader.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => $"Requires {PrettyPrintNumber(GameImpl.Instance.GetStarPrice())}", true) { + PositionOffset = new Vector2(0, 2) + }); + var upgradeList = upgradeUi.AddChild(new Panel(Anchor.AutoLeft, new Vector2(1, 0.75F), Vector2.Zero, false, true, new Point(10, 30), false) { + ChildPadding = new Padding(5, 15, 5, 5) + }); + foreach (var upgrade in Upgrade.Upgrades.Values) { + var button = upgradeList.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) { + ChildPadding = new Vector2(4), + OnPressed = e => { + GameImpl.Instance.Stars--; + GameImpl.Instance.AppliedUpgrades.Add(upgrade); + upgrade.OnApplied(); + } + }); + button.OnUpdated += (e, time) => { + // we want to hide if we're active, or if we still need a dependency to be active + button.IsHidden = Upgrade.IsActive(upgrade) || upgrade.Dependencies.Any(u => !Upgrade.IsActive(u)); + button.IsDisabled = GameImpl.Instance.Stars < upgrade.Price; + }; + var center = button.AddChild(new Group(Anchor.Center, new Vector2(0.8F, 1), false) {CanBeMoused = false}); + center.AddChild(new Paragraph(Anchor.AutoCenter, 1, upgrade.Name, true)); + //center.AddChild(new Paragraph(Anchor.AutoCenter, 1, upgrade.Description, true) {TextScale = 0.08F}); + var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(1), upgrade.Texture) { + Padding = new Vector2(4) + }); + button.OnAreaUpdated += e => image.Size = new Vector2(e.DisplayArea.Height / e.Scale); + button.AddChild(new Paragraph(Anchor.CenterRight, 1, p => upgrade.Price + "", true)); + } + this.uiSystem.Add("Upgrade", upgradeUi); + + this.swipeRelations = new Element[] {upgradeUi, main, buyUi}; } public void Update(GameTime time) { @@ -247,5 +300,23 @@ namespace TouchyTickets { element.Transform = Matrix.Identity; } + private static string PrettyPrintNumber(BigInteger number) { + if (number < 1000) + return number.ToString(); + // thousands + if (number < 1000000) + return number.ToString("0,.##K"); + // millions + if (number < 1000000000) + return number.ToString("0,,.##M"); + // billions + if (number < 1000000000000) + return number.ToString("0,,,.##B"); + // trillions + if (number < 1000000000000000) + return number.ToString("0,,,,.##T"); + return number.ToString("0,,,,,.##Q"); + } + } } \ No newline at end of file diff --git a/TouchyTickets/Upgrade.cs b/TouchyTickets/Upgrade.cs new file mode 100644 index 0000000..e005c17 --- /dev/null +++ b/TouchyTickets/Upgrade.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using MLEM.Extensions; +using MLEM.Textures; + +namespace TouchyTickets.Upgrades { + public class Upgrade { + + public static readonly Dictionary Upgrades = new Dictionary(); + public static readonly Upgrade[] MapSize = RegisterTiers("MapSize", 10, 1, 0.5F, Ui.Texture[0, 3]); + + public readonly string Name; + public readonly int Price; + public readonly TextureRegion Texture; + public readonly Upgrade[] Dependencies; + + public Upgrade(string name, int price, TextureRegion texture, params Upgrade[] dependencies) { + this.Name = name; + this.Price = price; + this.Texture = texture; + this.Dependencies = dependencies; + } + + private static Upgrade[] RegisterTiers(string name, int amount, int startPrice, float priceIncrease, TextureRegion 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 void OnApplied() { + // map size upgrades + if (MapSize.Contains(this)) { + var oldMap = GameImpl.Instance.Map; + var newMap = new ParkMap(oldMap.Width + 5, oldMap.Height + 5); + newMap.CopyAttractionsFrom(oldMap); + GameImpl.Instance.Map = newMap; + } + } + + private static void Register(Upgrade upgrade) { + Upgrades.Add(upgrade.Name, upgrade); + } + + public static bool IsActive(Upgrade upgrade) { + return GameImpl.Instance.AppliedUpgrades.Contains(upgrade); + } + + } +} \ No newline at end of file