using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.Botany; using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Interfaces; using Content.Shared.Utility; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; using YamlDotNet.RepresentationModel; namespace Content.Server.Botany { public enum HarvestType : byte { NoRepeat, Repeat, SelfHarvest, } /* public enum PlantSpread : byte { NoSpread, Creepers, Vines, } public enum PlantMutation : byte { NoMutation, Mutable, HighlyMutable, } public enum PlantCarnivorous : byte { NotCarnivorous, EatPests, EatLivingBeings, } public enum PlantJuicy : byte { NotJuicy, Juicy, Slippery, } */ public struct SeedChemQuantity { public int Min; public int Max; public int PotencyDivisor; } [Prototype("seed")] public class Seed : IPrototype, IIndexedPrototype, IExposeData { private const string SeedPrototype = "SeedBase"; public string ID { get; private set; } /// /// Unique identifier of this seed. Do NOT set this. /// public int Uid { get; internal set; } = -1; #region Tracking [ViewVariables] public string Name { get; set; } [ViewVariables] public string SeedName { get; set; } [ViewVariables] public string SeedNoun { get; set; } [ViewVariables] public string DisplayName { get; set; } [ViewVariables] public bool RoundStart { get; private set; } [ViewVariables] public bool Mysterious { get; set; } [ViewVariables] public bool Immutable { get; set; } #endregion #region Output [ViewVariables] public List ProductPrototypes { get; set; } [ViewVariables] public Dictionary Chemicals { get; set; } [ViewVariables] public Dictionary ConsumeGasses { get; set; } [ViewVariables]public Dictionary ExudeGasses { get; set; } #endregion #region Tolerances [ViewVariables] public float NutrientConsumption { get; set; } [ViewVariables] public float WaterConsumption { get; set; } [ViewVariables] public float IdealHeat { get; set; } [ViewVariables] public float HeatTolerance { get; set; } [ViewVariables] public float IdealLight { get; set; } [ViewVariables] public float LightTolerance { get; set; } [ViewVariables] public float ToxinsTolerance { get; set; } [ViewVariables] public float LowPressureTolerance { get; set; } [ViewVariables] public float HighPressureTolerance { get; set; } [ViewVariables] public float PestTolerance { get; set; } [ViewVariables] public float WeedTolerance { get; set; } #endregion #region General traits [ViewVariables] public float Endurance { get; set; } [ViewVariables] public int Yield { get; set; } [ViewVariables] public float Lifespan { get; set; } [ViewVariables] public float Maturation { get; set; } [ViewVariables] public float Production { get; set; } [ViewVariables] public int GrowthStages { get; set; } [ViewVariables] public HarvestType HarvestRepeat { get; set; } [ViewVariables] public float Potency { get; set; } // No, I'm not removing these. //public PlantSpread Spread { get; set; } //public PlantMutation Mutation { get; set; } //public float AlterTemperature { get; set; } //public PlantCarnivorous Carnivorous { get; set; } //public bool Parasite { get; set; } //public bool Hematophage { get; set; } //public bool Thorny { get; set; } //public bool Stinging { get; set; } public bool Ligneous { get; set; } // public bool Teleporting { get; set; } // public PlantJuicy Juicy { get; set; } #endregion #region Cosmetics [ViewVariables]public ResourcePath PlantRsi { get; set; } [ViewVariables] public string PlantIconState { get; set; } [ViewVariables] public bool Bioluminescent { get; set; } [ViewVariables] public Color BioluminescentColor { get; set; } [ViewVariables] public string SplatPrototype { get; set; } #endregion public void ExposeData(ObjectSerializer serializer) { serializer.DataField(this, x => x.ID, "id", string.Empty); serializer.DataField(this, x => x.Name, "name", string.Empty); serializer.DataField(this, x => x.SeedName, "seedName", string.Empty); serializer.DataField(this, x => x.SeedNoun, "seedNoun", "seeds"); serializer.DataField(this, x => x.DisplayName, "displayName", string.Empty); serializer.DataField(this, x => x.RoundStart, "roundStart", true); serializer.DataField(this, x => x.Mysterious, "mysterious", false); serializer.DataField(this, x => x.Immutable, "immutable", false); serializer.DataField(this, x => x.ProductPrototypes, "productPrototypes", new List()); serializer.DataField(this, x => x.Chemicals, "chemicals", new Dictionary()); serializer.DataField(this, x => x.ConsumeGasses, "consumeGasses", new Dictionary()); serializer.DataField(this, x => x.ExudeGasses, "exudeGasses", new Dictionary()); serializer.DataField(this, x => x.NutrientConsumption, "nutrientConsumption", 0.25f); serializer.DataField(this, x => x.WaterConsumption, "waterConsumption", 3f); serializer.DataField(this, x => x.IdealHeat, "idealHeat", 293f); serializer.DataField(this, x => x.HeatTolerance, "heatTolerance", 20f); serializer.DataField(this, x => x.IdealLight, "idealLight", 7f); serializer.DataField(this, x => x.LightTolerance, "lightTolerance", 5f); serializer.DataField(this, x => x.ToxinsTolerance, "toxinsTolerance", 4f); serializer.DataField(this, x => x.LowPressureTolerance, "lowPressureTolerance", 25f); serializer.DataField(this, x => x.HighPressureTolerance, "highPressureTolerance", 200f); serializer.DataField(this, x => x.PestTolerance, "pestTolerance", 5f); serializer.DataField(this, x => x.WeedTolerance, "weedTolerance", 5f); serializer.DataField(this, x => x.Endurance, "endurance", 100f); serializer.DataField(this, x => x.Yield, "yield", 0); serializer.DataField(this, x => x.Lifespan, "lifespan", 0f); serializer.DataField(this, x => x.Maturation, "maturation", 0f); serializer.DataField(this, x => x.Production, "production", 0f); serializer.DataField(this, x => x.GrowthStages, "growthStages", 6); serializer.DataField(this, x => x.HarvestRepeat, "harvestRepeat", HarvestType.NoRepeat); serializer.DataField(this, x => x.Potency, "potency", 1f); serializer.DataField(this, x => x.Ligneous, "ligneous", false); serializer.DataField(this, x => x.PlantRsi, "plantRsi", null); serializer.DataField(this, x => x.PlantIconState, "plantIconState", "produce"); serializer.DataField(this, x => x.Bioluminescent, "bioluminescent", false); serializer.DataField(this, x => x.BioluminescentColor, "bioluminescentColor", Color.White); serializer.DataField(this, x => x.SplatPrototype, "splatPrototype", string.Empty); } public void LoadFrom(YamlMappingNode mapping) { var serializer = YamlObjectSerializer.NewReader(mapping); ExposeData(serializer); } public Seed Clone() { var newSeed = new Seed() { ID = ID, Name = Name, SeedName = SeedName, SeedNoun = SeedNoun, RoundStart = RoundStart, Mysterious = Mysterious, ProductPrototypes = new List(ProductPrototypes), Chemicals = new Dictionary(Chemicals), ConsumeGasses = new Dictionary(ConsumeGasses), ExudeGasses = new Dictionary(ExudeGasses), NutrientConsumption = NutrientConsumption, WaterConsumption = WaterConsumption, IdealHeat = IdealHeat, HeatTolerance = HeatTolerance, IdealLight = IdealLight, LightTolerance = LightTolerance, ToxinsTolerance = ToxinsTolerance, LowPressureTolerance = LowPressureTolerance, HighPressureTolerance = HighPressureTolerance, PestTolerance = PestTolerance, WeedTolerance = WeedTolerance, Endurance = Endurance, Yield = Yield, Lifespan = Lifespan, Maturation = Maturation, Production = Production, GrowthStages = GrowthStages, HarvestRepeat = HarvestRepeat, Potency = Potency, PlantRsi = PlantRsi, PlantIconState = PlantIconState, Bioluminescent = Bioluminescent, BioluminescentColor = BioluminescentColor, SplatPrototype = SplatPrototype, }; return newSeed; } public IEntity SpawnSeedPacket(EntityCoordinates transformCoordinates, IEntityManager entityManager = null) { entityManager ??= IoCManager.Resolve(); var seed = entityManager.SpawnEntity(SeedPrototype, transformCoordinates); var seedComp = seed.EnsureComponent(); seedComp.Seed = this; if (seed.TryGetComponent(out SpriteComponent sprite)) { // Seed state will always be seed. Blame the spriter if that's not the case! sprite.LayerSetSprite(0, new SpriteSpecifier.Rsi(PlantRsi, "seed")); } seed.Name = Loc.GetString($"packet of {SeedName} {SeedNoun}"); return seed; } private void AddToDatabase() { var plantSystem = EntitySystem.Get(); if (plantSystem.AddSeedToDatabase(this)) { Name = Uid.ToString(); } } public IEnumerable AutoHarvest(EntityCoordinates position, int yieldMod = 1) { if (position.IsValid(IoCManager.Resolve()) && ProductPrototypes != null && ProductPrototypes.Count > 0) return GenerateProduct(position, yieldMod); return Enumerable.Empty(); } public IEnumerable Harvest(IEntity user, int yieldMod = 1) { AddToDatabase(); if (user == null) return Enumerable.Empty(); if (ProductPrototypes == null || ProductPrototypes.Count == 0 || Yield <= 0) { user.PopupMessageCursor(Loc.GetString("You fail to harvest anything useful.")); return Enumerable.Empty(); } user.PopupMessageCursor(Loc.GetString($"You harvest from the {DisplayName}")); return GenerateProduct(user.Transform.Coordinates, yieldMod); } public IEnumerable GenerateProduct(EntityCoordinates position, int yieldMod = 1) { var totalYield = 0; if (Yield > -1) { if (yieldMod < 0) { yieldMod = 1; totalYield = Yield; } else { totalYield = Yield * yieldMod; } totalYield = Math.Max(1, totalYield); } var random = IoCManager.Resolve(); var entityManager = IoCManager.Resolve(); var products = new List(); for (var i = 0; i < totalYield; i++) { var product = random.Pick(ProductPrototypes); var entity = entityManager.SpawnEntity(product, position); entity.RandomOffset(0.25f); products.Add(entity); var produce = entity.EnsureComponent(); produce.Seed = this; produce.Grown(); if (Mysterious) { entity.Name += "?"; entity.Description += Loc.GetString(" On second thought, something about this one looks strange."); } } return products; } public Seed Diverge(bool modified) { return Clone(); } public bool CheckHarvest(IEntity user, IEntity held = null) { return (!Ligneous || (Ligneous && held != null && held.HasComponent())); } } }