using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using V3.Data; using V3.Effects; namespace V3.Objects { /// /// Class for selecting creatures, holding the selection and drawing the selection rectangle on the screen. /// // ReSharper disable once ClassNeverInstantiated.Global public sealed class Selection { private IObjectsManager mObjectsManager; private IEffectsManager mEffectsManager; private readonly Color mColor = Color.Black; private Texture2D mTexture; public List SelectedCreatures { get; private set; } = new List(); private List TransformList { get; } = new List(); private readonly CreatureFactory mCreatureFactory; private int mDeadBodies; private int mExplosionDeaths; private Ellipse NecroArea { get; set; } public Rectangle LastSelection { get; private set; } private SoundEffect mSoundEffect; private SoundEffectInstance mSoundEffectInstance; private IOptionsManager mOptionsManager; private AchievementsAndStatistics mAchievementsAndStatistics; public Selection(ContentManager contentManager, CreatureFactory creatureFactory, IObjectsManager objectsManager, IEffectsManager effectsManager, IOptionsManager optionsmanager, AchievementsAndStatistics achievementsAndStatistics) { mCreatureFactory = creatureFactory; mObjectsManager = objectsManager; mEffectsManager = effectsManager; mOptionsManager = optionsmanager; mAchievementsAndStatistics = achievementsAndStatistics; LoadContent(contentManager); } private void LoadContent(ContentManager contentManager) { mTexture = contentManager.Load("Sprites/WhiteRectangle"); mSoundEffect = contentManager.Load("Sounds/zonk2"); mSoundEffectInstance = mSoundEffect.CreateInstance(); } /// /// Select all objects in the area of the rectangle between origin and destination. /// /// Position in pixels where the mouse button was pressed down. /// Position in pixels where the mouse button was released. public void Select(Point origin, Point destination) { // Clean last selection SelectedCreatures.ForEach(e => e.IsSelected=false); SelectedCreatures.Clear(); // New selection int x, y, width, height; if (origin.X > destination.X) { x = destination.X; width = origin.X - destination.X; } else { x = origin.X; width = destination.X - origin.X; } if (origin.Y > destination.Y) { y = destination.Y; height = origin.Y - destination.Y; } else { y = origin.Y; height = destination.Y - origin.Y; } // Creates area for necromancer var necroPos = mObjectsManager.PlayerCharacter; NecroArea = new Ellipse(new Vector2((int)necroPos.Position.X, (int)necroPos.Position.Y), 1280, 640); // List for all objects in the current selection LastSelection = new Rectangle(x, y, width, height); var selectedObjects = mObjectsManager.GetObjectsInRectangle(LastSelection); // Lists to seperate undead from enemy creatures var enemyCreatures = new List(); var undeadCreatures = new List(); foreach (var obj in selectedObjects) { var creature = obj as ICreature; // Give priority when selecting undead creatures. if (creature != null && !creature.IsDead && LastSelection.Intersects(creature.SelectionRectangle) && NecroArea.Contains(creature.Position)) { if (creature.Faction != Faction.Undead) { enemyCreatures.Add(creature); } else { undeadCreatures.Add(creature); } } } SelectedCreatures = undeadCreatures.Count == 0 ? enemyCreatures : undeadCreatures; // Make all selectable creatures selected foreach (var selectedCreature in SelectedCreatures) { selectedCreature.IsSelected = true; } } /// /// Draw the selection rectangle on the screen. /// /// Sprite batch used. /// Position in pixels where the mouse button was pressed down. /// Position in pixels where the mouse button was released. public void Draw(SpriteBatch spriteBatch, Point origin, Point destination) { if (origin.X > destination.X) { spriteBatch.Draw(mTexture, new Rectangle(destination.X, origin.Y, origin.X - destination.X, 2), mColor); spriteBatch.Draw(mTexture, new Rectangle(destination.X, destination.Y, origin.X - destination.X, 2), mColor); } else { spriteBatch.Draw(mTexture, new Rectangle(origin.X, origin.Y, destination.X - origin.X, 2), mColor); spriteBatch.Draw(mTexture, new Rectangle(origin.X, destination.Y, destination.X - origin.X, 2), mColor); } if (origin.Y > destination.Y) { spriteBatch.Draw(mTexture, new Rectangle(origin.X, destination.Y, 2, origin.Y - destination.Y), mColor); spriteBatch.Draw(mTexture, new Rectangle(destination.X, destination.Y, 2, origin.Y - destination.Y), mColor); } else { spriteBatch.Draw(mTexture, new Rectangle(origin.X, origin.Y, 2, destination.Y - origin.Y), mColor); spriteBatch.Draw(mTexture, new Rectangle(destination.X, origin.Y, 2, destination.Y - origin.Y), mColor); } } /// /// Press 1 to trigger the specialattack of the meatball /// All non undead creatures at the distance of 200 pixels take damage /// After the attack the meatball disappears and three zombies will be created /// public void Specialattack() { foreach (var creature in SelectedCreatures) { var meatball = creature as Meatball; if (meatball != null) { // Creates radius for meatballs explosion var explosionRadius = new Rectangle((int)meatball.Position.X - 200, (int)meatball.Position.Y - 200, 400, 400); var objectsInMeatballArea = mObjectsManager.GetObjectsInRectangle(explosionRadius); // Plays sounds and effects mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); mEffectsManager.PlayOnce(new Explosion(), meatball.Position.ToPoint(), explosionRadius.Size * new Point(3, 2)); // All attackable creatures take damage (own undead creatures won't take damage) foreach (var attackable in objectsInMeatballArea) { var toAttack = attackable as ICreature; if (toAttack != null && toAttack.Faction != Faction.Undead) { toAttack.TakeDamage(meatball.Attack); if (toAttack.IsDead) mExplosionDeaths += 1; } } if (mExplosionDeaths >= 10) mAchievementsAndStatistics.mKaboom = true; mExplosionDeaths = 0; // The new zombies after explosion var zombie1 = mCreatureFactory.CreateZombie(meatball.Position, meatball.MovementDirection); var zombie2 = mCreatureFactory.CreateZombie(new Vector2(meatball.Position.X + 50, meatball.Position.Y + 50), meatball.MovementDirection); var zombie3 = mCreatureFactory.CreateZombie(new Vector2(meatball.Position.X - 50, meatball.Position.Y - 50), meatball.MovementDirection); // Makes zombies be selected after explosion zombie1.IsSelected = true; zombie2.IsSelected = true; zombie3.IsSelected = true; meatball.IsSelected = false; SelectedCreatures.Add(zombie1); SelectedCreatures.Add(zombie2); SelectedCreatures.Add(zombie3); SelectedCreatures.Remove(meatball); // Remove meatball from game mObjectsManager.RemoveCreature(meatball); // Add zombies to game mObjectsManager.CreateCreature(zombie1); mObjectsManager.CreateCreature(zombie2); mObjectsManager.CreateCreature(zombie3); // For every zombie the necromancer gets healed var heal = mObjectsManager.PlayerCharacter.Life * 0.12; mObjectsManager.PlayerCharacter.Heal((int)heal); break; } } } /// /// Press 2 to create zombies from dead bodies /// At least one dead body should be in necromancers area /// public void TransformZombie() { // Creates area for necromancer var necroPos = mObjectsManager.PlayerCharacter; NecroArea = new Ellipse(new Vector2((int) necroPos.Position.X, (int) necroPos.Position.Y), 1280, 640); // Get all creatures in necromancers area var necroArea = mObjectsManager.GetCreaturesInEllipse(NecroArea); // Every dead body in necromancer area will be transformed to an zombie // The prince will be transformed to an elite zombie foreach (var creature in necroArea) { if (creature.Faction == Faction.Plebs || creature.Faction == Faction.Kingdom) { if (creature.IsDead) { ICreature zombie; if (creature is KingsGuard || creature is King || creature is Prince) zombie = mCreatureFactory.CreateSkeletonElite(creature.Position, creature.MovementDirection); else zombie = mCreatureFactory.CreateZombie(creature.Position, creature.MovementDirection); // Add zombie to game mObjectsManager.CreateCreature(zombie); // Remove dead body from game mObjectsManager.RemoveCreature(creature); // Play sounds and effects mEffectsManager.PlayOnce(new BloodBang(), zombie.Position.ToPoint(), new Point(128)); mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); // The number of dead bodies mDeadBodies++; // For every zombie the necromancer gets healed var heal = mObjectsManager.PlayerCharacter.Life * 0.04; mObjectsManager.PlayerCharacter.Heal((int)heal); } } } // Counts dead bodies in necromancers area and undead creatures var set = mDeadBodies + mObjectsManager.UndeadCreatures.Count; if (mObjectsManager.InGraveyardArea(mObjectsManager.PlayerCharacter) && set < 6) { mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); for (int i = 1; i <= 6 - set; i++) { var positionX = mObjectsManager.PlayerCharacter.Position.X + (float)Math.Sin(i) * 75; var positionY = mObjectsManager.PlayerCharacter.Position.Y + (float)Math.Cos(i) * 75; var zombie = mCreatureFactory.CreateZombie(new Vector2(positionX, positionY), mObjectsManager.PlayerCharacter.MovementDirection); mObjectsManager.CreateCreature(zombie); zombie.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(1)); } } mDeadBodies = 0; } /// /// Press 3 to create a meatball in exchange for five zombie /// At least five zombies should be selected /// public void TransformMeatball() { // The cost for this transformation var cost = mObjectsManager.PlayerCharacter.MaxLife * 0.12; // Transform only when necromancer life minus the transformations cost is not lower than 1 if (mObjectsManager.PlayerCharacter.Life - (int)cost >= 1) { // Puts all zombies in a seperate list called TransformList foreach (var creature in SelectedCreatures) { var zombie = creature as Zombie; if (zombie != null && TransformList.Count < 5) { TransformList.Add(zombie); } } // Creates the meatball and makes him selected if (TransformList.Count >= 5) { // Delete zombies from game and SelectedCratures foreach (var zombie in TransformList) { SelectedCreatures.Remove(zombie); mObjectsManager.RemoveCreature(zombie); } // Add meatball to game // Position will be randomly chosen from one of the zombies var randomNumber = new Random(); var positionMeatball = randomNumber.Next(5); var meatball = mCreatureFactory.CreateMeatball(TransformList[positionMeatball].Position, TransformList[positionMeatball].MovementDirection); meatball.IsSelected = true; SelectedCreatures.Add(meatball); mObjectsManager.CreateCreature(meatball); // Plays sounds and effects mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); mEffectsManager.PlayOnce(new BloodExplosion(), meatball.Position.ToPoint(), new Point(256)); // Necromancer takes damage mObjectsManager.PlayerCharacter.TakeDamage((int)cost); } TransformList.Clear(); } else { // Play sound when transformation not possible mSoundEffectInstance.Volume = mOptionsManager.Options.GetEffectiveVolume(); mSoundEffectInstance.Play(); } } /// /// Press 4 to create an skeleton in exchange for an zombie /// At least one zombie should be selected /// public void TransformSkeleton() { // Iterates through SelectedCreatures and takes the first zombie foreach (var creature in SelectedCreatures) { var zombie = creature as Zombie; if (zombie != null) { // Delete zombie SelectedCreatures.Remove(zombie); mObjectsManager.RemoveCreature(zombie); // Add skeleton var skeleton = mCreatureFactory.CreateSkeleton(zombie.Position, zombie.MovementDirection); skeleton.IsSelected = true; SelectedCreatures.Add(skeleton); mObjectsManager.CreateCreature(skeleton); // Plays sounds and effects mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); mEffectsManager.PlayOnce(new BloodFountain(), zombie.Position.ToPoint() - new Point(0, 64), new Point(128)); break; } } } /// /// Press 5 to create an skeletonhorse in exchange for three skeletons /// At least three skeletons should be selected /// public void TransformSkeletonHorse() { // The cost for this transformation var cost = mObjectsManager.PlayerCharacter.MaxLife * 0.06; // Transform only when necromancer life minus the transformations cost is not lower than 1 if (mObjectsManager.PlayerCharacter.Life - (int)cost >= 1) { // Puts all skeletons in a seperate list called TransformList foreach (var creature in SelectedCreatures) { var skeleton = creature as Skeleton; if (skeleton != null && TransformList.Count < 3) { TransformList.Add(skeleton); } } // Creates the skeletonhorse and makes him selected if (TransformList.Count >= 3) { // Delete zombies from game and SelectedCratures foreach (var skeleton in TransformList) { SelectedCreatures.Remove(skeleton); mObjectsManager.RemoveCreature(skeleton); } // Add skeletonhorse to game // Position will be randomly chosen from one of the skeletons var randomNumber = new Random(); var positionHorse = randomNumber.Next(3); var skeletonHorse = mCreatureFactory.CreateSkeletonHorse(TransformList[positionHorse].Position, TransformList[positionHorse].MovementDirection); skeletonHorse.IsSelected = true; SelectedCreatures.Add(skeletonHorse); mObjectsManager.CreateCreature(skeletonHorse); // Plays sounds and effects mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d)); mEffectsManager.PlayOnce(new HorseEffect(), skeletonHorse.Position.ToPoint() - new Point(0, 16), new Point(128)); // Necromancer takes damage mObjectsManager.PlayerCharacter.TakeDamage((int)cost); } TransformList.Clear(); } else { // Play sound when transformation not possible mSoundEffectInstance.Volume = mOptionsManager.Options.GetEffectiveVolume(); mSoundEffectInstance.Play(); } } /// /// If creature leaves area og the necromancer, he gets disselected /// public void UpdateSelection() { // Creates area for necromancer var necroPos = mObjectsManager.PlayerCharacter; NecroArea = new Ellipse(new Vector2((int)necroPos.Position.X, (int)necroPos.Position.Y), 1280, 640); foreach (var creature in SelectedCreatures) { if (!NecroArea.Contains(creature.Position) || creature.IsDead) creature.IsSelected = false; } if (mObjectsManager.AddToSelectables.Count > 0) { SelectedCreatures.Add(mObjectsManager.AddToSelectables[0]); mObjectsManager.AddToSelectables.Clear(); } } /// /// Give movement command to selected creatures. /// /// Desired destination in pixels. public void Move(Vector2 destination) { if (SelectedCreatures.Count == 0) return; var centreCreature = SelectedCreatures[0]; // You can not move enemy creatures. if (centreCreature.Faction != Faction.Undead) return; centreCreature.Move(destination); for (int i = 1; i < SelectedCreatures.Count; i++) { Vector2 flockDestination = destination; Vector2 distance = centreCreature.Position - SelectedCreatures[i].Position; distance.Normalize(); distance *= 70; flockDestination -= distance; SelectedCreatures[i].Move(flockDestination); } } } }