From 94895edbdb10f1ee023f09445ebe6f2cca80600d Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Thu, 6 Oct 2016 21:15:01 -0300 Subject: [PATCH] Fixes and minor enhancements for sub editor - Zoom now works relative to the mouse's position rather than the center of the screen, much like Valve's Hammer Editor (or pulsegun's map editor shameless plug ;) ) - Rectangles now have line widths dependent on the camera zoom, not perfect but it's better than having lines disappear - Fixed background resizing - Fixed editor being completely broken because of no vsync, might still have a few bugs here and there - Fixed selection rectangle not rendering at all when dragging from bottom right to top left - Newline change in Item.cs --- Subsurface/Source/Camera.cs | 8 +- Subsurface/Source/GUI/GUI.cs | 24 +- Subsurface/Source/Items/Item.cs | 3809 ++++++++++---------- Subsurface/Source/Items/ItemPrefab.cs | 53 +- Subsurface/Source/Map/Gap.cs | 7 +- Subsurface/Source/Map/Hull.cs | 9 +- Subsurface/Source/Map/MapEntity.cs | 6 +- Subsurface/Source/Map/MapEntityPrefab.cs | 42 +- Subsurface/Source/Map/Structure.cs | 16 + Subsurface/Source/Map/StructurePrefab.cs | 40 +- Subsurface/Source/Screens/EditMapScreen.cs | 60 +- Subsurface/Source/Screens/Screen.cs | 5 + 12 files changed, 2116 insertions(+), 1963 deletions(-) diff --git a/Subsurface/Source/Camera.cs b/Subsurface/Source/Camera.cs index bd0e83628..67a5b2431 100644 --- a/Subsurface/Source/Camera.cs +++ b/Subsurface/Source/Camera.cs @@ -193,9 +193,13 @@ namespace Barotrauma } } - moveCam = moveCam * deltaTime * 60.0f; + moveCam = moveCam * deltaTime * 60.0f; - Zoom = MathHelper.Clamp(zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, GameMain.DebugDraw ? 0.01f : 0.1f, 2.0f); + Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition); + Vector2 diffViewCenter; + diffViewCenter = ((mouseInWorld - Position) * Zoom); + Zoom = MathHelper.Clamp(zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, GameMain.DebugDraw ? 0.01f : 0.1f, 2.0f); + Position = mouseInWorld - (diffViewCenter / Zoom); } else { diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index ee9d1bccb..5ec7a82d4 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -241,12 +241,22 @@ namespace Barotrauma sb.DrawString(font, text, pos, color); } - public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f) + public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) { - DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth); + if (size.X < 0) + { + start.X += size.X; + size.X = -size.X; + } + if (size.Y < 0) + { + start.Y += size.Y; + size.Y = -size.Y; + } + DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness); } - public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f) + public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) { if (isFilled) { @@ -254,10 +264,10 @@ namespace Barotrauma } else { - sb.Draw(t, new Rectangle(rect.X, rect.Y, rect.Width, 1), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - sb.Draw(t, new Rectangle(rect.X, rect.Y+rect.Height-1, rect.Width, 1), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - sb.Draw(t, new Rectangle(rect.X, rect.Y, 1, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - sb.Draw(t, new Rectangle(rect.X+rect.Width-1, rect.Y, 1, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); + sb.Draw(t, new Rectangle(rect.X, rect.Y, rect.Width, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); + sb.Draw(t, new Rectangle(rect.X, rect.Y+rect.Height- thickness, rect.Width, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); + sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); + sb.Draw(t, new Rectangle(rect.X+rect.Width- thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); } } diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index b8a89263b..5499f6d72 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1,1084 +1,1087 @@ -using Barotrauma.Items.Components; -using Barotrauma.Networking; -using FarseerPhysics; -using FarseerPhysics.Dynamics; -using FarseerPhysics.Dynamics.Contacts; -using Lidgren.Network; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Xml.Linq; - -namespace Barotrauma -{ - - public enum ActionType - { - Always, OnPicked, OnUse, OnSecondaryUse, - OnWearing, OnContaining, OnContained, - OnActive, OnFailure, OnBroken, - OnFire, InWater, - OnImpact - } - - class Item : MapEntity, IDamageable, IPropertyObject - { - public static List ItemList = new List(); - private ItemPrefab prefab; - - public static ItemSpawner Spawner = new ItemSpawner(); - public static ItemRemover Remover = new ItemRemover(); - - public static bool ShowLinks = true; - - private List tags; - - public Hull CurrentHull; - - public bool Visible = true; - - public SpriteEffects SpriteEffects = SpriteEffects.None; - - //components that determine the functionality of the item - public List components; - public List drawableComponents; - - public PhysicsBody body; - - private float condition; - - private bool inWater; - - private Inventory parentInventory; - - //a dictionary containing lists of the status effects in all the components of the item - private Dictionary> statusEffectLists; - - public readonly Dictionary properties; - public Dictionary ObjectProperties - { - get { return properties; } - } - - private bool? hasInGameEditableProperties; - bool HasInGameEditableProperties - { - get - { - if (hasInGameEditableProperties==null) - { - hasInGameEditableProperties = GetProperties().Any(); - } - return (bool)hasInGameEditableProperties; - } - } - - //the inventory in which the item is contained in - public Inventory ParentInventory - { - get - { - return parentInventory; - } - set - { - parentInventory = value; - - if (parentInventory != null) Container = parentInventory.Owner as Item; - } - } - - public Item Container - { - get; - private set; - } - - public override bool SelectableInEditor - { - get - { - return parentInventory == null && (body == null || body.Enabled); - } - } - - public List FixRequirements; - - public override string Name - { - get { return prefab.Name; } - } - - public string Description - { - get { return prefab.Description; } - } - - public float ImpactTolerance - { - get { return prefab.ImpactTolerance; } - } - - public override Sprite Sprite - { - get { return prefab.sprite; } - } - - public float PickDistance - { - get { return prefab.PickDistance; } - } - - public override Vector2 SimPosition - { - get - { - return (body==null) ? base.SimPosition : body.SimPosition; - } - } - - protected Color spriteColor; - [Editable, HasDefaultValue("1.0,1.0,1.0,1.0", true)] - public string SpriteColor - { - get { return ToolBox.Vector4ToString(spriteColor.ToVector4()); } - set - { - spriteColor = new Color(ToolBox.ParseToVector4(value)); - } - } - - public Color Color - { - get { return spriteColor; } - } - - public float Condition - { - get { return condition; } - set - { - if (!MathUtils.IsValid(value)) return; - - float prev = condition; - condition = MathHelper.Clamp(value, 0.0f, 100.0f); - if (condition == 0.0f && prev>0.0f) - { - new NetworkEvent(this.ID, false); - - ApplyStatusEffects(ActionType.OnBroken, 1.0f, null); - foreach (FixRequirement req in FixRequirements) - { - req.Fixed = false; - } - } - } - } - - public float Health - { - get { return condition; } - } - - [Editable, HasDefaultValue("", true)] - public string Tags - { - get { return string.Join(",",tags); } - set - { - tags.Clear(); - if (value == null) return; - - string[] newTags = value.Split(','); - foreach (string tag in newTags) - { - string newTag = tag.Trim(); - if (!tags.Contains(newTag)) tags.Add(newTag); - } - - } - } - - public bool FireProof - { - get { return prefab.FireProof; } - } - - public bool CanUseOnSelf - { - get { return prefab.CanUseOnSelf; } - } - - public bool InWater - { - get - { - //if the item has an active physics body, inWater is updated in the Update method - if (body != null && body.Enabled) return inWater; - - //if not, we'll just have to check - return IsInWater(); - } - } - - public ItemPrefab Prefab - { - get { return prefab; } - } - - public string ConfigFile - { - get { return prefab.ConfigFile; } - } - - public bool Removed - { - get; - private set; - } - - //which type of inventory slots (head, torso, any, etc) the item can be placed in - public List AllowedSlots - { - get - { - Pickable p = GetComponent(); - return (p==null) ? new List() { InvSlotType.Any } : p.AllowedSlots; - } - } - - public int Capacity - { - get - { - ItemContainer c = GetComponent(); - return (c == null) ? 0 : c.Capacity; - } - } - - public List Connections - { - get - { - ConnectionPanel panel = GetComponent(); - if (panel == null) return null; - return panel.Connections; - } - } - - public Item[] ContainedItems - { - get - { - ItemContainer c = GetComponent(); - return (c == null) ? null : Array.FindAll(c.Inventory.Items, i=>i!=null); - } - } - - public override bool IsLinkable - { - get { return prefab.IsLinkable; } - } - - public override string ToString() - { - return (GameMain.DebugDraw) ? Name + "(ID: " + ID + ")" : Name; - } - - public List AllPropertyObjects - { - get - { - List pobjects = new List(); - pobjects.Add(this); - foreach (ItemComponent ic in components) - { - pobjects.Add(ic); - } - return pobjects; - } - } - - public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine) - : this(new Rectangle( - (int)(position.X - itemPrefab.sprite.size.X / 2), - (int)(position.Y + itemPrefab.sprite.size.Y / 2), - (int)itemPrefab.sprite.size.X, - (int)itemPrefab.sprite.size.Y), - itemPrefab, submarine) - { - - } - - public Item(Rectangle newRect, ItemPrefab itemPrefab, Submarine submarine) - : base(itemPrefab, submarine) - { - prefab = itemPrefab; - - spriteColor = prefab.SpriteColor; - - linkedTo = new ObservableCollection(); - components = new List(); - drawableComponents = new List(); - FixRequirements = new List(); - tags = new List(); - - rect = newRect; - - if (submarine==null || !submarine.Loading) FindHull(); - - condition = 100.0f; - - XElement element = prefab.ConfigElement; - if (element == null) return; - - properties = ObjectProperty.InitProperties(this, element); - - foreach (XElement subElement in element.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "body": - body = new PhysicsBody(subElement, ConvertUnits.ToSimUnits(Position)); - break; - case "trigger": - case "sprite": - case "deconstruct": - break; - case "aitarget": - aiTarget = new AITarget(this); - aiTarget.SightRange = ToolBox.GetAttributeFloat(subElement, "sightrange", 1000.0f); - aiTarget.SoundRange = ToolBox.GetAttributeFloat(subElement, "soundrange", 0.0f); - break; - case "fixrequirement": - FixRequirements.Add(new FixRequirement(subElement)); - break; - default: - ItemComponent ic = ItemComponent.Load(subElement, this, prefab.ConfigFile); - if (ic == null) break; - - components.Add(ic); - - if (ic is IDrawableComponent && ic.Drawable) drawableComponents.Add(ic as IDrawableComponent); - - if (ic.statusEffectLists == null) continue; - - if (statusEffectLists == null) - statusEffectLists = new Dictionary>(); - - //go through all the status effects of the component - //and add them to the corresponding statuseffect list - foreach (List componentEffectList in ic.statusEffectLists.Values) - { - - ActionType actionType = componentEffectList.First().type; - - List statusEffectList; - if (!statusEffectLists.TryGetValue(actionType, out statusEffectList)) - { - statusEffectList = new List(); - statusEffectLists.Add(actionType, statusEffectList); - } - - foreach (StatusEffect effect in componentEffectList) - { - statusEffectList.Add(effect); - } - } - - break; - } - } - - //containers need to handle collision events to notify items inside them about the impact - if (ImpactTolerance > 0.0f || GetComponent() != null) - { - if (body != null) body.FarseerBody.OnCollision += OnCollision; - } - - InsertToList(); - ItemList.Add(this); - } - - public T GetComponent() - { - foreach (ItemComponent ic in components) - { - if (ic is T) return (T)(object)ic; - } - - return default(T); - } - - public List GetComponents() - { - List components = new List(); - foreach (ItemComponent ic in this.components) - { - if (ic is T) components.Add((T)(object)ic); - } - - return components; - } - - public void RemoveContained(Item contained) - { - ItemContainer c = GetComponent(); - if (c == null) return; - - c.RemoveContained(contained); - contained.Container = null; - } - - - public void SetTransform(Vector2 simPosition, float rotation) - { - if (body != null) - { - try - { - body.SetTransform(simPosition, rotation); - } - catch (Exception e) - { -#if DEBUG - DebugConsole.ThrowError("Failed to set item transform", e); -#endif - } - } - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(simPosition); - - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); - - FindHull(); - } - - public override void Move(Vector2 amount) - { - base.Move(amount); - - if (ItemList != null && body != null) - { - //Vector2 pos = new Vector2(rect.X + rect.Width / 2.0f, rect.Y - rect.Height / 2.0f); - body.SetTransform(body.SimPosition+ConvertUnits.ToSimUnits(amount), body.Rotation); - } - foreach (ItemComponent ic in components) - { - ic.Move(amount); - } - - if (body != null && (Submarine==null || !Submarine.Loading)) FindHull(); - } - - public Rectangle TransformTrigger(Rectangle trigger, bool world = false) - { - return world ? - new Rectangle( - WorldRect.X + trigger.X, - WorldRect.Y + trigger.Y, - (trigger.Width == 0) ? Rect.Width : trigger.Width, - (trigger.Height == 0) ? Rect.Height : trigger.Height) - : - new Rectangle( - Rect.X + trigger.X, - Rect.Y + trigger.Y, - (trigger.Width == 0) ? Rect.Width : trigger.Width, - (trigger.Height == 0) ? Rect.Height : trigger.Height); - } - - /// - /// goes through every item and re-checks which hull they are in - /// - public static void UpdateHulls() - { - foreach (Item item in ItemList) item.FindHull(); - } - - public virtual Hull FindHull() - { - if (parentInventory != null && parentInventory.Owner != null) - { - if (parentInventory.Owner is Character) - { - CurrentHull = (parentInventory.Owner as Character).AnimController.CurrentHull; - } - else if (parentInventory.Owner is Item) - { - CurrentHull = (parentInventory.Owner as Item).CurrentHull; - } - - Submarine = parentInventory.Owner.Submarine; - if (body != null) body.Submarine = Submarine; - - return CurrentHull; - } - - - CurrentHull = Hull.FindHull(WorldPosition, CurrentHull); - if (body != null && body.Enabled) - { - Submarine = CurrentHull == null ? null : CurrentHull.Submarine; - body.Submarine = Submarine; - } - - return CurrentHull; - } - - public Item GetRootContainer() - { - if (Container == null) return null; - - Item rootContainer = Container; - - while (rootContainer.Container != null) - { - rootContainer = rootContainer.Container; - } - - return rootContainer; - } - - public void AddTag(string tag) - { - if (tags.Contains(tag)) return; - tags.Add(tag); - } - - public bool HasTag(string tag) - { - if (tag == null) return true; - - return (tags.Contains(tag) || tags.Contains(tag.ToLowerInvariant())); - } - - - public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null) - { - if (statusEffectLists == null) return; - - List statusEffects; - if (!statusEffectLists.TryGetValue(type, out statusEffects)) return; - - foreach (StatusEffect effect in statusEffects) - { - ApplyStatusEffect(effect, type, deltaTime, character); - } - } - - public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null) - { - if (condition == 0.0f && effect.type != ActionType.OnBroken) return; - if (effect.type != type) return; - - bool hasTargets = (effect.TargetNames == null); - - Item[] containedItems = ContainedItems; - if (effect.OnContainingNames!=null) - { - foreach (string s in effect.OnContainingNames) - { - if (containedItems.FirstOrDefault(x => x!=null && x.Name==s && x.Condition>0.0f) == null) return; - } - } - - List targets = new List(); - - if (containedItems != null) - { - if (effect.Targets.HasFlag(StatusEffect.TargetType.Contained)) - { - foreach (Item containedItem in containedItems) - { - if (containedItem == null) continue; - if (effect.TargetNames != null && !effect.TargetNames.Contains(containedItem.Name)) - { - bool tagFound = false; - foreach (string targetName in effect.TargetNames) - { - if (!containedItem.HasTag(targetName)) continue; - tagFound = true; - break; - } - if (!tagFound) continue; - } - - hasTargets = true; - targets.Add(containedItem); - //effect.Apply(type, deltaTime, containedItem); - //containedItem.ApplyStatusEffect(effect, type, deltaTime, containedItem); - } - } - } - - if (!hasTargets) return; - - if (effect.Targets.HasFlag(StatusEffect.TargetType.Hull) && CurrentHull != null) - { - targets.Add(CurrentHull); - } - - - if (effect.Targets.HasFlag(StatusEffect.TargetType.This)) - { - foreach (var pobject in AllPropertyObjects) - { - targets.Add(pobject); - } - } - //effect.Apply(type, deltaTime, this); - //ApplyStatusEffect(effect, type, deltaTime, this); - - if (effect.Targets.HasFlag(StatusEffect.TargetType.Character)) targets.Add(character); - //effect.Apply(type, deltaTime, null, Character); - //ApplyStatusEffect(effect, type, deltaTime, null, Character, limb); - - if (Container != null && effect.Targets.HasFlag(StatusEffect.TargetType.Parent)) targets.Add(Container); - //{ - // effect.Apply(type, deltaTime, container); - // //container.ApplyStatusEffect(effect, type, deltaTime, container); - //} - - effect.Apply(type, deltaTime, this, targets); - - } - - - public AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true) - { - float damageAmount = attack.GetStructureDamage(deltaTime); - Condition -= damageAmount; - - return new AttackResult(damageAmount, 0.0f, false); - } - - private bool IsInWater() - { - if (CurrentHull == null) return true; - - float surfaceY = CurrentHull.Surface; - - return Position.Y < surfaceY; - } - - - public override void Update(Camera cam, float deltaTime) - { - - ApplyStatusEffects(ActionType.Always, deltaTime, null); - - foreach (ItemComponent ic in components) - { - if (ic.Parent != null) ic.IsActive = ic.Parent.IsActive; - - if (!ic.WasUsed) - { - ic.StopSounds(ActionType.OnUse); - } - ic.WasUsed = false; - - if (parentInventory!=null) ic.ApplyStatusEffects(ActionType.OnContained, deltaTime); - - if (!ic.IsActive) continue; - - if (condition > 0.0f) - { - ic.Update(deltaTime, cam); - - if (ic.IsActive) ic.PlaySound(ActionType.OnActive, WorldPosition); - //ic.ApplyStatusEffects(ActionType.OnActive, deltaTime, null); - } - else - { - ic.UpdateBroken(deltaTime, cam); - } - } - - inWater = IsInWater(); - if (inWater) ApplyStatusEffects(ActionType.InWater, deltaTime); - - if (body == null || !body.Enabled) return; - - if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) - { - Submarine prevSub = Submarine; - - FindHull(); - - if (Submarine == null && prevSub != null) - { - body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation); - } - else if (Submarine != null && prevSub == null) - { - body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation); - } - - Vector2 moveAmount = body.SimPosition - body.LastSentPosition; - if (parentInventory == null && moveAmount != Vector2.Zero && moveAmount.Length() > NetConfig.ItemPosUpdateDistance) - { - new NetworkEvent(NetworkEventType.PhysicsBodyPosition, ID, false); - } - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); - } - - body.MoveToTargetPosition(); - - if (!inWater || Container != null || body == null) return; - - if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) - { - body.ResetDynamics(); - } - - ApplyWaterForces(); - - if(CurrentHull != null) CurrentHull.ApplyFlowForces(deltaTime, this); - - } - - /// - /// Applies buoyancy, drag and angular drag caused by water - /// - private void ApplyWaterForces() - { - if (!InWater || Container != null || body == null || !body.Enabled) return; - - float forceFactor = 1.0f; - if (CurrentHull != null) - { - float floor = CurrentHull.Rect.Y - CurrentHull.Rect.Height; - float waterLevel = (floor + CurrentHull.Volume / CurrentHull.Rect.Width); - - //forceFactor is 1.0f if the item is completely submerged, - //and goes to 0.0f as the item goes through the surface - forceFactor = Math.Min((waterLevel - Position.Y) / rect.Height, 1.0f); - if (forceFactor <= 0.0f) return; - } - - float volume = body.Mass / body.Density; - - var uplift = -GameMain.World.Gravity * forceFactor * volume; - - Vector2 drag = body.LinearVelocity * volume; - - body.ApplyForce((uplift - drag) * 10.0f); - - //apply simple angular drag - body.ApplyTorque(body.AngularVelocity * volume * -0.05f); - } - - private bool OnCollision(Fixture f1, Fixture f2, Contact contact) - { - if (GameMain.Client != null) return true; - - Vector2 normal = contact.Manifold.LocalNormal; - - float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal); - - if (ImpactTolerance > 0.0f && impact > ImpactTolerance) - { - ApplyStatusEffects(ActionType.OnImpact, 1.0f); - new NetworkEvent(NetworkEventType.ApplyStatusEffect, this.ID, false, ActionType.OnImpact); - } - - var containedItems = ContainedItems; - if (containedItems != null) - { - foreach (Item contained in containedItems) - { - if (contained.body == null) continue; - contained.OnCollision(f1, f2, contact); - } - } - - return true; - } - - public override void FlipX() - { - base.FlipX(); - - if (prefab.CanSpriteFlipX) - { - SpriteEffects ^= SpriteEffects.FlipHorizontally; - } - - foreach (ItemComponent component in components) - { - component.FlipX(); - } - } - - public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) - { - if (!Visible) return; - Color color = (isSelected && editing) ? color = Color.Red : spriteColor; - if (isHighlighted) color = Color.Orange; - - SpriteEffects oldEffects = prefab.sprite.effects; - prefab.sprite.effects ^= SpriteEffects; - - if (prefab.sprite != null) - { - float depth = Sprite.Depth; - depth += (ID % 255) * 0.000001f; - - if (body == null) - { - if (prefab.ResizeHorizontal || prefab.ResizeVertical || SpriteEffects.HasFlag(SpriteEffects.FlipHorizontally) || SpriteEffects.HasFlag(SpriteEffects.FlipVertically)) - { - prefab.sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X-rect.Width/2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), color); - } - else - { - prefab.sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, 0.0f, 1.0f, SpriteEffects.None, depth); - } - - } - else if (body.Enabled) - { - var holdable = GetComponent(); - if (holdable!=null && holdable.Picker !=null) - { - if (holdable.Picker.SelectedItems[0]==this) - { - depth = holdable.Picker.AnimController.GetLimb(LimbType.RightHand).sprite.Depth + 0.000001f; - } - else if (holdable.Picker.SelectedItems[1] == this) - { - depth = holdable.Picker.AnimController.GetLimb(LimbType.LeftArm).sprite.Depth - 0.000001f; - } - - body.Draw(spriteBatch, prefab.sprite, color, depth); - } - else - { - body.Draw(spriteBatch, prefab.sprite, color, depth); - } - } - } - - prefab.sprite.effects = oldEffects; - - for (int i = 0; i < drawableComponents.Count; i++ ) - { - drawableComponents[i].Draw(spriteBatch, editing); - } - - //foreach (ItemComponent component in components) component.Draw(spriteBatch, editing); - - if (GameMain.DebugDraw && aiTarget!=null) aiTarget.Draw(spriteBatch); - - if (!editing || (body != null && !body.Enabled)) - { - isHighlighted = false; - return; - } - - if (isSelected || isHighlighted) - { - GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), Color.Green); - - foreach (Rectangle t in prefab.Triggers) - { - Rectangle transformedTrigger = TransformTrigger(t); - - Vector2 rectWorldPos = new Vector2(transformedTrigger.X, transformedTrigger.Y); - if (Submarine!=null) rectWorldPos += Submarine.Position; - rectWorldPos.Y = -rectWorldPos.Y; - - GUI.DrawRectangle(spriteBatch, - rectWorldPos, - new Vector2(transformedTrigger.Width, transformedTrigger.Height), - Color.Green); - } - } - - if (!ShowLinks) return; - - foreach (MapEntity e in linkedTo) - { - GUI.DrawLine(spriteBatch, - new Vector2(WorldPosition.X, -WorldPosition.Y), - new Vector2(e.WorldPosition.X, -e.WorldPosition.Y), - Color.Red*0.3f); - } - } - - public override void DrawEditing(SpriteBatch spriteBatch, Camera cam) - { - if (editingHUD==null || editingHUD.UserData as Item != this) - { - editingHUD = CreateEditingHUD(); - } - - editingHUD.Draw(spriteBatch); - editingHUD.Update((float)Timing.Step); - - if (!prefab.IsLinkable) return; - - if (!PlayerInput.LeftButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return; - - Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); - - foreach (MapEntity entity in mapEntityList) - { - if (entity == this || !entity.IsHighlighted) continue; - if (linkedTo.Contains(entity)) continue; - if (!entity.IsMouseOn(position)) continue; - - linkedTo.Add(entity); - if (entity.IsLinkable && entity.linkedTo != null) entity.linkedTo.Add(this); - } - } - - public void DrawInGameEditing(SpriteBatch spriteBatch) - { - if (editingHUD == null || editingHUD.UserData as Item != this) - { - editingHUD = CreateEditingHUD(true); - } - - if (editingHUD.Rect.Height > 60) - { - editingHUD.Update((float)Timing.Step); - editingHUD.Draw(spriteBatch); - } - } - - private GUIComponent CreateEditingHUD(bool inGame=false) - { - int width = 450; - int x = GameMain.GraphicsWidth/2-width/2, y = 10; - - List editableProperties = inGame ? GetProperties() : GetProperties(); - - int requiredItemCount = 0; - if (!inGame) - { - foreach (ItemComponent ic in components) - { - requiredItemCount += ic.requiredItems.Count; - } - } - - editingHUD = new GUIFrame(new Rectangle(x, y, width, 70 + (editableProperties.Count() + requiredItemCount) * 30), GUI.Style); - editingHUD.Padding = new Vector4(10, 10, 0, 0); - editingHUD.UserData = this; - - new GUITextBlock(new Rectangle(0, 0, 100, 20), prefab.Name, GUI.Style, - Alignment.TopLeft, Alignment.TopLeft, editingHUD, false, GUI.LargeFont); - - y += 20; - - if (!inGame) - { - if (prefab.IsLinkable) - { - new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to another item", - GUI.Style, Alignment.TopRight, Alignment.TopRight, editingHUD).Font = GUI.SmallFont; - y += 25; - } - foreach (ItemComponent ic in components) - { - foreach (RelatedItem relatedItem in ic.requiredItems) - { - new GUITextBlock(new Rectangle(0, y, 100, 20), ic.Name + ": " + relatedItem.Type.ToString() + " required", GUI.Style, editingHUD); - GUITextBox namesBox = new GUITextBox(new Rectangle(-10, y, 160, 20), Alignment.Right, GUI.Style, editingHUD); - - PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (relatedItem); - PropertyDescriptor property = properties.Find("JoinedNames", false); - - namesBox.Text = relatedItem.JoinedNames; - namesBox.UserData = new ObjectProperty(property, relatedItem); - namesBox.OnEnterPressed = EnterProperty; - namesBox.OnTextChanged = PropertyChanged; - - y += 30; - } - } - - } - - foreach (var objectProperty in editableProperties) - { - int height = 20; - var editable = objectProperty.Attributes.OfType().FirstOrDefault(); - if (editable != null) height = (int)(Math.Ceiling(editable.MaxLength / 20.0f) * 20.0f); - - object value = objectProperty.GetValue(); - - if (value is bool) - { - GUITickBox propertyTickBox = new GUITickBox(new Rectangle(10, y, 20, 20), objectProperty.Name, - Alignment.Left, editingHUD); - - propertyTickBox.Selected = (bool)value; - - propertyTickBox.UserData = objectProperty; - propertyTickBox.OnSelected = EnterProperty; - } - else - { - - new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, GUI.Style, editingHUD); - - GUITextBox propertyBox = new GUITextBox(new Rectangle(180, y, 250, height), GUI.Style, editingHUD); - if (height > 20) propertyBox.Wrap = true; - - if (value != null) - { - if (value is float) - { - propertyBox.Text = ((float)value).ToString("G", System.Globalization.CultureInfo.InvariantCulture); - } - else - { - - propertyBox.Text = value.ToString(); - } - } - - propertyBox.UserData = objectProperty; - propertyBox.OnEnterPressed = EnterProperty; - propertyBox.OnTextChanged = PropertyChanged; - - } - y = y + height + 10; - - } - return editingHUD; - } - - public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) - { - if (condition <= 0.0f) - { - FixRequirement.DrawHud(spriteBatch, this, character); - return; - } - - if (HasInGameEditableProperties) - { - DrawInGameEditing(spriteBatch); - } - - foreach (ItemComponent ic in components) - { - ic.DrawHUD(spriteBatch, character); - } - } - +using Barotrauma.Items.Components; +using Barotrauma.Networking; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Contacts; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Xml.Linq; + +namespace Barotrauma +{ + + public enum ActionType + { + Always, OnPicked, OnUse, OnSecondaryUse, + OnWearing, OnContaining, OnContained, + OnActive, OnFailure, OnBroken, + OnFire, InWater, + OnImpact + } + + class Item : MapEntity, IDamageable, IPropertyObject + { + public static List ItemList = new List(); + private ItemPrefab prefab; + + public static ItemSpawner Spawner = new ItemSpawner(); + public static ItemRemover Remover = new ItemRemover(); + + public static bool ShowLinks = true; + + private List tags; + + public Hull CurrentHull; + + public bool Visible = true; + + public SpriteEffects SpriteEffects = SpriteEffects.None; + + //components that determine the functionality of the item + public List components; + public List drawableComponents; + + public PhysicsBody body; + + private float condition; + + private bool inWater; + + private Inventory parentInventory; + + //a dictionary containing lists of the status effects in all the components of the item + private Dictionary> statusEffectLists; + + public readonly Dictionary properties; + public Dictionary ObjectProperties + { + get { return properties; } + } + + private bool? hasInGameEditableProperties; + bool HasInGameEditableProperties + { + get + { + if (hasInGameEditableProperties==null) + { + hasInGameEditableProperties = GetProperties().Any(); + } + return (bool)hasInGameEditableProperties; + } + } + + //the inventory in which the item is contained in + public Inventory ParentInventory + { + get + { + return parentInventory; + } + set + { + parentInventory = value; + + if (parentInventory != null) Container = parentInventory.Owner as Item; + } + } + + public Item Container + { + get; + private set; + } + + public override bool SelectableInEditor + { + get + { + return parentInventory == null && (body == null || body.Enabled); + } + } + + public List FixRequirements; + + public override string Name + { + get { return prefab.Name; } + } + + public string Description + { + get { return prefab.Description; } + } + + public float ImpactTolerance + { + get { return prefab.ImpactTolerance; } + } + + public override Sprite Sprite + { + get { return prefab.sprite; } + } + + public float PickDistance + { + get { return prefab.PickDistance; } + } + + public override Vector2 SimPosition + { + get + { + return (body==null) ? base.SimPosition : body.SimPosition; + } + } + + protected Color spriteColor; + [Editable, HasDefaultValue("1.0,1.0,1.0,1.0", true)] + public string SpriteColor + { + get { return ToolBox.Vector4ToString(spriteColor.ToVector4()); } + set + { + spriteColor = new Color(ToolBox.ParseToVector4(value)); + } + } + + public Color Color + { + get { return spriteColor; } + } + + public float Condition + { + get { return condition; } + set + { + if (!MathUtils.IsValid(value)) return; + + float prev = condition; + condition = MathHelper.Clamp(value, 0.0f, 100.0f); + if (condition == 0.0f && prev>0.0f) + { + new NetworkEvent(this.ID, false); + + ApplyStatusEffects(ActionType.OnBroken, 1.0f, null); + foreach (FixRequirement req in FixRequirements) + { + req.Fixed = false; + } + } + } + } + + public float Health + { + get { return condition; } + } + + [Editable, HasDefaultValue("", true)] + public string Tags + { + get { return string.Join(",",tags); } + set + { + tags.Clear(); + if (value == null) return; + + string[] newTags = value.Split(','); + foreach (string tag in newTags) + { + string newTag = tag.Trim(); + if (!tags.Contains(newTag)) tags.Add(newTag); + } + + } + } + + public bool FireProof + { + get { return prefab.FireProof; } + } + + public bool CanUseOnSelf + { + get { return prefab.CanUseOnSelf; } + } + + public bool InWater + { + get + { + //if the item has an active physics body, inWater is updated in the Update method + if (body != null && body.Enabled) return inWater; + + //if not, we'll just have to check + return IsInWater(); + } + } + + public ItemPrefab Prefab + { + get { return prefab; } + } + + public string ConfigFile + { + get { return prefab.ConfigFile; } + } + + public bool Removed + { + get; + private set; + } + + //which type of inventory slots (head, torso, any, etc) the item can be placed in + public List AllowedSlots + { + get + { + Pickable p = GetComponent(); + return (p==null) ? new List() { InvSlotType.Any } : p.AllowedSlots; + } + } + + public int Capacity + { + get + { + ItemContainer c = GetComponent(); + return (c == null) ? 0 : c.Capacity; + } + } + + public List Connections + { + get + { + ConnectionPanel panel = GetComponent(); + if (panel == null) return null; + return panel.Connections; + } + } + + public Item[] ContainedItems + { + get + { + ItemContainer c = GetComponent(); + return (c == null) ? null : Array.FindAll(c.Inventory.Items, i=>i!=null); + } + } + + public override bool IsLinkable + { + get { return prefab.IsLinkable; } + } + + public override string ToString() + { + return (GameMain.DebugDraw) ? Name + "(ID: " + ID + ")" : Name; + } + + public List AllPropertyObjects + { + get + { + List pobjects = new List(); + pobjects.Add(this); + foreach (ItemComponent ic in components) + { + pobjects.Add(ic); + } + return pobjects; + } + } + + public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine) + : this(new Rectangle( + (int)(position.X - itemPrefab.sprite.size.X / 2), + (int)(position.Y + itemPrefab.sprite.size.Y / 2), + (int)itemPrefab.sprite.size.X, + (int)itemPrefab.sprite.size.Y), + itemPrefab, submarine) + { + + } + + public Item(Rectangle newRect, ItemPrefab itemPrefab, Submarine submarine) + : base(itemPrefab, submarine) + { + prefab = itemPrefab; + + spriteColor = prefab.SpriteColor; + + linkedTo = new ObservableCollection(); + components = new List(); + drawableComponents = new List(); + FixRequirements = new List(); + tags = new List(); + + rect = newRect; + + if (submarine==null || !submarine.Loading) FindHull(); + + condition = 100.0f; + + XElement element = prefab.ConfigElement; + if (element == null) return; + + properties = ObjectProperty.InitProperties(this, element); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLowerInvariant()) + { + case "body": + body = new PhysicsBody(subElement, ConvertUnits.ToSimUnits(Position)); + break; + case "trigger": + case "sprite": + case "deconstruct": + break; + case "aitarget": + aiTarget = new AITarget(this); + aiTarget.SightRange = ToolBox.GetAttributeFloat(subElement, "sightrange", 1000.0f); + aiTarget.SoundRange = ToolBox.GetAttributeFloat(subElement, "soundrange", 0.0f); + break; + case "fixrequirement": + FixRequirements.Add(new FixRequirement(subElement)); + break; + default: + ItemComponent ic = ItemComponent.Load(subElement, this, prefab.ConfigFile); + if (ic == null) break; + + components.Add(ic); + + if (ic is IDrawableComponent && ic.Drawable) drawableComponents.Add(ic as IDrawableComponent); + + if (ic.statusEffectLists == null) continue; + + if (statusEffectLists == null) + statusEffectLists = new Dictionary>(); + + //go through all the status effects of the component + //and add them to the corresponding statuseffect list + foreach (List componentEffectList in ic.statusEffectLists.Values) + { + + ActionType actionType = componentEffectList.First().type; + + List statusEffectList; + if (!statusEffectLists.TryGetValue(actionType, out statusEffectList)) + { + statusEffectList = new List(); + statusEffectLists.Add(actionType, statusEffectList); + } + + foreach (StatusEffect effect in componentEffectList) + { + statusEffectList.Add(effect); + } + } + + break; + } + } + + //containers need to handle collision events to notify items inside them about the impact + if (ImpactTolerance > 0.0f || GetComponent() != null) + { + if (body != null) body.FarseerBody.OnCollision += OnCollision; + } + + InsertToList(); + ItemList.Add(this); + } + + public T GetComponent() + { + foreach (ItemComponent ic in components) + { + if (ic is T) return (T)(object)ic; + } + + return default(T); + } + + public List GetComponents() + { + List components = new List(); + foreach (ItemComponent ic in this.components) + { + if (ic is T) components.Add((T)(object)ic); + } + + return components; + } + + public void RemoveContained(Item contained) + { + ItemContainer c = GetComponent(); + if (c == null) return; + + c.RemoveContained(contained); + contained.Container = null; + } + + + public void SetTransform(Vector2 simPosition, float rotation) + { + if (body != null) + { + try + { + body.SetTransform(simPosition, rotation); + } + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("Failed to set item transform", e); +#endif + } + } + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(simPosition); + + rect.X = (int)(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); + + FindHull(); + } + + public override void Move(Vector2 amount) + { + base.Move(amount); + + if (ItemList != null && body != null) + { + //Vector2 pos = new Vector2(rect.X + rect.Width / 2.0f, rect.Y - rect.Height / 2.0f); + body.SetTransform(body.SimPosition+ConvertUnits.ToSimUnits(amount), body.Rotation); + } + foreach (ItemComponent ic in components) + { + ic.Move(amount); + } + + if (body != null && (Submarine==null || !Submarine.Loading)) FindHull(); + } + + public Rectangle TransformTrigger(Rectangle trigger, bool world = false) + { + return world ? + new Rectangle( + WorldRect.X + trigger.X, + WorldRect.Y + trigger.Y, + (trigger.Width == 0) ? Rect.Width : trigger.Width, + (trigger.Height == 0) ? Rect.Height : trigger.Height) + : + new Rectangle( + Rect.X + trigger.X, + Rect.Y + trigger.Y, + (trigger.Width == 0) ? Rect.Width : trigger.Width, + (trigger.Height == 0) ? Rect.Height : trigger.Height); + } + + /// + /// goes through every item and re-checks which hull they are in + /// + public static void UpdateHulls() + { + foreach (Item item in ItemList) item.FindHull(); + } + + public virtual Hull FindHull() + { + if (parentInventory != null && parentInventory.Owner != null) + { + if (parentInventory.Owner is Character) + { + CurrentHull = (parentInventory.Owner as Character).AnimController.CurrentHull; + } + else if (parentInventory.Owner is Item) + { + CurrentHull = (parentInventory.Owner as Item).CurrentHull; + } + + Submarine = parentInventory.Owner.Submarine; + if (body != null) body.Submarine = Submarine; + + return CurrentHull; + } + + + CurrentHull = Hull.FindHull(WorldPosition, CurrentHull); + if (body != null && body.Enabled) + { + Submarine = CurrentHull == null ? null : CurrentHull.Submarine; + body.Submarine = Submarine; + } + + return CurrentHull; + } + + public Item GetRootContainer() + { + if (Container == null) return null; + + Item rootContainer = Container; + + while (rootContainer.Container != null) + { + rootContainer = rootContainer.Container; + } + + return rootContainer; + } + + public void AddTag(string tag) + { + if (tags.Contains(tag)) return; + tags.Add(tag); + } + + public bool HasTag(string tag) + { + if (tag == null) return true; + + return (tags.Contains(tag) || tags.Contains(tag.ToLowerInvariant())); + } + + + public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null) + { + if (statusEffectLists == null) return; + + List statusEffects; + if (!statusEffectLists.TryGetValue(type, out statusEffects)) return; + + foreach (StatusEffect effect in statusEffects) + { + ApplyStatusEffect(effect, type, deltaTime, character); + } + } + + public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null) + { + if (condition == 0.0f && effect.type != ActionType.OnBroken) return; + if (effect.type != type) return; + + bool hasTargets = (effect.TargetNames == null); + + Item[] containedItems = ContainedItems; + if (effect.OnContainingNames!=null) + { + foreach (string s in effect.OnContainingNames) + { + if (containedItems.FirstOrDefault(x => x!=null && x.Name==s && x.Condition>0.0f) == null) return; + } + } + + List targets = new List(); + + if (containedItems != null) + { + if (effect.Targets.HasFlag(StatusEffect.TargetType.Contained)) + { + foreach (Item containedItem in containedItems) + { + if (containedItem == null) continue; + if (effect.TargetNames != null && !effect.TargetNames.Contains(containedItem.Name)) + { + bool tagFound = false; + foreach (string targetName in effect.TargetNames) + { + if (!containedItem.HasTag(targetName)) continue; + tagFound = true; + break; + } + if (!tagFound) continue; + } + + hasTargets = true; + targets.Add(containedItem); + //effect.Apply(type, deltaTime, containedItem); + //containedItem.ApplyStatusEffect(effect, type, deltaTime, containedItem); + } + } + } + + if (!hasTargets) return; + + if (effect.Targets.HasFlag(StatusEffect.TargetType.Hull) && CurrentHull != null) + { + targets.Add(CurrentHull); + } + + + if (effect.Targets.HasFlag(StatusEffect.TargetType.This)) + { + foreach (var pobject in AllPropertyObjects) + { + targets.Add(pobject); + } + } + //effect.Apply(type, deltaTime, this); + //ApplyStatusEffect(effect, type, deltaTime, this); + + if (effect.Targets.HasFlag(StatusEffect.TargetType.Character)) targets.Add(character); + //effect.Apply(type, deltaTime, null, Character); + //ApplyStatusEffect(effect, type, deltaTime, null, Character, limb); + + if (Container != null && effect.Targets.HasFlag(StatusEffect.TargetType.Parent)) targets.Add(Container); + //{ + // effect.Apply(type, deltaTime, container); + // //container.ApplyStatusEffect(effect, type, deltaTime, container); + //} + + effect.Apply(type, deltaTime, this, targets); + + } + + + public AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true) + { + float damageAmount = attack.GetStructureDamage(deltaTime); + Condition -= damageAmount; + + return new AttackResult(damageAmount, 0.0f, false); + } + + private bool IsInWater() + { + if (CurrentHull == null) return true; + + float surfaceY = CurrentHull.Surface; + + return Position.Y < surfaceY; + } + + + public override void Update(Camera cam, float deltaTime) + { + + ApplyStatusEffects(ActionType.Always, deltaTime, null); + + foreach (ItemComponent ic in components) + { + if (ic.Parent != null) ic.IsActive = ic.Parent.IsActive; + + if (!ic.WasUsed) + { + ic.StopSounds(ActionType.OnUse); + } + ic.WasUsed = false; + + if (parentInventory!=null) ic.ApplyStatusEffects(ActionType.OnContained, deltaTime); + + if (!ic.IsActive) continue; + + if (condition > 0.0f) + { + ic.Update(deltaTime, cam); + + if (ic.IsActive) ic.PlaySound(ActionType.OnActive, WorldPosition); + //ic.ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + } + else + { + ic.UpdateBroken(deltaTime, cam); + } + } + + inWater = IsInWater(); + if (inWater) ApplyStatusEffects(ActionType.InWater, deltaTime); + + if (body == null || !body.Enabled) return; + + if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) + { + Submarine prevSub = Submarine; + + FindHull(); + + if (Submarine == null && prevSub != null) + { + body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation); + } + else if (Submarine != null && prevSub == null) + { + body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation); + } + + Vector2 moveAmount = body.SimPosition - body.LastSentPosition; + if (parentInventory == null && moveAmount != Vector2.Zero && moveAmount.Length() > NetConfig.ItemPosUpdateDistance) + { + new NetworkEvent(NetworkEventType.PhysicsBodyPosition, ID, false); + } + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); + rect.X = (int)(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); + } + + body.MoveToTargetPosition(); + + if (!inWater || Container != null || body == null) return; + + if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) + { + body.ResetDynamics(); + } + + ApplyWaterForces(); + + if(CurrentHull != null) CurrentHull.ApplyFlowForces(deltaTime, this); + + } + + /// + /// Applies buoyancy, drag and angular drag caused by water + /// + private void ApplyWaterForces() + { + if (!InWater || Container != null || body == null || !body.Enabled) return; + + float forceFactor = 1.0f; + if (CurrentHull != null) + { + float floor = CurrentHull.Rect.Y - CurrentHull.Rect.Height; + float waterLevel = (floor + CurrentHull.Volume / CurrentHull.Rect.Width); + + //forceFactor is 1.0f if the item is completely submerged, + //and goes to 0.0f as the item goes through the surface + forceFactor = Math.Min((waterLevel - Position.Y) / rect.Height, 1.0f); + if (forceFactor <= 0.0f) return; + } + + float volume = body.Mass / body.Density; + + var uplift = -GameMain.World.Gravity * forceFactor * volume; + + Vector2 drag = body.LinearVelocity * volume; + + body.ApplyForce((uplift - drag) * 10.0f); + + //apply simple angular drag + body.ApplyTorque(body.AngularVelocity * volume * -0.05f); + } + + private bool OnCollision(Fixture f1, Fixture f2, Contact contact) + { + if (GameMain.Client != null) return true; + + Vector2 normal = contact.Manifold.LocalNormal; + + float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal); + + if (ImpactTolerance > 0.0f && impact > ImpactTolerance) + { + ApplyStatusEffects(ActionType.OnImpact, 1.0f); + new NetworkEvent(NetworkEventType.ApplyStatusEffect, this.ID, false, ActionType.OnImpact); + } + + var containedItems = ContainedItems; + if (containedItems != null) + { + foreach (Item contained in containedItems) + { + if (contained.body == null) continue; + contained.OnCollision(f1, f2, contact); + } + } + + return true; + } + + public override void FlipX() + { + base.FlipX(); + + if (prefab.CanSpriteFlipX) + { + SpriteEffects ^= SpriteEffects.FlipHorizontally; + } + + foreach (ItemComponent component in components) + { + component.FlipX(); + } + } + + public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) + { + if (!Visible) return; + Color color = (isSelected && editing) ? color = Color.Red : spriteColor; + if (isHighlighted) color = Color.Orange; + + SpriteEffects oldEffects = prefab.sprite.effects; + prefab.sprite.effects ^= SpriteEffects; + + if (prefab.sprite != null) + { + float depth = Sprite.Depth; + depth += (ID % 255) * 0.000001f; + + if (body == null) + { + if (prefab.ResizeHorizontal || prefab.ResizeVertical || SpriteEffects.HasFlag(SpriteEffects.FlipHorizontally) || SpriteEffects.HasFlag(SpriteEffects.FlipVertically)) + { + prefab.sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X-rect.Width/2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), color); + } + else + { + prefab.sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, 0.0f, 1.0f, SpriteEffects.None, depth); + } + + } + else if (body.Enabled) + { + var holdable = GetComponent(); + if (holdable!=null && holdable.Picker !=null) + { + if (holdable.Picker.SelectedItems[0]==this) + { + depth = holdable.Picker.AnimController.GetLimb(LimbType.RightHand).sprite.Depth + 0.000001f; + } + else if (holdable.Picker.SelectedItems[1] == this) + { + depth = holdable.Picker.AnimController.GetLimb(LimbType.LeftArm).sprite.Depth - 0.000001f; + } + + body.Draw(spriteBatch, prefab.sprite, color, depth); + } + else + { + body.Draw(spriteBatch, prefab.sprite, color, depth); + } + } + } + + prefab.sprite.effects = oldEffects; + + for (int i = 0; i < drawableComponents.Count; i++ ) + { + drawableComponents[i].Draw(spriteBatch, editing); + } + + //foreach (ItemComponent component in components) component.Draw(spriteBatch, editing); + + if (GameMain.DebugDraw && aiTarget!=null) aiTarget.Draw(spriteBatch); + + if (!editing || (body != null && !body.Enabled)) + { + isHighlighted = false; + return; + } + + if (isSelected || isHighlighted) + { + GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), Color.Green,false,0,(int)Math.Max((1.5f/GameScreen.Selected.Cam.Zoom),1.0f)); + + foreach (Rectangle t in prefab.Triggers) + { + Rectangle transformedTrigger = TransformTrigger(t); + + Vector2 rectWorldPos = new Vector2(transformedTrigger.X, transformedTrigger.Y); + if (Submarine!=null) rectWorldPos += Submarine.Position; + rectWorldPos.Y = -rectWorldPos.Y; + + GUI.DrawRectangle(spriteBatch, + rectWorldPos, + new Vector2(transformedTrigger.Width, transformedTrigger.Height), + Color.Green, + false, + 0, + (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); + } + } + + if (!ShowLinks) return; + + foreach (MapEntity e in linkedTo) + { + GUI.DrawLine(spriteBatch, + new Vector2(WorldPosition.X, -WorldPosition.Y), + new Vector2(e.WorldPosition.X, -e.WorldPosition.Y), + Color.Red*0.3f); + } + } + + public override void DrawEditing(SpriteBatch spriteBatch, Camera cam) + { + if (editingHUD==null || editingHUD.UserData as Item != this) + { + editingHUD = CreateEditingHUD(); + } + + editingHUD.Draw(spriteBatch); + editingHUD.Update((float)Timing.Step); + + if (!prefab.IsLinkable) return; + + if (!PlayerInput.LeftButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return; + + Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); + + foreach (MapEntity entity in mapEntityList) + { + if (entity == this || !entity.IsHighlighted) continue; + if (linkedTo.Contains(entity)) continue; + if (!entity.IsMouseOn(position)) continue; + + linkedTo.Add(entity); + if (entity.IsLinkable && entity.linkedTo != null) entity.linkedTo.Add(this); + } + } + + public void DrawInGameEditing(SpriteBatch spriteBatch) + { + if (editingHUD == null || editingHUD.UserData as Item != this) + { + editingHUD = CreateEditingHUD(true); + } + + if (editingHUD.Rect.Height > 60) + { + editingHUD.Update((float)Timing.Step); + editingHUD.Draw(spriteBatch); + } + } + + private GUIComponent CreateEditingHUD(bool inGame=false) + { + int width = 450; + int x = GameMain.GraphicsWidth/2-width/2, y = 10; + + List editableProperties = inGame ? GetProperties() : GetProperties(); + + int requiredItemCount = 0; + if (!inGame) + { + foreach (ItemComponent ic in components) + { + requiredItemCount += ic.requiredItems.Count; + } + } + + editingHUD = new GUIFrame(new Rectangle(x, y, width, 70 + (editableProperties.Count() + requiredItemCount) * 30), GUI.Style); + editingHUD.Padding = new Vector4(10, 10, 0, 0); + editingHUD.UserData = this; + + new GUITextBlock(new Rectangle(0, 0, 100, 20), prefab.Name, GUI.Style, + Alignment.TopLeft, Alignment.TopLeft, editingHUD, false, GUI.LargeFont); + + y += 20; + + if (!inGame) + { + if (prefab.IsLinkable) + { + new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to another item", + GUI.Style, Alignment.TopRight, Alignment.TopRight, editingHUD).Font = GUI.SmallFont; + y += 25; + } + foreach (ItemComponent ic in components) + { + foreach (RelatedItem relatedItem in ic.requiredItems) + { + new GUITextBlock(new Rectangle(0, y, 100, 20), ic.Name + ": " + relatedItem.Type.ToString() + " required", GUI.Style, editingHUD); + GUITextBox namesBox = new GUITextBox(new Rectangle(-10, y, 160, 20), Alignment.Right, GUI.Style, editingHUD); + + PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (relatedItem); + PropertyDescriptor property = properties.Find("JoinedNames", false); + + namesBox.Text = relatedItem.JoinedNames; + namesBox.UserData = new ObjectProperty(property, relatedItem); + namesBox.OnEnterPressed = EnterProperty; + namesBox.OnTextChanged = PropertyChanged; + + y += 30; + } + } + + } + + foreach (var objectProperty in editableProperties) + { + int height = 20; + var editable = objectProperty.Attributes.OfType().FirstOrDefault(); + if (editable != null) height = (int)(Math.Ceiling(editable.MaxLength / 20.0f) * 20.0f); + + object value = objectProperty.GetValue(); + + if (value is bool) + { + GUITickBox propertyTickBox = new GUITickBox(new Rectangle(10, y, 20, 20), objectProperty.Name, + Alignment.Left, editingHUD); + + propertyTickBox.Selected = (bool)value; + + propertyTickBox.UserData = objectProperty; + propertyTickBox.OnSelected = EnterProperty; + } + else + { + + new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, GUI.Style, editingHUD); + + GUITextBox propertyBox = new GUITextBox(new Rectangle(180, y, 250, height), GUI.Style, editingHUD); + if (height > 20) propertyBox.Wrap = true; + + if (value != null) + { + if (value is float) + { + propertyBox.Text = ((float)value).ToString("G", System.Globalization.CultureInfo.InvariantCulture); + } + else + { + + propertyBox.Text = value.ToString(); + } + } + + propertyBox.UserData = objectProperty; + propertyBox.OnEnterPressed = EnterProperty; + propertyBox.OnTextChanged = PropertyChanged; + + } + y = y + height + 10; + + } + return editingHUD; + } + + public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) + { + if (condition <= 0.0f) + { + FixRequirement.DrawHud(spriteBatch, this, character); + return; + } + + if (HasInGameEditableProperties) + { + DrawInGameEditing(spriteBatch); + } + + foreach (ItemComponent ic in components) + { + ic.DrawHUD(spriteBatch, character); + } + } + public virtual void UpdateHUD(Character character) { if (condition <= 0.0f) @@ -1092,825 +1095,825 @@ namespace Barotrauma { ic.UpdateHUD(character); } - } - - public List GetConnectedComponents(bool recursive = false) - { - List connectedComponents = new List(); - - if (recursive) - { - List alreadySearched = new List() {this}; - GetConnectedComponentsRecursive(alreadySearched, connectedComponents); - - return connectedComponents; - } - - ConnectionPanel connectionPanel = GetComponent(); - if (connectionPanel == null) return connectedComponents; - - - foreach (Connection c in connectionPanel.Connections) - { - var recipients = c.Recipients; - foreach (Connection recipient in recipients) - { - var component = recipient.Item.GetComponent(); - if (component != null) connectedComponents.Add(component); - } - } - - return connectedComponents; - } - - private void GetConnectedComponentsRecursive(List alreadySearched, List connectedComponents) - { - alreadySearched.Add(this); - - ConnectionPanel connectionPanel = GetComponent(); - if (connectionPanel == null) return; - - foreach (Connection c in connectionPanel.Connections) - { - var recipients = c.Recipients; - foreach (Connection recipient in recipients) - { - if (alreadySearched.Contains(recipient.Item)) continue; - - var component = recipient.Item.GetComponent(); - - if (component != null) - { - connectedComponents.Add(component); - } - - recipient.Item.GetConnectedComponentsRecursive(alreadySearched, connectedComponents); - - - } - } - } - - public void SendSignal(int stepsTaken, string signal, string connectionName, float power = 0.0f) - { - stepsTaken++; - - ConnectionPanel panel = GetComponent(); - if (panel == null) return; - foreach (Connection c in panel.Connections) - { - if (c.Name != connectionName) continue; - - if (stepsTaken > 10) - { - //use a coroutine to prevent infinite loops by creating a one - //frame delay if the "signal chain" gets too long - CoroutineManager.StartCoroutine(SendSignal(signal, c, power)); - } - else - { - c.SendSignal(stepsTaken, signal, this, power); - } - } - } - - private IEnumerable SendSignal(string signal, Connection connection, float power = 0.0f) - { - //wait one frame - yield return CoroutineStatus.Running; - - ConnectionPanel panel = GetComponent(); - if (panel == null) yield return CoroutineStatus.Success; - - connection.SendSignal(0, signal, this, power); - - yield return CoroutineStatus.Success; - } - - public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull = null, Item[] ignoredItems = null) - { - float dist; - return FindPickable(position, pickPosition, hull, ignoredItems, out dist); - } - - /// Position of the Character doing the pick, only items that are close enough to this are checked - /// the item closest to pickPosition is returned - /// If a hull is specified, only items within that hull are checked - public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull, Item[] ignoredItems, out float distance) - { - float closestDist = 0.0f, dist; - Item closest = null; - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(position); - Vector2 displayPickPos = ConvertUnits.ToDisplayUnits(pickPosition); - - distance = 1000.0f; - - foreach (Item item in ItemList) - { - if (ignoredItems != null && ignoredItems.Contains(item)) continue; - if (item.body != null && !item.body.Enabled) continue; - - if (item.PickDistance == 0.0f && !item.prefab.Triggers.Any()) continue; - - Pickable pickableComponent = item.GetComponent(); - if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) continue; - - float pickDist = Vector2.Distance(item.WorldPosition, displayPickPos); - - bool insideTrigger = false; - foreach (Rectangle trigger in item.prefab.Triggers) - { - Rectangle transformedTrigger = item.TransformTrigger(trigger, true); - - if (!Submarine.RectContains(transformedTrigger, displayPos)) continue; - - insideTrigger = true; - - Vector2 triggerCenter = new Vector2(transformedTrigger.Center.X, transformedTrigger.Y - transformedTrigger.Height / 2); - pickDist = Math.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y)); - } - - if (!insideTrigger && item.prefab.Triggers.Any()) continue; - - if (pickDist > item.PickDistance && item.PickDistance > 0.0f) continue; - - dist = item.Sprite.Depth * 10.0f + pickDist; - if (item.IsMouseOn(displayPickPos)) dist = dist * 0.1f; - - if (closest == null || dist < closestDist) - { - if (item.PickDistance > 0.0f && Vector2.Distance(displayPos, item.WorldPosition) > item.prefab.PickDistance) continue; - - if (!item.prefab.PickThroughWalls && Screen.Selected != GameMain.EditMapScreen && !insideTrigger) - { - Body body = Submarine.CheckVisibility(item.Submarine == null ? position : position - item.Submarine.SimPosition, item.SimPosition, true); - if (body != null && body.UserData as Item != item) continue; - } - - closestDist = dist; - closest = item; - - distance = pickDist; - } - } - - return closest; - } - - public bool IsInsideTrigger(Vector2 worldPosition) - { - foreach (Rectangle trigger in prefab.Triggers) - { - Rectangle transformedTrigger = TransformTrigger(trigger, true); - - if (Submarine.RectContains(transformedTrigger, worldPosition)) return true; - } - - return false; - } - - public bool IsInPickRange(Vector2 worldPosition) - { - if (IsInsideTrigger(worldPosition)) return true; - - return Vector2.Distance(WorldPosition, worldPosition) < PickDistance; - } - - public bool Pick(Character picker, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceActionKey=false) - { - bool hasRequiredSkills = true; - - bool picked = false, selected = false; - - Skill requiredSkill = null; - - foreach (ItemComponent ic in components) - { - bool pickHit = false, selectHit = false; - if (Screen.Selected == GameMain.EditMapScreen) - { - pickHit = picker.IsKeyHit(InputType.Select); - selectHit = picker.IsKeyHit(InputType.Select); - } - else - { - if (forceSelectKey) - { - if (ic.PickKey == InputType.Select) pickHit = true; - if (ic.SelectKey == InputType.Select) selectHit = true; - } - else if (forceActionKey) - { - if (ic.PickKey == InputType.Use) pickHit = true; - if (ic.SelectKey == InputType.Use) selectHit = true; - } - else - { - pickHit = picker.IsKeyHit(ic.PickKey); - selectHit = picker.IsKeyHit(ic.SelectKey); - } - } - - - if (!pickHit && !selectHit) continue; - - Skill tempRequiredSkill; - if (!ic.HasRequiredSkills(picker, out tempRequiredSkill)) hasRequiredSkills = false; - - if (tempRequiredSkill != null) requiredSkill = tempRequiredSkill; - - bool showUiMsg = picker == Character.Controlled && Screen.Selected != GameMain.EditMapScreen; - if (!ignoreRequiredItems && !ic.HasRequiredItems(picker, showUiMsg)) continue; - if ((ic.CanBePicked && pickHit && ic.Pick(picker)) || - (ic.CanBeSelected && selectHit && ic.Select(picker))) - { - picked = true; - ic.ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker); - ic.PlaySound(ActionType.OnPicked, picker.WorldPosition); - - if (picker==Character.Controlled) GUIComponent.MouseOn = null; - - if (ic.CanBeSelected) selected = true; - } - } - - if (!picked) return false; - - System.Diagnostics.Debug.WriteLine("Item.Pick(" + picker + ", " + forceSelectKey + ")"); - - if (picker.SelectedConstruction == this) - { - if (picker.IsKeyHit(InputType.Select) || forceSelectKey) picker.SelectedConstruction = null; - } - else if (selected) - { - picker.SelectedConstruction = this; - } - - if (!hasRequiredSkills && Character.Controlled==picker && Screen.Selected != GameMain.EditMapScreen) - { - GUI.AddMessage("Your skills may be insufficient to use the item!", Color.Red, 5.0f); - if (requiredSkill != null) - { - GUI.AddMessage("("+requiredSkill.Name+" level "+requiredSkill.Level+" required)", Color.Red, 5.0f); - } - } - - if (Container!=null) Container.RemoveContained(this); - - return true; - } - - - public void Use(float deltaTime, Character character = null) - { - if (condition == 0.0f) return; - - bool remove = false; - - foreach (ItemComponent ic in components) - { - if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; - if (ic.Use(deltaTime, character)) - { - ic.WasUsed = true; - - ic.PlaySound(ActionType.OnUse, WorldPosition); - - ic.ApplyStatusEffects(ActionType.OnUse, deltaTime, character); - - if (ic.DeleteOnUse) remove = true; - } - } - - if (remove) Remove(); - } - - public void SecondaryUse(float deltaTime, Character character = null) - { - foreach (ItemComponent ic in components) - { - if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; - ic.SecondaryUse(deltaTime, character); - } - } - - public List GetHUDTexts(Character character) - { - List texts = new List(); - - foreach (ItemComponent ic in components) - { - if (string.IsNullOrEmpty(ic.Msg)) continue; - if (!ic.CanBePicked && !ic.CanBeSelected) continue; - - Color color = Color.Red; - if (ic.HasRequiredSkills(character) && ic.HasRequiredItems(character, false)) color = Color.Orange; - - texts.Add(new ColoredText(ic.Msg, color)); - } - - return texts; - } - - public bool Combine(Item item) - { - bool isCombined = false; - foreach (ItemComponent ic in components) - { - if (ic.Combine(item)) isCombined = true; - } - return isCombined; - } - - public void Drop(Character dropper = null, bool createNetworkEvent = true) - { - //if (dropper == Character.Controlled) - // new NetworkEvent(NetworkEventType.DropItem, ID, true); - - - //if (dropper != null) GameServer.Log(dropper.Name + " dropped " + Name, Color.Orange); - - foreach (ItemComponent ic in components) ic.Drop(dropper); - - if (Container != null) Container.RemoveContained(this); - } - - public void Equip(Character character) - { - foreach (ItemComponent ic in components) ic.Equip(character); - } - - public void Unequip(Character character) - { - character.DeselectItem(this); - foreach (ItemComponent ic in components) ic.Unequip(character); - } - - - public List GetProperties() - { - - List editableProperties = ObjectProperty.GetProperties(this); - - foreach (ItemComponent ic in components) - { - List componentProperties = ObjectProperty.GetProperties(ic); - foreach (var property in componentProperties) - { - editableProperties.Add(property); - } - } - - return editableProperties; - } - - private bool EnterProperty(GUITickBox tickBox) - { - var objectProperty = tickBox.UserData as ObjectProperty; - if (objectProperty == null) return false; - - objectProperty.TrySetValue(tickBox.Selected); - - return true; - } - - private bool EnterProperty(GUITextBox textBox, string text) - { - textBox.Color = Color.DarkGreen; - - var objectProperty = textBox.UserData as ObjectProperty; - if (objectProperty == null) return false; - - object prevValue = objectProperty.GetValue(); - - textBox.Deselect(); - - if (objectProperty.TrySetValue(text)) - { - textBox.Text = text; - - new NetworkEvent(NetworkEventType.UpdateProperty, ID, true, objectProperty.Name); - - return true; - } - else - { - if (prevValue != null) - { - textBox.Text = prevValue.ToString(); - } - return false; - } - } - - private bool PropertyChanged(GUITextBox textBox, string text) - { - textBox.Color = Color.Red; - - return true; - } - - public override XElement Save(XElement parentElement) - { - XElement element = new XElement("Item"); - - element.Add(new XAttribute("name", prefab.Name), - new XAttribute("ID", ID)); - - System.Diagnostics.Debug.Assert(Submarine != null); - - if (ResizeHorizontal || ResizeVertical) - { - element.Add(new XAttribute("rect", - (int)(rect.X - Submarine.HiddenSubPosition.X) + "," + - (int)(rect.Y - Submarine.HiddenSubPosition.Y) + "," + - rect.Width + "," + rect.Height)); - } - else - { - element.Add(new XAttribute("rect", - (int)(rect.X - Submarine.HiddenSubPosition.X) + "," + - (int)(rect.Y - Submarine.HiddenSubPosition.Y))); - } - - if (linkedTo != null && linkedTo.Count>0) - { - string[] linkedToIDs = new string[linkedTo.Count]; - - for (int i = 0; i < linkedTo.Count; i++ ) - { - linkedToIDs[i] = linkedTo[i].ID.ToString(); - } - - element.Add(new XAttribute("linked", string.Join(",", linkedToIDs))); - } - - - ObjectProperty.SaveProperties(this, element); - - foreach (ItemComponent ic in components) - { - ic.Save(element); - } - - parentElement.Add(element); - - return element; - } - - public static void Load(XElement element, Submarine submarine) - { - string rectString = ToolBox.GetAttributeString(element, "rect", "0,0,0,0"); - string[] rectValues = rectString.Split(','); - Rectangle rect = Rectangle.Empty; - if (rectValues.Length==4) - { - rect = new Rectangle( - int.Parse(rectValues[0]), - int.Parse(rectValues[1]), - int.Parse(rectValues[2]), - int.Parse(rectValues[3])); - } - else - { - rect = new Rectangle( - int.Parse(rectValues[0]), - int.Parse(rectValues[1]), - 0, 0); - } - - - string name = element.Attribute("name").Value; - - foreach (MapEntityPrefab ep in MapEntityPrefab.list) - { - ItemPrefab ip = ep as ItemPrefab; - if (ip == null) continue; - - if (ip.Name != name && (ip.Aliases == null || !ip.Aliases.Contains(name))) continue; - - if (rect.Width==0 && rect.Height==0) - { - rect.Width = (int)ip.Size.X; - rect.Height = (int)ip.Size.Y; - } - - Item item = new Item(rect, ip, submarine); - item.Submarine = submarine; - item.ID = (ushort)int.Parse(element.Attribute("ID").Value); - - item.linkedToID = new List(); - - foreach (XAttribute attribute in element.Attributes()) - { - ObjectProperty property = null; - if (!item.properties.TryGetValue(attribute.Name.ToString(), out property)) continue; - - bool shouldBeLoaded = false; - - foreach (var propertyAttribute in property.Attributes.OfType()) - { - if (propertyAttribute.isSaveable) - { - shouldBeLoaded = true; - break; - } - } - - if (shouldBeLoaded) property.TrySetValue(attribute.Value); - } - - string linkedToString = ToolBox.GetAttributeString(element, "linked", ""); - if (linkedToString!="") - { - string[] linkedToIds = linkedToString.Split(','); - for (int i = 0; i x.Name == subElement.Name.ToString()); - - if (component == null) continue; - - component.Load(subElement); - } - - break; - } - - } - - public override void OnMapLoaded() - { - FindHull(); - - foreach (ItemComponent ic in components) - { - ic.OnMapLoaded(); - } - } - - - public void NewComponentEvent(ItemComponent ic, bool isClient, bool isImportant) - { - int index = components.IndexOf(ic); - - new NetworkEvent(isImportant ? - NetworkEventType.ImportantComponentUpdate : NetworkEventType.ComponentUpdate, ID, isClient, index); - } - - public override bool FillNetworkData(NetworkEventType type, NetBuffer message, object data) - { - message.Write((byte)MathHelper.Clamp(condition*2.55f,0.0f,255.0f)); - - switch (type) - { - case NetworkEventType.DropItem: - if (body != null) body.FillNetworkData(message); - break; - case NetworkEventType.PhysicsBodyPosition: -#if DEBUG - System.Diagnostics.Debug.Assert(body != null, "Tried to send a PhysicsBodyPosition message for an item that has no physics body ("+Name+")"); -#else - if (body == null) return false; -#endif - - body.FillNetworkData(message); - break; - case NetworkEventType.ItemFixed: - byte requirementIndex = (byte)data; - - message.Write(requirementIndex); - break; - case NetworkEventType.InventoryUpdate: - var itemContainers = GetComponents(); - if (itemContainers == null || !itemContainers.Any()) return false; - - message.WriteRangedInteger(1, ItemContainer.MaxInventoryCount, itemContainers.Count); - foreach (ItemContainer container in itemContainers) - { - container.Inventory.FillNetworkData(NetworkEventType.InventoryUpdate, message, data); - } - - return true; - case NetworkEventType.ComponentUpdate: - case NetworkEventType.ImportantComponentUpdate: - - int componentIndex = (int)data; - if (componentIndex < 0 || componentIndex >= components.Count) return false; - - message.Write((byte)componentIndex); - bool sent = components[componentIndex].FillNetworkData(type, message); - if (sent) components[componentIndex].NetworkUpdateSent = true; - return sent; - case NetworkEventType.ApplyStatusEffect: - - ActionType actionType = (ActionType)data; - message.WriteRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length, (int)actionType); - - return true; - case NetworkEventType.UpdateProperty: - var allProperties = GetProperties(); - - ObjectProperty objectProperty = allProperties.Find(op => op.Name == (string)data); - if (objectProperty != null) - { - message.Write((string)data); - object value = objectProperty.GetValue(); - if (value is string) - { - message.Write((byte)0); - message.Write((string)value); - } - else if (value is float) - { - message.Write((byte)1); - message.Write((float)value); - } - else if (value is int) - { - message.Write((byte)2); - message.Write((int)value); - } - else if (value is bool) - { - message.Write((byte)3); - message.Write((bool)value); - } - else - { - message.Write((byte)200); - } - } - - - break; - } - - return true; - } - - public override bool ReadNetworkData(NetworkEventType type, NetIncomingMessage message, float sendingTime, out object data) - { - data = null; - - Condition = (float)message.ReadByte()/2.55f; - - Client sender = null; - if (GameMain.Server != null) - { - sender = GameMain.Server.ConnectedClients.Find(c => c.Connection == message.SenderConnection); - if (sender == null || sender.Character == null || !sender.Character.CanAccessItem(this)) - { - return false; - } - } - - switch (type) - { - case NetworkEventType.DropItem: - if (GameMain.Server != null) - { - Drop(sender.Character, false); - if (body != null) - { - body.TargetPosition = sender.Character.SimPosition; - body.MoveToTargetPosition(); - } - } - else - { - //client should not tell the server where the item should drop - Drop(null, false); - if (body != null) - { - body.ReadNetworkData(message, sendingTime); - body.MoveToTargetPosition(); - } - } - break; - case NetworkEventType.PhysicsBodyPosition: - //clients don't have authority over item positions - if (GameMain.Server != null) return false; - if (body != null) body.ReadNetworkData(message, sendingTime); - - FindHull(); - break; - case NetworkEventType.ItemFixed: - byte requirementIndex = message.ReadByte(); - data = requirementIndex; - - if (requirementIndex >= FixRequirements.Count) return false; - - FixRequirements[requirementIndex].Fixed = true; - break; - case NetworkEventType.InventoryUpdate: - var itemContainers = GetComponents(); - if (itemContainers == null || !itemContainers.Any()) return false; - - int containerCount = message.ReadRangedInteger(1, ItemContainer.MaxInventoryCount); - for (int i = 0; i < containerCount;i++ ) - { - itemContainers[i].Inventory.ReadNetworkData(type, message, sendingTime); - } - - break; - case NetworkEventType.ComponentUpdate: - case NetworkEventType.ImportantComponentUpdate: - int componentIndex = message.ReadByte(); - - data = componentIndex; - - if (componentIndex < 0 || componentIndex > components.Count - 1) return false; - - components[componentIndex].NetworkUpdateSent = true; - components[componentIndex].ReadNetworkData(type, message, sendingTime); - break; - case NetworkEventType.ApplyStatusEffect: - - ActionType actionType = (ActionType)message.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length); - - data = actionType; - - ApplyStatusEffects(actionType, 1.0f); - - break; - case NetworkEventType.UpdateProperty: - string propertyName = ""; - - try - { - propertyName = message.ReadString(); - data = propertyName; - } - catch - { - return false; - } - - var allProperties = GetProperties(); - ObjectProperty property = allProperties.Find(op => op.Name == propertyName); - if (property == null) return false; - - try - { - switch (message.ReadByte()) - { - case 0: - property.TrySetValue(message.ReadString()); - break; - case 1: - property.TrySetValue(message.ReadFloat()); - break; - case 2: - property.TrySetValue(message.ReadInt32()); - break; - case 3: - property.TrySetValue(message.ReadBoolean()); - break; - } - } - - catch - { - return false; - } - - break; - } - - return true; - } - - public override void Remove() - { - base.Remove(); - - Removed = true; - - //sprite.Remove(); - //if (body != null) body.Remove(); - - foreach (ItemComponent ic in components) - { - ic.Remove(); - } - ItemList.Remove(this); - - foreach (Item it in ItemList) - { - if (it.linkedTo.Contains(this)) - { - it.linkedTo.Remove(this); - } - } - } - - } -} + } + + public List GetConnectedComponents(bool recursive = false) + { + List connectedComponents = new List(); + + if (recursive) + { + List alreadySearched = new List() {this}; + GetConnectedComponentsRecursive(alreadySearched, connectedComponents); + + return connectedComponents; + } + + ConnectionPanel connectionPanel = GetComponent(); + if (connectionPanel == null) return connectedComponents; + + + foreach (Connection c in connectionPanel.Connections) + { + var recipients = c.Recipients; + foreach (Connection recipient in recipients) + { + var component = recipient.Item.GetComponent(); + if (component != null) connectedComponents.Add(component); + } + } + + return connectedComponents; + } + + private void GetConnectedComponentsRecursive(List alreadySearched, List connectedComponents) + { + alreadySearched.Add(this); + + ConnectionPanel connectionPanel = GetComponent(); + if (connectionPanel == null) return; + + foreach (Connection c in connectionPanel.Connections) + { + var recipients = c.Recipients; + foreach (Connection recipient in recipients) + { + if (alreadySearched.Contains(recipient.Item)) continue; + + var component = recipient.Item.GetComponent(); + + if (component != null) + { + connectedComponents.Add(component); + } + + recipient.Item.GetConnectedComponentsRecursive(alreadySearched, connectedComponents); + + + } + } + } + + public void SendSignal(int stepsTaken, string signal, string connectionName, float power = 0.0f) + { + stepsTaken++; + + ConnectionPanel panel = GetComponent(); + if (panel == null) return; + foreach (Connection c in panel.Connections) + { + if (c.Name != connectionName) continue; + + if (stepsTaken > 10) + { + //use a coroutine to prevent infinite loops by creating a one + //frame delay if the "signal chain" gets too long + CoroutineManager.StartCoroutine(SendSignal(signal, c, power)); + } + else + { + c.SendSignal(stepsTaken, signal, this, power); + } + } + } + + private IEnumerable SendSignal(string signal, Connection connection, float power = 0.0f) + { + //wait one frame + yield return CoroutineStatus.Running; + + ConnectionPanel panel = GetComponent(); + if (panel == null) yield return CoroutineStatus.Success; + + connection.SendSignal(0, signal, this, power); + + yield return CoroutineStatus.Success; + } + + public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull = null, Item[] ignoredItems = null) + { + float dist; + return FindPickable(position, pickPosition, hull, ignoredItems, out dist); + } + + /// Position of the Character doing the pick, only items that are close enough to this are checked + /// the item closest to pickPosition is returned + /// If a hull is specified, only items within that hull are checked + public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull, Item[] ignoredItems, out float distance) + { + float closestDist = 0.0f, dist; + Item closest = null; + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(position); + Vector2 displayPickPos = ConvertUnits.ToDisplayUnits(pickPosition); + + distance = 1000.0f; + + foreach (Item item in ItemList) + { + if (ignoredItems != null && ignoredItems.Contains(item)) continue; + if (item.body != null && !item.body.Enabled) continue; + + if (item.PickDistance == 0.0f && !item.prefab.Triggers.Any()) continue; + + Pickable pickableComponent = item.GetComponent(); + if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) continue; + + float pickDist = Vector2.Distance(item.WorldPosition, displayPickPos); + + bool insideTrigger = false; + foreach (Rectangle trigger in item.prefab.Triggers) + { + Rectangle transformedTrigger = item.TransformTrigger(trigger, true); + + if (!Submarine.RectContains(transformedTrigger, displayPos)) continue; + + insideTrigger = true; + + Vector2 triggerCenter = new Vector2(transformedTrigger.Center.X, transformedTrigger.Y - transformedTrigger.Height / 2); + pickDist = Math.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y)); + } + + if (!insideTrigger && item.prefab.Triggers.Any()) continue; + + if (pickDist > item.PickDistance && item.PickDistance > 0.0f) continue; + + dist = item.Sprite.Depth * 10.0f + pickDist; + if (item.IsMouseOn(displayPickPos)) dist = dist * 0.1f; + + if (closest == null || dist < closestDist) + { + if (item.PickDistance > 0.0f && Vector2.Distance(displayPos, item.WorldPosition) > item.prefab.PickDistance) continue; + + if (!item.prefab.PickThroughWalls && Screen.Selected != GameMain.EditMapScreen && !insideTrigger) + { + Body body = Submarine.CheckVisibility(item.Submarine == null ? position : position - item.Submarine.SimPosition, item.SimPosition, true); + if (body != null && body.UserData as Item != item) continue; + } + + closestDist = dist; + closest = item; + + distance = pickDist; + } + } + + return closest; + } + + public bool IsInsideTrigger(Vector2 worldPosition) + { + foreach (Rectangle trigger in prefab.Triggers) + { + Rectangle transformedTrigger = TransformTrigger(trigger, true); + + if (Submarine.RectContains(transformedTrigger, worldPosition)) return true; + } + + return false; + } + + public bool IsInPickRange(Vector2 worldPosition) + { + if (IsInsideTrigger(worldPosition)) return true; + + return Vector2.Distance(WorldPosition, worldPosition) < PickDistance; + } + + public bool Pick(Character picker, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceActionKey=false) + { + bool hasRequiredSkills = true; + + bool picked = false, selected = false; + + Skill requiredSkill = null; + + foreach (ItemComponent ic in components) + { + bool pickHit = false, selectHit = false; + if (Screen.Selected == GameMain.EditMapScreen) + { + pickHit = picker.IsKeyHit(InputType.Select); + selectHit = picker.IsKeyHit(InputType.Select); + } + else + { + if (forceSelectKey) + { + if (ic.PickKey == InputType.Select) pickHit = true; + if (ic.SelectKey == InputType.Select) selectHit = true; + } + else if (forceActionKey) + { + if (ic.PickKey == InputType.Use) pickHit = true; + if (ic.SelectKey == InputType.Use) selectHit = true; + } + else + { + pickHit = picker.IsKeyHit(ic.PickKey); + selectHit = picker.IsKeyHit(ic.SelectKey); + } + } + + + if (!pickHit && !selectHit) continue; + + Skill tempRequiredSkill; + if (!ic.HasRequiredSkills(picker, out tempRequiredSkill)) hasRequiredSkills = false; + + if (tempRequiredSkill != null) requiredSkill = tempRequiredSkill; + + bool showUiMsg = picker == Character.Controlled && Screen.Selected != GameMain.EditMapScreen; + if (!ignoreRequiredItems && !ic.HasRequiredItems(picker, showUiMsg)) continue; + if ((ic.CanBePicked && pickHit && ic.Pick(picker)) || + (ic.CanBeSelected && selectHit && ic.Select(picker))) + { + picked = true; + ic.ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker); + ic.PlaySound(ActionType.OnPicked, picker.WorldPosition); + + if (picker==Character.Controlled) GUIComponent.MouseOn = null; + + if (ic.CanBeSelected) selected = true; + } + } + + if (!picked) return false; + + System.Diagnostics.Debug.WriteLine("Item.Pick(" + picker + ", " + forceSelectKey + ")"); + + if (picker.SelectedConstruction == this) + { + if (picker.IsKeyHit(InputType.Select) || forceSelectKey) picker.SelectedConstruction = null; + } + else if (selected) + { + picker.SelectedConstruction = this; + } + + if (!hasRequiredSkills && Character.Controlled==picker && Screen.Selected != GameMain.EditMapScreen) + { + GUI.AddMessage("Your skills may be insufficient to use the item!", Color.Red, 5.0f); + if (requiredSkill != null) + { + GUI.AddMessage("("+requiredSkill.Name+" level "+requiredSkill.Level+" required)", Color.Red, 5.0f); + } + } + + if (Container!=null) Container.RemoveContained(this); + + return true; + } + + + public void Use(float deltaTime, Character character = null) + { + if (condition == 0.0f) return; + + bool remove = false; + + foreach (ItemComponent ic in components) + { + if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; + if (ic.Use(deltaTime, character)) + { + ic.WasUsed = true; + + ic.PlaySound(ActionType.OnUse, WorldPosition); + + ic.ApplyStatusEffects(ActionType.OnUse, deltaTime, character); + + if (ic.DeleteOnUse) remove = true; + } + } + + if (remove) Remove(); + } + + public void SecondaryUse(float deltaTime, Character character = null) + { + foreach (ItemComponent ic in components) + { + if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; + ic.SecondaryUse(deltaTime, character); + } + } + + public List GetHUDTexts(Character character) + { + List texts = new List(); + + foreach (ItemComponent ic in components) + { + if (string.IsNullOrEmpty(ic.Msg)) continue; + if (!ic.CanBePicked && !ic.CanBeSelected) continue; + + Color color = Color.Red; + if (ic.HasRequiredSkills(character) && ic.HasRequiredItems(character, false)) color = Color.Orange; + + texts.Add(new ColoredText(ic.Msg, color)); + } + + return texts; + } + + public bool Combine(Item item) + { + bool isCombined = false; + foreach (ItemComponent ic in components) + { + if (ic.Combine(item)) isCombined = true; + } + return isCombined; + } + + public void Drop(Character dropper = null, bool createNetworkEvent = true) + { + //if (dropper == Character.Controlled) + // new NetworkEvent(NetworkEventType.DropItem, ID, true); + + + //if (dropper != null) GameServer.Log(dropper.Name + " dropped " + Name, Color.Orange); + + foreach (ItemComponent ic in components) ic.Drop(dropper); + + if (Container != null) Container.RemoveContained(this); + } + + public void Equip(Character character) + { + foreach (ItemComponent ic in components) ic.Equip(character); + } + + public void Unequip(Character character) + { + character.DeselectItem(this); + foreach (ItemComponent ic in components) ic.Unequip(character); + } + + + public List GetProperties() + { + + List editableProperties = ObjectProperty.GetProperties(this); + + foreach (ItemComponent ic in components) + { + List componentProperties = ObjectProperty.GetProperties(ic); + foreach (var property in componentProperties) + { + editableProperties.Add(property); + } + } + + return editableProperties; + } + + private bool EnterProperty(GUITickBox tickBox) + { + var objectProperty = tickBox.UserData as ObjectProperty; + if (objectProperty == null) return false; + + objectProperty.TrySetValue(tickBox.Selected); + + return true; + } + + private bool EnterProperty(GUITextBox textBox, string text) + { + textBox.Color = Color.DarkGreen; + + var objectProperty = textBox.UserData as ObjectProperty; + if (objectProperty == null) return false; + + object prevValue = objectProperty.GetValue(); + + textBox.Deselect(); + + if (objectProperty.TrySetValue(text)) + { + textBox.Text = text; + + new NetworkEvent(NetworkEventType.UpdateProperty, ID, true, objectProperty.Name); + + return true; + } + else + { + if (prevValue != null) + { + textBox.Text = prevValue.ToString(); + } + return false; + } + } + + private bool PropertyChanged(GUITextBox textBox, string text) + { + textBox.Color = Color.Red; + + return true; + } + + public override XElement Save(XElement parentElement) + { + XElement element = new XElement("Item"); + + element.Add(new XAttribute("name", prefab.Name), + new XAttribute("ID", ID)); + + System.Diagnostics.Debug.Assert(Submarine != null); + + if (ResizeHorizontal || ResizeVertical) + { + element.Add(new XAttribute("rect", + (int)(rect.X - Submarine.HiddenSubPosition.X) + "," + + (int)(rect.Y - Submarine.HiddenSubPosition.Y) + "," + + rect.Width + "," + rect.Height)); + } + else + { + element.Add(new XAttribute("rect", + (int)(rect.X - Submarine.HiddenSubPosition.X) + "," + + (int)(rect.Y - Submarine.HiddenSubPosition.Y))); + } + + if (linkedTo != null && linkedTo.Count>0) + { + string[] linkedToIDs = new string[linkedTo.Count]; + + for (int i = 0; i < linkedTo.Count; i++ ) + { + linkedToIDs[i] = linkedTo[i].ID.ToString(); + } + + element.Add(new XAttribute("linked", string.Join(",", linkedToIDs))); + } + + + ObjectProperty.SaveProperties(this, element); + + foreach (ItemComponent ic in components) + { + ic.Save(element); + } + + parentElement.Add(element); + + return element; + } + + public static void Load(XElement element, Submarine submarine) + { + string rectString = ToolBox.GetAttributeString(element, "rect", "0,0,0,0"); + string[] rectValues = rectString.Split(','); + Rectangle rect = Rectangle.Empty; + if (rectValues.Length==4) + { + rect = new Rectangle( + int.Parse(rectValues[0]), + int.Parse(rectValues[1]), + int.Parse(rectValues[2]), + int.Parse(rectValues[3])); + } + else + { + rect = new Rectangle( + int.Parse(rectValues[0]), + int.Parse(rectValues[1]), + 0, 0); + } + + + string name = element.Attribute("name").Value; + + foreach (MapEntityPrefab ep in MapEntityPrefab.list) + { + ItemPrefab ip = ep as ItemPrefab; + if (ip == null) continue; + + if (ip.Name != name && (ip.Aliases == null || !ip.Aliases.Contains(name))) continue; + + if (rect.Width==0 && rect.Height==0) + { + rect.Width = (int)ip.Size.X; + rect.Height = (int)ip.Size.Y; + } + + Item item = new Item(rect, ip, submarine); + item.Submarine = submarine; + item.ID = (ushort)int.Parse(element.Attribute("ID").Value); + + item.linkedToID = new List(); + + foreach (XAttribute attribute in element.Attributes()) + { + ObjectProperty property = null; + if (!item.properties.TryGetValue(attribute.Name.ToString(), out property)) continue; + + bool shouldBeLoaded = false; + + foreach (var propertyAttribute in property.Attributes.OfType()) + { + if (propertyAttribute.isSaveable) + { + shouldBeLoaded = true; + break; + } + } + + if (shouldBeLoaded) property.TrySetValue(attribute.Value); + } + + string linkedToString = ToolBox.GetAttributeString(element, "linked", ""); + if (linkedToString!="") + { + string[] linkedToIds = linkedToString.Split(','); + for (int i = 0; i x.Name == subElement.Name.ToString()); + + if (component == null) continue; + + component.Load(subElement); + } + + break; + } + + } + + public override void OnMapLoaded() + { + FindHull(); + + foreach (ItemComponent ic in components) + { + ic.OnMapLoaded(); + } + } + + + public void NewComponentEvent(ItemComponent ic, bool isClient, bool isImportant) + { + int index = components.IndexOf(ic); + + new NetworkEvent(isImportant ? + NetworkEventType.ImportantComponentUpdate : NetworkEventType.ComponentUpdate, ID, isClient, index); + } + + public override bool FillNetworkData(NetworkEventType type, NetBuffer message, object data) + { + message.Write((byte)MathHelper.Clamp(condition*2.55f,0.0f,255.0f)); + + switch (type) + { + case NetworkEventType.DropItem: + if (body != null) body.FillNetworkData(message); + break; + case NetworkEventType.PhysicsBodyPosition: +#if DEBUG + System.Diagnostics.Debug.Assert(body != null, "Tried to send a PhysicsBodyPosition message for an item that has no physics body ("+Name+")"); +#else + if (body == null) return false; +#endif + + body.FillNetworkData(message); + break; + case NetworkEventType.ItemFixed: + byte requirementIndex = (byte)data; + + message.Write(requirementIndex); + break; + case NetworkEventType.InventoryUpdate: + var itemContainers = GetComponents(); + if (itemContainers == null || !itemContainers.Any()) return false; + + message.WriteRangedInteger(1, ItemContainer.MaxInventoryCount, itemContainers.Count); + foreach (ItemContainer container in itemContainers) + { + container.Inventory.FillNetworkData(NetworkEventType.InventoryUpdate, message, data); + } + + return true; + case NetworkEventType.ComponentUpdate: + case NetworkEventType.ImportantComponentUpdate: + + int componentIndex = (int)data; + if (componentIndex < 0 || componentIndex >= components.Count) return false; + + message.Write((byte)componentIndex); + bool sent = components[componentIndex].FillNetworkData(type, message); + if (sent) components[componentIndex].NetworkUpdateSent = true; + return sent; + case NetworkEventType.ApplyStatusEffect: + + ActionType actionType = (ActionType)data; + message.WriteRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length, (int)actionType); + + return true; + case NetworkEventType.UpdateProperty: + var allProperties = GetProperties(); + + ObjectProperty objectProperty = allProperties.Find(op => op.Name == (string)data); + if (objectProperty != null) + { + message.Write((string)data); + object value = objectProperty.GetValue(); + if (value is string) + { + message.Write((byte)0); + message.Write((string)value); + } + else if (value is float) + { + message.Write((byte)1); + message.Write((float)value); + } + else if (value is int) + { + message.Write((byte)2); + message.Write((int)value); + } + else if (value is bool) + { + message.Write((byte)3); + message.Write((bool)value); + } + else + { + message.Write((byte)200); + } + } + + + break; + } + + return true; + } + + public override bool ReadNetworkData(NetworkEventType type, NetIncomingMessage message, float sendingTime, out object data) + { + data = null; + + Condition = (float)message.ReadByte()/2.55f; + + Client sender = null; + if (GameMain.Server != null) + { + sender = GameMain.Server.ConnectedClients.Find(c => c.Connection == message.SenderConnection); + if (sender == null || sender.Character == null || !sender.Character.CanAccessItem(this)) + { + return false; + } + } + + switch (type) + { + case NetworkEventType.DropItem: + if (GameMain.Server != null) + { + Drop(sender.Character, false); + if (body != null) + { + body.TargetPosition = sender.Character.SimPosition; + body.MoveToTargetPosition(); + } + } + else + { + //client should not tell the server where the item should drop + Drop(null, false); + if (body != null) + { + body.ReadNetworkData(message, sendingTime); + body.MoveToTargetPosition(); + } + } + break; + case NetworkEventType.PhysicsBodyPosition: + //clients don't have authority over item positions + if (GameMain.Server != null) return false; + if (body != null) body.ReadNetworkData(message, sendingTime); + + FindHull(); + break; + case NetworkEventType.ItemFixed: + byte requirementIndex = message.ReadByte(); + data = requirementIndex; + + if (requirementIndex >= FixRequirements.Count) return false; + + FixRequirements[requirementIndex].Fixed = true; + break; + case NetworkEventType.InventoryUpdate: + var itemContainers = GetComponents(); + if (itemContainers == null || !itemContainers.Any()) return false; + + int containerCount = message.ReadRangedInteger(1, ItemContainer.MaxInventoryCount); + for (int i = 0; i < containerCount;i++ ) + { + itemContainers[i].Inventory.ReadNetworkData(type, message, sendingTime); + } + + break; + case NetworkEventType.ComponentUpdate: + case NetworkEventType.ImportantComponentUpdate: + int componentIndex = message.ReadByte(); + + data = componentIndex; + + if (componentIndex < 0 || componentIndex > components.Count - 1) return false; + + components[componentIndex].NetworkUpdateSent = true; + components[componentIndex].ReadNetworkData(type, message, sendingTime); + break; + case NetworkEventType.ApplyStatusEffect: + + ActionType actionType = (ActionType)message.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length); + + data = actionType; + + ApplyStatusEffects(actionType, 1.0f); + + break; + case NetworkEventType.UpdateProperty: + string propertyName = ""; + + try + { + propertyName = message.ReadString(); + data = propertyName; + } + catch + { + return false; + } + + var allProperties = GetProperties(); + ObjectProperty property = allProperties.Find(op => op.Name == propertyName); + if (property == null) return false; + + try + { + switch (message.ReadByte()) + { + case 0: + property.TrySetValue(message.ReadString()); + break; + case 1: + property.TrySetValue(message.ReadFloat()); + break; + case 2: + property.TrySetValue(message.ReadInt32()); + break; + case 3: + property.TrySetValue(message.ReadBoolean()); + break; + } + } + + catch + { + return false; + } + + break; + } + + return true; + } + + public override void Remove() + { + base.Remove(); + + Removed = true; + + //sprite.Remove(); + //if (body != null) body.Remove(); + + foreach (ItemComponent ic in components) + { + ic.Remove(); + } + ItemList.Remove(this); + + foreach (Item it in ItemList) + { + if (it.linkedTo.Contains(this)) + { + it.linkedTo.Remove(this); + } + } + } + + } +} diff --git a/Subsurface/Source/Items/ItemPrefab.cs b/Subsurface/Source/Items/ItemPrefab.cs index 91ce91db1..8a154b5aa 100644 --- a/Subsurface/Source/Items/ItemPrefab.cs +++ b/Subsurface/Source/Items/ItemPrefab.cs @@ -128,16 +128,16 @@ namespace Barotrauma get { return size; } } - public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) + public override void UpdatePlacing(Camera cam) { - Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); if (PlayerInput.RightButtonClicked()) { selected = null; return; } - + if (!resizeHorizontal && !resizeVertical) { if (PlayerInput.LeftButtonClicked()) @@ -145,16 +145,14 @@ namespace Barotrauma var item = new Item(new Rectangle((int)position.X, (int)position.Y, (int)sprite.size.X, (int)sprite.size.Y), this, Submarine.MainSub); //constructor.Invoke(lobject); item.Submarine = Submarine.MainSub; - item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub==null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f); + item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f); item.FindHull(); placePosition = Vector2.Zero; - // selected = null; + // selected = null; return; } - - sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f), SpriteColor); } else { @@ -179,17 +177,54 @@ namespace Barotrauma item.Submarine = Submarine.MainSub; item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f); item.FindHull(); - + //selected = null; return; } position = placePosition; } + } + + //if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null; + + } + + public override void DrawPlacing(SpriteBatch spriteBatch,Camera cam) + { + Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + + if (PlayerInput.RightButtonClicked()) + { + selected = null; + return; + } + + if (!resizeHorizontal && !resizeVertical) + { + sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f), SpriteColor); + } + else + { + Vector2 placeSize = size; + + if (placePosition == Vector2.Zero) + { + if (PlayerInput.LeftButtonHeld()) 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); + + position = placePosition; + } if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, SpriteColor); } - + //if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null; } diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 1f25e71b5..afc3071f4 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -189,7 +189,7 @@ namespace Barotrauma Color clr = (open == 0.0f) ? Color.Red : Color.Cyan; if (isHighlighted) clr = Color.Gold; - GUI.DrawRectangle(sb, new Rectangle(WorldRect.X, -WorldRect.Y, rect.Width, rect.Height), clr * 0.5f, true); + GUI.DrawRectangle(sb, new Rectangle(WorldRect.X, -WorldRect.Y, rect.Width, rect.Height), clr * 0.5f, true,0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); for (int i = 0; i < linkedTo.Count; i++) { @@ -212,7 +212,10 @@ namespace Barotrauma GUI.DrawRectangle(sb, new Vector2(WorldRect.X - 5, -WorldRect.Y - 5), new Vector2(rect.Width + 10, rect.Height + 10), - Color.Red); + Color.Red, + false, + 0, + (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); } } diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 8a69404e5..76ef052cb 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -570,7 +570,8 @@ namespace Barotrauma GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X, -drawRect.Y), new Vector2(rect.Width, rect.Height), - Color.Black,true); + Color.Black,true, + 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); } if (!ShowHulls && !GameMain.DebugDraw) return; @@ -585,11 +586,11 @@ namespace Barotrauma GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X, -drawRect.Y), new Vector2(rect.Width, rect.Height), - Color.Blue); + Color.Blue,false,0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height), - Color.Red*((100.0f-OxygenPercentage)/400.0f), true); + Color.Red*((100.0f-OxygenPercentage)/400.0f), true,0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); if (GameMain.DebugDraw) { @@ -604,7 +605,7 @@ namespace Barotrauma GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X + 5, -drawRect.Y + 5), new Vector2(rect.Width - 10, rect.Height - 10), - isHighlighted ? Color.LightBlue*0.5f : Color.Red*0.5f, true); + isHighlighted ? Color.LightBlue*0.5f : Color.Red*0.5f, true,0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); } } diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index c608941e8..0ea125f47 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -451,7 +451,7 @@ namespace Barotrauma GUI.DrawRectangle(spriteBatch, new Vector2(e.WorldRect.X, -e.WorldRect.Y) + moveAmount, new Vector2(e.rect.Width, e.rect.Height), - Color.DarkRed); + Color.DarkRed,false,0,(int)Math.Max(1.5f/GameScreen.Selected.Cam.Zoom,1.0f)); //stop dragging the "selection rectangle" selectionPos = Vector2.Zero; @@ -459,7 +459,7 @@ namespace Barotrauma } if (selectionPos != null && selectionPos != Vector2.Zero) { - GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed); + GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed,false,0,(int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f)); } } @@ -543,7 +543,7 @@ namespace Barotrauma bool highlighted = Vector2.Distance(PlayerInput.MousePosition, handlePos)<5.0f; - GUI.DrawRectangle(spriteBatch, handlePos - new Vector2(3.0f, 3.0f), new Vector2(6.0f, 6.0f), Color.White * (highlighted ? 1.0f : 0.6f), true); + GUI.DrawRectangle(spriteBatch, handlePos - new Vector2(3.0f, 3.0f), new Vector2(6.0f, 6.0f), Color.White * (highlighted ? 1.0f : 0.6f), true,0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f)); if (highlighted) { diff --git a/Subsurface/Source/Map/MapEntityPrefab.cs b/Subsurface/Source/Map/MapEntityPrefab.cs index 5d357841d..d3f12afdc 100644 --- a/Subsurface/Source/Map/MapEntityPrefab.cs +++ b/Subsurface/Source/Map/MapEntityPrefab.cs @@ -129,18 +129,14 @@ namespace Barotrauma Category = MapEntityCategory.Structure; } - public virtual void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) + public virtual void UpdatePlacing(Camera cam) { Vector2 placeSize = Submarine.GridSize; if (placePosition == Vector2.Zero) { Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); - - GUI.DrawLine(spriteBatch, new Vector2(position.X-GameMain.GraphicsWidth, -position.Y), new Vector2(position.X+GameMain.GraphicsWidth, -position.Y), Color.White); - - GUI.DrawLine(spriteBatch, new Vector2(position.X, -(position.Y - GameMain.GraphicsHeight)), new Vector2(position.X, -(position.Y + GameMain.GraphicsHeight)), Color.White); - + if (PlayerInput.LeftButtonHeld()) placePosition = position; } else @@ -167,7 +163,6 @@ namespace Barotrauma } newRect.Y = -newRect.Y; - GUI.DrawRectangle(spriteBatch, newRect, Color.DarkBlue); } if (PlayerInput.RightButtonHeld()) @@ -177,6 +172,39 @@ namespace Barotrauma } } + public virtual void DrawPlacing(SpriteBatch spriteBatch, Camera cam) + { + Vector2 placeSize = Submarine.GridSize; + + if (placePosition == Vector2.Zero) + { + Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + + GUI.DrawLine(spriteBatch, new Vector2(position.X - GameMain.GraphicsWidth, -position.Y), new Vector2(position.X + GameMain.GraphicsWidth, -position.Y), Color.White,0,(int)(2.0f/cam.Zoom)); + + GUI.DrawLine(spriteBatch, new Vector2(position.X, -(position.Y - GameMain.GraphicsHeight)), new Vector2(position.X, -(position.Y + GameMain.GraphicsHeight)), Color.White, 0, (int)(2.0f / cam.Zoom)); + } + else + { + Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + + if (resizeHorizontal) placeSize.X = position.X - placePosition.X; + if (resizeVertical) placeSize.Y = placePosition.Y - position.Y; + + Rectangle newRect = Submarine.AbsRect(placePosition, placeSize); + newRect.Width = (int)Math.Max(newRect.Width, Submarine.GridSize.X); + newRect.Height = (int)Math.Max(newRect.Height, Submarine.GridSize.Y); + + if (Submarine.MainSub != null) + { + newRect.Location -= Submarine.MainSub.Position.ToPoint(); + } + + newRect.Y = -newRect.Y; + GUI.DrawRectangle(spriteBatch, newRect, Color.DarkBlue); + } + } + protected virtual void CreateInstance(Rectangle rect) { object[] lobject = new object[] { this, rect }; diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index e8b67d086..f7514a0fd 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -143,8 +143,24 @@ namespace Barotrauma } set { + Rectangle oldRect = Rect; base.Rect = value; if (prefab.HasBody) CreateSections(); + else + { + foreach (WallSection sec in sections) + { + Rectangle secRect = sec.rect; + secRect.X -= oldRect.X; secRect.Y -= oldRect.Y; + secRect.X *= value.Width; secRect.X /= oldRect.Width; + secRect.Y *= value.Height; secRect.Y /= oldRect.Height; + secRect.Width *= value.Width; secRect.Width /= oldRect.Width; + secRect.Height *= value.Height; secRect.Height /= oldRect.Height; + secRect.X += value.X; secRect.Y += value.Y; + sec.rect = secRect; + } + } + } } diff --git a/Subsurface/Source/Map/StructurePrefab.cs b/Subsurface/Source/Map/StructurePrefab.cs index 9779779d6..3492fad31 100644 --- a/Subsurface/Source/Map/StructurePrefab.cs +++ b/Subsurface/Source/Map/StructurePrefab.cs @@ -162,7 +162,7 @@ namespace Barotrauma return sp; } - public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) + public override void UpdatePlacing(Camera cam) { Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); //Vector2 placeSize = size; @@ -183,7 +183,7 @@ namespace Barotrauma else { Vector2 placeSize = size; - if (resizeHorizontal) placeSize.X = position.X - placePosition.X; + if (resizeHorizontal) placeSize.X = position.X - placePosition.X; if (resizeVertical) placeSize.Y = placePosition.Y - position.Y; newRect = Submarine.AbsRect(placePosition, placeSize); @@ -199,13 +199,41 @@ namespace Barotrauma return; } } + + if (PlayerInput.RightButtonHeld()) selected = null; + } + + public override void DrawPlacing(SpriteBatch spriteBatch, Camera cam) + { + Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + //Vector2 placeSize = size; + + Rectangle newRect = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y); + + + if (placePosition == Vector2.Zero) + { + if (PlayerInput.LeftButtonHeld()) + placePosition = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); + + newRect.X = (int)position.X; + newRect.Y = (int)position.Y; + + //sprite.Draw(spriteBatch, new Vector2(position.X, -position.Y), placeSize, Color.White); + } + else + { + Vector2 placeSize = size; + if (resizeHorizontal) placeSize.X = position.X - placePosition.X; + if (resizeVertical) placeSize.Y = placePosition.Y - position.Y; + + newRect = Submarine.AbsRect(placePosition, placeSize); + } sprite.DrawTiled(spriteBatch, new Vector2(newRect.X, -newRect.Y), new Vector2(newRect.Width, newRect.Height), Color.White); - GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X - GameMain.GraphicsWidth, -newRect.Y, newRect.Width + GameMain.GraphicsWidth*2, newRect.Height), Color.White); - GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X, -newRect.Y - GameMain.GraphicsHeight, newRect.Width, newRect.Height + GameMain.GraphicsHeight*2), Color.White); - - if (PlayerInput.RightButtonHeld()) selected = null; + GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X - GameMain.GraphicsWidth, -newRect.Y, newRect.Width + GameMain.GraphicsWidth * 2, newRect.Height), Color.White); + GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X, -newRect.Y - GameMain.GraphicsHeight, newRect.Width, newRect.Height + GameMain.GraphicsHeight * 2), Color.White); } } } diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 3afc678fa..548b6ab20 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -38,7 +38,7 @@ namespace Barotrauma private Tutorials.EditorTutorial tutorial; - public Camera Cam + public override Camera Cam { get { return cam; } } @@ -887,6 +887,43 @@ namespace Barotrauma GUItabs[selectedTab].Update((float)deltaTime); if (PlayerInput.RightButtonClicked()) selectedTab = -1; } + + if (!characterMode && !wiringMode) + { + if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(cam); + } + + if ((characterMode || wiringMode) && dummyCharacter != null) + { + foreach (MapEntity me in MapEntity.mapEntityList) + { + me.IsHighlighted = false; + } + + dummyCharacter.AnimController.FindHull(dummyCharacter.CursorWorldPosition, false); + + foreach (Item item in dummyCharacter.SelectedItems) + { + if (item == null) continue; + item.SetTransform(dummyCharacter.SimPosition, 0.0f); + + item.Update(cam, (float)deltaTime); + } + + if (dummyCharacter.SelectedConstruction != null) + { + if (dummyCharacter.SelectedConstruction != null) + { + dummyCharacter.SelectedConstruction.UpdateHUD(dummyCharacter); + } + + if (PlayerInput.KeyHit(InputType.Select) && dummyCharacter.ClosestItem != dummyCharacter.SelectedConstruction) dummyCharacter.SelectedConstruction = null; + } + + CharacterHUD.Update((float)deltaTime, dummyCharacter); + } + + GUI.Update((float)deltaTime); } /// @@ -913,7 +950,7 @@ namespace Barotrauma if (!characterMode && !wiringMode) { - if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); + if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.DrawPlacing(spriteBatch,cam); MapEntity.DrawSelecting(spriteBatch, cam); } @@ -932,28 +969,11 @@ namespace Barotrauma if ((characterMode || wiringMode) && dummyCharacter != null) { - foreach (MapEntity me in MapEntity.mapEntityList) - { - me.IsHighlighted = false; - } - - dummyCharacter.AnimController.FindHull(dummyCharacter.CursorWorldPosition, false); - - foreach (Item item in dummyCharacter.SelectedItems) - { - if (item == null) continue; - item.SetTransform(dummyCharacter.SimPosition, 0.0f); - - item.Update(cam, (float)deltaTime); - } - if (dummyCharacter.SelectedConstruction != null) { dummyCharacter.SelectedConstruction.DrawHUD(spriteBatch, dummyCharacter); - - if (PlayerInput.KeyHit(InputType.Select) && dummyCharacter.ClosestItem != dummyCharacter.SelectedConstruction) dummyCharacter.SelectedConstruction = null; } - + dummyCharacter.DrawHUD(spriteBatch, cam); if (wiringMode) wiringToolPanel.Draw(spriteBatch); diff --git a/Subsurface/Source/Screens/Screen.cs b/Subsurface/Source/Screens/Screen.cs index cf45ddc39..0f69e280d 100644 --- a/Subsurface/Source/Screens/Screen.cs +++ b/Subsurface/Source/Screens/Screen.cs @@ -28,6 +28,11 @@ namespace Barotrauma selected = this; } + public virtual Camera Cam + { + get { return null; } + } + public virtual void Update(double deltaTime) { }