(c1468d39d) Overhauled docking interface (WIP)

This commit is contained in:
Joonas Rikkonen
2019-05-03 13:39:54 +03:00
parent 7eb0972d16
commit b551ae7999
101 changed files with 1249 additions and 2246 deletions
@@ -320,9 +320,8 @@ namespace Barotrauma
{
State = AIState.Idle;
}
else if (Character.Health < fleeHealthThreshold && SwarmBehavior == null)
else if (Character.Health < fleeHealthThreshold)
{
// Don't flee from damage if in a swarm.
State = AIState.Escape;
}
else if (targetingPriority != null)
@@ -331,6 +330,8 @@ namespace Barotrauma
}
}
LatchOntoAI?.Update(this, deltaTime);
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
{
State = AIState.Idle;
@@ -368,15 +369,8 @@ namespace Barotrauma
default:
throw new NotImplementedException();
}
LatchOntoAI?.Update(this, deltaTime);
IsSteeringThroughGap = false;
if (SwarmBehavior != null)
{
SwarmBehavior.IsActive = State == AIState.Idle && Character.CurrentHull == null;
SwarmBehavior.Refresh();
SwarmBehavior.UpdateSteering(deltaTime);
}
SwarmBehavior?.Update(deltaTime);
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
}
@@ -499,7 +493,7 @@ namespace Barotrauma
}
else
{
if (!IsLatchedOnSub)
if (!IsProperlyLatchedOnSub)
{
UpdateWallTarget();
}
@@ -558,7 +552,7 @@ namespace Barotrauma
{
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
if (section?.gap != null && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
{
return;
}
@@ -799,21 +793,9 @@ namespace Barotrauma
}
}
public bool IsSteeringThroughGap { get; private set; }
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
{
IsSteeringThroughGap = true;
SelectedAiTarget = wall.AiTarget;
wallTarget = null;
LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs();
Hull targetHull = section.gap?.FlowTargetHull;
float distance = Vector2.Distance(Character.WorldPosition, targetWorldPos);
float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height);
if (distance > maxDistance)
{
return false;
}
if (targetHull != null)
{
if (wall.IsHorizontal)
@@ -824,7 +806,16 @@ namespace Barotrauma
{
targetWorldPos.X = targetHull.WorldRect.Center.X;
}
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs();
if (steeringManager is IndoorsSteeringManager)
{
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
}
else
{
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
}
return true;
}
return false;
@@ -1056,43 +1047,19 @@ namespace Barotrauma
#region Targeting
private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub;
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
//goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the Character can see/hear the target and chooses the most preferable target within
//sight/hearing range
public AITarget UpdateTargets(Character character, out TargetingPriority priority)
{
if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub)
if (IsProperlyLatchedOnSub)
{
var wall = SelectedAiTarget.Entity as Structure;
if (wall == null)
{
wall = wallTarget?.Structure;
}
// The target is not a wall or it's not the same as we are attached to -> release
bool releaseTarget = wall == null || !wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB);
if (!releaseTarget)
{
for (int i = 0; i < wall.Sections.Length; i++)
{
if (CanPassThroughHole(wall, i))
{
releaseTarget = true;
}
}
}
if (releaseTarget)
{
SelectedAiTarget = null;
wallTarget = null;
LatchOntoAI.DeattachFromBody();
}
else if (SelectedAiTarget?.Entity == wallTarget?.Structure)
{
// If attached to a valid target, just keep the target.
// Priority not used in this case.
priority = null;
return SelectedAiTarget;
}
// If attached to a valid target, just keep the target.
// Priority not used in this case.
priority = null;
return SelectedAiTarget;
}
AITarget newTarget = null;
priority = null;
@@ -1175,7 +1142,7 @@ namespace Barotrauma
else if (target.Entity != null)
{
//skip the target if it's a room and the character is already inside a sub
if (character.CurrentHull != null && target.Entity is Hull) { continue; }
if (character.CurrentHull != null && target.Entity is Hull) continue;
Door door = null;
if (target.Entity is Item item)
@@ -1204,37 +1171,23 @@ namespace Barotrauma
// Ignore structures that doesn't have a body (not walls)
continue;
}
if (s.IsPlatform)
{
continue;
}
if (character.CurrentHull != null)
{
// Ignore walls when inside.
continue;
}
valueModifier = 1;
float wallMaxHealth = 400; // Anything more than this is ignored -> 200 = 1
// Prefer weaker targets.
valueModifier *= MathHelper.Lerp(1.5f, 0.5f, MathUtils.InverseLerp(0, 1, s.Health / wallMaxHealth));
// Ignore walls when inside.
valueModifier = character.CurrentHull == null ? 1 : 0;
if (aggressiveBoarding)
{
var hulls = s.Submarine.GetHulls(false);
for (int i = 0; i < s.Sections.Length; i++)
{
var section = s.Sections[i];
if (section.gap != null)
if (CanPassThroughHole(s, i))
{
if (CanPassThroughHole(s, i))
{
bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null && hulls.Any(h => h.Rect.Intersects(section.rect));
valueModifier *= leadsInside ? 5 : 0;
}
else
{
// up to 100% priority increase for every gap in the wall
valueModifier *= 1 + section.gap.Open;
}
// Ignore walls that can be passed through
valueModifier = 0;
break;
}
else if (section.gap != null)
{
// up to 100% priority increase for every gap in the wall
valueModifier *= 1 + section.gap.Open;
}
}
}
@@ -223,10 +223,8 @@ namespace Barotrauma
}
}
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
//only humanoids can climb ladders
if (!isDiving && character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
if (character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
{
if (character.SelectedConstruction != currentPath.CurrentNode.Ladders.Item &&
currentPath.CurrentNode.Ladders.Item.IsInsideTrigger(character.WorldPosition))
@@ -236,7 +234,7 @@ namespace Barotrauma
}
var collider = character.AnimController.Collider;
if (character.IsClimbing && !isDiving)
if (character.IsClimbing)
{
Vector2 diff = currentPath.CurrentNode.SimPosition - pos;
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
@@ -280,12 +278,6 @@ namespace Barotrauma
}
else if (character.AnimController.InWater)
{
// If the character is underwater, we don't need the ladders anymore
if (character.IsClimbing && isDiving)
{
character.AnimController.Anim = AnimController.Animation.None;
character.SelectedConstruction = null;
}
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2))
{
currentPath.SkipToNextNode();
@@ -54,7 +54,7 @@ namespace Barotrauma
get { return attachJoints.Count > 0; }
}
public bool IsAttachedToSub => IsAttached && (attachTargetBody?.UserData is Submarine || attachTargetBody?.UserData is Entity entity && entity.Submarine != null);
public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null);
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
{
@@ -190,7 +190,7 @@ namespace Barotrauma
case AIController.AIState.Attack:
if (enemyAI.AttackingLimb != null)
{
if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null)
if (attachToSub && wallAttachPos != Vector2.Zero && attachTargetBody != null)
{
// is not attached or is attached to something else
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
@@ -14,16 +14,11 @@ namespace Barotrauma
private float maxDistFromCenter;
private float cohesion;
public List<AICharacter> Members { get; private set; } = new List<AICharacter>();
public HashSet<AICharacter> ActiveMembers { get; private set; } = new HashSet<AICharacter>();
private List<AICharacter> members = new List<AICharacter>();
private EnemyAIController ai;
private AIController ai;
public bool IsActive { get; set; }
public bool IsEnoughMembers => ActiveMembers.Count > 1;
public SwarmBehavior(XElement element, EnemyAIController ai)
public SwarmBehavior(XElement element, AIController ai)
{
this.ai = ai;
minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f));
@@ -37,36 +32,21 @@ namespace Barotrauma
{
if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
{
enemyAI.SwarmBehavior.Members = swarm.ToList();
enemyAI.SwarmBehavior.members = swarm.ToList();
}
}
}
public void Refresh()
public void Update(float deltaTime)
{
Members.RemoveAll(m => m.IsDead || m.Removed);
foreach (var member in Members)
{
if (!member.AIController.Enabled && member.IsRemotePlayer || Character.Controlled == member || !((EnemyAIController)member.AIController).SwarmBehavior.IsActive)
{
ActiveMembers.Remove(member);
}
else
{
ActiveMembers.Add(member);
}
}
}
members.RemoveAll(m => m.IsDead || m.Removed);
if (members.Count < 2) { return; }
public void UpdateSteering(float deltaTime)
{
if (!IsActive) { return; }
if (!IsEnoughMembers) { return; }
//calculate the "center of mass" of the swarm and the distance to the closest character in the swarm
float closestDistSqr = float.MaxValue;
Vector2 center = Vector2.Zero;
AICharacter closest = null;
foreach (AICharacter member in Members)
foreach (AICharacter member in members)
{
center += member.SimPosition;
if (member == ai.Character) { continue; }
@@ -77,7 +57,7 @@ namespace Barotrauma
closest = member;
}
}
center /= Members.Count;
center /= members.Count;
if (closest == null) { return; }
@@ -103,11 +83,11 @@ namespace Barotrauma
if (cohesion > 0.0f)
{
Vector2 avgVel = Vector2.Zero;
foreach (AICharacter member in Members)
foreach (AICharacter member in members)
{
avgVel += member.AnimController.TargetMovement;
}
avgVel /= Members.Count;
avgVel /= members.Count;
ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion);
}
}
@@ -293,27 +293,6 @@ namespace Barotrauma
return;
}
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
//stop dragging if there's something between the pull limb and the target
Vector2 sourceSimPos = mouthLimb.SimPosition;
Vector2 targetSimPos = target.SimPosition;
if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
{
targetSimPos -= character.Submarine.SimPosition;
}
else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
{
sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
}
var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
if (body != null)
{
character.DeselectCharacter();
return;
}
}
Character targetCharacter = target;
float eatSpeed = character.Mass / targetCharacter.Mass * 0.1f;
eatTimer += deltaTime * eatSpeed;
@@ -751,6 +730,22 @@ namespace Barotrauma
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
}
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
{
//make sure the angle "has the same number of revolutions" as the reference limb
//(e.g. we don't want to rotate the legs to 0 if the torso is at 360, because that'd blow up the hip joints)
while (referenceLimb.Rotation - angle > MathHelper.TwoPi)
{
angle += MathHelper.TwoPi;
}
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
{
angle -= MathHelper.TwoPi;
}
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
}
public override void Flip()
{
base.Flip();
@@ -560,8 +560,7 @@ namespace Barotrauma
//TODO: take into account that the feet aren't necessarily in CurrentHull
//full slowdown (1.5f) when water is up to the torso
surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface);
float bottomPos = Math.Max(colliderPos.Y, currentHull.Rect.Y - currentHull.Rect.Height);
slowdownAmount = MathHelper.Clamp((surfaceY - bottomPos) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
}
float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f);
@@ -426,7 +426,7 @@ namespace Barotrauma
}
}
public bool CanSpeak;
private bool canSpeak;
private bool speechImpedimentSet;
@@ -436,7 +436,7 @@ namespace Barotrauma
{
get
{
if (!CanSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
if (!canSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
return speechImpediment;
}
set
@@ -710,7 +710,7 @@ namespace Barotrauma
displayName = TextManager.Get($"Character.{Path.GetFileName(Path.GetDirectoryName(file))}", true);
IsHumanoid = doc.Root.GetAttributeBool("humanoid", false);
CanSpeak = doc.Root.GetAttributeBool("canspeak", false);
canSpeak = doc.Root.GetAttributeBool("canspeak", false);
needsAir = doc.Root.GetAttributeBool("needsair", false);
Noise = doc.Root.GetAttributeFloat("noise", 100f);
@@ -853,7 +853,7 @@ namespace Barotrauma
{
if (characterConfigFiles == null)
{
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true);
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character);
}
return characterConfigFiles;
}
@@ -2555,9 +2555,11 @@ namespace Barotrauma
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
#endif
CharacterList.Remove(this);
#if CLIENT
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
#endif
if (Controlled == this) { Controlled = null; }
CharacterList.Remove(this);
if (Inventory != null)
{
@@ -399,7 +399,7 @@ namespace Barotrauma
{
ID = idCounter;
idCounter++;
Name = element.GetAttributeString("name", "");
Name = element.GetAttributeString("name", "unnamed");
string genderStr = element.GetAttributeString("gender", "male").ToLowerInvariant();
File = element.GetAttributeString("file", "");
SourceElement = GetConfig(File).Root;
@@ -423,29 +423,6 @@ namespace Barotrauma
element.GetAttributeInt("beardindex", -1),
element.GetAttributeInt("moustacheindex", -1),
element.GetAttributeInt("faceattachmentindex", -1));
if (string.IsNullOrEmpty(Name))
{
if (SourceElement.Element("name") != null)
{
string firstNamePath = SourceElement.Element("name").GetAttributeString("firstname", "");
if (firstNamePath != "")
{
firstNamePath = firstNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
Name = ToolBox.GetRandomLine(firstNamePath);
}
string lastNamePath = SourceElement.Element("name").GetAttributeString("lastname", "");
if (lastNamePath != "")
{
lastNamePath = lastNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
if (Name != "") Name += " ";
Name += ToolBox.GetRandomLine(lastNamePath);
}
}
}
StartItemsGiven = element.GetAttributeBool("startitemsgiven", false);
string personalityName = element.GetAttributeString("personality", "");
ragdollFileName = element.GetAttributeString("ragdoll", string.Empty);