TouchyTickets/TouchyTickets/ParkMap.cs

180 lines
9.1 KiB
C#
Raw Normal View History

2020-05-31 21:16:50 +02:00
using System;
2020-05-31 17:24:10 +02:00
using System.Collections.Generic;
2020-05-31 21:16:50 +02:00
using System.Linq;
2020-06-01 14:45:20 +02:00
using System.Runtime.Serialization;
2020-05-31 17:24:10 +02:00
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
2020-05-31 21:16:50 +02:00
using Microsoft.Xna.Framework.Input.Touch;
2020-05-31 17:24:10 +02:00
using MLEM.Extensions;
using MLEM.Misc;
using MLEM.Startup;
using MLEM.Textures;
2020-06-01 17:39:57 +02:00
using TouchyTickets.Attractions;
2020-05-31 17:24:10 +02:00
2020-06-01 17:39:57 +02:00
namespace TouchyTickets {
2020-06-01 14:45:20 +02:00
[DataContract]
2020-05-31 17:24:10 +02:00
public class ParkMap {
2020-06-01 16:18:07 +02:00
private static readonly UniformTextureAtlas TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Tiles"), 16, 16);
2020-06-01 23:02:47 +02:00
private const int AdditionalRadius = 15;
2020-06-01 14:45:20 +02:00
[DataMember]
2020-05-31 17:24:10 +02:00
public readonly int Width;
2020-06-01 14:45:20 +02:00
[DataMember]
2020-05-31 17:24:10 +02:00
public readonly int Height;
2020-06-01 14:45:20 +02:00
[DataMember]
private readonly List<(Point, Attraction)> attractions = new List<(Point, Attraction)>();
2020-06-01 16:18:07 +02:00
private readonly Dictionary<Point, int> treePositions = new Dictionary<Point, int>();
private readonly Dictionary<Point, int> fencePositions = new Dictionary<Point, int>();
2020-06-02 12:20:16 +02:00
private readonly Attraction[,] attractionGrid;
2020-05-31 17:24:10 +02:00
2020-06-01 16:18:07 +02:00
public float TicketsPerSecond { get; private set; }
2020-06-01 01:21:54 +02:00
public Attraction PlacingAttraction;
public Point PlacingPosition;
private bool draggingAttraction;
2020-05-31 17:24:10 +02:00
public ParkMap(int width, int height) {
this.Width = width;
this.Height = height;
2020-06-02 12:20:16 +02:00
this.attractionGrid = new Attraction[width, height];
2020-06-01 16:18:07 +02:00
// set up trees
var random = new Random();
2020-06-01 23:02:47 +02:00
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);
2020-06-01 16:18:07 +02:00
this.treePositions[new Point(x, y)] = type;
2020-06-01 23:02:47 +02:00
}
2020-06-01 16:18:07 +02:00
}
// set up fences
this.fencePositions[new Point(-1, -1)] = 2;
this.fencePositions[new Point(this.Width, -1)] = 3;
this.fencePositions[new Point(-1, this.Height)] = 4;
this.fencePositions[new Point(this.Width, this.Height)] = 5;
for (var x = 0; x < this.Width; x++) {
this.fencePositions[new Point(x, -1)] = 0;
this.fencePositions[new Point(x, this.Height)] = 0;
}
for (var y = 0; y < this.Height; y++) {
this.fencePositions[new Point(-1, y)] = 1;
this.fencePositions[new Point(this.Width, y)] = 1;
}
2020-05-31 17:24:10 +02:00
}
2020-06-01 14:45:20 +02:00
public void Update(GameTime time, TimeSpan passed) {
2020-06-01 16:18:07 +02:00
var tickets = 0F;
2020-06-02 12:20:16 +02:00
foreach (var (pos, attraction) in this.attractions) {
var genPerSecond = attraction.Update(time, passed, this, pos);
tickets += genPerSecond;
2020-06-01 16:18:07 +02:00
}
this.TicketsPerSecond = tickets;
2020-05-31 21:16:50 +02:00
// map movement
if (GameImpl.Instance.DrawMap) {
var camera = GameImpl.Instance.Camera;
if (MlemGame.Input.GetGesture(GestureType.Pinch, out var pinch)) {
var startDiff = pinch.Position2 - pinch.Position;
var endDiff = pinch.Position2 + pinch.Delta2 - (pinch.Position + pinch.Delta);
if (startDiff.LengthSquared() < endDiff.LengthSquared()) {
// zooming in
camera.Zoom(pinch.Delta.Length() / camera.Scale);
} else {
// zooming out
camera.Zoom(-pinch.Delta.Length() / camera.Scale);
}
} else if (MlemGame.Input.GetGesture(GestureType.FreeDrag, out var drag)) {
2020-06-01 01:21:54 +02:00
if (this.draggingAttraction) {
// move the current placing position
var nextPos = (camera.ToWorldPos(drag.Position + drag.Delta) / Attraction.TileSize).ToPoint();
2020-06-02 15:49:46 +02:00
// drag the center of the attraction
nextPos -= new Point(this.PlacingAttraction.Type.Width / 2, this.PlacingAttraction.Type.Height / 2);
if (this.PlacingAttraction.Type.GetCoveredTiles().Select(p => nextPos + p).All(p => p.X >= 0 && p.Y >= 0 && p.X < this.Width && p.Y < this.Height))
2020-06-01 01:21:54 +02:00
this.PlacingPosition = nextPos;
} else {
// move the camera
camera.Position -= drag.Delta / camera.Scale;
}
} else if (this.PlacingAttraction != null) {
foreach (var touch in MlemGame.Input.TouchState) {
if (touch.State != TouchLocationState.Pressed)
continue;
// when first pressing down, go into attraction drag mode if we're touching the place location
var offset = (camera.ToWorldPos(touch.Position) / Attraction.TileSize).ToPoint();
2020-06-02 15:49:46 +02:00
this.draggingAttraction = this.PlacingAttraction.Type.GetCoveredTiles()
2020-06-01 01:21:54 +02:00
.Any(p => this.PlacingPosition + p == offset);
}
2020-05-31 21:16:50 +02:00
}
2020-06-01 16:18:07 +02:00
camera.ConstrainWorldBounds(new Vector2(-AdditionalRadius) * Attraction.TileSize, new Vector2(this.Width + AdditionalRadius, this.Height + AdditionalRadius) * Attraction.TileSize);
2020-05-31 21:16:50 +02:00
}
2020-05-31 17:24:10 +02:00
}
public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale, float alpha, bool showSurroundings, RectangleF visibleArea) {
2020-05-31 17:24:10 +02:00
var tileSize = Attraction.TileSize * scale;
// draw ground
var additionalRadius = showSurroundings ? AdditionalRadius : 0;
var minX = Math.Max(-additionalRadius, visibleArea.Left / tileSize.X).Floor();
var minY = Math.Max(-additionalRadius, visibleArea.Top / tileSize.Y).Floor();
var maxX = Math.Min(this.Width + additionalRadius, visibleArea.Right / tileSize.X).Ceil();
var maxY = Math.Min(this.Height + additionalRadius, visibleArea.Bottom / tileSize.Y).Ceil();
for (var x = minX; x < maxX; x++) {
for (var y = minY; y < maxY; y++) {
2020-06-01 16:18:07 +02:00
var pos = new Vector2(x, y);
var drawPos = position + pos * tileSize;
batch.Draw(TilesTexture[0, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
if (this.fencePositions.TryGetValue(pos.ToPoint(), out var fenceType)) {
batch.Draw(TilesTexture[fenceType, 1], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
} else if (this.treePositions.TryGetValue(pos.ToPoint(), out var treeType)) {
batch.Draw(TilesTexture[1 + treeType, 0], drawPos, Color.White * alpha, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
}
}
}
2020-05-31 17:24:10 +02:00
// draw attractions
2020-06-01 14:45:20 +02:00
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);
2020-06-01 01:21:54 +02:00
// placing attraction
if (this.PlacingAttraction != null) {
var placingPos = position + this.PlacingPosition.ToVector2() * tileSize;
2020-06-02 15:49:46 +02:00
foreach (var pos in this.PlacingAttraction.Type.GetCoveredTiles())
2020-06-01 01:21:54 +02:00
batch.Draw(batch.GetBlankTexture(), new RectangleF(placingPos + pos.ToVector2() * tileSize, tileSize), Color.Black * 0.15F * alpha);
2020-06-01 14:45:20 +02:00
batch.Draw(this.PlacingAttraction.Type.TextureRegion, placingPos, Color.White * alpha * 0.5F, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
2020-06-01 01:21:54 +02:00
}
2020-05-31 17:24:10 +02:00
}
public bool CanPlace(Point position, Attraction attraction) {
2020-06-02 15:49:46 +02:00
foreach (var offset in attraction.Type.GetCoveredTiles()) {
2020-06-01 14:45:20 +02:00
if (this.GetAttractionAt(position + offset) != null)
2020-06-01 01:21:54 +02:00
return false;
2020-05-31 17:24:10 +02:00
}
return true;
}
public void Place(Point position, Attraction attraction) {
2020-06-02 15:49:46 +02:00
foreach (var (x, y) in attraction.Type.GetCoveredTiles())
2020-06-02 12:20:16 +02:00
this.attractionGrid[position.X + x, position.Y + y] = attraction;
2020-06-01 14:45:20 +02:00
this.attractions.Add((position, attraction));
}
public Attraction GetAttractionAt(Point position) {
2020-06-02 14:16:14 +02:00
if (position.X < 0 || position.Y < 0 || position.X >= this.Width || position.Y >= this.Height)
return null;
2020-06-02 12:20:16 +02:00
return this.attractionGrid[position.X, position.Y];
2020-05-31 17:24:10 +02:00
}
2020-06-01 15:18:20 +02:00
public int GetAttractionAmount(AttractionType type) {
return this.attractions.Count(a => a.Item2.Type == type);
}
2020-06-02 12:20:16 +02:00
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)
newMap.Place(pos, attraction);
return newMap;
2020-06-01 23:02:47 +02:00
}
2020-05-31 17:24:10 +02:00
}
}