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);
}
}
}
}