PlantholderComponent ECS (#12871)
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
using Content.Client.Botany.Components;
|
using Content.Client.Botany.Components;
|
||||||
using Content.Shared.Botany;
|
using Content.Shared.Botany;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Botany;
|
namespace Content.Client.Botany;
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,34 @@
|
|||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.Atmos.EntitySystems;
|
|
||||||
using Content.Server.Botany.Systems;
|
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
|
||||||
using Content.Server.Hands.Components;
|
|
||||||
using Content.Server.Ghost.Roles.Components;
|
|
||||||
using Content.Shared.Botany;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
|
||||||
using Content.Shared.Chemistry.Reagent;
|
|
||||||
using Content.Shared.FixedPoint;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Server.Botany.Components
|
namespace Content.Server.Botany.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class PlantHolderComponent : Component
|
public sealed class PlantHolderComponent : Component
|
||||||
{
|
{
|
||||||
public const float HydroponicsSpeedMultiplier = 1f;
|
[ViewVariables]
|
||||||
public const float HydroponicsConsumptionMultiplier = 4f;
|
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||||
|
public TimeSpan UpdateDelay = TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[ViewVariables]
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
public int LastProduce;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
[ViewVariables] private int _lastProduce;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] public int MissingGas;
|
|
||||||
|
|
||||||
private readonly TimeSpan _cycleDelay = TimeSpan.FromSeconds(15f);
|
|
||||||
|
|
||||||
[ViewVariables] public TimeSpan LastCycle = TimeSpan.Zero;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] private bool _updateSpriteAfterUpdate;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("drawWarnings")]
|
public int MissingGas;
|
||||||
public bool DrawWarnings { get; private set; } = false;
|
|
||||||
|
public readonly TimeSpan CycleDelay = TimeSpan.FromSeconds(15f);
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public TimeSpan LastCycle = TimeSpan.Zero;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float WaterLevel { get; private set; } = 100f;
|
public bool UpdateSpriteAfterUpdate;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("drawWarnings")]
|
||||||
|
public bool DrawWarnings = false;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float NutritionLevel { get; private set; } = 100f;
|
public float WaterLevel = 100f;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float NutritionLevel = 100f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float PestLevel { get; set; }
|
public float PestLevel { get; set; }
|
||||||
@@ -102,562 +86,5 @@ namespace Content.Server.Botany.Components
|
|||||||
|
|
||||||
[DataField("solution")]
|
[DataField("solution")]
|
||||||
public string SoilSolutionName { get; set; } = "soil";
|
public string SoilSolutionName { get; set; } = "soil";
|
||||||
|
|
||||||
public void WeedInvasion()
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
UpdateReagents();
|
|
||||||
|
|
||||||
var curTime = _gameTiming.CurTime;
|
|
||||||
|
|
||||||
if (ForceUpdate)
|
|
||||||
ForceUpdate = false;
|
|
||||||
else if (curTime < (LastCycle + _cycleDelay))
|
|
||||||
{
|
|
||||||
if (_updateSpriteAfterUpdate)
|
|
||||||
UpdateSprite();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LastCycle = curTime;
|
|
||||||
|
|
||||||
// todo ecs.
|
|
||||||
var botanySystem = EntitySystem.Get<BotanySystem>();
|
|
||||||
|
|
||||||
// Process mutations
|
|
||||||
if (MutationLevel > 0)
|
|
||||||
{
|
|
||||||
Mutate(Math.Min(MutationLevel, 25));
|
|
||||||
MutationLevel = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weeds like water and nutrients! They may appear even if there's not a seed planted.
|
|
||||||
if (WaterLevel > 10 && NutritionLevel > 2 && _random.Prob(Seed == null ? 0.05f : 0.01f))
|
|
||||||
{
|
|
||||||
WeedLevel += 1 * HydroponicsSpeedMultiplier * WeedCoefficient;
|
|
||||||
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's a chance for a weed explosion to happen if weeds take over.
|
|
||||||
// Plants that are themselves weeds (WeedTolerance > 8) are unaffected.
|
|
||||||
if (WeedLevel >= 10 && _random.Prob(0.1f))
|
|
||||||
{
|
|
||||||
if (Seed == null || WeedLevel >= Seed.WeedTolerance + 2)
|
|
||||||
WeedInvasion();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have no seed planted, or the plant is dead, stop processing here.
|
|
||||||
if (Seed == null || Dead)
|
|
||||||
{
|
|
||||||
if (_updateSpriteAfterUpdate)
|
|
||||||
UpdateSprite();
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
PestLevel += 0.5f * HydroponicsSpeedMultiplier;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance plant age here.
|
|
||||||
if (SkipAging > 0)
|
|
||||||
SkipAging--;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_random.Prob(0.8f))
|
|
||||||
Age += (int) (1 * HydroponicsSpeedMultiplier);
|
|
||||||
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nutrient consumption.
|
|
||||||
if (Seed.NutrientConsumption > 0 && NutritionLevel > 0 && _random.Prob(0.75f))
|
|
||||||
{
|
|
||||||
NutritionLevel -= MathF.Max(0f, Seed.NutrientConsumption * HydroponicsSpeedMultiplier);
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Water consumption.
|
|
||||||
if (Seed.WaterConsumption > 0 && WaterLevel > 0 && _random.Prob(0.75f))
|
|
||||||
{
|
|
||||||
WaterLevel -= MathF.Max(0f,
|
|
||||||
Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier);
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier;
|
|
||||||
|
|
||||||
// Make sure genetics are viable.
|
|
||||||
if (!Seed.Viable)
|
|
||||||
{
|
|
||||||
AffectGrowth(-1);
|
|
||||||
Health -= 6*healthMod;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the plant is not starving.
|
|
||||||
if (_random.Prob(0.35f))
|
|
||||||
{
|
|
||||||
if (NutritionLevel > 2)
|
|
||||||
{
|
|
||||||
Health += healthMod;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AffectGrowth(-1);
|
|
||||||
Health -= healthMod;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the plant is not thirsty.
|
|
||||||
if (_random.Prob(0.35f))
|
|
||||||
{
|
|
||||||
if (WaterLevel > 10)
|
|
||||||
{
|
|
||||||
Health += healthMod;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AffectGrowth(-1);
|
|
||||||
Health -= healthMod;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
|
||||||
var environment = atmosphereSystem.GetContainingMixture(Owner, true, true) ??
|
|
||||||
GasMixture.SpaceGas;
|
|
||||||
|
|
||||||
if (Seed.ConsumeGasses.Count > 0)
|
|
||||||
{
|
|
||||||
MissingGas = 0;
|
|
||||||
|
|
||||||
foreach (var (gas, amount) in Seed.ConsumeGasses)
|
|
||||||
{
|
|
||||||
if (environment.GetMoles(gas) < amount)
|
|
||||||
{
|
|
||||||
MissingGas++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
environment.AdjustMoles(gas, -amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MissingGas > 0)
|
|
||||||
{
|
|
||||||
Health -= MissingGas * HydroponicsSpeedMultiplier;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeedPrototype pressure resistance.
|
|
||||||
var pressure = environment.Pressure;
|
|
||||||
if (pressure < Seed.LowPressureTolerance || pressure > Seed.HighPressureTolerance)
|
|
||||||
{
|
|
||||||
Health -= healthMod;
|
|
||||||
ImproperPressure = true;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImproperPressure = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeedPrototype ideal temperature.
|
|
||||||
if (MathF.Abs(environment.Temperature - Seed.IdealHeat) > Seed.HeatTolerance)
|
|
||||||
{
|
|
||||||
Health -= healthMod;
|
|
||||||
ImproperHeat = true;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImproperHeat = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gas production.
|
|
||||||
var exudeCount = Seed.ExudeGasses.Count;
|
|
||||||
if (exudeCount > 0)
|
|
||||||
{
|
|
||||||
foreach (var (gas, amount) in Seed.ExudeGasses)
|
|
||||||
{
|
|
||||||
environment.AdjustMoles(gas,
|
|
||||||
MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toxin levels beyond the plant's tolerance cause damage.
|
|
||||||
// They are, however, slowly reduced over time.
|
|
||||||
if (Toxins > 0)
|
|
||||||
{
|
|
||||||
var toxinUptake = MathF.Max(1, MathF.Round(Toxins / 10f));
|
|
||||||
if (Toxins > Seed.ToxinsTolerance)
|
|
||||||
{
|
|
||||||
Health -= toxinUptake;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toxins -= toxinUptake;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weed levels.
|
|
||||||
if (PestLevel > 0)
|
|
||||||
{
|
|
||||||
// TODO: Carnivorous plants?
|
|
||||||
if (PestLevel > Seed.PestTolerance)
|
|
||||||
{
|
|
||||||
Health -= HydroponicsSpeedMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weed levels.
|
|
||||||
if (WeedLevel > 0)
|
|
||||||
{
|
|
||||||
// TODO: Parasitic plants.
|
|
||||||
if (WeedLevel >= Seed.WeedTolerance)
|
|
||||||
{
|
|
||||||
Health -= HydroponicsSpeedMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Age > Seed.Lifespan)
|
|
||||||
{
|
|
||||||
Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier;
|
|
||||||
if (DrawWarnings)
|
|
||||||
_updateSpriteAfterUpdate = true;
|
|
||||||
}
|
|
||||||
else if (Age < 0) // Revert back to seed packet!
|
|
||||||
{
|
|
||||||
botanySystem.SpawnSeedPacket(Seed, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
|
|
||||||
RemovePlant();
|
|
||||||
ForceUpdate = true;
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckHealth();
|
|
||||||
|
|
||||||
if (Harvest && Seed.HarvestRepeat == HarvestType.SelfHarvest)
|
|
||||||
AutoHarvest();
|
|
||||||
|
|
||||||
// If enough time has passed since the plant was harvested, we're ready to harvest again!
|
|
||||||
if (!Dead && Seed.ProductPrototypes.Count > 0)
|
|
||||||
{
|
|
||||||
if (Age > Seed.Production)
|
|
||||||
{
|
|
||||||
if ((Age - _lastProduce) > Seed.Production && !Harvest)
|
|
||||||
{
|
|
||||||
Harvest = true;
|
|
||||||
_lastProduce = Age;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Harvest)
|
|
||||||
{
|
|
||||||
Harvest = false;
|
|
||||||
_lastProduce = Age;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckLevelSanity();
|
|
||||||
|
|
||||||
if (Seed.Sentient)
|
|
||||||
{
|
|
||||||
var comp = _entMan.EnsureComponent<GhostTakeoverAvailableComponent>(Owner);
|
|
||||||
comp.RoleName = _entMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
|
||||||
comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_updateSpriteAfterUpdate)
|
|
||||||
UpdateSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckLevelSanity()
|
|
||||||
{
|
|
||||||
if (Seed != null)
|
|
||||||
Health = MathHelper.Clamp(Health, 0, Seed.Endurance);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Health = 0f;
|
|
||||||
Dead = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationLevel = MathHelper.Clamp(MutationLevel, 0f, 100f);
|
|
||||||
NutritionLevel = MathHelper.Clamp(NutritionLevel, 0f, 100f);
|
|
||||||
WaterLevel = MathHelper.Clamp(WaterLevel, 0f, 100f);
|
|
||||||
PestLevel = MathHelper.Clamp(PestLevel, 0f, 10f);
|
|
||||||
WeedLevel = MathHelper.Clamp(WeedLevel, 0f, 10f);
|
|
||||||
Toxins = MathHelper.Clamp(Toxins, 0f, 100f);
|
|
||||||
YieldMod = MathHelper.Clamp(YieldMod, 0, 2);
|
|
||||||
MutationMod = MathHelper.Clamp(MutationMod, 0f, 3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DoHarvest(EntityUid user)
|
|
||||||
{
|
|
||||||
if (Seed == null || _entMan.Deleted(user))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var botanySystem = EntitySystem.Get<BotanySystem>();
|
|
||||||
|
|
||||||
if (Harvest && !Dead)
|
|
||||||
{
|
|
||||||
if (_entMan.TryGetComponent(user, out HandsComponent? hands))
|
|
||||||
{
|
|
||||||
if (!botanySystem.CanHarvest(Seed, hands.ActiveHandEntity))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (!botanySystem.CanHarvest(Seed))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
botanySystem.Harvest(Seed, user, YieldMod);
|
|
||||||
AfterHarvest();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Dead) return false;
|
|
||||||
|
|
||||||
RemovePlant();
|
|
||||||
AfterHarvest();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AutoHarvest()
|
|
||||||
{
|
|
||||||
if (Seed == null || !Harvest)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var botanySystem = EntitySystem.Get<BotanySystem>();
|
|
||||||
|
|
||||||
botanySystem.AutoHarvest(Seed, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
|
|
||||||
AfterHarvest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AfterHarvest()
|
|
||||||
{
|
|
||||||
Harvest = false;
|
|
||||||
_lastProduce = Age;
|
|
||||||
|
|
||||||
if (Seed?.HarvestRepeat == HarvestType.NoRepeat)
|
|
||||||
RemovePlant();
|
|
||||||
|
|
||||||
CheckLevelSanity();
|
|
||||||
UpdateSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckHealth()
|
|
||||||
{
|
|
||||||
if (Health <= 0)
|
|
||||||
{
|
|
||||||
Die();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Die()
|
|
||||||
{
|
|
||||||
Dead = true;
|
|
||||||
Harvest = false;
|
|
||||||
MutationLevel = 0;
|
|
||||||
YieldMod = 1;
|
|
||||||
MutationMod = 1;
|
|
||||||
ImproperLight = false;
|
|
||||||
ImproperHeat = false;
|
|
||||||
ImproperPressure = false;
|
|
||||||
WeedLevel += 1 * HydroponicsSpeedMultiplier;
|
|
||||||
PestLevel = 0;
|
|
||||||
UpdateSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemovePlant()
|
|
||||||
{
|
|
||||||
YieldMod = 1;
|
|
||||||
MutationMod = 1;
|
|
||||||
PestLevel = 0;
|
|
||||||
Seed = null;
|
|
||||||
Dead = false;
|
|
||||||
Age = 0;
|
|
||||||
Sampled = false;
|
|
||||||
Harvest = false;
|
|
||||||
ImproperLight = false;
|
|
||||||
ImproperPressure = false;
|
|
||||||
ImproperHeat = false;
|
|
||||||
|
|
||||||
UpdateSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AffectGrowth(int amount)
|
|
||||||
{
|
|
||||||
if (Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (amount > 0)
|
|
||||||
{
|
|
||||||
if (Age < Seed.Maturation)
|
|
||||||
Age += amount;
|
|
||||||
else if (!Harvest && Seed.Yield <= 0f)
|
|
||||||
_lastProduce -= amount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Age < Seed.Maturation)
|
|
||||||
SkipAging++;
|
|
||||||
else if (!Harvest && Seed.Yield <= 0f)
|
|
||||||
_lastProduce += amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdjustNutrient(float amount)
|
|
||||||
{
|
|
||||||
NutritionLevel += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdjustWater(float amount)
|
|
||||||
{
|
|
||||||
WaterLevel += amount;
|
|
||||||
|
|
||||||
// Water dilutes toxins.
|
|
||||||
if (amount > 0)
|
|
||||||
{
|
|
||||||
Toxins -= amount * 4f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateReagents()
|
|
||||||
{
|
|
||||||
var solutionSystem = EntitySystem.Get<SolutionContainerSystem>();
|
|
||||||
if (!solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var solution))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (solution.TotalVolume > 0 && MutationLevel < 25)
|
|
||||||
{
|
|
||||||
var amt = FixedPoint2.New(1);
|
|
||||||
foreach (var (reagentId, quantity) in solutionSystem.RemoveEachReagent(Owner, solution, amt))
|
|
||||||
{
|
|
||||||
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagentId);
|
|
||||||
reagentProto.ReactionPlant(Owner, new Solution.ReagentQuantity(reagentId, quantity), solution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckLevelSanity();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Mutate(float severity)
|
|
||||||
{
|
|
||||||
if (Seed != null)
|
|
||||||
{
|
|
||||||
EnsureUniqueSeed();
|
|
||||||
_entMan.System<MutationSystem>().MutateSeed(Seed, severity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSprite()
|
|
||||||
{
|
|
||||||
_updateSpriteAfterUpdate = false;
|
|
||||||
|
|
||||||
if (Seed != null && Seed.Bioluminescent)
|
|
||||||
{
|
|
||||||
var light = _entMan.EnsureComponent<PointLightComponent>(Owner);
|
|
||||||
light.Radius = Seed.BioluminescentRadius;
|
|
||||||
light.Color = Seed.BioluminescentColor;
|
|
||||||
light.CastShadows = false; // this is expensive, and botanists make lots of plants
|
|
||||||
light.Dirty();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_entMan.RemoveComponent<PointLightComponent>(Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_entMan.TryGetComponent<AppearanceComponent>(Owner, out var appearanceComponent))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Seed != null)
|
|
||||||
{
|
|
||||||
if (DrawWarnings)
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.HealthLight, Health <= (Seed.Endurance / 2f));
|
|
||||||
|
|
||||||
if (Dead)
|
|
||||||
{
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString());
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantState, "dead");
|
|
||||||
}
|
|
||||||
else if (Harvest)
|
|
||||||
{
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString());
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantState, "harvest");
|
|
||||||
}
|
|
||||||
else if (Age < Seed.Maturation)
|
|
||||||
{
|
|
||||||
var growthStage = Math.Max(1, (int) ((Age * Seed.GrowthStages) / Seed.Maturation));
|
|
||||||
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString());
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantState, $"stage-{growthStage}");
|
|
||||||
_lastProduce = Age;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString());
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantState, $"stage-{Seed.GrowthStages}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.PlantState, "");
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.HealthLight, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DrawWarnings) return;
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.WaterLight, WaterLevel <= 10);
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.NutritionLight, NutritionLevel <= 2);
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.AlertLight,
|
|
||||||
WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure ||
|
|
||||||
MissingGas > 0);
|
|
||||||
appearanceComponent.SetData(PlantHolderVisuals.HarvestLight, Harvest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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()
|
|
||||||
{
|
|
||||||
if (Seed != null && !Seed.Unique)
|
|
||||||
Seed = Seed.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceUpdateByExternalCause()
|
|
||||||
{
|
|
||||||
SkipAging++; // We're forcing an update cycle, so one age hasn't passed.
|
|
||||||
ForceUpdate = true;
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using Robust.Shared.Player;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Botany
|
namespace Content.Server.Botany.Systems
|
||||||
{
|
{
|
||||||
public sealed class BotanySwabSystem : EntitySystem
|
public sealed class BotanySwabSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
using Content.Server.Botany.Components;
|
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.Botany.Systems
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
public sealed partial class BotanySystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
|
||||||
|
|
||||||
private float _timer = 0f;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
InitializeSeeds();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
_timer += frameTime;
|
|
||||||
if (_timer < 3f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_timer -= 3f;
|
|
||||||
|
|
||||||
foreach (var plantHolder in EntityManager.EntityQuery<PlantHolderComponent>())
|
|
||||||
{
|
|
||||||
plantHolder.Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
using Content.Server.Mind.Commands;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Botany;
|
using Content.Shared.Botany;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -12,15 +13,24 @@ using Content.Shared.StepTrigger.Components;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Botany.Systems;
|
namespace Content.Server.Botany.Systems;
|
||||||
|
|
||||||
public sealed partial class BotanySystem
|
public sealed partial class BotanySystem : EntitySystem
|
||||||
{
|
{
|
||||||
public void InitializeSeeds()
|
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SeedComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<SeedComponent, ExaminedEvent>(OnExamined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +100,7 @@ public sealed partial class BotanySystem
|
|||||||
sprite.LayerSetSprite(0, new SpriteSpecifier.Rsi(proto.PlantRsi, "seed"));
|
sprite.LayerSetSprite(0, new SpriteSpecifier.Rsi(proto.PlantRsi, "seed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
string val = Loc.GetString("botany-seed-packet-name", ("seedName", proto.Name), ("seedNoun", proto.Noun));
|
var val = Loc.GetString("botany-seed-packet-name", ("seedName", proto.Name), ("seedNoun", proto.Noun));
|
||||||
MetaData(seed).EntityName = val;
|
MetaData(seed).EntityName = val;
|
||||||
|
|
||||||
return seed;
|
return seed;
|
||||||
@@ -150,10 +160,7 @@ public sealed partial class BotanySystem
|
|||||||
produce.Seed = proto;
|
produce.Seed = proto;
|
||||||
ProduceGrown(entity, produce);
|
ProduceGrown(entity, produce);
|
||||||
|
|
||||||
if (TryComp<AppearanceComponent>(entity, out var appearance))
|
_appearance.SetData(entity, ProduceVisuals.Potency, proto.Potency);
|
||||||
{
|
|
||||||
appearance.SetData(ProduceVisuals.Potency, proto.Potency);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.Mysterious)
|
if (proto.Mysterious)
|
||||||
{
|
{
|
||||||
@@ -168,7 +175,7 @@ public sealed partial class BotanySystem
|
|||||||
light.Radius = proto.BioluminescentRadius;
|
light.Radius = proto.BioluminescentRadius;
|
||||||
light.Color = proto.BioluminescentColor;
|
light.Color = proto.BioluminescentColor;
|
||||||
light.CastShadows = false; // this is expensive, and botanists make lots of plants
|
light.CastShadows = false; // this is expensive, and botanists make lots of plants
|
||||||
light.Dirty();
|
Dirty(light);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto.Slip)
|
if (proto.Slip)
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
|
using Content.Server.Atmos;
|
||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Server.Fluids.Components;
|
using Content.Server.Fluids.Components;
|
||||||
|
using Content.Server.Ghost.Roles.Components;
|
||||||
|
using Content.Server.Hands.Components;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Botany;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Random.Helpers;
|
using Content.Shared.Random.Helpers;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Botany.Systems
|
namespace Content.Server.Botany.Systems
|
||||||
@@ -21,11 +29,19 @@ namespace Content.Server.Botany.Systems
|
|||||||
public sealed class PlantHolderSystem : EntitySystem
|
public sealed class PlantHolderSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly BotanySystem _botanySystem = default!;
|
[Dependency] private readonly BotanySystem _botanySystem = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
|
[Dependency] private readonly MutationSystem _mutation = default!;
|
||||||
|
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||||
|
|
||||||
|
public const float HydroponicsSpeedMultiplier = 1f;
|
||||||
|
public const float HydroponicsConsumptionMultiplier = 4f;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -35,6 +51,20 @@ namespace Content.Server.Botany.Systems
|
|||||||
SubscribeLocalEvent<PlantHolderComponent, InteractHandEvent>(OnInteractHand);
|
SubscribeLocalEvent<PlantHolderComponent, InteractHandEvent>(OnInteractHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var plantHolder in EntityQuery<PlantHolderComponent>())
|
||||||
|
{
|
||||||
|
if (plantHolder.NextUpdate > _gameTiming.CurTime)
|
||||||
|
continue;
|
||||||
|
plantHolder.NextUpdate = _gameTiming.CurTime + plantHolder.UpdateDelay;
|
||||||
|
|
||||||
|
Update(plantHolder.Owner, plantHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args)
|
private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args)
|
||||||
{
|
{
|
||||||
if (!args.IsInDetailsRange)
|
if (!args.IsInDetailsRange)
|
||||||
@@ -51,12 +81,14 @@ namespace Content.Server.Botany.Systems
|
|||||||
("toBeForm", component.Seed.DisplayName.EndsWith('s') ? "are" : "is")));
|
("toBeForm", component.Seed.DisplayName.EndsWith('s') ? "are" : "is")));
|
||||||
|
|
||||||
if (component.Health <= component.Seed.Endurance / 2)
|
if (component.Health <= component.Seed.Endurance / 2)
|
||||||
|
{
|
||||||
args.PushMarkup(Loc.GetString(
|
args.PushMarkup(Loc.GetString(
|
||||||
"plant-holder-component-something-already-growing-low-health-message",
|
"plant-holder-component-something-already-growing-low-health-message",
|
||||||
("healthState",
|
("healthState",
|
||||||
Loc.GetString(component.Age > component.Seed.Lifespan
|
Loc.GetString(component.Age > component.Seed.Lifespan
|
||||||
? "plant-holder-component-plant-old-adjective"
|
? "plant-holder-component-plant-old-adjective"
|
||||||
: "plant-holder-component-plant-unhealthy-adjective"))));
|
: "plant-holder-component-plant-unhealthy-adjective"))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -114,8 +146,8 @@ namespace Content.Server.Botany.Systems
|
|||||||
|
|
||||||
EntityManager.QueueDeleteEntity(args.Used);
|
EntityManager.QueueDeleteEntity(args.Used);
|
||||||
|
|
||||||
component.CheckLevelSanity();
|
CheckLevelSanity(uid, component);
|
||||||
component.UpdateSprite();
|
UpdateSprite(uid, component);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -134,7 +166,7 @@ namespace Content.Server.Botany.Systems
|
|||||||
_popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message",
|
_popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message",
|
||||||
("otherName", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User));
|
("otherName", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User));
|
||||||
component.WeedLevel = 0;
|
component.WeedLevel = 0;
|
||||||
component.UpdateSprite();
|
UpdateSprite(uid, component);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -152,7 +184,7 @@ namespace Content.Server.Botany.Systems
|
|||||||
("name", Comp<MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium);
|
("name", Comp<MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium);
|
||||||
_popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message",
|
_popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message",
|
||||||
("name", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User));
|
("name", Comp<MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User));
|
||||||
component.RemovePlant();
|
RemovePlant(uid, component);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,17 +196,15 @@ namespace Content.Server.Botany.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution)
|
if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution)
|
||||||
&& _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution) && TryComp(args.Used, out SprayComponent? spray))
|
&& _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution)
|
||||||
|
&& TryComp(args.Used, out SprayComponent? spray))
|
||||||
{
|
{
|
||||||
var amount = FixedPoint2.New(1);
|
var amount = FixedPoint2.New(1);
|
||||||
|
|
||||||
var targetEntity = uid;
|
var targetEntity = uid;
|
||||||
var solutionEntity = args.Used;
|
var solutionEntity = args.Used;
|
||||||
|
|
||||||
|
_audio.PlayPvs(spray.SpraySound, args.Used, AudioParams.Default.WithVariation(0.125f));
|
||||||
SoundSystem.Play(spray.SpraySound.GetSound(), Filter.Pvs(args.Used),
|
|
||||||
args.Used, AudioHelpers.WithVariation(0.125f));
|
|
||||||
|
|
||||||
|
|
||||||
var split =_solutionSystem.Drain(solutionEntity, solution, amount);
|
var split =_solutionSystem.Drain(solutionEntity, solution, amount);
|
||||||
|
|
||||||
@@ -191,7 +221,7 @@ namespace Content.Server.Botany.Systems
|
|||||||
|
|
||||||
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
|
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
|
||||||
|
|
||||||
component.ForceUpdateByExternalCause();
|
ForceUpdateByExternalCause(uid, component);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -227,14 +257,14 @@ namespace Content.Server.Botany.Systems
|
|||||||
component.Sampled = true;
|
component.Sampled = true;
|
||||||
|
|
||||||
// Just in case.
|
// Just in case.
|
||||||
component.CheckLevelSanity();
|
CheckLevelSanity(uid, component);
|
||||||
component.ForceUpdateByExternalCause();
|
ForceUpdateByExternalCause(uid, component);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasComp<SharpComponent>(args.Used))
|
if (HasComp<SharpComponent>(args.Used))
|
||||||
component.DoHarvest(args.User);
|
DoHarvest(uid, args.User, component);
|
||||||
|
|
||||||
if (TryComp<ProduceComponent?>(args.Used, out var produce))
|
if (TryComp<ProduceComponent?>(args.Used, out var produce))
|
||||||
{
|
{
|
||||||
@@ -252,7 +282,7 @@ namespace Content.Server.Botany.Systems
|
|||||||
_solutionSystem.TryAddSolution(args.Used, solution2,
|
_solutionSystem.TryAddSolution(args.Used, solution2,
|
||||||
_solutionSystem.SplitSolution(args.Used, solution2, solution2.TotalVolume));
|
_solutionSystem.SplitSolution(args.Used, solution2, solution2.TotalVolume));
|
||||||
|
|
||||||
component.ForceUpdateByExternalCause();
|
ForceUpdateByExternalCause(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityManager.QueueDeleteEntity(args.Used);
|
EntityManager.QueueDeleteEntity(args.Used);
|
||||||
@@ -261,7 +291,609 @@ namespace Content.Server.Botany.Systems
|
|||||||
|
|
||||||
private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args)
|
private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args)
|
||||||
{
|
{
|
||||||
component.DoHarvest(args.User);
|
DoHarvest(uid, args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.MutationLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weeds like water and nutrients! They may appear even if there's not a seed planted.
|
||||||
|
if (component.WaterLevel > 10 && component.NutritionLevel > 2 && _random.Prob(component.Seed == null ? 0.05f : 0.01f))
|
||||||
|
{
|
||||||
|
component.WeedLevel += 1 * HydroponicsSpeedMultiplier * component.WeedCoefficient;
|
||||||
|
|
||||||
|
if (component.DrawWarnings)
|
||||||
|
component.UpdateSpriteAfterUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
{
|
||||||
|
component.WaterLevel -= MathF.Max(0f,
|
||||||
|
component.Seed.NutrientConsumption * 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the plant is not starving.
|
||||||
|
if (_random.Prob(0.35f))
|
||||||
|
{
|
||||||
|
if (component.NutritionLevel > 2)
|
||||||
|
{
|
||||||
|
component.Health += healthMod;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AffectGrowth(uid, -1, component);
|
||||||
|
component.Health -= healthMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.DrawWarnings)
|
||||||
|
component.UpdateSpriteAfterUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the plant is not thirsty.
|
||||||
|
if (_random.Prob(0.35f))
|
||||||
|
{
|
||||||
|
if (component.WaterLevel > 10)
|
||||||
|
{
|
||||||
|
component.Health += healthMod;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AffectGrowth(uid, -1, component);
|
||||||
|
component.Health -= healthMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.DrawWarnings)
|
||||||
|
component.UpdateSpriteAfterUpdate = true;
|
||||||
|
}
|
||||||
|
var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas;
|
||||||
|
|
||||||
|
if (component.Seed.ConsumeGasses.Count > 0)
|
||||||
|
{
|
||||||
|
component.MissingGas = 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!
|
||||||
|
{
|
||||||
|
_botanySystem.SpawnSeedPacket(component.Seed, Transform(uid).Coordinates);
|
||||||
|
RemovePlant(uid, component);
|
||||||
|
component.ForceUpdate = true;
|
||||||
|
Update(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Seed.Sentient)
|
||||||
|
{
|
||||||
|
var comp = EnsureComp<GhostTakeoverAvailableComponent>(uid);
|
||||||
|
comp.RoleName = MetaData(uid).EntityName;
|
||||||
|
comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!_botanySystem.CanHarvest(component.Seed, hands.ActiveHandEntity))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!_botanySystem.CanHarvest(component.Seed))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_botanySystem.Harvest(component.Seed, user, component.YieldMod);
|
||||||
|
AfterHarvest(plantholder, component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component.Dead)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RemovePlant(plantholder, component);
|
||||||
|
AfterHarvest(plantholder, component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AutoHarvest(EntityUid uid, PlantHolderComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Seed == null || !component.Harvest)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_botanySystem.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;
|
||||||
|
|
||||||
|
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.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 (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (solution.TotalVolume > 0 && component.MutationLevel < 25)
|
||||||
|
{
|
||||||
|
var amt = FixedPoint2.New(1);
|
||||||
|
foreach (var (reagentId, quantity) in _solutionSystem.RemoveEachReagent(uid, solution, amt))
|
||||||
|
{
|
||||||
|
var reagentProto = _prototype.Index<ReagentPrototype>(reagentId);
|
||||||
|
reagentProto.ReactionPlant(uid, new Solution.ReagentQuantity(reagentId, quantity), 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(component.Seed, severity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSprite(EntityUid uid, PlantHolderComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.UpdateSpriteAfterUpdate = false;
|
||||||
|
|
||||||
|
if (component.Seed != null && component.Seed.Bioluminescent)
|
||||||
|
{
|
||||||
|
var light = EnsureComp<PointLightComponent>(uid);
|
||||||
|
light.Radius = component.Seed.BioluminescentRadius;
|
||||||
|
light.Color = component.Seed.BioluminescentColor;
|
||||||
|
light.CastShadows = false; // this is expensive, and botanists make lots of plants
|
||||||
|
Dirty(light);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemComp<PointLightComponent>(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = Math.Max(1, (int) (component.Age * component.Seed.GrowthStages / component.Seed.Maturation));
|
||||||
|
|
||||||
|
_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 <= 10, app);
|
||||||
|
_appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 2, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
||||||
{
|
{
|
||||||
@@ -9,8 +10,10 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
|
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
|
|
||||||
plantHolderComp.Health += Amount;
|
plantHolderComp.Health += Amount;
|
||||||
plantHolderComp.CheckHealth();
|
plantHolder.CheckHealth(args.SolutionEntity, plantHolderComp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
||||||
@@ -11,8 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
|
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plantHolderComp.AdjustNutrient(Amount);
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
return;
|
|
||||||
|
plantHolder.AdjustNutrient(args.SolutionEntity, Amount, plantHolderComp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
||||||
@@ -11,7 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
|
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plantHolderComp.AdjustWater(Amount);
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
|
|
||||||
|
plantHolder.AdjustWater(args.SolutionEntity, Amount, plantHolderComp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
||||||
@@ -11,7 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
|
if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plantHolderComp.AffectGrowth((int) Amount);
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
|
|
||||||
|
plantHolder.AffectGrowth(args.SolutionEntity, (int) Amount, plantHolderComp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
@@ -16,17 +17,20 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
plantHolderComp.Seed.Immutable)
|
plantHolderComp.Seed.Immutable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
|
|
||||||
var random = IoCManager.Resolve<IRobustRandom>();
|
var random = IoCManager.Resolve<IRobustRandom>();
|
||||||
|
|
||||||
if (random.Prob(0.1f))
|
if (random.Prob(0.1f))
|
||||||
{
|
{
|
||||||
plantHolderComp.EnsureUniqueSeed();
|
plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
|
||||||
plantHolderComp.Seed.Lifespan++;
|
plantHolderComp.Seed.Lifespan++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (random.Prob(0.1f))
|
if (random.Prob(0.1f))
|
||||||
{
|
{
|
||||||
plantHolderComp.EnsureUniqueSeed();
|
plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
|
||||||
plantHolderComp.Seed.Endurance++;
|
plantHolderComp.Seed.Endurance++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
@@ -25,11 +26,13 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
plantHolderComp.Seed.Immutable)
|
plantHolderComp.Seed.Immutable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
var plantHolder = args.EntityManager.System<PlantHolderSystem>();
|
||||||
var random = IoCManager.Resolve<IRobustRandom>();
|
var random = IoCManager.Resolve<IRobustRandom>();
|
||||||
|
|
||||||
if (plantHolderComp.Seed.Potency < PotencyLimit)
|
if (plantHolderComp.Seed.Potency < PotencyLimit)
|
||||||
{
|
{
|
||||||
plantHolderComp.EnsureUniqueSeed();
|
plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
|
||||||
plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit);
|
plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit);
|
||||||
|
|
||||||
if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold)
|
if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold)
|
||||||
@@ -40,7 +43,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
|
|||||||
else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f))
|
else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f))
|
||||||
{
|
{
|
||||||
// Too much of a good thing reduces yield
|
// Too much of a good thing reduces yield
|
||||||
plantHolderComp.EnsureUniqueSeed();
|
plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
|
||||||
plantHolderComp.Seed.Yield--;
|
plantHolderComp.Seed.Yield--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user