| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975 |
- using Content.Server.Atmos.EntitySystems;
- using Content.Server.Botany.Components;
- using Content.Server.Kitchen.Components;
- using Content.Server.Popups;
- using Content.Shared.Chemistry.EntitySystems;
- using Content.Shared.Atmos;
- using Content.Shared.Botany;
- using Content.Shared.Burial.Components;
- using Content.Shared.Chemistry.Reagent;
- using Content.Shared.Coordinates.Helpers;
- using Content.Shared.Examine;
- using Content.Shared.FixedPoint;
- using Content.Shared.Hands.Components;
- using Content.Shared.IdentityManagement;
- using Content.Shared.Interaction;
- using Content.Shared.Popups;
- using Content.Shared.Random;
- using Content.Shared.Tag;
- using Robust.Server.GameObjects;
- using Robust.Shared.Audio.Systems;
- using Robust.Shared.Player;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Random;
- using Robust.Shared.Timing;
- using Content.Server.Labels.Components;
- using Content.Shared.Containers.ItemSlots;
- using Content.Shared.Weather;
- namespace Content.Server.Botany.Systems;
- public sealed class PlantHolderSystem : EntitySystem
- {
- [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
- [Dependency] private readonly BotanySystem _botany = default!;
- [Dependency] private readonly IPrototypeManager _prototype = default!;
- [Dependency] private readonly MutationSystem _mutation = default!;
- [Dependency] private readonly AppearanceSystem _appearance = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly PopupSystem _popup = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
- [Dependency] private readonly TagSystem _tagSystem = default!;
- [Dependency] private readonly RandomHelperSystem _randomHelper = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
- public const float HydroponicsSpeedMultiplier = 1f;
- public const float HydroponicsConsumptionMultiplier = 2f;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<PlantHolderComponent, ExaminedEvent>(OnExamine);
- SubscribeLocalEvent<PlantHolderComponent, InteractUsingEvent>(OnInteractUsing);
- SubscribeLocalEvent<PlantHolderComponent, InteractHandEvent>(OnInteractHand);
- SubscribeLocalEvent<PlantHolderComponent, SolutionTransferredEvent>(OnSolutionTransferred);
- }
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- var query = EntityQueryEnumerator<PlantHolderComponent>();
- while (query.MoveNext(out var uid, out var plantHolder))
- {
- if (plantHolder.NextUpdate > _gameTiming.CurTime)
- continue;
- plantHolder.NextUpdate = _gameTiming.CurTime + plantHolder.UpdateDelay;
- Update(uid, plantHolder);
- }
- }
- private int GetCurrentGrowthStage(Entity<PlantHolderComponent> entity)
- {
- var (uid, component) = entity;
- if (component.Seed == null)
- return 0;
- var result = Math.Max(1, (int)(component.Age * component.Seed.GrowthStages / component.Seed.Maturation));
- return result;
- }
- private void OnExamine(Entity<PlantHolderComponent> entity, ref ExaminedEvent args)
- {
- if (!args.IsInDetailsRange)
- return;
- var (_, component) = entity;
- using (args.PushGroup(nameof(PlantHolderComponent)))
- {
- if (component.Seed == null)
- {
- args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
- }
- else if (!component.Dead)
- {
- var displayName = Loc.GetString(component.Seed.DisplayName);
- args.PushMarkup(Loc.GetString("plant-holder-component-something-already-growing-message",
- ("seedName", displayName),
- ("toBeForm", displayName.EndsWith('s') ? "are" : "is")));
- if (component.Health <= component.Seed.Endurance / 2)
- {
- args.PushMarkup(Loc.GetString(
- "plant-holder-component-something-already-growing-low-health-message",
- ("healthState",
- Loc.GetString(component.Age > component.Seed.Lifespan
- ? "plant-holder-component-plant-old-adjective"
- : "plant-holder-component-plant-unhealthy-adjective"))));
- }
- }
- else
- {
- args.PushMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message"));
- }
- if (component.WeedLevel >= 5)
- args.PushMarkup(Loc.GetString("plant-holder-component-weed-high-level-message"));
- if (component.PestLevel >= 5)
- args.PushMarkup(Loc.GetString("plant-holder-component-pest-high-level-message"));
- args.PushMarkup(Loc.GetString($"plant-holder-component-water-level-message",
- ("waterLevel", (int)component.WaterLevel)));
- args.PushMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
- ("nutritionLevel", (int)component.NutritionLevel)));
- if (component.DrawWarnings)
- {
- if (component.Toxins > 40f)
- args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning"));
- if (component.ImproperLight)
- args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning"));
- if (component.ImproperHeat)
- args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning"));
- if (component.ImproperPressure)
- args.PushMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning"));
- if (component.MissingGas > 0)
- args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
- }
- }
- }
- private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args)
- {
- var (uid, component) = entity;
- if (TryComp(args.Used, out SeedComponent? seeds))
- {
- if (component.Seed == null)
- {
- if (!_botany.TryGetSeed(seeds, out var seed))
- return;
- args.Handled = true;
- var name = Loc.GetString(seed.Name);
- var noun = Loc.GetString(seed.Noun);
- _popup.PopupCursor(Loc.GetString("plant-holder-component-plant-success-message",
- ("seedName", name),
- ("seedNoun", noun)), args.User, PopupType.Medium);
- component.Seed = seed;
- component.Dead = false;
- component.Age = 1;
- if (seeds.HealthOverride != null)
- {
- component.Health = seeds.HealthOverride.Value;
- }
- else
- {
- component.Health = component.Seed.Endurance;
- }
- component.LastCycle = _gameTiming.CurTime;
- if (TryComp<PaperLabelComponent>(args.Used, out var paperLabel))
- {
- _itemSlots.TryEjectToHands(args.Used, paperLabel.LabelSlot, args.User);
- }
- QueueDel(args.Used);
- CheckLevelSanity(uid, component);
- UpdateSprite(uid, component);
- return;
- }
- args.Handled = true;
- _popup.PopupCursor(Loc.GetString("plant-holder-component-already-seeded-message",
- ("name", Comp<MetaDataComponent>(uid).EntityName)), args.User, PopupType.Medium);
- return;
- }
- if (_tagSystem.HasTag(args.Used, "Hoe"))
- {
- args.Handled = true;
- if (component.WeedLevel > 0)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-weeds-message",
- ("name", Comp<MetaDataComponent>(uid).EntityName)), args.User, PopupType.Medium);
- _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message",
- ("otherName", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true);
- component.WeedLevel = 0;
- UpdateSprite(uid, component);
- }
- else
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-no-weeds-message"), args.User);
- }
- return;
- }
- if (HasComp<ShovelComponent>(args.Used))
- {
- args.Handled = true;
- if (component.Seed != null)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-plant-message",
- ("name", Comp<MetaDataComponent>(uid).EntityName)), args.User, PopupType.Medium);
- _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message",
- ("name", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true);
- RemovePlant(uid, component);
- }
- else
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-no-plant-message",
- ("name", Comp<MetaDataComponent>(uid).EntityName)), args.User);
- }
- return;
- }
- if (_tagSystem.HasTag(args.Used, "PlantSampleTaker"))
- {
- args.Handled = true;
- if (component.Seed == null)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-nothing-to-sample-message"), args.User);
- return;
- }
- if (component.Sampled)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-already-sampled-message"), args.User);
- return;
- }
- if (component.Dead)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-dead-plant-message"), args.User);
- return;
- }
- if (GetCurrentGrowthStage(entity) <= 1)
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-early-sample-message"), args.User);
- return;
- }
- component.Health -= (_random.Next(3, 5) * 10);
- float? healthOverride;
- if (component.Harvest)
- {
- healthOverride = null;
- }
- else
- {
- healthOverride = component.Health;
- }
- var packetSeed = component.Seed;
- var seed = _botany.SpawnSeedPacket(packetSeed, Transform(args.User).Coordinates, args.User, healthOverride);
- _randomHelper.RandomOffset(seed, 0.25f);
- var displayName = Loc.GetString(component.Seed.DisplayName);
- _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message",
- ("seedName", displayName)), args.User);
- DoScream(entity.Owner, component.Seed);
- if (_random.Prob(0.3f))
- component.Sampled = true;
- // Just in case.
- CheckLevelSanity(uid, component);
- ForceUpdateByExternalCause(uid, component);
- return;
- }
- if (HasComp<SharpComponent>(args.Used))
- {
- args.Handled = true;
- DoHarvest(uid, args.User, component);
- return;
- }
- if (TryComp<ProduceComponent>(args.Used, out var produce) && false) // Deactivated untill we ballance composting directly into the field
- {
- args.Handled = true;
- _popup.PopupCursor(Loc.GetString("plant-holder-component-compost-message",
- ("owner", uid),
- ("usingItem", args.Used)), args.User, PopupType.Medium);
- _popup.PopupEntity(Loc.GetString("plant-holder-component-compost-others-message",
- ("user", Identity.Entity(args.User, EntityManager)),
- ("usingItem", args.Used),
- ("owner", uid)), uid, Filter.PvsExcept(args.User), true);
- if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2))
- {
- if (_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution1))
- {
- // We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can,
- // since the plant will be consumed anyway.
- var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume);
- _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount));
- ForceUpdateByExternalCause(uid, component);
- }
- }
- var seed = produce.Seed;
- if (seed != null)
- {
- var nutrientBonus = seed.Potency / 2.5f;
- AdjustNutrient(uid, nutrientBonus, component);
- }
- QueueDel(args.Used);
- }
- }
- private void OnSolutionTransferred(Entity<PlantHolderComponent> ent, ref SolutionTransferredEvent args)
- {
- _audio.PlayPvs(ent.Comp.WateringSound, ent.Owner);
- }
- private void OnInteractHand(Entity<PlantHolderComponent> entity, ref InteractHandEvent args)
- {
- DoHarvest(entity, args.User, entity.Comp);
- }
- public void WeedInvasion()
- {
- // TODO
- }
- public void Update(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- UpdateReagents(uid, component);
- var curTime = _gameTiming.CurTime;
- if (component.ForceUpdate)
- component.ForceUpdate = false;
- else if (curTime < (component.LastCycle + component.CycleDelay))
- {
- if (component.UpdateSpriteAfterUpdate)
- UpdateSprite(uid, component);
- return;
- }
- component.LastCycle = curTime;
- // Process mutations
- if (component.MutationLevel > 0)
- {
- Mutate(uid, Math.Min(component.MutationLevel, 25), component);
- component.UpdateSpriteAfterUpdate = true;
- component.MutationLevel = 0;
- }
- // Weeds like water and nutrients! They may appear even if there's not a seed planted.
- if (component.WaterLevel > 10 && component.NutritionLevel > 5)
- {
- var chance = 0f;
- if (component.Seed == null)
- chance = 0.05f;
- else if (component.Seed.TurnIntoKudzu)
- chance = 1f;
- else
- chance = 0.01f;
- if (_random.Prob(chance))
- component.WeedLevel += 1 + HydroponicsSpeedMultiplier * component.WeedCoefficient;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- if (component.Seed != null && component.Seed.TurnIntoKudzu
- && component.WeedLevel >= component.Seed.WeedHighLevelThreshold)
- {
- Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager));
- component.Seed.TurnIntoKudzu = false;
- component.Health = 0;
- }
- // There's a chance for a weed explosion to happen if weeds take over.
- // Plants that are themselves weeds (WeedTolerance > 8) are unaffected.
- if (component.WeedLevel >= 10 && _random.Prob(0.1f))
- {
- if (component.Seed == null || component.WeedLevel >= component.Seed.WeedTolerance + 2)
- WeedInvasion();
- }
- // If we have no seed planted, or the plant is dead, stop processing here.
- if (component.Seed == null || component.Dead)
- {
- if (component.UpdateSpriteAfterUpdate)
- UpdateSprite(uid, component);
- return;
- }
- // There's a small chance the pest population increases.
- // Can only happen when there's a live seed planted.
- if (_random.Prob(0.01f))
- {
- component.PestLevel += 0.5f * HydroponicsSpeedMultiplier;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- // Advance plant age here.
- if (component.SkipAging > 0)
- component.SkipAging--;
- else
- {
- if (_random.Prob(0.8f))
- component.Age += (int)(1 * HydroponicsSpeedMultiplier);
- component.UpdateSpriteAfterUpdate = true;
- }
- // Nutrient consumption.
- if (component.Seed.NutrientConsumption > 0 && component.NutritionLevel > 0 && _random.Prob(0.75f))
- {
- component.NutritionLevel -= MathF.Max(0f, component.Seed.NutrientConsumption * HydroponicsSpeedMultiplier);
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- // Water consumption.
- if (component.Seed.WaterConsumption > 0 && component.WaterLevel > 0 && _random.Prob(0.75f))
- {
- var weather = EntityQueryEnumerator<WeatherNomadsComponent>();
- while (weather.MoveNext(out var uuid, out var weatherComponent))
- {
- if (weatherComponent.CurrentWeather == "Rain" || weatherComponent.CurrentWeather == "Storm")
- {
- component.WaterLevel += 2f;
- }
- }
- component.WaterLevel -= MathF.Max(0f,
- component.Seed.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier);
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier;
- // Make sure genetics are viable.
- if (!component.Seed.Viable)
- {
- AffectGrowth(uid, -1, component);
- component.Health -= 6 * healthMod;
- }
- // Prevents the plant from aging when lacking resources.
- // Limits the effect on aging so that when resources are added, the plant starts growing in a reasonable amount of time.
- if (component.SkipAging < 10)
- {
- // Make sure the plant is not starving.
- if (component.NutritionLevel > 5)
- {
- component.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod;
- }
- else
- {
- AffectGrowth(uid, -1, component);
- component.Health -= healthMod;
- }
- // Make sure the plant is not thirsty.
- if (component.WaterLevel > 10)
- {
- component.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod;
- }
- else
- {
- AffectGrowth(uid, -1, component);
- component.Health -= healthMod;
- }
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas;
- component.MissingGas = 0;
- if (component.Seed.ConsumeGasses.Count > 0)
- {
- foreach (var (gas, amount) in component.Seed.ConsumeGasses)
- {
- if (environment.GetMoles(gas) < amount)
- {
- component.MissingGas++;
- continue;
- }
- environment.AdjustMoles(gas, -amount);
- }
- if (component.MissingGas > 0)
- {
- component.Health -= component.MissingGas * HydroponicsSpeedMultiplier;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- }
- // SeedPrototype pressure resistance.
- var pressure = environment.Pressure;
- if (pressure < component.Seed.LowPressureTolerance || pressure > component.Seed.HighPressureTolerance)
- {
- component.Health -= healthMod;
- component.ImproperPressure = true;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- else
- {
- component.ImproperPressure = false;
- }
- // SeedPrototype ideal temperature.
- if (MathF.Abs(environment.Temperature - component.Seed.IdealHeat) > component.Seed.HeatTolerance)
- {
- component.Health -= healthMod;
- component.ImproperHeat = true;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- else
- {
- component.ImproperHeat = false;
- }
- // Gas production.
- var exudeCount = component.Seed.ExudeGasses.Count;
- if (exudeCount > 0)
- {
- foreach (var (gas, amount) in component.Seed.ExudeGasses)
- {
- environment.AdjustMoles(gas,
- MathF.Max(1f, MathF.Round(amount * MathF.Round(component.Seed.Potency) / exudeCount)));
- }
- }
- // Toxin levels beyond the plant's tolerance cause damage.
- // They are, however, slowly reduced over time.
- if (component.Toxins > 0)
- {
- var toxinUptake = MathF.Max(1, MathF.Round(component.Toxins / 10f));
- if (component.Toxins > component.Seed.ToxinsTolerance)
- {
- component.Health -= toxinUptake;
- }
- component.Toxins -= toxinUptake;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- // Weed levels.
- if (component.PestLevel > 0)
- {
- // TODO: Carnivorous plants?
- if (component.PestLevel > component.Seed.PestTolerance)
- {
- component.Health -= HydroponicsSpeedMultiplier;
- }
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- // Weed levels.
- if (component.WeedLevel > 0)
- {
- // TODO: Parasitic plants.
- if (component.WeedLevel >= component.Seed.WeedTolerance)
- {
- component.Health -= HydroponicsSpeedMultiplier;
- }
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- if (component.Age > component.Seed.Lifespan)
- {
- component.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier;
- if (component.DrawWarnings)
- component.UpdateSpriteAfterUpdate = true;
- }
- else if (component.Age < 0) // Revert back to seed packet!
- {
- var packetSeed = component.Seed;
- // will put it in the trays hands if it has any, please do not try doing this
- _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid);
- RemovePlant(uid, component);
- component.ForceUpdate = true;
- Update(uid, component);
- return;
- }
- CheckHealth(uid, component);
- if (component.Harvest && component.Seed.HarvestRepeat == HarvestType.SelfHarvest)
- AutoHarvest(uid, component);
- // If enough time has passed since the plant was harvested, we're ready to harvest again!
- if (!component.Dead && component.Seed.ProductPrototypes.Count > 0)
- {
- if (component.Age > component.Seed.Production)
- {
- if (component.Age - component.LastProduce > component.Seed.Production && !component.Harvest)
- {
- component.Harvest = true;
- component.LastProduce = component.Age;
- }
- }
- else
- {
- if (component.Harvest)
- {
- component.Harvest = false;
- component.LastProduce = component.Age;
- }
- }
- }
- CheckLevelSanity(uid, component);
- if (component.UpdateSpriteAfterUpdate)
- UpdateSprite(uid, component);
- }
- //TODO: kill this bullshit
- public void CheckLevelSanity(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Seed != null)
- component.Health = MathHelper.Clamp(component.Health, 0, component.Seed.Endurance);
- else
- {
- component.Health = 0f;
- component.Dead = false;
- }
- component.MutationLevel = MathHelper.Clamp(component.MutationLevel, 0f, 100f);
- component.NutritionLevel = MathHelper.Clamp(component.NutritionLevel, 0f, 100f);
- component.WaterLevel = MathHelper.Clamp(component.WaterLevel, 0f, 100f);
- component.PestLevel = MathHelper.Clamp(component.PestLevel, 0f, 10f);
- component.WeedLevel = MathHelper.Clamp(component.WeedLevel, 0f, 10f);
- component.Toxins = MathHelper.Clamp(component.Toxins, 0f, 100f);
- component.YieldMod = MathHelper.Clamp(component.YieldMod, 0, 2);
- component.MutationMod = MathHelper.Clamp(component.MutationMod, 0f, 3f);
- }
- public bool DoHarvest(EntityUid plantholder, EntityUid user, PlantHolderComponent? component = null)
- {
- if (!Resolve(plantholder, ref component))
- return false;
- if (component.Seed == null || Deleted(user))
- return false;
- if (component.Harvest && !component.Dead)
- {
- if (TryComp<HandsComponent>(user, out var hands))
- {
- if (!_botany.CanHarvest(component.Seed, hands.ActiveHandEntity))
- {
- _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user);
- return false;
- }
- }
- else if (!_botany.CanHarvest(component.Seed))
- {
- return false;
- }
- _botany.Harvest(component.Seed, user, component.YieldMod);
- AfterHarvest(plantholder, component);
- return true;
- }
- if (!component.Dead)
- return false;
- RemovePlant(plantholder, component);
- AfterHarvest(plantholder, component);
- return true;
- }
- /// <summary>
- /// Force do scream on PlantHolder (like plant is screaming) using seed's ScreamSound specifier (collection or soundPath)
- /// </summary>
- /// <returns></returns>
- public bool DoScream(EntityUid plantholder, SeedData? seed = null)
- {
- if (seed == null || seed.CanScream == false)
- return false;
- _audio.PlayPvs(seed.ScreamSound, plantholder);
- return true;
- }
- public void AutoHarvest(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Seed == null || !component.Harvest)
- return;
- _botany.AutoHarvest(component.Seed, Transform(uid).Coordinates);
- AfterHarvest(uid, component);
- }
- private void AfterHarvest(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.Harvest = false;
- component.LastProduce = component.Age;
- DoScream(uid, component.Seed);
- if (component.Seed?.HarvestRepeat == HarvestType.NoRepeat)
- RemovePlant(uid, component);
- CheckLevelSanity(uid, component);
- UpdateSprite(uid, component);
- }
- public void CheckHealth(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Health <= 0)
- {
- Die(uid, component);
- }
- }
- public void Die(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.Dead = true;
- component.Harvest = false;
- component.MutationLevel = 0;
- component.YieldMod = 1;
- component.MutationMod = 1;
- component.ImproperLight = false;
- component.ImproperHeat = false;
- component.ImproperPressure = false;
- component.WeedLevel += 1 * HydroponicsSpeedMultiplier;
- component.PestLevel = 0;
- UpdateSprite(uid, component);
- }
- public void RemovePlant(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.YieldMod = 1;
- component.MutationMod = 1;
- component.PestLevel = 0;
- component.Seed = null;
- component.Dead = false;
- component.Age = 0;
- component.LastProduce = 0;
- component.Sampled = false;
- component.Harvest = false;
- component.ImproperLight = false;
- component.ImproperPressure = false;
- component.ImproperHeat = false;
- UpdateSprite(uid, component);
- }
- public void AffectGrowth(EntityUid uid, int amount, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Seed == null)
- return;
- if (amount > 0)
- {
- if (component.Age < component.Seed.Maturation)
- component.Age += amount;
- else if (!component.Harvest && component.Seed.Yield <= 0f)
- component.LastProduce -= amount;
- }
- else
- {
- if (component.Age < component.Seed.Maturation)
- component.SkipAging++;
- else if (!component.Harvest && component.Seed.Yield <= 0f)
- component.LastProduce += amount;
- }
- }
- public void AdjustNutrient(EntityUid uid, float amount, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.NutritionLevel += amount;
- }
- public void AdjustWater(EntityUid uid, float amount, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.WaterLevel += amount;
- // Water dilutes toxins.
- if (amount > 0)
- {
- component.Toxins -= amount * 4f;
- }
- }
- public void UpdateReagents(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution))
- return;
- if (solution.Volume > 0 && component.MutationLevel < 25)
- {
- var amt = FixedPoint2.New(1);
- foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
- {
- var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
- reagentProto.ReactionPlant(uid, entry, solution);
- }
- }
- CheckLevelSanity(uid, component);
- }
- private void Mutate(EntityUid uid, float severity, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Seed != null)
- {
- EnsureUniqueSeed(uid, component);
- _mutation.MutateSeed(uid, ref component.Seed, severity);
- }
- }
- public void UpdateSprite(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.UpdateSpriteAfterUpdate = false;
- if (!TryComp<AppearanceComponent>(uid, out var app))
- return;
- if (component.Seed != null)
- {
- if (component.DrawWarnings)
- {
- _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= component.Seed.Endurance / 2f);
- }
- if (component.Dead)
- {
- _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app);
- _appearance.SetData(uid, PlantHolderVisuals.PlantState, "dead", app);
- }
- else if (component.Harvest)
- {
- _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app);
- _appearance.SetData(uid, PlantHolderVisuals.PlantState, "harvest", app);
- }
- else if (component.Age < component.Seed.Maturation)
- {
- var growthStage = GetCurrentGrowthStage((uid, component));
- _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app);
- _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{growthStage}", app);
- component.LastProduce = component.Age;
- }
- else
- {
- _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app);
- _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{component.Seed.GrowthStages}", app);
- }
- }
- else
- {
- _appearance.SetData(uid, PlantHolderVisuals.PlantState, "", app);
- _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app);
- }
- if (!component.DrawWarnings)
- return;
- _appearance.SetData(uid, PlantHolderVisuals.WaterLight, component.WaterLevel <= 15, app);
- _appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 8, app);
- _appearance.SetData(uid, PlantHolderVisuals.AlertLight,
- component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat ||
- component.ImproperLight || component.ImproperPressure || component.MissingGas > 0, app);
- _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, component.Harvest, app);
- }
- /// <summary>
- /// Check if the currently contained seed is unique. If it is not, clone it so that we have a unique seed.
- /// Necessary to avoid modifying global seeds.
- /// </summary>
- public void EnsureUniqueSeed(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- if (component.Seed is { Unique: false })
- component.Seed = component.Seed.Clone();
- }
- public void ForceUpdateByExternalCause(EntityUid uid, PlantHolderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
- component.SkipAging++; // We're forcing an update cycle, so one age hasn't passed.
- component.ForceUpdate = true;
- Update(uid, component);
- }
- }
|