using System.Linq; using Content.Shared.Hands.Components; using Content.Shared.Interaction.Components; using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Whitelist; using Robust.Shared.Containers; namespace Content.Server.Silicons.Borgs; /// public sealed partial class BorgSystem { public void InitializeModules() { SubscribeLocalEvent(OnModuleGotInserted); SubscribeLocalEvent(OnModuleGotRemoved); SubscribeLocalEvent(OnSelectableInstalled); SubscribeLocalEvent(OnSelectableUninstalled); SubscribeLocalEvent(OnSelectableAction); SubscribeLocalEvent(OnProvideItemStartup); SubscribeLocalEvent(OnItemModuleSelected); SubscribeLocalEvent(OnItemModuleUnselected); } private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, EntGotInsertedIntoContainerMessage args) { var chassis = args.Container.Owner; if (!TryComp(chassis, out var chassisComp) || args.Container != chassisComp.ModuleContainer || !Toggle.IsActivated(chassis)) return; if (!_powerCell.HasDrawCharge(uid)) return; InstallModule(chassis, uid, chassisComp, component); } private void OnModuleGotRemoved(EntityUid uid, BorgModuleComponent component, EntGotRemovedFromContainerMessage args) { var chassis = args.Container.Owner; if (!TryComp(chassis, out var chassisComp) || args.Container != chassisComp.ModuleContainer) return; UninstallModule(chassis, uid, chassisComp, component); } private void OnProvideItemStartup(EntityUid uid, ItemBorgModuleComponent component, ComponentStartup args) { Container.EnsureContainer(uid, component.HoldingContainer); } private void OnSelectableInstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleInstalledEvent args) { var chassis = args.ChassisEnt; if (_actions.AddAction(chassis, ref component.ModuleSwapActionEntity, out var action, component.ModuleSwapActionId, uid)) { var actEnt = (component.ModuleSwapActionEntity.Value, action); _actions.SetEntityIcon(actEnt, uid); if (TryComp(uid, out var moduleIconComp)) _actions.SetIcon(actEnt, moduleIconComp.Icon); /// Set a custom name and description on the action. The borg module action prototypes are shared across /// all modules. Extract localized names, then populate variables with the info from the module itself. var moduleName = Name(uid); var actionMetaData = MetaData(component.ModuleSwapActionEntity.Value); var instanceName = Loc.GetString("borg-module-action-name", ("moduleName", moduleName)); _metaData.SetEntityName(component.ModuleSwapActionEntity.Value, instanceName, actionMetaData); var instanceDesc = Loc.GetString("borg-module-action-description", ("moduleName", moduleName)); _metaData.SetEntityDescription(component.ModuleSwapActionEntity.Value, instanceDesc, actionMetaData); } if (!TryComp(chassis, out BorgChassisComponent? chassisComp)) return; if (chassisComp.SelectedModule == null) SelectModule(chassis, uid, chassisComp, component); } private void OnSelectableUninstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleUninstalledEvent args) { var chassis = args.ChassisEnt; _actions.RemoveProvidedActions(chassis, uid); if (!TryComp(chassis, out BorgChassisComponent? chassisComp)) return; if (chassisComp.SelectedModule == uid) UnselectModule(chassis, chassisComp); } private void OnSelectableAction(EntityUid uid, SelectableBorgModuleComponent component, BorgModuleActionSelectedEvent args) { var chassis = args.Performer; if (!TryComp(chassis, out var chassisComp)) return; var selected = chassisComp.SelectedModule; args.Handled = true; UnselectModule(chassis, chassisComp); if (selected != uid) { SelectModule(chassis, uid, chassisComp, component); } } /// /// Selects a module, enabling the borg to use its provided abilities. /// public void SelectModule(EntityUid chassis, EntityUid moduleUid, BorgChassisComponent? chassisComp = null, SelectableBorgModuleComponent? selectable = null, BorgModuleComponent? moduleComp = null) { if (LifeStage(chassis) >= EntityLifeStage.Terminating) return; if (!Resolve(chassis, ref chassisComp)) return; if (!Resolve(moduleUid, ref moduleComp) || !moduleComp.Installed || moduleComp.InstalledEntity != chassis) { Log.Error($"{ToPrettyString(chassis)} attempted to select uninstalled module {ToPrettyString(moduleUid)}"); return; } if (selectable == null && !HasComp(moduleUid)) { Log.Error($"{ToPrettyString(chassis)} attempted to select invalid module {ToPrettyString(moduleUid)}"); return; } if (!chassisComp.ModuleContainer.Contains(moduleUid)) { Log.Error($"{ToPrettyString(chassis)} does not contain the installed module {ToPrettyString(moduleUid)}"); return; } if (chassisComp.SelectedModule != null) return; if (chassisComp.SelectedModule == moduleUid) return; UnselectModule(chassis, chassisComp); var ev = new BorgModuleSelectedEvent(chassis); RaiseLocalEvent(moduleUid, ref ev); chassisComp.SelectedModule = moduleUid; Dirty(chassis, chassisComp); } /// /// Unselects a module, removing its provided abilities /// public void UnselectModule(EntityUid chassis, BorgChassisComponent? chassisComp = null) { if (LifeStage(chassis) >= EntityLifeStage.Terminating) return; if (!Resolve(chassis, ref chassisComp)) return; if (chassisComp.SelectedModule == null) return; var ev = new BorgModuleUnselectedEvent(chassis); RaiseLocalEvent(chassisComp.SelectedModule.Value, ref ev); chassisComp.SelectedModule = null; Dirty(chassis, chassisComp); } private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args) { ProvideItems(args.Chassis, uid, component: component); } private void OnItemModuleUnselected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleUnselectedEvent args) { RemoveProvidedItems(args.Chassis, uid, component: component); } private void ProvideItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) { if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) return; if (!TryComp(chassis, out var hands)) return; if (!_container.TryGetContainer(uid, component.HoldingContainer, out var container)) return; var xform = Transform(chassis); for (var i = 0; i < component.Hands.Count; i++) { var hand = component.Hands[i]; var handId = $"{uid}-hand-{i}"; _hands.AddHand((chassis, hands), handId, hand.Hand); EntityUid? item = null; if (component.StoredItems is not null) { if (component.StoredItems.TryGetValue(handId, out var storedItem)) { item = storedItem; _container.Remove(storedItem, container, force: true); } } else if (hand.Item is { } itemProto) { item = Spawn(itemProto, xform.Coordinates); } if (item is { } pickUp) { _hands.DoPickup(chassis, handId, pickUp, hands); if (!hand.ForceRemovable && hand.Hand.Whitelist == null && hand.Hand.Blacklist == null) { EnsureComp(pickUp); } } } Dirty(uid, component); } private void RemoveProvidedItems(EntityUid chassis, EntityUid uid, BorgChassisComponent? chassisComponent = null, ItemBorgModuleComponent? component = null) { if (!Resolve(chassis, ref chassisComponent) || !Resolve(uid, ref component)) return; if (!TryComp(chassis, out var hands)) return; if (!_container.TryGetContainer(uid, component.HoldingContainer, out var container)) return; if (TerminatingOrDeleted(uid)) return; component.StoredItems ??= new(); for (var i = 0; i < component.Hands.Count; i++) { var handId = $"{uid}-hand-{i}"; if (_hands.TryGetHeldItem(chassis, handId, out var held)) { RemComp(held.Value); _container.Insert(held.Value, container); component.StoredItems[handId] = held.Value; } else { component.StoredItems.Remove(handId); } _hands.RemoveHand(chassis, handId); } Dirty(uid, component); } /// /// Checks if a given module can be inserted into a borg /// public bool CanInsertModule(EntityUid uid, EntityUid module, BorgChassisComponent? component = null, BorgModuleComponent? moduleComponent = null, EntityUid? user = null) { if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) return false; if (component.ModuleContainer.ContainedEntities.Count >= component.MaxModules) { if (user != null) Popup.PopupEntity(Loc.GetString("borg-module-too-many"), uid, user.Value); return false; } if (_whitelistSystem.IsWhitelistFail(component.ModuleWhitelist, module)) { if (user != null) Popup.PopupEntity(Loc.GetString("borg-module-whitelist-deny"), uid, user.Value); return false; } if (TryComp(module, out var itemModuleComp)) { foreach (var containedModuleUid in component.ModuleContainer.ContainedEntities) { if (!TryComp(containedModuleUid, out var containedItemModuleComp)) continue; if (containedItemModuleComp.Hands.Count == itemModuleComp.Hands.Count && containedItemModuleComp.Hands.All(itemModuleComp.Hands.Contains)) { if (user != null) Popup.PopupEntity(Loc.GetString("borg-module-duplicate"), uid, user.Value); return false; } } } return true; } /// /// Check if a module can be removed from a borg. /// /// The borg that the module is being removed from. /// The module to remove from the borg. /// The user attempting to remove the module. /// True if the module can be removed. public bool CanRemoveModule( Entity borg, Entity module, EntityUid? user = null) { if (module.Comp.DefaultModule) return false; return true; } /// /// Installs and activates all modules currently inside the borg's module container /// public void InstallAllModules(EntityUid uid, BorgChassisComponent? component = null) { if (!Resolve(uid, ref component)) return; var query = GetEntityQuery(); foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) { if (!query.TryGetComponent(moduleEnt, out var moduleComp)) continue; InstallModule(uid, moduleEnt, component, moduleComp); } } /// /// Deactivates all modules currently inside the borg's module container /// /// /// public void DisableAllModules(EntityUid uid, BorgChassisComponent? component = null) { if (!Resolve(uid, ref component)) return; var query = GetEntityQuery(); foreach (var moduleEnt in new List(component.ModuleContainer.ContainedEntities)) { if (!query.TryGetComponent(moduleEnt, out var moduleComp)) continue; UninstallModule(uid, moduleEnt, component, moduleComp); } } /// /// Installs a single module into a borg. /// public void InstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) { if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) return; if (moduleComponent.Installed) return; moduleComponent.InstalledEntity = uid; var ev = new BorgModuleInstalledEvent(uid); RaiseLocalEvent(module, ref ev); } /// /// Uninstalls a single module from a borg. /// public void UninstallModule(EntityUid uid, EntityUid module, BorgChassisComponent? component, BorgModuleComponent? moduleComponent = null) { if (!Resolve(uid, ref component) || !Resolve(module, ref moduleComponent)) return; if (!moduleComponent.Installed) return; moduleComponent.InstalledEntity = null; var ev = new BorgModuleUninstalledEvent(uid); RaiseLocalEvent(module, ref ev); } /// /// Sets . /// /// The borg to modify. /// The new max module count. public void SetMaxModules(Entity ent, int maxModules) { ent.Comp.MaxModules = maxModules; } /// /// Sets . /// /// The borg to modify. /// The new module whitelist. public void SetModuleWhitelist(Entity ent, EntityWhitelist? whitelist) { ent.Comp.ModuleWhitelist = whitelist; } }