using Content.Shared.Database; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using JetBrains.Annotations; namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { /// /// Syncs the primary entity's database to that of the secondary entity's database. /// public void Sync(EntityUid primaryUid, EntityUid otherUid, TechnologyDatabaseComponent? primaryDb = null, TechnologyDatabaseComponent? otherDb = null) { if (!Resolve(primaryUid, ref primaryDb) || !Resolve(otherUid, ref otherDb)) return; primaryDb.MainDiscipline = otherDb.MainDiscipline; primaryDb.CurrentTechnologyCards = otherDb.CurrentTechnologyCards; primaryDb.SupportedDisciplines = otherDb.SupportedDisciplines; primaryDb.UnlockedTechnologies = otherDb.UnlockedTechnologies; primaryDb.UnlockedRecipes = otherDb.UnlockedRecipes; Dirty(primaryUid, primaryDb); var ev = new TechnologyDatabaseSynchronizedEvent(); RaiseLocalEvent(primaryUid, ref ev); } /// /// If there's a research client component attached to the owner entity, /// and the research client is connected to a research server, this method /// syncs against the research server, and the server against the local database. /// /// Whether it could sync or not public void SyncClientWithServer(EntityUid uid, TechnologyDatabaseComponent? databaseComponent = null, ResearchClientComponent? clientComponent = null) { if (!Resolve(uid, ref databaseComponent, ref clientComponent, false)) return; if (!TryComp(clientComponent.Server, out var serverDatabase)) return; Sync(uid, clientComponent.Server.Value, databaseComponent, serverDatabase); } /// /// Tries to add a technology to a database, checking if it is able to /// /// If the technology was successfully added public bool UnlockTechnology(EntityUid client, string prototypeid, EntityUid user, ResearchClientComponent? component = null, TechnologyDatabaseComponent? clientDatabase = null) { if (!PrototypeManager.TryIndex(prototypeid, out var prototype)) return false; return UnlockTechnology(client, prototype, user, component, clientDatabase); } /// /// Tries to add a technology to a database, checking if it is able to /// /// If the technology was successfully added public bool UnlockTechnology(EntityUid client, TechnologyPrototype prototype, EntityUid user, ResearchClientComponent? component = null, TechnologyDatabaseComponent? clientDatabase = null) { if (!Resolve(client, ref component, ref clientDatabase, false)) return false; if (!TryGetClientServer(client, out var serverEnt, out _, component)) return false; if (!CanServerUnlockTechnology(client, prototype, clientDatabase, component)) return false; AddTechnology(serverEnt.Value, prototype); TrySetMainDiscipline(prototype, serverEnt.Value); ModifyServerPoints(serverEnt.Value, -prototype.Cost); UpdateTechnologyCards(serverEnt.Value); _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} unlocked {prototype.ID} (discipline: {prototype.Discipline}, tier: {prototype.Tier}) at {ToPrettyString(client)}, for server {ToPrettyString(serverEnt.Value)}."); return true; } /// /// Adds a technology to the database without checking if it could be unlocked. /// [PublicAPI] public void AddTechnology(EntityUid uid, string technology, TechnologyDatabaseComponent? component = null) { if (!Resolve(uid, ref component)) return; if (!PrototypeManager.TryIndex(technology, out var prototype)) return; AddTechnology(uid, prototype, component); } /// /// Adds a technology to the database without checking if it could be unlocked. /// public void AddTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? component = null) { if (!Resolve(uid, ref component)) return; //todo this needs to support some other stuff, too foreach (var generic in technology.GenericUnlocks) { if (generic.PurchaseEvent != null) RaiseLocalEvent(generic.PurchaseEvent); } component.UnlockedTechnologies.Add(technology.ID); foreach (var unlock in technology.RecipeUnlocks) { if (component.UnlockedRecipes.Contains(unlock)) continue; component.UnlockedRecipes.Add(unlock); } Dirty(uid, component); var ev = new TechnologyDatabaseModifiedEvent(); RaiseLocalEvent(uid, ref ev); } /// /// Returns whether a technology can be unlocked on this database, /// taking parent technologies into account. /// /// Whether it could be unlocked or not public bool CanServerUnlockTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? database = null, ResearchClientComponent? client = null) { if (!Resolve(uid, ref client, ref database, false)) return false; if (!TryGetClientServer(uid, out _, out var serverComp, client)) return false; if (!IsTechnologyAvailable(database, technology)) return false; if (technology.Cost > serverComp.Points) return false; return true; } private void OnDatabaseRegistrationChanged(EntityUid uid, TechnologyDatabaseComponent component, ref ResearchRegistrationChangedEvent args) { if (args.Server != null) return; component.MainDiscipline = null; component.CurrentTechnologyCards = new List(); component.SupportedDisciplines = new List(); component.UnlockedTechnologies = new List(); component.UnlockedRecipes = new List(); Dirty(uid, component); } }