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; using MLEM.Extensions; using MLEM.Misc; using MLEM.Startup; using MLEM.Textures; using ThemeParkClicker.Attractions; namespace ThemeParkClicker { [DataContract] public class ParkMap { private static readonly UniformTextureAtlas TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent("Textures/Tiles"), 16, 16); private const int AdditionalRadius = 10; [DataMember] public readonly int Width; [DataMember] public readonly int Height; [DataMember] private readonly List<(Point, Attraction)> attractions = new List<(Point, Attraction)>(); private readonly Dictionary treePositions = new Dictionary(); private readonly Dictionary fencePositions = new Dictionary(); public float TicketsPerSecond { get; private set; } public Attraction PlacingAttraction; public Point PlacingPosition; private bool draggingAttraction; public ParkMap(int width, int height) { this.Width = width; this.Height = height; // set up trees var random = new Random(); for (var i = 0; i < 100; i++) { var type = random.Next(3); var x = random.Next(-AdditionalRadius, this.Width + AdditionalRadius); var y = random.Next(-AdditionalRadius, this.Height + AdditionalRadius); if (x < 0 || y < 0 || x >= this.Width || y >= this.Width) this.treePositions[new Point(x, y)] = type; } // 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; } } public void Update(GameTime time, TimeSpan passed) { var tickets = 0F; foreach (var (_, attraction) in this.attractions) { attraction.Update(time, passed); tickets += attraction.Type.GenerationPerSecond; } this.TicketsPerSecond = tickets; // 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)) { if (this.draggingAttraction) { // move the current placing position var nextPos = (camera.ToWorldPos(drag.Position + drag.Delta) / Attraction.TileSize).ToPoint(); if (nextPos.X >= 0 && nextPos.Y >= 0 && nextPos.X < this.Width && nextPos.Y < this.Height) 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(); this.draggingAttraction = this.PlacingAttraction.GetCoveredTiles() .Any(p => this.PlacingPosition + p == offset); } } camera.ConstrainWorldBounds(new Vector2(-AdditionalRadius) * Attraction.TileSize, new Vector2(this.Width + AdditionalRadius, this.Height + AdditionalRadius) * Attraction.TileSize); } } public void Draw(GameTime time, SpriteBatch batch, Vector2 position, float scale, float alpha, bool showSurroundings, RectangleF visibleArea) { 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++) { 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); } } } // draw attractions 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.Type.TextureRegion, placingPos, Color.White * alpha * 0.5F, 0, Vector2.Zero, scale, SpriteEffects.None, 0); } } public bool CanPlace(Point position, Attraction attraction) { foreach (var offset in attraction.GetCoveredTiles()) { if (this.GetAttractionAt(position + offset) != null) return false; } return true; } public void Place(Point position, Attraction 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; } public int GetAttractionAmount(AttractionType type) { return this.attractions.Count(a => a.Item2.Type == type); } } }