diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index 16a7277ad..0006eb558 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -436,7 +436,7 @@ namespace Barotrauma InfoFrame.FindChild("showlog").Visible = GameMain.Server != null; - campaignViewButton = new GUIButton(new Rectangle(0, 0, 130, 30), "Campaign view", Alignment.BottomRight, "", defaultModeContainer); + campaignViewButton = new GUIButton(new Rectangle(-80, 0, 120, 30), "Campaign view", Alignment.BottomRight, "", defaultModeContainer); campaignViewButton.OnClicked = (btn, obj) => { ToggleCampaignView(true); return true; }; campaignViewButton.Visible = false; diff --git a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems index e28252c31..b1477e812 100644 --- a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems +++ b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems @@ -741,6 +741,9 @@ PreserveNewest + + Always + PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Data/clientpermissions.xml b/Barotrauma/BarotraumaShared/Data/clientpermissions.xml new file mode 100644 index 000000000..ccc105b28 --- /dev/null +++ b/Barotrauma/BarotraumaShared/Data/clientpermissions.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs index 1ba1069c6..3e0f20f84 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs @@ -64,7 +64,7 @@ namespace Barotrauma protected override Vector2 DoSteeringSeek(Vector2 target, float speed = 1) { //find a new path if one hasn't been found yet or the target is different from the current target - if (currentPath == null || Vector2.Distance(target, currentTarget)>1.0f || findPathTimer < -5.0f) + if (currentPath == null || Vector2.Distance(target, currentTarget) > 1.0f || findPathTimer < -1.0f) { if (findPathTimer > 0.0f) return Vector2.Zero; @@ -73,21 +73,21 @@ namespace Barotrauma if (character != null && character.Submarine == null) { var targetHull = Hull.FindHull(FarseerPhysics.ConvertUnits.ToDisplayUnits(target), null, false); - if (targetHull!=null && targetHull.Submarine != null) + if (targetHull != null && targetHull.Submarine != null) { pos -= targetHull.SimPosition; } - } + } currentPath = pathFinder.FindPath(pos, target); - findPathTimer = Rand.Range(1.0f,1.2f); + findPathTimer = Rand.Range(1.0f, 1.2f); return DiffToCurrentNode(); } - + Vector2 diff = DiffToCurrentNode(); - + var collider = character.AnimController.Collider; //if not in water and the waypoint is between the top and bottom of the collider, no need to move vertically if (!character.AnimController.InWater && @@ -104,7 +104,7 @@ namespace Barotrauma private Vector2 DiffToCurrentNode() { - if (currentPath == null || currentPath.Finished || currentPath.Unreachable) return Vector2.Zero; + if (currentPath == null || currentPath.Unreachable) return Vector2.Zero; if (currentPath.Finished) { @@ -113,8 +113,8 @@ namespace Barotrauma { //todo: take multiple subs into account pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition; - } - return currentTarget-pos2; + } + return currentTarget - pos2; } if (canOpenDoors && !character.LockHands) CheckDoorsInPath(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs index ee6381c50..0e527227e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs @@ -4,21 +4,13 @@ using System.Linq; namespace Barotrauma { - class AIObjective + abstract class AIObjective { - protected List subObjectives; - + protected readonly List subObjectives; protected float priority; - - protected Character character; - + protected readonly Character character; protected string option; - public virtual bool IsCompleted() - { - return false; - } - public virtual bool CanBeCompleted { get { return true; } @@ -27,15 +19,12 @@ namespace Barotrauma public string Option { get { return option; } - } - + } public AIObjective(Character character, string option) { subObjectives = new List(); - this.character = character; - this.option = option; #if DEBUG @@ -60,8 +49,6 @@ namespace Barotrauma Act(deltaTime); } - protected virtual void Act(float deltaTime) { } - public void AddSubObjective(AIObjective objective) { if (subObjectives.Any(o => o.IsDuplicate(objective))) return; @@ -69,19 +56,20 @@ namespace Barotrauma subObjectives.Add(objective); } - public virtual float GetPriority(Character character) + public AIObjective GetCurrentSubObjective() { - return 0.0f; + AIObjective currentSubObjective = this; + while (currentSubObjective.subObjectives.Count > 0) + { + currentSubObjective = subObjectives[0]; + } + return currentSubObjective; } - public virtual bool IsDuplicate(AIObjective otherObjective) - { -#if DEBUG - throw new NotImplementedException(); -#else - return (this.GetType() == otherObjective.GetType()); -#endif - - } + protected abstract void Act(float deltaTime); + + public abstract bool IsCompleted(); + public abstract float GetPriority(AIObjectiveManager objectiveManager); + public abstract bool IsDuplicate(AIObjective otherObjective); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index cbe66e791..cf84ae4f3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -33,7 +33,6 @@ namespace Barotrauma } coolDownTimer = CoolDown; - } protected override void Act(float deltaTime) @@ -41,17 +40,24 @@ namespace Barotrauma coolDownTimer -= deltaTime; var weapon = character.Inventory.FindItem("weapon"); - - if (weapon==null) + + if (weapon == null) { Escape(deltaTime); } else { + //TODO: make sure the weapon is ready to use (projectiles/batteries loaded) if (!character.SelectedItems.Contains(weapon)) { - character.Inventory.TryPutItem(weapon, 3, false, character); - weapon.Equip(character); + if (character.Inventory.TryPutItem(weapon, 3, false, character)) + { + weapon.Equip(character); + } + else + { + return; + } } character.CursorPosition = enemy.Position; character.SetInput(InputType.Aim, false, true); @@ -99,8 +105,13 @@ namespace Barotrauma return enemy.IsDead || coolDownTimer <= 0.0f; } - public override float GetPriority(Character character) + public override float GetPriority(AIObjectiveManager objectiveManager) { + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + //clamp the strength to the health of this character //(it doesn't make a difference whether the enemy does 200 or 600 damage, it's one hit kill anyway) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs index bc92c927e..9dc4e26bc 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs @@ -19,15 +19,6 @@ namespace Barotrauma { this.itemName = itemName; this.container = container; - - //check if the container has room for more items - //canBeCompleted = false; - //foreach (Item contained in container.inventory.Items) - //{ - // if (contained != null) continue; - // canBeCompleted = true; - // break; - //} } public override bool IsCompleted() @@ -35,6 +26,16 @@ namespace Barotrauma return isCompleted || container.Inventory.FindItem(itemName)!=null; } + public override float GetPriority(AIObjectiveManager objectiveManager) + { + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + + return 1.0f; + } + protected override void Act(float deltaTime) { if (isCompleted) return; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs index 0b9928d66..132408dd3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs @@ -15,7 +15,7 @@ namespace Barotrauma if (item == null) return false; var containedItems = item.ContainedItems; - var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank" && i.Condition > 0.0f); + var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank") && i.Condition > 0.0f); return oxygenTank != null; } @@ -42,7 +42,7 @@ namespace Barotrauma if (containedItems == null) return; //check if there's an oxygen tank in the mask - var oxygenTank = Array.Find(containedItems, i => i.Name == "Oxygen Tank"); + var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank")); if (oxygenTank != null) { @@ -66,15 +66,18 @@ namespace Barotrauma if (subObjective != null) { subObjective.TryComplete(deltaTime); - - //isCompleted = subObjective.IsCompleted(); } } - public override float GetPriority(Character character) + public override float GetPriority(AIObjectiveManager objectiveManager) { if (character.AnimController.CurrentHull == null) return 100.0f; + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + return 100.0f - character.Oxygen; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 56de5b8e5..391900d20 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Xna.Framework; +using System; using System.Collections.Generic; using System.Linq; @@ -27,39 +28,23 @@ namespace Barotrauma unreachable = new List(); } + public override bool IsCompleted() + { + return false; + } + protected override void Act(float deltaTime) { - var currentHull = character.AnimController.CurrentHull; currenthullSafety = OverrideCurrentHullSafety == null ? GetHullSafety(currentHull, character) : (float)OverrideCurrentHullSafety; - - if (currentHull != null) + + if (NeedsDivingGear()) { - if (NeedsDivingGear()) - { - if (!FindDivingGear(deltaTime)) return; - } - - if (currenthullSafety > MinSafety) - { - if (Math.Abs(currentHull.WorldPosition.X - character.WorldPosition.X) > 100.0f) - { - character.AIController.SteeringManager.SteeringSeek(currentHull.SimPosition, 0.5f); - } - else - { - - character.AIController.SteeringManager.Reset(); - } - - character.AIController.SelectTarget(null); - - goToObjective = null; - return; - } + if (!FindDivingGear(deltaTime)) return; } + if (searchHullTimer > 0.0f) { @@ -79,7 +64,7 @@ namespace Barotrauma if (goToObjective != null) { var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager; - if (pathSteering!=null && pathSteering.CurrentPath!= null && + if (pathSteering != null && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable && !unreachable.Contains(goToObjective.Target)) { unreachable.Add(goToObjective.Target as Hull); @@ -92,7 +77,7 @@ namespace Barotrauma private bool FindDivingGear(float deltaTime) { - if (divingGearObjective==null) + if (divingGearObjective == null) { divingGearObjective = new AIObjectiveFindDivingGear(character, false); } @@ -113,8 +98,9 @@ namespace Barotrauma if (hull == character.AnimController.CurrentHull || unreachable.Contains(hull)) continue; float hullValue = GetHullSafety(hull, character); - hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X)); - hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y) * 2.0f); + //slight preference over hulls that are closer + hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.X - hull.Position.X)) * 0.1f; + hullValue -= (float)Math.Sqrt(Math.Abs(character.Position.Y - hull.Position.Y)) * 0.2f; if (bestHull == null || hullValue > bestValue) { @@ -144,13 +130,13 @@ namespace Barotrauma return false; } - public override float GetPriority(Character character) + public override float GetPriority(AIObjectiveManager objectiveManager) { if (character.Oxygen < 80.0f) { return 150.0f - character.Oxygen; } - + if (character.AnimController.CurrentHull == null) return 5.0f; currenthullSafety = GetHullSafety(character.AnimController.CurrentHull, character); priority = 100.0f - currenthullSafety; @@ -171,13 +157,12 @@ namespace Barotrauma } } } - - + if (NeedsDivingGear()) { if (divingGearObjective != null && !divingGearObjective.IsCompleted()) priority += 20.0f; } - + return priority; } @@ -185,11 +170,28 @@ namespace Barotrauma { if (hull == null) return 0.0f; + float safety = 100.0f; + float waterPercentage = (hull.WaterVolume / hull.Volume) * 100.0f; + if (hull.LethalPressure > 0.0f && character.PressureProtection <= 0.0f) + { + safety -= 100.0f; + } + else if (character.OxygenAvailable <= 0.0f) + { + safety -= waterPercentage; + } + else + { + safety -= waterPercentage * 0.1f; + } + + if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f; + + if (safety <= 0.0f) return 0.0f; + float fireAmount = 0.0f; - var nearbyHulls = hull.GetConnectedHulls(3); - foreach (Hull hull2 in nearbyHulls) { foreach (FireSource fireSource in hull2.FireSources) @@ -204,13 +206,9 @@ namespace Barotrauma } } } + safety -= fireAmount; - float safety = 100.0f - fireAmount; - - if (waterPercentage > 30.0f && character.OxygenAvailable <= 0.0f) safety -= waterPercentage; - if (hull.OxygenPercentage < 30.0f) safety -= (30.0f - hull.OxygenPercentage) * 5.0f; - - return safety; + return MathHelper.Clamp(safety, 0.0f, 100.0f); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs index 356ed3590..f1e9bfd97 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs @@ -7,7 +7,9 @@ namespace Barotrauma { class AIObjectiveFixLeak : AIObjective { - private Gap leak; + private readonly Gap leak; + + private AIObjectiveGoTo gotoObjective; public Gap Leak { @@ -20,15 +22,20 @@ namespace Barotrauma this.leak = leak; } - public override float GetPriority(Character character) + public override bool IsCompleted() + { + return leak.Open <= 0.0f || leak.Removed; + } + + public override float GetPriority(AIObjectiveManager objectiveManager) { if (leak.Open == 0.0f) return 0.0f; float leakSize = (leak.IsHorizontal ? leak.Rect.Height : leak.Rect.Width) * Math.Max(leak.Open, 0.1f); float dist = Vector2.DistanceSquared(character.SimPosition, leak.SimPosition); - dist = Math.Max(dist/100.0f, 1.0f); - return Math.Min(leakSize/dist, 40.0f); + dist = Math.Max(dist / 100.0f, 1.0f); + return Math.Min(leakSize / dist, 40.0f); } public override bool IsDuplicate(AIObjective otherObjective) @@ -44,7 +51,7 @@ namespace Barotrauma if (weldingTool == null) { - subObjectives.Add(new AIObjectiveGetItem(character, "Welding Tool", true)); + AddSubObjective(new AIObjectiveGetItem(character, "Welding Tool", true)); return; } else @@ -52,25 +59,31 @@ namespace Barotrauma var containedItems = weldingTool.ContainedItems; if (containedItems == null) return; - var fuelTank = Array.Find(containedItems, i => i.Name == "Welding Fuel Tank" && i.Condition > 0.0f); + var fuelTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Welding Fuel Tank") && i.Condition > 0.0f); if (fuelTank == null) { AddSubObjective(new AIObjectiveContainItem(character, "Welding Fuel Tank", weldingTool.GetComponent())); + return; } } var repairTool = weldingTool.GetComponent(); if (repairTool == null) return; - if (Vector2.Distance(character.WorldPosition, leak.WorldPosition) > 300.0f) + Vector2 standPosition = GetStandPosition(); + + if (Vector2.DistanceSquared(character.WorldPosition, leak.WorldPosition) > 100.0f * 100.0f) { - AddSubObjective(new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character)); + var gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(standPosition), character); + if (!gotoObjective.IsCompleted()) + { + AddSubObjective(gotoObjective); + return; + } } - else - { - AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", leak)); - } + + AddSubObjective(new AIObjectiveOperateItem(repairTool, character, "", true, leak)); } private Vector2 GetStandPosition() diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs index f29971f03..2414a726c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs @@ -7,38 +7,80 @@ namespace Barotrauma { class AIObjectiveFixLeaks : AIObjective { - const float UpdateGapListInterval = 10.0f; + const float UpdateGapListInterval = 5.0f; - private float updateGapListTimer; + private double lastGapUpdate; private AIObjectiveIdle idleObjective; + private AIObjectiveFindDivingGear findDivingGear; + private List objectiveList; public AIObjectiveFixLeaks(Character character) : base (character, "") { - objectiveList = new List(); } public override bool IsCompleted() { - return false; + if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null) + { + UpdateGapList(); + lastGapUpdate = Timing.TotalTime; + } + + return objectiveList.Count == 0; + } + + public override float GetPriority(AIObjectiveManager objectiveManager) + { + if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null) + { + UpdateGapList(); + lastGapUpdate = Timing.TotalTime; + } + + float priority = 0.0f; + foreach (AIObjectiveFixLeak fixObjective in objectiveList) + { + //gaps from outside to inside significantly increase the priority + if (!fixObjective.Leak.IsRoomToRoom) + { + priority = Math.Max(priority + fixObjective.Leak.Open * 100.0f, 50.0f); + } + else + { + priority += fixObjective.Leak.Open * 10.0f; + } + + if (priority >= 100.0f) break; + } + + return Math.Min(priority, 100.0f); } protected override void Act(float deltaTime) { - updateGapListTimer -= deltaTime; - - if (updateGapListTimer<=0.0f) + if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null) { UpdateGapList(); - - updateGapListTimer = UpdateGapListInterval; + lastGapUpdate = Timing.TotalTime; } if (objectiveList.Any()) { + if (!objectiveList[objectiveList.Count - 1].Leak.IsRoomToRoom) + { + if (findDivingGear == null) findDivingGear = new AIObjectiveFindDivingGear(character, true); + + if (!findDivingGear.IsCompleted() && findDivingGear.CanBeCompleted) + { + findDivingGear.TryComplete(deltaTime); + return; + } + } + objectiveList[objectiveList.Count - 1].TryComplete(deltaTime); if (!objectiveList[objectiveList.Count - 1].CanBeCompleted || @@ -56,6 +98,7 @@ namespace Barotrauma private void UpdateGapList() { + if (objectiveList == null) objectiveList = new List(); objectiveList.Clear(); foreach (Gap gap in Gap.GapList) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs index 07aec2f29..05c80fa69 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -2,6 +2,7 @@ using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Linq; +using System; namespace Barotrauma { @@ -26,6 +27,16 @@ namespace Barotrauma get { return canBeCompleted; } } + public override float GetPriority(AIObjectiveManager objectiveManager) + { + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + + return 1.0f; + } + public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) : base(character, "") { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs index 3a9b2e037..cf566bd8a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -18,6 +18,16 @@ namespace Barotrauma private bool getDivingGearIfNeeded; + public override float GetPriority(AIObjectiveManager objectiveManager) + { + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + + return 1.0f; + } + public override bool CanBeCompleted { get @@ -93,7 +103,7 @@ namespace Barotrauma } } - if (Vector2.Distance(currTargetPos, character.SimPosition) < 1.0f) + if (Vector2.DistanceSquared(currTargetPos, character.SimPosition) < 0.5f * 0.5f) { character.AIController.SteeringManager.Reset(); character.AnimController.TargetDir = currTargetPos.X > character.SimPosition.X ? Direction.Right : Direction.Left; @@ -104,7 +114,7 @@ namespace Barotrauma var indoorsSteering = character.AIController.SteeringManager as IndoorsSteeringManager; - if (indoorsSteering.CurrentPath==null || indoorsSteering.CurrentPath.Unreachable) + if (indoorsSteering.CurrentPath == null || indoorsSteering.CurrentPath.Unreachable) { indoorsSteering.SteeringWander(); } @@ -130,7 +140,7 @@ namespace Barotrauma if (item.IsInsideTrigger(character.WorldPosition)) completed = true; } - completed = completed || Vector2.Distance(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance; + completed = completed || Vector2.DistanceSquared(target != null ? target.SimPosition : targetPos, character.SimPosition) < allowedDistance * allowedDistance; if (completed) character.AIController.SteeringManager.Reset(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index 59b5b6f6a..6d0c6df9f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -9,15 +9,21 @@ namespace Barotrauma { const float WallAvoidDistance = 150.0f; - AITarget currentTarget; + private AITarget currentTarget; private float newTargetTimer; + private AIObjectiveFindSafety findSafety; + public AIObjectiveIdle(Character character) : base(character, "") { - } - public override float GetPriority(Character character) + public override bool IsCompleted() + { + return false; + } + + public override float GetPriority(AIObjectiveManager objectiveManager) { return 1.0f; } @@ -31,6 +37,14 @@ namespace Barotrauma { return; } + + if (character.AnimController.InWater) + { + //attempt to find a safer place if in water + if (findSafety == null) findSafety = new AIObjectiveFindSafety(character); + findSafety.TryComplete(deltaTime); + return; + } if (newTargetTimer <= 0.0f) { @@ -92,17 +106,10 @@ namespace Barotrauma return; } } - - if (character.AnimController.InWater) - { - character.AIController.SteeringManager.SteeringManual(deltaTime, new Vector2(0.0f, 0.5f)); - } - else - { - character.AIController.SteeringManager.SteeringWander(); - //reset vertical steering to prevent dropping down from platforms etc - character.AIController.SteeringManager.ResetY(); - } + + character.AIController.SteeringManager.SteeringWander(); + //reset vertical steering to prevent dropping down from platforms etc + character.AIController.SteeringManager.ResetY(); return; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs index 8482e3867..468434e8c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs @@ -11,15 +11,17 @@ namespace Barotrauma private Character character; - private AIObjective currentObjective; + private AIObjective currentOrder; + public AIObjective CurrentOrder + { + get { return currentOrder; } + } + public AIObjective CurrentObjective { - get - { - if (currentObjective != null) return currentObjective; - return objectives.Any() ? objectives[0] : null; - } + get; + private set; } public AIObjectiveManager(Character character) @@ -47,8 +49,13 @@ namespace Barotrauma public float GetCurrentPriority(Character character) { - if (currentObjective != null) return OrderPriority; - return (CurrentObjective == null) ? 0.0f : CurrentObjective.GetPriority(character); + if (CurrentOrder != null && + (objectives.Count == 0 || currentOrder.GetPriority(this) > objectives[0].GetPriority(this))) + { + return CurrentOrder.GetPriority(this); + } + + return objectives.Count == 0 ? 0.0f : objectives[0].GetPriority(this); } public void UpdateObjectives() @@ -59,43 +66,46 @@ namespace Barotrauma objectives = objectives.FindAll(o => !o.IsCompleted() && o.CanBeCompleted); //sort objectives according to priority - objectives.Sort((x, y) => y.GetPriority(character).CompareTo(x.GetPriority(character))); + objectives.Sort((x, y) => y.GetPriority(this).CompareTo(x.GetPriority(this))); } public void DoCurrentObjective(float deltaTime) { - if (currentObjective != null && (!objectives.Any() || objectives[0].GetPriority(character) < OrderPriority)) + if (currentOrder != null && (!objectives.Any() || objectives[0].GetPriority(this) < currentOrder.GetPriority(this))) { - currentObjective.TryComplete(deltaTime); + CurrentObjective = currentOrder; + currentOrder.TryComplete(deltaTime); return; } if (!objectives.Any()) return; objectives[0].TryComplete(deltaTime); + + CurrentObjective = objectives[0]; } public void SetOrder(Order order, string option) { if (order == null) return; - currentObjective = null; + currentOrder = null; switch (order.Name.ToLowerInvariant()) { case "follow": - currentObjective = new AIObjectiveGoTo(Character.Controlled, character, true); + currentOrder = new AIObjectiveGoTo(Character.Controlled, character, true); break; case "wait": - currentObjective = new AIObjectiveGoTo(character, character, true); + currentOrder = new AIObjectiveGoTo(character, character, true); break; case "fixleaks": case "fix leaks": - currentObjective = new AIObjectiveFixLeaks(character); + currentOrder = new AIObjectiveFixLeaks(character); break; default: if (order.TargetItem == null) return; - currentObjective = new AIObjectiveOperateItem(order.TargetItem, character, option, null, order.UseController); + currentOrder = new AIObjectiveOperateItem(order.TargetItem, character, option, false, null, order.UseController); break; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs index 9788393a3..061442e73 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs @@ -1,5 +1,6 @@ using Barotrauma.Items.Components; using Microsoft.Xna.Framework; +using System.Collections.Generic; using System.Linq; namespace Barotrauma @@ -14,6 +15,8 @@ namespace Barotrauma private bool canBeCompleted; + private bool requireEquip; + public override bool CanBeCompleted { get @@ -27,11 +30,21 @@ namespace Barotrauma get { return operateTarget; } } - public AIObjectiveOperateItem(ItemComponent item, Character character, string option, Entity operateTarget = null, bool useController = false) - :base (character, option) + public override float GetPriority(AIObjectiveManager objectiveManager) + { + if (objectiveManager.CurrentOrder == this) + { + return AIObjectiveManager.OrderPriority; + } + + return 1.0f; + } + + public AIObjectiveOperateItem(ItemComponent item, Character character, string option, bool requireEquip, Entity operateTarget = null, bool useController = false) + : base (character, option) { this.component = item; - + this.requireEquip = requireEquip; this.operateTarget = operateTarget; if (useController) @@ -72,6 +85,43 @@ namespace Barotrauma } else { + if (requireEquip && !character.HasEquippedItem(component.Item)) + { + //the item has to be equipped before using it if it's holdable + var holdable = component.Item.GetComponent(); + if (holdable == null) + { + DebugConsole.ThrowError("AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component"); + return; + } + + for (int i = 0; i < CharacterInventory.limbSlots.Length; i++) + { + if (CharacterInventory.limbSlots[i] == InvSlotType.Any || + !holdable.AllowedSlots.Any(s => s.HasFlag(CharacterInventory.limbSlots[i]))) + { + continue; + } + + //equip slot already taken + if (character.Inventory.Items[i] != null) + { + //try to put the item in an Any slot, and drop it if that fails + if (!character.Inventory.Items[i].AllowedSlots.Contains(InvSlotType.Any) || + !character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List() { InvSlotType.Any })) + { + character.Inventory.Items[i].Drop(); + } + } + if (character.Inventory.TryPutItem(component.Item, i, true, character)) + { + component.Item.Equip(character); + break; + } + } + return; + } + if (component.AIOperate(deltaTime, character, this)) isCompleted = true; } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs index 84f291d48..60391493f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -3,7 +3,7 @@ using System.Diagnostics; namespace Barotrauma { - class AIObjectiveRescue : AIObjective + /*class AIObjectiveRescue : AIObjective { private readonly Character targetCharacter; @@ -30,5 +30,5 @@ namespace Barotrauma return targetCharacter.IsDead ? 1000.0f / distance : 10000.0f / distance; } - } + }*/ } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs index adeb631b9..784b76859 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs @@ -3,7 +3,7 @@ using System.Linq; namespace Barotrauma { - class AIObjectiveRescueAll : AIObjective + /*class AIObjectiveRescueAll : AIObjective { private List rescueTargets; @@ -44,5 +44,5 @@ namespace Barotrauma AddSubObjective(new AIObjectiveRescue(character, target)); } } - } + }*/ } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 4424e713a..da7694ae1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -197,7 +197,7 @@ namespace Barotrauma { get { return !IsUnconscious && Stun <= 0.0f && !isDead; } } - + public bool CanInteract { get { return AllowInput && IsHumanoid && !LockHands; } @@ -303,11 +303,11 @@ namespace Barotrauma get { return needsAir; } set { needsAir = value; } } - + public float Oxygen { get { return oxygen; } - set + set { if (!MathUtils.IsValid(value)) return; oxygen = MathHelper.Clamp(value, -100.0f, 100.0f); @@ -331,7 +331,7 @@ namespace Barotrauma { if (GameMain.Client != null) return; - SetStun(value); + SetStun(value); } } @@ -358,7 +358,7 @@ namespace Barotrauma }*/ } } - + public float MaxHealth { get { return maxHealth; } @@ -367,12 +367,12 @@ namespace Barotrauma public float Bleeding { get { return bleeding; } - set + set { if (!MathUtils.IsValid(value)) return; if (GameMain.Client != null) return; - float newBleeding = MathHelper.Clamp(value, 0.0f, 5.0f); + float newBleeding = MathHelper.Clamp(value, 0.0f, 5.0f); //if (newBleeding == bleeding) return; bleeding = newBleeding; @@ -381,18 +381,18 @@ namespace Barotrauma GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });*/ } } - + public HuskInfection huskInfection; public float HuskInfectionState { - get - { - return huskInfection == null ? 0.0f : huskInfection.IncubationTimer; + get + { + return huskInfection == null ? 0.0f : huskInfection.IncubationTimer; } set { if (ConfigPath != humanConfigFile) return; - + if (value <= 0.0f) { if (huskInfection != null) @@ -448,7 +448,7 @@ namespace Barotrauma get; set; } - + public Item[] SelectedItems { get { return selectedItems; } @@ -465,6 +465,12 @@ namespace Barotrauma get { return focusedItem; } } + public Item PickingItem + { + get; + set; + } + public virtual AIController AIController { get { return null; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs index 69e06f783..482aee51f 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs @@ -13,7 +13,6 @@ namespace Barotrauma.Items.Components private float pickTimer; - public List AllowedSlots { get { return allowedSlots; } @@ -55,13 +54,15 @@ namespace Barotrauma.Items.Components public override bool Pick(Character picker) { //return if someone is already trying to pick the item - if (pickTimer>0.0f) return false; + if (pickTimer > 0.0f) return false; if (picker == null || picker.Inventory == null) return false; - if (PickingTime>0.0f) + if (PickingTime > 0.0f) { - CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime)); - + if (picker.PickingItem == null) + { + CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime)); + } return false; } else @@ -104,6 +105,8 @@ namespace Barotrauma.Items.Components private IEnumerable WaitForPick(Character picker, float requiredTime) { + picker.PickingItem = item; + var leftHand = picker.AnimController.GetLimb(LimbType.LeftHand); var rightHand = picker.AnimController.GetLimb(LimbType.RightHand); @@ -151,7 +154,8 @@ namespace Barotrauma.Items.Components private void StopPicking(Character picker) { picker.AnimController.Anim = AnimController.Animation.None; - pickTimer = 0.0f; + picker.PickingItem = null; + pickTimer = 0.0f; } protected void DropConnectedWires(Character character) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index b6bc208fc..74ae7f130 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -103,11 +103,7 @@ namespace Barotrauma.Items.Components { if (character == null) return false; if (!character.IsKeyDown(InputType.Aim)) return false; - - //if (DoesUseFail(Character)) return false; - - //targetPosition = targetPosition.X, -targetPosition.Y); - + float degreeOfSuccess = DegreeOfSuccess(character)/100.0f; if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) @@ -241,7 +237,7 @@ namespace Barotrauma.Items.Components { Gap leak = objective.OperateTarget as Gap; if (leak == null) return true; - + float dist = Vector2.Distance(leak.WorldPosition, item.WorldPosition); //too far away -> consider this done and hope the AI is smart enough to move closer diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs index 29d5c60e4..5af91073e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs @@ -258,9 +258,9 @@ namespace Barotrauma.Items.Components if (batteryToLoad == null) return true; - if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed*0.4f) + if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f) { - objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "")); + objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "", false)); return false; } } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/GameServerLogin.cs b/Barotrauma/BarotraumaShared/Source/Networking/GameServerLogin.cs index a34727cd1..56d21c33d 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/GameServerLogin.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/GameServerLogin.cs @@ -179,7 +179,7 @@ namespace Barotrauma.Networking DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", Color.Red); return; } - Client nameTaken = ConnectedClients.Find(c => c.Name.ToLower() == clName.ToLower()); + Client nameTaken = ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), clName.ToLower())); if (nameTaken != null) { if (nameTaken.Connection.RemoteEndPoint.Address.ToString() == inc.SenderEndPoint.Address.ToString())