From 93ea869a16f2051d68ca9966b69fe20c4935aba9 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 8 Jun 2020 18:01:10 +0200 Subject: [PATCH] modifiers, part 1 --- Android/Android.csproj | 4 +- TouchyTickets/Attractions/Attraction.cs | 34 ++++++- TouchyTickets/Attractions/AttractionFlags.cs | 7 +- .../Attractions/AttractionModifier.cs | 70 ++++++++++++++ TouchyTickets/Attractions/AttractionType.cs | 4 +- .../Content/Localization/Localization.json | 7 ++ TouchyTickets/Content/Textures/Ui.aseprite | Bin 2418 -> 2666 bytes TouchyTickets/Content/Textures/Ui.png | Bin 3805 -> 4206 bytes TouchyTickets/ParkMap.cs | 7 +- TouchyTickets/Ui.cs | 89 +++++++++++++++++- iOS/iOS.csproj | 4 +- 11 files changed, 210 insertions(+), 16 deletions(-) create mode 100644 TouchyTickets/Attractions/AttractionModifier.cs diff --git a/Android/Android.csproj b/Android/Android.csproj index 65e06a0..257cbb7 100644 --- a/Android/Android.csproj +++ b/Android/Android.csproj @@ -63,8 +63,10 @@ - + + Content\Content.mgcb + diff --git a/TouchyTickets/Attractions/Attraction.cs b/TouchyTickets/Attractions/Attraction.cs index dd47b4b..af22666 100644 --- a/TouchyTickets/Attractions/Attraction.cs +++ b/TouchyTickets/Attractions/Attraction.cs @@ -18,6 +18,8 @@ namespace TouchyTickets.Attractions { public static readonly UniformTextureAtlas Texture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Attractions"), 16, 16); public static readonly Vector2 TileSize = new Vector2(Texture.RegionWidth, Texture.RegionHeight); + [DataMember] + public readonly List Modifiers = new List(); [DataMember] public readonly AttractionType Type; [DataMember] @@ -30,6 +32,11 @@ namespace TouchyTickets.Attractions { public float Update(GameTime time, TimeSpan passed, ParkMap map, Point position) { var genRate = this.Type.GetGenerationRate(); + // apply attraction modifiers + foreach (var modifier in this.Modifiers) + genRate *= (float) Math.Pow(modifier.Modifier.Multiplier, modifier.Amount); + + // apply star upgrades if (Upgrade.FoodCourtModifier.IsActive() && this.GetSurrounding(map, position, FoodCourt).Any()) genRate *= 2; if (Upgrade.SpiralSlideModifier.IsActive() && this.GetSurrounding(map, position, SpiralSlide).Any()) @@ -37,17 +44,34 @@ namespace TouchyTickets.Attractions { if (Upgrade.HauntedHouseModifier.IsActive() && this.Type.Flags.HasFlag(Relaxed) && this.GetSurrounding(map, position, HauntedHouse).Any()) genRate *= 3; + // apply generation rate to ticket amount this.ticketPercentage += genRate * (float) passed.TotalSeconds; - var amount = this.ticketPercentage.Floor(); - if (amount > 0) { - GameImpl.Instance.Tickets += amount; - this.ticketPercentage -= amount; + var total = this.ticketPercentage.Floor(); + if (total > 0) { + GameImpl.Instance.Tickets += total; + this.ticketPercentage -= total; } // return the generation rate per second return genRate; } - public IEnumerable GetSurrounding(ParkMap map, Point position, AttractionType type) { + public void ApplyModifier(AttractionModifier modifier) { + // increase the amount of existing modifiers + foreach (var mod in this.Modifiers) { + if (mod.Modifier == modifier) { + mod.Amount++; + return; + } + } + // or add a new modifier + this.Modifiers.Add(new ActiveModifier(modifier, 1)); + } + + public int GetModifierAmount(AttractionModifier modifier) { + return this.Modifiers.Where(m => m.Modifier == modifier).Sum(m => m.Amount); + } + + private IEnumerable GetSurrounding(ParkMap map, Point position, AttractionType type) { foreach (var tile in this.Type.GetCoveredTiles()) { foreach (var dir in Direction2Helper.Adjacent) { var other = map.GetAttractionAt(position + tile + dir.Offset()); diff --git a/TouchyTickets/Attractions/AttractionFlags.cs b/TouchyTickets/Attractions/AttractionFlags.cs index d786cfd..2acc9d5 100644 --- a/TouchyTickets/Attractions/AttractionFlags.cs +++ b/TouchyTickets/Attractions/AttractionFlags.cs @@ -4,10 +4,13 @@ namespace TouchyTickets.Attractions { [Flags] public enum AttractionFlags { + // base flags None = 0, Relaxed = 1, - FastCars = 2, - Walking = 4 + Cars = 2, + Walking = 4, + FastCars = Cars | 8, + All = ~0 } } \ No newline at end of file diff --git a/TouchyTickets/Attractions/AttractionModifier.cs b/TouchyTickets/Attractions/AttractionModifier.cs new file mode 100644 index 0000000..6c97f6f --- /dev/null +++ b/TouchyTickets/Attractions/AttractionModifier.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using MLEM.Textures; +using Newtonsoft.Json; + +namespace TouchyTickets.Attractions { + [JsonConverter(typeof(Converter))] + public class AttractionModifier { + + public static readonly Dictionary Modifiers = new Dictionary(); + + static AttractionModifier() { + Register(new AttractionModifier("Lubricant", 200, AttractionFlags.Cars, 1.05F, Ui.Texture[0, 4])); + Register(new AttractionModifier("LongerQueue", 1000, AttractionFlags.All, 1.01F, Ui.Texture[1, 4])); + } + + public readonly string Name; + public readonly long InitialPrice; + public readonly TextureRegion Texture; + public readonly float Multiplier; + private readonly AttractionFlags affectedFlags; + + public AttractionModifier(string name, long initialPrice, AttractionFlags affectedFlags, float multiplier, TextureRegion texture) { + this.Name = name; + this.InitialPrice = initialPrice; + this.affectedFlags = affectedFlags; + this.Texture = texture; + this.Multiplier = multiplier; + } + + public bool IsAffected(Attraction attraction) { + return (attraction.Type.Flags & this.affectedFlags) != 0; + } + + private static AttractionModifier Register(AttractionModifier type) { + Modifiers.Add(type.Name, type); + return type; + } + + public class Converter : JsonConverter { + + public override void WriteJson(JsonWriter writer, AttractionModifier value, JsonSerializer serializer) { + if (value != null) + writer.WriteValue(value.Name); + } + + public override AttractionModifier ReadJson(JsonReader reader, Type objectType, AttractionModifier existingValue, bool hasExistingValue, JsonSerializer serializer) { + return reader.Value != null ? Modifiers[reader.Value.ToString()] : null; + } + + } + + } + + [DataContract] + public class ActiveModifier { + + [DataMember] + public readonly AttractionModifier Modifier; + [DataMember] + public int Amount; + + public ActiveModifier(AttractionModifier modifier, int amount) { + this.Modifier = modifier; + this.Amount = amount; + } + + } +} \ No newline at end of file diff --git a/TouchyTickets/Attractions/AttractionType.cs b/TouchyTickets/Attractions/AttractionType.cs index e2b908b..3636408 100644 --- a/TouchyTickets/Attractions/AttractionType.cs +++ b/TouchyTickets/Attractions/AttractionType.cs @@ -10,12 +10,12 @@ namespace TouchyTickets.Attractions { public class AttractionType { public static readonly Dictionary Attractions = new Dictionary(); - public static readonly AttractionType Carousel = Register(new AttractionType("Carousel", RectArea(1, 1), Attraction.Texture[0, 0, 1, 1], 0.5F, 50, Relaxed)); + public static readonly AttractionType Carousel = Register(new AttractionType("Carousel", RectArea(1, 1), Attraction.Texture[0, 0, 1, 1], 0.5F, 50, Relaxed | Cars)); public static readonly AttractionType MirrorHouse = Register(new AttractionType("MirrorHouse", RectArea(1, 1), Attraction.Texture[3, 0, 1, 1], 1, 150, Relaxed | Walking)); public static readonly AttractionType FoodCourt = Register(new AttractionType("FoodCourt", RectArea(2, 1), Attraction.Texture[1, 0, 2, 1], 2F, 300, None)); public static readonly AttractionType SpiralSlide = Register(new AttractionType("SpiralSlide", RectArea(1, 2), Attraction.Texture[5, 0, 1, 2], 4, 1200, Relaxed | Walking)); public static readonly AttractionType HedgeMaze = Register(new AttractionType("HedgeMaze", RectArea(2, 2), Attraction.Texture[3, 3, 2, 2], 8, 2500, Relaxed | Walking)); - public static readonly AttractionType FerrisWheel = Register(new AttractionType("FerrisWheel", RectArea(2, 2), Attraction.Texture[0, 1, 2, 2], 12, 4000, Relaxed)); + public static readonly AttractionType FerrisWheel = Register(new AttractionType("FerrisWheel", RectArea(2, 2), Attraction.Texture[0, 1, 2, 2], 12, 4000, Relaxed | Cars)); public static readonly AttractionType FreefallCoaster = Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, Attraction.Texture[6, 0, 3, 2], 24, 8000, FastCars)); public static readonly AttractionType HauntedHouse = Register(new AttractionType("HauntedHouse", RectArea(2, 2), Attraction.Texture[3, 5, 2, 2], 30, 12000, FastCars)); public static readonly AttractionType WildMouse = Register(new AttractionType("WildMouse", RectArea(3, 2), Attraction.Texture[2, 1, 3, 2], 50, 24000, FastCars)); diff --git a/TouchyTickets/Content/Localization/Localization.json b/TouchyTickets/Content/Localization/Localization.json index 5bdc3a5..0c02ea5 100644 --- a/TouchyTickets/Content/Localization/Localization.json +++ b/TouchyTickets/Content/Localization/Localization.json @@ -6,8 +6,11 @@ "ReallyEarnStar": "Cashing in your now would earn you {0}. It will also remove all placed attractions and reset your . Are you sure?", "Yes": "Yes", "Okay": "Okay", + "Add": "Add", "RequiresTickets": "{0} for 1. Maximum {1}.", "AppliedUpgrades": "Active Upgrades", + "Attractions": "Attractions", + "Modifiers": "Modifiers", "Tutorial1": "Hi! Welcome to Touchy Tickets. To start the game, simply tap the ticket booth to sell a . Start by racking up 50!", "Tutorial2": "Great! Now, you can buy your first attraction. Access the menu on the right by swiping and purchase a carousel.", "Tutorial3": "This is your park map. The area inside the fence is what belongs to you. Place the carousel by dragging it to the perfect location and then pressing the button below.", @@ -29,6 +32,10 @@ "FreefallCoaster": "Freefall Coaster", "MirrorHouse": "House of Mirrors", "HauntedHouse": "Haunted House", + "Lubricant": "Slip and Slide", + "LubricantDescription": "A ride's cars are lubricated for greater speed. Works on attractions with cars.", + "LongerQueue": "Queuete", + "LongerQueueDescription": "The queue length of an attraction is increased. Works on all attractions.", "MapSize1": "Big Park", "MapSize1Description": "Increases your park's buildable area. Existing attractions are not removed.", "MapSize2": "Bigger Park", diff --git a/TouchyTickets/Content/Textures/Ui.aseprite b/TouchyTickets/Content/Textures/Ui.aseprite index 82c85079ead50bbdd216bf31bf8fb6378f2f0fd0..b2ca80782255cfd5e41103ee12dd82c17c7125b5 100644 GIT binary patch delta 2179 zcmV-}2z>YQ66zEJY6_78egf(Vv4V#Ie_;p!00kfb000010RI92000000001f08RjS zob8-Xh*eb_$EOx;TvdjlPD5jkna()Q!5oKM zB$|n0mgrOzQc_TYE-Fls6r{}%L^~I?IrY8vef-{U&bjxTckg@m&Un7?<(_lie|`7Q z`MlrnoO|ZY>*$!Q9rNxnt+3@(Q56yxjcH_Lb}qRn9@EEhc1}bsx=eCvz}nY?HP8e>{Zr=Twj_zcY4=YRh5|zqoB{z3g%tdorhe*@HK^ zGrm!$8TZZhZ&ju9bSrG*PUb_kCFbns8;z{9O_p8E!I#XhPi#*t*=O+wXK=lKZf|17 zInB>PQT<&7$y}UwEca>C=LbtLmGO?cwk_TzZ!-V$((Q?5+-LD8^Z9pnem_?~*38rTf!rbF^}jEDn3(nH$Q?Z1cxGYC{rsBQlg9b9c{>^Ft?ORoJYIed zeCKwIahTj?TM_+T6)^{cf6LB|b6RR}w~g^dJilw^XbWY}ye?T!XYR0c#ocx8Ja?!g_iXNI z`CaCAzTdW=E8f9z>zaWeYwn@Xlaaf9e3!B6YSXXj{0AMmnlZBJnzeW^E$iJ z@;un(ao#oKviiD8%9H0^%q4eQo{uVDK6!rG(!Rva%>8~yDiU0m9H}9 z^HJprbELfU`K@QN`=M z1q%#J=Bzh!EU>=3XHVjZ{rjElPrkI=ggDqkcu_x1J~Sx;kM)bHte}fI&QFn1K>i74vnA_NM3YEx%-Uw%&N?#Ywf<7PLn&1b>`q(iNTa**`Dweuc2Nfcc_Tp-zWFu$B!qLtYsUy%efo5Z`l2G zD)%h*@vd{d>|?OqzG);e8Ozrs7tLK>e^XYh4=qR0*KORv6?|!Omu+e6`Fq>oJn{S02BXdO`}>-EXJ=<(#-8*Q05Sx#Y4IKJijmOnP+NC7MZj*a3@#rbd_~Ie{Ehz z-dwMdb=S3$CAhl`?jg_bI@hOhmkM2@ZH74ojz`{I>g6KWY?#AC&A%(#j+tk@4t)Rm zexKFg?sE*VMT~*kV`B@=p<}D-e*eI5gKh68PdwXl>nh8(cHd`IqriR7L%j_SdB2WhU$^Rh zqWlEz+9B@;L&R@nIkmL=l?RRVj%RDZ+&RAMzge>^=Xg=oP!Zg1^EOwxaaH#b6{e!N z`^@Vi_v+}=&rX;xzW%1}gC89V=I&#T1+BV|s4#*1oVlHKhjRCk%=JYdJE}S5iFeN3 zIB&D9(Ls@W^(5mxgu87%=bFpmGZ4r3YN^?Hx2?m)dEL|z=I`1*?+aO*i_dY@(Z6-B F^(2Sr$khM< delta 1929 zcmV;42X^@C6!H=RatV{sfA4+g z^M1c`?wvQcqhqn&>Uqqx!j?lN)kt78yYJ~zT?O|m-`(HTF}HPf7}!@{Y9jNK+r~08 zR#|VhEyRBJ&aEcTHY6~=b?E_?OEPo1|R8IRL--%yG1%T%XlrL&2~(% zyz$eN$+Hc8j%K)%IT$mx$y$CNfAWmy-5^_jXY7{M7Ge*-e(*q}?6QwNnbWcC!JFI} z->B1!`)0?tuF`qB6}E9F^ReL#=Hl0TjjXdxmR-!jm&|X^PiL0wL;S%RTyI}GnwfD< z^Se;ic-KKP7Z;t&W7_ob!4gbmyrZsdi+9PJ%>TUhU}hQjA^v3E`%p(_fAU8=>q{-( zTQQfr+BEl2yu+;9a{TcRml>GsZM?~xeQjev?th$nH#51*`cmSq*YK^J-(73_P`tCw ztu5C}_T;RY`^JIXA>{SHuYH!8^`*!iJl=bGZOi@Y_En4KdEdI7jP=&_S>!rieh*^T zc8qbD++|xC<6Rdq2ZPI@f4!?)YH+vB@nu}UYvyRn%bs~%vR=yEk$cJgpPR!C+t{J~ z&7F^W`MTZ#pU?3SV1PJ@!>9l;>R>_12Z=Wgi>&UBgf2_Qd+`26rAiuM_t$cVB*& zxt;H~^>f8LI3CzOl*^iX-s{Q4-9Ep|Sar4O*Yxw0_zMhh~nA|syY;;;OpB(FV zn%q;KucvgLC-?Eu#n#-h1!G$-NO`{AvgP?Qxx4Z_7&hbde|MR3!c!i-o;#Ux8?by>J^jcM>Y;*Zf0F)%kxRqt7x7N&F{86pH#i7SkEU_ zE3A=d%?9e^5p4mB%_=$JI~re^5 z=kHDV{zoyC+hga2#JzrM*ZsPxKWCC(Uk(i!xa#vrYM;ow4ECq{`V3vx`6ZRFPu8q4 zFqyO7%)Y?-`jI1<=VxY|?9aZj$>eddPgq=Buh?Gef9vfvu&dr`oj-rxxE%BTBD^6m zHdn728SkviexDu~G|jl#^D0C94X|zp-(8cFnFsp&jja3Fm-YO8u)TcwvePd1`=1#z zF7KX*A8W}NaSkKqS+~Xg+mlb3cb*;a8hv7pKRLA8gxu&m&}Cqh`#M(|ShwrfLfpqj zM;CBMf8E8stmp5CnA_NM4hP>CPP}F{xyQ`+%}* z*Wo8tI8E-@*M-kN$PA_|%l3??cn$S3xkF_(OvX*V+F4u14zUTdyyxc?V z$(OM%<6Y-^ImTc+y>B8j8Ozrs7tLLsQz+KQf7h*S^p!kaWnFJYyvyftU%m3{0>?am zA3VW!e0)6f?CfmD8C$mDb@bEgc3ZsjI{@x^*7?V}yRGv(+I3$WcgDMoyXLLW32dL= z^0?EI`>Os41N+LlZsQKF;7gOcZ1b__^R~fx{`a{BMw{#T`1WX ze|g>C$Nj5Qr!vNjbr*9vreG_}x~^H+u06;-Hr~tT4&F4{SaxyO@lNj7fBU6j#(d2D zE_1rvGX9z8Ic_NHJTLRT%=0nKKRSFk>jT!bt3SsS>o!@|bn`r#d-mdwnaN$(ZJ(dtD*W|Y@ z?ufGO8eX3hxQ2{=V7Q`tF!SkyQ_ixi&%QF{g7}xJ%`|N81c@3LH;V)yodBJW5^xIIlMip diff --git a/TouchyTickets/Content/Textures/Ui.png b/TouchyTickets/Content/Textures/Ui.png index f3658a513c209bb20c8993e8a952007947e41615..544e74f3ed89fbb4d5ac894470f96c2adbecaae3 100644 GIT binary patch delta 3937 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 delta 3515 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 p&;{2Ivr!KO0h3@48W kv.Item2 == attraction).Item1; - } else { + } else if (GameImpl.Instance.UiSystem.Controls.GetElementUnderPos(tap.Position) == null) { this.SelectedPosition = null; } } @@ -199,6 +200,10 @@ namespace TouchyTickets { return this.attractions.Count(a => a.Item2.Type == type); } + public int GetModifierAmount(AttractionModifier modifier) { + return this.attractions.Sum(a => a.Item2.GetModifierAmount(modifier)); + } + public ParkMap Copy(int? newWidth = null, int? newHeight = null) { var newMap = new ParkMap(newWidth ?? this.Width, newHeight ?? this.Height); foreach (var (pos, attraction) in this.attractions) diff --git a/TouchyTickets/Ui.cs b/TouchyTickets/Ui.cs index 8afc0f9..f7ea5cb 100644 --- a/TouchyTickets/Ui.cs +++ b/TouchyTickets/Ui.cs @@ -39,6 +39,8 @@ namespace TouchyTickets { this.uiSystem.Style.TextScale = 0.1F; this.uiSystem.TextFormatter.AddImage("ticket", Texture[2, 0]); this.uiSystem.TextFormatter.AddImage("star", Texture[3, 0]); + foreach (var modifier in AttractionModifier.Modifiers.Values) + this.uiSystem.TextFormatter.AddImage(modifier.Name, modifier.Texture); // main ticket store ui var rainingTickets = new List(); @@ -121,7 +123,13 @@ namespace TouchyTickets { return; var map = GameImpl.Instance.Map; var infoUi = new Group(Anchor.BottomLeft, new Vector2(1)); - infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Back")) { + infoUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => { + if (map.SelectedPosition == null) + return string.Empty; + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + return string.Join(" ", attraction.Modifiers.Select(m => $"{m.Amount}")); + }, true) {TextScale = 0.15F}); + infoUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) }); infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Remove")) { @@ -206,6 +214,77 @@ namespace TouchyTickets { } this.uiSystem.Add("Buy", buyUi); + // modifier ui + var modifierUi = 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 modifier in AttractionModifier.Modifiers.Values) { + BigInteger price = 0; + + BigInteger CalculatePrice() { + var modAmount = GameImpl.Instance.Map.GetModifierAmount(modifier); + price = (modifier.InitialPrice * (float) Math.Pow(1 + 0.4F, modAmount)).Ceil(); + return price; + } + + var button = modifierUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(1)) { + SetHeightBasedOnChildren = true, + PositionOffset = new Vector2(0, 1), + ChildPadding = new Vector2(4), + OnPressed = e => { + if (this.swipeProgress != 0) + return; + var map = GameImpl.Instance.Map; + var infoUi = new Group(Anchor.BottomLeft, new Vector2(1)); + infoUi.AddChild(new Paragraph(Anchor.AutoCenter, 1, p => { + if (map.SelectedPosition == null) + return string.Empty; + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + return string.Join(" ", attraction.Modifiers.Select(m => $"{m.Amount}")); + }, true) {TextScale = 0.15F}); + infoUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(0.5F, 30), Localization.Get("Back")) { + OnPressed = e2 => this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)) + }); + infoUi.AddChild(new Button(Anchor.AutoInlineIgnoreOverflow, new Vector2(0.5F, 30), Localization.Get("Add")) { + OnPressed = e2 => { + if (map.SelectedPosition == null) + return; + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + attraction.ApplyModifier(modifier); + + // if we're out of tickets to buy this modifier, automatically go back + if (GameImpl.Instance.Tickets < CalculatePrice()) + this.FadeUi(false, () => this.uiSystem.Remove(e2.Root.Name)); + }, + OnUpdated = (e2, time) => { + var disabled = map.SelectedPosition == null; + if (!disabled) { + var attraction = map.GetAttractionAt(map.SelectedPosition.Value); + disabled = attraction == null || !modifier.IsAffected(attraction); + } + ((Button) e2).IsDisabled = disabled; + } + }); + // we want this to render below the main ui while it fades away + this.uiSystem.Add("ModifierInfo", infoUi).Priority = -100; + + this.FadeUi(true); + }, + OnAreaUpdated = e => CalculatePrice() + }); + button.OnUpdated += (e, time) => button.IsDisabled = GameImpl.Instance.Tickets < price; + button.AddChild(new Image(Anchor.CenterLeft, new Vector2(0.2F, 40), modifier.Texture) { + Padding = new Vector2(4) + }); + var right = button.AddChild(new Group(Anchor.TopRight, new Vector2(0.8F, 1)) {CanBeMoused = false}); + right.AddChild(new Paragraph(Anchor.TopLeft, 1, Localization.Get(modifier.Name), true)); + right.AddChild(new Paragraph(Anchor.AutoLeft, 1, Localization.Get(modifier.Name + "Description"), true) {TextScale = 0.08F}); + right.AddChild(new Paragraph(Anchor.BottomLeft, 1, $"x{modifier.Multiplier}", true) {TextScale = 0.08F}); + right.AddChild(new Paragraph(Anchor.AutoRight, 1, p => PrettyPrintNumber(price) + "", true)); + } + this.uiSystem.Add("Modifiers", modifierUi); + // upgrade ui var upgradeUi = new Group(Anchor.TopLeft, Vector2.One, false) { IsHidden = true, @@ -248,7 +327,7 @@ namespace TouchyTickets { PopulateUpgradeList(upgradeList); this.uiSystem.Add("Upgrade", upgradeUi); - this.swipeRelations = new Element[] {upgradeUi, main, buyUi}; + this.swipeRelations = new Element[] {upgradeUi, main, buyUi, modifierUi}; } public void Update(GameTime time) { @@ -345,8 +424,10 @@ namespace TouchyTickets { // 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; - GameImpl.Instance.Map.SelectedPosition = null; + var map = GameImpl.Instance.Map; + map.PlacingAttraction = null; + map.SelectedPosition = null; + map.PlacingModifier = null; } after?.Invoke(); } diff --git a/iOS/iOS.csproj b/iOS/iOS.csproj index de13a1a..c5459f2 100644 --- a/iOS/iOS.csproj +++ b/iOS/iOS.csproj @@ -117,8 +117,10 @@ - + + Content\Content.mgcb +