2024-03-29 20:59:16 -07:00
using Content.Shared.Chemistry.EntitySystems ;
using Content.Shared.Chemistry.Components ;
using Content.Shared.Chemistry.Components.SolutionManager ;
2024-06-30 05:43:43 +02:00
using Content.Shared.Chemistry ;
2024-03-29 20:59:16 -07:00
using Content.Shared.Database ;
using Content.Shared.FixedPoint ;
using Content.Shared.Forensics ;
using Content.Shared.IdentityManagement ;
using Content.Shared.Interaction ;
using Content.Shared.Interaction.Events ;
using Content.Shared.Mobs.Components ;
using Content.Shared.Timing ;
using Content.Shared.Weapons.Melee.Events ;
using Content.Server.Interaction ;
using Content.Server.Body.Components ;
using Robust.Shared.GameStates ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using Robust.Server.Audio ;
namespace Content.Server.Chemistry.EntitySystems ;
public sealed class HypospraySystem : SharedHypospraySystem
{
[Dependency] private readonly AudioSystem _audio = default ! ;
[Dependency] private readonly InteractionSystem _interaction = default ! ;
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < HyposprayComponent , AfterInteractEvent > ( OnAfterInteract ) ;
SubscribeLocalEvent < HyposprayComponent , MeleeHitEvent > ( OnAttack ) ;
SubscribeLocalEvent < HyposprayComponent , UseInHandEvent > ( OnUseInHand ) ;
}
2024-05-30 00:18:07 -04:00
private bool TryUseHypospray ( Entity < HyposprayComponent > entity , EntityUid target , EntityUid user )
2024-03-29 20:59:16 -07:00
{
// if target is ineligible but is a container, try to draw from the container
if ( ! EligibleEntity ( target , EntityManager , entity )
& & _solutionContainers . TryGetDrawableSolution ( target , out var drawableSolution , out _ ) )
{
2024-05-30 00:18:07 -04:00
return TryDraw ( entity , target , drawableSolution . Value , user ) ;
2024-03-29 20:59:16 -07:00
}
2024-05-30 00:18:07 -04:00
return TryDoInject ( entity , target , user ) ;
2024-03-29 20:59:16 -07:00
}
private void OnUseInHand ( Entity < HyposprayComponent > entity , ref UseInHandEvent args )
{
if ( args . Handled )
return ;
2024-05-30 00:18:07 -04:00
args . Handled = TryDoInject ( entity , args . User , args . User ) ;
2024-03-29 20:59:16 -07:00
}
public void OnAfterInteract ( Entity < HyposprayComponent > entity , ref AfterInteractEvent args )
{
if ( args . Handled | | ! args . CanReach | | args . Target = = null )
return ;
2024-05-30 00:18:07 -04:00
args . Handled = TryUseHypospray ( entity , args . Target . Value , args . User ) ;
2024-03-29 20:59:16 -07:00
}
public void OnAttack ( Entity < HyposprayComponent > entity , ref MeleeHitEvent args )
{
if ( ! args . HitEntities . Any ( ) )
return ;
TryDoInject ( entity , args . HitEntities . First ( ) , args . User ) ;
}
public bool TryDoInject ( Entity < HyposprayComponent > entity , EntityUid target , EntityUid user )
{
var ( uid , component ) = entity ;
if ( ! EligibleEntity ( target , EntityManager , component ) )
return false ;
if ( TryComp ( uid , out UseDelayComponent ? delayComp ) )
{
if ( _useDelay . IsDelayed ( ( uid , delayComp ) ) )
return false ;
}
string? msgFormat = null ;
if ( target = = user )
msgFormat = "hypospray-component-inject-self-message" ;
else if ( EligibleEntity ( user , EntityManager , component ) & & _interaction . TryRollClumsy ( user , component . ClumsyFailChance ) )
{
msgFormat = "hypospray-component-inject-self-clumsy-message" ;
target = user ;
}
if ( ! _solutionContainers . TryGetSolution ( uid , component . SolutionName , out var hypoSpraySoln , out var hypoSpraySolution ) | | hypoSpraySolution . Volume = = 0 )
{
_popup . PopupEntity ( Loc . GetString ( "hypospray-component-empty-message" ) , target , user ) ;
return true ;
}
if ( ! _solutionContainers . TryGetInjectableSolution ( target , out var targetSoln , out var targetSolution ) )
{
_popup . PopupEntity ( Loc . GetString ( "hypospray-cant-inject" , ( "target" , Identity . Entity ( target , EntityManager ) ) ) , target , user ) ;
return false ;
}
_popup . PopupEntity ( Loc . GetString ( msgFormat ? ? "hypospray-component-inject-other-message" , ( "other" , target ) ) , target , user ) ;
if ( target ! = user )
{
_popup . PopupEntity ( Loc . GetString ( "hypospray-component-feel-prick-message" ) , target , target ) ;
// TODO: This should just be using melee attacks...
// meleeSys.SendLunge(angle, user);
}
_audio . PlayPvs ( component . InjectSound , user ) ;
// Medipens and such use this system and don't have a delay, requiring extra checks
// BeginDelay function returns if item is already on delay
if ( delayComp ! = null )
_useDelay . TryResetDelay ( ( uid , delayComp ) ) ;
// Get transfer amount. May be smaller than component.TransferAmount if not enough room
var realTransferAmount = FixedPoint2 . Min ( component . TransferAmount , targetSolution . AvailableVolume ) ;
if ( realTransferAmount < = 0 )
{
_popup . PopupEntity ( Loc . GetString ( "hypospray-component-transfer-already-full-message" , ( "owner" , target ) ) , target , user ) ;
return true ;
}
// Move units from attackSolution to targetSolution
var removedSolution = _solutionContainers . SplitSolution ( hypoSpraySoln . Value , realTransferAmount ) ;
if ( ! targetSolution . CanAddSolution ( removedSolution ) )
return true ;
_reactiveSystem . DoEntityReaction ( target , removedSolution , ReactionMethod . Injection ) ;
_solutionContainers . TryAddSolution ( targetSoln . Value , removedSolution ) ;
var ev = new TransferDnaEvent { Donor = target , Recipient = uid } ;
RaiseLocalEvent ( target , ref ev ) ;
// same LogType as syringes...
2024-09-02 06:26:04 -05:00
_adminLogger . Add ( LogType . ForceFeed , $"{EntityManager.ToPrettyString(user):user} injected {EntityManager.ToPrettyString(target):target} with a solution {SharedSolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {EntityManager.ToPrettyString(uid):using}" ) ;
2024-03-29 20:59:16 -07:00
return true ;
}
2024-05-30 00:18:07 -04:00
private bool TryDraw ( Entity < HyposprayComponent > entity , Entity < BloodstreamComponent ? > target , Entity < SolutionComponent > targetSolution , EntityUid user )
2024-03-29 20:59:16 -07:00
{
if ( ! _solutionContainers . TryGetSolution ( entity . Owner , entity . Comp . SolutionName , out var soln ,
out var solution ) | | solution . AvailableVolume = = 0 )
{
2024-05-30 00:18:07 -04:00
return false ;
2024-03-29 20:59:16 -07:00
}
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
var realTransferAmount = FixedPoint2 . Min ( entity . Comp . TransferAmount , targetSolution . Comp . Solution . Volume ,
solution . AvailableVolume ) ;
if ( realTransferAmount < = 0 )
{
_popup . PopupEntity (
Loc . GetString ( "injector-component-target-is-empty-message" ,
( "target" , Identity . Entity ( target , EntityManager ) ) ) ,
entity . Owner , user ) ;
2024-05-30 00:18:07 -04:00
return false ;
2024-03-29 20:59:16 -07:00
}
var removedSolution = _solutionContainers . Draw ( target . Owner , targetSolution , realTransferAmount ) ;
if ( ! _solutionContainers . TryAddSolution ( soln . Value , removedSolution ) )
{
2024-05-30 00:18:07 -04:00
return false ;
2024-03-29 20:59:16 -07:00
}
_popup . PopupEntity ( Loc . GetString ( "injector-component-draw-success-message" ,
( "amount" , removedSolution . Volume ) ,
( "target" , Identity . Entity ( target , EntityManager ) ) ) , entity . Owner , user ) ;
2024-05-30 00:18:07 -04:00
return true ;
2024-03-29 20:59:16 -07:00
}
private bool EligibleEntity ( EntityUid entity , IEntityManager entMan , HyposprayComponent component )
{
// TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag?
// In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else.
// But this is 14, we dont do what SS13 does just because SS13 does it.
return component . OnlyAffectsMobs
? entMan . HasComponent < SolutionContainerManagerComponent > ( entity ) & &
entMan . HasComponent < MobStateComponent > ( entity )
: entMan . HasComponent < SolutionContainerManagerComponent > ( entity ) ;
}
}