using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using FarseerPhysics; using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Barotrauma.Networking; using System; using Barotrauma.Items.Components; using System.ComponentModel; using System.Collections.ObjectModel; using FarseerPhysics.Dynamics; namespace Barotrauma { public enum ActionType { Always, OnPicked, OnWearing, OnContaining, OnContained, OnActive, OnUse, OnFailure, OnBroken, OnFire } class Item : MapEntity, IDamageable, IPropertyObject { public static List ItemList = new List(); protected 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; //components that determine the functionality of the item public List components; public readonly Dictionary properties; public Dictionary ObjectProperties { get { return properties; } } private bool? hasInGameEditableProperties; bool HasInGameEditableProperties { get { if (hasInGameEditableProperties==null) { hasInGameEditableProperties = GetProperties().Count>0; } return (bool)hasInGameEditableProperties; } } public PhysicsBody body; private float condition; private bool inWater; private Inventory inventory; //the inventory in which the item is contained in public Inventory Inventory { get { return inventory; } set { inventory = value; if (inventory != null) Container = inventory.Owner as Item; } } public Item Container { get; private set; } public List FixRequirements; public override string Name { get { return prefab.Name; } } public string Description { get { return prefab.Description; } } 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) { 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 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() { LimbSlot.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; } } //List highlightText; //public List HighlightText //{ // get { return highlightText;} //} public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine) : this(new Rectangle((int)position.X, (int)position.Y, (int)itemPrefab.sprite.size.X, (int)itemPrefab.sprite.size.Y), itemPrefab, submarine) { } public Item(Rectangle newRect, ItemPrefab itemPrefab, Submarine submarine) : base(submarine) { prefab = itemPrefab; spriteColor = prefab.SpriteColor; linkedTo = new ObservableCollection(); components = new List(); FixRequirements = new List(); tags = new List(); rect = newRect; FindHull(); condition = 100.0f; XElement element = ToolBox.TryLoadXml(Prefab.ConfigFile).Root; if (element == null) return; if (ToolBox.GetAttributeString(element, "name", "") != Name) { foreach (XElement subElement in element.Elements()) { if (ToolBox.GetAttributeString(subElement, "name", "") != Name) continue; element = subElement; break; } } properties = ObjectProperty.InitProperties(this, element); foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLower()) { 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 (!string.IsNullOrWhiteSpace(ic.Msg)) highlightText.Add(ic.Msg); break; } } 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) FindHull(); } public Rectangle TransformTrigger(Rectangle trigger, bool world = false) { return world ? new Rectangle( WorldRect.X + trigger.X, WorldRect.Y + trigger.Y, (trigger.Width == 0) ? (int)Rect.Width : trigger.Width, (trigger.Height == 0) ? (int)Rect.Height : trigger.Height) : new Rectangle( Rect.X + trigger.X, Rect.Y + trigger.Y, (trigger.Width == 0) ? (int)Rect.Width : trigger.Width, (trigger.Height == 0) ? (int)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() { CurrentHull = Hull.FindHull(WorldPosition, CurrentHull); if (body!=null) { body.Submarine = CurrentHull == null ? null : Submarine.Loaded; } return CurrentHull; } 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.ToLower())); } public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null) { foreach (ItemComponent ic in components) { foreach (StatusEffect effect in ic.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; 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.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 position, Attack attack, float deltaTime, bool playSound = true) { float damageAmount = attack.GetDamage(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) { 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 (Container != 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); } } if (body == null || !body.Enabled) return; if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) { FindHull(); 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.SetToTargetPosition(); inWater = IsInWater(); if (!inWater) return; //calculate (a rough approximation of) buoyancy float volume = body.Mass / body.Density; Vector2 buoyancy = new Vector2(0, volume * 10.0f); //apply buoyancy and drag //if ((buoyancy - body.LinearVelocity * volume) == Vector2.Zero) DebugConsole.ThrowError("v.zero "); if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) { body.ResetDynamics(); } body.ApplyForce(buoyancy - body.LinearVelocity * volume); //apply simple angular drag body.ApplyTorque(body.AngularVelocity * volume * -0.05f); } public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { Color color = (isSelected && editing) ? color = Color.Red : spriteColor; if (isHighlighted) color = Color.Orange; if (prefab.sprite != null) { if (body == null) { if (prefab.ResizeHorizontal || prefab.ResizeVertical) { 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); } } else if (body.Enabled) { var holdable = GetComponent(); if (holdable!=null && holdable.Picker !=null) { float depth = Sprite.Depth; 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); } } } foreach (ItemComponent component in components) component.Draw(spriteBatch, editing); if (!editing || (body != null && !body.Enabled)) { isHighlighted = false; return; } 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)Physics.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); } } 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) { new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, GUI.Style, editingHUD); int height = 20; var editable = objectProperty.Attributes.OfType().FirstOrDefault(); if (editable != null) height = (int)(Math.Ceiling(editable.MaxLength / 20.0f) * 20.0f); GUITextBox propertyBox = new GUITextBox(new Rectangle(180, y, 250, height), GUI.Style, editingHUD); if (height>20) propertyBox.Wrap = true; object value = objectProperty.GetValue(); 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) { if (editingHUD == null || editingHUD.UserData as Item != this) { editingHUD = CreateEditingHUD(true); } if (editingHUD.Rect.Height > 60) { editingHUD.Update((float)Physics.step); editingHUD.Draw(spriteBatch); } } foreach (ItemComponent ic in components) { ic.DrawHUD(spriteBatch, character); } } public List GetConnectedComponents() { ConnectionPanel connectionPanel = GetComponent(); if (connectionPanel == null) return new List(); List connectedComponents = new List(); 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; } public void SendSignal(string signal, string connectionName, float power = 0.0f) { ConnectionPanel panel = GetComponent(); if (panel == null) return; foreach (Connection c in panel.Connections) { if (c.Name != connectionName) continue; c.SendSignal(signal, this, power); } } /// 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 = null, Item[] ignoredItems=null) { float closestDist = 0.0f, dist; Item closest = null; Vector2 displayPos = ConvertUnits.ToDisplayUnits(position); Vector2 displayPickPos = ConvertUnits.ToDisplayUnits(pickPosition); foreach (Item item in ItemList) { if (ignoredItems!=null && ignoredItems.Contains(item)) continue; //if (hull != item.CurrentHull && (hull==null || (item.Rect.Height(); if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) continue; foreach (Rectangle trigger in item.prefab.Triggers) { Rectangle transformedTrigger = item.TransformTrigger(trigger, true); if (!Submarine.RectContains(transformedTrigger, displayPos)) continue; Vector2 triggerCenter = new Vector2( transformedTrigger.X + transformedTrigger.Width / 2.0f, transformedTrigger.Y - transformedTrigger.Height / 2.0f); //dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPos.X), Math.Abs(triggerCenter.Y-displayPos.Y)); //if (dist > closestDist && closest!=null) continue; dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y)); if (closest == null || dist < closestDist) { closest = item; closestDist = dist; } } if (item.prefab.PickDistance == 0.0f) continue; if (Vector2.Distance(displayPos, item.WorldPosition) > item.prefab.PickDistance) continue; if (!item.prefab.PickThroughWalls) { Body body = Submarine.CheckVisibility(item.Submarine == null ? position : position - item.Submarine.SimPosition, item.SimPosition, true); if (body != null && body.UserData as Item != item) continue; } dist = Vector2.Distance(displayPickPos, item.WorldPosition); if (dist < item.prefab.PickDistance && (closest == null || dist < closestDist)) { closest = item; closestDist = dist; } } 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); 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)) 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); 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(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; } //private void Init() //{ //} public override XElement Save(XDocument doc) { XElement element = new XElement("Item"); element.Add(new XAttribute("name", prefab.Name), new XAttribute("ID", ID)); if (prefab.ResizeHorizontal || prefab.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); } doc.Root.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) 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) { message.Write(body.SimPosition.X); message.Write(body.SimPosition.Y); } break; case NetworkEventType.ItemFixed: byte requirementIndex = (byte)data; message.Write(requirementIndex); break; case NetworkEventType.InventoryUpdate: var itemContainer = GetComponent(); if (itemContainer == null || itemContainer.Inventory == null) return false; return itemContainer.Inventory.FillNetworkData(NetworkEventType.InventoryUpdate, message, data); 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.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 void ReadNetworkData(NetworkEventType type, NetIncomingMessage message, float sendingTime, out object data) { data = null; Condition = (float)message.ReadByte()/2.55f; switch (type) { case NetworkEventType.DropItem: Vector2 newSimPos = Vector2.Zero; newSimPos = new Vector2(message.ReadFloat(), message.ReadFloat()); SetTransform(newSimPos, body.Rotation); Drop(null, false); break; case NetworkEventType.ItemFixed: byte requirementIndex = message.ReadByte(); data = requirementIndex; if (requirementIndex>=FixRequirements.Count) return; FixRequirements[requirementIndex].Fixed = true; break; case NetworkEventType.InventoryUpdate: var itemContainer = GetComponent(); if (itemContainer == null || itemContainer.Inventory == null) return; itemContainer.Inventory.ReadNetworkData(NetworkEventType.DropItem, message, sendingTime); break; case NetworkEventType.ComponentUpdate: case NetworkEventType.ImportantComponentUpdate: int componentIndex = message.ReadByte(); data = componentIndex; if (componentIndex < 0 || componentIndex > components.Count - 1) return; components[componentIndex].NetworkUpdateSent = true; components[componentIndex].ReadNetworkData(type, message, sendingTime); break; case NetworkEventType.UpdateProperty: string propertyName = ""; try { propertyName = message.ReadString(); data = propertyName; } catch { return; } var allProperties = GetProperties(); ObjectProperty property = allProperties.Find(op => op.Name == propertyName); if (property == null) return; 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; } break; } } 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); } } } } }