2020-10-29 09:55:27 +01:00
#nullable enable
using System.Linq ;
2020-09-24 18:18:50 +02:00
using Content.Shared.GameObjects.Components.Mobs ;
2020-10-11 13:13:45 +02:00
using Content.Shared.Interfaces.GameObjects.Components ;
2020-08-30 11:37:06 +02:00
using Content.Shared.Utility ;
2019-07-19 10:45:04 +02:00
using JetBrains.Annotations ;
2020-08-15 20:38:37 +02:00
using Robust.Shared.Containers ;
2020-10-28 13:04:29 +01:00
using Robust.Shared.GameObjects ;
2020-09-24 18:18:50 +02:00
using Robust.Shared.Map ;
2020-08-01 17:37:12 +02:00
using Robust.Shared.Maths ;
using Robust.Shared.Utility ;
2020-08-30 11:37:06 +02:00
using static Content . Shared . GameObjects . EntitySystems . SharedInteractionSystem ;
2019-07-19 10:45:04 +02:00
namespace Content.Shared.GameObjects.EntitySystems
{
2020-08-01 17:37:12 +02:00
public interface IExamine
{
/// <summary>
/// Returns a status examine value for components appended to the end of the description of the entity
/// </summary>
/// <param name="message">The message to append to which will be displayed.</param>
/// <param name="inDetailsRange">Whether the examiner is within the 'Details' range, allowing you to show information logically only availabe when close to the examined entity.</param>
void Examine ( FormattedMessage message , bool inDetailsRange ) ;
}
2020-10-10 15:25:13 +02:00
2019-07-19 10:45:04 +02:00
public abstract class ExamineSystemShared : EntitySystem
{
2020-04-25 11:37:59 +02:00
public const float ExamineRange = 16f ;
2019-07-19 10:45:04 +02:00
public const float ExamineRangeSquared = ExamineRange * ExamineRange ;
2020-08-01 17:37:12 +02:00
protected const float ExamineDetailsRange = 3f ;
2019-07-19 10:45:04 +02:00
2020-08-15 20:38:37 +02:00
private static bool IsInDetailsRange ( IEntity examiner , IEntity entity )
{
2020-08-30 11:37:06 +02:00
return examiner . InRangeUnobstructed ( entity , ExamineDetailsRange , ignoreInsideBlocker : true ) & &
examiner . IsInSameOrNoContainer ( entity ) ;
2020-08-15 20:38:37 +02:00
}
2019-07-19 10:45:04 +02:00
[Pure]
protected static bool CanExamine ( IEntity examiner , IEntity examined )
{
2020-10-29 09:55:27 +01:00
if ( ! examiner . TryGetComponent ( out ExaminerComponent ? examinerComponent ) )
2019-07-19 10:45:04 +02:00
{
return false ;
}
if ( ! examinerComponent . DoRangeCheck )
{
return true ;
}
if ( examiner . Transform . MapID ! = examined . Transform . MapID )
{
return false ;
}
2020-08-30 11:37:06 +02:00
Ignored predicate = entity = > entity = = examiner | | entity = = examined ;
2020-08-15 20:38:37 +02:00
2020-11-13 20:25:04 +13:00
if ( examiner . TryGetContainer ( out var container ) )
2020-08-15 20:38:37 +02:00
{
predicate + = entity = > entity = = container . Owner ;
}
2020-09-24 18:18:50 +02:00
return InRangeUnOccluded (
examiner . Transform . MapPosition ,
examined . Transform . MapPosition ,
ExamineRange ,
predicate : predicate ,
ignoreInsideBlocker : true ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( MapCoordinates origin , MapCoordinates other , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-09-24 18:18:50 +02:00
{
var occluderSystem = Get < OccluderSystem > ( ) ;
if ( ! origin . InRange ( other , range ) ) return false ;
var dir = other . Position - origin . Position ;
if ( dir . LengthSquared . Equals ( 0f ) ) return true ;
if ( range > 0f & & ! ( dir . LengthSquared < = range * range ) ) return false ;
predicate ? ? = _ = > false ;
var ray = new Ray ( origin . Position , dir . Normalized ) ;
var rayResults = occluderSystem
. IntersectRayWithPredicate ( origin . MapId , ray , dir . Length , predicate . Invoke , false ) . ToList ( ) ;
if ( rayResults . Count = = 0 ) return true ;
if ( ! ignoreInsideBlocker ) return false ;
2020-10-28 13:04:29 +01:00
foreach ( var result in rayResults )
{
2020-10-29 09:55:27 +01:00
if ( ! result . HitEntity . TryGetComponent ( out OccluderComponent ? o ) )
2020-10-28 13:04:29 +01:00
{
continue ;
}
var bBox = o . BoundingBox . Translated ( o . Owner . Transform . WorldPosition ) ;
if ( bBox . Contains ( origin . Position ) | | bBox . Contains ( other . Position ) )
{
continue ;
}
return false ;
}
2020-09-24 18:18:50 +02:00
2020-10-28 13:04:29 +01:00
return true ;
2019-07-19 10:45:04 +02:00
}
2020-08-01 17:37:12 +02:00
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( IEntity origin , IEntity other , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = origin . Transform . MapPosition ;
var otherPos = other . Transform . MapPosition ;
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( IEntity origin , IComponent other , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = origin . Transform . MapPosition ;
var otherPos = other . Owner . Transform . MapPosition ;
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( IEntity origin , EntityCoordinates other , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = origin . Transform . MapPosition ;
var otherPos = other . ToMap ( origin . EntityManager ) ;
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( IEntity origin , MapCoordinates other , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = origin . Transform . MapPosition ;
return InRangeUnOccluded ( originPos , other , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( ITargetedInteractEventArgs args , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = args . User . Transform . MapPosition ;
var otherPos = args . Target . Transform . MapPosition ;
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( DragDropEventArgs args , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = args . User . Transform . MapPosition ;
var otherPos = args . DropLocation . ToMap ( args . User . EntityManager ) ;
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-10-29 09:55:27 +01:00
public static bool InRangeUnOccluded ( AfterInteractEventArgs args , float range , Ignored ? predicate , bool ignoreInsideBlocker = true )
2020-10-11 13:13:45 +02:00
{
var originPos = args . User . Transform . MapPosition ;
2021-01-11 09:36:21 +01:00
var otherPos = args . Target ? . Transform . MapPosition ? ? args . ClickLocation . ToMap ( args . User . EntityManager ) ;
2020-10-11 13:13:45 +02:00
return InRangeUnOccluded ( originPos , otherPos , range , predicate , ignoreInsideBlocker ) ;
}
2020-08-01 17:37:12 +02:00
public static FormattedMessage GetExamineText ( IEntity entity , IEntity examiner )
{
var message = new FormattedMessage ( ) ;
var doNewline = false ;
//Add an entity description if one is declared
if ( ! string . IsNullOrEmpty ( entity . Description ) )
{
message . AddText ( entity . Description ) ;
doNewline = true ;
}
message . PushColor ( Color . DarkGray ) ;
//Add component statuses from components that report one
foreach ( var examineComponent in entity . GetAllComponents < IExamine > ( ) )
{
var subMessage = new FormattedMessage ( ) ;
2020-08-15 20:38:37 +02:00
examineComponent . Examine ( subMessage , IsInDetailsRange ( examiner , entity ) ) ;
2020-08-01 17:37:12 +02:00
if ( subMessage . Tags . Count = = 0 )
continue ;
if ( doNewline )
message . AddText ( "\n" ) ;
message . AddMessage ( subMessage ) ;
doNewline = true ;
}
message . Pop ( ) ;
return message ;
}
2019-07-19 10:45:04 +02:00
}
}