diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 19811887f..cabf888d6 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -122,6 +122,7 @@ + diff --git a/Subsurface/Content/Items/Clothes/clothes.xml b/Subsurface/Content/Items/Clothes/clothes.xml index a225f60c2..5bf5762d7 100644 --- a/Subsurface/Content/Items/Clothes/clothes.xml +++ b/Subsurface/Content/Items/Clothes/clothes.xml @@ -188,10 +188,28 @@ + name="Health Scanner HUD" + category="Equipment" + pickdistance="150" + tags="smallitem" + description="A heads-up display that displays information of the vital signs and status of nearby humans."> + + + + + + + + + + + + + diff --git a/Subsurface/Content/Items/Clothes/doctorgear.png b/Subsurface/Content/Items/Clothes/doctorgear.png index ae181cec4..7ff7a3368 100644 Binary files a/Subsurface/Content/Items/Clothes/doctorgear.png and b/Subsurface/Content/Items/Clothes/doctorgear.png differ diff --git a/Subsurface/Content/Items/Fabricators/fabricators.xml b/Subsurface/Content/Items/Fabricators/fabricators.xml index ae2c1785a..0bbd0675a 100644 --- a/Subsurface/Content/Items/Fabricators/fabricators.xml +++ b/Subsurface/Content/Items/Fabricators/fabricators.xml @@ -14,15 +14,25 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -43,9 +53,13 @@ - + + + - + + + @@ -72,25 +86,37 @@ category="Machine" description="A machine that can be used to manufacture various medicines."> - - + + - + + + - - - - - + + + - - - + + + + + + + + + + + + + + + @@ -109,8 +135,8 @@ pickdistance="150" category="Machine" description="Disassembles and breaks down items to reusable components and material bars."> - - + + diff --git a/Subsurface/Content/Items/Medical/medical.xml b/Subsurface/Content/Items/Medical/medical.xml index 65b0b4380..21662e6e8 100644 --- a/Subsurface/Content/Items/Medical/medical.xml +++ b/Subsurface/Content/Items/Medical/medical.xml @@ -1,5 +1,5 @@  - + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -88,6 +60,7 @@ category="Equipment" Tags="smallitem" pickdistance="150" + canuseonself="true" price="20" description="Treated with a hemostatic agent that quickly seals most minor wounds."> @@ -95,9 +68,10 @@ - - - + + + @@ -125,13 +99,17 @@ - + + + + + @@ -140,7 +118,11 @@ - + + + + + - - + + + + + + - + + + + + @@ -185,12 +175,17 @@ - + + + + + + @@ -198,21 +193,34 @@ - + + + + + + + + + - - + + + + + + - - + + + + + + - - + + + + + + + pickdistance="150"> @@ -260,17 +275,13 @@ - - - - @@ -278,7 +289,29 @@ - + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/Items/machines.png b/Subsurface/Content/Items/machines.png index 0088db9cd..d8232ce3f 100644 Binary files a/Subsurface/Content/Items/machines.png and b/Subsurface/Content/Items/machines.png differ diff --git a/Subsurface/Content/Jobs.xml b/Subsurface/Content/Jobs.xml index 498469f99..fa1a7841f 100644 --- a/Subsurface/Content/Jobs.xml +++ b/Subsurface/Content/Jobs.xml @@ -10,14 +10,16 @@ + - + + @@ -30,6 +32,7 @@ + @@ -40,6 +43,7 @@ + @@ -61,6 +65,8 @@ + + @@ -68,6 +74,7 @@ + diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 744559acc..5cee70842 100644 --- a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -97,6 +97,8 @@ namespace Barotrauma divingGearObjective = new AIObjectiveFindDivingGear(character, false); } + if (divingGearObjective.IsCompleted()) return true; + divingGearObjective.TryComplete(deltaTime); return divingGearObjective.IsCompleted(); } diff --git a/Subsurface/Source/Characters/StatusEffect.cs b/Subsurface/Source/Characters/StatusEffect.cs index ea15a4e0d..5b710d33e 100644 --- a/Subsurface/Source/Characters/StatusEffect.cs +++ b/Subsurface/Source/Characters/StatusEffect.cs @@ -28,6 +28,8 @@ namespace Barotrauma private string[] onContainingNames; + private readonly bool useItem; + public readonly ActionType type; private Explosion explosion; @@ -147,6 +149,10 @@ namespace Barotrauma case "fire": FireSize = ToolBox.GetAttributeFloat(subElement,"size",10.0f); break; + case "use": + case "useitem": + useItem = true; + break; case "requireditem": case "requireditems": RelatedItem newRequiredItem = RelatedItem.Load(subElement); @@ -223,17 +229,28 @@ namespace Barotrauma if (sound != null) sound.Play(1.0f, 1000.0f, entity.WorldPosition); - for (int i = 0; i < propertyNames.Count(); i++) + if (useItem) { - ObjectProperty property; - foreach (IPropertyObject target in targets) + foreach (Item item in targets.FindAll(t => t is Item).Cast()) { + item.Use(deltaTime, targets.FirstOrDefault(t => t is Character) as Character); + } + } + + foreach (IPropertyObject target in targets) + { + for (int i = 0; i < propertyNames.Count(); i++) + { + ObjectProperty property; + //if (targetNames!=null && !targetNames.Contains(target.Name)) continue; if (!target.ObjectProperties.TryGetValue(propertyNames[i], out property)) continue; ApplyToProperty(property, propertyEffects[i], deltaTime); } + + } } diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index 2ecc873c8..8ae72d316 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -416,15 +416,7 @@ namespace Barotrauma.Items.Components public virtual void Draw(SpriteBatch spriteBatch, bool editing = false) { } public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { } - - /// - /// a construction has activated the item (such as a turret shooting a projectile) - /// call the Activate-methods of the components - /// The construction which activated the item - /// A vector that can be used to pass additional information to the components - public virtual void ItemActivate(Item item, Vector2 modifier) { } - - + /// true if the operation was completed public virtual bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { diff --git a/Subsurface/Source/Items/Components/Machines/Fabricator.cs b/Subsurface/Source/Items/Components/Machines/Fabricator.cs index db5486383..714145741 100644 --- a/Subsurface/Source/Items/Components/Machines/Fabricator.cs +++ b/Subsurface/Source/Items/Components/Machines/Fabricator.cs @@ -16,7 +16,7 @@ namespace Barotrauma.Items.Components public readonly float RequiredTime; - //ListOrSomething requiredLevels + public readonly List RequiredSkills; public FabricableItem(XElement element) { @@ -29,6 +29,8 @@ namespace Barotrauma.Items.Components return; } + RequiredSkills = new List(); + RequiredTime = ToolBox.GetAttributeFloat(element, "requiredtime", 1.0f); RequiredItems = new List>(); @@ -58,7 +60,18 @@ namespace Barotrauma.Items.Components RequiredItems.Remove(existing); RequiredItems.Add(new Tuple(requiredItem, existing.Item2+1)); } + } + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "requiredskill": + RequiredSkills.Add(new Skill( + ToolBox.GetAttributeString(subElement, "name", ""), + ToolBox.GetAttributeInt(subElement, "level", 0))); + break; + } } } @@ -143,17 +156,41 @@ namespace Barotrauma.Items.Components Alignment.TopLeft, null, selectedItemFrame, true); - string text = "Required items:\n"; - foreach (Tuple ip in targetItem.RequiredItems) + + List inadequateSkills = new List(); + + if (Character.Controlled != null) { - text += " - " + ip.Item1.Name + " x"+ip.Item2+"\n"; + + inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level); + } + + Color textColor = Color.White; + string text; + if (!inadequateSkills.Any()) + { + text = "Required items:\n"; + foreach (Tuple ip in targetItem.RequiredItems) + { + text += " - " + ip.Item1.Name + " x"+ip.Item2+"\n"; + } + text += "Required time: " + targetItem.RequiredTime + " s"; + } + else + { + text = "Skills required to calibrate:\n"; + foreach (Skill skill in inadequateSkills) + { + text += " - " + skill.Name + " lvl " + skill.Level + "\n"; + } + + textColor = Color.Red; } - text += "Required time: " + targetItem.RequiredTime + " s"; new GUITextBlock( new Rectangle(0, 50, 0, 25), text, - Color.Transparent, Color.White, + Color.Transparent, textColor, Alignment.TopLeft, Alignment.TopLeft, null, selectedItemFrame); @@ -284,21 +321,32 @@ namespace Barotrauma.Items.Components FabricableItem targetItem = itemList.SelectedData as FabricableItem; if (targetItem != null) { - activateButton.Enabled = true; - - ItemContainer container = item.GetComponent(); - foreach (Tuple ip in targetItem.RequiredItems) - { - if (Array.FindAll(container.Inventory.Items, it => it != null && it.Prefab == ip.Item1).Count() >= ip.Item2) continue; - activateButton.Enabled = false; - break; - } + activateButton.Enabled = CanBeFabricated(targetItem, character); } GuiFrame.Update((float)Physics.step); GuiFrame.Draw(spriteBatch); } + private bool CanBeFabricated(FabricableItem fabricableItem, Character user) + { + if (fabricableItem == null) return false; + + if (user != null && + fabricableItem.RequiredSkills.Any(skill => user.GetSkillLevel(skill.Name) < skill.Level)) + { + return false; + } + + ItemContainer container = item.GetComponent(); + foreach (Tuple ip in fabricableItem.RequiredItems) + { + if (Array.FindAll(container.Inventory.Items, it => it != null && it.Prefab == ip.Item1).Count() < ip.Item2) return false; + } + + return true; + } + public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetBuffer message) { int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem); diff --git a/Subsurface/Source/Items/Components/StatusHUD.cs b/Subsurface/Source/Items/Components/StatusHUD.cs new file mode 100644 index 000000000..d4f2de7da --- /dev/null +++ b/Subsurface/Source/Items/Components/StatusHUD.cs @@ -0,0 +1,75 @@ +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using Microsoft.Xna.Framework; + +namespace Barotrauma.Items.Components +{ + class StatusHUD : ItemComponent + { + private static readonly string[] BleedingTexts = {"Minor bleeding", "Bleeding", "Bleeding heavily", "Catastrophic Bleeding"}; + + private static readonly string[] HealthTexts = { "No visible injuries", "Minor injuries", "Injured", "Major injuries", "Critically injured" }; + + private static readonly string[] OxygenTexts = { "Oxygen level normal", "Gasping for air", "Signs of oxygen deprivation", "Not breathing" }; + + public StatusHUD(Item item, XElement element) + : base(item, element) + { + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + if (character == null) return; + + GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), + Color.Green * 0.1f, true); + + if (character.ClosestCharacter == null) return; + + var target = character.ClosestCharacter; + + Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.WorldPosition); + hudPos += Vector2.UnitX * 50.0f; + + List texts = new List(); + + texts.Add(target.Name); + + if (target.IsDead) + { + texts.Add("Deceased"); + } + else + { + if (target.IsUnconscious) texts.Add("Unconscious"); + if (target.Stun > 0.01f) texts.Add("Stunned"); + + int healthTextIndex = target.Health > 95.0f ? 0 : + MathHelper.Clamp((int)Math.Ceiling((1.0f - (target.Health / 200.0f + 0.5f)) * HealthTexts.Length), 0, HealthTexts.Length - 1); + + texts.Add(HealthTexts[healthTextIndex]); + + int oxygenTextIndex = MathHelper.Clamp((int)Math.Floor((1.0f - (target.Oxygen / 200.0f + 0.5f)) * OxygenTexts.Length), 0, OxygenTexts.Length - 1); + texts.Add(OxygenTexts[oxygenTextIndex]); + + if (target.Bleeding > 0.0f) + { + int bleedingTextIndex = MathHelper.Clamp((int)Math.Floor(target.Bleeding / 4.0f) * BleedingTexts.Length, 0, BleedingTexts.Length - 1); + texts.Add(BleedingTexts[bleedingTextIndex]); + } + } + + + + foreach (string text in texts) + { + GUI.DrawString(spriteBatch, hudPos, text, Color.LightGreen, Color.Black * 0.7f, 2); + hudPos.Y += 24.0f; + } + } + } +} diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 5615b91b3..292a7e34b 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1110,7 +1110,7 @@ namespace Barotrauma picked = true; ic.ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker); - GUIComponent.MouseOn = null; + if (picker==Character.Controlled) GUIComponent.MouseOn = null; if (ic.CanBeSelected) selected = true; } diff --git a/Subsurface/Source/Items/RelatedItem.cs b/Subsurface/Source/Items/RelatedItem.cs index 9f666c1b7..4d42d1f1d 100644 --- a/Subsurface/Source/Items/RelatedItem.cs +++ b/Subsurface/Source/Items/RelatedItem.cs @@ -13,7 +13,8 @@ namespace Barotrauma None = 0, Contained = 1, Equipped = 2, - Picked = 4 + Picked = 4, + Container = 8 } string[] names; @@ -86,6 +87,10 @@ namespace Barotrauma if (contained.Condition > 0.0f && MatchesItem(contained)) return true; } break; + case RelationType.Container: + if (parentItem == null || parentItem.Container == null) return false; + + return parentItem.Container.Condition > 0.0f && MatchesItem(parentItem.Container); case RelationType.Equipped: if (character == null) return false; foreach (Item equippedItem in character.SelectedItems)