using Content.Shared.Cargo; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Lathe; using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Popups; using Content.Shared.Random.Helpers; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using Content.Shared.Research.Systems; using Content.Shared.Research.TechnologyDisk.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; namespace Content.Shared.Research.TechnologyDisk.Systems; public sealed class TechnologyDiskSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedResearchSystem _research = default!; [Dependency] private readonly SharedLatheSystem _lathe = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NameModifierSystem _nameModifier = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnExamine); SubscribeLocalEvent(OnPriceCalculation); SubscribeLocalEvent(OnRefreshNameModifiers); } private void OnMapInit(Entity ent, ref MapInitEvent args) { TryPickAndSetRecipe(ent); TrySetVisuals(ent); } /// /// Attempts to pick and set a random recipe as the chosen one. /// If the disk already has recipes, does nothing. /// private void TryPickAndSetRecipe(Entity ent) { if (ent.Comp.Recipes != null) return; int tier; if (ent.Comp.Tier.HasValue) { tier = ent.Comp.Tier.Value; } else { var weightedRandom = _protoMan.Index(ent.Comp.TierWeightPrototype); tier = int.Parse(weightedRandom.Pick(_random)); ent.Comp.Tier = tier; } // get a list of every distinct recipe in all the technologies. var bundles = new HashSet<(ProtoId recipe, ProtoId discipline)>(); foreach (var tech in _protoMan.EnumeratePrototypes()) { if (tech.Tier != tier) continue; if (ent.Comp.Discipline != null && tech.Discipline != ent.Comp.Discipline.Value) continue; foreach (var recipe in tech.RecipeUnlocks) { bundles.Add((recipe, tech.Discipline)); } } if (bundles.Count == 0) { Log.Error($"Failed to pick recipe for a tech disk: no suitable recipes were found"); return; } // pick one var bundle = _random.Pick(bundles); ent.Comp.Discipline = bundle.discipline; ent.Comp.Recipes = []; ent.Comp.Recipes.Add(bundle.recipe); Dirty(ent); _nameModifier.RefreshNameModifiers(ent.Owner); } /// /// Attempts to set tier and discipline visuals based on chosen tier and discipline. /// private void TrySetVisuals(Entity ent) { TrySetTierVisuals(ent); TrySetDisciplineVisuals(ent); } /// /// Attempts to set tier visuals based on chosen tier. /// private void TrySetTierVisuals(Entity ent) { if (ent.Comp.Tier is not { } tier) return; _appearance.SetData(ent.Owner, TechDiskVisuals.Tier, tier); } /// /// Attempts to set discipline visuals based on chosen discipline. /// private void TrySetDisciplineVisuals(Entity ent) { if (!_protoMan.Resolve(ent.Comp.Discipline, out var discipline)) return; _appearance.SetData(ent.Owner, TechDiskVisuals.Discipline, discipline.ID); } private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target is not { } target) return; if (!HasComp(target) || !TryComp(target, out var database)) return; if (ent.Comp.Recipes != null) { foreach (var recipe in ent.Comp.Recipes) { _research.AddLatheRecipe(target, recipe, database); } } _popup.PopupClient(Loc.GetString("tech-disk-inserted"), target, args.User); PredictedQueueDel(ent.Owner); args.Handled = true; } private void OnExamine(Entity ent, ref ExaminedEvent args) { if (ent.Comp is { Tier: not null, Discipline: not null } && _protoMan.Resolve(ent.Comp.Discipline, out var disciplineProto)) { var desc = Loc.GetString("tech-disk-examine-desc", ("tier", ent.Comp.Tier), ("branch", Loc.GetString(disciplineProto.Name)) ); args.PushMarkup(desc); } else { args.PushMarkup(Loc.GetString("tech-disk-examine-desc-unknown")); } var message = Loc.GetString("tech-disk-examine-none"); if (ent.Comp.Recipes != null && ent.Comp.Recipes.Count > 0) { var prototype = _protoMan.Index(ent.Comp.Recipes[0]); message = Loc.GetString("tech-disk-examine", ("result", _lathe.GetRecipeName(prototype))); if (ent.Comp.Recipes.Count > 1) //idk how to do this well. sue me. message += " " + Loc.GetString("tech-disk-examine-more"); } args.PushMarkup(message); } private void OnPriceCalculation(Entity ent, ref PriceCalculationEvent args) { if (ent.Comp.Tier is not { } tier) return; if (!ent.Comp.DiskPricePerTier.TryGetValue(tier, out var price)) return; args.Price = price; args.Handled = true; } private void OnRefreshNameModifiers(Entity entity, ref RefreshNameModifiersEvent args) { if (entity.Comp.Recipes != null) { foreach (var recipe in entity.Comp.Recipes) { var proto = _protoMan.Index(recipe); args.AddModifier("tech-disk-name-format", extraArgs: ("technology", _lathe.GetRecipeName(proto))); } } } } [Serializable, NetSerializable] public enum TechDiskVisuals : byte { Tier, Discipline }