saving!
This commit is contained in:
parent
7c51c923b9
commit
6a013648f4
7 changed files with 202 additions and 64 deletions
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MLEM.Extensions;
|
||||
|
@ -6,49 +8,36 @@ using MLEM.Startup;
|
|||
using MLEM.Textures;
|
||||
|
||||
namespace ThemeParkClicker.Attractions {
|
||||
[DataContract]
|
||||
public class Attraction {
|
||||
|
||||
public static readonly Dictionary<string, Constructor> Attractions = new Dictionary<string, Constructor>();
|
||||
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);
|
||||
|
||||
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], 1.1F, 300));
|
||||
}
|
||||
|
||||
private readonly bool[,] area;
|
||||
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;
|
||||
[DataMember]
|
||||
public readonly AttractionType Type;
|
||||
[DataMember]
|
||||
private float ticketPercentage;
|
||||
|
||||
public Attraction(bool[,] area, TextureRegion texture, float generationPerSecond, long initialPrice) {
|
||||
this.area = area;
|
||||
this.TextureRegion = texture;
|
||||
this.GenerationPerSecond = generationPerSecond;
|
||||
this.InitialPrice = initialPrice;
|
||||
public Attraction(AttractionType type) {
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
public IEnumerable<Point> GetCoveredTiles() {
|
||||
for (var x = 0; x < this.Width; x++) {
|
||||
for (var y = 0; y < this.Height; y++) {
|
||||
if (this.area[y, x])
|
||||
for (var x = 0; x < this.Type.Width; x++) {
|
||||
for (var y = 0; y < this.Type.Height; y++) {
|
||||
if (this.Type.Area[y, x])
|
||||
yield return new Point(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public delegate Attraction Constructor();
|
||||
|
||||
public long GetPrice() {
|
||||
return this.InitialPrice;
|
||||
return this.Type.InitialPrice;
|
||||
}
|
||||
|
||||
public void GainTickets(GameTime time) {
|
||||
this.ticketPercentage += this.GenerationPerSecond * (float) time.ElapsedGameTime.TotalSeconds;
|
||||
public void Update(GameTime time, TimeSpan passed) {
|
||||
this.ticketPercentage += this.Type.GenerationPerSecond * (float) passed.TotalSeconds;
|
||||
var amount = this.ticketPercentage.Floor();
|
||||
if (amount > 0) {
|
||||
GameImpl.Instance.Tickets += amount;
|
||||
|
|
59
ThemeParkClicker/Attractions/AttractionType.cs
Normal file
59
ThemeParkClicker/Attractions/AttractionType.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MLEM.Textures;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ThemeParkClicker.Attractions {
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public class AttractionType {
|
||||
|
||||
public static readonly Dictionary<string, AttractionType> Attractions = new Dictionary<string, AttractionType>();
|
||||
|
||||
static AttractionType() {
|
||||
Register(new AttractionType("Carousel", new[,] {{true}}, Attraction.Texture[0, 0, 1, 1], 0.5F, 50));
|
||||
Register(new AttractionType("FoodCourt", new[,] {{true, true}}, Attraction.Texture[1, 0, 2, 1], 1.1F, 300));
|
||||
}
|
||||
|
||||
public readonly string Name;
|
||||
public readonly bool[,] Area;
|
||||
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;
|
||||
private readonly Constructor create;
|
||||
|
||||
public AttractionType(string name, bool[,] area, TextureRegion texture, float generationPerSecond, long initialPrice, Constructor constructor = null) {
|
||||
this.Name = name;
|
||||
this.Area = area;
|
||||
this.TextureRegion = texture;
|
||||
this.GenerationPerSecond = generationPerSecond;
|
||||
this.InitialPrice = initialPrice;
|
||||
this.create = constructor ?? (t => new Attraction(t));
|
||||
}
|
||||
|
||||
public Attraction Create() {
|
||||
return this.create(this);
|
||||
}
|
||||
|
||||
private static void Register(AttractionType type) {
|
||||
Attractions[type.Name] = type;
|
||||
}
|
||||
|
||||
public delegate Attraction Constructor(AttractionType type);
|
||||
|
||||
public class Converter : JsonConverter<AttractionType> {
|
||||
|
||||
public override void WriteJson(JsonWriter writer, AttractionType value, JsonSerializer serializer) {
|
||||
if (value != null)
|
||||
writer.WriteValue(value.Name);
|
||||
}
|
||||
|
||||
public override AttractionType ReadJson(JsonReader reader, Type objectType, AttractionType existingValue, bool hasExistingValue, JsonSerializer serializer) {
|
||||
return reader.Value != null ? Attractions[reader.Value.ToString()] : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
@ -21,14 +22,15 @@ namespace ThemeParkClicker {
|
|||
this.tickets = value;
|
||||
}
|
||||
}
|
||||
public ParkMap Map { get; private set; }
|
||||
public ParkMap Map;
|
||||
public Camera Camera { get; private set; }
|
||||
public Ui Ui { get; private set; }
|
||||
public bool DrawMap;
|
||||
|
||||
public DateTime LastUpdate;
|
||||
public float TicketsPerSecond { get; private set; }
|
||||
private long ticketsPerSecondCounter;
|
||||
private double secondCounter;
|
||||
private double saveCounter;
|
||||
|
||||
public GameImpl() {
|
||||
Instance = this;
|
||||
|
@ -37,7 +39,6 @@ namespace ThemeParkClicker {
|
|||
protected override void LoadContent() {
|
||||
base.LoadContent();
|
||||
this.Ui = new Ui(this.UiSystem);
|
||||
this.Map = new ParkMap(10, 10);
|
||||
this.Camera = new Camera(this.GraphicsDevice) {
|
||||
Scale = 5,
|
||||
AutoScaleWithScreen = true,
|
||||
|
@ -46,31 +47,40 @@ namespace ThemeParkClicker {
|
|||
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() {
|
||||
return this.Tickets.ToString();
|
||||
if (!SaveHandler.Load(this))
|
||||
this.Map = new ParkMap(10, 10);
|
||||
}
|
||||
|
||||
protected override void DoUpdate(GameTime gameTime) {
|
||||
base.DoUpdate(gameTime);
|
||||
this.Map.Update(gameTime);
|
||||
|
||||
var now = DateTime.Now;
|
||||
if (this.LastUpdate != default) {
|
||||
var passed = now - this.LastUpdate;
|
||||
// if more than 1 second passed, the app is minimized or a save was loaded, so we penalize
|
||||
if (passed.TotalSeconds >= 1)
|
||||
passed = new TimeSpan(passed.Ticks / 2);
|
||||
this.Map.Update(gameTime, passed);
|
||||
}
|
||||
this.LastUpdate = now;
|
||||
|
||||
this.Ui.Update(gameTime);
|
||||
|
||||
// we average tickets per second over two seconds
|
||||
// we average tickets per second over 4 seconds
|
||||
const float avgTime = 4;
|
||||
this.secondCounter += gameTime.ElapsedGameTime.TotalSeconds;
|
||||
if (this.secondCounter >= 2) {
|
||||
this.secondCounter -= 2;
|
||||
this.TicketsPerSecond = this.ticketsPerSecondCounter / 2F;
|
||||
if (this.secondCounter >= avgTime) {
|
||||
this.secondCounter -= avgTime;
|
||||
this.TicketsPerSecond = this.ticketsPerSecondCounter / avgTime;
|
||||
this.ticketsPerSecondCounter = 0;
|
||||
}
|
||||
|
||||
// save every 3 seconds
|
||||
this.saveCounter += gameTime.ElapsedGameTime.TotalSeconds;
|
||||
if (this.saveCounter >= 3) {
|
||||
this.saveCounter = 0;
|
||||
SaveHandler.Save(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoDraw(GameTime gameTime) {
|
||||
|
@ -83,5 +93,9 @@ namespace ThemeParkClicker {
|
|||
base.DoDraw(gameTime);
|
||||
}
|
||||
|
||||
public string DisplayTicketCount() {
|
||||
return this.Tickets.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input.Touch;
|
||||
|
@ -11,13 +12,15 @@ using MLEM.Textures;
|
|||
using ThemeParkClicker.Attractions;
|
||||
|
||||
namespace ThemeParkClicker {
|
||||
[DataContract]
|
||||
public class ParkMap {
|
||||
|
||||
[DataMember]
|
||||
public readonly int Width;
|
||||
[DataMember]
|
||||
public readonly int Height;
|
||||
|
||||
private readonly Attraction[,] grid;
|
||||
private readonly Dictionary<Point, Attraction> attractions = new Dictionary<Point, Attraction>();
|
||||
[DataMember]
|
||||
private readonly List<(Point, Attraction)> attractions = new List<(Point, Attraction)>();
|
||||
|
||||
public Attraction PlacingAttraction;
|
||||
public Point PlacingPosition;
|
||||
|
@ -26,12 +29,11 @@ namespace ThemeParkClicker {
|
|||
public ParkMap(int width, int height) {
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this.grid = new Attraction[width, height];
|
||||
}
|
||||
|
||||
public void Update(GameTime time) {
|
||||
foreach (var attraction in this.attractions.Values)
|
||||
attraction.GainTickets(time);
|
||||
public void Update(GameTime time, TimeSpan passed) {
|
||||
foreach (var (_, attraction) in this.attractions)
|
||||
attraction.Update(time, passed);
|
||||
|
||||
// map movement
|
||||
if (GameImpl.Instance.DrawMap) {
|
||||
|
@ -74,29 +76,36 @@ namespace ThemeParkClicker {
|
|||
// draw ground
|
||||
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 * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
|
||||
foreach (var (pos, attraction) in this.attractions)
|
||||
batch.Draw(attraction.Type.TextureRegion, position + pos.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);
|
||||
batch.Draw(this.PlacingAttraction.Type.TextureRegion, placingPos, Color.White * alpha * 0.5F, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanPlace(Point position, Attraction attraction) {
|
||||
foreach (var (x, y) in attraction.GetCoveredTiles()) {
|
||||
if (this.grid[position.X + x, position.Y + y] != null)
|
||||
foreach (var offset in attraction.GetCoveredTiles()) {
|
||||
if (this.GetAttractionAt(position + offset) != null)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Place(Point position, Attraction attraction) {
|
||||
foreach (var (x, y) in attraction.GetCoveredTiles())
|
||||
this.grid[position.X + x, position.Y + y] = attraction;
|
||||
this.attractions[position] = attraction;
|
||||
this.attractions.RemoveAll(pa => pa.Item1 == position);
|
||||
this.attractions.Add((position, attraction));
|
||||
}
|
||||
|
||||
public Attraction GetAttractionAt(Point position) {
|
||||
foreach (var (pos, attraction) in this.attractions) {
|
||||
if (attraction.GetCoveredTiles().Any(p => pos + p == position))
|
||||
return attraction;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
66
ThemeParkClicker/SaveHandler.cs
Normal file
66
ThemeParkClicker/SaveHandler.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ThemeParkClicker {
|
||||
public static class SaveHandler {
|
||||
|
||||
private static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings {
|
||||
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
Formatting = Formatting.Indented
|
||||
});
|
||||
private const int SaveVersion = 1;
|
||||
|
||||
public static void Save(GameImpl game) {
|
||||
var file = GetSaveFile(true);
|
||||
using (var stream = new JsonTextWriter(file.CreateText())) {
|
||||
var data = new SaveData {
|
||||
SaveVersion = SaveVersion,
|
||||
Tickets = game.Tickets,
|
||||
LastUpdate = game.LastUpdate,
|
||||
Map = game.Map
|
||||
};
|
||||
Serializer.Serialize(stream, data);
|
||||
}
|
||||
Console.WriteLine("Saved at " + file.FullName);
|
||||
}
|
||||
|
||||
public static bool Load(GameImpl game) {
|
||||
var file = GetSaveFile(false);
|
||||
if (!file.Exists)
|
||||
return false;
|
||||
using (var stream = new JsonTextReader(file.OpenText())) {
|
||||
var data = Serializer.Deserialize<SaveData>(stream);
|
||||
game.Tickets = data.Tickets;
|
||||
game.LastUpdate = data.LastUpdate;
|
||||
game.Map = data.Map;
|
||||
}
|
||||
Console.WriteLine("Loaded from " + file.FullName);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static DirectoryInfo GetGameDirectory(bool create) {
|
||||
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
var dir = new DirectoryInfo(Path.Combine(path, "ThemeParkClicker"));
|
||||
if (!dir.Exists && create)
|
||||
dir.Create();
|
||||
return dir;
|
||||
}
|
||||
|
||||
private static FileInfo GetSaveFile(bool create) {
|
||||
return new FileInfo(Path.Combine(GetGameDirectory(create).FullName, "Save"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SaveData {
|
||||
|
||||
public int SaveVersion;
|
||||
public BigInteger Tickets;
|
||||
public DateTime LastUpdate;
|
||||
public ParkMap Map;
|
||||
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
<PackageReference Include="MonoGame.Framework.Portable" Version="3.7.1.189">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -107,8 +107,8 @@ namespace ThemeParkClicker {
|
|||
ChildPadding = new Padding(5, 15, 5, 5),
|
||||
IsHidden = true
|
||||
};
|
||||
foreach (var attraction in Attraction.Attractions) {
|
||||
var instance = attraction.Value();
|
||||
foreach (var attraction in AttractionType.Attractions) {
|
||||
var instance = attraction.Value.Create();
|
||||
var button = buyUi.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 40)) {
|
||||
ChildPadding = new Vector2(4),
|
||||
Padding = new Padding(0, 0, 0, 4),
|
||||
|
@ -116,7 +116,7 @@ namespace ThemeParkClicker {
|
|||
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.PlacingAttraction = attraction.Value.Create();
|
||||
map.PlacingPosition = pos.ToPoint();
|
||||
|
||||
var yesNoUi = new Group(Anchor.BottomLeft, new Vector2(1));
|
||||
|
@ -142,8 +142,8 @@ namespace ThemeParkClicker {
|
|||
};
|
||||
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 + "<i ticket>/s", true) {TextScale = 0.08F});
|
||||
var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(1), instance.TextureRegion) {
|
||||
center.AddChild(new Paragraph(Anchor.AutoCenter, 1, instance.Type.GenerationPerSecond + "<i ticket>/s", true) {TextScale = 0.08F});
|
||||
var image = button.AddChild(new Image(Anchor.CenterLeft, new Vector2(1), instance.Type.TextureRegion) {
|
||||
Padding = new Vector2(4)
|
||||
});
|
||||
button.OnAreaUpdated += e => image.Size = new Vector2(e.DisplayArea.Height / e.Scale);
|
||||
|
|
Loading…
Reference in a new issue