diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 0beadea18..f75ce7183 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -60,6 +60,10 @@ + + + + diff --git a/Subsurface/Source/Characters/AI/AIController.cs b/Subsurface/Source/Characters/AI/AIController.cs index 5ecd56a8b..d0991bba7 100644 --- a/Subsurface/Source/Characters/AI/AIController.cs +++ b/Subsurface/Source/Characters/AI/AIController.cs @@ -16,6 +16,11 @@ namespace Barotrauma protected SteeringManager steeringManager; + public SteeringManager SteeringManager + { + get { return steeringManager; } + } + public Vector2 Steering { get { return Character.AnimController.TargetMovement; } @@ -41,8 +46,6 @@ namespace Barotrauma public AIController (Character c) { Character = c; - - steeringManager = new SteeringManager(this); } public virtual void DebugDraw(SpriteBatch spriteBatch) { } diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index c692e491e..8e57c410e 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -80,6 +80,8 @@ namespace Barotrauma sight = ToolBox.GetAttributeFloat(aiElement, "sight", 0.0f); hearing = ToolBox.GetAttributeFloat(aiElement, "hearing", 0.0f); + steeringManager = new SteeringManager(this); + state = AiState.None; } diff --git a/Subsurface/Source/Characters/AI/HumanAIController.cs b/Subsurface/Source/Characters/AI/HumanAIController.cs new file mode 100644 index 000000000..e67a0d049 --- /dev/null +++ b/Subsurface/Source/Characters/AI/HumanAIController.cs @@ -0,0 +1,89 @@ +using FarseerPhysics; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma +{ + class HumanAIController : AIController + { + const float UpdateObjectiveInterval = 0.5f; + + private AIObjectiveManager objectiveManager; + + private AITarget selectedAiTarget; + + private float updateObjectiveTimer; + + public HumanAIController(Character c) : base(c) + { + steeringManager = new PathSteeringManager(this); + + objectiveManager = new AIObjectiveManager(c); + objectiveManager.AddObjective(new AIObjectiveFindSafety()); + } + + public override void Update(float deltaTime) + { + if (updateObjectiveTimer>0.0f) + { + updateObjectiveTimer -= deltaTime; + } + else + { + objectiveManager.UpdateObjectives(); + updateObjectiveTimer = UpdateObjectiveInterval; + } + + objectiveManager.DoCurrentObjective(deltaTime); + + //if (Character.Controlled != null) + //{ + // steeringManager.SteeringSeek(Character.Controlled.Position); + //} + + Character.AnimController.IgnorePlatforms = (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); + + if (Math.Abs(Character.AnimController.TargetMovement.X)>0.1f) + { + Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; + } + + steeringManager.Update(); + } + + public override void SelectTarget(AITarget target) + { + selectedAiTarget = target; + } + + public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) + { + + if (selectedAiTarget != null) + { + GUI.DrawLine(spriteBatch, new Vector2(Character.Position.X, -Character.Position.Y), ConvertUnits.ToDisplayUnits(new Vector2(selectedAiTarget.Position.X, -selectedAiTarget.Position.Y)), Color.Red); + } + + PathSteeringManager pathSteering = steeringManager as PathSteeringManager; + if (pathSteering == null || pathSteering.CurrentPath == null || pathSteering.CurrentPath.CurrentNode==null) return; + + GUI.DrawLine(spriteBatch, + new Vector2(Character.Position.X, -Character.Position.Y), + new Vector2(pathSteering.CurrentPath.CurrentNode.Position.X, -pathSteering.CurrentPath.CurrentNode.Position.Y), + Color.LightGreen); + + + for (int i = 1; i < pathSteering.CurrentPath.Nodes.Count; i++) + { + GUI.DrawLine(spriteBatch, + new Vector2(pathSteering.CurrentPath.Nodes[i].Position.X, -pathSteering.CurrentPath.Nodes[i].Position.Y), + new Vector2(pathSteering.CurrentPath.Nodes[i - 1].Position.X, -pathSteering.CurrentPath.Nodes[i-1].Position.Y), + Color.LightGreen); + } + + } + } +} diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjective.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjective.cs index e00457bb8..420d1441c 100644 --- a/Subsurface/Source/Characters/AI/Objectives/AIObjective.cs +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjective.cs @@ -9,6 +9,8 @@ namespace Barotrauma { protected List subObjectives; + protected float priority; + public virtual bool IsCompleted() { return false; @@ -21,7 +23,7 @@ namespace Barotrauma /// /// makes the character act according to the objective, or according to any subobjectives that - /// need to be completed before this one (starting from the one with the highest priority) + /// need to be completed before this one /// /// the character who's trying to achieve the objective public void TryComplete(float deltaTime, Character character) @@ -38,5 +40,15 @@ namespace Barotrauma } protected virtual void Act(float deltaTime, Character character) { } + + public virtual float GetPriority(Character character) + { + return 0.0f; + } + + public virtual bool IsDuplicate(AIObjective otherObjective) + { + return true; + } } } diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs new file mode 100644 index 000000000..e79d065a3 --- /dev/null +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -0,0 +1,89 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma +{ + class AIObjectiveFindSafety : AIObjective + { + const float SearchHullInterval = 1.0f; + const float MinSafety = 50.0f; + + AIObjectiveGoTo gotoObjective; + + float currenthullSafety; + + float searchHullTimer; + + protected override void Act(float deltaTime, Character character) + { + if (character.AnimController.CurrentHull == null || GetHullSafety(character.AnimController.CurrentHull) > MinSafety) + { + character.AIController.SteeringManager.SteeringSeek(character.AnimController.CurrentHull.Position); + + gotoObjective = null; + return; + } + + if (searchHullTimer>0.0f) + { + searchHullTimer -= deltaTime; + return; + } + + searchHullTimer = SearchHullInterval; + + Hull bestHull = null; + float bestValue = currenthullSafety; + + foreach (Hull hull in Hull.hullList) + { + if (hull == character.AnimController.CurrentHull) continue; + + float hullValue = GetHullSafety(hull); + 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); + + if (bestHull==null || hullValue > bestValue) + { + bestHull = hull; + bestValue = hullValue; + } + } + + if (bestHull != null) + { + gotoObjective = new AIObjectiveGoTo(bestHull.AiTarget, character); + //character.AIController.SelectTarget(bestHull.AiTarget); + } + + gotoObjective.TryComplete(deltaTime, character); + } + + public override float GetPriority(Character character) + { + if (character.AnimController.CurrentHull == null) return 0.0f; + currenthullSafety = GetHullSafety(character.AnimController.CurrentHull); + priority = 100.0f - currenthullSafety; + return priority; + } + + private float GetHullSafety(Hull hull) + { + float waterPercentage = (hull.Volume / hull.FullVolume)*100.0f; + float fireAmount = 0.0f; + + foreach (FireSource fireSource in hull.FireSources) + { + fireAmount += fireSource.Size.X; + } + + float safety = 100.0f - fireAmount - waterPercentage; + if (hull.OxygenPercentage < 30.0f) safety -= (30.0f-hull.OxygenPercentage)*3.0f; + + return safety; + } + } +} diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs new file mode 100644 index 000000000..e8bd7d3ff --- /dev/null +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -0,0 +1,33 @@ +using Barotrauma.Items.Components; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma +{ + class AIObjectiveGoTo : AIObjective + { + AITarget target; + + private Character character; + + public AIObjectiveGoTo(AITarget target, Character character) + { + this.character = character; + this.target = target; + + } + + protected override void Act(float deltaTime, Character character) + { + if (target == null) return; + + character.AIController.SelectTarget(target); + + character.AIController.SteeringManager.SteeringSeek(ConvertUnits.ToDisplayUnits(target.Position)); + } + } +} diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveManager.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveManager.cs new file mode 100644 index 000000000..68d4f82ea --- /dev/null +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveManager.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma +{ + class AIObjectiveManager + { + private List objectives; + + private Character character; + + public AIObjectiveManager(Character character) + { + this.character = character; + + objectives = new List(); + } + + public void AddObjective(AIObjective objective) + { + if (objectives.Find(o => o.IsDuplicate(objective)) != null) return; + + objectives.Add(objective); + } + + public void UpdateObjectives() + { + if (!objectives.Any()) return; + + //remove completed objectives + objectives = objectives.FindAll(o => !o.IsCompleted()); + + //sort objectives according to priority + objectives.Sort((x, y) => x.GetPriority(character).CompareTo(y.GetPriority(character))); + + } + + public void DoCurrentObjective(float deltaTime) + { + if (!objectives.Any()) return; + objectives[0].TryComplete(deltaTime, character); + } + } +} diff --git a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs index f1459595a..8da97fd54 100644 --- a/Subsurface/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs +++ b/Subsurface/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs @@ -18,5 +18,13 @@ namespace Barotrauma { //item.AIOperate(float deltaTime, Character character) or something } + + public override bool IsDuplicate(AIObjective otherObjective) + { + AIObjectiveOperateItem operateItem = otherObjective as AIObjectiveOperateItem; + if (operateItem == null) return false; + + return (operateItem.targetItem == targetItem); + } } } diff --git a/Subsurface/Source/Characters/AI/PathFinder.cs b/Subsurface/Source/Characters/AI/PathFinder.cs index 692b83e3c..f6fe77c3a 100644 --- a/Subsurface/Source/Characters/AI/PathFinder.cs +++ b/Subsurface/Source/Characters/AI/PathFinder.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework; +using FarseerPhysics; +using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Linq; @@ -76,6 +77,9 @@ namespace Barotrauma class PathFinder { + public delegate float GetNodePenaltyHandler(PathNode node, PathNode prevNode); + public GetNodePenaltyHandler GetNodePriority; + List nodes; private bool insideSubmarine; @@ -89,6 +93,9 @@ namespace Barotrauma public SteeringPath FindPath(Vector2 start, Vector2 end) { + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + float closestDist = 0.0f; PathNode startNode = null; foreach (PathNode node in nodes) @@ -96,11 +103,19 @@ namespace Barotrauma float dist = Vector2.Distance(start,node.Position); if (dist openableButtons; + + public SteeringPath CurrentPath + { + get { return currentPath; } + } + + public PathFinder PathFinder + { + get { return pathFinder; } + } + private Vector2 currentTarget; private float findPathTimer; public PathSteeringManager(ISteerable host) : base(host) - {} + { + pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true); + pathFinder.GetNodePriority = GetNodePriority; + + character = (host as AIController).Character; + + openableButtons = new List(); + } public override void Update(float speed = 1) { @@ -36,14 +58,31 @@ namespace Barotrauma if (findPathTimer > 0.0f) return Vector2.Zero; currentTarget = target; - currentPath = pathFinder.FindPath(ConvertUnits.ToDisplayUnits(host.SimPosition), target); + currentPath = pathFinder.FindPath(host.SimPosition, ConvertUnits.ToSimUnits(target)); findPathTimer = 1.0f; return DiffToCurrentNode(); } + + + //if (pathSteering == null || pathSteering.CurrentPath == null || pathSteering.CurrentPath.CurrentNode == null) return; + + //if (currentPath.CurrentNode.ConnectedGap != null && currentPath.CurrentNode.ConnectedGap.Open < 0.9f) + //{ + foreach (Controller controller in openableButtons) + { + if (Vector2.Distance(controller.Item.SimPosition, character.SimPosition) > controller.Item.PickDistance) continue; + + controller.Item.Pick(character, false, true); + } + //} + + Vector2 diff = DiffToCurrentNode(); + + if (diff == Vector2.Zero) return -host.Steering; return (diff == Vector2.Zero) ? Vector2.Zero : Vector2.Normalize(diff)*speed; } @@ -52,11 +91,61 @@ namespace Barotrauma { if (currentPath == null) return Vector2.Zero; - currentPath.CheckProgress(host.SimPosition, 0.1f); + currentPath.CheckProgress(host.SimPosition, 0.45f); if (currentPath.CurrentNode == null) return Vector2.Zero; return currentPath.CurrentNode.SimPosition - host.SimPosition; } + + private float GetNodePriority(PathNode node, PathNode nextNode) + { + if (character==null) return 0.0f; + if (nextNode.Waypoint.ConnectedGap!=null) + { + if (nextNode.Waypoint.ConnectedGap.Open > 0.9f) return 0.0f; + if (nextNode.Waypoint.ConnectedGap.ConnectedDoor == null) return 100.0f; + + var doorButtons = GetDoorButtons(nextNode.Waypoint.ConnectedGap.ConnectedDoor); + foreach (Controller button in doorButtons) + { + if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) != + Math.Sign(node.Position.X - nextNode.Position.X)) continue; + + if (!button.HasRequiredItems(character, false)) return 1000.0f; + } + } + + return 0.0f; + } + + private List GetDoorButtons(Door door) + { + if (door == null) return new List(); + ConnectionPanel connectionPanel = door.Item.GetComponent(); + + List doorButtons = new List(); + + foreach (Connection c in connectionPanel.Connections) + { + foreach (Wire w in c.Wires) + { + if (w == null) continue; + var otherConnection = w.OtherConnection(c); + + if (otherConnection.Item == door.Item || otherConnection == null) continue; + + var controller = otherConnection.Item.GetComponent(); + if (controller != null) + { + doorButtons.Add(controller); + if (!openableButtons.Contains(controller)) openableButtons.Add(controller); + } + } + } + + return doorButtons; + } } + } diff --git a/Subsurface/Source/Characters/AICharacter.cs b/Subsurface/Source/Characters/AICharacter.cs index 21a1fa076..e4563302b 100644 --- a/Subsurface/Source/Characters/AICharacter.cs +++ b/Subsurface/Source/Characters/AICharacter.cs @@ -19,41 +19,45 @@ namespace Barotrauma get { return aiController; } } - public AICharacter(string file) : this(file, Vector2.Zero, null) - { - } + //public AICharacter(string file) : this(file, Vector2.Zero, null) + //{ + //} - public AICharacter(string file, Vector2 position) - : this(file, position, null) - { - } + //public AICharacter(string file, Vector2 position) + // : this(file, position, null) + //{ + //} - public AICharacter(CharacterInfo characterInfo, WayPoint spawnPoint, bool isNetworkPlayer = false) - : this(characterInfo.File, spawnPoint.SimPosition, characterInfo, isNetworkPlayer) - { + //public AICharacter(CharacterInfo characterInfo, WayPoint spawnPoint, bool isNetworkPlayer = false) + // : this(characterInfo.File, spawnPoint.SimPosition, characterInfo, isNetworkPlayer) + //{ - } + //} - public AICharacter(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) - : this(characterInfo.File, position, characterInfo, isNetworkPlayer) - { - } + //public AICharacter(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) + // : this(characterInfo.File, position, characterInfo, isNetworkPlayer) + //{ + //} public AICharacter(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) : base(file, position, characterInfo, isNetworkPlayer) { - aiController = new EnemyAIController(this, file); - - if (GameMain.Client != null && GameMain.Server == null) Enabled = false; } + public void SetAI(AIController aiController) + { + this.aiController = aiController; + } + public override void Update(Camera cam, float deltaTime) { base.Update(cam, deltaTime); if (isDead) return; + if (Controlled == this) return; + if (soundTimer > 0) { soundTimer -= deltaTime; diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index cfacf2c26..6c0a21867 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -257,12 +257,12 @@ namespace Barotrauma public override Vector2 SimPosition { - get { return AnimController.Limbs[0].SimPosition; } + get { return AnimController.RefLimb.SimPosition; } } public Vector2 Position { - get { return ConvertUnits.ToDisplayUnits(AnimController.Limbs[0].SimPosition); } + get { return ConvertUnits.ToDisplayUnits(AnimController.RefLimb.SimPosition); } } static Character() @@ -274,28 +274,53 @@ namespace Barotrauma DeathMsg[(int)CauseOfDeath.Pressure] = "been crushed by water pressure"; DeathMsg[(int)CauseOfDeath.Burn] = "burnt to death"; } - - public Character(string file) : this(file, Vector2.Zero, null) + + public static Character Create(string file, Vector2 position) { + return Create(file, position, null); } - public Character(string file, Vector2 position) - : this(file, position, null) + public static Character Create(CharacterInfo characterInfo, WayPoint spawnPoint, bool isNetworkPlayer = false) { + return Create(characterInfo.File, spawnPoint.SimPosition, characterInfo, isNetworkPlayer); } - public Character(CharacterInfo characterInfo, WayPoint spawnPoint, bool isNetworkPlayer = false) - : this(characterInfo.File, spawnPoint.SimPosition, characterInfo, isNetworkPlayer) - { + public static Character Create(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) + { + return Create(characterInfo.File, position, characterInfo, isNetworkPlayer); } - public Character(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) - : this(characterInfo.File, position, characterInfo, isNetworkPlayer) + public static Character Create(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) { + if (file != humanConfigFile) + { + var enemyCharacter = new AICharacter(file, position, characterInfo, isNetworkPlayer); + var ai = new EnemyAIController(enemyCharacter, file); + enemyCharacter.SetAI(ai); + + return enemyCharacter; + } + else + { + if (isNetworkPlayer) + { + var netCharacter = new Character(file, position, characterInfo, isNetworkPlayer); + + return netCharacter; + } + else + { + var character = new AICharacter(file, position, characterInfo, isNetworkPlayer); + var ai = new HumanAIController(character); + character.SetAI(ai); + + return character; + } + } } - public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) + protected Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) { keys = new Key[Enum.GetNames(typeof(InputType)).Length]; @@ -686,6 +711,8 @@ namespace Barotrauma /// public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true) { + AnimController.IsStanding = true; + Limb head = AnimController.GetLimb(LimbType.Head); Lights.LightManager.ViewPos = ConvertUnits.ToDisplayUnits(head.SimPosition); @@ -873,7 +900,7 @@ namespace Barotrauma ControlLocalPlayer(deltaTime, cam); } - if (!(this is AICharacter)) Control(deltaTime, cam); + if (controlled==this || !(this is AICharacter)) Control(deltaTime, cam); UpdateSightRange(); if (aiTarget != null) aiTarget.SoundRange = 0.0f; diff --git a/Subsurface/Source/Characters/Ragdoll.cs b/Subsurface/Source/Characters/Ragdoll.cs index 0aa080fca..870e9c459 100644 --- a/Subsurface/Source/Characters/Ragdoll.cs +++ b/Subsurface/Source/Characters/Ragdoll.cs @@ -314,17 +314,21 @@ namespace Barotrauma if (targetMovement.Y >= 0.0f && lowestLimb.SimPosition.Y > ConvertUnits.ToSimUnits(structure.Rect.Y - Submarine.GridSize.Y * 8.0f)) { - stairs = null; - return false; + //stairs = null; + //return false; } Limb limb = f1.Body.UserData as Limb; - if (limb != null && (limb.type == LimbType.LeftFoot || limb.type == LimbType.RightFoot)) + if (limb != null)// && (limb.type == LimbType.LeftFoot || limb.type == LimbType.RightFoot)) { if (contact.Manifold.LocalNormal.Y >= 0.0f) { - stairs = structure; - return true; + if (limb.SimPosition.Y < lowestLimb.SimPosition.Y+0.2f) + { + stairs = structure; + return true; + } + } else { @@ -332,10 +336,6 @@ namespace Barotrauma return false; } } - else - { - return false; - } } @@ -356,12 +356,15 @@ namespace Barotrauma avgVelocity = avgVelocity / Limbs.Count(); float impact = Vector2.Dot((f1.Body.LinearVelocity + avgVelocity) / 2.0f, -normal); - + if (GameMain.Server != null) impact = impact / 2.0f; Limb l = (Limb)f1.Body.UserData; - if (impact > 1.0f && l.HitSound != null && l.soundTimer <= 0.0f) l.HitSound.Play(Math.Min(impact / 5.0f, 1.0f), impact * 100.0f, l.body.FarseerBody); + float volume = stairs == null ? impact/5.0f : impact; + volume= Math.Min(impact, 1.0f); + + if (impact > 0.8f && l.HitSound != null && l.soundTimer <= 0.0f) l.HitSound.Play(volume, impact * 100.0f, l.body.FarseerBody); if (impact > l.impactTolerance) { diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index ab3363973..b245b55df 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -223,7 +223,7 @@ namespace Barotrauma if (commands[1].ToLower()=="human") { WayPoint spawnPoint = WayPoint.GetRandom(SpawnType.Human); - Character.Controlled = new Character(Character.HumanConfigFile, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); + Character.Controlled = Character.Create(Character.HumanConfigFile, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); if (GameMain.GameSession != null) { SinglePlayerMode mode = GameMain.GameSession.gameMode as SinglePlayerMode; @@ -235,7 +235,7 @@ namespace Barotrauma else { WayPoint spawnPoint = WayPoint.GetRandom(SpawnType.Enemy); - new AICharacter("Content/Characters/" + commands[1] + "/" + commands[1] + ".xml", (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); + Character.Create("Content/Characters/" + commands[1] + "/" + commands[1] + ".xml", (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); } break; diff --git a/Subsurface/Source/Events/MonsterEvent.cs b/Subsurface/Source/Events/MonsterEvent.cs index 7bd7bdc44..e16939bf2 100644 --- a/Subsurface/Source/Events/MonsterEvent.cs +++ b/Subsurface/Source/Events/MonsterEvent.cs @@ -45,7 +45,7 @@ namespace Barotrauma position.X += Rand.Range(-0.5f, 0.5f); position.Y += Rand.Range(-0.5f, 0.5f); - monsters[i] = new AICharacter(characterFile, position); + monsters[i] = Character.Create(characterFile, position); } } diff --git a/Subsurface/Source/Events/Quests/MonsterQuest.cs b/Subsurface/Source/Events/Quests/MonsterQuest.cs index 08f308a14..860d926ed 100644 --- a/Subsurface/Source/Events/Quests/MonsterQuest.cs +++ b/Subsurface/Source/Events/Quests/MonsterQuest.cs @@ -31,7 +31,7 @@ namespace Barotrauma { Vector2 position = level.PositionsOfInterest[Rand.Int(level.PositionsOfInterest.Count, false)]; - monster = new AICharacter(monsterFile, ConvertUnits.ToSimUnits(position+level.Position)); + monster = Character.Create(monsterFile, ConvertUnits.ToSimUnits(position+level.Position)); } public override void Update(float deltaTime) diff --git a/Subsurface/Source/GameSession/CrewManager.cs b/Subsurface/Source/GameSession/CrewManager.cs index 279260332..c81d87ffb 100644 --- a/Subsurface/Source/GameSession/CrewManager.cs +++ b/Subsurface/Source/GameSession/CrewManager.cs @@ -199,7 +199,7 @@ namespace Barotrauma //WayPoint randomWayPoint = WayPoint.GetRandom(SpawnType.Human); //Vector2 position = (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.SimPosition; - Character character = new Character(characterInfos[i], waypoints[i]); + Character character = Character.Create(characterInfos[i], waypoints[i]); Character.Controlled = character; if (!character.Info.StartItemsGiven) diff --git a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs index 53b0f5150..96d6fec8b 100644 --- a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs +++ b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs @@ -47,7 +47,7 @@ namespace Barotrauma CharacterInfo charInfo = new CharacterInfo(Character.HumanConfigFile, "", Gender.None, JobPrefab.List.Find(jp => jp.Name=="Engineer")); - Character character = new Character(charInfo, wayPoint.SimPosition); + Character character = Character.Create(charInfo, wayPoint.SimPosition); Character.Controlled = character; character.GiveJobItems(null); @@ -331,7 +331,7 @@ namespace Barotrauma } yield return new WaitForSeconds(1.0f); - var moloch = new AICharacter("Content/Characters/Moloch/moloch.xml", steering.Item.SimPosition + Vector2.UnitX * 25.0f); + var moloch = Character.Create("Content/Characters/Moloch/moloch.xml", steering.Item.SimPosition + Vector2.UnitX * 25.0f); moloch.PlaySound(AIController.AiState.Attack); yield return new WaitForSeconds(1.0f); diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index 23b0a0604..eb83c9986 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -42,6 +42,7 @@ namespace Barotrauma.Items.Components foreach (MapEntity e in item.linkedTo) { linkedGap = e as Gap; + linkedGap.ConnectedDoor = this; if (linkedGap != null) return linkedGap; } linkedGap = new Gap(item.Rect); diff --git a/Subsurface/Source/Items/Components/Holdable/Pickable.cs b/Subsurface/Source/Items/Components/Holdable/Pickable.cs index d3b3ebd5d..d27a504e0 100644 --- a/Subsurface/Source/Items/Components/Holdable/Pickable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Pickable.cs @@ -65,7 +65,7 @@ namespace Barotrauma.Items.Components var connectionPanel = item.GetComponent(); if (connectionPanel!=null) { - foreach (Connection c in connectionPanel.connections) + foreach (Connection c in connectionPanel.Connections) { foreach (Wire w in c.Wires) { diff --git a/Subsurface/Source/Items/Components/Signal/Connection.cs b/Subsurface/Source/Items/Components/Signal/Connection.cs index 3b79d54c3..1f41d7b6a 100644 --- a/Subsurface/Source/Items/Components/Signal/Connection.cs +++ b/Subsurface/Source/Items/Components/Signal/Connection.cs @@ -210,7 +210,7 @@ namespace Barotrauma.Items.Components float rightWireX = x+width / 2 + wireInterval; float leftWireX = x + width / 2 - wireInterval; - foreach (Connection c in panel.connections) + foreach (Connection c in panel.Connections) { //if dragging a wire, let the Inventory know so that the wire can be //dropped or dragged from the panel to the players inventory @@ -250,7 +250,7 @@ namespace Barotrauma.Items.Components //and the wire hasn't been connected yet, draw it on the panel if (equippedWire!=null) { - if (panel.connections.Find(c => c.Wires.Contains(equippedWire)) == null) + if (panel.Connections.Find(c => c.Wires.Contains(equippedWire)) == null) { DrawWire(spriteBatch, equippedWire.Item, equippedWire.Item, new Vector2(x + width / 2, y + height - 100), diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index 937aeeae6..06a5cb5cb 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -8,24 +8,24 @@ namespace Barotrauma.Items.Components { class ConnectionPanel : ItemComponent { - public List connections; + public List Connections; Character user; public ConnectionPanel(Item item, XElement element) : base(item, element) { - connections = new List(); + Connections = new List(); foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString()) { case "input": - connections.Add(new Connection(subElement, item)); + Connections.Add(new Connection(subElement, item)); break; case "output": - connections.Add(new Connection(subElement, item)); + Connections.Add(new Connection(subElement, item)); break; } } @@ -48,7 +48,7 @@ namespace Barotrauma.Items.Components { XElement componentElement = base.Save(parentElement); - foreach (Connection c in connections) + foreach (Connection c in Connections) { c.Save(componentElement); } @@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components public override void OnMapLoaded() { - foreach (Connection c in connections) + foreach (Connection c in Connections) { c.ConnectLinked(); } @@ -113,9 +113,9 @@ namespace Barotrauma.Items.Components } } - for (int i = 0; i w != null); message.Write((byte)wires.Length); @@ -142,7 +142,7 @@ namespace Barotrauma.Items.Components public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetBuffer message, float sendingTime) { System.Diagnostics.Debug.WriteLine("connectionpanel update"); - foreach (Connection c in connections) + foreach (Connection c in Connections) { //int wireCount = c.Wires.Length; c.ClearConnections(); diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 8a09a75c8..3a9e957a5 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -210,7 +210,7 @@ namespace Barotrauma { ConnectionPanel panel = GetComponent(); if (panel == null) return null; - return panel.connections; + return panel.Connections; } } @@ -790,7 +790,7 @@ namespace Barotrauma { ConnectionPanel panel = GetComponent(); if (panel == null) return; - foreach (Connection c in panel.connections) + foreach (Connection c in panel.Connections) { if (c.Name != connectionName) continue; diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 4fa897c26..3a62159f0 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -5,6 +5,7 @@ using FarseerPhysics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.ObjectModel; +using Barotrauma.Items.Components; namespace Barotrauma { @@ -37,6 +38,8 @@ namespace Barotrauma set { open = MathHelper.Clamp(value, 0.0f, 1.0f); } } + public Door ConnectedDoor; + public Vector2 FlowForce { get { return flowForce*soundVolume; } @@ -133,43 +136,6 @@ namespace Barotrauma linkedTo.Add(hulls[0]); if (hulls[1] != null) linkedTo.Add(hulls[1]); - //if (hull1 != null && hull2 != null) - //{ - // if (isHorizontal) - // { - // //make sure that water1 is the lefthand room - // //or that water2 is null if the gap doesn't lead to another room - // if (hull1.Rect.X < hull2.Rect.X) - // { - // linkedTo.Add(hull1); - // linkedTo.Add(hull2); - // } - // else - // { - // linkedTo.Add(hull2); - // linkedTo.Add(hull1); - // } - // } - // else - // { - // //make sure that water1 is the room on the top - // //or that water2 is null if the gap doesn't lead to another room - // if (hull1.Rect.Y > hull2.Rect.Y) - // { - // linkedTo.Add(hull1); - // linkedTo.Add(hull2); - // } - // else - // { - // linkedTo.Add(hull2); - // linkedTo.Add(hull1); - // } - // } - //} - //else - //{ - // linkedTo.Add(hull1); - //} } public override void Draw(SpriteBatch sb, bool editing, bool back = true) diff --git a/Subsurface/Source/Map/MapEntityPrefab.cs b/Subsurface/Source/Map/MapEntityPrefab.cs index f553214ff..d078c0d57 100644 --- a/Subsurface/Source/Map/MapEntityPrefab.cs +++ b/Subsurface/Source/Map/MapEntityPrefab.cs @@ -39,6 +39,7 @@ namespace Barotrauma public static MapEntityPrefab Selected { get { return selected; } + set { selected = value; } } public virtual bool IsLinkable diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index cae9a77b7..d825fc3bd 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -10,7 +10,7 @@ using System.Collections.ObjectModel; namespace Barotrauma { - public enum SpawnType { None, Human, Enemy, Cargo }; + public enum SpawnType { None, Human, Enemy, Cargo, Path }; class WayPoint : MapEntity { public static List WayPointList = new List(); @@ -23,6 +23,12 @@ namespace Barotrauma //only characters with this job will be spawned at the waypoint private JobPrefab assignedJob; + public Gap ConnectedGap + { + get; + private set; + } + public SpawnType SpawnType { get { return spawnType; } @@ -60,6 +66,13 @@ namespace Barotrauma WayPointList.Add(this); } + public WayPoint(Vector2 position, SpawnType spawnType, Gap gap = null) + :this(new Rectangle((int)position.X-3, (int)position.Y+3, 6, 6)) + { + this.spawnType = spawnType; + ConnectedGap = gap; + } + public override void Draw(SpriteBatch spriteBatch, bool editing, bool back=true) { @@ -70,6 +83,9 @@ namespace Barotrauma Color clr = (isSelected) ? Color.Red : Color.LightGreen; GUI.DrawRectangle(spriteBatch, new Rectangle(pos.X - rect.Width / 2, -pos.Y - rect.Height / 2, rect.Width, rect.Height), clr, true); + + spriteBatch.DrawString(GUI.SmallFont, Position.ToString(), new Vector2(Position.X, -Position.Y), Color.White); + foreach (MapEntity e in linkedTo) { GUI.DrawLine(spriteBatch, @@ -194,6 +210,120 @@ namespace Barotrauma return editingHUD; } + public static void GenerateSubWaypoints() + { + float minDist = 200.0f; + float heightFromFloor = 100.0f; + + foreach (Hull hull in Hull.hullList) + { + WayPoint prevWaypoint = null; + + if (hull.Rect.Width stairList = new List(); + foreach (MapEntity me in MapEntity.mapEntityList) + { + Structure stairs = me as Structure; + if (stairs == null) continue; + + if (stairs.StairDirection != Direction.None) stairList.Add(stairs); + } + + foreach (Structure stairs in stairList) + { + WayPoint[] stairPoints = new WayPoint[2]; + + stairPoints[0] = new WayPoint( + new Vector2(stairs.Rect.X - 50.0f, + stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? 80 : stairs.Rect.Height) + heightFromFloor), SpawnType.Path); + + stairPoints[1] = new WayPoint( + new Vector2(stairs.Rect.Right + 50.0f, + stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? stairs.Rect.Height : 80) + heightFromFloor), SpawnType.Path); + + for (int i = 0; i < 2; i++ ) + { + for (int dir = -1; dir <= 1; dir += 2) + { + WayPoint closest = stairPoints[i].FindClosest(dir, true, 30.0f); + if (closest == null) continue; + stairPoints[i].ConnectTo(closest); + } + } + + stairPoints[0].ConnectTo(stairPoints[1]); + } + + foreach (Gap gap in Gap.GapList) + { + if (!gap.isHorizontal) continue; + + var wayPoint = new WayPoint( + new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor), SpawnType.Path, gap); + + for (int dir = -1; dir <= 1; dir += 2) + { + WayPoint closest = wayPoint.FindClosest(dir, true, 30.0f); + if (closest == null) continue; + wayPoint.ConnectTo(closest); + } + } + } + + private WayPoint FindClosest(int dir, bool horizontalSearch, float tolerance) + { + if (dir != -1 && dir != 1) return null; + + float closestDist = 0.0f; + WayPoint closest = null; + + if (horizontalSearch) + { + foreach (WayPoint wp in WayPointList) + { + if (wp.SpawnType != SpawnType.Path || wp == this) continue; + + if (Math.Abs(wp.Position.Y - Position.Y) > tolerance) continue; + + float diff = wp.Position.X - Position.X; + if (Math.Sign(diff) != dir) continue; + + diff = Math.Abs(diff); + if (closest == null || diff < closestDist) + { + if (Submarine.CheckVisibility(SimPosition, wp.SimPosition) != null) continue; + + closestDist = diff; + closest = wp; + } + } + } + + return closest; + } + + private void ConnectTo(WayPoint wayPoint2) + { + linkedTo.Add(wayPoint2); + wayPoint2.linkedTo.Add(this); + } + public static WayPoint GetRandom(SpawnType spawnType = SpawnType.None, Job assignedJob = null) { List wayPoints = new List(); diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index e5fb122e0..0194b5b11 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -751,8 +751,8 @@ namespace Barotrauma.Networking } Character character = (closestWaypoint == null) ? - new Character(ch, position, !isMyCharacter) : - new Character(ch, closestWaypoint, !isMyCharacter); + Character.Create(ch, position, !isMyCharacter) : + Character.Create(ch, closestWaypoint, !isMyCharacter); character.ID = ID; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 86acbfe01..9f130bf95 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -756,14 +756,14 @@ namespace Barotrauma.Networking for (int i = 0; i < ConnectedClients.Count; i++) { - ConnectedClients[i].Character = new Character( + ConnectedClients[i].Character = Character.Create( ConnectedClients[i].characterInfo, assignedWayPoints[i], true); ConnectedClients[i].Character.GiveJobItems(assignedWayPoints[i]); } if (characterInfo != null) { - myCharacter = new Character(characterInfo, assignedWayPoints[assignedWayPoints.Length - 1]); + myCharacter = Character.Create(characterInfo, assignedWayPoints[assignedWayPoints.Length - 1]); Character.Controlled = myCharacter; myCharacter.GiveJobItems(assignedWayPoints[assignedWayPoints.Length - 1]); diff --git a/Subsurface/Source/PlayerInput.cs b/Subsurface/Source/PlayerInput.cs index 6cb886594..3d0eb8d1f 100644 --- a/Subsurface/Source/PlayerInput.cs +++ b/Subsurface/Source/PlayerInput.cs @@ -278,7 +278,7 @@ namespace Barotrauma return GameMain.Config.KeyBind(inputType).IsHit(); } - public static bool KeyDOwn(InputType inputType) + public static bool KeyDown(InputType inputType) { return GameMain.Config.KeyBind(inputType).IsDown(); } diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index e20bb27b9..7ff101292 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -81,6 +81,9 @@ namespace Barotrauma button = new GUIButton(new Rectangle(0, 220, 0, 20), "Character mode", Alignment.Left, GUI.Style, GUIpanel); button.ToolTip = "Allows you to pick up and use items. Useful for things such as placing items inside closets, turning devices on/off and doing the wiring."; button.OnClicked = ToggleCharacterMode; + + button = new GUIButton(new Rectangle(0, 270, 0, 20), "Generate waypoints", Alignment.Left, GUI.Style, GUIpanel); + button.OnClicked = GenerateWaypoints; GUItabs = new GUIComponent[2]; int width = 400, height = 400; @@ -140,6 +143,8 @@ namespace Barotrauma GUIComponent.MouseOn = null; + MapEntityPrefab.Selected = null; + if (dummyCharacter != null) { dummyCharacter.Remove(); @@ -152,7 +157,7 @@ namespace Barotrauma { if (dummyCharacter != null) dummyCharacter.Remove(); - dummyCharacter = new Character(Character.HumanConfigFile, Vector2.Zero); + dummyCharacter = Character.Create(Character.HumanConfigFile, Vector2.Zero); Character.Controlled = dummyCharacter; GameMain.World.ProcessChanges(); } @@ -204,6 +209,12 @@ namespace Barotrauma return true; } + private bool GenerateWaypoints(GUIButton button, object obj) + { + WayPoint.GenerateSubWaypoints(); + return true; + } + /// /// Allows the game to run logic such as updating the world, @@ -324,10 +335,6 @@ namespace Barotrauma } - //if (PlayerInput.GetMouseState.LeftButton != ButtonState.Pressed) - //{ - // Inventory.draggingItem = null; - //} } else { @@ -336,10 +343,9 @@ namespace Barotrauma GUI.Draw((float)deltaTime, spriteBatch, cam); - if (!PlayerInput.LeftButtonDown()) Inventory.draggingItem = null; + if (!PlayerInput.LeftButtonDown()) Inventory.draggingItem = null; spriteBatch.End(); - } } } diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index e6e125c33..54ca7c8c8 100644 Binary files a/Subsurface_Solution.v12.suo and b/Subsurface_Solution.v12.suo differ