Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Map/ISpatialEntity.cs
2024-12-11 13:26:13 +02:00

129 lines
5.9 KiB
C#

using System;
using Barotrauma.Items.Components;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
interface ISpatialEntity
{
Vector2 Position { get; }
Vector2 WorldPosition { get; }
Vector2 SimPosition { get; }
Submarine Submarine { get; }
public static bool IsTargetVisible(ISpatialEntity target, ISpatialEntity seeingEntity, bool seeThroughWindows = false, bool checkFacing = false)
{
if (seeingEntity is Character seeingCharacter)
{
return seeingCharacter.CanSeeTarget(target, seeThroughWindows: seeThroughWindows, checkFacing: checkFacing);
}
if (target is Character targetCharacter)
{
return IsCharacterVisible(targetCharacter, seeingEntity, seeThroughWindows, checkFacing);
}
else
{
return CheckVisibility(target, seeingEntity, seeThroughWindows, checkFacing);
}
}
public static bool IsCharacterVisible(Character target, ISpatialEntity seeingEntity, bool seeThroughWindows = false, bool checkFacing = false)
{
System.Diagnostics.Debug.Assert(target != null);
if (target == null || target.Removed) { return false; }
if (seeingEntity == null) { return false; }
if (CheckVisibility(target, seeingEntity, seeThroughWindows, checkFacing)) { return true; }
if (!target.AnimController.SimplePhysicsEnabled)
{
//find the limbs that are furthest from the target's position (from the viewer's point of view)
Limb leftExtremity = null, rightExtremity = null;
float leftMostDot = 0.0f, rightMostDot = 0.0f;
Vector2 dir = target.WorldPosition - seeingEntity.WorldPosition;
Vector2 leftDir = new Vector2(dir.Y, -dir.X);
Vector2 rightDir = new Vector2(-dir.Y, dir.X);
foreach (Limb limb in target.AnimController.Limbs)
{
if (limb.IsSevered || limb == target.AnimController.MainLimb) { continue; }
if (limb.Hidden) { continue; }
Vector2 limbDir = limb.WorldPosition - seeingEntity.WorldPosition;
float leftDot = Vector2.Dot(limbDir, leftDir);
if (leftDot > leftMostDot)
{
leftMostDot = leftDot;
leftExtremity = limb;
continue;
}
float rightDot = Vector2.Dot(limbDir, rightDir);
if (rightDot > rightMostDot)
{
rightMostDot = rightDot;
rightExtremity = limb;
}
}
if (leftExtremity != null && CheckVisibility(leftExtremity, seeingEntity, seeThroughWindows, checkFacing)) { return true; }
if (rightExtremity != null && CheckVisibility(rightExtremity, seeingEntity, seeThroughWindows, checkFacing)) { return true; }
}
return false;
}
public static bool CheckVisibility(ISpatialEntity target, ISpatialEntity seeingEntity, bool seeThroughWindows = true, bool checkFacing = false)
{
System.Diagnostics.Debug.Assert(target != null);
if (target == null) { return false; }
if (seeingEntity == null) { return false; }
// TODO: Could we just use the method below? If not, let's refactor it so that we can.
Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - seeingEntity.WorldPosition);
if (checkFacing && seeingEntity is Character seeingCharacter)
{
if (Math.Sign(diff.X) != seeingCharacter.AnimController.Dir) { return false; }
}
//both inside the same sub (or both outside)
//OR the we're inside, the other character outside
if (target.Submarine == seeingEntity.Submarine || target.Submarine == null)
{
return Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff, blocksVisibilityPredicate: IsBlocking) == null;
}
//we're outside, the other character inside
else if (seeingEntity.Submarine == null)
{
return Submarine.CheckVisibility(target.SimPosition, target.SimPosition - diff, blocksVisibilityPredicate: IsBlocking) == null;
}
//both inside different subs
else
{
return
Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff, blocksVisibilityPredicate: IsBlocking) == null &&
Submarine.CheckVisibility(target.SimPosition, target.SimPosition - diff, blocksVisibilityPredicate: IsBlocking) == null;
}
bool IsBlocking(Fixture f)
{
var body = f.Body;
if (body == null) { return false; }
if (body.UserData is Structure wall)
{
if (!wall.CastShadow && seeThroughWindows) { return false; }
return wall != target;
}
else if (body.UserData is Item item)
{
if (item.GetComponent<Door>() is { HasWindow: true } door && seeThroughWindows)
{
if (door.IsPositionOnWindow(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition))) { return false; }
}
return item != target;
}
return true;
}
}
}
interface IIgnorable : ISpatialEntity
{
bool IgnoreByAI(Character character);
bool OrderedToBeIgnored { get; set; }
}
}