Human AI with pathfinding and room hazard avoidance

This commit is contained in:
Regalis
2015-11-23 01:22:38 +02:00
parent 77024a31fb
commit c456fa3c90
32 changed files with 665 additions and 126 deletions

View File

@@ -60,6 +60,10 @@
<Compile Include="Source\Characters\AICharacter.cs" />
<Compile Include="Source\Characters\AI\AIController.cs" />
<Compile Include="Source\Characters\AI\AITarget.cs" />
<Compile Include="Source\Characters\AI\HumanAIController.cs" />
<Compile Include="Source\Characters\AI\Objectives\AIObjectiveFindSafety.cs" />
<Compile Include="Source\Characters\AI\Objectives\AIObjectiveGoTo.cs" />
<Compile Include="Source\Characters\AI\Objectives\AIObjectiveManager.cs" />
<Compile Include="Source\Characters\AI\Objectives\AIObjectiveOperateItem.cs" />
<Compile Include="Source\Characters\AI\Objectives\AIObjective.cs" />
<Compile Include="Source\Characters\AI\PathFinder.cs" />

View File

@@ -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) { }

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -9,6 +9,8 @@ namespace Barotrauma
{
protected List<AIObjective> subObjectives;
protected float priority;
public virtual bool IsCompleted()
{
return false;
@@ -21,7 +23,7 @@ namespace Barotrauma
/// <summary>
/// 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
/// </summary>
/// <param name="character">the character who's trying to achieve the objective</param>
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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma
{
class AIObjectiveManager
{
private List<AIObjective> objectives;
private Character character;
public AIObjectiveManager(Character character)
{
this.character = character;
objectives = new List<AIObjective>();
}
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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<PathNode> 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<closestDist || startNode==null)
{
if (insideSubmarine && Submarine.CheckVisibility(start, node.Position) != null) continue;
closestDist = dist;
startNode = node;
}
}
if (startNode == null)
{
DebugConsole.ThrowError("Pathfinding error, couldn't find a start node");
return new SteeringPath();
}
closestDist = 0.0f;
PathNode endNode = null;
foreach (PathNode node in nodes)
@@ -108,18 +123,26 @@ namespace Barotrauma
float dist = Vector2.Distance(end, node.Position);
if (dist < closestDist || endNode == null)
{
if (insideSubmarine && Submarine.CheckVisibility(end, node.Waypoint.SimPosition) != null) continue;
closestDist = dist;
endNode = node;
}
}
if (startNode == null || endNode == null)
if (endNode == null)
{
DebugConsole.ThrowError("Pathfinding error, couldn't find pathnodes");
DebugConsole.ThrowError("Pathfinding error, couldn't find an end node");
return new SteeringPath();
}
return FindPath(startNode,endNode);
var path = FindPath(startNode,endNode);
sw.Stop();
System.Diagnostics.Debug.WriteLine("findpath: " + sw.ElapsedTicks);
return path;
}
public SteeringPath FindPath(WayPoint start, WayPoint end)
@@ -157,7 +180,7 @@ namespace Barotrauma
node.G = 0.0f;
node.H = 0.0f;
}
start.state = 1;
while (true)
{
@@ -185,7 +208,10 @@ namespace Barotrauma
//a node that hasn't been searched yet
if (nextNode.state==0)
{
nextNode.H = Vector2.Distance(nextNode.Position,end.Position);
nextNode.H = Vector2.DistanceSquared(nextNode.Position,end.Position);
if (GetNodePriority != null) nextNode.H += GetNodePriority(currNode, nextNode);
nextNode.G = currNode.G + currNode.distances[i];
nextNode.F = nextNode.G + nextNode.H;
nextNode.Parent = currNode;

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using FarseerPhysics;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -12,13 +13,34 @@ namespace Barotrauma
private PathFinder pathFinder;
private SteeringPath currentPath;
private Character character;
private List<Controller> 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<Controller>();
}
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<Controller> GetDoorButtons(Door door)
{
if (door == null) return new List<Controller>();
ConnectionPanel connectionPanel = door.Item.GetComponent<ConnectionPanel>();
List<Controller> doorButtons = new List<Controller>();
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<Controller>();
if (controller != null)
{
doorButtons.Add(controller);
if (!openableButtons.Contains(controller)) openableButtons.Add(controller);
}
}
}
return doorButtons;
}
}
}

View File

@@ -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;

View File

@@ -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
/// </summary>
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;

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -65,7 +65,7 @@ namespace Barotrauma.Items.Components
var connectionPanel = item.GetComponent<ConnectionPanel>();
if (connectionPanel!=null)
{
foreach (Connection c in connectionPanel.connections)
foreach (Connection c in connectionPanel.Connections)
{
foreach (Wire w in c.Wires)
{

View File

@@ -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),

View File

@@ -8,24 +8,24 @@ namespace Barotrauma.Items.Components
{
class ConnectionPanel : ItemComponent
{
public List<Connection> connections;
public List<Connection> Connections;
Character user;
public ConnectionPanel(Item item, XElement element)
: base(item, element)
{
connections = new List<Connection>();
Connections = new List<Connection>();
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<loadedConnections.Count && i<connections.Count; i++)
for (int i = 0; i<loadedConnections.Count && i<Connections.Count; i++)
{
loadedConnections[i].wireId.CopyTo(connections[i].wireId, 0);
loadedConnections[i].wireId.CopyTo(Connections[i].wireId, 0);
}
}
@@ -126,7 +126,7 @@ namespace Barotrauma.Items.Components
public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetBuffer message)
{
foreach (Connection c in connections)
foreach (Connection c in Connections)
{
Wire[] wires = Array.FindAll(c.Wires, w => 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();

View File

@@ -210,7 +210,7 @@ namespace Barotrauma
{
ConnectionPanel panel = GetComponent<ConnectionPanel>();
if (panel == null) return null;
return panel.connections;
return panel.Connections;
}
}
@@ -790,7 +790,7 @@ namespace Barotrauma
{
ConnectionPanel panel = GetComponent<ConnectionPanel>();
if (panel == null) return;
foreach (Connection c in panel.connections)
foreach (Connection c in panel.Connections)
{
if (c.Name != connectionName) continue;

View File

@@ -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)

View File

@@ -39,6 +39,7 @@ namespace Barotrauma
public static MapEntityPrefab Selected
{
get { return selected; }
set { selected = value; }
}
public virtual bool IsLinkable

View File

@@ -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<WayPoint> WayPointList = new List<WayPoint>();
@@ -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<minDist*3.0f)
{
var wayPoint = new WayPoint(
new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path);
continue;
}
for (float x = hull.Rect.X + minDist; x <= hull.Rect.X + hull.Rect.Width - minDist; x += minDist)
{
var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path);
if (prevWaypoint != null) wayPoint.ConnectTo(prevWaypoint);
prevWaypoint = wayPoint;
}
}
List<Structure> stairList = new List<Structure>();
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<WayPoint> wayPoints = new List<WayPoint>();

View File

@@ -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;

View File

@@ -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]);

View File

@@ -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();
}

View File

@@ -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;
}
/// <summary>
/// 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();
}
}
}

Binary file not shown.