using Content.Server.Botany.Components; using Content.Server.PowerCell; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared._NF.PlantAnalyzer; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using System.Linq; using System.Text; using Content.Shared.Atmos; namespace Content.Server.Botany.Systems; public sealed class PlantAnalyzerSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly PowerCellSystem _cell = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnModeSelected); } private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) { if (args.Target == null || !args.CanReach || !_cell.HasActivatableCharge(ent, user: args.User)) return; if (ent.Comp.DoAfter != null) return; if (HasComp(args.Target) || TryComp(args.Target, out var plantHolder) && plantHolder.Seed != null) { if (ent.Comp.Settings.AdvancedScan) { var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.Settings.AdvScanDelay, new PlantAnalyzerDoAfterEvent(), ent, target: args.Target, used: ent) { NeedHand = true, BreakOnDamage = true, BreakOnMove = true, MovementThreshold = 0.01f }; _doAfterSystem.TryStartDoAfter(doAfterArgs, out ent.Comp.DoAfter); } else { var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.Settings.ScanDelay, new PlantAnalyzerDoAfterEvent(), ent, target: args.Target, used: ent) { NeedHand = true, BreakOnDamage = true, BreakOnMove = true, MovementThreshold = 0.01f }; _doAfterSystem.TryStartDoAfter(doAfterArgs, out ent.Comp.DoAfter); } } } private void OnDoAfter(Entity ent, ref PlantAnalyzerDoAfterEvent args) { ent.Comp.DoAfter = null; // Double charge use for advanced scan. if (ent.Comp.Settings.AdvancedScan) { if (!_cell.TryUseActivatableCharge(ent, user: args.User)) return; } if (args.Handled || args.Cancelled || args.Args.Target == null || !_cell.TryUseActivatableCharge(ent.Owner, user: args.User)) return; _audio.PlayPvs(ent.Comp.ScanningEndSound, ent); OpenUserInterface(args.User, ent); UpdateScannedUser(ent, args.Args.Target.Value); args.Handled = true; } private void OpenUserInterface(EntityUid user, EntityUid analyzer) { if (!TryComp(user, out var actor) || !_uiSystem.HasUi(analyzer, PlantAnalyzerUiKey.Key)) return; _uiSystem.OpenUi(analyzer, PlantAnalyzerUiKey.Key, actor.PlayerSession); } public void UpdateScannedUser(Entity ent, EntityUid target) { if (!_uiSystem.HasUi(ent, PlantAnalyzerUiKey.Key)) return; if (TryComp(target, out var seedComp)) { if (seedComp.Seed != null) { var state = ObtainingGeneDataSeed(seedComp.Seed, target, false, ent.Comp.Settings.AdvancedScan); _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state); } else if (seedComp.SeedId != null && _prototypeManager.TryIndex(seedComp.SeedId, out SeedPrototype? protoSeed)) { var state = ObtainingGeneDataSeed(protoSeed, target, false, ent.Comp.Settings.AdvancedScan); _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state); } } else if (TryComp(target, out var plantComp)) { if (plantComp.Seed != null) { var state = ObtainingGeneDataSeed(plantComp.Seed, target, true, ent.Comp.Settings.AdvancedScan); _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state); } } } /// /// Analysis of seed from prototype. /// public PlantAnalyzerScannedSeedPlantInformation ObtainingGeneDataSeed(SeedData seedData, EntityUid target, bool isTray, bool scanIsAdvanced) { // Get trickier fields first. AnalyzerHarvestType harvestType = AnalyzerHarvestType.Unknown; switch (seedData.HarvestRepeat) { case HarvestType.Repeat: harvestType = AnalyzerHarvestType.Repeat; break; case HarvestType.NoRepeat: harvestType = AnalyzerHarvestType.NoRepeat; break; case HarvestType.SelfHarvest: harvestType = AnalyzerHarvestType.SelfHarvest; break; default: break; } var mutationProtos = seedData.MutationPrototypes; List mutationStrings = new(); foreach (var mutationProto in mutationProtos) { if (_prototypeManager.TryIndex(mutationProto, out var seed)) { mutationStrings.Add(seed.DisplayName); } } PlantAnalyzerScannedSeedPlantInformation ret = new() { TargetEntity = GetNetEntity(target), IsTray = isTray, SeedName = seedData.DisplayName, SeedChem = seedData.Chemicals.Keys.ToArray(), HarvestType = harvestType, ExudeGases = GetGasFlags(seedData.ExudeGasses.Keys), ConsumeGases = GetGasFlags(seedData.ConsumeGasses.Keys), Endurance = seedData.Endurance, SeedYield = seedData.Yield, Lifespan = seedData.Lifespan, Maturation = seedData.Maturation, Production = seedData.Production, GrowthStages = seedData.GrowthStages, SeedPotency = seedData.Potency, Speciation = mutationStrings.ToArray() }; if (scanIsAdvanced) { AdvancedScanInfo advancedInfo = new() { NutrientConsumption = seedData.NutrientConsumption, WaterConsumption = seedData.WaterConsumption, IdealHeat = seedData.IdealHeat, HeatTolerance = seedData.HeatTolerance, IdealLight = seedData.IdealLight, LightTolerance = seedData.LightTolerance, ToxinsTolerance = seedData.ToxinsTolerance, LowPressureTolerance = seedData.LowPressureTolerance, HighPressureTolerance = seedData.HighPressureTolerance, PestTolerance = seedData.PestTolerance, WeedTolerance = seedData.WeedTolerance, Mutations = GetMutationFlags(seedData) }; ret.AdvancedInfo = advancedInfo; } return ret; } public MutationFlags GetMutationFlags(SeedData plant) { MutationFlags ret = MutationFlags.None; if (plant.TurnIntoKudzu) ret |= MutationFlags.TurnIntoKudzu; if (plant.Seedless || plant.PermanentlySeedless) ret |= MutationFlags.Seedless; if (plant.Ligneous) ret |= MutationFlags.Ligneous; if (plant.CanScream) ret |= MutationFlags.CanScream; return ret; } public GasFlags GetGasFlags(IEnumerable gases) { var gasFlags = GasFlags.None; foreach (var gas in gases) { switch (gas) { case Gas.Nitrogen: gasFlags |= GasFlags.Nitrogen; break; case Gas.Oxygen: gasFlags |= GasFlags.Oxygen; break; case Gas.CarbonDioxide: gasFlags |= GasFlags.CarbonDioxide; break; case Gas.Plasma: gasFlags |= GasFlags.Plasma; break; case Gas.Tritium: gasFlags |= GasFlags.Tritium; break; case Gas.WaterVapor: gasFlags |= GasFlags.WaterVapor; break; case Gas.Ammonia: gasFlags |= GasFlags.Ammonia; break; case Gas.NitrousOxide: gasFlags |= GasFlags.NitrousOxide; break; case Gas.Frezon: gasFlags |= GasFlags.Frezon; break; } } return gasFlags; } private void OnModeSelected(Entity ent, ref PlantAnalyzerSetMode args) { SetMode(ent, args.AdvancedScan); } public void SetMode(Entity ent, bool isAdvMode) { if (ent.Comp.DoAfter != null) return; ent.Comp.Settings.AdvancedScan = isAdvMode; } }