diff --git a/Subsurface/Characters/Character.cs b/Subsurface/Characters/Character.cs index 49374a430..4eea16735 100644 --- a/Subsurface/Characters/Character.cs +++ b/Subsurface/Characters/Character.cs @@ -1,963 +1,960 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Xml.Linq; -using FarseerPhysics; -using FarseerPhysics.Dynamics.Joints; -using Lidgren.Network; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using Subsurface.Networking; -using Subsurface.Particles; - -namespace Subsurface -{ - class Character : Entity, IDamageable - { - public static List characterList = new List(); - - public static Queue newCharacterQueue = new Queue(); - - public static bool disableControls; - - //the character that the player is currently controlling - private static Character controlled; - - public static Character Controlled - { - get { return controlled; } - set { controlled = value; } - } - - public readonly bool IsNetworkPlayer; - - private Inventory inventory; - - public double lastNetworkUpdate; - - public byte largeUpdateTimer; - - public readonly Dictionary properties; - - protected Key selectKeyHit; - protected Key actionKeyHit; - protected Key actionKeyDown; - protected Key secondaryKeyHit; - protected Key secondaryKeyDown; - - private Item selectedConstruction; - private Item[] selectedItems; - - public AnimController animController; - private AIController aiController; - - private Vector2 cursorPosition; - - protected bool needsAir; - protected float oxygen; - protected float drowningTime; - - protected Item closestItem; - - protected bool isDead; - - bool isHumanoid; - - //the name of the species (e.q. human) - public readonly string speciesName; - - public CharacterInfo info; - - protected float soundTimer; - protected float soundInterval; - - private float blood; - - private Sound[] sounds; - //which AIstate each sound is for - private AIController.AiState[] soundStates; - - public Inventory Inventory - { - get { return inventory; } - } - - public Vector2 CursorPosition - { - get { return cursorPosition; } - } - - public float SoundRange - { - get { return aiTarget.SoundRange; } - } - - public float SightRange - { - get { return aiTarget.SightRange; } - } - private float pressureProtection; - public float PressureProtection - { - get { return pressureProtection; } - set - { - //if (!value && pressureProtection) Debug.WriteLine("pp set: " + value); - pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f); - - } - } - - public float Oxygen - { - get { return oxygen; } - set - { - oxygen = MathHelper.Clamp(value, 0.0f, 100.0f); - if (oxygen == 0.0f) Kill(); - } - } - - public float Blood - { - get { return blood; } - set - { - blood = MathHelper.Clamp(value, 0.0f, 100.0f); - if (blood == 0.0f) Kill(); - } - } - - public Item[] SelectedItems - { - get { return selectedItems; } - } - - public bool HasSelectedItem(Item item) - { - return selectedItems.Contains(item); - } - - public bool TrySelectItem(Item item) - { - bool rightHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.RightHand); - bool leftHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.LeftHand); - - bool selected = false; - if (rightHand && SelectedItems[0] == null) - { - selectedItems[0] = item; - selected = true; - } - if (leftHand && SelectedItems[1] == null) - { - selectedItems[1] = item; - selected = true; - } - - return selected; - } - - public bool TrySelectItem(Item item, int index) - { - if (selectedItems[index] != null) return false; - - selectedItems[index] = item; - return true; - } - - public void DeselectItem(Item item) - { - for (int i = 0; i < selectedItems.Length; i++) - { - if (selectedItems[i] == item) selectedItems[i] = null; - } - } - - public Item SelectedConstruction - { - get { return selectedConstruction; } - set { selectedConstruction = value; } - } - - public Item ClosestItem - { - get { return closestItem; } - } - - public Key SelectKeyHit - { - get { return selectKeyHit; } - } - - public Key ActionKeyHit - { - get { return actionKeyHit; } - } - - public Key ActionKeyDown - { - get { return actionKeyDown; } - } - - public Key SecondaryKeyHit - { - get { return secondaryKeyHit; } - } - - public Key SecondaryKeyDown - { - get { return secondaryKeyDown; } - } - - public bool IsDead - { - get { return isDead; } - } - - public override Vector2 SimPosition - { - get { return animController.limbs[0].SimPosition; } - } - - public Vector2 Position - { - get { return ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); } - } - - public Character(string file) : this(file, Vector2.Zero, null) - { - } - - public Character(string file, Vector2 position) - : this(file, position, null) - { - } - - public Character(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) - : this(characterInfo.file, position, characterInfo, isNetworkPlayer) - { - } - - public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) - { - selectKeyHit = new Key(false); - actionKeyDown = new Key(true); - actionKeyHit = new Key(false); - secondaryKeyHit = new Key(false); - secondaryKeyDown = new Key(true); - - selectedItems = new Item[2]; - - IsNetworkPlayer = isNetworkPlayer; - - oxygen = 100.0f; - blood = 100.0f; - aiTarget = new AITarget(this); - - properties = ObjectProperty.GetProperties(this); - - XDocument doc = ToolBox.TryLoadXml(file); - if (doc == null) return; - - speciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown"); - - isHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false); - - info = characterInfo ?? new CharacterInfo(file); - - if (isHumanoid) - { - animController = new HumanoidAnimController(this, doc.Root.Element("ragdoll")); - animController.targetDir = Direction.Right; - inventory = new CharacterInventory(10, this); - } - else - { - animController = new FishAnimController(this, doc.Root.Element("ragdoll")); - PressureProtection = 100.0f; - //FishAnimController fishAnim = (FishAnimController)animController; - - aiController = new EnemyAIController(this, file); - } - - foreach (Limb limb in animController.limbs) - { - limb.body.SetTransform(position+limb.SimPosition, 0.0f); - //limb.prevPosition = ConvertUnits.ToDisplayUnits(position); - } - - needsAir = ToolBox.GetAttributeBool(doc.Root, "needsair", false); - drowningTime = ToolBox.GetAttributeFloat(doc.Root, "drowningtime", 10.0f); - - soundInterval = ToolBox.GetAttributeFloat(doc.Root, "soundinterval", 10.0f); - - var xSounds = doc.Root.Elements("sound"); - if (xSounds.Count() > 0) - { - sounds = new Sound[xSounds.Count()]; - soundStates = new AIController.AiState[xSounds.Count()]; - int i = 0; - foreach (XElement xSound in xSounds) - { - sounds[i] = Sound.Load(xSound.Attribute("file").Value); - if (xSound.Attribute("state") == null) - { - soundStates[i] = AIController.AiState.None; - } - else - { - soundStates[i] = (AIController.AiState)Enum.Parse( - typeof(AIController.AiState), xSound.Attribute("state").Value, true); - } - i++; - } - } - - - animController.FindHull(); - - if (this.info.ID >= 0) - { - ID = this.info.ID; - } - - characterList.Add(this); - } - - - /// - /// Control the characte - /// - public void Control(Camera cam, bool forcePick=false) - { - if (isDead) return; - - //find the closest item if selectkey has been hit, or if the character is being - //controlled by the player (in order to highlight it) - //closestItem = null; - if (controlled==this) - { - Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); - closestItem = FindClosestItem(mouseSimPos); - - if (closestItem != null) - { - closestItem.IsHighlighted = true; - if (selectKeyHit.State && closestItem.Pick(this, forcePick)) - { - new NetworkEvent(NetworkEventType.PickItem, ID, true, closestItem.ID); - } - } - } - - for (int i = 0; i < selectedItems.Length; i++ ) - { - if (selectedItems[i] == null) continue; - if (i == 1 && selectedItems[0] == selectedItems[1]) continue; - - if (actionKeyDown.State) selectedItems[i].Use(this); - if (secondaryKeyDown.State && selectedItems[i] != null) selectedItems[i].SecondaryUse(this); - - } - - if (selectedConstruction != null) - { - if (actionKeyDown.State) selectedConstruction.Use(this); - if (secondaryKeyDown.State) selectedConstruction.SecondaryUse(this); - } - - if (IsNetworkPlayer) - { - selectKeyHit.Reset(); - actionKeyHit.Reset(); - actionKeyDown.Reset(); - secondaryKeyHit.Reset(); - secondaryKeyDown.Reset(); - } - } - - private Item FindClosestItem(Vector2 mouseSimPos) - { - Limb torso = animController.GetLimb(LimbType.Torso); - Vector2 pos = (torso.body.TargetPosition != Vector2.Zero) ? torso.body.TargetPosition : torso.SimPosition; - - return Item.FindPickable(pos, selectedConstruction == null ? mouseSimPos : selectedConstruction.SimPosition, null, selectedItems); - } - - /// - /// Control the character according to player input - /// - public void ControlLocalPlayer(Camera cam, bool moveCam = true) - { - if (isDead) return; - - Limb head = animController.GetLimb(LimbType.Head); - - Lights.LightManager.viewPos = ConvertUnits.ToDisplayUnits(head.SimPosition); - - Vector2 targetMovement = Vector2.Zero; - - if (!disableControls) - { - if (PlayerInput.KeyDown(Keys.W)) targetMovement.Y += 1.0f; - if (PlayerInput.KeyDown(Keys.S)) targetMovement.Y -= 1.0f; - if (PlayerInput.KeyDown(Keys.A)) targetMovement.X -= 1.0f; - if (PlayerInput.KeyDown(Keys.D)) targetMovement.X += 1.0f; - - //the vertical component is only used for falling through platforms and climbing ladders when not in water, - //so the movement can't be normalized or the character would walk slower when pressing down/up - if (animController.InWater) - { - float length = targetMovement.Length(); - if (length > 0.0f) targetMovement = targetMovement / length; - } - - if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) && Math.Sign(targetMovement.X) == Math.Sign(animController.Dir)) - targetMovement *= 3.0f; - - selectKeyHit.SetState(PlayerInput.KeyHit(Keys.E)); - actionKeyHit.SetState(PlayerInput.LeftButtonClicked()); - actionKeyDown.SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed); - secondaryKeyHit.SetState(PlayerInput.RightButtonClicked()); - secondaryKeyDown.SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed); - } - else - { - selectKeyHit.SetState(false); - actionKeyHit.SetState(false); - actionKeyDown.SetState(false); - secondaryKeyHit.SetState(false); - secondaryKeyDown.SetState(false); - } - - animController.TargetMovement = targetMovement; - animController.isStanding = true; - - if (moveCam) - { - cam.TargetPos = ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); - cam.OffsetAmount = 250.0f; - } - - cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition); - Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition); - - if (animController.onGround && - !animController.InWater && - animController.anim != AnimController.Animation.UsingConstruction) - { - if (mouseSimPos.X < head.SimPosition.X-1.0f) - { - animController.targetDir = Direction.Left; - } - else if (mouseSimPos.X > head.SimPosition.X + 1.0f) - { - animController.targetDir = Direction.Right; - } - } - - disableControls = false; - } - - - public static void UpdateAnimAll(float deltaTime) - { - foreach (Character c in characterList) - { - if (c.isDead) continue; - c.animController.UpdateAnim(deltaTime); - } - } - - public static void UpdateAll(Camera cam, float deltaTime) - { - if (newCharacterQueue.Count>0) - { - new Character(newCharacterQueue.Dequeue(), Vector2.Zero); - } - - foreach (Character c in characterList) - { - c.Update(cam, deltaTime); - } - } - - public void Update(Camera cam, float deltaTime) - { - if (isDead) return; - - Debug.WriteLine("pp: " + PressureProtection); - if (PressureProtection==0.0f && - (animController.CurrentHull == null || animController.CurrentHull.LethalPressure >= 100.0f)) - { - Implode(); - return; - } - - if (controlled == this) ControlLocalPlayer(cam); - - Control(cam); - - UpdateSightRange(); - aiTarget.SoundRange = 0.0f; - - if (needsAir) - { - if (animController.HeadInWater) - { - Oxygen -= deltaTime*100.0f / drowningTime; - } - else if (animController.CurrentHull != null) - { - float hullOxygen = animController.CurrentHull.OxygenPercentage; - hullOxygen -= 30.0f; - - Oxygen += deltaTime * 100.0f * (hullOxygen / 500.0f); - - animController.CurrentHull.Oxygen -= Hull.OxygenConsumptionSpeed * deltaTime; - } - PressureProtection -= deltaTime*100.0f; - } - - if (soundTimer > 0) - { - soundTimer -= deltaTime; - } - else - { - PlaySound((aiController == null) ? AIController.AiState.None : aiController.State); - soundTimer = soundInterval; - } - - foreach (Limb limb in animController.limbs) - { - Blood = blood - limb.Bleeding * deltaTime * 0.1f; - } - - if (aiController != null) aiController.Update(deltaTime); - } - - private void UpdateSightRange() - { - aiTarget.SightRange = 0.0f; - - //distance is approximated based on the mass of the character - //(which corresponds to size because all the characters have the same limb density) - foreach (Limb limb in animController.limbs) - { - aiTarget.SightRange += limb.Mass * 1000.0f; - } - //the faster the character is moving, the easier it is to see it - Limb torso = animController.GetLimb(LimbType.Torso); - if (torso !=null) - { - aiTarget.SightRange += torso.LinearVelocity.Length() * 500.0f; - } - } - - public void Draw(SpriteBatch spriteBatch) - { - animController.Draw(spriteBatch); - - if (IsNetworkPlayer) - { - Vector2 pos = new Vector2(Position.X, -Position.Y - 50.0f) - GUI.font.MeasureString(info.name) * 0.5f; - spriteBatch.DrawString(GUI.font, info.name, pos - new Vector2(1.0f, 1.0f), Color.Black); - spriteBatch.DrawString(GUI.font, info.name, pos, Color.White); - } - - //spriteBatch.DrawString(GUI.font, ID.ToString(), ConvertUnits.ToDisplayUnits(animController.limbs[0].Position), Color.White); - //GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y), - // ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y) + - // ConvertUnits.ToDisplayUnits(animController.targetMovement.X, animController.targetMovement.Y), Color.Green); - } - - public void PlaySound(AIController.AiState state) - { - if (sounds == null || sounds.Count()==0) return; - var matchingSoundStates = soundStates.Where(x => x == state).ToList(); - - int selectedSound = Game1.localRandom.Next(matchingSoundStates.Count()); - - int n = 0; - for (int i = 0; i < sounds.Count(); i++) - { - if (soundStates[i] != state) continue; - if (n == selectedSound) - { - sounds[i].Play(1.0f, 2000.0f, - animController.limbs[0].body.FarseerBody); - Debug.WriteLine("playing: " + sounds[i]); - return; - } - n++; - } - } - - public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun) - { - int bloodAmount = 0; - - animController.StunTimer = Math.Max(animController.StunTimer, stun); - - Limb closestLimb = null; - float closestDistance = 0.0f; - foreach (Limb limb in animController.limbs) - { - float distance = Vector2.Distance(position, limb.SimPosition); - if (closestLimb == null || distance < closestDistance) - { - closestLimb = limb; - closestDistance = distance; - } - } - - Vector2 pull = position - closestLimb.SimPosition; - if (pull != Vector2.Zero) pull = Vector2.Normalize(pull); - closestLimb.body.ApplyForce(pull*Math.Min(amount*100.0f, 100.0f)); - - closestLimb.Bleeding += bleedingAmount; - closestLimb.Damage += amount; - bloodAmount = (int)Math.Min((int)(amount*2.0f),20); - //if (closestLimb.Damage>=100.0f) - //{ - // bloodAmount *= 2; - // foreach (var joint in animController.limbJoints) - // { - // if (!(joint.BodyA == closestLimb.body.FarseerBody) && !(joint.BodyB == closestLimb.body.FarseerBody)) continue; - - // joint.Enabled = false; - // break; - // } - //} - - for (int i = 0; i < bloodAmount; i++) - { - Vector2 particleVel = closestLimb.SimPosition-position; - if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel); - - Game1.particleManager.CreateParticle( - closestLimb.SimPosition, - ToolBox.RandomFloat(0.0f, 3.1f), - particleVel * ToolBox.RandomFloat(1.0f,3.0f), - "blood"); - } - - for (int i = 0; i < bloodAmount / 2; i++) - { - Game1.particleManager.CreateParticle(closestLimb.SimPosition, - 0.0f, - Vector2.Zero, "waterblood"); - } - } - - public void Stun() - { - for (int i = 0; i < selectedItems.Length; i++ ) - { - if (selectedItems[i] == null) continue; - selectedItems[i].Drop(); - selectedItems[i] = null; - } - - selectedConstruction = null; - } - - private void Implode() - { - Limb torso= animController.GetLimb(LimbType.Torso); - if (torso == null) torso = animController.GetLimb(LimbType.Head); - - Vector2 centerOfMass = Vector2.Zero; - float totalMass = 0.0f; - foreach (Limb limb in animController.limbs) - { - centerOfMass += limb.Mass * limb.SimPosition; - totalMass += limb.Mass; - } - - centerOfMass /= totalMass; - - foreach (Limb limb in animController.limbs) - { - Vector2 diff = centerOfMass - limb.SimPosition; - if (diff == Vector2.Zero) continue; - limb.body.ApplyLinearImpulse(diff * 10.0f); - limb.Damage = 100.0f; - } - - AmbientSoundManager.PlayDamageSound(DamageSoundType.Implode, 50.0f, torso.body.FarseerBody); - - for (int i = 0; i < 10; i++) - { - Particle p = Game1.particleManager.CreateParticle( - torso.SimPosition + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-0.5f, 0.5f)), - 0.0f, - Vector2.Zero, "waterblood"); - if (p!=null) p.Size *= 2.0f; - - Game1.particleManager.CreateParticle( - torso.SimPosition, - 0.0f, - new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-1.0f,0.5f)), - "bubbles"); - } - - foreach (var joint in animController.limbJoints) - { - joint.LimitEnabled = false; - } - Kill(true); - } - - public void Kill(bool networkMessage = false) - { - if (isDead) return; - - //if the game is run by a client, characters are only killed when the server says so - if (Game1.client != null) - { - if (networkMessage) - { - new NetworkEvent(NetworkEventType.KillCharacter, ID, true); - } - else - { - return; - } - } - - if (Game1.server != null) - { - new NetworkEvent(NetworkEventType.KillCharacter, ID, false); - } - - if (Game1.gameSession.crewManager!=null) - { - Game1.gameSession.crewManager.KillCharacter(this); - } - - isDead = true; - animController.movement = Vector2.Zero; - animController.TargetMovement = Vector2.Zero; - - for (int i = 0; i < selectedItems.Length; i++ ) - { - if (selectedItems[i] != null) selectedItems[i].Drop(this); - } - - - aiTarget.Remove(); - aiTarget = null; - - foreach (Limb limb in animController.limbs) - { - if (limb.pullJoint == null) continue; - limb.pullJoint.Enabled = false; - } - - foreach (RevoluteJoint joint in animController.limbJoints) - { - joint.MotorEnabled = false; - joint.MaxMotorTorque = 0.0f; - } - } - - public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) - { - if (type == NetworkEventType.PickItem) - { - message.Write((int)data); - Debug.WriteLine("pickitem"); - return; - } - else if (type == NetworkEventType.KillCharacter) - { - return; - } - - - //if (type == Networking.NetworkEventType.KeyHit) - //{ - // message.Write(selectKeyHit.Dequeue); - message.Write(actionKeyDown.Dequeue); - message.Write(secondaryKeyDown.Dequeue); - //} - - message.Write(NetTime.Now); - - // Write byte = move direction - message.Write(animController.TargetMovement.X); - message.Write(animController.TargetMovement.Y); - - message.Write(animController.targetDir==Direction.Right); - - message.Write(cursorPosition.X); - message.Write(cursorPosition.Y); - - message.Write(largeUpdateTimer <= 0); - - if (largeUpdateTimer<=0) - { - foreach (Limb limb in animController.limbs) - { - message.Write(limb.body.Position.X); - message.Write(limb.body.Position.Y); - - message.Write(limb.body.LinearVelocity.X); - message.Write(limb.body.LinearVelocity.Y); - - message.Write(limb.body.Rotation); - message.Write(limb.body.AngularVelocity); - } - - message.Write(animController.StunTimer); - - largeUpdateTimer = 5; - } - else - { - Limb torso = animController.GetLimb(LimbType.Torso); - message.Write(torso.body.Position.X); - message.Write(torso.body.Position.Y); - - largeUpdateTimer = (byte)Math.Max(0, largeUpdateTimer-1); - } - - - - if (aiController != null) aiController.FillNetworkData(message); - - } - - public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) - { - if (type == NetworkEventType.PickItem) - { - int itemId = message.ReadInt32(); - Item item = FindEntityByID(itemId) as Item; - if (item != null) - { - Debug.WriteLine("pickitem "+itemId ); - item.Pick(this); - } - else - { - } - - //DebugConsole.ThrowError("pickitem"); - - return; - } - else if (type == NetworkEventType.KillCharacter) - { - Kill(true); - if (Game1.client != null && controlled == this) - { - Game1.client.AddChatMessage("YOU HAVE DIED. Your chat messages will only be visible to other dead players.", ChatMessageType.Dead); - } - return; - } - - //if (type == Networking.NetworkEventType.KeyHit) - //{ - // selectKeyHit.State = message.ReadBoolean(); - - //} - actionKeyDown.State = message.ReadBoolean(); - secondaryKeyDown.State = message.ReadBoolean(); - - double sendingTime = message.ReadDouble(); - - Vector2 targetMovement = Vector2.Zero; - - targetMovement.X = message.ReadFloat(); - targetMovement.Y = message.ReadFloat(); - - animController.isStanding = true; - - bool targetDir = message.ReadBoolean(); - - Vector2 cursorPos = Vector2.Zero; - cursorPos.X = message.ReadFloat(); - cursorPos.Y = message.ReadFloat(); - - if (sendingTime > lastNetworkUpdate) - { - cursorPosition = cursorPos; - - animController.TargetMovement= targetMovement; - animController.targetDir = (targetDir) ? Direction.Right : Direction.Left; - - if (message.ReadBoolean()) - { - foreach (Limb limb in animController.limbs) - { - Vector2 pos = Vector2.Zero; - pos.X = message.ReadFloat(); - pos.Y = message.ReadFloat(); - - Vector2 vel = Vector2.Zero; - vel.X = message.ReadFloat(); - vel.Y = message.ReadFloat(); - - float rotation = message.ReadFloat(); - float angularVel = message.ReadFloat(); - - if (limb.body == null) continue; - - if (vel != Vector2.Zero && vel.Length() > 100.0f) { } - - if (pos != Vector2.Zero && pos.Length() > 100.0f) { } - - limb.body.TargetVelocity = vel; - limb.body.TargetPosition = pos;// +vel * (float)(deltaTime / 60.0); - limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0); - limb.body.TargetAngularVelocity = angularVel; - } - - animController.StunTimer = message.ReadFloat(); - - largeUpdateTimer = 1; - } - else - { - Vector2 pos = Vector2.Zero; - pos.X = message.ReadFloat(); - pos.Y = message.ReadFloat(); - - Limb torso = animController.GetLimb(LimbType.Torso); - torso.body.TargetPosition = pos; - - largeUpdateTimer = 0; - } - - if (aiController != null) aiController.ReadNetworkData(message); - - lastNetworkUpdate = sendingTime; - } - } - - public override void Remove() - { - base.Remove(); - - characterList.Remove(this); - - if (controlled == this) controlled = null; - - if (Game1.client!=null && Game1.client.Character == this) Game1.client.Character = null; - - if (aiTarget != null) - aiTarget.Remove(); - - if (animController!=null) - animController.Remove(); - } - - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics.Joints; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Subsurface.Networking; +using Subsurface.Particles; + +namespace Subsurface +{ + class Character : Entity, IDamageable + { + public static List characterList = new List(); + + public static Queue newCharacterQueue = new Queue(); + + public static bool disableControls; + + //the character that the player is currently controlling + private static Character controlled; + + public static Character Controlled + { + get { return controlled; } + set { controlled = value; } + } + + public readonly bool IsNetworkPlayer; + + private Inventory inventory; + + public double lastNetworkUpdate; + + public byte largeUpdateTimer; + + public readonly Dictionary properties; + + protected Key selectKeyHit; + protected Key actionKeyHit; + protected Key actionKeyDown; + protected Key secondaryKeyHit; + protected Key secondaryKeyDown; + + private Item selectedConstruction; + private Item[] selectedItems; + + public AnimController animController; + private AIController aiController; + + private Vector2 cursorPosition; + + protected bool needsAir; + protected float oxygen; + protected float drowningTime; + + protected Item closestItem; + + protected bool isDead; + + bool isHumanoid; + + //the name of the species (e.q. human) + public readonly string speciesName; + + public CharacterInfo info; + + protected float soundTimer; + protected float soundInterval; + + private float blood; + + private Sound[] sounds; + //which AIstate each sound is for + private AIController.AiState[] soundStates; + + public Inventory Inventory + { + get { return inventory; } + } + + public Vector2 CursorPosition + { + get { return cursorPosition; } + } + + public float SoundRange + { + get { return aiTarget.SoundRange; } + } + + public float SightRange + { + get { return aiTarget.SightRange; } + } + private float pressureProtection; + public float PressureProtection + { + get { return pressureProtection; } + set + { + pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f); + } + } + + public float Oxygen + { + get { return oxygen; } + set + { + oxygen = MathHelper.Clamp(value, 0.0f, 100.0f); + if (oxygen == 0.0f) Kill(); + } + } + + public float Blood + { + get { return blood; } + set + { + blood = MathHelper.Clamp(value, 0.0f, 100.0f); + if (blood == 0.0f) Kill(); + } + } + + public Item[] SelectedItems + { + get { return selectedItems; } + } + + public bool HasSelectedItem(Item item) + { + return selectedItems.Contains(item); + } + + public bool TrySelectItem(Item item) + { + bool rightHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.RightHand); + bool leftHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.LeftHand); + + bool selected = false; + if (rightHand && SelectedItems[0] == null) + { + selectedItems[0] = item; + selected = true; + } + if (leftHand && SelectedItems[1] == null) + { + selectedItems[1] = item; + selected = true; + } + + return selected; + } + + public bool TrySelectItem(Item item, int index) + { + if (selectedItems[index] != null) return false; + + selectedItems[index] = item; + return true; + } + + public void DeselectItem(Item item) + { + for (int i = 0; i < selectedItems.Length; i++) + { + if (selectedItems[i] == item) selectedItems[i] = null; + } + } + + public Item SelectedConstruction + { + get { return selectedConstruction; } + set { selectedConstruction = value; } + } + + public Item ClosestItem + { + get { return closestItem; } + } + + public Key SelectKeyHit + { + get { return selectKeyHit; } + } + + public Key ActionKeyHit + { + get { return actionKeyHit; } + } + + public Key ActionKeyDown + { + get { return actionKeyDown; } + } + + public Key SecondaryKeyHit + { + get { return secondaryKeyHit; } + } + + public Key SecondaryKeyDown + { + get { return secondaryKeyDown; } + } + + public bool IsDead + { + get { return isDead; } + } + + public override Vector2 SimPosition + { + get { return animController.limbs[0].SimPosition; } + } + + public Vector2 Position + { + get { return ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); } + } + + public Character(string file) : this(file, Vector2.Zero, null) + { + } + + public Character(string file, Vector2 position) + : this(file, position, null) + { + } + + public Character(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) + : this(characterInfo.file, position, characterInfo, isNetworkPlayer) + { + } + + public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) + { + selectKeyHit = new Key(false); + actionKeyDown = new Key(true); + actionKeyHit = new Key(false); + secondaryKeyHit = new Key(false); + secondaryKeyDown = new Key(true); + + selectedItems = new Item[2]; + + IsNetworkPlayer = isNetworkPlayer; + + oxygen = 100.0f; + blood = 100.0f; + aiTarget = new AITarget(this); + + properties = ObjectProperty.GetProperties(this); + + XDocument doc = ToolBox.TryLoadXml(file); + if (doc == null) return; + + speciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown"); + + isHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false); + + info = characterInfo ?? new CharacterInfo(file); + + if (isHumanoid) + { + animController = new HumanoidAnimController(this, doc.Root.Element("ragdoll")); + animController.targetDir = Direction.Right; + inventory = new CharacterInventory(10, this); + } + else + { + animController = new FishAnimController(this, doc.Root.Element("ragdoll")); + PressureProtection = 100.0f; + //FishAnimController fishAnim = (FishAnimController)animController; + + aiController = new EnemyAIController(this, file); + } + + foreach (Limb limb in animController.limbs) + { + limb.body.SetTransform(position+limb.SimPosition, 0.0f); + //limb.prevPosition = ConvertUnits.ToDisplayUnits(position); + } + + needsAir = ToolBox.GetAttributeBool(doc.Root, "needsair", false); + drowningTime = ToolBox.GetAttributeFloat(doc.Root, "drowningtime", 10.0f); + + soundInterval = ToolBox.GetAttributeFloat(doc.Root, "soundinterval", 10.0f); + + var xSounds = doc.Root.Elements("sound"); + if (xSounds.Count() > 0) + { + sounds = new Sound[xSounds.Count()]; + soundStates = new AIController.AiState[xSounds.Count()]; + int i = 0; + foreach (XElement xSound in xSounds) + { + sounds[i] = Sound.Load(xSound.Attribute("file").Value); + if (xSound.Attribute("state") == null) + { + soundStates[i] = AIController.AiState.None; + } + else + { + soundStates[i] = (AIController.AiState)Enum.Parse( + typeof(AIController.AiState), xSound.Attribute("state").Value, true); + } + i++; + } + } + + + animController.FindHull(); + + if (this.info.ID >= 0) + { + ID = this.info.ID; + } + + characterList.Add(this); + } + + + /// + /// Control the characte + /// + public void Control(Camera cam, bool forcePick=false) + { + if (isDead) return; + + //find the closest item if selectkey has been hit, or if the character is being + //controlled by the player (in order to highlight it) + //closestItem = null; + if (controlled==this) + { + Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); + closestItem = FindClosestItem(mouseSimPos); + + if (closestItem != null) + { + closestItem.IsHighlighted = true; + if (selectKeyHit.State && closestItem.Pick(this, forcePick)) + { + new NetworkEvent(NetworkEventType.PickItem, ID, true, closestItem.ID); + } + } + } + + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] == null) continue; + if (i == 1 && selectedItems[0] == selectedItems[1]) continue; + + if (actionKeyDown.State) selectedItems[i].Use(this); + if (secondaryKeyDown.State && selectedItems[i] != null) selectedItems[i].SecondaryUse(this); + + } + + if (selectedConstruction != null) + { + if (actionKeyDown.State) selectedConstruction.Use(this); + if (secondaryKeyDown.State) selectedConstruction.SecondaryUse(this); + } + + if (IsNetworkPlayer) + { + selectKeyHit.Reset(); + actionKeyHit.Reset(); + actionKeyDown.Reset(); + secondaryKeyHit.Reset(); + secondaryKeyDown.Reset(); + } + } + + private Item FindClosestItem(Vector2 mouseSimPos) + { + Limb torso = animController.GetLimb(LimbType.Torso); + Vector2 pos = (torso.body.TargetPosition != Vector2.Zero) ? torso.body.TargetPosition : torso.SimPosition; + + return Item.FindPickable(pos, selectedConstruction == null ? mouseSimPos : selectedConstruction.SimPosition, null, selectedItems); + } + + /// + /// Control the character according to player input + /// + public void ControlLocalPlayer(Camera cam, bool moveCam = true) + { + if (isDead) return; + + Limb head = animController.GetLimb(LimbType.Head); + + Lights.LightManager.viewPos = ConvertUnits.ToDisplayUnits(head.SimPosition); + + Vector2 targetMovement = Vector2.Zero; + + if (!disableControls) + { + if (PlayerInput.KeyDown(Keys.W)) targetMovement.Y += 1.0f; + if (PlayerInput.KeyDown(Keys.S)) targetMovement.Y -= 1.0f; + if (PlayerInput.KeyDown(Keys.A)) targetMovement.X -= 1.0f; + if (PlayerInput.KeyDown(Keys.D)) targetMovement.X += 1.0f; + + //the vertical component is only used for falling through platforms and climbing ladders when not in water, + //so the movement can't be normalized or the character would walk slower when pressing down/up + if (animController.InWater) + { + float length = targetMovement.Length(); + if (length > 0.0f) targetMovement = targetMovement / length; + } + + if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) && Math.Sign(targetMovement.X) == Math.Sign(animController.Dir)) + targetMovement *= 3.0f; + + selectKeyHit.SetState(PlayerInput.KeyHit(Keys.E)); + actionKeyHit.SetState(PlayerInput.LeftButtonClicked()); + actionKeyDown.SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed); + secondaryKeyHit.SetState(PlayerInput.RightButtonClicked()); + secondaryKeyDown.SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed); + } + else + { + selectKeyHit.SetState(false); + actionKeyHit.SetState(false); + actionKeyDown.SetState(false); + secondaryKeyHit.SetState(false); + secondaryKeyDown.SetState(false); + } + + animController.TargetMovement = targetMovement; + animController.isStanding = true; + + if (moveCam) + { + cam.TargetPos = ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); + cam.OffsetAmount = 250.0f; + } + + cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition); + Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition); + + if (animController.onGround && + !animController.InWater && + animController.anim != AnimController.Animation.UsingConstruction) + { + if (mouseSimPos.X < head.SimPosition.X-1.0f) + { + animController.targetDir = Direction.Left; + } + else if (mouseSimPos.X > head.SimPosition.X + 1.0f) + { + animController.targetDir = Direction.Right; + } + } + + disableControls = false; + } + + + public static void UpdateAnimAll(float deltaTime) + { + foreach (Character c in characterList) + { + if (c.isDead) continue; + c.animController.UpdateAnim(deltaTime); + } + } + + public static void UpdateAll(Camera cam, float deltaTime) + { + if (newCharacterQueue.Count>0) + { + new Character(newCharacterQueue.Dequeue(), Vector2.Zero); + } + + foreach (Character c in characterList) + { + c.Update(cam, deltaTime); + } + } + + public void Update(Camera cam, float deltaTime) + { + if (isDead) return; + + if (PressureProtection==0.0f && + (animController.CurrentHull == null || animController.CurrentHull.LethalPressure >= 100.0f)) + { + Implode(); + return; + } + + if (controlled == this) ControlLocalPlayer(cam); + + Control(cam); + + UpdateSightRange(); + aiTarget.SoundRange = 0.0f; + + if (needsAir) + { + if (animController.HeadInWater) + { + Oxygen -= deltaTime*100.0f / drowningTime; + } + else if (animController.CurrentHull != null) + { + float hullOxygen = animController.CurrentHull.OxygenPercentage; + hullOxygen -= 30.0f; + + Oxygen += deltaTime * 100.0f * (hullOxygen / 500.0f); + + animController.CurrentHull.Oxygen -= Hull.OxygenConsumptionSpeed * deltaTime; + } + PressureProtection -= deltaTime*100.0f; + } + + if (soundTimer > 0) + { + soundTimer -= deltaTime; + } + else + { + PlaySound((aiController == null) ? AIController.AiState.None : aiController.State); + soundTimer = soundInterval; + } + + foreach (Limb limb in animController.limbs) + { + Blood = blood - limb.Bleeding * deltaTime * 0.1f; + } + + if (aiController != null) aiController.Update(deltaTime); + } + + private void UpdateSightRange() + { + aiTarget.SightRange = 0.0f; + + //distance is approximated based on the mass of the character + //(which corresponds to size because all the characters have the same limb density) + foreach (Limb limb in animController.limbs) + { + aiTarget.SightRange += limb.Mass * 1000.0f; + } + //the faster the character is moving, the easier it is to see it + Limb torso = animController.GetLimb(LimbType.Torso); + if (torso !=null) + { + aiTarget.SightRange += torso.LinearVelocity.Length() * 500.0f; + } + } + + public void Draw(SpriteBatch spriteBatch) + { + animController.Draw(spriteBatch); + + if (IsNetworkPlayer) + { + Vector2 pos = new Vector2(Position.X, -Position.Y - 50.0f) - GUI.font.MeasureString(info.name) * 0.5f; + spriteBatch.DrawString(GUI.font, info.name, pos - new Vector2(1.0f, 1.0f), Color.Black); + spriteBatch.DrawString(GUI.font, info.name, pos, Color.White); + } + + //spriteBatch.DrawString(GUI.font, ID.ToString(), ConvertUnits.ToDisplayUnits(animController.limbs[0].Position), Color.White); + //GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y), + // ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y) + + // ConvertUnits.ToDisplayUnits(animController.targetMovement.X, animController.targetMovement.Y), Color.Green); + } + + public void PlaySound(AIController.AiState state) + { + if (sounds == null || sounds.Count()==0) return; + var matchingSoundStates = soundStates.Where(x => x == state).ToList(); + + int selectedSound = Game1.localRandom.Next(matchingSoundStates.Count()); + + int n = 0; + for (int i = 0; i < sounds.Count(); i++) + { + if (soundStates[i] != state) continue; + if (n == selectedSound) + { + sounds[i].Play(1.0f, 2000.0f, + animController.limbs[0].body.FarseerBody); + Debug.WriteLine("playing: " + sounds[i]); + return; + } + n++; + } + } + + public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun) + { + int bloodAmount = 0; + + animController.StunTimer = Math.Max(animController.StunTimer, stun); + + Limb closestLimb = null; + float closestDistance = 0.0f; + foreach (Limb limb in animController.limbs) + { + float distance = Vector2.Distance(position, limb.SimPosition); + if (closestLimb == null || distance < closestDistance) + { + closestLimb = limb; + closestDistance = distance; + } + } + + Vector2 pull = position - closestLimb.SimPosition; + if (pull != Vector2.Zero) pull = Vector2.Normalize(pull); + closestLimb.body.ApplyForce(pull*Math.Min(amount*100.0f, 100.0f)); + + closestLimb.Bleeding += bleedingAmount; + closestLimb.Damage += amount; + bloodAmount = (int)Math.Min((int)(amount*2.0f),20); + //if (closestLimb.Damage>=100.0f) + //{ + // bloodAmount *= 2; + // foreach (var joint in animController.limbJoints) + // { + // if (!(joint.BodyA == closestLimb.body.FarseerBody) && !(joint.BodyB == closestLimb.body.FarseerBody)) continue; + + // joint.Enabled = false; + // break; + // } + //} + + for (int i = 0; i < bloodAmount; i++) + { + Vector2 particleVel = closestLimb.SimPosition-position; + if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel); + + Game1.particleManager.CreateParticle( + closestLimb.SimPosition, + ToolBox.RandomFloat(0.0f, 3.1f), + particleVel * ToolBox.RandomFloat(1.0f,3.0f), + "blood"); + } + + for (int i = 0; i < bloodAmount / 2; i++) + { + Game1.particleManager.CreateParticle(closestLimb.SimPosition, + 0.0f, + Vector2.Zero, "waterblood"); + } + } + + public void Stun() + { + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] == null) continue; + selectedItems[i].Drop(); + selectedItems[i] = null; + } + + selectedConstruction = null; + } + + private void Implode() + { + Limb torso= animController.GetLimb(LimbType.Torso); + if (torso == null) torso = animController.GetLimb(LimbType.Head); + + Vector2 centerOfMass = Vector2.Zero; + float totalMass = 0.0f; + foreach (Limb limb in animController.limbs) + { + centerOfMass += limb.Mass * limb.SimPosition; + totalMass += limb.Mass; + } + + centerOfMass /= totalMass; + + foreach (Limb limb in animController.limbs) + { + Vector2 diff = centerOfMass - limb.SimPosition; + if (diff == Vector2.Zero) continue; + limb.body.ApplyLinearImpulse(diff * 10.0f); + limb.Damage = 100.0f; + } + + AmbientSoundManager.PlayDamageSound(DamageSoundType.Implode, 50.0f, torso.body.FarseerBody); + + for (int i = 0; i < 10; i++) + { + Particle p = Game1.particleManager.CreateParticle( + torso.SimPosition + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-0.5f, 0.5f)), + 0.0f, + Vector2.Zero, "waterblood"); + if (p!=null) p.Size *= 2.0f; + + Game1.particleManager.CreateParticle( + torso.SimPosition, + 0.0f, + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-1.0f,0.5f)), + "bubbles"); + } + + foreach (var joint in animController.limbJoints) + { + joint.LimitEnabled = false; + } + Kill(true); + } + + public void Kill(bool networkMessage = false) + { + if (isDead) return; + + //if the game is run by a client, characters are only killed when the server says so + if (Game1.client != null) + { + if (networkMessage) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, true); + } + else + { + return; + } + } + + if (Game1.server != null) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, false); + } + + if (Game1.gameSession.crewManager!=null) + { + Game1.gameSession.crewManager.KillCharacter(this); + } + + isDead = true; + animController.movement = Vector2.Zero; + animController.TargetMovement = Vector2.Zero; + + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] != null) selectedItems[i].Drop(this); + } + + + aiTarget.Remove(); + aiTarget = null; + + foreach (Limb limb in animController.limbs) + { + if (limb.pullJoint == null) continue; + limb.pullJoint.Enabled = false; + } + + foreach (RevoluteJoint joint in animController.limbJoints) + { + joint.MotorEnabled = false; + joint.MaxMotorTorque = 0.0f; + } + } + + public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + if (type == NetworkEventType.PickItem) + { + message.Write((int)data); + Debug.WriteLine("pickitem"); + return; + } + else if (type == NetworkEventType.KillCharacter) + { + return; + } + + + //if (type == Networking.NetworkEventType.KeyHit) + //{ + // message.Write(selectKeyHit.Dequeue); + message.Write(actionKeyDown.Dequeue); + message.Write(secondaryKeyDown.Dequeue); + //} + + message.Write(NetTime.Now); + + // Write byte = move direction + message.Write(animController.TargetMovement.X); + message.Write(animController.TargetMovement.Y); + + message.Write(animController.targetDir==Direction.Right); + + message.Write(cursorPosition.X); + message.Write(cursorPosition.Y); + + message.Write(largeUpdateTimer <= 0); + + if (largeUpdateTimer<=0) + { + foreach (Limb limb in animController.limbs) + { + message.Write(limb.body.Position.X); + message.Write(limb.body.Position.Y); + + message.Write(limb.body.LinearVelocity.X); + message.Write(limb.body.LinearVelocity.Y); + + message.Write(limb.body.Rotation); + message.Write(limb.body.AngularVelocity); + } + + message.Write(animController.StunTimer); + + largeUpdateTimer = 5; + } + else + { + Limb torso = animController.GetLimb(LimbType.Torso); + message.Write(torso.body.Position.X); + message.Write(torso.body.Position.Y); + + largeUpdateTimer = (byte)Math.Max(0, largeUpdateTimer-1); + } + + + + if (aiController != null) aiController.FillNetworkData(message); + + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + if (type == NetworkEventType.PickItem) + { + int itemId = message.ReadInt32(); + Item item = FindEntityByID(itemId) as Item; + if (item != null) + { + Debug.WriteLine("pickitem "+itemId ); + item.Pick(this); + } + else + { + } + + //DebugConsole.ThrowError("pickitem"); + + return; + } + else if (type == NetworkEventType.KillCharacter) + { + Kill(true); + if (Game1.client != null && controlled == this) + { + Game1.client.AddChatMessage("YOU HAVE DIED. Your chat messages will only be visible to other dead players.", ChatMessageType.Dead); + } + return; + } + + //if (type == Networking.NetworkEventType.KeyHit) + //{ + // selectKeyHit.State = message.ReadBoolean(); + + //} + actionKeyDown.State = message.ReadBoolean(); + secondaryKeyDown.State = message.ReadBoolean(); + + double sendingTime = message.ReadDouble(); + + Vector2 targetMovement = Vector2.Zero; + + targetMovement.X = message.ReadFloat(); + targetMovement.Y = message.ReadFloat(); + + animController.isStanding = true; + + bool targetDir = message.ReadBoolean(); + + Vector2 cursorPos = Vector2.Zero; + cursorPos.X = message.ReadFloat(); + cursorPos.Y = message.ReadFloat(); + + if (sendingTime > lastNetworkUpdate) + { + cursorPosition = cursorPos; + + animController.TargetMovement= targetMovement; + animController.targetDir = (targetDir) ? Direction.Right : Direction.Left; + + if (message.ReadBoolean()) + { + foreach (Limb limb in animController.limbs) + { + Vector2 pos = Vector2.Zero; + pos.X = message.ReadFloat(); + pos.Y = message.ReadFloat(); + + Vector2 vel = Vector2.Zero; + vel.X = message.ReadFloat(); + vel.Y = message.ReadFloat(); + + float rotation = message.ReadFloat(); + float angularVel = message.ReadFloat(); + + if (limb.body == null) continue; + + if (vel != Vector2.Zero && vel.Length() > 100.0f) { } + + if (pos != Vector2.Zero && pos.Length() > 100.0f) { } + + limb.body.TargetVelocity = vel; + limb.body.TargetPosition = pos;// +vel * (float)(deltaTime / 60.0); + limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0); + limb.body.TargetAngularVelocity = angularVel; + } + + animController.StunTimer = message.ReadFloat(); + + largeUpdateTimer = 1; + } + else + { + Vector2 pos = Vector2.Zero; + pos.X = message.ReadFloat(); + pos.Y = message.ReadFloat(); + + Limb torso = animController.GetLimb(LimbType.Torso); + torso.body.TargetPosition = pos; + + largeUpdateTimer = 0; + } + + if (aiController != null) aiController.ReadNetworkData(message); + + lastNetworkUpdate = sendingTime; + } + } + + public override void Remove() + { + base.Remove(); + + characterList.Remove(this); + + if (controlled == this) controlled = null; + + if (Game1.client!=null && Game1.client.Character == this) Game1.client.Character = null; + + if (aiTarget != null) + aiTarget.Remove(); + + if (animController!=null) + animController.Remove(); + } + + } +} diff --git a/Subsurface/Content/Items/Door/doors.xml b/Subsurface/Content/Items/Door/doors.xml index 0eedccff0..4a4387447 100644 --- a/Subsurface/Content/Items/Door/doors.xml +++ b/Subsurface/Content/Items/Door/doors.xml @@ -1,41 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Particles/prefabs.xml b/Subsurface/Content/Particles/prefabs.xml index 5f23d673d..8c9e90b53 100644 --- a/Subsurface/Content/Particles/prefabs.xml +++ b/Subsurface/Content/Particles/prefabs.xml @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/Subsurface/Items/Components/Door.cs b/Subsurface/Items/Components/Door.cs index adbd339c2..a0655ea8d 100644 --- a/Subsurface/Items/Components/Door.cs +++ b/Subsurface/Items/Components/Door.cs @@ -1,267 +1,304 @@ -using System; -using System.IO; -using System.Xml.Linq; -using FarseerPhysics; -using FarseerPhysics.Dynamics; -using FarseerPhysics.Factories; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Subsurface.Lights; - -namespace Subsurface.Items.Components -{ - class Door : ItemComponent - { - Gap linkedGap; - - Rectangle window; - - ConvexHull convexHull; - ConvexHull convexHull2; - - Gap LinkedGap - { - get - { - if (linkedGap != null) return linkedGap; - foreach (MapEntity e in item.linkedTo) - { - linkedGap = e as Gap; - if (linkedGap != null) return linkedGap; - } - linkedGap = new Gap(item.Rect); - linkedGap.Open = openState; - item.linkedTo.Add(linkedGap); - return linkedGap; - } - } - - bool isOpen; - float openState; - - [HasDefaultValue("0.0,0.0,0.0,0.0", false)] - public string Window - { - get { return ToolBox.Vector4ToString(new Vector4(window.X, window.Y, window.Width, window.Height)); } - set - { - Vector4 vector = ToolBox.ParseToVector4(value); - if (vector.Z!=0.0f || vector.W !=0.0f) - { - window = new Rectangle((int)vector.X, (int)vector.Y, (int)vector.Z, (int)vector.W); - } - } - } - - [Editable, HasDefaultValue(false, true)] - public bool IsOpen - { - get { return isOpen; } - set - { - isOpen = value; - openState = (isOpen) ? 1.0f : 0.0f; - } - } - - public float OpenState - { - get { return openState; } - set - { - if (openState == value) return; - openState = MathHelper.Clamp(value, 0.0f, 1.0f); - - - - if (window==null) - { - Rectangle rect = item.Rect; - rect.Height = (int)(rect.Height * (1.0f - openState)); - - } - else - { - //Rectangle rect = item.Rect; - //rect.Height = (int)(rect.Height * (1.0f - openState)); - - Rectangle rect1 = item.Rect; - rect1.Height = -window.Y; - - rect1.Y += (int)(item.Rect.Height * openState); - rect1.Height = Math.Max(rect1.Height - (rect1.Y - item.Rect.Y), 0); - rect1.Y = Math.Min(item.Rect.Y, rect1.Y); - - Rectangle rect2 = item.Rect; - rect2.Y = rect2.Y + window.Y - window.Height; - - rect2.Y += (int)(item.Rect.Height * openState); - //rect2.Height = Math.Max(rect2.Height - (rect2.Y - item.Rect.Y), 0); - rect2.Y = Math.Min(item.Rect.Y, rect2.Y); - rect2.Height = rect2.Y - (item.Rect.Y - (int)(item.Rect.Height * (1.0f - openState))); - - convexHull.SetVertices(GetConvexHullCorners(rect1)); - if (convexHull2!=null) convexHull2.SetVertices(GetConvexHullCorners(rect2)); - } - - } - } - - PhysicsBody body; - - Sprite doorSprite; - - public Door(Item item, XElement element) - : base(item, element) - { - //Vector2 position = new Vector2(newRect.X, newRect.Y); - - // isOpen = false; - - body = new PhysicsBody(BodyFactory.CreateRectangle(Game1.world, - ConvertUnits.ToSimUnits(Math.Max(item.Rect.Width, 1)), - ConvertUnits.ToSimUnits(Math.Max(item.Rect.Height, 1)), - 1.5f)); - - body.BodyType = BodyType.Static; - body.SetTransform( - ConvertUnits.ToSimUnits(new Vector2(item.Rect.X + item.Rect.Width / 2, item.Rect.Y - item.Rect.Height / 2)), - 0.0f); - body.Friction = 0.5f; - - - //string spritePath = Path.GetDirectoryName(item.Prefab.ConfigFile) + "\\"+ ToolBox.GetAttributeString(element, "sprite", ""); - - foreach (XElement subElement in element.Elements()) - { - if (subElement.Name.ToString().ToLower() != "sprite") continue; - doorSprite = new Sprite(subElement, Path.GetDirectoryName(item.Prefab.ConfigFile)); - break; - } - - isActive = true; - - Vector2[] corners = GetConvexHullCorners(item.Rect); - - convexHull = new ConvexHull(corners, Color.Black); - if (window!=null) convexHull2 = new ConvexHull(corners, Color.Black); - - OpenState = openState; - //powerConsumption = -100.0f; - - //LinkedGap.Open = openState; - } - - private Vector2[] GetConvexHullCorners(Rectangle rect) - { - Vector2[] corners = new Vector2[4]; - corners[0] = new Vector2(rect.X, rect.Y - rect.Height); - corners[1] = new Vector2(rect.X, rect.Y); - corners[2] = new Vector2(rect.Right, rect.Y); - corners[3] = new Vector2(rect.Right, rect.Y - rect.Height); - - return corners; - } - - public override void Move(Vector2 amount) - { - base.Move(amount); - - body.SetTransform(body.Position + ConvertUnits.ToSimUnits(amount), 0.0f); - - convexHull.Move(amount); - } - - - public override bool Pick(Character picker) - { - isActive = true; - isOpen = !isOpen; - - return true; - } - - public override void Update(float deltaTime, Camera cam) - { - OpenState += deltaTime * ((isOpen) ? 3.0f : -3.0f); - - item.SendSignal((isOpen) ? "1" : "0", "state_out"); - } - - public override void Draw(SpriteBatch spriteBatch) - { - - LinkedGap.Open = openState; - - Color color = (item.IsSelected) ? Color.Green : Color.White; - - //prefab.sprite.Draw(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color); - - if (openState == 1.0f) - { - body.Enabled = false; - convexHull.Enabled = false; - } - else - { - spriteBatch.Draw(doorSprite.Texture, new Vector2(item.Rect.Center.X, -item.Rect.Y), - new Rectangle(0, (int)(doorSprite.size.Y * openState), - (int)doorSprite.size.X, (int)(doorSprite.size.Y * (1.0f - openState))), - color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth); - - convexHull.Enabled = true; - - if (openState == 0.0f) - { - body.Enabled = true; - } - else - { - - - //push characters out of the doorway when the door is closing/opening - Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y)); - Vector2 simSize = ConvertUnits.ToSimUnits(new Vector2(item.Rect.Width, - item.Rect.Height * (1.0f - openState))); - - foreach (Character c in Character.characterList) - { - int dir = Math.Sign(c.animController.limbs[0].SimPosition.X - simPos.X); - foreach (Limb l in c.animController.limbs) - { - if (l.SimPosition.Y < simPos.Y || l.SimPosition.Y > simPos.Y - simSize.Y) continue; - if (Math.Abs(l.SimPosition.X - simPos.X) > simSize.X * 2.0f) continue; - - l.body.ApplyForce(new Vector2(dir * 10.0f, 0.0f)); - } - } - } - } - } - - public override void Remove() - { - base.Remove(); - - Game1.world.RemoveBody(body.FarseerBody); - - doorSprite.Remove(); - - convexHull.Remove(); - if (convexHull2 != null) convexHull2.Remove(); - } - - public override void ReceiveSignal(string signal, Connection connection, Item sender) - { - isActive = true; - if (connection.name=="toggle") - { - isOpen = !isOpen; - } - else if (connection.name == "set_state") - { - isOpen = (signal!="0"); - } - } - } -} +using System; +using System.IO; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Lights; + +namespace Subsurface.Items.Components +{ + class Door : ItemComponent + { + Gap linkedGap; + + Rectangle window; + + ConvexHull convexHull; + ConvexHull convexHull2; + + Gap LinkedGap + { + get + { + if (linkedGap != null) return linkedGap; + foreach (MapEntity e in item.linkedTo) + { + linkedGap = e as Gap; + if (linkedGap != null) return linkedGap; + } + linkedGap = new Gap(item.Rect); + linkedGap.Open = openState; + item.linkedTo.Add(linkedGap); + return linkedGap; + } + } + + bool isOpen; + float openState; + + [HasDefaultValue("0.0,0.0,0.0,0.0", false)] + public string Window + { + get { return ToolBox.Vector4ToString(new Vector4(window.X, window.Y, window.Width, window.Height)); } + set + { + Vector4 vector = ToolBox.ParseToVector4(value); + if (vector.Z!=0.0f || vector.W !=0.0f) + { + window = new Rectangle((int)vector.X, (int)vector.Y, (int)vector.Z, (int)vector.W); + } + } + } + + [Editable, HasDefaultValue(false, true)] + public bool IsOpen + { + get { return isOpen; } + set + { + isOpen = value; + openState = (isOpen) ? 1.0f : 0.0f; + } + } + + private Rectangle doorRect; + + + public float OpenState + { + get { return openState; } + set + { + if (openState == value) return; + openState = MathHelper.Clamp(value, 0.0f, 1.0f); + + + + if (window==null) + { + Rectangle rect = item.Rect; + rect.Height = (int)(rect.Height * (1.0f - openState)); + + } + else + { + //Rectangle rect = item.Rect; + //rect.Height = (int)(rect.Height * (1.0f - openState)); + + Rectangle rect1 = doorRect; + rect1.Height = -window.Y; + + rect1.Y += (int)(doorRect.Height * openState); + rect1.Height = Math.Max(rect1.Height - (rect1.Y - doorRect.Y), 0); + rect1.Y = Math.Min(doorRect.Y, rect1.Y); + + if (rect1.Height == 0) + { + convexHull.Enabled = false; + } + else + { + convexHull.SetVertices(GetConvexHullCorners(rect1)); + } + + if (convexHull2 != null) + { + Rectangle rect2 = doorRect; + rect2.Y = rect2.Y + window.Y - window.Height; + + rect2.Y += (int)(doorRect.Height * openState); + rect2.Y = Math.Min(doorRect.Y, rect2.Y); + rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState))); + convexHull2.SetVertices(GetConvexHullCorners(rect2)); + + if (rect2.Height == 0) + { + convexHull2.Enabled = false; + } + else + { + convexHull2.SetVertices(GetConvexHullCorners(rect2)); + } + } + + + + + } + + } + } + + PhysicsBody body; + + Sprite doorSprite; + + public Door(Item item, XElement element) + : base(item, element) + { + //Vector2 position = new Vector2(newRect.X, newRect.Y); + + // isOpen = false; + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "sprite") continue; + doorSprite = new Sprite(subElement, Path.GetDirectoryName(item.Prefab.ConfigFile)); + break; + } + + doorRect = new Rectangle( + item.Rect.Center.X - (int)(doorSprite.size.X / 2), + item.Rect.Y, + (int)doorSprite.size.X, + (int)doorSprite.size.Y); + + + body = new PhysicsBody(BodyFactory.CreateRectangle(Game1.world, + ConvertUnits.ToSimUnits(Math.Max(doorRect.Width, 1)), + ConvertUnits.ToSimUnits(Math.Max(doorRect.Height, 1)), + 1.5f)); + + body.BodyType = BodyType.Static; + body.SetTransform( + ConvertUnits.ToSimUnits(new Vector2(doorRect.Center.X, doorRect.Y - doorRect.Height / 2)), + 0.0f); + body.Friction = 0.5f; + + + //string spritePath = Path.GetDirectoryName(item.Prefab.ConfigFile) + "\\"+ ToolBox.GetAttributeString(element, "sprite", ""); + + + + isActive = true; + + + Vector2[] corners = GetConvexHullCorners(doorRect); + + convexHull = new ConvexHull(corners, Color.Black); + if (window!=null) convexHull2 = new ConvexHull(corners, Color.Black); + + OpenState = openState; + //powerConsumption = -100.0f; + + //LinkedGap.Open = openState; + } + + private Vector2[] GetConvexHullCorners(Rectangle rect) + { + Vector2[] corners = new Vector2[4]; + corners[0] = new Vector2(rect.X, rect.Y - rect.Height); + corners[1] = new Vector2(rect.X, rect.Y); + corners[2] = new Vector2(rect.Right, rect.Y); + corners[3] = new Vector2(rect.Right, rect.Y - rect.Height); + + return corners; + } + + public override void Move(Vector2 amount) + { + base.Move(amount); + + linkedGap.Move(amount); + + body.SetTransform(body.Position + ConvertUnits.ToSimUnits(amount), 0.0f); + + convexHull.Move(amount); + if (convexHull2 != null) convexHull2.Move(amount); + } + + + public override bool Pick(Character picker) + { + isActive = true; + isOpen = !isOpen; + + return true; + } + + public override void Update(float deltaTime, Camera cam) + { + OpenState += deltaTime * ((isOpen) ? 3.0f : -3.0f); + + item.SendSignal((isOpen) ? "1" : "0", "state_out"); + } + + public override void Draw(SpriteBatch spriteBatch) + { + + LinkedGap.Open = openState; + + Color color = (item.IsSelected) ? Color.Green : Color.White; + + //prefab.sprite.Draw(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color); + + if (openState == 1.0f) + { + body.Enabled = false; + convexHull.Enabled = false; + } + else + { + spriteBatch.Draw(doorSprite.Texture, new Vector2(item.Rect.Center.X, -item.Rect.Y), + new Rectangle(doorSprite.SourceRect.X, (int)(doorSprite.size.Y * openState), + (int)doorSprite.size.X, (int)(doorSprite.size.Y * (1.0f - openState))), + color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth); + + convexHull.Enabled = true; + + if (openState == 0.0f) + { + body.Enabled = true; + } + else + { + //push characters out of the doorway when the door is closing/opening + Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y)); + Vector2 simSize = ConvertUnits.ToSimUnits(new Vector2(item.Rect.Width, + item.Rect.Height * (1.0f - openState))); + + foreach (Character c in Character.characterList) + { + int dir = Math.Sign(c.animController.limbs[0].SimPosition.X - simPos.X); + foreach (Limb l in c.animController.limbs) + { + if (l.SimPosition.Y < simPos.Y || l.SimPosition.Y > simPos.Y - simSize.Y) continue; + if (Math.Abs(l.SimPosition.X - simPos.X) > simSize.X * 2.0f) continue; + + l.body.ApplyForce(new Vector2(dir * 10.0f, 0.0f)); + } + } + } + } + } + + public override void Remove() + { + base.Remove(); + + Game1.world.RemoveBody(body.FarseerBody); + + linkedGap.Remove(); + + doorSprite.Remove(); + + convexHull.Remove(); + if (convexHull2 != null) convexHull2.Remove(); + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + isActive = true; + if (connection.name=="toggle") + { + isOpen = !isOpen; + } + else if (connection.name == "set_state") + { + isOpen = (signal!="0"); + } + } + } +} diff --git a/Subsurface/Items/ItemPrefab.cs b/Subsurface/Items/ItemPrefab.cs index ed5e30a9b..890932c7d 100644 --- a/Subsurface/Items/ItemPrefab.cs +++ b/Subsurface/Items/ItemPrefab.cs @@ -1,219 +1,218 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml.Linq; -using FarseerPhysics; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; - -namespace Subsurface -{ - class ItemPrefab : MapEntityPrefab - { - static string contentFolder = "Content/Items/"; - - string configFile; - - //should the camera focus on the construction when selected - protected bool focusOnSelected; - //the amount of "camera offset" when selecting the construction - protected float offsetOnSelected; - //default size - protected Vector2 size; - - //how close the character has to be to the item to pick it up - float pickDistance; - - - public List sounds; - - //an area next to the construction - //the construction can be Activated() by a character inside the area - public List triggers; - - public string ConfigFile - { - get { return configFile; } - } - - public float PickDistance - { - get { return pickDistance; } - } - - - public override bool IsLinkable - { - get { return isLinkable; } - } - - public bool FocusOnSelected - { - get { return focusOnSelected; } - } - - public float OffsetOnSelected - { - get { return offsetOnSelected; } - } - - public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) - { - Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); - position = cam.ScreenToWorld(position); - - if (!resizeHorizontal && !resizeVertical) - { - if (PlayerInput.LeftButtonClicked()) - { - new Item(new Rectangle((int)position.X, (int)position.Y, (int)sprite.size.X, (int)sprite.size.Y), this); - //constructor.Invoke(lobject); - - placePosition = Vector2.Zero; - - selected = null; - return; - } - - sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f)); - } - else - { - Vector2 placeSize = size; - - if (placePosition == Vector2.Zero) - { - if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) - placePosition = position; - } - else - { - if (resizeHorizontal) - placeSize.X = Math.Max(position.X - placePosition.X, size.X); - if (resizeVertical) - placeSize.Y = Math.Max(placePosition.Y - position.Y, size.Y); - - if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released) - { - new Item(new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y), this); - - selected = null; - return; - } - - position = placePosition; - } - - sprite.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, Color.White); - } - - if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null; - - } - - public static void LoadAll() - { - string[] files = Directory.GetFiles(contentFolder, "*.xml", SearchOption.AllDirectories); - - foreach (string filePath in files) - { - XDocument doc = ToolBox.TryLoadXml(filePath); - if (doc == null) return; - - if (doc.Root.Name.ToString().ToLower() == "item") - { - new ItemPrefab(doc.Root, filePath); - } - else - { - foreach (XElement element in doc.Root.Elements()) - { - if (element.Name.ToString().ToLower() != "item") continue; - - new ItemPrefab(element, filePath); - } - } - } - } - - public ItemPrefab (XElement element, string filePath) - { - - configFile = filePath; - - name = ToolBox.GetAttributeString(element, "name", ""); - if (name == "") DebugConsole.ThrowError("Unnamed item in "+filePath+"!"); - - //if (element.Attribute("sprite") != null) - //{ - // sprite = new Sprite(Path.GetDirectoryName(filePath) + "/" + element.Attribute("sprite").Value, new Vector2(0.5f, 0.5f)); - // sprite.Depth = 0.5f; - //} - - //var initableProperties = GetProperties(); - //foreach (ObjectProperty initableProperty in initableProperties) - //{ - // object value = ToolBox.GetAttributeObject(element, initableProperty.Name.ToLower()); - // if (value == null) - // { - // foreach (var ini in initableProperty.Attributes.OfType()) - // { - // value = ini.defaultValue; - // break; - // } - // } - - // initableProperty.TrySetValue(value); - //} - - pickDistance = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f)); - - isLinkable = ToolBox.GetAttributeBool(element, "linkable", false); - - resizeHorizontal = ToolBox.GetAttributeBool(element, "resizehorizontal", false); - resizeVertical = ToolBox.GetAttributeBool(element, "resizevertical", false); - - focusOnSelected = ToolBox.GetAttributeBool(element, "focusonselected", false); - - offsetOnSelected = ToolBox.GetAttributeFloat(element, "offsetonselected", 0.0f); - - triggers = new List(); - foreach (XElement subElement in element.Elements()) - { - switch (subElement.Name.ToString().ToLower()) - { - case "sprite": - sprite = new Sprite(subElement, Path.GetDirectoryName(filePath) + "/"); - size = sprite.size; - break; - case "trigger": - Rectangle trigger = new Rectangle(0, 0, 10,10); - - trigger.X = ToolBox.GetAttributeInt(subElement, "x", 0); - trigger.Y = ToolBox.GetAttributeInt(subElement, "y", 0); - - trigger.Width = ToolBox.GetAttributeInt(subElement, "width", 0); - trigger.Height = ToolBox.GetAttributeInt(subElement, "height", 0); - - triggers.Add(trigger); - - break; - } - } - - sounds = new List(); - var soundElements = element.Descendants("Sound"); - foreach (XElement soundElement in soundElements) - { - string soundPath = ToolBox.GetAttributeString(soundElement, "path", ""); - if (soundPath == "") continue; - - Sound sound = Sound.Load(soundPath); - if (sound != null) sounds.Add(sound); - } - - list.Add(this); - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + class ItemPrefab : MapEntityPrefab + { + static string contentFolder = "Content/Items/"; + + string configFile; + + //should the camera focus on the construction when selected + protected bool focusOnSelected; + //the amount of "camera offset" when selecting the construction + protected float offsetOnSelected; + //default size + protected Vector2 size; + + //how close the character has to be to the item to pick it up + float pickDistance; + + + public List sounds; + + //an area next to the construction + //the construction can be Activated() by a character inside the area + public List triggers; + + public string ConfigFile + { + get { return configFile; } + } + + public float PickDistance + { + get { return pickDistance; } + } + + + public override bool IsLinkable + { + get { return isLinkable; } + } + + public bool FocusOnSelected + { + get { return focusOnSelected; } + } + + public float OffsetOnSelected + { + get { return offsetOnSelected; } + } + + public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) + { + Vector2 position = Map.MouseToWorldGrid(cam); + + if (!resizeHorizontal && !resizeVertical) + { + if (PlayerInput.LeftButtonClicked()) + { + new Item(new Rectangle((int)position.X, (int)position.Y, (int)sprite.size.X, (int)sprite.size.Y), this); + //constructor.Invoke(lobject); + + placePosition = Vector2.Zero; + + selected = null; + return; + } + + sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f)); + } + else + { + Vector2 placeSize = size; + + if (placePosition == Vector2.Zero) + { + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + placePosition = position; + } + else + { + if (resizeHorizontal) + placeSize.X = Math.Max(position.X - placePosition.X, size.X); + if (resizeVertical) + placeSize.Y = Math.Max(placePosition.Y - position.Y, size.Y); + + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released) + { + new Item(new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y), this); + + selected = null; + return; + } + + position = placePosition; + } + + sprite.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, Color.White); + } + + if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null; + + } + + public static void LoadAll() + { + string[] files = Directory.GetFiles(contentFolder, "*.xml", SearchOption.AllDirectories); + + foreach (string filePath in files) + { + XDocument doc = ToolBox.TryLoadXml(filePath); + if (doc == null) return; + + if (doc.Root.Name.ToString().ToLower() == "item") + { + new ItemPrefab(doc.Root, filePath); + } + else + { + foreach (XElement element in doc.Root.Elements()) + { + if (element.Name.ToString().ToLower() != "item") continue; + + new ItemPrefab(element, filePath); + } + } + } + } + + public ItemPrefab (XElement element, string filePath) + { + + configFile = filePath; + + name = ToolBox.GetAttributeString(element, "name", ""); + if (name == "") DebugConsole.ThrowError("Unnamed item in "+filePath+"!"); + + //if (element.Attribute("sprite") != null) + //{ + // sprite = new Sprite(Path.GetDirectoryName(filePath) + "/" + element.Attribute("sprite").Value, new Vector2(0.5f, 0.5f)); + // sprite.Depth = 0.5f; + //} + + //var initableProperties = GetProperties(); + //foreach (ObjectProperty initableProperty in initableProperties) + //{ + // object value = ToolBox.GetAttributeObject(element, initableProperty.Name.ToLower()); + // if (value == null) + // { + // foreach (var ini in initableProperty.Attributes.OfType()) + // { + // value = ini.defaultValue; + // break; + // } + // } + + // initableProperty.TrySetValue(value); + //} + + pickDistance = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f)); + + isLinkable = ToolBox.GetAttributeBool(element, "linkable", false); + + resizeHorizontal = ToolBox.GetAttributeBool(element, "resizehorizontal", false); + resizeVertical = ToolBox.GetAttributeBool(element, "resizevertical", false); + + focusOnSelected = ToolBox.GetAttributeBool(element, "focusonselected", false); + + offsetOnSelected = ToolBox.GetAttributeFloat(element, "offsetonselected", 0.0f); + + triggers = new List(); + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "sprite": + sprite = new Sprite(subElement, Path.GetDirectoryName(filePath) + "/"); + size = sprite.size; + break; + case "trigger": + Rectangle trigger = new Rectangle(0, 0, 10,10); + + trigger.X = ToolBox.GetAttributeInt(subElement, "x", 0); + trigger.Y = ToolBox.GetAttributeInt(subElement, "y", 0); + + trigger.Width = ToolBox.GetAttributeInt(subElement, "width", 0); + trigger.Height = ToolBox.GetAttributeInt(subElement, "height", 0); + + triggers.Add(trigger); + + break; + } + } + + sounds = new List(); + var soundElements = element.Descendants("Sound"); + foreach (XElement soundElement in soundElements) + { + string soundPath = ToolBox.GetAttributeString(soundElement, "path", ""); + if (soundPath == "") continue; + + Sound sound = Sound.Load(soundPath); + if (sound != null) sounds.Add(sound); + } + + list.Add(this); + } + } +} diff --git a/Subsurface/Map/Gap.cs b/Subsurface/Map/Gap.cs index c19975db3..86d2433ee 100644 --- a/Subsurface/Map/Gap.cs +++ b/Subsurface/Map/Gap.cs @@ -1,572 +1,578 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; -using FarseerPhysics; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using System.Collections.ObjectModel; - -namespace Subsurface -{ - class Gap : MapEntity - { - public bool isHorizontal; - - //private Sound waterSound; - - //a value between 0.0f-1.0f (0.0 = closed, 1.0f = open) - float open; - - //the forces of the water flow which are exerted on physics bodies - Vector2 flowForce; - - Hull flowTargetHull; - - float higherSurface; - float lowerSurface; - - private int soundIndex; - - float soundVolume; - - public float Open - { - get { return open; } - set { open = MathHelper.Clamp(value, 0.0f, 1.0f); } - } - - public Vector2 FlowForce - { - get { return flowForce*soundVolume; } - } - - public Hull FlowTargetHull - { - get { return flowTargetHull; } - } - - public Gap(Rectangle newRect) - { - rect = newRect; - linkedTo = new ObservableCollection(); - - //waterSound = new Sound("waterstream", 0.0f); - - flowForce = Vector2.Zero; - - isHorizontal = (rect.Width < rect.Height); - - open = 1.0f; - - FindHulls(); - - mapEntityList.Add(this); - } - - public Gap(Rectangle newRect, bool isHorizontal) - { - rect = newRect; - linkedTo = new ObservableCollection(); - - flowForce = Vector2.Zero; - - this.isHorizontal = isHorizontal; - - open = 1.0f; - - FindHulls(); - - mapEntityList.Add(this); - } - - public static void UpdateHulls() - { - foreach (MapEntity entity in mapEntityList) - { - Gap g = entity as Gap; - if (g != null) g.FindHulls(); - } - } - - private void FindHulls() - { - Hull hull1 = null, hull2 = null; - - linkedTo.Clear(); - - foreach (Hull h in Hull.hullList) - { - if (!Map.RectsOverlap(h.Rect, rect)) continue; - - //if the gap is inside the hull completely, ignore it - if (rect.X > h.Rect.X && rect.X + rect.Width < h.Rect.X+h.Rect.Width && - rect.Y < h.Rect.Y && rect.Y - rect.Height > h.Rect.Y - h.Rect.Height) continue; - - if (hull1 == null) - { - hull1 = h; - } - else - { - hull2 = h; - break; - } - } - - if (hull1 == null && hull2 == null) return; - - if (hull1 != null && hull2 != null) - { - if (isHorizontal) - { - //make sure that water1 is the lefthand room - //or that water2 is null if the gap doesn't lead to another room - if (hull1.Rect.X < hull2.Rect.X) - { - linkedTo.Add(hull1); - linkedTo.Add(hull2); - } - else - { - linkedTo.Add(hull2); - linkedTo.Add(hull1); - } - } - else - { - //make sure that water1 is the room on the top - //or that water2 is null if the gap doesn't lead to another room - if (hull1.Rect.Y > hull2.Rect.Y) - { - linkedTo.Add(hull1); - linkedTo.Add(hull2); - } - else - { - linkedTo.Add(hull2); - linkedTo.Add(hull1); - } - } - } - else - { - linkedTo.Add(hull1); - } - } - - public override void Draw(SpriteBatch sb, bool editing) - { - //if (linkedTo[0] != null) - // GUI.DrawLine(sb, new Vector2(Position.X, Position.Y), - // new Vector2(linkedTo[0].Position.X, linkedTo[0].Position.Y), Color.Blue); - - //if (linkedTo.Count > 1 && linkedTo[1] != null) - // GUI.DrawLine(sb, new Vector2(Position.X, Position.Y), - // new Vector2(linkedTo[1].Position.X, linkedTo[1].Position.Y), Color.Blue); - - - //GUI.DrawLine(sb, new Vector2(Position.X, -Position.Y), new Vector2(Position.X, -Position.Y)+new Vector2(flowForce.X, -flowForce.Y), Color.LightBlue); - - if (!editing) return; - - Color clr = (open == 0.0f) ? Color.Red : Color.Cyan; - - GUI.DrawRectangle(sb, new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), clr); - - if (isSelected && editing) - { - GUI.DrawRectangle(sb, - new Vector2(rect.X - 5, -rect.Y - 5), - new Vector2(rect.Width + 10, rect.Height + 10), - Color.Red); - } - - //HUD.DrawLine(sb, new Vector2(position.X, -position.Y), - // isHorizontal ? new Vector2(position.X, -position.Y + size) : new Vector2(position.X + size, -position.Y), - // clr); - } - - public override void Update(Camera cam, float deltaTime) - { - - soundVolume = soundVolume + ((flowForce.Length() < 100.0f) ? -deltaTime * 0.5f : deltaTime * 0.5f); - soundVolume = MathHelper.Clamp(soundVolume, 0.0f, 1.0f); - - //if (soundVolume < 0.01f) - //{ - // if (soundIndex > -1) - // { - // Sound.Stop(soundIndex); - // soundIndex = -1; - // } - - //} - //else - { - int index = (int)Math.Floor(flowForce.Length() / 100.0f); - index = Math.Min(index,2); - - soundIndex = AmbientSoundManager.flowSounds[index].Loop(soundIndex, soundVolume, Position, 2000.0f); - //soundVolume = Math.Max(0.0f, soundVolume-deltaTime); - //Sound.UpdatePosition(soundIndex, Position, 2000.0f); - } - - flowForce = Vector2.Zero; - - if (open == 0.0f) return; - - UpdateOxygen(); - - - if (linkedTo.Count == 1) - { - //gap leading from a room to outside - UpdateRoomToOut(deltaTime); - } - else - { - //gap leading from a room to another - UpdateRoomToRoom(deltaTime); - } - - if (FlowForce.Length() > 150.0f && flowTargetHull!=null && flowTargetHull.Volume < flowTargetHull.FullVolume) - { - //UpdateFlowForce(); - - Vector2 pos = SimPosition; - if (isHorizontal) - { - pos.Y = ConvertUnits.ToSimUnits(MathHelper.Clamp(lowerSurface, rect.Y-rect.Height, rect.Y)); - - Game1.particleManager.CreateParticle(new Vector2(pos.X, pos.Y - ToolBox.RandomFloat(0.0f, 0.1f)), - 0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.007f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.007f)), "watersplash"); - - pos.Y = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(lowerSurface, rect.Y - rect.Height)); - Game1.particleManager.CreateParticle(pos, 0.0f, flowForce / 200.0f, "bubbles"); - } - else - { - pos.Y += Math.Sign(flowForce.Y) * ConvertUnits.ToSimUnits(rect.Height / 2.0f); - for (int i = 0; i < rect.Width; i += (int)ToolBox.RandomFloat(20, 50)) - { - pos.X = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(rect.X, rect.X+rect.Width)); - Game1.particleManager.CreateParticle(pos, - 0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.008f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.008f)), "watersplash"); - - Game1.particleManager.CreateParticle(pos, ToolBox.VectorToAngle(flowForce), flowForce / 200.0f, "bubbles"); - } - } - - } - - - } - - void UpdateRoomToRoom(float deltaTime) - { - if (linkedTo.Count < 2) return; - Hull hull1 = (Hull)linkedTo[0]; - Hull hull2 = (Hull)linkedTo[1]; - - if (hull1.Volume == 0.0 && hull2.Volume == 0.0) return; - - float size = (isHorizontal) ? rect.Height : rect.Width; - - //a variable affecting the water flow through the gap - //the larger the gap is, the faster the water flows - float sizeModifier = size / 100.0f * open; - - //horizontal gap (such as a regular door) - if (isHorizontal) - { - //higherSurface = Math.Min(hull1.Surface,hull2.Surface); - float delta=0.0f; - //water level is above the lower boundary of the gap - if (Math.Max(hull1.Surface+hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface+hull2.WaveY[0]) > rect.Y - size) - { - - int dir = (hull1.Pressure > hull2.Pressure) ? 1 : -1; - - //water flowing from the righthand room to the lefthand room - if (dir == -1) - { - if (!(hull2.Volume > 0.0f)) return; - lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1]; - //delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume)); - //delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress); - - flowTargetHull = hull1; - - //make sure not to move more than what the room contains - delta = Math.Min((hull2.Pressure - hull1.Pressure) * sizeModifier, Math.Min(hull2.Volume, hull2.FullVolume)); - - //make sure not to place more water to the target room than it can hold - delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - (hull1.Volume)); - hull1.Volume += delta; - hull2.Volume -= delta; - if (hull1.Volume > hull1.FullVolume) - hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2); - - flowForce = new Vector2(-delta, 0.0f); - } - else if (dir == 1) - { - if (!(hull1.Volume > 0.0f)) return; - lowerSurface = hull2.Surface - hull2.WaveY[1]; - - flowTargetHull = hull2; - - //make sure not to move more than what the room contains - delta = Math.Min((hull1.Pressure - hull2.Pressure) * sizeModifier, Math.Min(hull1.Volume, hull1.FullVolume)); - - //make sure not to place more water to the target room than it can hold - delta = Math.Min(delta, hull2.FullVolume + Hull.MaxCompress - (hull2.Volume)); - hull1.Volume -= delta; - hull2.Volume += delta; - if (hull2.Volume > hull2.FullVolume) - hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2); - - flowForce = new Vector2(delta, 0.0f); - } - - if (delta>100.0f) - { - float avg = (hull1.Surface + hull2.Surface) / 2.0f; - //float avgVel = (hull2.WaveVel[1] + hull1.WaveVel[hull1.WaveY.Length - 2]) / 2.0f; - - if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress && - hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1] < rect.Y) - { - hull1.WaveVel[hull1.WaveY.Length - 1] = (avg-(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1]))*0.1f; - hull1.WaveVel[hull1.WaveY.Length - 2] = hull1.WaveVel[hull1.WaveY.Length - 1]; - } - - if (hull2.Volume < hull2.FullVolume - Hull.MaxCompress && - hull2.Surface + hull2.WaveY[0] < rect.Y) - { - hull2.WaveVel[0] = (avg - (hull2.Surface + hull2.WaveY[0])) * 0.1f; - hull2.WaveVel[1] = hull2.WaveVel[0]; - } - } - - - - } - - } - else - { - //lower room is full of water - if (hull2.Pressure > hull1.Pressure) - { - float delta = Math.Min(hull2.Volume - hull2.FullVolume + Hull.MaxCompress / 2.0f, deltaTime * 5000f * sizeModifier); - delta = Math.Max(delta, 0.0f); - hull1.Volume += delta; - hull2.Volume -= delta; - - flowTargetHull = hull1; - - //delta = (water2.Pressure - water1.Pressure) * 0.1f; - //if (delta > 0.1f) - //{ - // int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth); - // //water1.WaveY[posX] = delta; - // water1.WaveVel[posX] = delta * 0.01f; - //} - if (hull1.Volume > hull1.FullVolume) - { - hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2); - } - - flowForce = new Vector2(0.0f, delta); - - } - //there's water in the upper room, drop to lower - else if (hull1.Volume > 0) - { - flowTargetHull = hull2; - - //make sure the amount of water moved isn't more than what the room contains - float delta = Math.Min(hull1.Volume, deltaTime * 10000f * sizeModifier); - //make sure not to place more water to the target room than it can hold - delta = Math.Min(delta, (hull2.FullVolume + Math.Max(hull1.Volume - hull1.FullVolume, 0.0f)) - hull2.Volume + Hull.MaxCompress / 4.0f); - - hull1.Volume -= delta; - hull2.Volume += delta; - - if (hull2.Volume > hull2.FullVolume) - { - hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2); - } - - flowForce = new Vector2(0.0f,-delta); - - //if (water2.Volume < water2.FullVolume - Hull.MaxCompress) - //{ - // int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth); - // //water1.WaveY[posX] = -delta; - // if (posX > -1 && posX < water2.WaveVel.Length) - // water1.WaveVel[posX] = -delta * 0.01f; - - // posX = (int)((rect.X + size / 2.0f - water2.Rect.X) / Hull.WaveWidth); - // //water2.WaveY[posX] = delta; - // if (posX > -1 && posX 0.0f) - { - if (hull1.Volume>hull1.FullVolume && hull2.Volume>hull2.FullVolume) - { - float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f; - hull1.LethalPressure = avgLethality; - hull2.LethalPressure = avgLethality; - } - else - { - hull1.LethalPressure = 0.0f; - hull2.LethalPressure = 0.0f; - } - } - - - - } - - void UpdateRoomToOut(float deltaTime) - { - if (linkedTo.Count != 1) return; - - float size = (isHorizontal) ? rect.Height : rect.Width; - - Hull hull1 = (Hull)linkedTo[0]; - - //a variable affecting the water flow through the gap - //the larger the gap is, the faster the water flows - float sizeModifier = size * open; - - float delta = Hull.MaxCompress * sizeModifier * deltaTime; - - //make sure not to place more water to the target room than it can hold - delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - hull1.Volume); - hull1.Volume += delta; - - if (hull1.Volume > hull1.FullVolume) hull1.Pressure += 0.5f; - - flowTargetHull = hull1; - - if (isHorizontal) - { - //water flowing from right to left - if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) - { - flowForce = new Vector2(-delta, 0.0f); - - } - else - { - flowForce = new Vector2(delta, 0.0f); - } - - higherSurface = hull1.Surface; - lowerSurface = rect.Y; - - if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress && - hull1.Surface > -rect.Y) - { - float vel = (rect.Y + hull1.Surface) * 0.03f; - - if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) - { - hull1.WaveVel[hull1.WaveY.Length - 1] += vel; - hull1.WaveVel[hull1.WaveY.Length - 2] += vel; - } - else - { - hull1.WaveVel[0] += vel; - hull1.WaveVel[1] += vel; - } - } - } - else - { - if (rect.Y > hull1.Rect.Y - hull1.Rect.Height / 2.0f) - { - flowForce = new Vector2(0.0f, -delta); - } - else - { - flowForce = new Vector2(0.0f,delta); - } - } - } - - private void UpdateOxygen() - { - if (linkedTo.Count < 2) return; - Hull hull1 = (Hull)linkedTo[0]; - Hull hull2 = (Hull)linkedTo[1]; - - float totalOxygen = hull1.Oxygen + hull2.Oxygen; - float totalVolume = (hull1.FullVolume + hull2.FullVolume); - - hull1.Oxygen += Math.Sign(totalOxygen*hull1.FullVolume/(totalVolume) - hull1.Oxygen) * Hull.OxygenDistributionSpeed; - hull2.Oxygen += Math.Sign(totalOxygen*hull2.FullVolume/(totalVolume) - hull2.Oxygen) * Hull.OxygenDistributionSpeed; - } - - public override void Remove() - { - base.Remove(); - - if (soundIndex > -1) Sounds.SoundManager.Stop(soundIndex); - } - - public override XElement Save(XDocument doc) - { - XElement element = new XElement("Gap"); - - element.Add(new XAttribute("ID", ID), - new XAttribute("x", rect.X), - new XAttribute("y", rect.Y), - new XAttribute("width", rect.Width), - new XAttribute("height", rect.Height)); - - //if (linkedTo != null) - //{ - // int i = 0; - // foreach (Entity e in linkedTo) - // { - // if (e == null) continue; - // element.Add(new XAttribute("linkedto" + i, e.ID)); - // i += 1; - // } - //} - - doc.Root.Add(element); - - return element; - } - - - public static void Load(XElement element) - { - Rectangle rect = new Rectangle( - int.Parse(element.Attribute("x").Value), - int.Parse(element.Attribute("y").Value), - int.Parse(element.Attribute("width").Value), - int.Parse(element.Attribute("height").Value)); - - Gap g = new Gap(rect); - g.ID = int.Parse(element.Attribute("ID").Value); - - g.linkedToID = new List(); - //int i = 0; - //while (element.Attribute("linkedto" + i) != null) - //{ - // g.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value)); - // i += 1; - //} - } - } -} +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System.Collections.ObjectModel; + +namespace Subsurface +{ + class Gap : MapEntity + { + public bool isHorizontal; + + //private Sound waterSound; + + //a value between 0.0f-1.0f (0.0 = closed, 1.0f = open) + float open; + + //the forces of the water flow which are exerted on physics bodies + Vector2 flowForce; + + Hull flowTargetHull; + + float higherSurface; + float lowerSurface; + + private int soundIndex; + + float soundVolume; + + public float Open + { + get { return open; } + set { open = MathHelper.Clamp(value, 0.0f, 1.0f); } + } + + public Vector2 FlowForce + { + get { return flowForce*soundVolume; } + } + + public Hull FlowTargetHull + { + get { return flowTargetHull; } + } + + public Gap(Rectangle newRect) + { + rect = newRect; + linkedTo = new ObservableCollection(); + + //waterSound = new Sound("waterstream", 0.0f); + + flowForce = Vector2.Zero; + + isHorizontal = (rect.Width < rect.Height); + + open = 1.0f; + + FindHulls(); + + mapEntityList.Add(this); + } + + public Gap(Rectangle newRect, bool isHorizontal) + { + rect = newRect; + linkedTo = new ObservableCollection(); + + flowForce = Vector2.Zero; + + this.isHorizontal = isHorizontal; + + open = 1.0f; + + FindHulls(); + + mapEntityList.Add(this); + } + + public static void UpdateHulls() + { + foreach (MapEntity entity in mapEntityList) + { + Gap g = entity as Gap; + if (g != null) g.FindHulls(); + } + } + + public override bool Contains(Vector2 position) + { + return (Map.RectContains(rect, position) && + !Map.RectContains(new Rectangle(rect.X + 4, rect.Y - 4, rect.Width - 8, rect.Height - 8), position)); + } + + private void FindHulls() + { + Hull hull1 = null, hull2 = null; + + linkedTo.Clear(); + + foreach (Hull h in Hull.hullList) + { + if (!Map.RectsOverlap(h.Rect, rect)) continue; + + //if the gap is inside the hull completely, ignore it + if (rect.X > h.Rect.X && rect.X + rect.Width < h.Rect.X+h.Rect.Width && + rect.Y < h.Rect.Y && rect.Y - rect.Height > h.Rect.Y - h.Rect.Height) continue; + + if (hull1 == null) + { + hull1 = h; + } + else + { + hull2 = h; + break; + } + } + + if (hull1 == null && hull2 == null) return; + + if (hull1 != null && hull2 != null) + { + if (isHorizontal) + { + //make sure that water1 is the lefthand room + //or that water2 is null if the gap doesn't lead to another room + if (hull1.Rect.X < hull2.Rect.X) + { + linkedTo.Add(hull1); + linkedTo.Add(hull2); + } + else + { + linkedTo.Add(hull2); + linkedTo.Add(hull1); + } + } + else + { + //make sure that water1 is the room on the top + //or that water2 is null if the gap doesn't lead to another room + if (hull1.Rect.Y > hull2.Rect.Y) + { + linkedTo.Add(hull1); + linkedTo.Add(hull2); + } + else + { + linkedTo.Add(hull2); + linkedTo.Add(hull1); + } + } + } + else + { + linkedTo.Add(hull1); + } + } + + public override void Draw(SpriteBatch sb, bool editing) + { + //if (linkedTo[0] != null) + // GUI.DrawLine(sb, new Vector2(Position.X, Position.Y), + // new Vector2(linkedTo[0].Position.X, linkedTo[0].Position.Y), Color.Blue); + + //if (linkedTo.Count > 1 && linkedTo[1] != null) + // GUI.DrawLine(sb, new Vector2(Position.X, Position.Y), + // new Vector2(linkedTo[1].Position.X, linkedTo[1].Position.Y), Color.Blue); + + + //GUI.DrawLine(sb, new Vector2(Position.X, -Position.Y), new Vector2(Position.X, -Position.Y)+new Vector2(flowForce.X, -flowForce.Y), Color.LightBlue); + + if (!editing) return; + + Color clr = (open == 0.0f) ? Color.Red : Color.Cyan; + + GUI.DrawRectangle(sb, new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), clr); + + if (isSelected && editing) + { + GUI.DrawRectangle(sb, + new Vector2(rect.X - 5, -rect.Y - 5), + new Vector2(rect.Width + 10, rect.Height + 10), + Color.Red); + } + + //HUD.DrawLine(sb, new Vector2(position.X, -position.Y), + // isHorizontal ? new Vector2(position.X, -position.Y + size) : new Vector2(position.X + size, -position.Y), + // clr); + } + + public override void Update(Camera cam, float deltaTime) + { + + soundVolume = soundVolume + ((flowForce.Length() < 100.0f) ? -deltaTime * 0.5f : deltaTime * 0.5f); + soundVolume = MathHelper.Clamp(soundVolume, 0.0f, 1.0f); + + //if (soundVolume < 0.01f) + //{ + // if (soundIndex > -1) + // { + // Sound.Stop(soundIndex); + // soundIndex = -1; + // } + + //} + //else + { + int index = (int)Math.Floor(flowForce.Length() / 100.0f); + index = Math.Min(index,2); + + soundIndex = AmbientSoundManager.flowSounds[index].Loop(soundIndex, soundVolume, Position, 2000.0f); + //soundVolume = Math.Max(0.0f, soundVolume-deltaTime); + //Sound.UpdatePosition(soundIndex, Position, 2000.0f); + } + + flowForce = Vector2.Zero; + + if (open == 0.0f) return; + + UpdateOxygen(); + + + if (linkedTo.Count == 1) + { + //gap leading from a room to outside + UpdateRoomToOut(deltaTime); + } + else + { + //gap leading from a room to another + UpdateRoomToRoom(deltaTime); + } + + if (FlowForce.Length() > 150.0f && flowTargetHull!=null && flowTargetHull.Volume < flowTargetHull.FullVolume) + { + //UpdateFlowForce(); + + Vector2 pos = SimPosition; + if (isHorizontal) + { + pos.Y = ConvertUnits.ToSimUnits(MathHelper.Clamp(lowerSurface, rect.Y-rect.Height, rect.Y)); + + Game1.particleManager.CreateParticle(new Vector2(pos.X, pos.Y - ToolBox.RandomFloat(0.0f, 0.1f)), + 0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.007f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.007f)), "watersplash"); + + pos.Y = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(lowerSurface, rect.Y - rect.Height)); + Game1.particleManager.CreateParticle(pos, 0.0f, flowForce / 200.0f, "bubbles"); + } + else + { + pos.Y += Math.Sign(flowForce.Y) * ConvertUnits.ToSimUnits(rect.Height / 2.0f); + for (int i = 0; i < rect.Width; i += (int)ToolBox.RandomFloat(20, 50)) + { + pos.X = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(rect.X, rect.X+rect.Width)); + Game1.particleManager.CreateParticle(pos, + 0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.008f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.008f)), "watersplash"); + + Game1.particleManager.CreateParticle(pos, ToolBox.VectorToAngle(flowForce), flowForce / 200.0f, "bubbles"); + } + } + + } + + + } + + void UpdateRoomToRoom(float deltaTime) + { + if (linkedTo.Count < 2) return; + Hull hull1 = (Hull)linkedTo[0]; + Hull hull2 = (Hull)linkedTo[1]; + + if (hull1.Volume == 0.0 && hull2.Volume == 0.0) return; + + float size = (isHorizontal) ? rect.Height : rect.Width; + + //a variable affecting the water flow through the gap + //the larger the gap is, the faster the water flows + float sizeModifier = size / 100.0f * open; + + //horizontal gap (such as a regular door) + if (isHorizontal) + { + //higherSurface = Math.Min(hull1.Surface,hull2.Surface); + float delta=0.0f; + //water level is above the lower boundary of the gap + if (Math.Max(hull1.Surface+hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface+hull2.WaveY[0]) > rect.Y - size) + { + + int dir = (hull1.Pressure > hull2.Pressure) ? 1 : -1; + + //water flowing from the righthand room to the lefthand room + if (dir == -1) + { + if (!(hull2.Volume > 0.0f)) return; + lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1]; + //delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume)); + //delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress); + + flowTargetHull = hull1; + + //make sure not to move more than what the room contains + delta = Math.Min((hull2.Pressure - hull1.Pressure) * sizeModifier, Math.Min(hull2.Volume, hull2.FullVolume)); + + //make sure not to place more water to the target room than it can hold + delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - (hull1.Volume)); + hull1.Volume += delta; + hull2.Volume -= delta; + if (hull1.Volume > hull1.FullVolume) + hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2); + + flowForce = new Vector2(-delta, 0.0f); + } + else if (dir == 1) + { + if (!(hull1.Volume > 0.0f)) return; + lowerSurface = hull2.Surface - hull2.WaveY[1]; + + flowTargetHull = hull2; + + //make sure not to move more than what the room contains + delta = Math.Min((hull1.Pressure - hull2.Pressure) * sizeModifier, Math.Min(hull1.Volume, hull1.FullVolume)); + + //make sure not to place more water to the target room than it can hold + delta = Math.Min(delta, hull2.FullVolume + Hull.MaxCompress - (hull2.Volume)); + hull1.Volume -= delta; + hull2.Volume += delta; + if (hull2.Volume > hull2.FullVolume) + hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2); + + flowForce = new Vector2(delta, 0.0f); + } + + if (delta>100.0f) + { + float avg = (hull1.Surface + hull2.Surface) / 2.0f; + //float avgVel = (hull2.WaveVel[1] + hull1.WaveVel[hull1.WaveY.Length - 2]) / 2.0f; + + if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress && + hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1] < rect.Y) + { + hull1.WaveVel[hull1.WaveY.Length - 1] = (avg-(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1]))*0.1f; + hull1.WaveVel[hull1.WaveY.Length - 2] = hull1.WaveVel[hull1.WaveY.Length - 1]; + } + + if (hull2.Volume < hull2.FullVolume - Hull.MaxCompress && + hull2.Surface + hull2.WaveY[0] < rect.Y) + { + hull2.WaveVel[0] = (avg - (hull2.Surface + hull2.WaveY[0])) * 0.1f; + hull2.WaveVel[1] = hull2.WaveVel[0]; + } + } + + + + } + + } + else + { + //lower room is full of water + if (hull2.Pressure > hull1.Pressure) + { + float delta = Math.Min(hull2.Volume - hull2.FullVolume + Hull.MaxCompress / 2.0f, deltaTime * 5000f * sizeModifier); + delta = Math.Max(delta, 0.0f); + hull1.Volume += delta; + hull2.Volume -= delta; + + flowTargetHull = hull1; + + //delta = (water2.Pressure - water1.Pressure) * 0.1f; + //if (delta > 0.1f) + //{ + // int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth); + // //water1.WaveY[posX] = delta; + // water1.WaveVel[posX] = delta * 0.01f; + //} + if (hull1.Volume > hull1.FullVolume) + { + hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2); + } + + flowForce = new Vector2(0.0f, delta); + + } + //there's water in the upper room, drop to lower + else if (hull1.Volume > 0) + { + flowTargetHull = hull2; + + //make sure the amount of water moved isn't more than what the room contains + float delta = Math.Min(hull1.Volume, deltaTime * 10000f * sizeModifier); + //make sure not to place more water to the target room than it can hold + delta = Math.Min(delta, (hull2.FullVolume + Math.Max(hull1.Volume - hull1.FullVolume, 0.0f)) - hull2.Volume + Hull.MaxCompress / 4.0f); + + hull1.Volume -= delta; + hull2.Volume += delta; + + if (hull2.Volume > hull2.FullVolume) + { + hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2); + } + + flowForce = new Vector2(0.0f,-delta); + + //if (water2.Volume < water2.FullVolume - Hull.MaxCompress) + //{ + // int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth); + // //water1.WaveY[posX] = -delta; + // if (posX > -1 && posX < water2.WaveVel.Length) + // water1.WaveVel[posX] = -delta * 0.01f; + + // posX = (int)((rect.X + size / 2.0f - water2.Rect.X) / Hull.WaveWidth); + // //water2.WaveY[posX] = delta; + // if (posX > -1 && posX 0.0f) + { + if (hull1.Volume>hull1.FullVolume && hull2.Volume>hull2.FullVolume) + { + float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f; + hull1.LethalPressure = avgLethality; + hull2.LethalPressure = avgLethality; + } + else + { + hull1.LethalPressure = 0.0f; + hull2.LethalPressure = 0.0f; + } + } + + + + } + + void UpdateRoomToOut(float deltaTime) + { + if (linkedTo.Count != 1) return; + + float size = (isHorizontal) ? rect.Height : rect.Width; + + Hull hull1 = (Hull)linkedTo[0]; + + //a variable affecting the water flow through the gap + //the larger the gap is, the faster the water flows + float sizeModifier = size * open; + + float delta = Hull.MaxCompress * sizeModifier * deltaTime; + + //make sure not to place more water to the target room than it can hold + delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - hull1.Volume); + hull1.Volume += delta; + + if (hull1.Volume > hull1.FullVolume) hull1.Pressure += 0.5f; + + flowTargetHull = hull1; + + if (isHorizontal) + { + //water flowing from right to left + if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) + { + flowForce = new Vector2(-delta, 0.0f); + + } + else + { + flowForce = new Vector2(delta, 0.0f); + } + + higherSurface = hull1.Surface; + lowerSurface = rect.Y; + + if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress && + hull1.Surface > -rect.Y) + { + float vel = (rect.Y + hull1.Surface) * 0.03f; + + if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) + { + hull1.WaveVel[hull1.WaveY.Length - 1] += vel; + hull1.WaveVel[hull1.WaveY.Length - 2] += vel; + } + else + { + hull1.WaveVel[0] += vel; + hull1.WaveVel[1] += vel; + } + } + } + else + { + if (rect.Y > hull1.Rect.Y - hull1.Rect.Height / 2.0f) + { + flowForce = new Vector2(0.0f, -delta); + } + else + { + flowForce = new Vector2(0.0f,delta); + } + } + } + + private void UpdateOxygen() + { + if (linkedTo.Count < 2) return; + Hull hull1 = (Hull)linkedTo[0]; + Hull hull2 = (Hull)linkedTo[1]; + + float totalOxygen = hull1.Oxygen + hull2.Oxygen; + float totalVolume = (hull1.FullVolume + hull2.FullVolume); + + hull1.Oxygen += Math.Sign(totalOxygen*hull1.FullVolume/(totalVolume) - hull1.Oxygen) * Hull.OxygenDistributionSpeed; + hull2.Oxygen += Math.Sign(totalOxygen*hull2.FullVolume/(totalVolume) - hull2.Oxygen) * Hull.OxygenDistributionSpeed; + } + + public override void Remove() + { + base.Remove(); + + if (soundIndex > -1) Sounds.SoundManager.Stop(soundIndex); + } + + public override XElement Save(XDocument doc) + { + XElement element = new XElement("Gap"); + + element.Add(new XAttribute("ID", ID), + new XAttribute("x", rect.X), + new XAttribute("y", rect.Y), + new XAttribute("width", rect.Width), + new XAttribute("height", rect.Height)); + + //if (linkedTo != null) + //{ + // int i = 0; + // foreach (Entity e in linkedTo) + // { + // if (e == null) continue; + // element.Add(new XAttribute("linkedto" + i, e.ID)); + // i += 1; + // } + //} + + doc.Root.Add(element); + + return element; + } + + + public static void Load(XElement element) + { + Rectangle rect = new Rectangle( + int.Parse(element.Attribute("x").Value), + int.Parse(element.Attribute("y").Value), + int.Parse(element.Attribute("width").Value), + int.Parse(element.Attribute("height").Value)); + + Gap g = new Gap(rect); + g.ID = int.Parse(element.Attribute("ID").Value); + + g.linkedToID = new List(); + //int i = 0; + //while (element.Attribute("linkedto" + i) != null) + //{ + // g.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value)); + // i += 1; + //} + } + } +} diff --git a/Subsurface/Map/Hull.cs b/Subsurface/Map/Hull.cs index c16135c64..f49def308 100644 --- a/Subsurface/Map/Hull.cs +++ b/Subsurface/Map/Hull.cs @@ -1,436 +1,436 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Xml.Linq; -using FarseerPhysics; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace Subsurface -{ - - class Hull : MapEntity - { - public static List hullList = new List(); - - public static bool EditWater; - - public static WaterRenderer renderer; - - public static bool DebugDraw; - - public const float OxygenDistributionSpeed = 500.0f; - public const float OxygenDetoriationSpeed = 0.3f; - public const float OxygenConsumptionSpeed = 1000.0f; - - public const int WaveWidth = 16; - const float WaveStiffness = 0.003f; - const float WaveSpread = 0.05f; - const float WaveDampening = 0.01f; - - //how much excess water the room can contain (= more than the volume of the room) - public const float MaxCompress = 10000f; - - public readonly Dictionary properties; - - float lethalPressure; - - float surface; - float volume; - float pressure; - - float oxygen; - - bool update; - - float[] waveY; //displacement from the surface of the water - float[] waveVel; //velocity of the point - - float[] leftDelta; - float[] rightDelta; - - public override bool IsLinkable - { - get { return true; } - } - - public float LethalPressure - { - get { return lethalPressure; } - set { lethalPressure = MathHelper.Clamp(value, 0.0f, 100.0f); } - } - - public Vector2 Size - { - get { return new Vector2(rect.Width, rect.Height); } - } - - public float Surface - { - get { return surface; } - } - - public float Volume - { - get { return volume; } - set - { - volume = MathHelper.Clamp(value, 0.0f, FullVolume + MaxCompress); - if (volume < FullVolume) Pressure = rect.Y - rect.Height + volume / rect.Width; - if (volume > 0.0f) update = true; - } - } - - public float Oxygen - { - get { return oxygen; } - set { oxygen = MathHelper.Clamp(value, 0.0f, FullVolume); } - } - - public float OxygenPercentage - { - get { return oxygen / FullVolume * 100.0f; } - set { Oxygen = (value / 100.0f) * FullVolume; } - } - - public float FullVolume - { - get { return (rect.Width * rect.Height); } - } - - public float Pressure - { - get { return pressure; } - set { pressure = value; } - } - - public float[] WaveY - { - get { return waveY; } - } - - public float[] WaveVel - { - get { return waveVel; } - } - - public Hull(Rectangle rectangle) - { - rect = rectangle; - - OxygenPercentage = (float)(Game1.random.NextDouble() * 100.0); - - properties = TypeDescriptor.GetProperties(this.GetType()) - .Cast() - .ToDictionary(pr => pr.Name); - - int arraySize = (rectangle.Width / WaveWidth + 1); - waveY = new float[arraySize]; - waveVel = new float[arraySize]; - - leftDelta = new float[arraySize]; - rightDelta = new float[arraySize]; - - surface = rect.Y - rect.Height; - - aiTarget = new AITarget(this); - aiTarget.SightRange = (rect.Width + rect.Height)*10.0f; - - hullList.Add(this); - - Item.UpdateHulls(); - Gap.UpdateHulls(); - - Volume = 0.0f; - - //add to list of entities as well - mapEntityList.Add(this); - } - - public override bool Contains(Vector2 position) - { - return (Map.RectContains(rect, position) && - !Map.RectContains(new Rectangle(rect.X + 8, rect.Y - 8, rect.Width -16, rect.Height -16), position)); - } - - public int GetWaveIndex(Vector2 position) - { - int index = (int)(position.X - rect.X) / WaveWidth; - index = MathHelper.Clamp(index, 0, waveY.Length-1); - return index; - } - - public override void Move(Vector2 amount) - { - rect.X += (int)amount.X; - rect.Y += (int)amount.Y; - - Item.UpdateHulls(); - Gap.UpdateHulls(); - } - - public override void Remove() - { - base.Remove(); - - Item.UpdateHulls(); - Gap.UpdateHulls(); - - //renderer.Dispose(); - - hullList.Remove(this); - } - - public override void Update(Camera cam, float deltaTime) - { - Oxygen -= OxygenDetoriationSpeed * deltaTime; - - if (EditWater) - { - Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); - if (Map.RectContains(rect, position)) - { - if (PlayerInput.LeftButtonDown()) - { - waveY[(int)(position.X - rect.X) / Hull.WaveWidth] = 100.0f; - Volume = Volume + 1500.0f; - } - else if (PlayerInput.GetMouseState.RightButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) - { - Volume = Volume - 1500.0f; - } - } - } - - - if (!update) return; - - float surfaceY = rect.Y - rect.Height + Volume / rect.Width; - for (int i = 0; i < waveY.Length; i++) - { - float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i])); - if (maxDelta > ToolBox.RandomFloat(0.2f,10.0f)) - { - Game1.particleManager.CreateParticle(ConvertUnits.ToSimUnits(new Vector2(rect.X + WaveWidth * i,surface + waveY[i])), - 0.0f, new Vector2(0.0f, waveVel[i]/10.0f-1.0f), "mist"); - } - - waveY[i] = waveY[i] + waveVel[i]; - - if (surfaceY + waveY[i] > rect.Y) - { - waveY[i] -= (surfaceY + waveY[i]) - rect.Y; - waveVel[i] = waveVel[i] * -0.5f; - } - else if (surfaceY + waveY[i] < rect.Y - rect.Height) - { - waveY[i] -= (surfaceY + waveY[i]) - (rect.Y - rect.Height); - waveVel[i] = waveVel[i] * -0.5f; - } - - - //acceleration - float a = -WaveStiffness * waveY[i] - waveVel[i] * WaveDampening; - waveVel[i] = waveVel[i] + a; - - } - - for (int j = 0; j < 2; j++) - { - for (int i = 1; i < waveY.Length - 1; i++) - { - leftDelta[i] = WaveSpread * (waveY[i] - waveY[i - 1]); - waveVel[i - 1] = waveVel[i - 1] + leftDelta[i]; - - rightDelta[i] = WaveSpread * (waveY[i] - waveY[i + 1]); - waveVel[i + 1] = waveVel[i + 1] + rightDelta[i]; - } - - for (int i = 1; i < waveY.Length - 1; i++) - { - waveY[i - 1] = waveY[i - 1] + leftDelta[i]; - waveY[i + 1] = waveY[i + 1] + rightDelta[i]; - } - } - - if (volume 0.1f) return; - } - update = false; - } - } - else - { - LethalPressure += 1.0f; - } - - - } - - public override void Draw(SpriteBatch spriteBatch, bool editing) - { - if (!editing && !DebugDraw) return; - - GUI.DrawRectangle(spriteBatch, - new Vector2(rect.X, -rect.Y), - new Vector2(rect.Width, rect.Height), - isHighlighted ? Color.Green : Color.Blue); - - GUI.DrawRectangle(spriteBatch, - new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), - Color.Red*((100.0f-OxygenPercentage)/400.0f), true); - - spriteBatch.DrawString(GUI.font, "Pressure: " + ((int)pressure - rect.Y).ToString() + - " - Lethality: " + lethalPressure + - " - Oxygen: "+((int)OxygenPercentage).ToString(), new Vector2(rect.X+10, -rect.Y+10), Color.Black); - spriteBatch.DrawString(GUI.font, volume.ToString() +" / "+ FullVolume.ToString(), new Vector2(rect.X+10, -rect.Y+30), Color.Black); - - if (isSelected && editing) - { - GUI.DrawRectangle(spriteBatch, - new Vector2(rect.X - 5, -rect.Y - 5), - new Vector2(rect.Width + 10, rect.Height + 10), - Color.Red); - } - } - - public void Render(GraphicsDevice graphicsDevice, Camera cam) - { - if (renderer.positionInBuffer > renderer.vertices.Length - 6) return; - - //calculate where the surface should be based on the water volume - float top = rect.Y; - float bottom = rect.Y - rect.Height; - float surfaceY = bottom + Volume / rect.Width; - - //interpolate the position of the rendered surface towards the "target surface" - surface = surface + (surfaceY - surface) / 10.0f; - - Matrix transform = - cam.Transform - * Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1) * 0.5f; - - if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) return; - - if (!update) - { - // create the four corners of our triangle. - Vector3 p1 = new Vector3(rect.X, top, 0.0f); - Vector3 p2 = new Vector3(rect.X + rect.Width, top, 0.0f); - - Vector3 p3 = new Vector3(p2.X, bottom, 0.0f); - Vector3 p4 = new Vector3(p1.X, bottom, 0.0f); - - renderer.vertices[renderer.positionInBuffer] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform); - renderer.vertices[renderer.positionInBuffer + 1] = new WaterVertex(p2, new Vector2(p2.X, -p2.Y), transform); - renderer.vertices[renderer.positionInBuffer + 2] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform); - - renderer.vertices[renderer.positionInBuffer + 3] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform); - renderer.vertices[renderer.positionInBuffer + 4] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform); - renderer.vertices[renderer.positionInBuffer + 5] = new WaterVertex(p4, new Vector2(p4.X, -p4.Y), transform); - - renderer.positionInBuffer += 6; - - return; - } - - int x = rect.X; - int start = (int)Math.Floor((float)(cam.WorldView.X - x) / WaveWidth); - start = Math.Max(start, 0); - - int end = (waveY.Length - 1) - - (int)Math.Floor((float)((x + rect.Width) - (cam.WorldView.X + cam.WorldView.Width)) / WaveWidth); - end = Math.Min(end, waveY.Length - 1); - - x += start * WaveWidth; - - for (int i = start; i < end; i++) - { - if (renderer.positionInBuffer > renderer.vertices.Length - 6) return; - - Vector3 p1 = new Vector3(x, top, 0.0f); - Vector3 p4 = new Vector3(p1.X, surface + waveY[i], 0.0f); - - //skip adjacent "water rects" if the surface of the water is roughly at the same position - int width = WaveWidth; - while (i hullList = new List(); + + public static bool EditWater; + + public static WaterRenderer renderer; + + public static bool DebugDraw; + + public const float OxygenDistributionSpeed = 500.0f; + public const float OxygenDetoriationSpeed = 0.3f; + public const float OxygenConsumptionSpeed = 1000.0f; + + public const int WaveWidth = 16; + const float WaveStiffness = 0.003f; + const float WaveSpread = 0.05f; + const float WaveDampening = 0.01f; + + //how much excess water the room can contain (= more than the volume of the room) + public const float MaxCompress = 10000f; + + public readonly Dictionary properties; + + float lethalPressure; + + float surface; + float volume; + float pressure; + + float oxygen; + + bool update; + + float[] waveY; //displacement from the surface of the water + float[] waveVel; //velocity of the point + + float[] leftDelta; + float[] rightDelta; + + public override bool IsLinkable + { + get { return true; } + } + + public float LethalPressure + { + get { return lethalPressure; } + set { lethalPressure = MathHelper.Clamp(value, 0.0f, 100.0f); } + } + + public Vector2 Size + { + get { return new Vector2(rect.Width, rect.Height); } + } + + public float Surface + { + get { return surface; } + } + + public float Volume + { + get { return volume; } + set + { + volume = MathHelper.Clamp(value, 0.0f, FullVolume + MaxCompress); + if (volume < FullVolume) Pressure = rect.Y - rect.Height + volume / rect.Width; + if (volume > 0.0f) update = true; + } + } + + public float Oxygen + { + get { return oxygen; } + set { oxygen = MathHelper.Clamp(value, 0.0f, FullVolume); } + } + + public float OxygenPercentage + { + get { return oxygen / FullVolume * 100.0f; } + set { Oxygen = (value / 100.0f) * FullVolume; } + } + + public float FullVolume + { + get { return (rect.Width * rect.Height); } + } + + public float Pressure + { + get { return pressure; } + set { pressure = value; } + } + + public float[] WaveY + { + get { return waveY; } + } + + public float[] WaveVel + { + get { return waveVel; } + } + + public Hull(Rectangle rectangle) + { + rect = rectangle; + + OxygenPercentage = (float)(Game1.random.NextDouble() * 100.0); + + properties = TypeDescriptor.GetProperties(this.GetType()) + .Cast() + .ToDictionary(pr => pr.Name); + + int arraySize = (rectangle.Width / WaveWidth + 1); + waveY = new float[arraySize]; + waveVel = new float[arraySize]; + + leftDelta = new float[arraySize]; + rightDelta = new float[arraySize]; + + surface = rect.Y - rect.Height; + + aiTarget = new AITarget(this); + aiTarget.SightRange = (rect.Width + rect.Height)*10.0f; + + hullList.Add(this); + + Item.UpdateHulls(); + Gap.UpdateHulls(); + + Volume = 0.0f; + + //add to list of entities as well + mapEntityList.Add(this); + } + + public override bool Contains(Vector2 position) + { + return (Map.RectContains(rect, position) && + !Map.RectContains(new Rectangle(rect.X + 8, rect.Y - 8, rect.Width - 16, rect.Height - 16), position)); + } + + public int GetWaveIndex(Vector2 position) + { + int index = (int)(position.X - rect.X) / WaveWidth; + index = MathHelper.Clamp(index, 0, waveY.Length-1); + return index; + } + + public override void Move(Vector2 amount) + { + rect.X += (int)amount.X; + rect.Y += (int)amount.Y; + + Item.UpdateHulls(); + Gap.UpdateHulls(); + } + + public override void Remove() + { + base.Remove(); + + Item.UpdateHulls(); + Gap.UpdateHulls(); + + //renderer.Dispose(); + + hullList.Remove(this); + } + + public override void Update(Camera cam, float deltaTime) + { + Oxygen -= OxygenDetoriationSpeed * deltaTime; + + if (EditWater) + { + Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); + if (Map.RectContains(rect, position)) + { + if (PlayerInput.LeftButtonDown()) + { + waveY[(int)(position.X - rect.X) / Hull.WaveWidth] = 100.0f; + Volume = Volume + 1500.0f; + } + else if (PlayerInput.GetMouseState.RightButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) + { + Volume = Volume - 1500.0f; + } + } + } + + + if (!update) return; + + float surfaceY = rect.Y - rect.Height + Volume / rect.Width; + for (int i = 0; i < waveY.Length; i++) + { + float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i])); + if (maxDelta > ToolBox.RandomFloat(0.2f,10.0f)) + { + Game1.particleManager.CreateParticle(ConvertUnits.ToSimUnits(new Vector2(rect.X + WaveWidth * i,surface + waveY[i])), + ToolBox.RandomFloat(0.0f,6.2f), new Vector2(0.0f, -0.5f), "mist"); + } + + waveY[i] = waveY[i] + waveVel[i]; + + if (surfaceY + waveY[i] > rect.Y) + { + waveY[i] -= (surfaceY + waveY[i]) - rect.Y; + waveVel[i] = waveVel[i] * -0.5f; + } + else if (surfaceY + waveY[i] < rect.Y - rect.Height) + { + waveY[i] -= (surfaceY + waveY[i]) - (rect.Y - rect.Height); + waveVel[i] = waveVel[i] * -0.5f; + } + + + //acceleration + float a = -WaveStiffness * waveY[i] - waveVel[i] * WaveDampening; + waveVel[i] = waveVel[i] + a; + + } + + for (int j = 0; j < 2; j++) + { + for (int i = 1; i < waveY.Length - 1; i++) + { + leftDelta[i] = WaveSpread * (waveY[i] - waveY[i - 1]); + waveVel[i - 1] = waveVel[i - 1] + leftDelta[i]; + + rightDelta[i] = WaveSpread * (waveY[i] - waveY[i + 1]); + waveVel[i + 1] = waveVel[i + 1] + rightDelta[i]; + } + + for (int i = 1; i < waveY.Length - 1; i++) + { + waveY[i - 1] = waveY[i - 1] + leftDelta[i]; + waveY[i + 1] = waveY[i + 1] + rightDelta[i]; + } + } + + if (volume 0.1f) return; + } + update = false; + } + } + else + { + LethalPressure += 1.0f; + } + + + } + + public override void Draw(SpriteBatch spriteBatch, bool editing) + { + if (!editing && !DebugDraw) return; + + GUI.DrawRectangle(spriteBatch, + new Vector2(rect.X, -rect.Y), + new Vector2(rect.Width, rect.Height), + isHighlighted ? Color.Green : Color.Blue); + + GUI.DrawRectangle(spriteBatch, + new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), + Color.Red*((100.0f-OxygenPercentage)/400.0f), true); + + spriteBatch.DrawString(GUI.font, "Pressure: " + ((int)pressure - rect.Y).ToString() + + " - Lethality: " + lethalPressure + + " - Oxygen: "+((int)OxygenPercentage).ToString(), new Vector2(rect.X+10, -rect.Y+10), Color.Black); + spriteBatch.DrawString(GUI.font, volume.ToString() +" / "+ FullVolume.ToString(), new Vector2(rect.X+10, -rect.Y+30), Color.Black); + + if (isSelected && editing) + { + GUI.DrawRectangle(spriteBatch, + new Vector2(rect.X - 5, -rect.Y - 5), + new Vector2(rect.Width + 10, rect.Height + 10), + Color.Red); + } + } + + public void Render(GraphicsDevice graphicsDevice, Camera cam) + { + if (renderer.positionInBuffer > renderer.vertices.Length - 6) return; + + //calculate where the surface should be based on the water volume + float top = rect.Y; + float bottom = rect.Y - rect.Height; + float surfaceY = bottom + Volume / rect.Width; + + //interpolate the position of the rendered surface towards the "target surface" + surface = surface + (surfaceY - surface) / 10.0f; + + Matrix transform = + cam.Transform + * Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1) * 0.5f; + + if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) return; + + if (!update) + { + // create the four corners of our triangle. + Vector3 p1 = new Vector3(rect.X, top, 0.0f); + Vector3 p2 = new Vector3(rect.X + rect.Width, top, 0.0f); + + Vector3 p3 = new Vector3(p2.X, bottom, 0.0f); + Vector3 p4 = new Vector3(p1.X, bottom, 0.0f); + + renderer.vertices[renderer.positionInBuffer] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform); + renderer.vertices[renderer.positionInBuffer + 1] = new WaterVertex(p2, new Vector2(p2.X, -p2.Y), transform); + renderer.vertices[renderer.positionInBuffer + 2] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform); + + renderer.vertices[renderer.positionInBuffer + 3] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform); + renderer.vertices[renderer.positionInBuffer + 4] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform); + renderer.vertices[renderer.positionInBuffer + 5] = new WaterVertex(p4, new Vector2(p4.X, -p4.Y), transform); + + renderer.positionInBuffer += 6; + + return; + } + + int x = rect.X; + int start = (int)Math.Floor((float)(cam.WorldView.X - x) / WaveWidth); + start = Math.Max(start, 0); + + int end = (waveY.Length - 1) + - (int)Math.Floor((float)((x + rect.Width) - (cam.WorldView.X + cam.WorldView.Width)) / WaveWidth); + end = Math.Min(end, waveY.Length - 1); + + x += start * WaveWidth; + + for (int i = start; i < end; i++) + { + if (renderer.positionInBuffer > renderer.vertices.Length - 6) return; + + Vector3 p1 = new Vector3(x, top, 0.0f); + Vector3 p4 = new Vector3(p1.X, surface + waveY[i], 0.0f); + + //skip adjacent "water rects" if the surface of the water is roughly at the same position + int width = WaveWidth; + while (i list = new List(); - static BasicEffect drawingEffect; - - private VertexPositionColor[] vertices; - private short[] indices; - int primitiveCount; - - bool[] backFacing; - VertexPositionColor[] shadowVertices; - - public bool Enabled - { - get; - set; - } - - //private Vector2 position = Vector2.Zero; - //public Vector2 Position - //{ - // get { return position; } - // set { position = value; } - //} - - public ConvexHull(Vector2[] points, Color color) - { - int vertexCount = points.Length; - vertices = new VertexPositionColor[vertexCount + 1]; - Vector2 center = Vector2.Zero; - - for (int i = 0; i < vertexCount; i++) - { - vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), color); - center += points[i]; - } - center /= points.Length; - vertices[vertexCount] = new VertexPositionColor(new Vector3(center, 0), color); - - primitiveCount = points.Length; - indices = new short[primitiveCount * 3]; - - for (int i = 0; i < primitiveCount; i++) - { - indices[3 * i] = (short)i; - indices[3 * i + 1] = (short)((i + 1) % vertexCount); - indices[3 * i + 2] = (short)vertexCount; - } - backFacing = new bool[vertexCount]; - - - Enabled = true; - - list.Add(this); - } - - public void Move(Vector2 amount) - { - for (int i = 0; i < vertices.Count(); i++) - { - vertices[i].Position = new Vector3(vertices[i].Position.X + amount.X, vertices[i].Position.Y + amount.Y, vertices[i].Position.Z); - } - } - - public void SetVertices(Vector2[] points) - { - int vertexCount = points.Length; - vertices = new VertexPositionColor[vertexCount + 1]; - - for (int i = 0; i < vertexCount; i++) - { - vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), Color.Black); - } - } - - //public void Draw(GameTime gameTime) - //{ - // device.RasterizerState = RasterizerState.CullNone; - // device.BlendState = BlendState.Opaque; - - // drawingEffect.World = Matrix.CreateTranslation(position.X, position.Y, 0); - - // foreach (EffectPass pass in drawingEffect.CurrentTechnique.Passes) - // { - // pass.Apply(); - // device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, primitiveCount); - // } - //} - - public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos) - { - if (!Enabled) return; - - if (drawingEffect == null) - { - drawingEffect = new BasicEffect(graphicsDevice); - drawingEffect.VertexColorEnabled = true; - } - - //compute facing of each edge, using N*L - for (int i = 0; i < primitiveCount; i++) - { - Vector2 firstVertex = new Vector2(vertices[i].Position.X, vertices[i].Position.Y); - int secondIndex = (i + 1) % primitiveCount; - Vector2 secondVertex = new Vector2(vertices[secondIndex].Position.X, vertices[secondIndex].Position.Y); - Vector2 middle = (firstVertex + secondVertex) / 2; - - Vector2 L = lightSourcePos - middle; - - Vector2 N = new Vector2(); - N.X = -(secondVertex.Y - firstVertex.Y); - N.Y = secondVertex.X - firstVertex.X; - - backFacing[i] = (Vector2.Dot(N, L) < 0); - } - - //find beginning and ending vertices which - //belong to the shadow - int startingIndex = 0; - int endingIndex = 0; - for (int i = 0; i < primitiveCount; i++) - { - int currentEdge = i; - int nextEdge = (i + 1) % primitiveCount; - - if (backFacing[currentEdge] && !backFacing[nextEdge]) - endingIndex = nextEdge; - - if (!backFacing[currentEdge] && backFacing[nextEdge]) - startingIndex = nextEdge; - } - - int shadowVertexCount; - - //nr of vertices that are in the shadow - if (endingIndex > startingIndex) - shadowVertexCount = endingIndex - startingIndex + 1; - else - shadowVertexCount = primitiveCount + 1 - startingIndex + endingIndex; - - shadowVertices = new VertexPositionColor[shadowVertexCount * 2]; - - //create a triangle strip that has the shape of the shadow - int currentIndex = startingIndex; - int svCount = 0; - while (svCount != shadowVertexCount * 2) - { - Vector3 vertexPos = vertices[currentIndex].Position; - - //one vertex on the hull - shadowVertices[svCount] = new VertexPositionColor(); - shadowVertices[svCount].Color = Color.Black; - shadowVertices[svCount].Position = vertexPos; - - //one extruded by the light direction - shadowVertices[svCount + 1] = new VertexPositionColor(); - shadowVertices[svCount + 1].Color = Color.Black; - Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0); - L2P.Normalize(); - shadowVertices[svCount + 1].Position = new Vector3(lightSourcePos, 0) + L2P * 9000; - - svCount += 2; - currentIndex = (currentIndex + 1) % primitiveCount; - } - - drawingEffect.World = cam.ShaderTransform - * Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1) * 0.5f; - drawingEffect.CurrentTechnique.Passes[0].Apply(); - - graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2); - - } - - public void Remove() - { - list.Remove(this); - } - - - } - -} +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Subsurface.Lights +{ + class ConvexHull + { + public static List list = new List(); + static BasicEffect drawingEffect; + + private VertexPositionColor[] vertices; + private short[] indices; + int primitiveCount; + + bool[] backFacing; + VertexPositionColor[] shadowVertices; + + public bool Enabled + { + get; + set; + } + + public ConvexHull(Vector2[] points, Color color) + { + int vertexCount = points.Length; + vertices = new VertexPositionColor[vertexCount + 1]; + Vector2 center = Vector2.Zero; + + for (int i = 0; i < vertexCount; i++) + { + vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), color); + center += points[i]; + } + center /= points.Length; + vertices[vertexCount] = new VertexPositionColor(new Vector3(center, 0), color); + + primitiveCount = points.Length; + indices = new short[primitiveCount * 3]; + + for (int i = 0; i < primitiveCount; i++) + { + indices[3 * i] = (short)i; + indices[3 * i + 1] = (short)((i + 1) % vertexCount); + indices[3 * i + 2] = (short)vertexCount; + } + backFacing = new bool[vertexCount]; + + Enabled = true; + + list.Add(this); + } + + public void Move(Vector2 amount) + { + for (int i = 0; i < vertices.Count(); i++) + { + vertices[i].Position = new Vector3(vertices[i].Position.X + amount.X, vertices[i].Position.Y + amount.Y, vertices[i].Position.Z); + } + } + + public void SetVertices(Vector2[] points) + { + int vertexCount = points.Length; + vertices = new VertexPositionColor[vertexCount + 1]; + + for (int i = 0; i < vertexCount; i++) + { + vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), Color.Black); + } + } + + //public void Draw(GameTime gameTime) + //{ + // device.RasterizerState = RasterizerState.CullNone; + // device.BlendState = BlendState.Opaque; + + // drawingEffect.World = Matrix.CreateTranslation(position.X, position.Y, 0); + + // foreach (EffectPass pass in drawingEffect.CurrentTechnique.Passes) + // { + // pass.Apply(); + // device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, primitiveCount); + // } + //} + + public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos) + { + if (!Enabled) return; + + if (drawingEffect == null) + { + drawingEffect = new BasicEffect(graphicsDevice); + drawingEffect.VertexColorEnabled = true; + } + + //compute facing of each edge, using N*L + for (int i = 0; i < primitiveCount; i++) + { + Vector2 firstVertex = new Vector2(vertices[i].Position.X, vertices[i].Position.Y); + int secondIndex = (i + 1) % primitiveCount; + Vector2 secondVertex = new Vector2(vertices[secondIndex].Position.X, vertices[secondIndex].Position.Y); + Vector2 middle = (firstVertex + secondVertex) / 2; + + Vector2 L = lightSourcePos - middle; + + Vector2 N = new Vector2(); + N.X = -(secondVertex.Y - firstVertex.Y); + N.Y = secondVertex.X - firstVertex.X; + + backFacing[i] = (Vector2.Dot(N, L) < 0); + } + + //find beginning and ending vertices which + //belong to the shadow + int startingIndex = 0; + int endingIndex = 0; + for (int i = 0; i < primitiveCount; i++) + { + int currentEdge = i; + int nextEdge = (i + 1) % primitiveCount; + + if (backFacing[currentEdge] && !backFacing[nextEdge]) + endingIndex = nextEdge; + + if (!backFacing[currentEdge] && backFacing[nextEdge]) + startingIndex = nextEdge; + } + + int shadowVertexCount; + + //nr of vertices that are in the shadow + if (endingIndex > startingIndex) + shadowVertexCount = endingIndex - startingIndex + 1; + else + shadowVertexCount = primitiveCount + 1 - startingIndex + endingIndex; + + shadowVertices = new VertexPositionColor[shadowVertexCount * 2]; + + //create a triangle strip that has the shape of the shadow + int currentIndex = startingIndex; + int svCount = 0; + while (svCount != shadowVertexCount * 2) + { + Vector3 vertexPos = vertices[currentIndex].Position; + + //one vertex on the hull + shadowVertices[svCount] = new VertexPositionColor(); + shadowVertices[svCount].Color = Color.Black; + shadowVertices[svCount].Position = vertexPos; + + //one extruded by the light direction + shadowVertices[svCount + 1] = new VertexPositionColor(); + shadowVertices[svCount + 1].Color = Color.Black; + Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0); + L2P.Normalize(); + shadowVertices[svCount + 1].Position = new Vector3(lightSourcePos, 0) + L2P * 9000; + + svCount += 2; + currentIndex = (currentIndex + 1) % primitiveCount; + } + + drawingEffect.World = cam.ShaderTransform + * Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1) * 0.5f; + drawingEffect.CurrentTechnique.Passes[0].Apply(); + + graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2); + + } + + public void Remove() + { + list.Remove(this); + } + + + } + +} diff --git a/Subsurface/Map/Map.cs b/Subsurface/Map/Map.cs index f2c3deb42..e93d22697 100644 --- a/Subsurface/Map/Map.cs +++ b/Subsurface/Map/Map.cs @@ -1,381 +1,398 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Xml.Linq; -using FarseerPhysics; -using FarseerPhysics.Dynamics; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using FarseerPhysics.Collision; - -namespace Subsurface -{ - public enum Direction : byte - { - None = 0, Left = 1, Right = 2 - } - - static class Map - { - public static Vector2 gridSize = new Vector2(16.0f, 16.0f); - - private static Vector2 lastPickedPosition; - private static float lastPickedFraction; - - private static Rectangle borders; - - private static string filePath; - - public static Vector2 LastPickedPosition - { - get { return lastPickedPosition; } - } - - public static float LastPickedFraction - { - get { return lastPickedFraction; } - } - - public static Rectangle Borders - { - get { return borders; } - } - - public static string FilePath - { - get { return filePath; } - } - - public static void Draw(SpriteBatch spriteBatch, bool editing = false) - { - for (int i = 0; i < MapEntity.mapEntityList.Count(); i++ ) - { - MapEntity.mapEntityList[i].Draw(spriteBatch, editing); - } - } - - public static void DrawFront(SpriteBatch spriteBatch, bool editing = false) - { - for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) - { - if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth < 0.5f) - MapEntity.mapEntityList[i].Draw(spriteBatch, editing); - } - } - - public static void DrawBack(SpriteBatch spriteBatch, bool editing = false) - { - - for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) - { - if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth >= 0.5f) - MapEntity.mapEntityList[i].Draw(spriteBatch, editing); - } - } - - public static Vector2 MouseToWorldGrid(Camera cam) - { - Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); - position = cam.ScreenToWorld(position); - - return VectorToWorldGrid(position); - } - - public static Vector2 VectorToWorldGrid(Vector2 position) - { - position.X = (float)Math.Floor(Convert.ToDouble(position.X / gridSize.X)) * gridSize.X; - position.Y = (float)Math.Ceiling(Convert.ToDouble(position.Y / gridSize.Y)) * gridSize.Y; - - return position; - } - - - public static Rectangle AbsRect(Vector2 pos, Vector2 size) - { - if (size.X < 0.0f) - { - pos.X += size.X; - size.X = -size.X; - } - if (size.Y < 0.0f) - { - pos.Y -= size.Y; - size.Y = -size.Y; - } - - return new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y); - } - - public static bool RectContains(Rectangle rect, Vector2 pos) - { - return (pos.X > rect.X && pos.X < rect.X + rect.Width - && pos.Y < rect.Y && pos.Y > rect.Y - rect.Height); - } - - public static bool RectsOverlap(Rectangle rect1, Rectangle rect2) - { - return !(rect1.X > rect2.X + rect2.Width || rect1.X + rect1.Width < rect2.X || - rect1.Y < rect2.Y - rect2.Height || rect1.Y - rect1.Height > rect2.Y); - } - - public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, List ignoredBodies = null) - { - float closestFraction = 1.0f; - Body closestBody = null; - Game1.world.RayCast((fixture, point, normal, fraction) => - { - if (fixture == null || fixture.CollisionCategories == Category.None) return -1; - if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1; - - Structure structure = fixture.Body.UserData as Structure; - if (structure != null && (structure.IsPlatform || !structure.HasBody)) return -1; - - if (fraction < closestFraction) - { - closestFraction = fraction; - if (fixture.Body!=null) closestBody = fixture.Body; - } - return fraction; - } - , rayStart, rayEnd); - - lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; - lastPickedFraction = closestFraction; - return closestBody; - } - - - public static Structure CheckVisibility(Vector2 rayStart, Vector2 rayEnd) - { - Structure closestStructure = null; - float closestFraction = 1.0f; - - if (Vector2.Distance(rayStart,rayEnd)<0.01f) - { - closestFraction = 0.01f; - return null; - } - - Game1.world.RayCast((fixture, point, normal, fraction) => - { - if (fixture == null || fixture.CollisionCategories != Physics.CollisionWall) return -1; - - Structure structure = fixture.Body.UserData as Structure; - if (structure != null) - { - if (structure.IsPlatform || structure.StairDirection != Direction.None) return -1; - int sectionIndex = structure.FindSectionIndex(ConvertUnits.ToDisplayUnits(point)); - if (sectionIndex > -1 && structure.SectionHasHole(sectionIndex)) return -1; - } - - if (fraction < closestFraction) - { - if (structure != null) closestStructure = structure; - closestFraction = fraction; - } - return closestFraction; - } - , rayStart, rayEnd); - - - lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; - lastPickedFraction = closestFraction; - return closestStructure; - } - - public static Body PickBody(Vector2 point) - { - Body foundBody = null; - AABB aabb = new AABB(point, point); - - Game1.world.QueryAABB(p => - { - foundBody = p.Body; - - return true; - - }, ref aabb); - - return foundBody; - } - - public static bool InsideWall(Vector2 point) - { - Body foundBody = Map.PickBody(point); - if (foundBody==null) return false; - - Structure wall = foundBody.UserData as Structure; - if (wall == null || wall.IsPlatform) return false; - - return true; - } - - public static void Save(string filePath, string fileName) - { - if (fileName==null) - { - DebugConsole.ThrowError("No save file selected"); - return; - } - XDocument doc = new XDocument( - new XElement((XName)fileName)); - - foreach (MapEntity e in MapEntity.mapEntityList) - { - e.Save(doc); - } - - try - { - string docString = doc.ToString(); - ToolBox.CompressStringToFile(filePath+fileName+".gz", doc.ToString()); - } - catch - { - DebugConsole.ThrowError("Saving map ''" + filePath + fileName + "'' failed!"); - } - - - //doc.Save(filePath + fileName); - } - - public static string[] GetMapFilePaths() - { - string[] mapFilePaths; - try - { - mapFilePaths = Directory.GetFiles("Content/SavedMaps"); - } - catch (Exception e) - { - DebugConsole.ThrowError("Couldn't open directory ''Content/SavedMaps''!", e); - return null; - } - - return mapFilePaths; - } - - public static void Load(string filePath, string fileName) - { - Load(filePath + fileName); - } - - public static void Load(string file) - { - Clear(); - filePath = file; - XDocument doc = null; - - string extension = Path.GetExtension(file); - - if (extension==".gz") - { - Stream stream = ToolBox.DecompressFiletoStream(file); - if (stream == null) - { - DebugConsole.ThrowError("Loading map ''" + file + "'' failed!"); - return; - } - - try - { - stream.Position = 0; - doc = XDocument.Load(stream); //ToolBox.TryLoadXml(file); - stream.Close(); - stream.Dispose(); - } - - catch - { - DebugConsole.ThrowError("Loading map ''" + file + "'' failed!"); - return; - } - } - else if (extension ==".xml") - { - doc = XDocument.Load(file); - } - else - { - DebugConsole.ThrowError("Couldn't load map ''"+file+"! (Unrecognized file extension)"); - return; - } - - - foreach (XElement element in doc.Root.Elements()) - { - string typeName = element.Name.ToString(); - - Type t; - try - { - // Get the type of a specified class. - t = Type.GetType("Subsurface." + typeName + ", Subsurface", true, true); - if (t == null) - { - DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''."); - continue; - } - } - catch (Exception e) - { - DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''.", e); - continue; - } - - try - { - MethodInfo loadMethod = t.GetMethod("Load"); - loadMethod.Invoke(t, new object[] { element }); - } - catch (Exception e) - { - DebugConsole.ThrowError("Could not find the method ''Load'' in " + t + ".", e); - } - - } - - borders = new Rectangle(0, 0, 1, 1); - foreach (Hull hull in Hull.hullList) - { - if (hull.Rect.X < borders.X || borders.X == 0) borders.X = hull.Rect.X; - if (hull.Rect.Y > borders.Y || borders.Y == 0) borders.Y = hull.Rect.Y; - - if (hull.Rect.X + hull.Rect.Width > borders.X + borders.Width) borders.Width = hull.Rect.X + hull.Rect.Width - borders.X; - if (hull.Rect.Y - hull.Rect.Height < borders.Y - borders.Height) borders.Height = borders.Y - (hull.Rect.Y - hull.Rect.Height); - } - - MapEntity.LinkAll(); - foreach (Item item in Item.itemList) - { - foreach (ItemComponent ic in item.components) - { - ic.OnMapLoaded(); - } - } - } - - - - public static void Clear() - { - Map.filePath = ""; - - if (Game1.gameScreen.Cam != null) Game1.gameScreen.Cam.TargetPos = Vector2.Zero; - - Entity.RemoveAll(); - - if (Game1.gameSession!=null) - Game1.gameSession.crewManager.EndShift(); - - PhysicsBody.list.Clear(); - - Ragdoll.list.Clear(); - - Game1.world.Clear(); - } - - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using FarseerPhysics.Collision; + +namespace Subsurface +{ + public enum Direction : byte + { + None = 0, Left = 1, Right = 2 + } + + static class Map + { + static string MapFolder = "Content/SavedMaps"; + + public static Vector2 gridSize = new Vector2(16.0f, 16.0f); + + private static Vector2 lastPickedPosition; + private static float lastPickedFraction; + + private static Rectangle borders; + + private static string filePath; + + public static Vector2 LastPickedPosition + { + get { return lastPickedPosition; } + } + + public static float LastPickedFraction + { + get { return lastPickedFraction; } + } + + public static Rectangle Borders + { + get { return borders; } + } + + public static string FilePath + { + get { return filePath; } + } + + public static void Draw(SpriteBatch spriteBatch, bool editing = false) + { + for (int i = 0; i < MapEntity.mapEntityList.Count(); i++ ) + { + MapEntity.mapEntityList[i].Draw(spriteBatch, editing); + } + } + + public static void DrawFront(SpriteBatch spriteBatch, bool editing = false) + { + for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) + { + if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth < 0.5f) + MapEntity.mapEntityList[i].Draw(spriteBatch, editing); + } + } + + public static void DrawBack(SpriteBatch spriteBatch, bool editing = false) + { + + for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) + { + if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth >= 0.5f) + MapEntity.mapEntityList[i].Draw(spriteBatch, editing); + } + } + + public static Vector2 MouseToWorldGrid(Camera cam) + { + Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); + position = cam.ScreenToWorld(position); + + return VectorToWorldGrid(position); + } + + public static Vector2 VectorToWorldGrid(Vector2 position) + { + position.X = (float)Math.Floor(Convert.ToDouble(position.X / gridSize.X)) * gridSize.X; + position.Y = (float)Math.Ceiling(Convert.ToDouble(position.Y / gridSize.Y)) * gridSize.Y; + + return position; + } + + + public static Rectangle AbsRect(Vector2 pos, Vector2 size) + { + if (size.X < 0.0f) + { + pos.X += size.X; + size.X = -size.X; + } + if (size.Y < 0.0f) + { + pos.Y -= size.Y; + size.Y = -size.Y; + } + + return new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y); + } + + public static bool RectContains(Rectangle rect, Vector2 pos) + { + return (pos.X > rect.X && pos.X < rect.X + rect.Width + && pos.Y < rect.Y && pos.Y > rect.Y - rect.Height); + } + + public static bool RectsOverlap(Rectangle rect1, Rectangle rect2) + { + return !(rect1.X > rect2.X + rect2.Width || rect1.X + rect1.Width < rect2.X || + rect1.Y < rect2.Y - rect2.Height || rect1.Y - rect1.Height > rect2.Y); + } + + public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, List ignoredBodies = null) + { + float closestFraction = 1.0f; + Body closestBody = null; + Game1.world.RayCast((fixture, point, normal, fraction) => + { + if (fixture == null || fixture.CollisionCategories == Category.None) return -1; + if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1; + + Structure structure = fixture.Body.UserData as Structure; + if (structure != null && (structure.IsPlatform || !structure.HasBody)) return -1; + + if (fraction < closestFraction) + { + closestFraction = fraction; + if (fixture.Body!=null) closestBody = fixture.Body; + } + return fraction; + } + , rayStart, rayEnd); + + lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; + lastPickedFraction = closestFraction; + return closestBody; + } + + + public static Structure CheckVisibility(Vector2 rayStart, Vector2 rayEnd) + { + Structure closestStructure = null; + float closestFraction = 1.0f; + + if (Vector2.Distance(rayStart,rayEnd)<0.01f) + { + closestFraction = 0.01f; + return null; + } + + Game1.world.RayCast((fixture, point, normal, fraction) => + { + if (fixture == null || fixture.CollisionCategories != Physics.CollisionWall) return -1; + + Structure structure = fixture.Body.UserData as Structure; + if (structure != null) + { + if (structure.IsPlatform || structure.StairDirection != Direction.None) return -1; + int sectionIndex = structure.FindSectionIndex(ConvertUnits.ToDisplayUnits(point)); + if (sectionIndex > -1 && structure.SectionHasHole(sectionIndex)) return -1; + } + + if (fraction < closestFraction) + { + if (structure != null) closestStructure = structure; + closestFraction = fraction; + } + return closestFraction; + } + , rayStart, rayEnd); + + + lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction; + lastPickedFraction = closestFraction; + return closestStructure; + } + + public static Body PickBody(Vector2 point) + { + Body foundBody = null; + AABB aabb = new AABB(point, point); + + Game1.world.QueryAABB(p => + { + foundBody = p.Body; + + return true; + + }, ref aabb); + + return foundBody; + } + + public static bool InsideWall(Vector2 point) + { + Body foundBody = Map.PickBody(point); + if (foundBody==null) return false; + + Structure wall = foundBody.UserData as Structure; + if (wall == null || wall.IsPlatform) return false; + + return true; + } + + public static void Save(string filePath, string fileName) + { + if (fileName==null) + { + DebugConsole.ThrowError("No save file selected"); + return; + } + XDocument doc = new XDocument( + new XElement((XName)fileName)); + + foreach (MapEntity e in MapEntity.mapEntityList) + { + e.Save(doc); + } + + try + { + string docString = doc.ToString(); + ToolBox.CompressStringToFile(filePath+fileName+".gz", doc.ToString()); + } + catch + { + DebugConsole.ThrowError("Saving map ''" + filePath + fileName + "'' failed!"); + } + + + //doc.Save(filePath + fileName); + } + + public static string[] GetMapFilePaths() + { + string[] mapFilePaths; + + if (!Directory.Exists(MapFolder)) + { + try + { + Directory.CreateDirectory(MapFolder); + } + catch + { + + DebugConsole.ThrowError("Directory ''Content/SavedMaps'' not found and creating the directory failed."); + return null; + } + } + + try + { + mapFilePaths = Directory.GetFiles(MapFolder); + } + catch (Exception e) + { + DebugConsole.ThrowError("Couldn't open directory ''Content/SavedMaps''!", e); + return null; + } + + return mapFilePaths; + } + + public static void Load(string filePath, string fileName) + { + Load(filePath + fileName); + } + + public static void Load(string file) + { + Clear(); + filePath = file; + XDocument doc = null; + + string extension = Path.GetExtension(file); + + if (extension==".gz") + { + Stream stream = ToolBox.DecompressFiletoStream(file); + if (stream == null) + { + DebugConsole.ThrowError("Loading map ''" + file + "'' failed!"); + return; + } + + try + { + stream.Position = 0; + doc = XDocument.Load(stream); //ToolBox.TryLoadXml(file); + stream.Close(); + stream.Dispose(); + } + + catch + { + DebugConsole.ThrowError("Loading map ''" + file + "'' failed!"); + return; + } + } + else if (extension ==".xml") + { + doc = XDocument.Load(file); + } + else + { + DebugConsole.ThrowError("Couldn't load map ''"+file+"! (Unrecognized file extension)"); + return; + } + + + foreach (XElement element in doc.Root.Elements()) + { + string typeName = element.Name.ToString(); + + Type t; + try + { + // Get the type of a specified class. + t = Type.GetType("Subsurface." + typeName + ", Subsurface", true, true); + if (t == null) + { + DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''."); + continue; + } + } + catch (Exception e) + { + DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''.", e); + continue; + } + + try + { + MethodInfo loadMethod = t.GetMethod("Load"); + loadMethod.Invoke(t, new object[] { element }); + } + catch (Exception e) + { + DebugConsole.ThrowError("Could not find the method ''Load'' in " + t + ".", e); + } + + } + + borders = new Rectangle(0, 0, 1, 1); + foreach (Hull hull in Hull.hullList) + { + if (hull.Rect.X < borders.X || borders.X == 0) borders.X = hull.Rect.X; + if (hull.Rect.Y > borders.Y || borders.Y == 0) borders.Y = hull.Rect.Y; + + if (hull.Rect.X + hull.Rect.Width > borders.X + borders.Width) borders.Width = hull.Rect.X + hull.Rect.Width - borders.X; + if (hull.Rect.Y - hull.Rect.Height < borders.Y - borders.Height) borders.Height = borders.Y - (hull.Rect.Y - hull.Rect.Height); + } + + MapEntity.LinkAll(); + foreach (Item item in Item.itemList) + { + foreach (ItemComponent ic in item.components) + { + ic.OnMapLoaded(); + } + } + } + + + + public static void Clear() + { + Map.filePath = ""; + + if (Game1.gameScreen.Cam != null) Game1.gameScreen.Cam.TargetPos = Vector2.Zero; + + Entity.RemoveAll(); + + if (Game1.gameSession!=null) + Game1.gameSession.crewManager.EndShift(); + + PhysicsBody.list.Clear(); + + Ragdoll.list.Clear(); + + Game1.world.Clear(); + } + + } +} diff --git a/Subsurface/Screens/EditMapScreen.cs b/Subsurface/Screens/EditMapScreen.cs index 1d8cef236..d6b9f4593 100644 --- a/Subsurface/Screens/EditMapScreen.cs +++ b/Subsurface/Screens/EditMapScreen.cs @@ -1,291 +1,291 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using System; - -namespace Subsurface -{ - class EditMapScreen : Screen - { - Camera cam; - - GUIComponent GUIpanel; - - GUIComponent[] GUItabs; - int selectedTab; - - //a character used for picking up and manipulating items - Character dummyCharacter; - - bool characterMode; - - public Camera Cam - { - get { return cam; } - } - - public EditMapScreen() - { - cam = new Camera(); - cam.Translate(new Vector2(-10.0f, 50.0f)); - - selectedTab = -1; - - GUIpanel = new GUIFrame(new Rectangle(0,0, 120, Game1.GraphicsHeight), Color.DarkGray*0.8f); - GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); - //GUIListBox constructionList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUIpanel); - //constructionList.OnSelected = MapEntityPrefab.SelectPrefab; - //constructionList.CheckSelected = MapEntityPrefab.GetSelected; - - GUIButton button = new GUIButton(new Rectangle(0, 50, 100, 20), "Items", GUI.style, Alignment.Left, GUIpanel); - button.UserData = 0; - button.OnClicked = SelectTab; - - button = new GUIButton(new Rectangle(0, 80, 100, 20), "Structures", GUI.style, Alignment.Left, GUIpanel); - button.UserData = 1; - button.OnClicked = SelectTab; - - button = new GUIButton(new Rectangle(0, 140, 100, 20), "Character mode", GUI.style, Alignment.Left, GUIpanel); - button.OnClicked = ToggleCharacterMode; - - GUItabs = new GUIComponent[2]; - int width = 400, height = 400; - GUItabs[0] = new GUIFrame(new Rectangle(Game1.GraphicsWidth/2-width/2, Game1.GraphicsHeight/2-height/2, width, height), Color.DarkGray*0.8f); - GUItabs[0].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); - GUIListBox itemList = new GUIListBox(new Rectangle(0, 0, 0, 0), Color.White * 0.7f, GUItabs[0]); - itemList.OnSelected = SelectPrefab; - itemList.CheckSelected = MapEntityPrefab.GetSelected; - - GUItabs[1] = new GUIFrame(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, Game1.GraphicsHeight / 2 - height / 2, width, height), Color.DarkGray * 0.8f); - GUItabs[1].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); - GUIListBox structureList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUItabs[1]); - structureList.OnSelected = SelectPrefab; - structureList.CheckSelected = MapEntityPrefab.GetSelected; - - foreach (MapEntityPrefab ep in MapEntityPrefab.list) - { - GUIListBox parent = ((ep as ItemPrefab) == null) ? structureList : itemList; - Color color = ((parent.CountChildren % 2) == 0) ? Color.White : Color.LightGray; - - GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, parent); - frame.UserData = ep; - frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); - frame.Color = color; - frame.HoverColor = Color.Gold * 0.2f; - frame.SelectedColor = Color.Gold * 0.5f; - - GUITextBlock textBlock = new GUITextBlock( - new Rectangle(40, 0, 0, 25), - ep.Name, - Color.Transparent, Color.Black, - Alignment.Left, - Alignment.Left, - frame); - textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); - - if (ep.sprite != null) - { - GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), ep.sprite, Alignment.Left, frame); - img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); - } - } - } - - public override void Select() - { - base.Select(); - - GUIComponent.MouseOn = null; - characterMode = false; - //CreateDummyCharacter(); - } - - public override void Deselect() - { - base.Deselect(); - - GUIComponent.MouseOn = null; - - if (dummyCharacter != null) - { - dummyCharacter.Remove(); - dummyCharacter = null; - Game1.world.ProcessChanges(); - } - } - - private void CreateDummyCharacter() - { - if (dummyCharacter != null) dummyCharacter.Remove(); - - dummyCharacter = new Character("Content/Characters/Human/human.xml", Vector2.Zero); - Character.Controlled = dummyCharacter; - Game1.world.ProcessChanges(); - } - - private bool SelectTab(GUIButton button, object obj) - { - selectedTab = (int)obj; - return true; - } - - private bool ToggleCharacterMode(GUIButton button, object obj) - { - characterMode = !characterMode; - button.Color = (characterMode) ? Color.Gold : Color.White; - - if (characterMode) - { - CreateDummyCharacter(); - } - else if (dummyCharacter != null) - { - dummyCharacter.Remove(); - dummyCharacter = null; - } - - foreach (MapEntity me in MapEntity.mapEntityList) - { - me.IsHighlighted = false; - me.IsSelected = false; - } - - return true; - } - - private bool SelectPrefab(object obj) - { - MapEntityPrefab.SelectPrefab(obj); - selectedTab = -1; - GUIComponent.MouseOn = null; - return true; - } - - - /// - /// Allows the game to run logic such as updating the world, - /// checking for collisions, gathering input, and playing audio. - /// - /// Provides a snapshot of timing values. - public override void Update(double deltaTime) - { - //Vector2 mousePosition = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); - //mousePosition = cam.ScreenToWorld(mousePosition); - - //if (!Character.characterList.Contains(dummyCharacter)) - //{ - // CreateDummyCharacter(); - //} - - cam.MoveCamera((float)deltaTime); - cam.Zoom = MathHelper.Clamp(cam.Zoom + PlayerInput.ScrollWheelSpeed/1000.0f,0.1f, 2.0f); - - if (characterMode) - { - foreach (MapEntity me in MapEntity.mapEntityList) - { - me.IsHighlighted = false; - } - - if (dummyCharacter.SelectedConstruction==null) - { - Vector2 mouseSimPos = FarseerPhysics.ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); - foreach (Limb limb in dummyCharacter.animController.limbs) - { - limb.body.SetTransform(mouseSimPos, 0.0f); - } - } - - dummyCharacter.ControlLocalPlayer(cam, false); - dummyCharacter.Control(cam); - } - else - { - - MapEntity.UpdateSelecting(cam); - } - - - GUIpanel.Update((float)deltaTime); - if (selectedTab > -1) - { - GUItabs[selectedTab].Update((float)deltaTime); - if (PlayerInput.RightButtonClicked()) selectedTab = -1; - } - } - - /// - /// This is called when the game should draw itself. - /// - /// Provides a snapshot of timing values. - public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) - { - //cam.UpdateTransform(); - - spriteBatch.Begin(SpriteSortMode.BackToFront, - BlendState.AlphaBlend, - null, null, null, null, - cam.Transform); - - graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f)); - - Map.Draw(spriteBatch, true); - - if (!characterMode) - { - if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); - - MapEntity.DrawSelecting(spriteBatch, cam); - } - - spriteBatch.End(); - - //-------------------- HUD ----------------------------- - - spriteBatch.Begin(); - - GUIpanel.Draw(spriteBatch); - - if (selectedTab > -1) GUItabs[selectedTab].Draw(spriteBatch); - - GUI.Draw((float)deltaTime, spriteBatch); - - //EntityPrefab.DrawList(spriteBatch, new Vector2(20,50)); - - - - if (characterMode) - { - if (dummyCharacter != null && dummyCharacter.SelectedConstruction != null) - { - if (Character.Controlled.SelectedConstruction == dummyCharacter.ClosestItem) - { - dummyCharacter.SelectedConstruction.DrawHUD(spriteBatch, dummyCharacter); - } - else - { - dummyCharacter.SelectedConstruction = null; - } - } - - if (PlayerInput.GetMouseState.LeftButton != ButtonState.Pressed) - { - //if (Inventory.draggingItem!=null) - //{ - // Inventory.draggingItem.see - //} - Inventory.draggingItem = null; - } - } - else - { - MapEntity.Edit(spriteBatch, cam); - } - - - - spriteBatch.End(); - - } - } -} +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System; + +namespace Subsurface +{ + class EditMapScreen : Screen + { + Camera cam; + + GUIComponent GUIpanel; + + GUIComponent[] GUItabs; + int selectedTab; + + //a character used for picking up and manipulating items + Character dummyCharacter; + + bool characterMode; + + public Camera Cam + { + get { return cam; } + } + + public EditMapScreen() + { + cam = new Camera(); + cam.Translate(new Vector2(-10.0f, 50.0f)); + + selectedTab = -1; + + GUIpanel = new GUIFrame(new Rectangle(0,0, 120, Game1.GraphicsHeight), Color.DarkGray*0.8f); + GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + //GUIListBox constructionList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUIpanel); + //constructionList.OnSelected = MapEntityPrefab.SelectPrefab; + //constructionList.CheckSelected = MapEntityPrefab.GetSelected; + + GUIButton button = new GUIButton(new Rectangle(0, 50, 100, 20), "Items", GUI.style, Alignment.Left, GUIpanel); + button.UserData = 0; + button.OnClicked = SelectTab; + + button = new GUIButton(new Rectangle(0, 80, 100, 20), "Structures", GUI.style, Alignment.Left, GUIpanel); + button.UserData = 1; + button.OnClicked = SelectTab; + + button = new GUIButton(new Rectangle(0, 140, 100, 20), "Character mode", GUI.style, Alignment.Left, GUIpanel); + button.OnClicked = ToggleCharacterMode; + + GUItabs = new GUIComponent[2]; + int width = 400, height = 400; + GUItabs[0] = new GUIFrame(new Rectangle(Game1.GraphicsWidth/2-width/2, Game1.GraphicsHeight/2-height/2, width, height), Color.DarkGray*0.8f); + GUItabs[0].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + GUIListBox itemList = new GUIListBox(new Rectangle(0, 0, 0, 0), Color.White * 0.7f, GUItabs[0]); + itemList.OnSelected = SelectPrefab; + itemList.CheckSelected = MapEntityPrefab.GetSelected; + + GUItabs[1] = new GUIFrame(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, Game1.GraphicsHeight / 2 - height / 2, width, height), Color.DarkGray * 0.8f); + GUItabs[1].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + GUIListBox structureList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUItabs[1]); + structureList.OnSelected = SelectPrefab; + structureList.CheckSelected = MapEntityPrefab.GetSelected; + + foreach (MapEntityPrefab ep in MapEntityPrefab.list) + { + GUIListBox parent = ((ep as ItemPrefab) == null) ? structureList : itemList; + Color color = ((parent.CountChildren % 2) == 0) ? Color.White : Color.LightGray; + + GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, parent); + frame.UserData = ep; + frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + frame.Color = color; + frame.HoverColor = Color.Gold * 0.2f; + frame.SelectedColor = Color.Gold * 0.5f; + + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(40, 0, 0, 25), + ep.Name, + Color.Transparent, Color.Black, + Alignment.Left, + Alignment.Left, + frame); + textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + + if (ep.sprite != null) + { + GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), ep.sprite, Alignment.Left, frame); + img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); + } + } + } + + public override void Select() + { + base.Select(); + + GUIComponent.MouseOn = null; + characterMode = false; + //CreateDummyCharacter(); + } + + public override void Deselect() + { + base.Deselect(); + + GUIComponent.MouseOn = null; + + if (dummyCharacter != null) + { + dummyCharacter.Remove(); + dummyCharacter = null; + Game1.world.ProcessChanges(); + } + } + + private void CreateDummyCharacter() + { + if (dummyCharacter != null) dummyCharacter.Remove(); + + dummyCharacter = new Character("Content/Characters/Human/human.xml", Vector2.Zero); + Character.Controlled = dummyCharacter; + Game1.world.ProcessChanges(); + } + + private bool SelectTab(GUIButton button, object obj) + { + selectedTab = (int)obj; + return true; + } + + private bool ToggleCharacterMode(GUIButton button, object obj) + { + characterMode = !characterMode; + button.Color = (characterMode) ? Color.Gold : Color.White; + + if (characterMode) + { + CreateDummyCharacter(); + } + else if (dummyCharacter != null) + { + dummyCharacter.Remove(); + dummyCharacter = null; + } + + foreach (MapEntity me in MapEntity.mapEntityList) + { + me.IsHighlighted = false; + me.IsSelected = false; + } + + return true; + } + + private bool SelectPrefab(object obj) + { + MapEntityPrefab.SelectPrefab(obj); + selectedTab = -1; + GUIComponent.MouseOn = null; + return true; + } + + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + public override void Update(double deltaTime) + { + //Vector2 mousePosition = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); + //mousePosition = cam.ScreenToWorld(mousePosition); + + //if (!Character.characterList.Contains(dummyCharacter)) + //{ + // CreateDummyCharacter(); + //} + + cam.MoveCamera((float)deltaTime); + cam.Zoom = MathHelper.Clamp(cam.Zoom + PlayerInput.ScrollWheelSpeed/1000.0f,0.1f, 2.0f); + + if (characterMode) + { + foreach (MapEntity me in MapEntity.mapEntityList) + { + me.IsHighlighted = false; + } + + if (dummyCharacter.SelectedConstruction==null) + { + Vector2 mouseSimPos = FarseerPhysics.ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); + foreach (Limb limb in dummyCharacter.animController.limbs) + { + limb.body.SetTransform(mouseSimPos, 0.0f); + } + } + + dummyCharacter.ControlLocalPlayer(cam, false); + dummyCharacter.Control(cam); + } + else + { + + MapEntity.UpdateSelecting(cam); + } + + + GUIpanel.Update((float)deltaTime); + if (selectedTab > -1) + { + GUItabs[selectedTab].Update((float)deltaTime); + if (PlayerInput.RightButtonClicked()) selectedTab = -1; + } + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + //cam.UpdateTransform(); + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f)); + + Map.Draw(spriteBatch, true); + + if (!characterMode) + { + if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); + + MapEntity.DrawSelecting(spriteBatch, cam); + } + + spriteBatch.End(); + + //-------------------- HUD ----------------------------- + + spriteBatch.Begin(); + + GUIpanel.Draw(spriteBatch); + + if (selectedTab > -1) GUItabs[selectedTab].Draw(spriteBatch); + + GUI.Draw((float)deltaTime, spriteBatch); + + //EntityPrefab.DrawList(spriteBatch, new Vector2(20,50)); + + + + if (characterMode) + { + if (dummyCharacter != null && dummyCharacter.SelectedConstruction != null) + { + if (dummyCharacter.SelectedConstruction == dummyCharacter.ClosestItem) + { + dummyCharacter.SelectedConstruction.DrawHUD(spriteBatch, dummyCharacter); + } + else + { + dummyCharacter.SelectedConstruction = null; + } + } + + if (PlayerInput.GetMouseState.LeftButton != ButtonState.Pressed) + { + //if (Inventory.draggingItem!=null) + //{ + // Inventory.draggingItem.see + //} + Inventory.draggingItem = null; + } + } + else + { + MapEntity.Edit(spriteBatch, cam); + } + + + + spriteBatch.End(); + + } + } +} diff --git a/Subsurface/Subsurface.csproj.user b/Subsurface/Subsurface.csproj.user index 6e4c912e4..505c3a0bf 100644 --- a/Subsurface/Subsurface.csproj.user +++ b/Subsurface/Subsurface.csproj.user @@ -1,14 +1,14 @@ - - - - publish\ - - - - - - en-US - false - ShowAllFiles - + + + + publish\ + + + + + + en-US + false + ProjectFiles + \ No newline at end of file diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 6ce7aed9f..38fae000f 100644 Binary files a/Subsurface_Solution.v12.suo and b/Subsurface_Solution.v12.suo differ