modifiers, part 1
This commit is contained in:
parent
eeca5b37e4
commit
93ea869a16
11 changed files with 210 additions and 16 deletions
|
@ -63,8 +63,10 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb"/>
|
||||
<None Include="..\TouchyTickets\Content\*\**" />
|
||||
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb">
|
||||
<Link>Content\Content.mgcb</Link>
|
||||
</MonoGameContentReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Contentless" Version="2.0.6" />
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace TouchyTickets.Attractions {
|
|||
public static readonly UniformTextureAtlas Texture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Attractions"), 16, 16);
|
||||
public static readonly Vector2 TileSize = new Vector2(Texture.RegionWidth, Texture.RegionHeight);
|
||||
|
||||
[DataMember]
|
||||
public readonly List<ActiveModifier> Modifiers = new List<ActiveModifier>();
|
||||
[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<Attraction> 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<Attraction> 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());
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
70
TouchyTickets/Attractions/AttractionModifier.cs
Normal file
70
TouchyTickets/Attractions/AttractionModifier.cs
Normal file
|
@ -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<string, AttractionModifier> Modifiers = new Dictionary<string, AttractionModifier>();
|
||||
|
||||
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<AttractionModifier> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -10,12 +10,12 @@ namespace TouchyTickets.Attractions {
|
|||
public class AttractionType {
|
||||
|
||||
public static readonly Dictionary<string, AttractionType> Attractions = new Dictionary<string, AttractionType>();
|
||||
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));
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
"ReallyEarnStar": "Cashing in your <i ticket> now would earn you {0}<i star>. It will also remove all placed attractions and reset your <i ticket>. Are you sure?",
|
||||
"Yes": "Yes",
|
||||
"Okay": "Okay",
|
||||
"Add": "Add",
|
||||
"RequiresTickets": "{0}<i ticket> for 1<i star>. 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 <i ticket>. Start by racking up 50<i ticket>!",
|
||||
"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",
|
||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.1 KiB |
|
@ -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)
|
||||
|
|
|
@ -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<RainingTicket>();
|
||||
|
@ -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}<i {m.Modifier.Name}>"));
|
||||
}, 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}<i {m.Modifier.Name}>"));
|
||||
}, 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}<i ticket>", true) {TextScale = 0.08F});
|
||||
right.AddChild(new Paragraph(Anchor.AutoRight, 1, p => PrettyPrintNumber(price) + "<i ticket>", 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();
|
||||
}
|
||||
|
|
|
@ -117,8 +117,10 @@
|
|||
<ItemGroup>
|
||||
<None Include="Entitlements.plist" />
|
||||
<None Include="Info.plist" />
|
||||
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb" />
|
||||
<None Include="..\TouchyTickets\Content\*\**" />
|
||||
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb">
|
||||
<Link>Content\Content.mgcb</Link>
|
||||
</MonoGameContentReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Default.png" />
|
||||
|
|
Loading…
Reference in a new issue