(6f4bea467) Updated doors
This commit is contained in:
@@ -320,8 +320,9 @@ namespace Barotrauma
|
||||
{
|
||||
State = AIState.Idle;
|
||||
}
|
||||
else if (Character.Health < fleeHealthThreshold)
|
||||
else if (Character.Health < fleeHealthThreshold && SwarmBehavior == null)
|
||||
{
|
||||
// Don't flee from damage if in a swarm.
|
||||
State = AIState.Escape;
|
||||
}
|
||||
else if (targetingPriority != null)
|
||||
@@ -330,8 +331,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
|
||||
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
|
||||
{
|
||||
State = AIState.Idle;
|
||||
@@ -369,8 +368,15 @@ namespace Barotrauma
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
SwarmBehavior?.Update(deltaTime);
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
IsSteeringThroughGap = false;
|
||||
if (SwarmBehavior != null)
|
||||
{
|
||||
SwarmBehavior.IsActive = State == AIState.Idle && Character.CurrentHull == null;
|
||||
SwarmBehavior.Refresh();
|
||||
SwarmBehavior.UpdateSteering(deltaTime);
|
||||
}
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||
}
|
||||
|
||||
@@ -493,7 +499,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsProperlyLatchedOnSub)
|
||||
if (!IsLatchedOnSub)
|
||||
{
|
||||
UpdateWallTarget();
|
||||
}
|
||||
@@ -552,7 +558,7 @@ namespace Barotrauma
|
||||
{
|
||||
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
|
||||
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
|
||||
if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
if (section?.gap != null && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -794,9 +800,21 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -807,16 +825,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetWorldPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
if (steeringManager is IndoorsSteeringManager)
|
||||
{
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
|
||||
}
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1048,43 +1057,43 @@ namespace Barotrauma
|
||||
#region Targeting
|
||||
private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
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 (IsProperlyLatchedOnSub)
|
||||
if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub)
|
||||
{
|
||||
// If attached to a valid target, just keep the target.
|
||||
// Priority not used in this case.
|
||||
priority = null;
|
||||
return SelectedAiTarget;
|
||||
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;
|
||||
}
|
||||
}
|
||||
AITarget newTarget = null;
|
||||
priority = null;
|
||||
@@ -1167,7 +1176,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)
|
||||
@@ -1196,23 +1205,37 @@ namespace Barotrauma
|
||||
// Ignore structures that doesn't have a body (not walls)
|
||||
continue;
|
||||
}
|
||||
// Ignore walls when inside.
|
||||
valueModifier = character.CurrentHull == null ? 1 : 0;
|
||||
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));
|
||||
if (aggressiveBoarding)
|
||||
{
|
||||
var hulls = s.Submarine.GetHulls(false);
|
||||
for (int i = 0; i < s.Sections.Length; i++)
|
||||
{
|
||||
var section = s.Sections[i];
|
||||
if (CanPassThroughHole(s, i))
|
||||
if (section.gap != null)
|
||||
{
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,8 +223,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
|
||||
|
||||
//only humanoids can climb ladders
|
||||
if (character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
if (!isDiving && character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
{
|
||||
if (character.SelectedConstruction != currentPath.CurrentNode.Ladders.Item &&
|
||||
currentPath.CurrentNode.Ladders.Item.IsInsideTrigger(character.WorldPosition))
|
||||
@@ -234,7 +236,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var collider = character.AnimController.Collider;
|
||||
if (character.IsClimbing)
|
||||
if (character.IsClimbing && !isDiving)
|
||||
{
|
||||
Vector2 diff = currentPath.CurrentNode.SimPosition - pos;
|
||||
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
|
||||
@@ -278,6 +280,12 @@ 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 Entity entity && (entity is Submarine sub || entity?.Submarine != null);
|
||||
public bool IsAttachedToSub => IsAttached && (attachTargetBody?.UserData is Submarine || attachTargetBody?.UserData is Entity entity && 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 && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
{
|
||||
// is not attached or is attached to something else
|
||||
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
|
||||
|
||||
@@ -14,11 +14,16 @@ namespace Barotrauma
|
||||
private float maxDistFromCenter;
|
||||
private float cohesion;
|
||||
|
||||
private List<AICharacter> members = new List<AICharacter>();
|
||||
public List<AICharacter> Members { get; private set; } = new List<AICharacter>();
|
||||
public HashSet<AICharacter> ActiveMembers { get; private set; } = new HashSet<AICharacter>();
|
||||
|
||||
private AIController ai;
|
||||
private EnemyAIController ai;
|
||||
|
||||
public SwarmBehavior(XElement element, AIController ai)
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsEnoughMembers => ActiveMembers.Count > 1;
|
||||
|
||||
|
||||
public SwarmBehavior(XElement element, EnemyAIController ai)
|
||||
{
|
||||
this.ai = ai;
|
||||
minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f));
|
||||
@@ -32,21 +37,36 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
|
||||
{
|
||||
enemyAI.SwarmBehavior.members = swarm.ToList();
|
||||
enemyAI.SwarmBehavior.Members = swarm.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
public void Refresh()
|
||||
{
|
||||
members.RemoveAll(m => m.IsDead || m.Removed);
|
||||
if (members.Count < 2) { return; }
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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; }
|
||||
@@ -57,7 +77,7 @@ namespace Barotrauma
|
||||
closest = member;
|
||||
}
|
||||
}
|
||||
center /= members.Count;
|
||||
center /= Members.Count;
|
||||
|
||||
if (closest == null) { return; }
|
||||
|
||||
@@ -83,11 +103,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,6 +293,27 @@ 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;
|
||||
@@ -712,6 +733,12 @@ namespace Barotrauma
|
||||
|
||||
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
|
||||
}
|
||||
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
|
||||
{
|
||||
angle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
|
||||
|
||||
@@ -560,7 +560,8 @@ 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);
|
||||
slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
|
||||
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;
|
||||
}
|
||||
|
||||
float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f);
|
||||
|
||||
@@ -853,7 +853,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (characterConfigFiles == null)
|
||||
{
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character);
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true);
|
||||
}
|
||||
return characterConfigFiles;
|
||||
}
|
||||
@@ -2551,64 +2551,14 @@ namespace Barotrauma
|
||||
|
||||
if (info != null) { info.Remove(); }
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
CharacterList.Remove(this);
|
||||
|
||||
if (Controlled == this) { Controlled = null; }
|
||||
|
||||
if (Inventory != null)
|
||||
{
|
||||
foreach (Item item in Inventory.Items)
|
||||
|
||||
@@ -217,6 +217,7 @@ namespace Barotrauma
|
||||
{
|
||||
return corePackageRequiredFiles.All(fileType => Files.Any(file => file.Type == fileType));
|
||||
}
|
||||
|
||||
public bool ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes)
|
||||
{
|
||||
missingContentTypes = new List<ContentType>();
|
||||
@@ -230,6 +231,25 @@ namespace Barotrauma
|
||||
return missingContentTypes.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure all the files defined in the content package are present
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool VerifyFiles(out List<string> errorMessages)
|
||||
{
|
||||
errorMessages = new List<string>();
|
||||
foreach (ContentFile file in Files)
|
||||
{
|
||||
if (!File.Exists(file.Path))
|
||||
{
|
||||
errorMessages.Add("File \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.Count == 0;
|
||||
}
|
||||
|
||||
public static ContentPackage CreatePackage(string name, string path, bool corePackage)
|
||||
{
|
||||
ContentPackage newPackage = new ContentPackage()
|
||||
@@ -398,6 +418,13 @@ namespace Barotrauma
|
||||
return path == "Mods";
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Are mods allowed to install a file into the specified path. If a content package XML includes files
|
||||
/// with a prohibited path, they are treated as references to external files. For example, a mod could include
|
||||
/// some vanilla files in the XML, in which case the game will simply use the vanilla files present in the game folder.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsModFilePathAllowed(string path)
|
||||
{
|
||||
while (true)
|
||||
@@ -422,16 +449,6 @@ namespace Barotrauma
|
||||
return contentPackages.SelectMany(f => f.Files).Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
return Files.Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFilesOfType(IEnumerable<ContentPackage> contentPackages, ContentType type)
|
||||
{
|
||||
return contentPackages.SelectMany(f => f.Files).Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
return Files.Where(f => f.Type == type).Select(f => f.Path);
|
||||
|
||||
@@ -34,14 +34,33 @@ namespace Barotrauma
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
//bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
//for (int i = 0; i < monsterCount; i++)
|
||||
//{
|
||||
// monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false));
|
||||
//}
|
||||
//monsters.ForEach(m => m.Enabled = false);
|
||||
//SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
//sonarPositions.Add(spawnPos);
|
||||
|
||||
float offsetAmount = 500;
|
||||
for (int i = 0; i < monsterCount; i++)
|
||||
{
|
||||
monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false));
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
var monster = Character.Create(monsterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true);
|
||||
monster.Enabled = false;
|
||||
monsters.Add(monster);
|
||||
if (monsters.Count == monsterCount)
|
||||
{
|
||||
//this will do nothing if the monsters have no swarm behavior defined,
|
||||
//otherwise it'll make the spawned characters act as a swarm
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
sonarPositions.Add(spawnPos);
|
||||
}
|
||||
}, Rand.Range(0f, monsterCount / 2, Rand.RandSync.Server));
|
||||
}
|
||||
monsters.ForEach(m => m.Enabled = false);
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
sonarPositions.Add(spawnPos);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
|
||||
@@ -604,13 +604,14 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
bool packageOk = contentPackage.VerifyFiles(out List<string> errorMessages);
|
||||
if (!packageOk)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\":\n" + string.Join("\n", errorMessages));
|
||||
continue;
|
||||
}
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
@@ -970,13 +971,14 @@ namespace Barotrauma
|
||||
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
bool packageOk = contentPackage.VerifyFiles(out List<string> errorMessages);
|
||||
if (!packageOk)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\":\n" + string.Join("\n", errorMessages));
|
||||
continue;
|
||||
}
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,10 +293,35 @@ namespace Barotrauma.Items.Components
|
||||
//steer closer if almost in range
|
||||
if (dist > Range)
|
||||
{
|
||||
Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f);
|
||||
standPos = leak.WorldPosition + standPos * Range;
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
Vector2 standPos = new Vector2(Math.Sign(-fromItemToLeak.X), Math.Sign(-fromItemToLeak.Y)) / 2;
|
||||
if (!character.AnimController.InWater)
|
||||
{
|
||||
if (leak.IsHorizontal)
|
||||
{
|
||||
standPos.X *= 2;
|
||||
standPos.Y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
standPos.X = 0;
|
||||
}
|
||||
}
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
{
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -305,30 +330,29 @@ namespace Barotrauma.Items.Components
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
|
||||
}
|
||||
else
|
||||
else if (dist <= Range)
|
||||
{
|
||||
// In range
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sinTime += deltaTime;
|
||||
character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
|
||||
if (item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
// If the character is climbing, ignore the check, because we cannot aim while climbing.
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4)
|
||||
var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
|
||||
if (angle < MathHelper.PiOver4)
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
}
|
||||
else
|
||||
{
|
||||
sinTime -= deltaTime * 2;
|
||||
}
|
||||
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
|
||||
|
||||
@@ -47,9 +47,8 @@ namespace Barotrauma.Items.Components
|
||||
//was the last ping sent with directional pinging
|
||||
private bool isLastPingDirectional;
|
||||
|
||||
private Sprite pingCircle, directionalPingCircle, screenOverlay, screenBackground;
|
||||
private Sprite sonarBlip;
|
||||
private Sprite lineSprite;
|
||||
private readonly Sprite pingCircle, directionalPingCircle, screenOverlay, screenBackground;
|
||||
private readonly Sprite sonarBlip;
|
||||
|
||||
private bool aiPingCheckPending;
|
||||
|
||||
@@ -87,16 +86,6 @@ namespace Barotrauma.Items.Components
|
||||
get { return zoom; }
|
||||
}
|
||||
|
||||
//TODO: remove, only for testing
|
||||
#if DEBUG
|
||||
[Serialize(false, false), Editable]
|
||||
public bool DynamicDockingIndicator
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
#endif
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
@@ -122,7 +111,29 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
connectedTransducers = new List<ConnectedTransducer>();
|
||||
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "pingcircle":
|
||||
pingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "directionalpingcircle":
|
||||
directionalPingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "screenoverlay":
|
||||
screenOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "screenbackground":
|
||||
screenBackground = new Sprite(subElement);
|
||||
break;
|
||||
case "blip":
|
||||
sonarBlip = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IsActive = false;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
@@ -194,7 +205,6 @@ namespace Barotrauma.Items.Components
|
||||
directionalPingCircle?.Remove();
|
||||
screenOverlay?.Remove();
|
||||
screenBackground?.Remove();
|
||||
lineSprite?.Remove();
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
@@ -249,24 +259,15 @@ namespace Barotrauma.Items.Components
|
||||
int clockDir = (int)Math.Round((angle / MathHelper.TwoPi) * 12);
|
||||
if (clockDir == 0) clockDir = 12;
|
||||
|
||||
return TextManager.Get("SubDirOClock").Replace("[dir]", clockDir.ToString());
|
||||
return TextManager.Get("roomname.subdiroclock").Replace("[dir]", clockDir.ToString());
|
||||
}
|
||||
|
||||
private Vector2 GetTransducerPos()
|
||||
private Vector2 GetTransducerCenter()
|
||||
{
|
||||
if (!UseTransducers || connectedTransducers.Count == 0)
|
||||
{
|
||||
//use the position of the sub if the item is static (no body) and inside a sub
|
||||
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
|
||||
}
|
||||
|
||||
if (!UseTransducers || connectedTransducers.Count == 0) return Vector2.Zero;
|
||||
Vector2 transducerPosSum = Vector2.Zero;
|
||||
foreach (ConnectedTransducer transducer in connectedTransducers)
|
||||
{
|
||||
if (transducer.Transducer.Item.Submarine != null)
|
||||
{
|
||||
return transducer.Transducer.Item.Submarine.WorldPosition;
|
||||
}
|
||||
transducerPosSum += transducer.Transducer.Item.WorldPosition;
|
||||
}
|
||||
return transducerPosSum / connectedTransducers.Count;
|
||||
|
||||
@@ -172,6 +172,19 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
sonar = item.GetComponent<Sonar>();
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (!CanBeSelected) return false;
|
||||
|
||||
user = character;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
networkUpdateTimer -= deltaTime;
|
||||
@@ -474,9 +487,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!posToMaintain.HasValue)
|
||||
{
|
||||
unsentChanges = true;
|
||||
posToMaintain = controlledSub != null ?
|
||||
controlledSub.WorldPosition :
|
||||
item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
|
||||
posToMaintain = controlledSub == null ? item.WorldPosition : controlledSub.WorldPosition;
|
||||
}
|
||||
|
||||
if (!AutoPilot || !MaintainPos) unsentChanges = true;
|
||||
|
||||
@@ -1149,6 +1149,10 @@ namespace Barotrauma
|
||||
{
|
||||
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
else if (Submarine != null && prevSub != null && Submarine != prevSub)
|
||||
{
|
||||
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
|
||||
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
|
||||
@@ -1265,7 +1269,7 @@ namespace Barotrauma
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
HashSet<Connection> alreadySearched = new HashSet<Connection>();
|
||||
List<Item> alreadySearched = new List<Item>() { this };
|
||||
GetConnectedComponentsRecursive(alreadySearched, connectedComponents);
|
||||
|
||||
return connectedComponents;
|
||||
@@ -1287,7 +1291,7 @@ namespace Barotrauma
|
||||
return connectedComponents;
|
||||
}
|
||||
|
||||
private void GetConnectedComponentsRecursive<T>(HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
private void GetConnectedComponentsRecursive<T>(List<Item> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
{
|
||||
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel == null) { return; }
|
||||
@@ -1332,14 +1336,28 @@ namespace Barotrauma
|
||||
new Pair<string, string>("signal_in2", "signal_out")
|
||||
};
|
||||
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
recipient.Item.GetConnectedComponentsRecursive<T>(alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
|
||||
{
|
||||
alreadySearched.Add(c);
|
||||
List<T> connectedComponents = new List<T>();
|
||||
List<Item> alreadySearched = new List<Item>() { this };
|
||||
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
|
||||
|
||||
return connectedComponents;
|
||||
}
|
||||
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, List<Item> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
{
|
||||
alreadySearched.Add(this);
|
||||
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (alreadySearched.Contains(recipient)) { continue; }
|
||||
if (alreadySearched.Contains(recipient.Item)) continue;
|
||||
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
if (component != null)
|
||||
@@ -1348,29 +1366,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents);
|
||||
}
|
||||
|
||||
foreach (Pair<string, string> connectionPair in connectionPairs)
|
||||
{
|
||||
if (connectionPair.First == c.Name)
|
||||
{
|
||||
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == connectionPair.Second);
|
||||
if (pairedConnection != null)
|
||||
{
|
||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
else if (connectionPair.Second == c.Name)
|
||||
{
|
||||
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == connectionPair.First);
|
||||
if (pairedConnection != null)
|
||||
{
|
||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -635,24 +635,6 @@ namespace Barotrauma
|
||||
var character = ((Limb)f2.Body.UserData).character;
|
||||
if (character.DisableImpactDamageTimer > 0.0f || ((Limb)f2.Body.UserData).Mass < 100.0f) return true;
|
||||
}
|
||||
|
||||
if (!Prefab.Platform && Prefab.StairDirection == Direction.None)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(f2.Body.Position);
|
||||
|
||||
int section = FindSectionIndex(pos);
|
||||
if (section > -1)
|
||||
{
|
||||
Vector2 normal = contact.Manifold.LocalNormal;
|
||||
|
||||
float impact = Vector2.Dot(f2.Body.LinearVelocity, -normal) * f2.Body.Mass * 0.1f;
|
||||
if (impact < 10.0f) return true;
|
||||
#if CLIENT
|
||||
SoundPlayer.PlayDamageSound("StructureBlunt", impact, SectionPosition(section, true), tags: Tags);
|
||||
#endif
|
||||
AddDamage(section, impact);
|
||||
}
|
||||
}
|
||||
|
||||
OnImpactProjSpecific(f1, f2, contact);
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ namespace Barotrauma
|
||||
{
|
||||
//prevent picking up (or deattaching) items
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession.GameMode is TutorialMode)
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ namespace Barotrauma
|
||||
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
targetPos = character.WorldPosition;
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 200.0f);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 500.0f);
|
||||
if (adjacentGap == null) return true;
|
||||
|
||||
if (newHull != null)
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace;
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, condition));
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace;
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, condition));
|
||||
|
||||
@@ -401,7 +401,7 @@ namespace Barotrauma.Networking
|
||||
public bool AllowRagdollButton
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
|
||||
@@ -795,18 +795,6 @@ namespace Barotrauma
|
||||
return new Vector2((float)x, (float)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a point in 2d space around the origin
|
||||
/// </summary>
|
||||
public static Vector2 RotatePoint(Vector2 point, float radians)
|
||||
{
|
||||
var sin = Math.Sin(radians);
|
||||
var cos = Math.Cos(radians);
|
||||
var x = (cos * point.X) - (sin * point.Y);
|
||||
var y = (sin * point.X) + (cos * point.Y);
|
||||
return new Vector2((float)x, (float)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corners of an imaginary rectangle.
|
||||
/// Unlike the XNA rectangle, this can be rotated with the up parameter.
|
||||
|
||||
Reference in New Issue
Block a user