CatStation/Content.Shared/Botany/Systems/BotanySystem.Seed.cs
Pok 6f6a5694c8
Botany refactor (#44576)
* Restarting files from scratch

* reduce file count, more progress

* Additional partial work

* update 1

* update 2

* TODO execution

* fix

* Update PlantGrowthComponent.cs

* fix 1

* growth correction

* PlantGrowthComponent

* update 99

* last update

* oops

* Update vegan-meatball.yml

* Data loss

* ToxinsComponent

* Default components

* minor fix

* PlantTraitsComponent

* fix error

* fix error 2

* Copy growth components

* last update within this PR

* AutoHarvestGrowth remote

* Viable

* fix

* Fix growth rate

* Improvements to the YAML

* merge update

* fix

* code cleanup

* TODO

* fix

* oops

* oops 2

* more TODO

* fix names

* fix names oops

* Harvest bug fixes

* fix tests

* refactor: cleanup after mering master

* Update ConsumeExudeGasGrowthSystem.cs

* refactor: make this piece of happiness and ponies work

* review

* oops

* fix <summary>

* Separation of plant components into PlantComponent and PlantTraitsComponent and cleaning

* EnsureUniqueSeed

* refactor: change Resolve to TryComp where appropriate

* refactor: review comments - try get for components, clenaups

* refactor: minor yaml cleanups

* fixing trifles

* botany-rework-part-3

* API and mutations

* minor fixes

* Update WeedPestGrowthSystem.cs

* Update plants.yml

* PlantChemicals

* YAMLLinter

* review and some lost code

* YAMLLinter part 2

* plants.yml

* Aaaa tests

* seedId

* clone plant component

* Update clone.yml

* refactor: remove suppress possible null reference exception

* refactor: changed HasPlantAlive to TryGetAlivePlant, minor cleanup

* Separation of tray and plant

* oops

* PlantingPlant

* a little more cleaning

* oops 2

* botany-rework-part-4

* predict and API

* Field OldAgeDamage

* oops

* YMAL

* YMAL 2

* YMAAAAAL

* Update PlantToxinsSystem.cs

* review

* Update randomMutations.yml

* minor fix design

* Updates the plants visuals not every frame

* bug fixes

* some fixes

* bugfix and renames

* Dirty

* Replace ComponentRegistry

* unnecessary API

* fix merge

* build

* build 2

* Review by AJCM

* del PlantDataComponent and fix harvest

* fix all

* final review

* Update PlantTrayVisualizerSystem.cs

* fix merge

* Update PlantTraitsComponent.cs

* Traits LocId fix

* Update SpawnEntitiesBehavior.cs

* aaaaaaaaa

* fix all part 2

* PlantDataComponent revert

* aoao

* fix all part 3

* Update PlantTraitsComponent.cs

* Update clone.yml

* Reviews

---------

Co-authored-by: PraxisMapper <praxismapper@gmail.com>
Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>
Co-authored-by: AJCM-git <ajcm_mail@disroot.org>
2026-08-08 21:58:01 +00:00

182 lines
7.1 KiB
C#

using JetBrains.Annotations;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Botany.Components;
using Content.Shared.Botany.Items.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Cloning;
using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Random;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Shared.Botany.Systems;
public sealed partial class BotanySystem : EntitySystem
{
[Dependency] private IComponentFactory _componentFactory = default!;
[Dependency] private IGameTiming _timing = default!;
[Dependency] private MetaDataSystem _metaData = default!;
[Dependency] private PlantSystem _plant = default!;
[Dependency] private RandomHelperSystem _randomHelper = default!;
[Dependency] private SharedAppearanceSystem _appearance = default!;
[Dependency] private SharedCloningSystem _cloning = default!;
[Dependency] private SharedHandsSystem _hands = default!;
[Dependency] private SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private SharedTransformSystem _transform = default!;
public readonly ProtoId<CloningSettingsPrototype> SettingsId = "PlantClone";
public readonly ProtoId<CloningSettingsPrototype> LifecycleSettingsId = "PlantLifecycleClone";
[SubscribeLocalEvent]
private void OnExamined(Entity<SeedComponent> ent, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
if (!TryGetPlantComponent<PlantComponent>(ent.Comp.PlantData, ent.Comp.PlantProtoId, out var plant)
|| !TryGetPlantComponent<PlantDataComponent>(ent.Comp.PlantData, ent.Comp.PlantProtoId, out var plantData))
return;
using (args.PushGroup(nameof(SeedComponent), 1))
{
var name = Loc.GetString(plantData.Name);
args.PushMarkup(Loc.GetString("seed-component-description", ("seedName", name)));
args.PushMarkup(_plant.GetPlantStateMarkup(ent.Owner, plant));
}
}
[SubscribeLocalEvent]
private void OnSeedShutdown(Entity<SeedComponent> ent, ref ComponentShutdown args)
{
DeletePlantSnapshot(ent.Comp.PlantData);
}
[SubscribeLocalEvent]
private void OnProduceShutdown(Entity<ProduceComponent> ent, ref ComponentShutdown args)
{
DeletePlantSnapshot(ent.Comp.PlantData);
}
[SubscribeLocalEvent]
private void OnSwabShutdown(Entity<BotanySwabComponent> ent, ref ComponentShutdown args)
{
DeletePlantSnapshot(ent.Comp.PlantData);
}
/// <summary>
/// Tries to get a plant component from a snapshot or prototype.
/// </summary>
/// <typeparam name="T">The type of component to get.</typeparam>
/// <param name="snapshot">The snapshot to get the component from.</param>
/// <param name="plantProtoId">The prototype ID to get the component from.</param>
/// <param name="plant">The plant component if found.</param>
[PublicAPI]
public bool TryGetPlantComponent<T>(EntityUid? snapshot, EntProtoId? plantProtoId, [NotNullWhen(true)] out T? plant)
where T : class, IComponent, new()
{
plant = null;
if (snapshot != null && TryComp(snapshot, out plant))
return true;
if (plantProtoId == null)
return false;
if (!ProtoMan.TryIndex(plantProtoId.Value, out var proto))
return false;
return proto.TryComp(out plant, _componentFactory);
}
/// <summary>
/// Clones a component snapshot of a plant.
/// </summary>
/// <param name="source">The entity to clone the snapshot from.</param>
/// <param name="parent">The entity that should own the snapshot, if any.</param>
/// <param name="cloneLifecycle">If true, also clone lifecycle state into the snapshot.</param>
[PublicAPI]
public EntityUid? ClonePlantSnapshotData(EntityUid source, EntityUid? parent = null, bool cloneLifecycle = false)
{
var settingsId = cloneLifecycle ? LifecycleSettingsId : SettingsId;
if (!ProtoMan.TryIndex(settingsId, out var settings))
return null;
var snapshot = EntityManager.CreateEntityUninitialized(null);
_cloning.CloneComponents(source, snapshot, settings);
EntityManager.InitializeAndStartEntity(snapshot, doMapInit: false);
if (parent is { } parentUid)
_transform.SetParent(snapshot, parentUid);
return snapshot;
}
/// <summary>
/// Deletes a stored plant snapshot, if one exists.
/// </summary>
[PublicAPI]
public void DeletePlantSnapshot(EntityUid? snapshot)
{
if (snapshot == null)
return;
PredictedQueueDel(snapshot.Value);
}
/// <summary>
/// Applies the component data stored in a plant snapshot to a target entity.
/// </summary>
/// <param name="snapshot">The snapshot entity to copy component data from.</param>
/// <param name="target">The entity to apply the snapshot data to.</param>
/// <param name="cloneLifecycle">If true, also copy lifecycle state.</param>
[PublicAPI]
public void ApplyPlantSnapshotData(EntityUid? snapshot, EntityUid target, bool cloneLifecycle = false)
{
if (snapshot == null)
return;
var settingsId = cloneLifecycle ? LifecycleSettingsId : SettingsId;
if (!ProtoMan.TryIndex(settingsId, out var settings))
return;
_cloning.CloneComponents(snapshot.Value, target, settings);
}
/// <summary>
/// Internal method to spawn a seed packet from a plant component.
/// </summary>
/// <param name="plantData">The plant data component to spawn.</param>
/// <param name="plantProtoId">The plant prototype ID to store in the seed component.</param>
/// <param name="snapshot">The component snapshot to store in the seed component.</param>
/// <param name="coords">The coordinates to spawn the seed packet at.</param>
/// <param name="user">The user who is spawning the seed packet.</param>
/// <param name="healthOverride">The health override to store in the seed component.</param>
/// <returns>The spawned seed packet entity.</returns>
[PublicAPI]
public EntityUid SpawnSeedPacket(
PlantDataComponent plantData,
EntProtoId plantProtoId,
EntityUid? snapshot,
EntityCoordinates coords,
EntityUid user,
float? healthOverride = null)
{
var seedItem = PredictedSpawnAtPosition(plantData.PacketPrototype, coords);
var seedComp = EnsureComp<SeedComponent>(seedItem);
seedComp.PlantProtoId = plantProtoId;
seedComp.PlantData = snapshot.HasValue
? ClonePlantSnapshotData(snapshot.Value, parent: seedItem)
: null;
seedComp.HealthOverride = healthOverride;
Dirty(seedItem, seedComp);
var name = Loc.GetString(plantData.Name);
var noun = Loc.GetString(plantData.Noun);
_metaData.SetEntityName(seedItem, Loc.GetString("botany-seed-packet-name", ("seedName", name), ("seedNoun", noun)));
_hands.TryPickupAnyHand(user, seedItem);
return seedItem;
}
}