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 82c8507..b2ca807 100644
Binary files a/TouchyTickets/Content/Textures/Ui.aseprite and b/TouchyTickets/Content/Textures/Ui.aseprite differ
diff --git a/TouchyTickets/Content/Textures/Ui.png b/TouchyTickets/Content/Textures/Ui.png
index f3658a5..544e74f 100644
Binary files a/TouchyTickets/Content/Textures/Ui.png and b/TouchyTickets/Content/Textures/Ui.png differ
diff --git a/TouchyTickets/ParkMap.cs b/TouchyTickets/ParkMap.cs
index e6a0ebc..ffa3fad 100644
--- a/TouchyTickets/ParkMap.cs
+++ b/TouchyTickets/ParkMap.cs
@@ -29,6 +29,7 @@ namespace TouchyTickets {
public float TicketsPerSecond { get; private set; }
public Attraction PlacingAttraction;
+ public AttractionModifier PlacingModifier;
public Point PlacingPosition;
public Point? SelectedPosition;
private bool draggingAttraction;
@@ -116,7 +117,7 @@ namespace TouchyTickets {
if (attraction != null) {
// actually select the top left for easy usage later
this.SelectedPosition = this.attractions.First(kv => 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
+