2022-09-16 19:49:05 -04:00
using System.Diagnostics.CodeAnalysis ;
2022-07-06 23:44:31 -04:00
using System.Linq ;
2022-12-27 11:01:36 -06:00
using Content.Server.Administration.Logs ;
2023-12-30 11:18:58 -05:00
using Content.Server.Atmos.EntitySystems ;
2024-08-01 00:15:05 -04:00
using Content.Server.Fluids.EntitySystems ;
2022-11-03 02:41:12 +01:00
using Content.Server.Lathe.Components ;
2022-09-16 19:49:05 -04:00
using Content.Server.Materials ;
2024-08-01 00:15:05 -04:00
using Content.Server.Popups ;
2022-08-11 21:33:32 -04:00
using Content.Server.Power.Components ;
2022-09-16 19:49:05 -04:00
using Content.Server.Power.EntitySystems ;
2025-04-16 15:29:25 -04:00
using Content.Server.Radio.EntitySystems ;
2023-01-19 23:06:02 -05:00
using Content.Server.Stack ;
2024-04-30 14:31:05 -07:00
using Content.Shared.Atmos ;
2024-08-01 00:15:05 -04:00
using Content.Shared.Chemistry.Components ;
using Content.Shared.Chemistry.EntitySystems ;
using Content.Shared.Chemistry.Reagent ;
2024-02-01 11:45:24 +03:00
using Content.Shared.UserInterface ;
2022-12-27 11:01:36 -06:00
using Content.Shared.Database ;
2023-08-07 17:21:04 +03:00
using Content.Shared.Emag.Components ;
2025-01-30 05:05:47 +01:00
using Content.Shared.Emag.Systems ;
2024-08-01 00:15:05 -04:00
using Content.Shared.Examine ;
2022-11-03 02:41:12 +01:00
using Content.Shared.Lathe ;
2025-02-07 18:22:49 +00:00
using Content.Shared.Lathe.Prototypes ;
2025-04-16 15:29:25 -04:00
using Content.Shared.Localizations ;
2022-11-03 02:41:12 +01:00
using Content.Shared.Materials ;
2024-08-25 22:18:42 +10:00
using Content.Shared.Power ;
2024-05-07 18:20:43 +00:00
using Content.Shared.ReagentSpeed ;
2022-11-03 02:41:12 +01:00
using Content.Shared.Research.Components ;
using Content.Shared.Research.Prototypes ;
using JetBrains.Annotations ;
2024-08-01 00:15:05 -04:00
using Robust.Server.Containers ;
2022-11-03 02:41:12 +01:00
using Robust.Server.GameObjects ;
2023-11-27 22:12:34 +11:00
using Robust.Shared.Audio.Systems ;
2022-11-03 02:41:12 +01:00
using Robust.Shared.Prototypes ;
2022-10-30 03:12:11 -04:00
using Robust.Shared.Timing ;
2019-04-26 15:51:05 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Lathe
2019-04-26 15:51:05 +02:00
{
2020-08-13 22:17:12 +10:00
[UsedImplicitly]
2022-09-16 19:49:05 -04:00
public sealed class LatheSystem : SharedLatheSystem
2019-04-26 15:51:05 +02:00
{
2022-10-30 03:12:11 -04:00
[Dependency] private readonly IGameTiming _timing = default ! ;
2022-09-16 19:49:05 -04:00
[Dependency] private readonly IPrototypeManager _proto = default ! ;
2022-12-27 11:01:36 -06:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2023-12-30 11:18:58 -05:00
[Dependency] private readonly AtmosphereSystem _atmosphere = default ! ;
2022-09-16 19:49:05 -04:00
[Dependency] private readonly SharedAppearanceSystem _appearance = default ! ;
[Dependency] private readonly SharedAudioSystem _audio = default ! ;
2024-08-01 00:15:05 -04:00
[Dependency] private readonly ContainerSystem _container = default ! ;
2025-01-30 05:05:47 +01:00
[Dependency] private readonly EmagSystem _emag = default ! ;
2022-08-04 12:38:56 +12:00
[Dependency] private readonly UserInterfaceSystem _uiSys = default ! ;
2022-09-16 19:49:05 -04:00
[Dependency] private readonly MaterialStorageSystem _materialStorage = default ! ;
2024-08-01 00:15:05 -04:00
[Dependency] private readonly PopupSystem _popup = default ! ;
[Dependency] private readonly PuddleSystem _puddle = default ! ;
2024-05-07 18:20:43 +00:00
[Dependency] private readonly ReagentSpeedSystem _reagentSpeed = default ! ;
2024-08-01 00:15:05 -04:00
[Dependency] private readonly SharedSolutionContainerSystem _solution = default ! ;
2023-01-19 23:06:02 -05:00
[Dependency] private readonly StackSystem _stack = default ! ;
2023-12-30 11:18:58 -05:00
[Dependency] private readonly TransformSystem _transform = default ! ;
2025-04-16 15:29:25 -04:00
[Dependency] private readonly RadioSystem _radio = default ! ;
2023-12-30 11:18:58 -05:00
/// <summary>
/// Per-tick cache
/// </summary>
private readonly List < GasMixture > _environments = new ( ) ;
2023-08-17 12:22:01 -05:00
2022-04-17 03:34:14 -04:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-09-16 19:49:05 -04:00
SubscribeLocalEvent < LatheComponent , GetMaterialWhitelistEvent > ( OnGetWhitelist ) ;
SubscribeLocalEvent < LatheComponent , MapInitEvent > ( OnMapInit ) ;
SubscribeLocalEvent < LatheComponent , PowerChangedEvent > ( OnPowerChanged ) ;
2022-12-25 16:22:23 -05:00
SubscribeLocalEvent < LatheComponent , TechnologyDatabaseModifiedEvent > ( OnDatabaseModified ) ;
2025-04-16 15:29:25 -04:00
SubscribeLocalEvent < LatheAnnouncingComponent , TechnologyDatabaseModifiedEvent > ( OnTechnologyDatabaseModified ) ;
2023-06-20 02:39:35 -04:00
SubscribeLocalEvent < LatheComponent , ResearchRegistrationChangedEvent > ( OnResearchRegistrationChanged ) ;
2022-09-16 19:49:05 -04:00
2022-08-04 12:38:56 +12:00
SubscribeLocalEvent < LatheComponent , LatheQueueRecipeMessage > ( OnLatheQueueRecipeMessage ) ;
SubscribeLocalEvent < LatheComponent , LatheSyncRequestMessage > ( OnLatheSyncRequestMessage ) ;
2025-08-24 11:02:47 -04:00
SubscribeLocalEvent < LatheComponent , LatheDeleteRequestMessage > ( OnLatheDeleteRequestMessage ) ;
SubscribeLocalEvent < LatheComponent , LatheMoveRequestMessage > ( OnLatheMoveRequestMessage ) ;
SubscribeLocalEvent < LatheComponent , LatheAbortFabricationMessage > ( OnLatheAbortFabricationMessage ) ;
2022-09-16 19:49:05 -04:00
2023-07-08 09:02:17 -07:00
SubscribeLocalEvent < LatheComponent , BeforeActivatableUIOpenEvent > ( ( u , c , _ ) = > UpdateUserInterfaceState ( u , c ) ) ;
2023-01-07 21:36:50 -05:00
SubscribeLocalEvent < LatheComponent , MaterialAmountChangedEvent > ( OnMaterialAmountChanged ) ;
2022-09-16 19:49:05 -04:00
SubscribeLocalEvent < TechnologyDatabaseComponent , LatheGetRecipesEvent > ( OnGetRecipes ) ;
2023-08-07 17:21:04 +03:00
SubscribeLocalEvent < EmagLatheRecipesComponent , LatheGetRecipesEvent > ( GetEmagLatheRecipes ) ;
2023-12-30 11:18:58 -05:00
SubscribeLocalEvent < LatheHeatProducingComponent , LatheStartPrintingEvent > ( OnHeatStartPrinting ) ;
2022-04-17 03:34:14 -04:00
}
2019-04-26 15:51:05 +02:00
public override void Update ( float frameTime )
{
2023-06-18 18:35:19 -04:00
var query = EntityQueryEnumerator < LatheProducingComponent , LatheComponent > ( ) ;
2023-07-08 09:02:17 -07:00
while ( query . MoveNext ( out var uid , out var comp , out var lathe ) )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
if ( lathe . CurrentRecipe = = null )
continue ;
2023-07-08 09:02:17 -07:00
if ( _timing . CurTime - comp . StartTime > = comp . ProductionLength )
2023-06-18 18:35:19 -04:00
FinishProducing ( uid , lathe ) ;
2022-04-17 03:34:14 -04:00
}
2023-12-30 11:18:58 -05:00
var heatQuery = EntityQueryEnumerator < LatheHeatProducingComponent , LatheProducingComponent , TransformComponent > ( ) ;
while ( heatQuery . MoveNext ( out var uid , out var heatComp , out _ , out var xform ) )
{
if ( _timing . CurTime < heatComp . NextSecond )
continue ;
heatComp . NextSecond + = TimeSpan . FromSeconds ( 1 ) ;
2023-12-31 01:08:33 +00:00
var position = _transform . GetGridTilePositionOrDefault ( ( uid , xform ) ) ;
2023-12-30 11:18:58 -05:00
_environments . Clear ( ) ;
if ( _atmosphere . GetTileMixture ( xform . GridUid , xform . MapUid , position , true ) is { } tileMix )
_environments . Add ( tileMix ) ;
if ( xform . GridUid ! = null )
{
2024-03-28 15:22:19 +13:00
var enumerator = _atmosphere . GetAdjacentTileMixtures ( xform . GridUid . Value , position , false , true ) ;
while ( enumerator . MoveNext ( out var mix ) )
{
_environments . Add ( mix ) ;
}
2023-12-30 11:18:58 -05:00
}
if ( _environments . Count > 0 )
{
var heatPerTile = heatComp . EnergyPerSecond / _environments . Count ;
foreach ( var env in _environments )
{
_atmosphere . AddHeat ( env , heatPerTile ) ;
}
}
}
2022-04-17 03:34:14 -04:00
}
2023-01-07 21:36:50 -05:00
private void OnGetWhitelist ( EntityUid uid , LatheComponent component , ref GetMaterialWhitelistEvent args )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
if ( args . Storage ! = uid )
2022-04-17 03:34:14 -04:00
return ;
2023-09-28 16:20:29 -07:00
var materialWhitelist = new List < ProtoId < MaterialPrototype > > ( ) ;
2024-01-02 11:11:13 +04:00
var recipes = GetAvailableRecipes ( uid , component , true ) ;
2022-09-16 19:49:05 -04:00
foreach ( var id in recipes )
2022-08-04 12:38:56 +12:00
{
2025-09-09 18:17:56 +02:00
if ( ! _proto . Resolve ( id , out var proto ) )
2022-09-16 19:49:05 -04:00
continue ;
2024-08-01 00:15:05 -04:00
foreach ( var ( mat , _ ) in proto . Materials )
2022-08-04 12:38:56 +12:00
{
2022-09-16 19:49:05 -04:00
if ( ! materialWhitelist . Contains ( mat ) )
{
materialWhitelist . Add ( mat ) ;
}
2022-08-04 12:38:56 +12:00
}
}
2022-09-16 19:49:05 -04:00
var combined = args . Whitelist . Union ( materialWhitelist ) . ToList ( ) ;
args . Whitelist = combined ;
2022-04-17 03:34:14 -04:00
}
2022-09-16 19:49:05 -04:00
[PublicAPI]
2024-01-02 11:11:13 +04:00
public bool TryGetAvailableRecipes ( EntityUid uid , [ NotNullWhen ( true ) ] out List < ProtoId < LatheRecipePrototype > > ? recipes , [ NotNullWhen ( true ) ] LatheComponent ? component = null , bool getUnavailable = false )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
recipes = null ;
if ( ! Resolve ( uid , ref component ) )
return false ;
2024-01-02 11:11:13 +04:00
recipes = GetAvailableRecipes ( uid , component , getUnavailable ) ;
2022-09-16 19:49:05 -04:00
return true ;
}
2022-08-04 12:38:56 +12:00
2024-01-02 11:11:13 +04:00
public List < ProtoId < LatheRecipePrototype > > GetAvailableRecipes ( EntityUid uid , LatheComponent component , bool getUnavailable = false )
2022-09-16 19:49:05 -04:00
{
2025-04-17 21:07:51 +10:00
var ev = new LatheGetRecipesEvent ( ( uid , component ) , getUnavailable ) ;
AddRecipesFromPacks ( ev . Recipes , component . StaticPacks ) ;
2023-01-19 23:06:02 -05:00
RaiseLocalEvent ( uid , ev ) ;
2024-08-25 08:06:50 -04:00
return ev . Recipes . ToList ( ) ;
2022-04-17 03:34:14 -04:00
}
2025-08-24 11:02:47 -04:00
public bool TryAddToQueue ( EntityUid uid , LatheRecipePrototype recipe , int quantity , LatheComponent ? component = null )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
if ( ! Resolve ( uid , ref component ) )
2022-08-04 12:38:56 +12:00
return false ;
2022-04-17 03:34:14 -04:00
2025-08-24 11:02:47 -04:00
if ( quantity < = 0 )
return false ;
quantity = int . Min ( quantity , MaxItemsPerRequest ) ;
if ( ! CanProduce ( uid , recipe , quantity , component ) )
2022-08-04 12:38:56 +12:00
return false ;
2024-08-01 00:15:05 -04:00
foreach ( var ( mat , amount ) in recipe . Materials )
2022-05-03 22:43:25 -04:00
{
2022-11-01 19:05:00 -04:00
var adjustedAmount = recipe . ApplyMaterialDiscount
2025-08-24 11:02:47 -04:00
? ( int ) ( - amount * component . MaterialUseMultiplier )
2022-11-01 19:05:00 -04:00
: - amount ;
2025-08-24 11:02:47 -04:00
adjustedAmount * = quantity ;
2022-11-01 19:05:00 -04:00
_materialStorage . TryChangeMaterialAmount ( uid , mat , adjustedAmount ) ;
2022-08-05 11:19:31 +12:00
}
2025-08-24 11:02:47 -04:00
if ( component . Queue . Last is { } node & & node . ValueRef . Recipe = = recipe . ID )
node . ValueRef . ItemsRequested + = quantity ;
else
component . Queue . AddLast ( new LatheRecipeBatch ( recipe . ID , 0 , quantity ) ) ;
2022-08-04 12:38:56 +12:00
2022-09-16 19:49:05 -04:00
return true ;
}
2022-04-17 03:34:14 -04:00
2022-09-16 19:49:05 -04:00
public bool TryStartProducing ( EntityUid uid , LatheComponent ? component = null )
{
if ( ! Resolve ( uid , ref component ) )
return false ;
if ( component . CurrentRecipe ! = null | | component . Queue . Count < = 0 | | ! this . IsPowered ( uid , EntityManager ) )
2022-08-04 12:38:56 +12:00
return false ;
2022-04-17 03:34:14 -04:00
2025-08-24 11:02:47 -04:00
var batch = component . Queue . First ( ) ;
batch . ItemsPrinted + + ;
if ( batch . ItemsPrinted > = batch . ItemsRequested | | batch . ItemsPrinted < 0 ) // Rollover sanity check
component . Queue . RemoveFirst ( ) ;
var recipe = _proto . Index ( batch . Recipe ) ;
2022-08-04 12:38:56 +12:00
2024-08-17 01:12:55 -04:00
var time = _reagentSpeed . ApplySpeed ( uid , recipe . CompleteTime ) * component . TimeMultiplier ;
2024-05-07 18:20:43 +00:00
2022-10-30 03:12:11 -04:00
var lathe = EnsureComp < LatheProducingComponent > ( uid ) ;
lathe . StartTime = _timing . CurTime ;
2024-08-17 01:12:55 -04:00
lathe . ProductionLength = time ;
2022-09-16 19:49:05 -04:00
component . CurrentRecipe = recipe ;
2022-08-04 12:38:56 +12:00
2023-12-30 11:18:58 -05:00
var ev = new LatheStartPrintingEvent ( recipe ) ;
RaiseLocalEvent ( uid , ref ev ) ;
2023-01-19 23:06:02 -05:00
_audio . PlayPvs ( component . ProducingSound , uid ) ;
2022-08-04 12:38:56 +12:00
UpdateRunningAppearance ( uid , true ) ;
2022-09-16 19:49:05 -04:00
UpdateUserInterfaceState ( uid , component ) ;
2024-08-17 01:12:55 -04:00
if ( time = = TimeSpan . Zero )
{
FinishProducing ( uid , component , lathe ) ;
}
2022-08-04 12:38:56 +12:00
return true ;
2022-04-17 03:34:14 -04:00
}
2022-09-16 19:49:05 -04:00
public void FinishProducing ( EntityUid uid , LatheComponent ? comp = null , LatheProducingComponent ? prodComp = null )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
if ( ! Resolve ( uid , ref comp , ref prodComp , false ) )
return ;
if ( comp . CurrentRecipe ! = null )
2023-01-19 23:06:02 -05:00
{
2025-06-26 18:08:01 -04:00
var currentRecipe = _proto . Index ( comp . CurrentRecipe . Value ) ;
if ( currentRecipe . Result is { } resultProto )
2024-08-01 00:15:05 -04:00
{
var result = Spawn ( resultProto , Transform ( uid ) . Coordinates ) ;
_stack . TryMergeToContacts ( result ) ;
}
2025-06-26 18:08:01 -04:00
if ( currentRecipe . ResultReagents is { } resultReagents & &
2024-08-01 00:15:05 -04:00
comp . ReagentOutputSlotId is { } slotId )
{
var toAdd = new Solution (
resultReagents . Select ( p = > new ReagentQuantity ( p . Key . Id , p . Value , null ) ) ) ;
// dispense it in the container if we have it and dump it if we don't
if ( _container . TryGetContainer ( uid , slotId , out var container ) & &
container . ContainedEntities . Count = = 1 & &
_solution . TryGetFitsInDispenser ( container . ContainedEntities . First ( ) , out var solution , out _ ) )
{
_solution . AddSolution ( solution . Value , toAdd ) ;
}
else
{
_popup . PopupEntity ( Loc . GetString ( "lathe-reagent-dispense-no-container" , ( "name" , uid ) ) , uid ) ;
_puddle . TrySpillAt ( uid , toAdd , out _ ) ;
}
}
2023-01-19 23:06:02 -05:00
}
2022-09-16 19:49:05 -04:00
comp . CurrentRecipe = null ;
2022-10-30 03:12:11 -04:00
prodComp . StartTime = _timing . CurTime ;
2022-09-16 19:49:05 -04:00
if ( ! TryStartProducing ( uid , comp ) )
2022-04-17 03:34:14 -04:00
{
2023-01-19 23:06:02 -05:00
RemCompDeferred ( uid , prodComp ) ;
2022-09-16 19:49:05 -04:00
UpdateUserInterfaceState ( uid , comp ) ;
2022-08-04 12:38:56 +12:00
UpdateRunningAppearance ( uid , false ) ;
2022-04-17 03:34:14 -04:00
}
2022-09-16 19:49:05 -04:00
}
2022-08-04 12:38:56 +12:00
2022-09-16 19:49:05 -04:00
public void UpdateUserInterfaceState ( EntityUid uid , LatheComponent ? component = null )
{
if ( ! Resolve ( uid , ref component ) )
return ;
2025-06-26 18:08:01 -04:00
var producing = component . CurrentRecipe ;
2025-08-24 11:02:47 -04:00
if ( producing = = null & & component . Queue . First is { } node )
producing = node . Value . Recipe ;
2022-08-04 12:38:56 +12:00
2025-06-26 18:08:01 -04:00
var state = new LatheUpdateState ( GetAvailableRecipes ( uid , component ) , component . Queue . ToArray ( ) , producing ) ;
2024-04-26 18:16:24 +10:00
_uiSys . SetUiState ( uid , LatheUiKey . Key , state ) ;
2022-09-16 19:49:05 -04:00
}
2022-08-04 12:38:56 +12:00
2025-02-07 18:22:49 +00:00
/// <summary>
/// Adds every unlocked recipe from each pack to the recipes list.
/// </summary>
public void AddRecipesFromDynamicPacks ( ref LatheGetRecipesEvent args , TechnologyDatabaseComponent database , IEnumerable < ProtoId < LatheRecipePackPrototype > > packs )
{
foreach ( var id in packs )
{
var pack = _proto . Index ( id ) ;
foreach ( var recipe in pack . Recipes )
{
2025-04-17 21:07:51 +10:00
if ( args . GetUnavailable | | database . UnlockedRecipes . Contains ( recipe ) )
2025-02-07 18:22:49 +00:00
args . Recipes . Add ( recipe ) ;
}
}
}
2022-09-16 19:49:05 -04:00
private void OnGetRecipes ( EntityUid uid , TechnologyDatabaseComponent component , LatheGetRecipesEvent args )
{
2025-04-17 21:07:51 +10:00
if ( uid = = args . Lathe )
AddRecipesFromDynamicPacks ( ref args , component , args . Comp . DynamicPacks ) ;
2022-09-16 19:49:05 -04:00
}
2023-08-07 17:21:04 +03:00
private void GetEmagLatheRecipes ( EntityUid uid , EmagLatheRecipesComponent component , LatheGetRecipesEvent args )
{
2025-02-07 18:22:49 +00:00
if ( uid ! = args . Lathe )
2023-08-07 17:21:04 +03:00
return ;
2025-02-07 18:22:49 +00:00
2025-04-17 21:07:51 +10:00
if ( ! args . GetUnavailable & & ! _emag . CheckFlag ( uid , EmagType . Interaction ) )
2023-08-07 17:21:04 +03:00
return ;
2025-02-07 18:22:49 +00:00
AddRecipesFromPacks ( args . Recipes , component . EmagStaticPacks ) ;
if ( TryComp < TechnologyDatabaseComponent > ( uid , out var database ) )
AddRecipesFromDynamicPacks ( ref args , database , component . EmagDynamicPacks ) ;
2023-08-07 17:21:04 +03:00
}
2023-12-30 11:18:58 -05:00
private void OnHeatStartPrinting ( EntityUid uid , LatheHeatProducingComponent component , LatheStartPrintingEvent args )
{
component . NextSecond = _timing . CurTime ;
}
2023-01-07 21:36:50 -05:00
private void OnMaterialAmountChanged ( EntityUid uid , LatheComponent component , ref MaterialAmountChangedEvent args )
{
UpdateUserInterfaceState ( uid , component ) ;
}
2022-09-16 19:49:05 -04:00
/// <summary>
/// Initialize the UI and appearance.
/// Appearance requires initialization or the layers break
/// </summary>
private void OnMapInit ( EntityUid uid , LatheComponent component , MapInitEvent args )
{
_appearance . SetData ( uid , LatheVisuals . IsInserting , false ) ;
_appearance . SetData ( uid , LatheVisuals . IsRunning , false ) ;
_materialStorage . UpdateMaterialWhitelist ( uid ) ;
}
2022-04-17 03:34:14 -04:00
/// <summary>
/// Sets the machine sprite to either play the running animation
/// or stop.
/// </summary>
private void UpdateRunningAppearance ( EntityUid uid , bool isRunning )
{
2022-09-16 19:49:05 -04:00
_appearance . SetData ( uid , LatheVisuals . IsRunning , isRunning ) ;
2022-04-17 03:34:14 -04:00
}
2022-10-15 15:08:15 +11:00
private void OnPowerChanged ( EntityUid uid , LatheComponent component , ref PowerChangedEvent args )
2022-09-16 19:49:05 -04:00
{
if ( ! args . Powered )
{
2025-08-24 11:02:47 -04:00
AbortProduction ( uid ) ;
2022-09-16 19:49:05 -04:00
}
2025-08-24 11:02:47 -04:00
else
2022-09-16 19:49:05 -04:00
{
TryStartProducing ( uid , component ) ;
}
}
2022-12-25 16:22:23 -05:00
private void OnDatabaseModified ( EntityUid uid , LatheComponent component , ref TechnologyDatabaseModifiedEvent args )
{
UpdateUserInterfaceState ( uid , component ) ;
}
2025-04-16 15:29:25 -04:00
private void OnTechnologyDatabaseModified ( Entity < LatheAnnouncingComponent > ent , ref TechnologyDatabaseModifiedEvent args )
{
if ( args . NewlyUnlockedRecipes is null )
return ;
if ( ! TryGetAvailableRecipes ( ent . Owner , out var potentialRecipes ) )
return ;
var recipeNames = new List < string > ( ) ;
foreach ( var recipeId in args . NewlyUnlockedRecipes )
{
if ( ! potentialRecipes . Contains ( new ( recipeId ) ) )
continue ;
if ( ! _proto . TryIndex ( recipeId , out LatheRecipePrototype ? recipe ) )
continue ;
var itemName = GetRecipeName ( recipe ! ) ;
recipeNames . Add ( Loc . GetString ( "lathe-unlock-recipe-radio-broadcast-item" , ( "item" , itemName ) ) ) ;
}
if ( recipeNames . Count = = 0 )
return ;
2025-04-26 17:45:53 -04:00
var message =
recipeNames . Count > ent . Comp . MaximumItems ?
Loc . GetString (
"lathe-unlock-recipe-radio-broadcast-overflow" ,
( "items" , ContentLocalizationManager . FormatList ( recipeNames . GetRange ( 0 , ent . Comp . MaximumItems ) ) ) ,
( "count" , recipeNames . Count )
) :
Loc . GetString (
"lathe-unlock-recipe-radio-broadcast" ,
( "items" , ContentLocalizationManager . FormatList ( recipeNames ) )
) ;
2025-04-16 15:29:25 -04:00
foreach ( var channel in ent . Comp . Channels )
{
_radio . SendRadioMessage ( ent . Owner , message , channel , ent . Owner , escapeMarkup : false ) ;
}
}
2023-06-20 02:39:35 -04:00
private void OnResearchRegistrationChanged ( EntityUid uid , LatheComponent component , ref ResearchRegistrationChangedEvent args )
{
UpdateUserInterfaceState ( uid , component ) ;
}
2022-09-16 19:49:05 -04:00
protected override bool HasRecipe ( EntityUid uid , LatheRecipePrototype recipe , LatheComponent component )
{
2023-01-19 23:06:02 -05:00
return GetAvailableRecipes ( uid , component ) . Contains ( recipe . ID ) ;
2022-04-17 03:34:14 -04:00
}
2024-08-01 00:15:05 -04:00
2025-08-24 11:02:47 -04:00
public void AbortProduction ( EntityUid uid , LatheComponent ? component = null )
{
if ( ! Resolve ( uid , ref component ) )
return ;
if ( component . CurrentRecipe ! = null )
{
if ( component . Queue . Count > 0 )
{
// Batch abandoned while printing last item, need to create a one-item batch
var batch = component . Queue . First ( ) ;
if ( batch . Recipe ! = component . CurrentRecipe )
{
var newBatch = new LatheRecipeBatch ( component . CurrentRecipe . Value , 0 , 1 ) ;
component . Queue . AddFirst ( newBatch ) ;
}
else if ( batch . ItemsPrinted > 0 )
{
batch . ItemsPrinted - - ;
}
}
component . CurrentRecipe = null ;
}
RemCompDeferred < LatheProducingComponent > ( uid ) ;
UpdateUserInterfaceState ( uid , component ) ;
UpdateRunningAppearance ( uid , false ) ;
}
2022-08-04 12:38:56 +12:00
#region UI Messages
2022-04-17 03:34:14 -04:00
2022-08-04 12:38:56 +12:00
private void OnLatheQueueRecipeMessage ( EntityUid uid , LatheComponent component , LatheQueueRecipeMessage args )
{
2022-09-16 19:49:05 -04:00
if ( _proto . TryIndex ( args . ID , out LatheRecipePrototype ? recipe ) )
2022-04-17 03:34:14 -04:00
{
2025-08-24 11:02:47 -04:00
if ( TryAddToQueue ( uid , recipe , args . Quantity , component ) )
2023-01-07 21:36:50 -05:00
{
2024-08-01 00:15:05 -04:00
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
2025-08-24 11:02:47 -04:00
$"{ToPrettyString(args.Actor):player} queued {args.Quantity} {GetRecipeName(recipe)} at {ToPrettyString(uid):lathe}" ) ;
2023-01-07 21:36:50 -05:00
}
2022-04-17 03:34:14 -04:00
}
2022-09-16 19:49:05 -04:00
TryStartProducing ( uid , component ) ;
UpdateUserInterfaceState ( uid , component ) ;
2022-04-17 03:34:14 -04:00
}
2022-08-04 12:38:56 +12:00
private void OnLatheSyncRequestMessage ( EntityUid uid , LatheComponent component , LatheSyncRequestMessage args )
2022-04-17 03:34:14 -04:00
{
2022-09-16 19:49:05 -04:00
UpdateUserInterfaceState ( uid , component ) ;
2019-04-26 15:51:05 +02:00
}
2025-08-24 11:02:47 -04:00
/// <summary>
/// Removes a batch from the batch queue by index.
/// If the index given does not exist or is outside of the bounds of the lathe's batch queue, nothing happens.
/// </summary>
/// <param name="uid">The lathe whose queue is being altered.</param>
/// <param name="component"></param>
/// <param name="args"></param>
public void OnLatheDeleteRequestMessage ( EntityUid uid , LatheComponent component , ref LatheDeleteRequestMessage args )
{
if ( args . Index < 0 | | args . Index > = component . Queue . Count )
return ;
var node = component . Queue . First ;
for ( int i = 0 ; i < args . Index ; i + + )
node = node ? . Next ;
if ( node = = null ) // Shouldn't happen with checks above.
return ;
var batch = node . Value ;
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
$"{ToPrettyString(args.Actor):player} deleted a lathe job for ({batch.ItemsPrinted}/{batch.ItemsRequested}) {GetRecipeName(batch.Recipe)} at {ToPrettyString(uid):lathe}" ) ;
component . Queue . Remove ( node ) ;
UpdateUserInterfaceState ( uid , component ) ;
}
public void OnLatheMoveRequestMessage ( EntityUid uid , LatheComponent component , ref LatheMoveRequestMessage args )
{
if ( args . Change = = 0 | | args . Index < 0 | | args . Index > = component . Queue . Count )
return ;
// New index must be within the bounds of the batch.
var newIndex = args . Index + args . Change ;
if ( newIndex < 0 | | newIndex > = component . Queue . Count )
return ;
var node = component . Queue . First ;
for ( int i = 0 ; i < args . Index ; i + + )
node = node ? . Next ;
if ( node = = null ) // Something went wrong.
return ;
if ( args . Change > 0 )
{
var newRelativeNode = node . Next ;
for ( int i = 1 ; i < args . Change ; i + + ) // 1-indexed: starting from Next
newRelativeNode = newRelativeNode ? . Next ;
if ( newRelativeNode = = null ) // Something went wrong.
return ;
component . Queue . Remove ( node ) ;
component . Queue . AddAfter ( newRelativeNode , node ) ;
}
else
{
var newRelativeNode = node . Previous ;
for ( int i = 1 ; i < - args . Change ; i + + ) // 1-indexed: starting from Previous
newRelativeNode = newRelativeNode ? . Previous ;
if ( newRelativeNode = = null ) // Something went wrong.
return ;
component . Queue . Remove ( node ) ;
component . Queue . AddBefore ( newRelativeNode , node ) ;
}
UpdateUserInterfaceState ( uid , component ) ;
}
public void OnLatheAbortFabricationMessage ( EntityUid uid , LatheComponent component , ref LatheAbortFabricationMessage args )
{
if ( component . CurrentRecipe = = null )
return ;
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
$"{ToPrettyString(args.Actor):player} aborted printing {GetRecipeName(component.CurrentRecipe.Value)} at {ToPrettyString(uid):lathe}" ) ;
component . CurrentRecipe = null ;
FinishProducing ( uid , component ) ;
}
2022-08-04 12:38:56 +12:00
#endregion
2019-04-26 15:51:05 +02:00
}
}