From 8710eba79542da466e7a9494a9273fccfeaaea8e Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:51:52 +0300 Subject: [PATCH 01/13] first --- .../DNAConsole/DNAConsoleBoundInterface.cs | 49 ++++ .../_White/DNAConsole/DNAConsoleWindow.xaml | 26 ++ .../DNAConsole/DNAConsoleWindow.xaml.cs | 86 +++++++ .../DNAModifier/DNAModifierComponent.cs | 8 + .../Components/ActiveModifierComponent.cs | 9 + .../Components/DNAConsoleComponent.cs | 17 ++ .../Components/DNAModifierComponent.cs | 16 ++ .../Genetics/Components/GenomeComponent.cs | 38 +++ .../_White/Genetics/DNAConsoleSystem.cs | 201 +++++++++++++++ .../_White/Genetics/DNAModifierSystem.cs | 237 ++++++++++++++++++ .../_White/Genetics/GenomeSystem.cs | 179 +++++++++++++ .../_White/Genetics/MutationSystem.cs | 70 ++++++ .../_White/DNAConsole/SharedDNAConsole.cs | 54 ++++ .../DNAModifier/SharedDNAModifierComponent.cs | 22 ++ .../_White/Genetics/GenesPrototype.cs | 26 ++ Content.Shared/_White/Genetics/Genome.cs | 184 ++++++++++++++ .../_White/Genetics/GenomeLayout.cs | 89 +++++++ .../_White/Genetics/GenomePrototype.cs | 25 ++ .../Genetics/MutationCollectionPrototype.cs | 21 ++ .../_White/Genetics/MutationPrototype.cs | 30 +++ .../Prototypes/White/genetic_prototypes.yml | 112 +++++++++ .../Devices/dna_modifier.rsi/closed.png | Bin 0 -> 1901 bytes .../dna_modifier.rsi/closed_unpowered.png | Bin 0 -> 1816 bytes .../Devices/dna_modifier.rsi/idle_unlit.png | Bin 0 -> 338 bytes .../Devices/dna_modifier.rsi/maint_unlit.png | Bin 0 -> 142 bytes .../Devices/dna_modifier.rsi/meta.json | 67 +++++ .../Devices/dna_modifier.rsi/occupied.png | Bin 0 -> 5126 bytes .../dna_modifier.rsi/occupied_unlit.png | Bin 0 -> 88 bytes .../Devices/dna_modifier.rsi/off_unlit.png | Bin 0 -> 112 bytes .../Objects/Devices/dna_modifier.rsi/open.png | Bin 0 -> 1920 bytes .../dna_modifier.rsi/open_unpowered.png | Bin 0 -> 1856 bytes .../Devices/dna_modifier.rsi/red_unlit.png | Bin 0 -> 160 bytes 32 files changed, 1566 insertions(+) create mode 100644 Content.Client/_White/DNAConsole/DNAConsoleBoundInterface.cs create mode 100644 Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml create mode 100644 Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml.cs create mode 100644 Content.Client/_White/DNAModifier/DNAModifierComponent.cs create mode 100644 Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs create mode 100644 Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs create mode 100644 Content.Server/_White/Genetics/Components/DNAModifierComponent.cs create mode 100644 Content.Server/_White/Genetics/Components/GenomeComponent.cs create mode 100644 Content.Server/_White/Genetics/DNAConsoleSystem.cs create mode 100644 Content.Server/_White/Genetics/DNAModifierSystem.cs create mode 100644 Content.Server/_White/Genetics/GenomeSystem.cs create mode 100644 Content.Server/_White/Genetics/MutationSystem.cs create mode 100644 Content.Shared/_White/DNAConsole/SharedDNAConsole.cs create mode 100644 Content.Shared/_White/DNAModifier/SharedDNAModifierComponent.cs create mode 100644 Content.Shared/_White/Genetics/GenesPrototype.cs create mode 100644 Content.Shared/_White/Genetics/Genome.cs create mode 100644 Content.Shared/_White/Genetics/GenomeLayout.cs create mode 100644 Content.Shared/_White/Genetics/GenomePrototype.cs create mode 100644 Content.Shared/_White/Genetics/MutationCollectionPrototype.cs create mode 100644 Content.Shared/_White/Genetics/MutationPrototype.cs create mode 100644 Resources/Prototypes/White/genetic_prototypes.yml create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/closed.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/closed_unpowered.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/idle_unlit.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/maint_unlit.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/meta.json create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/occupied.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/occupied_unlit.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/off_unlit.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/open.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/open_unpowered.png create mode 100644 Resources/Textures/White/Objects/Devices/dna_modifier.rsi/red_unlit.png diff --git a/Content.Client/_White/DNAConsole/DNAConsoleBoundInterface.cs b/Content.Client/_White/DNAConsole/DNAConsoleBoundInterface.cs new file mode 100644 index 00000000000..06fd3f27859 --- /dev/null +++ b/Content.Client/_White/DNAConsole/DNAConsoleBoundInterface.cs @@ -0,0 +1,49 @@ +using Content.Shared.DNAConsole; +using JetBrains.Annotations; + +namespace Content.Client.DNAConsole.UI +{ + [UsedImplicitly] + public sealed class DNAConsoleBoundUserInterface : BoundUserInterface + { + [ViewVariables] + private DNAConsoleWindow? _window; + + public DNAConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _window = new DNAConsoleWindow + { + Title = "DNAConsole" + }; + _window.OnClose += Close; + // _window.ModifierButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone)); + _window.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + _window?.Populate((DNAConsoleBoundUserInterfaceState) state); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + + if (_window != null) + { + _window.OnClose -= Close; + //_window.DNAButton.OnPressed -= _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone)); + } + _window?.Dispose(); + } + } +} diff --git a/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml b/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml new file mode 100644 index 00000000000..21f7722829a --- /dev/null +++ b/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml.cs b/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml.cs new file mode 100644 index 00000000000..b5e607b1fbc --- /dev/null +++ b/Content.Client/_White/DNAConsole/DNAConsoleWindow.xaml.cs @@ -0,0 +1,86 @@ +using Content.Client.Message; +using Content.Shared.DNAConsole; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.DNAConsole; + +[GenerateTypedNameReferences] +public sealed partial class DNAConsoleWindow : DefaultWindow +{ + +public DNAConsoleWindow() +{ + RobustXamlLoader.Load(this); +} + +private DNAConsoleBoundUserInterfaceState? _lastUpdate; + +public void Populate(DNAConsoleBoundUserInterfaceState state) + { + _lastUpdate = state; + // BUILD SCANNER UI + if (state.ModifierConnected) + { + if (!state.ModifierInRange) + { + DNAModifierFar.Visible = true; + DNAConsoleContent.Visible = false; + DNAModifierMissing.Visible = false; + return; + } + + DNAConsoleContent.Visible = true; + DNAModifierFar.Visible = false; + DNAModifierMissing.Visible = false; + + switch (state.ModifierStatus) + { + case ModifierStatus.Ready: + ModifierActivity.Text = (Loc.GetString("dna-console-component-msg-ready")); + break; + case ModifierStatus.ModifierOccupied: + ModifierActivity.Text = (Loc.GetString("dna-console-component-msg-occupied")); + break; + case ModifierStatus.ModifierEmpty: + ModifierActivity.Text = (Loc.GetString("dna-console-component-msg-empty")); + break; + case ModifierStatus.OccupantMetaphyiscal: + ModifierActivity.Text = (Loc.GetString("dna-console-component-msg-already-alive")); + break; + } + // Set label depending on if scanner is occupied or not. + ModifierInfoLabel.SetMarkup(state.ModifierBodyInfo != null ? + Loc.GetString("dna-console-window-scanner-id", ("modifierOccupantName", state.ModifierBodyInfo)) : + Loc.GetString("dna-console-window-id-blank")); + } + else + { + // Scanner is missing, set error message visible + DNAConsoleContent.Visible = false; + DNAModifierFar.Visible = false; + DNAModifierMissing.Visible = true; + } + + // BUILD ClONER UI + if (state.ModifierConnected) + { + if (!state.ModifierInRange) + { + DNATest.Visible = false; + return; + } + + DNATest.Visible = true; + } + else + { + // Clone pod is missing, set error message visible + DNATest.Visible = false; + } + } + +} + diff --git a/Content.Client/_White/DNAModifier/DNAModifierComponent.cs b/Content.Client/_White/DNAModifier/DNAModifierComponent.cs new file mode 100644 index 00000000000..60fb6ace3de --- /dev/null +++ b/Content.Client/_White/DNAModifier/DNAModifierComponent.cs @@ -0,0 +1,8 @@ +using Content.Shared.DNAModifier; + +namespace Content.Client.DNAModifier; + +[RegisterComponent] +public sealed partial class DNAModifierComponent : SharedDNAModifierComponent +{ +} diff --git a/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs b/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs new file mode 100644 index 00000000000..61dc4e43038 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Genetics.Components; + +/// +/// Shrimply a tracking component for pods that are cloning. +/// +[RegisterComponent] +public sealed partial class ActiveModifierComponent : Component +{ +} diff --git a/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs b/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs new file mode 100644 index 00000000000..75e50087ec8 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs @@ -0,0 +1,17 @@ +namespace Content.Server.Genetics.Components +{ + [RegisterComponent] + public sealed partial class DNAConsoleComponent : Component + { + public const string ScannerPort = "DNAModifierSender"; + + [ViewVariables] + public EntityUid? Modifier = null; + + /// Maximum distance between console and one if its machines + [DataField("maxDistance")] + public float MaxDistance = 4f; + + public bool ModifierInRange = true; + } +} diff --git a/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs b/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs new file mode 100644 index 00000000000..29e5d6104b7 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.DNAModifier; +using Robust.Shared.Containers; + +namespace Content.Server.Genetics.Components +{ + [RegisterComponent] + public sealed partial class DNAModifierComponent : SharedDNAModifierComponent + { + public const string ScannerPort = "DNAModifierReceiver"; + public ContainerSlot BodyContainer = default!; + public EntityUid? ConnectedConsole; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float CloningFailChanceMultiplier = 1f; + } +} diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs new file mode 100644 index 00000000000..983ca83b750 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -0,0 +1,38 @@ +using Content.Shared.Genetics; +using Robust.Shared.Prototypes; + +namespace Content.Server.Genetics.Components; + +/// +/// Gives this entity a genome for traits to be passed on and potentially mutated. +/// Both of those must be handled by other systems, on its own it has no functionality. +/// +[RegisterComponent] +public sealed partial class GenomeComponent : Component +{ + /// + /// Name of the to create on init. + /// + [DataField(required: true)] + public ProtoId GenomeId = string.Empty; + + /// + /// Genome layout to use for this round and genome type. + /// Stored in . + /// + [ViewVariables] + public GenomeLayout Layout = new(); + + [ViewVariables] + public List Mutations = default!; + + /// + /// Genome bits themselves. + /// Data can be retrieved with comp.Layout.GetInt(comp.Genome, "name"), etc. + /// + /// + /// Completely empty by default, another system must use to load genes or copy from a parent. + /// + [DataField] + public Genome Genome = new(); +} diff --git a/Content.Server/_White/Genetics/DNAConsoleSystem.cs b/Content.Server/_White/Genetics/DNAConsoleSystem.cs new file mode 100644 index 00000000000..6a2b16396af --- /dev/null +++ b/Content.Server/_White/Genetics/DNAConsoleSystem.cs @@ -0,0 +1,201 @@ +using System.Linq; +using Content.Server.Administration.Logs; +using Content.Server.DeviceLinking.Systems; +using Content.Server.Genetics.Components; +using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Shared.Database; +using Content.Shared.DeviceLinking; +using Content.Shared.DeviceLinking.Events; +using Content.Shared.IdentityManagement; +using Content.Shared.Mind; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.UserInterface; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Server.Player; +using Content.Shared.DNAConsole; + +namespace Content.Server.Genetics +{ + [UsedImplicitly] + public sealed class CloningConsoleSystem : EntitySystem + { + [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnButtonPressed); + SubscribeLocalEvent(OnUIOpen); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnNewLink); + SubscribeLocalEvent(OnPortDisconnected); + SubscribeLocalEvent(OnAnchorChanged); + } + + private void OnInit(EntityUid uid, DNAConsoleComponent component, ComponentInit args) + { + _signalSystem.EnsureSourcePorts(uid, DNAConsoleComponent.ScannerPort); + } + private void OnButtonPressed(EntityUid uid, DNAConsoleComponent consoleComponent, UiButtonPressedMessage args) + { + if (!_powerReceiverSystem.IsPowered(uid)) + return; +/* +ПОСЛЕ ГЕНОВ + switch (args.Button) + { + case UiButton.Clone: + if (consoleComponent.Modifier != null) + TryClone(uid,consoleComponent.Modifier.Value, consoleComponent: consoleComponent); + break; + } +*/ + UpdateUserInterface(uid, consoleComponent); + } + + private void OnPowerChanged(EntityUid uid, DNAConsoleComponent component, ref PowerChangedEvent args) + { + UpdateUserInterface(uid, component); + } + + private void OnMapInit(EntityUid uid, DNAConsoleComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var receiver)) + return; + + foreach (var port in receiver.Outputs.Values.SelectMany(ports => ports)) + { + if (TryComp(port, out var scanner)) + { + component.Modifier = port; + scanner.ConnectedConsole = uid; + } + } + } + + private void OnNewLink(EntityUid uid, DNAConsoleComponent component, NewLinkEvent args) + { + if (TryComp(args.Sink, out var scanner) && args.SourcePort == DNAConsoleComponent.ScannerPort) + { + component.Modifier = args.Sink; + scanner.ConnectedConsole = uid; + } + + RecheckConnections(uid, component.Modifier, component); + } + + private void OnPortDisconnected(EntityUid uid, DNAConsoleComponent component, PortDisconnectedEvent args) + { + if (args.Port == DNAConsoleComponent.ScannerPort) + component.Modifier = null; + + UpdateUserInterface(uid, component); + } + + private void OnUIOpen(EntityUid uid, DNAConsoleComponent component, AfterActivatableUIOpenEvent args) + { + UpdateUserInterface(uid, component); + } + + private void OnAnchorChanged(EntityUid uid, DNAConsoleComponent component, ref AnchorStateChangedEvent args) + { + if (args.Anchored) + { + RecheckConnections(uid, component.Modifier, component); + return; + } + UpdateUserInterface(uid, component); + } + + public void UpdateUserInterface(EntityUid consoleUid, DNAConsoleComponent consoleComponent) + { + if (!_uiSystem.TryGetUi(consoleUid, DNAConsoleUiKey.Key, out var ui)) + return; + + if (!_powerReceiverSystem.IsPowered(consoleUid)) + { + _uiSystem.CloseAll(ui); + return; + } + + var newState = GetUserInterfaceState(consoleComponent); + _uiSystem.SetUiState(ui, newState); + } +/* + public void TryGens(EntityUid uid, DNAModifierComponent? scannerComp = null, DNAConsoleComponent? consoleComponent = null) + { + логика на гены, будет после добавления самих генов + } + +*/ + + public void RecheckConnections(EntityUid console, EntityUid? scanner, DNAConsoleComponent? consoleComp = null) + { + if (!Resolve(console, ref consoleComp)) + return; + + if (scanner != null) + { + Transform(scanner.Value).Coordinates.TryDistance(EntityManager, Transform((console)).Coordinates, out float scannerDistance); + consoleComp.ModifierInRange = scannerDistance <= consoleComp.MaxDistance; + } + + UpdateUserInterface(console, consoleComp); + } + private DNAConsoleBoundUserInterfaceState GetUserInterfaceState(DNAConsoleComponent consoleComponent) + { + ModifierStatus _modifierStatus = ModifierStatus.Ready; + + // modifier info + string scanBodyInfo = Loc.GetString("generic-unknown"); + bool modifierConnected = false; + bool modifierInRange = consoleComponent.ModifierInRange; + if (consoleComponent.Modifier != null && TryComp(consoleComponent.Modifier, out var scanner)) + { + modifierConnected = true; + EntityUid? scanBody = scanner.BodyContainer.ContainedEntity; + + // GET STATE + if (scanBody == null || !HasComp(scanBody)) + _modifierStatus = ModifierStatus.ModifierEmpty; + else + { + scanBodyInfo = MetaData(scanBody.Value).EntityName; + + if (!_mobStateSystem.IsDead(scanBody.Value)) + { + _modifierStatus = ModifierStatus.ModifierOccupantAlive; + } + } + + var modifierBodyInfo = Loc.GetString("generic-unknown"); + + if (HasComp(consoleComponent.Modifier)) + { + if(scanBody != null) + modifierBodyInfo = Identity.Name(scanBody.Value, EntityManager); + _modifierStatus = ModifierStatus.ModifierOccupied; + } + } + + return new DNAConsoleBoundUserInterfaceState( + scanBodyInfo, + _modifierStatus, + modifierConnected, + modifierInRange + ); + } + + } +} diff --git a/Content.Server/_White/Genetics/DNAModifierSystem.cs b/Content.Server/_White/Genetics/DNAModifierSystem.cs new file mode 100644 index 00000000000..d8c359677e2 --- /dev/null +++ b/Content.Server/_White/Genetics/DNAModifierSystem.cs @@ -0,0 +1,237 @@ +using Content.Server.Cloning; +using Content.Server.Cloning.Components; +using Content.Server.DeviceLinking.Systems; +using Content.Server.Genetics.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.Speech.Components; +using Content.Shared.ActionBlocker; +using Content.Shared.Body.Components; +using Content.Shared.Climbing.Systems; +using Content.Shared.Destructible; +using Content.Shared.DeviceLinking.Events; +using Content.Shared.DNAModifier; +using Content.Shared.DragDrop; +using Content.Shared.Humanoid; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Events; +using Content.Shared.Verbs; +using Robust.Server.Containers; +using Robust.Shared.Containers; +using static Content.Shared.DNAModifier.SharedDNAModifierComponent; + +namespace Content.Server.Genetics; + +public sealed class DNAModifierSystem : EntitySystem +{ + [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly ClimbSystem _climbSystem = default!; + [Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly ContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + private const float UpdateRate = 1f; + private float _updateDif; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnRelayMovement); + SubscribeLocalEvent>(AddInsertOtherVerb); + SubscribeLocalEvent>(AddAlternativeVerbs); + SubscribeLocalEvent(OnDestroyed); + SubscribeLocalEvent(OnDragDropOn); + SubscribeLocalEvent(OnPortDisconnected); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnCanDragDropOn); + } + + private void OnCanDragDropOn(EntityUid uid, DNAModifierComponent component, ref CanDropTargetEvent args) + { + args.Handled = true; + args.CanDrop |= CanModifierInsert(uid, args.Dragged, component); + } + public bool CanModifierInsert(EntityUid uid, EntityUid target, DNAModifierComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + return HasComp(target); + } + + private void OnComponentInit(EntityUid uid, DNAModifierComponent scannerComponent, ComponentInit args) + { + base.Initialize(); + scannerComponent.BodyContainer = _containerSystem.EnsureContainer(uid, $"scanner-bodyContainer"); + _signalSystem.EnsureSinkPorts(uid, DNAModifierComponent.ScannerPort); + } + + private void OnRelayMovement(EntityUid uid, DNAModifierComponent scannerComponent, ref ContainerRelayMovementEntityEvent args) + { + if (!_blocker.CanInteract(args.Entity, uid)) + return; + + EjectBody(uid, scannerComponent); + } + private void AddInsertOtherVerb(EntityUid uid, DNAModifierComponent component, GetVerbsEvent args) + { + if (args.Using == null || + !args.CanAccess || + !args.CanInteract || + IsOccupied(component) || + !CanModifierInsert(uid, args.Using.Value, component)) + return; + + var name = "Unknown"; + if (TryComp(args.Using.Value, out var metadata)) + name = metadata.EntityName; + + InteractionVerb verb = new() + { + Act = () => InsertBody(uid, args.Target, component), + Category = VerbCategory.Insert, + Text = name + }; + args.Verbs.Add(verb); + } + private void AddAlternativeVerbs(EntityUid uid, DNAModifierComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + // Eject verb + if (IsOccupied(component)) + { + AlternativeVerb verb = new() + { + Act = () => EjectBody(uid, component), + Category = VerbCategory.Eject, + Text = Loc.GetString("dna-modifier-verb-noun-occupant"), + Priority = 1 // Promote to top to make ejecting the ALT-click action + }; + args.Verbs.Add(verb); + } + } + private void OnDestroyed(EntityUid uid, DNAModifierComponent scannerComponent, DestructionEventArgs args) + { + EjectBody(uid, scannerComponent); + } + + private void OnDragDropOn(EntityUid uid, DNAModifierComponent scannerComponent, ref DragDropTargetEvent args) + { + InsertBody(uid, args.Dragged, scannerComponent); + + } + private void OnPortDisconnected(EntityUid uid, DNAModifierComponent component, PortDisconnectedEvent args) + { + component.ConnectedConsole = null; + } + + private void OnAnchorChanged(EntityUid uid, DNAModifierComponent component, ref AnchorStateChangedEvent args) + { + if (component.ConnectedConsole == null || !TryComp(component.ConnectedConsole, out var console)) + return; + + if (args.Anchored) + { + _cloningConsoleSystem.RecheckConnections(component.ConnectedConsole.Value, uid, console); + return; + } + _cloningConsoleSystem.UpdateUserInterface(component.ConnectedConsole.Value, console); + } + private DNAModifierStatus GetStatus(EntityUid uid, DNAModifierComponent scannerComponent) + { + if (this.IsPowered(uid, EntityManager)) + { + var body = scannerComponent.BodyContainer.ContainedEntity; + if (body == null) + return DNAModifierStatus.Open; + + if (!TryComp(body.Value, out var state)) + { // Is not alive or dead or critical + return DNAModifierStatus.Yellow; + } + + return GetStatusFromDamageState(body.Value, state); + } + return DNAModifierStatus.Off; + } + public static bool IsOccupied(DNAModifierComponent scannerComponent) + { + return scannerComponent.BodyContainer.ContainedEntity != null; + } + private DNAModifierStatus GetStatusFromDamageState(EntityUid uid, MobStateComponent state) + { + if (_mobStateSystem.IsAlive(uid, state)) + return DNAModifierStatus.Green; + + if (_mobStateSystem.IsCritical(uid, state)) + return DNAModifierStatus.Red; + + if (_mobStateSystem.IsDead(uid, state)) + return DNAModifierStatus.Death; + + return DNAModifierStatus.Yellow; + } + private void UpdateAppearance(EntityUid uid, DNAModifierComponent scannerComponent) + { + if (TryComp(uid, out var appearance)) + { + _appearance.SetData(uid, DNAModifierVisual.Status, GetStatus(uid, scannerComponent), appearance); + } + } + public override void Update(float frameTime) + { + base.Update(frameTime); + + _updateDif += frameTime; + if (_updateDif < UpdateRate) + return; + + _updateDif -= UpdateRate; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var scanner)) + { + UpdateAppearance(uid, scanner); + } + } + public void InsertBody(EntityUid uid, EntityUid to_insert, DNAModifierComponent? scannerComponent) + { + if (!Resolve(uid, ref scannerComponent)) + return; + + if (scannerComponent.BodyContainer.ContainedEntity != null) + return; + + if (!HasComp(to_insert)) + return; + + if(HasComp(to_insert)) + return; + + if (!HasComp(uid) && !HasComp(to_insert)) + return; + + _containerSystem.Insert(to_insert, scannerComponent.BodyContainer); + AddComp(to_insert); + UpdateAppearance(uid, scannerComponent); + } + public void EjectBody(EntityUid uid, DNAModifierComponent? scannerComponent) + { + if (!Resolve(uid, ref scannerComponent)) + return; + + if (scannerComponent.BodyContainer.ContainedEntity is not { Valid: true } contained) + return; + + _containerSystem.Remove(contained, scannerComponent.BodyContainer); + _climbSystem.ForciblySetClimbing(contained, uid); + RemCompDeferred(contained); + UpdateAppearance(uid, scannerComponent); + } +} diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs new file mode 100644 index 00000000000..7433cc671be --- /dev/null +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -0,0 +1,179 @@ +using System.Collections; +using Content.Server.Genetics.Components; +using Content.Shared.GameTicking; +using Content.Shared.Genetics; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.GameTicking.Events; +using Content.Shared.Damage; +using Content.Shared.Movement.Systems; +using Content.Shared.Weapons.Melee; + +namespace Content.Server.Genetics; + +/// +/// Assigns each a random roundstart. +/// +public sealed class GenomeSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MutationSystem _mutationSystem = default!; + + + // This is where all the genome layouts are stored. + // TODO: store on round entity when thats done, so persistence reloading doesnt scramble genes + [ViewVariables] + private readonly Dictionary _layouts = new(); + + private string _mutationsPool = "StandardHumanMutations"; + private Dictionary _mutations= new (); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(Reset); + } + + private void OnInit(EntityUid uid, GenomeComponent comp, MapInitEvent args) + { + // only empty in test and when vving + if (comp.GenomeId != string.Empty) + comp.Layout = GetOrCreateLayout(comp.GenomeId); + } + + private void Reset(RoundRestartCleanupEvent args) + { + _layouts.Clear(); + } + + private void OnRoundStart(RoundStartingEvent ev) + { + _proto.TryIndex(_mutationsPool, out var pool); + if (pool == null) + { + //TODO: throw an error here + } + else + { + foreach (var mutation in pool.Mutations) + { + _proto.TryIndex(mutation, out var mutationProto); + if (mutationProto != null) + _mutations.Add(mutationProto.Name, (mutationProto.Length, GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length))); + } + } + } + + public Genome GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(int length) + { + var sequence = new Genome(length); + bool flag = true; + while (flag || !_mutations.ContainsValue((length, sequence))) + { + sequence.Mutate(0, length, 0.5f); + + flag = false; + } + + return sequence; + } + + public void ApplyMutation(EntityUid uid, string id, GenomeComponent comp) + { + if (!HasComp(uid)) + return; + switch (id) + { + case "SuperStrength": + _mutationSystem.SuperStrength(uid, comp, true); + break; + case "XrayVision": + _mutationSystem.XrayVision(uid, comp, true); + break; + } + } + + /// + /// Either gets an existing genome layout or creates a new random one. + /// Genome layouts are reset between rounds. + /// Anything with calls this on mapinit to ensure it uses the correct layout. + /// + /// Genome prototype id to create the layout from + public GenomeLayout GetOrCreateLayout(string id) + { + // already created for this round so just use it + if (TryGetLayout(id, out var layout)) + return layout; + + // prototype must exist + var proto = _proto.Index(id); + + // create the new random genome layout! + layout = new GenomeLayout(); + var names = new List(proto.ValueBits.Keys); + _random.Shuffle(names); + foreach (var name in names) + { + var length = proto.ValueBits[name]; + layout.Add(name, length); + } + + // save it for the rest of the round + AddLayout(id, layout); + return layout; + } + + /// + /// Sets the Genome bits from a 's values. + /// + public void LoadGenes(EntityUid uid, ProtoId id, GenomeComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + var genes = _proto.Index(id); + foreach (var name in genes.Bools) + { + comp.Layout.SetBool(comp.Genome, name, true); + } + + foreach (var (name, value) in genes.Ints) + { + comp.Layout.SetInt(comp.Genome, name, value); + } + } + + /// + /// Copies the Genome bits from a parent to a child. + /// They must use the same genome layout or it will be logged and copy nothing. + /// + public void CopyParentGenes(EntityUid uid, EntityUid parent, GenomeComponent? comp = null, GenomeComponent? parentComp = null) + { + if (!Resolve(uid, ref comp) || !Resolve(parent, ref parentComp)) + return; + + if (parentComp.GenomeId != comp.GenomeId) + { + Log.Error($"Tried to copy incompatible genome from {ToPrettyString(parent):parent)} ({parentComp.GenomeId}) to {ToPrettyString(uid):child)} ({comp.GenomeId})"); + return; + } + + parentComp.Genome.CopyTo(comp.Genome); + } + + private bool TryGetLayout(string id, [NotNullWhen(true)] out GenomeLayout? layout) + { + return _layouts.TryGetValue(id, out layout); + } + + private void AddLayout(string id, GenomeLayout layout) + { + _layouts.Add(id, layout); + } +} diff --git a/Content.Server/_White/Genetics/MutationSystem.cs b/Content.Server/_White/Genetics/MutationSystem.cs new file mode 100644 index 00000000000..2d73ce9a0a7 --- /dev/null +++ b/Content.Server/_White/Genetics/MutationSystem.cs @@ -0,0 +1,70 @@ +using Content.Server.Genetics.Components; +using Content.Shared.Weapons.Melee; +using Robust.Shared.Prototypes; + +namespace Content.Server.Genetics; + +/// +/// Apply Mutation Effect? +/// +public sealed class MutationSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + + public override void Initialize() + { + base.Initialize(); + } + + public int GetDamage(string race) + { + var races = new Dictionary() + { + { "Arachnid", 5}, + { "Diona", 5}, + { "Dwarf", 5}, + { "Human", 5} + }; + + //TODO: больше рас + + return races[race]; + } + + public void SuperStrength(EntityUid uid, GenomeComponent genomeComp, bool state) + { + if (!HasComp(uid)) + return; + + if (state == true) + { + TryComp(uid, out var comp); + comp?.Damage.DamageDict.Add("Blunt", 30); + + genomeComp.Mutations.Add("SuperStrength"); + } + else + { + TryComp(uid, out var comp); + comp?.Damage.DamageDict.Add("Blunt", 5); + + genomeComp.Mutations.Remove("SuperStrength"); + } + } + + public void XrayVision(EntityUid uid, GenomeComponent genomeComp, bool state) + { + if (!HasComp(uid)) + return; + + if (state == true) + { + //TODO + } + else + { + //TODO + } + } +} diff --git a/Content.Shared/_White/DNAConsole/SharedDNAConsole.cs b/Content.Shared/_White/DNAConsole/SharedDNAConsole.cs new file mode 100644 index 00000000000..2f30dabec32 --- /dev/null +++ b/Content.Shared/_White/DNAConsole/SharedDNAConsole.cs @@ -0,0 +1,54 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.DNAConsole +{ + [Serializable, NetSerializable] + public sealed class DNAConsoleBoundUserInterfaceState : BoundUserInterfaceState + { + public readonly string? ModifierBodyInfo; + public readonly ModifierStatus ModifierStatus; + public readonly bool ModifierConnected; + public readonly bool ModifierInRange; + public DNAConsoleBoundUserInterfaceState(string? scannerBodyInfo, ModifierStatus cloningStatus, bool scannerConnected, bool scannerInRange) + { + ModifierBodyInfo = scannerBodyInfo; + ModifierStatus = cloningStatus; + ModifierConnected = scannerConnected; + ModifierInRange = scannerInRange; + } + } + + [Serializable, NetSerializable] + public enum ModifierStatus : byte + { + Ready, + ModifierEmpty, + ModifierOccupantAlive, + OccupantMetaphyiscal, + ModifierOccupied + } + + [Serializable, NetSerializable] + public enum DNAConsoleUiKey : byte + { + Key + } + + [Serializable, NetSerializable] + public enum UiButton : byte + { + Clone, + Eject + } + + [Serializable, NetSerializable] + public sealed class UiButtonPressedMessage : BoundUserInterfaceMessage + { + public readonly UiButton Button; + + public UiButtonPressedMessage(UiButton button) + { + Button = button; + } + } +} diff --git a/Content.Shared/_White/DNAModifier/SharedDNAModifierComponent.cs b/Content.Shared/_White/DNAModifier/SharedDNAModifierComponent.cs new file mode 100644 index 00000000000..f42eec7760c --- /dev/null +++ b/Content.Shared/_White/DNAModifier/SharedDNAModifierComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.DNAModifier; + +public abstract partial class SharedDNAModifierComponent : Component +{ + [Serializable, NetSerializable] + public enum DNAModifierVisual : byte + { + Status + } + [Serializable, NetSerializable] + public enum DNAModifierStatus : byte + { + Off, + Open, + Red, + Death, + Green, + Yellow, + } +} diff --git a/Content.Shared/_White/Genetics/GenesPrototype.cs b/Content.Shared/_White/Genetics/GenesPrototype.cs new file mode 100644 index 00000000000..cd2a6e9930b --- /dev/null +++ b/Content.Shared/_White/Genetics/GenesPrototype.cs @@ -0,0 +1,26 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Genetics; + +/// +/// Genes for an organism, provides the actual values which are used to build bits. +/// +[Prototype("genes")] +public sealed partial class GenesPrototype : IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + /// + /// Each bool which will be set to true. + /// + [DataField] + public HashSet Bools = new(); + + /// + /// Values of each int to set. + /// Any unused bits are dropped silently. + /// + [DataField] + public Dictionary Ints = new(); +} diff --git a/Content.Shared/_White/Genetics/Genome.cs b/Content.Shared/_White/Genetics/Genome.cs new file mode 100644 index 00000000000..4437bfb6544 --- /dev/null +++ b/Content.Shared/_White/Genetics/Genome.cs @@ -0,0 +1,184 @@ +using Robust.Shared.Serialization; +using System.Collections; +using System.Text; +using Robust.Shared.Random; + +namespace Content.Shared.Genetics; + +/// +/// Genome for an organism. +/// Internally represented as a bit array, shown to players as bases using . +/// Other systems can get data using and . +/// Each bit can either be a boolean or be part of a number, which has its bits stored sequentially. +/// Each species has its own unique genome layout that maps bits to data, which is randomized roundstart. +/// Variable length information such as a list of reagents cannot be stored here. +/// +[DataDefinition] +public sealed partial class Genome +{ + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + /// + /// Bits that represent the genes bools and ints. + /// + [ViewVariables] + public BitArray Bits = new BitArray(0); + + private static char[] Bases = new[] { 'A', 'C', 'G', 'T'}; + + /// + /// Creates a new genome from all zeroes. + /// + public Genome(int count = 0) + { + Bits = new BitArray(count); + } + + /// + /// Copy this genome's bits to another one. + /// + public void CopyTo(Genome other) + { + other.Bits = new BitArray(Bits); + } + + /// + /// Get the value of a single bool at an index. + /// If it is out of bounds false is returned. + /// + /// Bit index to get from + public bool GetBool(int index) + { + return index < Bits.Length && Bits[index]; + } + + /// + /// Get the value of an integer with multiple bits. + /// It should be clamped to a reasonable number afterwards. + /// + /// Starting bit index to get from + /// Number of bits to read + public int GetInt(int index, int bits) + { + var value = 0; + for (int i = 0; i < bits; i++) + { + var bit = 1 << i; + if (GetBool(index + i)) + { + value |= bit; + } + } + + return value; + } + + /// + /// Return a base pair string for a range of bits in the genome. + /// + /// Starting bit index to get from + /// Number of bases to include in the string + public string GetBases(int index, int bases) + { + var builder = new StringBuilder(bases); + for (int i = 0; i < bases; i++) + { + // 2 bits makes a base + var c = Bases[GetInt(index + i * 2, 2)]; + builder.Append(c); + } + + return builder.ToString(); + } + + /// + /// Sets a boolean value at a bit index to a value. + /// + /// Bit index to set + public void SetBool(int index, bool value) + { + Bits[index] = value; + } + + /// + /// Sets the bits of an integer to a value. + /// This will truncate the integer to fit inside the possible values. + /// Also handles writing past the end correctly. + /// + /// Starting bit index to set + /// Number of bits to set + /// Number to set + public void SetInt(int index, int bits, int value) + { + for (int i = 0; i < bits; i++) + { + var bit = 1 << i; + Bits[index + i] = (value & bit) != 0; + } + } + + /// + /// Flips a bit at an index from a 0 to a 1. + /// + /// Bit index to flip + public void FlipBit(int index) + { + Bits[index] = !Bits[index]; + } + + /// + /// Sets the 2 bits at an index to the value of a base. + /// + /// Bit index to set at + /// Base character to set + public void SetBase(int index, char c) + { + int value = 0; + switch (c) + { + case 'A': + value = 0; + break; + case 'C': + value = 1; + break; + case 'G': + value = 2; + break; + case 'T': + value = 3; + break; + } + + SetInt(index, bits: 2, value: value); + } + + /// + /// Mutates a part of Genome from index to index + length. The more severity - the more mutated it will be. + /// May need to move it to a different system. + /// + /// + /// + /// + public void Mutate(int index, int length, float severity) + { + //float prob = severity; // This may be unnecessary, needs consideration. + severity = Math.Clamp(severity, 0, 1); + + for (int i = 0; i < length; i++) + { + if (_robustRandom.Prob(severity)) + FlipBit(index + i); + } + } + + /// + /// Future function that will extend the genome (used for tg mutators) + /// + /// + /// + public void Extend(int length, BitArray? array) // possibly more arguments + { + + } +} diff --git a/Content.Shared/_White/Genetics/GenomeLayout.cs b/Content.Shared/_White/Genetics/GenomeLayout.cs new file mode 100644 index 00000000000..8df1b559211 --- /dev/null +++ b/Content.Shared/_White/Genetics/GenomeLayout.cs @@ -0,0 +1,89 @@ +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Shared.Genetics; + +/// +/// Maps a 's bits to bools and ints. +/// Stores indices for every bool and int that can be retrieved. +/// +public sealed class GenomeLayout +{ + /// + /// Indices and bit lengths of every value stored in the genome. + /// For bit length must be 1. + /// + [DataField] + public Dictionary Values = new(); + + /// + /// How many bits this genome has total + /// + [DataField] + public int TotalBits; + + /// + /// Get a bool from the genome by name. + /// + public bool GetBool(Genome genome, string name) + { + var (index, bits) = Values[name]; + DebugTools.Assert(bits == 1, "Do not use GetBool for int genome values"); + + return genome.GetBool(index); + } + + /// + /// Get an int from the genome by name. + /// + public int GetInt(Genome genome, string name) + { + var (index, bits) = Values[name]; + return genome.GetInt(index, bits); + } + + /// + /// Sets a bool value on the genome by name. + /// + public void SetBool(Genome genome, string name, bool value) + { + var (index, bits) = Values[name]; + DebugTools.Assert(bits == 1, "Do not use SetBool for int genome values"); + + genome.SetBool(index, value); + } + + /// + /// Sets an int on the genome by name. + /// + /// + /// Unused bits are silently ignored. + /// + public void SetInt(Genome genome, string name, int value) + { + var (index, bits) = Values[name]; + genome.SetInt(index, bits: bits, value: value); + } + + /// + /// Add a mapping to the layout. + /// If length is 1 it will be a bool, int otherwise. + /// + /// Name of the value to add + /// Number of bits the value has + public void Add(string name, ushort bits) + { + DebugTools.Assert(bits > 0, "Genome value bit count must be positive"); + + var index = TotalBits; + Values.Add(name, (index, bits)); + TotalBits += bits; + } + + public void MutateLayout(Genome genome, string name, float severity) + { + var (index, length) = Values[name]; + + genome.Mutate(index, length, severity); + } +} diff --git a/Content.Shared/_White/Genetics/GenomePrototype.cs b/Content.Shared/_White/Genetics/GenomePrototype.cs new file mode 100644 index 00000000000..b64325a4648 --- /dev/null +++ b/Content.Shared/_White/Genetics/GenomePrototype.cs @@ -0,0 +1,25 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Genetics; + +/// +/// Genome for an organism. +/// Internally represented as a bit array, shown to players as bases using . +/// Other systems can get data using and . +/// Each bit can either be a boolean or be part of a number, which has its bits stored sequentially. +/// Each species has its own unique genome layout that maps bits to data, which is randomized roundstart. +/// Variable length information such as a list of reagents cannot be stored here. +/// +[Prototype("genome")] +public sealed partial class GenomePrototype : IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + /// + /// Names and bit lengths of each value in the genome. + /// If length is 1 then it will be a bool. + /// + [DataField("valuebits", required: true)] + public Dictionary ValueBits = new(); +} diff --git a/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs b/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs new file mode 100644 index 00000000000..71cd061bfb9 --- /dev/null +++ b/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs @@ -0,0 +1,21 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.Genetics +{ + /// + /// This is a prototype for a mutation pool. + /// + [Prototype("mutationCollection")] + public sealed partial class MutationCollectionPrototype : IPrototype + { + /// + [IdDataField] + public string ID { get; } = default!; + + /// + /// List of Ids of mutations in the collection. + /// + [DataField("mutations", required: true)] + public IReadOnlyList Mutations { get; private set; } = Array.Empty(); + } +} diff --git a/Content.Shared/_White/Genetics/MutationPrototype.cs b/Content.Shared/_White/Genetics/MutationPrototype.cs new file mode 100644 index 00000000000..39f5f2ca453 --- /dev/null +++ b/Content.Shared/_White/Genetics/MutationPrototype.cs @@ -0,0 +1,30 @@ +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Server.Genetics; + +/// +/// This is a prototype for mutation +/// +[Prototype(type: "mutation"), PublicAPI] +public sealed partial class MutationPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField("name", required: true)] + public string Name { get; private set; } = default!; + + /// + /// Instability that brings this mutation if add via mutator. + /// + [DataField("instability", required: false)] + public int Instability { get; private set; } = 0; + + /// + /// Length of genetic sequence for this mutation. + /// + [DataField("length", required: true)] + public int Length { get; private set; } = 64; +} diff --git a/Resources/Prototypes/White/genetic_prototypes.yml b/Resources/Prototypes/White/genetic_prototypes.yml new file mode 100644 index 00000000000..cbaa9e62c96 --- /dev/null +++ b/Resources/Prototypes/White/genetic_prototypes.yml @@ -0,0 +1,112 @@ +- type: wireLayout + id: DNAModifier + dummyWires: 2 + wires: + - !type:PowerWireAction + - !type:PowerWireAction + pulseTimeout: 15 + + - type: entity + parent: BaseComputerCircuitboard + id: DNAConsoleComputerCircuitboard + name: dna console computer board + description: A computer printed circuit board for a dna console. + components: + - type: Sprite + state: cpu_dna + - type: ComputerBoard + prototype: ComputerDNAConsole + +- type: entity + parent: BaseComputer + id: ComputerDNAConsole + name: dna console computer + description: soon + components: + - type: DNAConsole + - type: DeviceList + - type: DeviceNetwork + deviceNetId: Wired + - type: Sprite + layers: + - map: ["computerLayerBody"] + state: computer + - map: ["computerLayerKeyboard"] + state: generic_keyboard + - map: ["computerLayerScreen"] + state: dna + - map: ["computerLayerKeys"] + state: generic_keys + - type: ApcPowerReceiver + powerLoad: 3400 #We want this to fail first so I transferred most of the scanner and pod's power here. (3500 in total) + - type: Computer + board: DNAConsoleComputerCircuitboard + - type: PointLight + radius: 1.5 + energy: 1.6 + color: "#00908D" + - type: DeviceLinkSource + range: 4 + ports: + - DNAModifierSender + - type: ActivatableUI + key: enum.DNAConsoleUiKey.Key + - type: UserInterface + interfaces: + - key: enum.DNAConsoleUiKey.Key + type: DNAConsoleBoundUserInterface + - type: Speech + speechVerb: Robotic + speechSounds: Pai + - type: Damageable + damageContainer: Inorganic + damageModifierSet: StrongMetallic + +- type: entity + id: DNAModifierMachineCircuitboard + parent: BaseMachineCircuitboard + name: dna modifier machine board + description: A machine printed circuit board for a dna modifier. + components: + - type: Sprite + state: dnamodifier + - type: MachineBoard + prototype: DNAModifier + requirements: + Capacitor: 1 + materialRequirements: + Glass: 5 + Cable: 1 + +- type: sourcePort + id: DNAModifierSender + name: signal-port-name-dna-modifier-sender + description: signal-port-description-dna-modifier-sender + +- type: sinkPort + id: DNAModifierReceiver + name: signal-port-name-dna-modifier-receiver + description: signal-port-description-dna-modifier-receiver + +- type: genome + id: HumanGenomePrototype + valuebits: + + +- type: mutationCollection + id: StandardHumanMutations + mutations: + - SuperStrength + - XrayVision + +- type: mutation + id: SuperStrength + name: Super Strength + instability: 10 + length: 64 + +- type: mutation + id: XrayVision + name: X-ray vision + instability: 30 + length: 64 diff --git a/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/closed.png b/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/closed.png new file mode 100644 index 0000000000000000000000000000000000000000..b98dc797322bff5dd7d7e525f759a4e6b9b561a4 GIT binary patch literal 1901 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/maint_unlit.png b/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/maint_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..1726aa5d1021b6b44b543605b8a3cd5acabef113 GIT binary patch literal 142 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/occupied_unlit.png b/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/occupied_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff8021e00e9a0ff596082c95bd5c66c4145613c GIT binary patch literal 88 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzc~2L|kcif|2OSw17+6>g3@c*) jPhr`(7AVzly=oJ?5C>yGy2}k;AfLh0)z4*}Q$iB}F^?60 literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/off_unlit.png b/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/off_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..445ddaae70666150c445aad740310f405eb0c227 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzb59q?kcif|mlpCeCUOJEP@}s`GB$vp00i_ I>zopr09P&{cK`qY literal 0 HcmV?d00001 diff --git a/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/open.png b/Resources/Textures/White/Objects/Devices/dna_modifier.rsi/open.png new file mode 100644 index 0000000000000000000000000000000000000000..c97816e322b4b1d29df7d12dfa8b260cafcc22c2 GIT binary patch literal 1920 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 Date: Sat, 15 Jun 2024 14:36:43 +0300 Subject: [PATCH 02/13] ByteArray to bool?[], also namespaces --- .../Components/ActiveModifierComponent.cs | 2 +- .../Components/DNAConsoleComponent.cs | 2 +- .../Components/DNAModifierComponent.cs | 2 +- .../Genetics/Components/GenomeComponent.cs | 4 +- .../_White/Genetics/DNAConsoleSystem.cs | 7 ++-- .../_White/Genetics/DNAModifierSystem.cs | 7 +--- .../_White/Genetics/GenomeSystem.cs | 15 +++---- .../_White/Genetics/MutationSystem.cs | 4 +- .../_White/Genetics/GenesPrototype.cs | 2 +- Content.Shared/_White/Genetics/Genome.cs | 41 ++++++++++++++----- .../_White/Genetics/GenomeLayout.cs | 8 ++-- .../_White/Genetics/GenomePrototype.cs | 2 +- .../Genetics/MutationCollectionPrototype.cs | 2 +- .../_White/Genetics/MutationPrototype.cs | 2 +- 14 files changed, 56 insertions(+), 44 deletions(-) diff --git a/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs b/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs index 61dc4e43038..ad723d950e7 100644 --- a/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs +++ b/Content.Server/_White/Genetics/Components/ActiveModifierComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Genetics.Components; +namespace Content.Server._White.Genetics.Components; ///

/// Shrimply a tracking component for pods that are cloning. diff --git a/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs b/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs index 75e50087ec8..a5ae999a5d7 100644 --- a/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs +++ b/Content.Server/_White/Genetics/Components/DNAConsoleComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Genetics.Components +namespace Content.Server._White.Genetics.Components { [RegisterComponent] public sealed partial class DNAConsoleComponent : Component diff --git a/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs b/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs index 29e5d6104b7..c75bc49a464 100644 --- a/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs +++ b/Content.Server/_White/Genetics/Components/DNAModifierComponent.cs @@ -1,7 +1,7 @@ using Content.Shared.DNAModifier; using Robust.Shared.Containers; -namespace Content.Server.Genetics.Components +namespace Content.Server._White.Genetics.Components { [RegisterComponent] public sealed partial class DNAModifierComponent : SharedDNAModifierComponent diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs index 983ca83b750..36f63024374 100644 --- a/Content.Server/_White/Genetics/Components/GenomeComponent.cs +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -1,7 +1,7 @@ -using Content.Shared.Genetics; +using Content.Shared._White.Genetics; using Robust.Shared.Prototypes; -namespace Content.Server.Genetics.Components; +namespace Content.Server._White.Genetics.Components; /// /// Gives this entity a genome for traits to be passed on and potentially mutated. diff --git a/Content.Server/_White/Genetics/DNAConsoleSystem.cs b/Content.Server/_White/Genetics/DNAConsoleSystem.cs index 6a2b16396af..bcec1812238 100644 --- a/Content.Server/_White/Genetics/DNAConsoleSystem.cs +++ b/Content.Server/_White/Genetics/DNAConsoleSystem.cs @@ -1,12 +1,12 @@ using System.Linq; +using Content.Server._White.Genetics.Components; using Content.Server.Administration.Logs; using Content.Server.DeviceLinking.Systems; -using Content.Server.Genetics.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Shared.Database; using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking.Events; +using Content.Shared.DNAConsole; using Content.Shared.IdentityManagement; using Content.Shared.Mind; using Content.Shared.Mobs.Components; @@ -15,9 +15,8 @@ using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.Player; -using Content.Shared.DNAConsole; -namespace Content.Server.Genetics +namespace Content.Server._White.Genetics { [UsedImplicitly] public sealed class CloningConsoleSystem : EntitySystem diff --git a/Content.Server/_White/Genetics/DNAModifierSystem.cs b/Content.Server/_White/Genetics/DNAModifierSystem.cs index d8c359677e2..93832b0a529 100644 --- a/Content.Server/_White/Genetics/DNAModifierSystem.cs +++ b/Content.Server/_White/Genetics/DNAModifierSystem.cs @@ -1,7 +1,5 @@ -using Content.Server.Cloning; -using Content.Server.Cloning.Components; +using Content.Server._White.Genetics.Components; using Content.Server.DeviceLinking.Systems; -using Content.Server.Genetics.Components; using Content.Server.Power.EntitySystems; using Content.Server.Speech.Components; using Content.Shared.ActionBlocker; @@ -9,7 +7,6 @@ using Content.Shared.Climbing.Systems; using Content.Shared.Destructible; using Content.Shared.DeviceLinking.Events; -using Content.Shared.DNAModifier; using Content.Shared.DragDrop; using Content.Shared.Humanoid; using Content.Shared.Mobs.Components; @@ -20,7 +17,7 @@ using Robust.Shared.Containers; using static Content.Shared.DNAModifier.SharedDNAModifierComponent; -namespace Content.Server.Genetics; +namespace Content.Server._White.Genetics; public sealed class DNAModifierSystem : EntitySystem { diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index 7433cc671be..ebf7193b991 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -1,17 +1,12 @@ -using System.Collections; -using Content.Server.Genetics.Components; +using System.Diagnostics.CodeAnalysis; +using Content.Server._White.Genetics.Components; +using Content.Server.GameTicking.Events; +using Content.Shared._White.Genetics; using Content.Shared.GameTicking; -using Content.Shared.Genetics; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Server.GameTicking.Events; -using Content.Shared.Damage; -using Content.Shared.Movement.Systems; -using Content.Shared.Weapons.Melee; -namespace Content.Server.Genetics; +namespace Content.Server._White.Genetics; /// /// Assigns each a random roundstart. diff --git a/Content.Server/_White/Genetics/MutationSystem.cs b/Content.Server/_White/Genetics/MutationSystem.cs index 2d73ce9a0a7..d89dbdb9d9b 100644 --- a/Content.Server/_White/Genetics/MutationSystem.cs +++ b/Content.Server/_White/Genetics/MutationSystem.cs @@ -1,8 +1,8 @@ -using Content.Server.Genetics.Components; +using Content.Server._White.Genetics.Components; using Content.Shared.Weapons.Melee; using Robust.Shared.Prototypes; -namespace Content.Server.Genetics; +namespace Content.Server._White.Genetics; /// /// Apply Mutation Effect? diff --git a/Content.Shared/_White/Genetics/GenesPrototype.cs b/Content.Shared/_White/Genetics/GenesPrototype.cs index cd2a6e9930b..5f0e5d1341b 100644 --- a/Content.Shared/_White/Genetics/GenesPrototype.cs +++ b/Content.Shared/_White/Genetics/GenesPrototype.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Shared.Genetics; +namespace Content.Shared._White.Genetics; /// /// Genes for an organism, provides the actual values which are used to build bits. diff --git a/Content.Shared/_White/Genetics/Genome.cs b/Content.Shared/_White/Genetics/Genome.cs index 4437bfb6544..c9e436c2afa 100644 --- a/Content.Shared/_White/Genetics/Genome.cs +++ b/Content.Shared/_White/Genetics/Genome.cs @@ -1,9 +1,10 @@ using Robust.Shared.Serialization; using System.Collections; +using System.Linq; using System.Text; using Robust.Shared.Random; -namespace Content.Shared.Genetics; +namespace Content.Shared._White.Genetics; /// /// Genome for an organism. @@ -22,7 +23,7 @@ public sealed partial class Genome /// Bits that represent the genes bools and ints. /// [ViewVariables] - public BitArray Bits = new BitArray(0); + public bool?[] Bits = Array.Empty(); private static char[] Bases = new[] { 'A', 'C', 'G', 'T'}; @@ -31,7 +32,7 @@ public sealed partial class Genome /// public Genome(int count = 0) { - Bits = new BitArray(count); + Bits = new bool?[count]; } /// @@ -39,7 +40,11 @@ public Genome(int count = 0) /// public void CopyTo(Genome other) { - other.Bits = new BitArray(Bits); + other.Bits = Array.Empty(); + foreach (var bit in Bits) + { + var enumerable = other.Bits.Append(null); //weird + } } /// @@ -47,9 +52,9 @@ public void CopyTo(Genome other) /// If it is out of bounds false is returned. /// /// Bit index to get from - public bool GetBool(int index) + public bool? GetBool(int index) { - return index < Bits.Length && Bits[index]; + return index > Bits.Length ? null : Bits[index]; } /// @@ -58,13 +63,18 @@ public bool GetBool(int index) /// /// Starting bit index to get from /// Number of bits to read - public int GetInt(int index, int bits) + public int? GetInt(int index, int bits) { var value = 0; for (int i = 0; i < bits; i++) { var bit = 1 << i; - if (GetBool(index + i)) + var v = GetBool(index + i); + + if (v == null) + return null; + + if (v.Value) { value |= bit; } @@ -84,7 +94,11 @@ public string GetBases(int index, int bases) for (int i = 0; i < bases; i++) { // 2 bits makes a base - var c = Bases[GetInt(index + i * 2, 2)]; + var c = 'N'; // null genome + var b = GetInt(index + i * 2, 2); + if (b != null) + c = Bases[b.Value]; + builder.Append(c); } @@ -123,7 +137,14 @@ public void SetInt(int index, int bits, int value) /// Bit index to flip public void FlipBit(int index) { - Bits[index] = !Bits[index]; + if (Bits[index] != null) + Bits[index] = !Bits[index]; + else + { + var coin = _robustRandom.Prob(0.5f); + Bits[index] = coin; + } + } /// diff --git a/Content.Shared/_White/Genetics/GenomeLayout.cs b/Content.Shared/_White/Genetics/GenomeLayout.cs index 8df1b559211..601986e0f75 100644 --- a/Content.Shared/_White/Genetics/GenomeLayout.cs +++ b/Content.Shared/_White/Genetics/GenomeLayout.cs @@ -1,7 +1,7 @@ -using Robust.Shared.Random; +using Content.Shared._White.Genetics; using Robust.Shared.Utility; -namespace Content.Shared.Genetics; +namespace Content.Shared._White.Genetics; /// /// Maps a 's bits to bools and ints. @@ -25,7 +25,7 @@ public sealed class GenomeLayout /// /// Get a bool from the genome by name. /// - public bool GetBool(Genome genome, string name) + public bool? GetBool(Genome genome, string name) { var (index, bits) = Values[name]; DebugTools.Assert(bits == 1, "Do not use GetBool for int genome values"); @@ -36,7 +36,7 @@ public bool GetBool(Genome genome, string name) /// /// Get an int from the genome by name. /// - public int GetInt(Genome genome, string name) + public int? GetInt(Genome genome, string name) { var (index, bits) = Values[name]; return genome.GetInt(index, bits); diff --git a/Content.Shared/_White/Genetics/GenomePrototype.cs b/Content.Shared/_White/Genetics/GenomePrototype.cs index b64325a4648..8e0b10a5fd4 100644 --- a/Content.Shared/_White/Genetics/GenomePrototype.cs +++ b/Content.Shared/_White/Genetics/GenomePrototype.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Shared.Genetics; +namespace Content.Shared._White.Genetics; /// /// Genome for an organism. diff --git a/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs b/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs index 71cd061bfb9..2e723aae49f 100644 --- a/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs +++ b/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Server.Genetics +namespace Content.Shared._White.Genetics { /// /// This is a prototype for a mutation pool. diff --git a/Content.Shared/_White/Genetics/MutationPrototype.cs b/Content.Shared/_White/Genetics/MutationPrototype.cs index 39f5f2ca453..5cf26e91d11 100644 --- a/Content.Shared/_White/Genetics/MutationPrototype.cs +++ b/Content.Shared/_White/Genetics/MutationPrototype.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using Robust.Shared.Prototypes; -namespace Content.Server.Genetics; +namespace Content.Shared._White.Genetics; /// /// This is a prototype for mutation From 8451a43c09e7cd608420ecbb63616d5240df0ea5 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sat, 15 Jun 2024 18:01:16 +0300 Subject: [PATCH 03/13] retvrn to BitArray --- .../_White/Genetics/GenomeSystem.cs | 2 +- Content.Shared/_White/Genetics/Genome.cs | 38 +++++-------------- .../_White/Genetics/GenomeLayout.cs | 4 +- .../_White/Genetics/MutationEffect.cs | 13 +++++++ .../_White/Genetics/MutationPrototype.cs | 3 ++ .../_White/Genetics/Mutations/XrayMutation.cs | 18 +++++++++ .../Prototypes/White/genetic_prototypes.yml | 21 +++++----- 7 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 Content.Shared/_White/Genetics/MutationEffect.cs create mode 100644 Content.Shared/_White/Genetics/Mutations/XrayMutation.cs diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index ebf7193b991..7a2ccffc8cf 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -94,7 +94,7 @@ public void ApplyMutation(EntityUid uid, string id, GenomeComponent comp) } } - /// + /// /// Either gets an existing genome layout or creates a new random one. /// Genome layouts are reset between rounds. /// Anything with calls this on mapinit to ensure it uses the correct layout. diff --git a/Content.Shared/_White/Genetics/Genome.cs b/Content.Shared/_White/Genetics/Genome.cs index c9e436c2afa..ecacd4a1465 100644 --- a/Content.Shared/_White/Genetics/Genome.cs +++ b/Content.Shared/_White/Genetics/Genome.cs @@ -23,7 +23,7 @@ public sealed partial class Genome /// Bits that represent the genes bools and ints. /// [ViewVariables] - public bool?[] Bits = Array.Empty(); + public BitArray Bits = new BitArray(0); private static char[] Bases = new[] { 'A', 'C', 'G', 'T'}; @@ -32,7 +32,7 @@ public sealed partial class Genome /// public Genome(int count = 0) { - Bits = new bool?[count]; + Bits = new BitArray(count); } /// @@ -40,11 +40,7 @@ public Genome(int count = 0) /// public void CopyTo(Genome other) { - other.Bits = Array.Empty(); - foreach (var bit in Bits) - { - var enumerable = other.Bits.Append(null); //weird - } + other.Bits = new BitArray(Bits); } /// @@ -52,9 +48,9 @@ public void CopyTo(Genome other) /// If it is out of bounds false is returned. /// /// Bit index to get from - public bool? GetBool(int index) + public bool GetBool(int index) { - return index > Bits.Length ? null : Bits[index]; + return index < Bits.Length && Bits[index]; } /// @@ -63,18 +59,13 @@ public void CopyTo(Genome other) /// /// Starting bit index to get from /// Number of bits to read - public int? GetInt(int index, int bits) + public int GetInt(int index, int bits) { var value = 0; for (int i = 0; i < bits; i++) { var bit = 1 << i; - var v = GetBool(index + i); - - if (v == null) - return null; - - if (v.Value) + if (GetBool(index + i)) { value |= bit; } @@ -94,11 +85,7 @@ public string GetBases(int index, int bases) for (int i = 0; i < bases; i++) { // 2 bits makes a base - var c = 'N'; // null genome - var b = GetInt(index + i * 2, 2); - if (b != null) - c = Bases[b.Value]; - + var c = Bases[GetInt(index + i * 2, 2)]; builder.Append(c); } @@ -137,14 +124,7 @@ public void SetInt(int index, int bits, int value) /// Bit index to flip public void FlipBit(int index) { - if (Bits[index] != null) - Bits[index] = !Bits[index]; - else - { - var coin = _robustRandom.Prob(0.5f); - Bits[index] = coin; - } - + Bits[index] = !Bits[index]; } /// diff --git a/Content.Shared/_White/Genetics/GenomeLayout.cs b/Content.Shared/_White/Genetics/GenomeLayout.cs index 601986e0f75..a7be58e3283 100644 --- a/Content.Shared/_White/Genetics/GenomeLayout.cs +++ b/Content.Shared/_White/Genetics/GenomeLayout.cs @@ -25,7 +25,7 @@ public sealed class GenomeLayout /// /// Get a bool from the genome by name. /// - public bool? GetBool(Genome genome, string name) + public bool GetBool(Genome genome, string name) { var (index, bits) = Values[name]; DebugTools.Assert(bits == 1, "Do not use GetBool for int genome values"); @@ -36,7 +36,7 @@ public sealed class GenomeLayout /// /// Get an int from the genome by name. /// - public int? GetInt(Genome genome, string name) + public int GetInt(Genome genome, string name) { var (index, bits) = Values[name]; return genome.GetInt(index, bits); diff --git a/Content.Shared/_White/Genetics/MutationEffect.cs b/Content.Shared/_White/Genetics/MutationEffect.cs new file mode 100644 index 00000000000..6d8b99bdc86 --- /dev/null +++ b/Content.Shared/_White/Genetics/MutationEffect.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; + +namespace Content.Shared._White.Genetics +{ + [ImplicitDataDefinitionForInheritors] + [MeansImplicitUse] + public abstract partial class MutationEffect + { + public abstract void Effect(MutationEffectArgs args); + } + + public readonly record struct MutationEffectArgs(EntityUid AppliedEntity, IEntityManager EntityManager); +} diff --git a/Content.Shared/_White/Genetics/MutationPrototype.cs b/Content.Shared/_White/Genetics/MutationPrototype.cs index 5cf26e91d11..cf87d5049b4 100644 --- a/Content.Shared/_White/Genetics/MutationPrototype.cs +++ b/Content.Shared/_White/Genetics/MutationPrototype.cs @@ -27,4 +27,7 @@ public sealed partial class MutationPrototype : IPrototype /// [DataField("length", required: true)] public int Length { get; private set; } = 64; + + [DataField("effects")] + public MutationEffect[] Effect = default!; } diff --git a/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs b/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs new file mode 100644 index 00000000000..cd000431c62 --- /dev/null +++ b/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs @@ -0,0 +1,18 @@ +using Content.Shared.Movement.Systems; +using JetBrains.Annotations; + +namespace Content.Shared._White.Genetics.Mutations +{ + [UsedImplicitly] + [DataDefinition] + public sealed partial class XrayMutation : MutationEffect + { + public override void Effect(MutationEffectArgs args) + { + if (args.EntityManager.TryGetComponent(args.AppliedEntity, out var eye)) + { + var eyes = args.EntityManager.SystemOrNull(); + } + } + } +} diff --git a/Resources/Prototypes/White/genetic_prototypes.yml b/Resources/Prototypes/White/genetic_prototypes.yml index cbaa9e62c96..276d0e0cd18 100644 --- a/Resources/Prototypes/White/genetic_prototypes.yml +++ b/Resources/Prototypes/White/genetic_prototypes.yml @@ -5,18 +5,18 @@ - !type:PowerWireAction - !type:PowerWireAction pulseTimeout: 15 - - - type: entity + +- type: entity parent: BaseComputerCircuitboard id: DNAConsoleComputerCircuitboard name: dna console computer board description: A computer printed circuit board for a dna console. components: - - type: Sprite - state: cpu_dna - - type: ComputerBoard - prototype: ComputerDNAConsole - + - type: Sprite + state: cpu_dna + - type: ComputerBoard + prototype: ComputerDNAConsole + - type: entity parent: BaseComputer id: ComputerDNAConsole @@ -61,7 +61,7 @@ - type: Damageable damageContainer: Inorganic damageModifierSet: StrongMetallic - + - type: entity id: DNAModifierMachineCircuitboard parent: BaseMachineCircuitboard @@ -77,12 +77,12 @@ materialRequirements: Glass: 5 Cable: 1 - + - type: sourcePort id: DNAModifierSender name: signal-port-name-dna-modifier-sender description: signal-port-description-dna-modifier-sender - + - type: sinkPort id: DNAModifierReceiver name: signal-port-name-dna-modifier-receiver @@ -92,7 +92,6 @@ id: HumanGenomePrototype valuebits: - - type: mutationCollection id: StandardHumanMutations mutations: From 7d28a43466c52923db3e2c4c1017953057a53eb5 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sat, 15 Jun 2024 23:53:59 +0300 Subject: [PATCH 04/13] small --- .../Genetics/Components/GenomeComponent.cs | 3 ++ .../_White/Genetics/GenomeSystem.cs | 51 +++++++++---------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs index 36f63024374..d84b0ddd423 100644 --- a/Content.Server/_White/Genetics/Components/GenomeComponent.cs +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -26,6 +26,9 @@ public sealed partial class GenomeComponent : Component [ViewVariables] public List Mutations = default!; + [DataField("humanGenes")] + public bool HumanGenes = false; + /// /// Genome bits themselves. /// Data can be retrieved with comp.Layout.GetInt(comp.Genome, "name"), etc. diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index 7a2ccffc8cf..2a67c67a7ab 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -1,8 +1,10 @@ +using System.Collections; using System.Diagnostics.CodeAnalysis; using Content.Server._White.Genetics.Components; using Content.Server.GameTicking.Events; using Content.Shared._White.Genetics; using Content.Shared.GameTicking; +using Content.Shared.Humanoid; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -16,26 +18,30 @@ public sealed class GenomeSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly MutationSystem _mutationSystem = default!; + [Dependency] private readonly ILogManager _log = default!; + protected ISawmill Sawmill = default!; // This is where all the genome layouts are stored. // TODO: store on round entity when thats done, so persistence reloading doesnt scramble genes [ViewVariables] private readonly Dictionary _layouts = new(); private string _mutationsPool = "StandardHumanMutations"; - private Dictionary _mutations= new (); + private bool _mutationsInitialized = false; + private Dictionary _mutations = new (); public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnGenomeCompInit); SubscribeLocalEvent(Reset); + InitializeMutations(); + Sawmill = _log.GetSawmill("genetics"); } - private void OnInit(EntityUid uid, GenomeComponent comp, MapInitEvent args) + private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit args) { // only empty in test and when vving if (comp.GenomeId != string.Empty) @@ -45,14 +51,16 @@ private void OnInit(EntityUid uid, GenomeComponent comp, MapInitEvent args) private void Reset(RoundRestartCleanupEvent args) { _layouts.Clear(); + _mutations.Clear(); } - private void OnRoundStart(RoundStartingEvent ev) + private void InitializeMutations() { _proto.TryIndex(_mutationsPool, out var pool); if (pool == null) { //TODO: throw an error here + return; } else { @@ -60,38 +68,29 @@ private void OnRoundStart(RoundStartingEvent ev) { _proto.TryIndex(mutation, out var mutationProto); if (mutationProto != null) - _mutations.Add(mutationProto.Name, (mutationProto.Length, GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length))); + _mutations.Add(mutationProto.Name, (GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length), mutationProto.Effect)); } } + + _mutationsInitialized = true; } - public Genome GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(int length) + public Genome GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(int length, int cycle = 0) { var sequence = new Genome(length); - bool flag = true; - while (flag || !_mutations.ContainsValue((length, sequence))) - { - sequence.Mutate(0, length, 0.5f); + sequence.Mutate(0, length, 0.5f); - flag = false; + if (cycle >= 10) // i think 10 cycles is enough + { + return sequence; } - return sequence; - } - - public void ApplyMutation(EntityUid uid, string id, GenomeComponent comp) - { - if (!HasComp(uid)) - return; - switch (id) + foreach (var (_, (seq, _)) in _mutations) { - case "SuperStrength": - _mutationSystem.SuperStrength(uid, comp, true); - break; - case "XrayVision": - _mutationSystem.XrayVision(uid, comp, true); - break; + if (sequence == seq) + return GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(length, cycle + 1); } + return sequence; } /// From da1aee5ea8fdae02b914e21a7783b227d434a668 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sun, 16 Jun 2024 01:08:46 +0300 Subject: [PATCH 05/13] erm unfinished genome initialization --- .../_White/Genetics/GenomeSystem.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index 2a67c67a7ab..04dc1b41b94 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -46,6 +46,29 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit // only empty in test and when vving if (comp.GenomeId != string.Empty) comp.Layout = GetOrCreateLayout(comp.GenomeId); + + var mutations = Array.Empty(); + foreach (var (name, (index, len)) in comp.Layout.Values) + { + if (!name.Contains("mutation")) + continue; + + // инициализируй тут последовательность случайно мутацией, проверь нет ли ее в mutations + var mutatedBits = Array.Empty(); + var bits = comp.Layout.GetInt(comp.Genome, name); + + // мутируй тут случайный бит, сохрани в mutatedBits + + var prob = 0.4f; // need to evaluate + while (prob > 0.01f) + { + if (!_random.Prob(prob)) + break; + // мутируй тут случайный бит, если его нет в mutatedBits + // сохрани его в mutatedBits + prob = prob / 2; + } + } } private void Reset(RoundRestartCleanupEvent args) From 39c9d1d173fff9289635a66d88fbd9928ed32e9e Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:42:05 +0300 Subject: [PATCH 06/13] GenomeComponent initialization, bitarray operations --- .../Genetics/Components/GenomeComponent.cs | 1 + .../_White/Genetics/GenomeSystem.cs | 84 +++++++++++++++---- Content.Shared/_White/Genetics/Genome.cs | 15 +++- .../_White/Genetics/GenomeLayout.cs | 20 +++++ 4 files changed, 101 insertions(+), 19 deletions(-) diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs index d84b0ddd423..fb2fc77f27a 100644 --- a/Content.Server/_White/Genetics/Components/GenomeComponent.cs +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -6,6 +6,7 @@ namespace Content.Server._White.Genetics.Components; /// /// Gives this entity a genome for traits to be passed on and potentially mutated. /// Both of those must be handled by other systems, on its own it has no functionality. +/// TODO: комплиментарные последовательности /// [RegisterComponent] public sealed partial class GenomeComponent : Component diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index 04dc1b41b94..c10aeee9bcc 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -1,8 +1,10 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Server._White.Genetics.Components; using Content.Server.GameTicking.Events; using Content.Shared._White.Genetics; +using Content.Shared.Flash.Components; using Content.Shared.GameTicking; using Content.Shared.Humanoid; using Robust.Shared.Prototypes; @@ -21,7 +23,7 @@ public sealed class GenomeSystem : EntitySystem [Dependency] private readonly ILogManager _log = default!; - protected ISawmill Sawmill = default!; + protected ISawmill _sawmill = default!; // This is where all the genome layouts are stored. // TODO: store on round entity when thats done, so persistence reloading doesnt scramble genes [ViewVariables] @@ -38,36 +40,68 @@ public override void Initialize() SubscribeLocalEvent(OnGenomeCompInit); SubscribeLocalEvent(Reset); InitializeMutations(); - Sawmill = _log.GetSawmill("genetics"); + _sawmill = _log.GetSawmill("genetics"); } + + /// + /// TODO: test this whole thing + /// + /// + /// + /// private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit args) { // only empty in test and when vving if (comp.GenomeId != string.Empty) comp.Layout = GetOrCreateLayout(comp.GenomeId); - var mutations = Array.Empty(); + var mutationsPool = _mutations; foreach (var (name, (index, len)) in comp.Layout.Values) { if (!name.Contains("mutation")) continue; - // инициализируй тут последовательность случайно мутацией, проверь нет ли ее в mutations - var mutatedBits = Array.Empty(); - var bits = comp.Layout.GetInt(comp.Genome, name); + var mutationName = mutationsPool.Keys.ToArray()[_random.Next(mutationsPool.Count)]; - // мутируй тут случайный бит, сохрани в mutatedBits + var (genome, _) = mutationsPool[mutationName]; + var bits = genome.Bits; + if (bits.Length == 0) + { + _sawmill.Error($"Error while initializing a sequence in GenomeComponent. Name: {name}; Length: {len}"); + //throw new Exception() + continue; + } + var mutatedBits = Array.Empty(); - var prob = 0.4f; // need to evaluate - while (prob > 0.01f) + var prob = 0.99f; + while (prob > 0.001f) { if (!_random.Prob(prob)) + { + if (float.Round(prob, 2) == 0.99f) + { + // TODO: send the event ot apply the mutation here + } + break; + } + + var i = _random.Next(len); + + if (mutatedBits.Contains(i)) + { break; - // мутируй тут случайный бит, если его нет в mutatedBits - // сохрани его в mutatedBits + } + + bits[i] = !bits[i]; + mutatedBits.Append(i); + prob = prob / 2; } + + mutationsPool.Remove(mutationName); + comp.Layout.SetBitArray(comp.Genome, name, bits); + // TODO: сгенерить тут комплиментарную последовательность? } } @@ -75,6 +109,7 @@ private void Reset(RoundRestartCleanupEvent args) { _layouts.Clear(); _mutations.Clear(); + _mutationsInitialized = false; } private void InitializeMutations() @@ -85,14 +120,12 @@ private void InitializeMutations() //TODO: throw an error here return; } - else + + foreach (var mutation in pool.Mutations) { - foreach (var mutation in pool.Mutations) - { - _proto.TryIndex(mutation, out var mutationProto); - if (mutationProto != null) - _mutations.Add(mutationProto.Name, (GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length), mutationProto.Effect)); - } + _proto.TryIndex(mutation, out var mutationProto); + if (mutationProto != null) + _mutations.Add(mutationProto.Name, (GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length), mutationProto.Effect)); } _mutationsInitialized = true; @@ -105,6 +138,7 @@ public Genome GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunctio if (cycle >= 10) // i think 10 cycles is enough { + _sawmill.Error("Mutations initialization error. Try making longer sequences or less mutations"); return sequence; } @@ -193,4 +227,18 @@ private void AddLayout(string id, GenomeLayout layout) { _layouts.Add(id, layout); } + + public BitArray? GenerateRandomBitArrayPossiblyNull(int length) + { + if (length <= 0) + return null; + + var array = new BitArray(length); + for (int i = 0; i < length; i++) + { + array[i] = _random.Prob(0.5f); + } + + return array; + } } diff --git a/Content.Shared/_White/Genetics/Genome.cs b/Content.Shared/_White/Genetics/Genome.cs index ecacd4a1465..a6c82f45b29 100644 --- a/Content.Shared/_White/Genetics/Genome.cs +++ b/Content.Shared/_White/Genetics/Genome.cs @@ -154,6 +154,19 @@ public void SetBase(int index, char c) SetInt(index, bits: 2, value: value); } + /// + /// TODO: recheck + /// + /// + /// + public void SetBitArray(int index, BitArray array) + { + for (int i = index; i < array.Length; i++) + { + Bits[i] = array[i - index]; + } + } + /// /// Mutates a part of Genome from index to index + length. The more severity - the more mutated it will be. /// May need to move it to a different system. @@ -174,7 +187,7 @@ public void Mutate(int index, int length, float severity) } /// - /// Future function that will extend the genome (used for tg mutators) + /// Future function that will extend the genome (used for tg mutators). TODO /// /// /// diff --git a/Content.Shared/_White/Genetics/GenomeLayout.cs b/Content.Shared/_White/Genetics/GenomeLayout.cs index a7be58e3283..e0191b02d96 100644 --- a/Content.Shared/_White/Genetics/GenomeLayout.cs +++ b/Content.Shared/_White/Genetics/GenomeLayout.cs @@ -1,3 +1,4 @@ +using System.Collections; using Content.Shared._White.Genetics; using Robust.Shared.Utility; @@ -65,6 +66,25 @@ public void SetInt(Genome genome, string name, int value) genome.SetInt(index, bits: bits, value: value); } + /// + /// TODO: recheck + /// + /// + /// + /// + public void SetBitArray(Genome genome, string name, BitArray array) + { + var (index, bits) = Values[name]; + + if (bits != array.Length) + return; + + for (int i = 0; i < array.Length; i++) + { + genome.SetBool(index + i, array[i]); + } + } + /// /// Add a mapping to the layout. /// If length is 1 it will be a bool, int otherwise. From cdf839b4409592995173f4cc1b69c81faae5715e Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:33:08 +0300 Subject: [PATCH 07/13] GenomeSystem.Mutations, Glowing mutation, perekur --- .../Genetics/Components/GenomeComponent.cs | 26 +++- .../_White/Genetics/GenomeChangedEvent.cs | 18 +++ .../_White/Genetics/GenomePrototype.cs | 2 +- .../_White/Genetics/GenomeSystem.Mutations.cs | 117 ++++++++++++++++++ .../_White/Genetics/GenomeSystem.cs | 20 +-- .../Genetics/MutationCollectionPrototype.cs | 2 +- .../_White/Genetics/MutationEffect.cs | 12 ++ .../_White/Genetics/MutationPrototype.cs | 2 +- .../_White/Genetics/MutationSystem.cs | 70 ----------- .../Genetics/Mutations/GlowingMutation.cs | 22 ++++ Content.Shared/_White/Genetics/Genome.cs | 5 + .../_White/Genetics/MutationEffect.cs | 13 -- .../_White/Genetics/Mutations/XrayMutation.cs | 18 --- 13 files changed, 212 insertions(+), 115 deletions(-) create mode 100644 Content.Server/_White/Genetics/GenomeChangedEvent.cs rename {Content.Shared => Content.Server}/_White/Genetics/GenomePrototype.cs (95%) create mode 100644 Content.Server/_White/Genetics/GenomeSystem.Mutations.cs rename {Content.Shared => Content.Server}/_White/Genetics/MutationCollectionPrototype.cs (93%) create mode 100644 Content.Server/_White/Genetics/MutationEffect.cs rename {Content.Shared => Content.Server}/_White/Genetics/MutationPrototype.cs (95%) delete mode 100644 Content.Server/_White/Genetics/MutationSystem.cs create mode 100644 Content.Server/_White/Genetics/Mutations/GlowingMutation.cs delete mode 100644 Content.Shared/_White/Genetics/MutationEffect.cs delete mode 100644 Content.Shared/_White/Genetics/Mutations/XrayMutation.cs diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs index fb2fc77f27a..d2ac57998bc 100644 --- a/Content.Server/_White/Genetics/Components/GenomeComponent.cs +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -24,12 +24,30 @@ public sealed partial class GenomeComponent : Component [ViewVariables] public GenomeLayout Layout = new(); + /// + /// Activated mutations. Acquired through activators or radiation. + /// [ViewVariables] - public List Mutations = default!; + public List ActivatedMutations = new List(); + + /// + /// Mutations that were acquired via mutators. + /// + [DataField] + public List MutatedMutations = new List(); + /// + /// TODO: review + /// [DataField("humanGenes")] public bool HumanGenes = false; + /// + /// Key is the name of the region in GenomeLayout, value is the name of corresponding mutation. + /// + [DataField] + public Dictionary MutationRegions = new Dictionary(); + /// /// Genome bits themselves. /// Data can be retrieved with comp.Layout.GetInt(comp.Genome, "name"), etc. @@ -39,4 +57,10 @@ public sealed partial class GenomeComponent : Component /// [DataField] public Genome Genome = new(); + + /// + /// It is changed when a mutation through mutator is applied. + /// + [DataField] + public int Instability = 0; } diff --git a/Content.Server/_White/Genetics/GenomeChangedEvent.cs b/Content.Server/_White/Genetics/GenomeChangedEvent.cs new file mode 100644 index 00000000000..1fc69f6409b --- /dev/null +++ b/Content.Server/_White/Genetics/GenomeChangedEvent.cs @@ -0,0 +1,18 @@ +using System.Collections; +using Content.Server._White.Genetics.Components; + +namespace Content.Server._White.Genetics; + +public sealed class GenomeChangedEvent : EntityEventArgs +{ + public EntityUid Uid; + public GenomeComponent Comp = default!; + public Dictionary RegionsChanged = default!; + + public GenomeChangedEvent(EntityUid uid, GenomeComponent comp, Dictionary regions) + { + Uid = uid; + Comp = comp; + RegionsChanged = regions; + } +} diff --git a/Content.Shared/_White/Genetics/GenomePrototype.cs b/Content.Server/_White/Genetics/GenomePrototype.cs similarity index 95% rename from Content.Shared/_White/Genetics/GenomePrototype.cs rename to Content.Server/_White/Genetics/GenomePrototype.cs index 8e0b10a5fd4..52aac028212 100644 --- a/Content.Shared/_White/Genetics/GenomePrototype.cs +++ b/Content.Server/_White/Genetics/GenomePrototype.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Shared._White.Genetics; +namespace Content.Server._White.Genetics; /// /// Genome for an organism. diff --git a/Content.Server/_White/Genetics/GenomeSystem.Mutations.cs b/Content.Server/_White/Genetics/GenomeSystem.Mutations.cs new file mode 100644 index 00000000000..bcaee0fbbd3 --- /dev/null +++ b/Content.Server/_White/Genetics/GenomeSystem.Mutations.cs @@ -0,0 +1,117 @@ +using System.Collections; +using Content.Server._White.Genetics.Components; +using Content.Shared._White.Genetics; +using Content.Shared.Weapons.Melee; +using Robust.Shared.Prototypes; + +namespace Content.Server._White.Genetics; + +/// +/// Apply Mutation Apply? +/// +public sealed partial class GenomeSystem +{ + /// + /// TODO: recheck + /// + /// + private void OnGenomeChanged(GenomeChangedEvent args) + { + foreach (var (region, (was, became)) in args.RegionsChanged) + { + if (!args.Comp.Layout.Values.TryGetValue(region, out var indexes)) + continue; + + if (!args.Comp.MutationRegions.TryGetValue(region, out var possibleMutation)) + continue; + + if (!_mutations.TryGetValue(possibleMutation, out var mutation)) + continue; + + + if (args.Comp.ActivatedMutations.Contains(possibleMutation)) + { + if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) != + mutation.Genome.GetInt(0, mutation.Genome.GetLength())) + { + CancelMutation(args.Uid, args.Comp, possibleMutation); + } + } + else + { + if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) == + mutation.Genome.GetInt(0, mutation.Genome.GetLength())) + { + ApplyMutation(args.Uid, args.Comp, possibleMutation); + } + } + + //TODO: incorporated mutator mutations? + } + } + + public void ApplyMutation(EntityUid uid, GenomeComponent comp, string mutationName) + { + if (!_mutations.TryGetValue(mutationName, out var mutation)) + return; + + comp.Instability += mutation.Instability; + comp.ActivatedMutations.Add(mutationName); + foreach (var effect in mutation.Effects) + { + effect.Apply(uid, EntityManager); + } + } + + public void CancelMutation(EntityUid uid, GenomeComponent comp, string mutationName) + { + if (!comp.ActivatedMutations.Contains(mutationName) || _mutations.TryGetValue(mutationName, out var mutation)) + return; + + comp.ActivatedMutations.Remove(mutationName); + comp.Instability -= mutation.Instability; + foreach (var effect in mutation.Effects) + { + effect.Cancel(uid, EntityManager); + } + } + + + // TODO: снести ес + /* + public int GetDamage(string race) + { + var races = new Dictionary() + { + { "Arachnid", 5}, + { "Diona", 5}, + { "Dwarf", 5}, + { "Human", 5} + }; + + //TODO: больше рас + + return races[race]; + } + + public void SuperStrength(EntityUid uid, GenomeComponent genomeComp, bool state) + { + if (!HasComp(uid)) + return; + + if (state == true) + { + TryComp(uid, out var comp); + comp?.Damage.DamageDict.Add("Blunt", 30); + + genomeComp.ActivatedMutations.Add("SuperStrength"); + } + else + { + TryComp(uid, out var comp); + comp?.Damage.DamageDict.Add("Blunt", 5); + + genomeComp.ActivatedMutations.Remove("SuperStrength"); + } + } */ +} diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/GenomeSystem.cs index c10aeee9bcc..0c6f4efa864 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/GenomeSystem.cs @@ -15,11 +15,10 @@ namespace Content.Server._White.Genetics; /// /// Assigns each a random roundstart. /// -public sealed class GenomeSystem : EntitySystem +public sealed partial class GenomeSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly MutationSystem _mutationSystem = default!; [Dependency] private readonly ILogManager _log = default!; @@ -31,7 +30,7 @@ public sealed class GenomeSystem : EntitySystem private string _mutationsPool = "StandardHumanMutations"; private bool _mutationsInitialized = false; - private Dictionary _mutations = new (); + private Dictionary _mutations = new (); public override void Initialize() { @@ -39,6 +38,7 @@ public override void Initialize() SubscribeLocalEvent(OnGenomeCompInit); SubscribeLocalEvent(Reset); + SubscribeLocalEvent(OnGenomeChanged); InitializeMutations(); _sawmill = _log.GetSawmill("genetics"); } @@ -64,7 +64,7 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit var mutationName = mutationsPool.Keys.ToArray()[_random.Next(mutationsPool.Count)]; - var (genome, _) = mutationsPool[mutationName]; + var (genome, effect, _) = mutationsPool[mutationName]; //TODO: are mutations standardised in size? var bits = genome.Bits; if (bits.Length == 0) { @@ -79,9 +79,9 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit { if (!_random.Prob(prob)) { - if (float.Round(prob, 2) == 0.99f) + if (prob == 0.99f) { - // TODO: send the event ot apply the mutation here + ApplyMutation(uid, comp, mutationName); } break; } @@ -101,7 +101,7 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit mutationsPool.Remove(mutationName); comp.Layout.SetBitArray(comp.Genome, name, bits); - // TODO: сгенерить тут комплиментарную последовательность? + comp.MutationRegions.Add(name, mutationName); } } @@ -125,7 +125,7 @@ private void InitializeMutations() { _proto.TryIndex(mutation, out var mutationProto); if (mutationProto != null) - _mutations.Add(mutationProto.Name, (GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length), mutationProto.Effect)); + _mutations.Add(mutationProto.Name, (GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(mutationProto.Length), mutationProto.Effect, mutationProto.Instability)); } _mutationsInitialized = true; @@ -138,11 +138,11 @@ public Genome GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunctio if (cycle >= 10) // i think 10 cycles is enough { - _sawmill.Error("Mutations initialization error. Try making longer sequences or less mutations"); + _sawmill.Error("ActivatedMutations initialization error. Try making longer sequences or less mutations"); return sequence; } - foreach (var (_, (seq, _)) in _mutations) + foreach (var (_, (seq, _, _)) in _mutations) { if (sequence == seq) return GenerateSomeRandomGeneticSequenceAndCheckIfItIsIn_mutationsFunction(length, cycle + 1); diff --git a/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs b/Content.Server/_White/Genetics/MutationCollectionPrototype.cs similarity index 93% rename from Content.Shared/_White/Genetics/MutationCollectionPrototype.cs rename to Content.Server/_White/Genetics/MutationCollectionPrototype.cs index 2e723aae49f..32fc4bfa2c5 100644 --- a/Content.Shared/_White/Genetics/MutationCollectionPrototype.cs +++ b/Content.Server/_White/Genetics/MutationCollectionPrototype.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Shared._White.Genetics +namespace Content.Server._White.Genetics { /// /// This is a prototype for a mutation pool. diff --git a/Content.Server/_White/Genetics/MutationEffect.cs b/Content.Server/_White/Genetics/MutationEffect.cs new file mode 100644 index 00000000000..04024f6f7e9 --- /dev/null +++ b/Content.Server/_White/Genetics/MutationEffect.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace Content.Server._White.Genetics +{ + [ImplicitDataDefinitionForInheritors] + [MeansImplicitUse] + public abstract partial class MutationEffect + { + public abstract void Apply(EntityUid appliedEntity, IEntityManager entityManager); + public abstract void Cancel(EntityUid appliedEntity, IEntityManager entityManager); + } +} diff --git a/Content.Shared/_White/Genetics/MutationPrototype.cs b/Content.Server/_White/Genetics/MutationPrototype.cs similarity index 95% rename from Content.Shared/_White/Genetics/MutationPrototype.cs rename to Content.Server/_White/Genetics/MutationPrototype.cs index cf87d5049b4..3662d5f5f31 100644 --- a/Content.Shared/_White/Genetics/MutationPrototype.cs +++ b/Content.Server/_White/Genetics/MutationPrototype.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using Robust.Shared.Prototypes; -namespace Content.Shared._White.Genetics; +namespace Content.Server._White.Genetics; /// /// This is a prototype for mutation diff --git a/Content.Server/_White/Genetics/MutationSystem.cs b/Content.Server/_White/Genetics/MutationSystem.cs deleted file mode 100644 index d89dbdb9d9b..00000000000 --- a/Content.Server/_White/Genetics/MutationSystem.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Content.Server._White.Genetics.Components; -using Content.Shared.Weapons.Melee; -using Robust.Shared.Prototypes; - -namespace Content.Server._White.Genetics; - -/// -/// Apply Mutation Effect? -/// -public sealed class MutationSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _proto = default!; - - - public override void Initialize() - { - base.Initialize(); - } - - public int GetDamage(string race) - { - var races = new Dictionary() - { - { "Arachnid", 5}, - { "Diona", 5}, - { "Dwarf", 5}, - { "Human", 5} - }; - - //TODO: больше рас - - return races[race]; - } - - public void SuperStrength(EntityUid uid, GenomeComponent genomeComp, bool state) - { - if (!HasComp(uid)) - return; - - if (state == true) - { - TryComp(uid, out var comp); - comp?.Damage.DamageDict.Add("Blunt", 30); - - genomeComp.Mutations.Add("SuperStrength"); - } - else - { - TryComp(uid, out var comp); - comp?.Damage.DamageDict.Add("Blunt", 5); - - genomeComp.Mutations.Remove("SuperStrength"); - } - } - - public void XrayVision(EntityUid uid, GenomeComponent genomeComp, bool state) - { - if (!HasComp(uid)) - return; - - if (state == true) - { - //TODO - } - else - { - //TODO - } - } -} diff --git a/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs b/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs new file mode 100644 index 00000000000..eba1eb1a5d3 --- /dev/null +++ b/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs @@ -0,0 +1,22 @@ +using Content.Shared.Movement.Systems; +using JetBrains.Annotations; +using Robust.Server.GameObjects; + +namespace Content.Server._White.Genetics.Mutations +{ + [UsedImplicitly] + [DataDefinition] + public sealed partial class GlowingMutation : MutationEffect + { + public override void Apply(EntityUid uid, IEntityManager entityManager) + { + var light = new PointLightComponent(); + entityManager.AddComponent(uid, light); + } + + public override void Cancel(EntityUid uid, IEntityManager entityManager) + { + entityManager.RemoveComponent(uid); + } + } +} diff --git a/Content.Shared/_White/Genetics/Genome.cs b/Content.Shared/_White/Genetics/Genome.cs index a6c82f45b29..f25ba660add 100644 --- a/Content.Shared/_White/Genetics/Genome.cs +++ b/Content.Shared/_White/Genetics/Genome.cs @@ -92,6 +92,11 @@ public string GetBases(int index, int bases) return builder.ToString(); } + public int GetLength() + { + return Bits.Length; + } + /// /// Sets a boolean value at a bit index to a value. /// diff --git a/Content.Shared/_White/Genetics/MutationEffect.cs b/Content.Shared/_White/Genetics/MutationEffect.cs deleted file mode 100644 index 6d8b99bdc86..00000000000 --- a/Content.Shared/_White/Genetics/MutationEffect.cs +++ /dev/null @@ -1,13 +0,0 @@ -using JetBrains.Annotations; - -namespace Content.Shared._White.Genetics -{ - [ImplicitDataDefinitionForInheritors] - [MeansImplicitUse] - public abstract partial class MutationEffect - { - public abstract void Effect(MutationEffectArgs args); - } - - public readonly record struct MutationEffectArgs(EntityUid AppliedEntity, IEntityManager EntityManager); -} diff --git a/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs b/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs deleted file mode 100644 index cd000431c62..00000000000 --- a/Content.Shared/_White/Genetics/Mutations/XrayMutation.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Shared.Movement.Systems; -using JetBrains.Annotations; - -namespace Content.Shared._White.Genetics.Mutations -{ - [UsedImplicitly] - [DataDefinition] - public sealed partial class XrayMutation : MutationEffect - { - public override void Effect(MutationEffectArgs args) - { - if (args.EntityManager.TryGetComponent(args.AppliedEntity, out var eye)) - { - var eyes = args.EntityManager.SystemOrNull(); - } - } - } -} From d3d36099f62857882dc29416cc854020fd0d6391 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:05:57 +0300 Subject: [PATCH 08/13] injectors part 1 --- .../Components/GeneticInjectorComponent.cs | 13 ++++++ .../Genetics/Systems/GeneticInjectorSystem.cs | 45 +++++++++++++++++++ .../{ => Systems}/GenomeSystem.Mutations.cs | 28 ++++++------ .../Genetics/{ => Systems}/GenomeSystem.cs | 2 +- 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs create mode 100644 Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs rename Content.Server/_White/Genetics/{ => Systems}/GenomeSystem.Mutations.cs (75%) rename Content.Server/_White/Genetics/{ => Systems}/GenomeSystem.cs (99%) diff --git a/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs b/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs new file mode 100644 index 00000000000..8afbcfb31c0 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs @@ -0,0 +1,13 @@ +namespace Content.Server._White.Genetics.Components; + +public sealed partial class GeneticInjectorComponent : Component +{ + [DataField("mutatorMutations")] + public List MutationProtos = new List(); + + [DataField("activatorMutations")] + public List ActivatorMutations = new List(); + + [DataField("forced")] + public bool Forced = false; +} diff --git a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs new file mode 100644 index 00000000000..5d972efa894 --- /dev/null +++ b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs @@ -0,0 +1,45 @@ +using Content.Server._White.Genetics.Components; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Pidgin.Configuration; + +namespace Content.Server._White.Genetics; + +public sealed class GeneticInjectorSystem : EntitySystem +{ + [Dependency] private readonly GenomeSystem _genome = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAfterInteract); + } + + public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Target == null) + return; + + if (!TryComp(args.Target.Value, out var targetGenome)) + { + if (!entity.Comp.Forced) + { + return; + } + + targetGenome = new GenomeComponent(); + AddComp(args.Target.Value, targetGenome); + } + + // mutator mutations + foreach (var mutation in entity.Comp.MutationProtos) + { + _genome.ApplyMutatorMutation(args.Target.Value, targetGenome, mutation); + } + + // activator mutations + + + + + } +} diff --git a/Content.Server/_White/Genetics/GenomeSystem.Mutations.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs similarity index 75% rename from Content.Server/_White/Genetics/GenomeSystem.Mutations.cs rename to Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs index bcaee0fbbd3..25416d96c75 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.Mutations.cs +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs @@ -1,8 +1,4 @@ -using System.Collections; -using Content.Server._White.Genetics.Components; -using Content.Shared._White.Genetics; -using Content.Shared.Weapons.Melee; -using Robust.Shared.Prototypes; +using Content.Server._White.Genetics.Components; namespace Content.Server._White.Genetics; @@ -34,7 +30,7 @@ private void OnGenomeChanged(GenomeChangedEvent args) if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) != mutation.Genome.GetInt(0, mutation.Genome.GetLength())) { - CancelMutation(args.Uid, args.Comp, possibleMutation); + CancelMutatorMutation(args.Uid, args.Comp, possibleMutation); } } else @@ -42,7 +38,7 @@ private void OnGenomeChanged(GenomeChangedEvent args) if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) == mutation.Genome.GetInt(0, mutation.Genome.GetLength())) { - ApplyMutation(args.Uid, args.Comp, possibleMutation); + ApplyMutatorMutation(args.Uid, args.Comp, possibleMutation); } } @@ -50,25 +46,25 @@ private void OnGenomeChanged(GenomeChangedEvent args) } } - public void ApplyMutation(EntityUid uid, GenomeComponent comp, string mutationName) + public void ApplyMutatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) { if (!_mutations.TryGetValue(mutationName, out var mutation)) return; - comp.Instability += mutation.Instability; - comp.ActivatedMutations.Add(mutationName); + comp.Instability += mutation.Instability; //TODO: think + comp.MutatedMutations.Add(mutationName); foreach (var effect in mutation.Effects) { effect.Apply(uid, EntityManager); } } - public void CancelMutation(EntityUid uid, GenomeComponent comp, string mutationName) + public void CancelMutatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) { - if (!comp.ActivatedMutations.Contains(mutationName) || _mutations.TryGetValue(mutationName, out var mutation)) + if (!comp.MutatedMutations.Contains(mutationName) || _mutations.TryGetValue(mutationName, out var mutation)) return; - comp.ActivatedMutations.Remove(mutationName); + comp.MutatedMutations.Remove(mutationName); comp.Instability -= mutation.Instability; foreach (var effect in mutation.Effects) { @@ -76,6 +72,12 @@ public void CancelMutation(EntityUid uid, GenomeComponent comp, string mutationN } } + public void ApplyActivatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) + { + if (!_mutations.TryGetValue(mutationName, out var mutation)) + return; + } + // TODO: снести ес /* diff --git a/Content.Server/_White/Genetics/GenomeSystem.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.cs similarity index 99% rename from Content.Server/_White/Genetics/GenomeSystem.cs rename to Content.Server/_White/Genetics/Systems/GenomeSystem.cs index 0c6f4efa864..60876b1c37c 100644 --- a/Content.Server/_White/Genetics/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.cs @@ -81,7 +81,7 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit { if (prob == 0.99f) { - ApplyMutation(uid, comp, mutationName); + ApplyMutatorMutation(uid, comp, mutationName); } break; } From ec77fb8bd2162e4e4a6eb6249c2d5c9afb0bcb8d Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:45:00 +0300 Subject: [PATCH 09/13] future instability and radiations effects, moving stuff around --- .../Genetics/Systems/GeneticInjectorSystem.cs | 5 +- .../Systems/GenomeSystem.Instability.cs | 23 ++++++++ .../Systems/GenomeSystem.Mutations.cs | 56 +++++++++++++++++-- .../_White/Genetics/Systems/GenomeSystem.cs | 54 +++++++----------- 4 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 Content.Server/_White/Genetics/Systems/GenomeSystem.Instability.cs diff --git a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs index 5d972efa894..e0174ba2f85 100644 --- a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs +++ b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs @@ -3,7 +3,7 @@ using Content.Shared.Interaction; using Pidgin.Configuration; -namespace Content.Server._White.Genetics; +namespace Content.Server._White.Genetics.Systems; public sealed class GeneticInjectorSystem : EntitySystem { @@ -38,8 +38,5 @@ public void OnAfterInteract(Entity entity, ref AfterIn // activator mutations - - - } } diff --git a/Content.Server/_White/Genetics/Systems/GenomeSystem.Instability.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.Instability.cs new file mode 100644 index 00000000000..c0cc1b7686a --- /dev/null +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.Instability.cs @@ -0,0 +1,23 @@ +using Content.Server._White.Genetics.Components; + +namespace Content.Server._White.Genetics.Systems; + +public sealed partial class GenomeSystem +{ + public void CheckInstability(EntityUid uid, GenomeComponent comp, int delta) + { + if (delta > 0) + { + if (comp.Instability < 100) + return; + + // если эффекты уже готовятся примениться, но сущность себе еще мутации колит - надо отменить подготовку, пересчитать и применить новые + + // TODO: нужна прикольная функция плотности вероятности p(instability, x), где х - "плохость" эффекта + // ну или другой способ задать распределение для произвольного количества эффектов с произвольными показателями плохости и для произвольной 200 (?) > Instability > 100 + } + + + + } +} diff --git a/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs index 25416d96c75..b2b60254c7c 100644 --- a/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs @@ -1,6 +1,6 @@ using Content.Server._White.Genetics.Components; -namespace Content.Server._White.Genetics; +namespace Content.Server._White.Genetics.Systems; /// /// Apply Mutation Apply? @@ -30,7 +30,7 @@ private void OnGenomeChanged(GenomeChangedEvent args) if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) != mutation.Genome.GetInt(0, mutation.Genome.GetLength())) { - CancelMutatorMutation(args.Uid, args.Comp, possibleMutation); + CancelActivatorMutation(args.Uid, args.Comp, possibleMutation); } } else @@ -38,11 +38,11 @@ private void OnGenomeChanged(GenomeChangedEvent args) if (args.Comp.Genome.GetInt(indexes.Item1, indexes.Item2) == mutation.Genome.GetInt(0, mutation.Genome.GetLength())) { - ApplyMutatorMutation(args.Uid, args.Comp, possibleMutation); + ApplyActivatorMutation(args.Uid, args.Comp, possibleMutation); } } - //TODO: incorporated mutator mutations? + //TODO: incorporate mutator mutations? } } @@ -51,12 +51,14 @@ public void ApplyMutatorMutation(EntityUid uid, GenomeComponent comp, string mut if (!_mutations.TryGetValue(mutationName, out var mutation)) return; - comp.Instability += mutation.Instability; //TODO: think + comp.Instability += mutation.Instability; comp.MutatedMutations.Add(mutationName); foreach (var effect in mutation.Effects) { effect.Apply(uid, EntityManager); } + + CheckInstability(uid, comp, mutation.Instability); } public void CancelMutatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) @@ -70,12 +72,56 @@ public void CancelMutatorMutation(EntityUid uid, GenomeComponent comp, string mu { effect.Cancel(uid, EntityManager); } + + CheckInstability(uid, comp, -mutation.Instability); } public void ApplyActivatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) { + if (comp.ActivatedMutations.Contains(mutationName)) + return; + if (!_mutations.TryGetValue(mutationName, out var mutation)) return; + + if (!comp.MutationRegions.ContainsValue(mutationName)) + return; + + var activated = false; + foreach (var region in comp.MutationRegions.Keys) + { + if (mutationName != comp.MutationRegions[region]) + continue; + + comp.Layout.SetBitArray(comp.Genome, region, mutation.Genome.Bits); + + if (activated) + continue; + + comp.ActivatedMutations.Add(mutationName); + foreach (var effect in mutation.Effects) + { + effect.Apply(uid, EntityManager); + } + + activated = true; + } + } + + public void CancelActivatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) + { + if (!comp.ActivatedMutations.Contains(mutationName)) + return; + + if (!_mutations.TryGetValue(mutationName, out var mutation)) + return; + + foreach (var effect in mutation.Effects) + { + effect.Cancel(uid, EntityManager); + } + + comp.ActivatedMutations.Remove(mutationName); } diff --git a/Content.Server/_White/Genetics/Systems/GenomeSystem.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.cs index 60876b1c37c..eeacd67a447 100644 --- a/Content.Server/_White/Genetics/Systems/GenomeSystem.cs +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.cs @@ -4,13 +4,15 @@ using Content.Server._White.Genetics.Components; using Content.Server.GameTicking.Events; using Content.Shared._White.Genetics; +using Content.Shared.Damage; using Content.Shared.Flash.Components; using Content.Shared.GameTicking; using Content.Shared.Humanoid; +using Content.Shared.Radiation.Events; using Robust.Shared.Prototypes; using Robust.Shared.Random; -namespace Content.Server._White.Genetics; +namespace Content.Server._White.Genetics.Systems; /// /// Assigns each a random roundstart. @@ -39,6 +41,7 @@ public override void Initialize() SubscribeLocalEvent(OnGenomeCompInit); SubscribeLocalEvent(Reset); SubscribeLocalEvent(OnGenomeChanged); + SubscribeLocalEvent(OnIrradiated); InitializeMutations(); _sawmill = _log.GetSawmill("genetics"); } @@ -105,6 +108,21 @@ private void OnGenomeCompInit(EntityUid uid, GenomeComponent comp, ComponentInit } } + + /// + /// TODO: Test this shit + /// + /// + /// + /// + public void OnIrradiated(EntityUid uid, GenomeComponent comp, OnIrradiatedEvent args) + { + if (args.TotalRads > 20) + { + // TODO: change genome, apply random mutation via mutator + } + } + private void Reset(RoundRestartCleanupEvent args) { _layouts.Clear(); @@ -180,26 +198,6 @@ public GenomeLayout GetOrCreateLayout(string id) return layout; } - /// - /// Sets the Genome bits from a 's values. - /// - public void LoadGenes(EntityUid uid, ProtoId id, GenomeComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - var genes = _proto.Index(id); - foreach (var name in genes.Bools) - { - comp.Layout.SetBool(comp.Genome, name, true); - } - - foreach (var (name, value) in genes.Ints) - { - comp.Layout.SetInt(comp.Genome, name, value); - } - } - /// /// Copies the Genome bits from a parent to a child. /// They must use the same genome layout or it will be logged and copy nothing. @@ -227,18 +225,4 @@ private void AddLayout(string id, GenomeLayout layout) { _layouts.Add(id, layout); } - - public BitArray? GenerateRandomBitArrayPossiblyNull(int length) - { - if (length <= 0) - return null; - - var array = new BitArray(length); - for (int i = 0; i < length; i++) - { - array[i] = _random.Prob(0.5f); - } - - return array; - } } From 1b505b319515fc16b8999a5b8ec0269b43a986a2 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:45:09 +0300 Subject: [PATCH 10/13] the server side of injectors done + comments Took 48 minutes --- .../Components/GeneticInjectorComponent.cs | 14 +++++ .../Genetics/Mutations/GlowingMutation.cs | 1 + .../Genetics/Systems/GeneticInjectorSystem.cs | 63 ++++++++++++++++++- .../Systems/GenomeSystem.Mutations.cs | 8 ++- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs b/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs index 8afbcfb31c0..6a85341e48e 100644 --- a/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs +++ b/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + namespace Content.Server._White.Genetics.Components; public sealed partial class GeneticInjectorComponent : Component @@ -10,4 +13,15 @@ public sealed partial class GeneticInjectorComponent : Component [DataField("forced")] public bool Forced = false; + + [DataField("useDelay")] + public float UseDelay = 2.5f; + + [DataField("name")] + public string? Name; +} + +[Serializable, NetSerializable] +public sealed partial class GeneticInjectorDoAfterEvent : SimpleDoAfterEvent +{ } diff --git a/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs b/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs index eba1eb1a5d3..8b7c5780d9c 100644 --- a/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs +++ b/Content.Server/_White/Genetics/Mutations/GlowingMutation.cs @@ -8,6 +8,7 @@ namespace Content.Server._White.Genetics.Mutations [DataDefinition] public sealed partial class GlowingMutation : MutationEffect { + // Issues will arise if mutation is added to already glowing entity. public override void Apply(EntityUid uid, IEntityManager entityManager) { var light = new PointLightComponent(); diff --git a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs index e0174ba2f85..bd8930c2cc7 100644 --- a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs +++ b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs @@ -1,22 +1,31 @@ using Content.Server._White.Genetics.Components; +using Content.Server.DoAfter; +using Content.Server.Popups; using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction; -using Pidgin.Configuration; +using Content.Shared.Mobs.Systems; namespace Content.Server._White.Genetics.Systems; public sealed class GeneticInjectorSystem : EntitySystem { [Dependency] private readonly GenomeSystem _genome = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnInjectDoAfterComplete); SubscribeLocalEvent(OnAfterInteract); } + + // TODO: log public void OnAfterInteract(Entity entity, ref AfterInteractEvent args) { - if (args.Target == null) + if (args.Handled || !args.CanReach || args.Target == null) return; if (!TryComp(args.Target.Value, out var targetGenome)) @@ -30,13 +39,61 @@ public void OnAfterInteract(Entity entity, ref AfterIn AddComp(args.Target.Value, targetGenome); } + var delay = entity.Comp.UseDelay; + + if (args.User != args.Target.Value) + { + // Create a pop-up for the target + var userName = Identity.Entity(args.User, EntityManager); + _popup.PopupEntity(Loc.GetString("injector-component-injecting-target", + ("user", userName)), args.User, args.Target.Value); + + // Check if the target is incapacitated or in combat mode and modify time accordingly. + if (_mobState.IsIncapacitated(args.Target.Value)) + { + delay /= 2.5f; + } + } + else + { + delay /= 2.5f; + } + + // TODO: admin log here + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, delay, new GeneticInjectorDoAfterEvent(), + entity.Owner, target: args.Target.Value, used: entity.Owner) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 0.1f, + }); + } + + public void OnInjectDoAfterComplete(Entity injector, ref GeneticInjectorDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Target == null) + return; + + if (!TryComp(args.Target.Value, out var targetGenome)) + return; + + var mutationList = new List(); + // mutator mutations - foreach (var mutation in entity.Comp.MutationProtos) + foreach (var mutation in injector.Comp.MutationProtos) { _genome.ApplyMutatorMutation(args.Target.Value, targetGenome, mutation); + mutationList.Add(mutation); } // activator mutations + foreach (var mutation in injector.Comp.ActivatorMutations) + { + _genome.ApplyActivatorMutation(args.Target.Value, targetGenome, mutation); + mutationList.Add(mutation); + } + // TODO: admin log here, use mutationList } } diff --git a/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs index b2b60254c7c..5c3abb91da5 100644 --- a/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs +++ b/Content.Server/_White/Genetics/Systems/GenomeSystem.Mutations.cs @@ -76,6 +76,12 @@ public void CancelMutatorMutation(EntityUid uid, GenomeComponent comp, string mu CheckInstability(uid, comp, -mutation.Instability); } + /// + /// Applies the mutation indefinitely if entity has the corresponding region. sets that region + /// + /// + /// + /// public void ApplyActivatorMutation(EntityUid uid, GenomeComponent comp, string mutationName) { if (comp.ActivatedMutations.Contains(mutationName)) @@ -93,7 +99,7 @@ public void ApplyActivatorMutation(EntityUid uid, GenomeComponent comp, string m if (mutationName != comp.MutationRegions[region]) continue; - comp.Layout.SetBitArray(comp.Genome, region, mutation.Genome.Bits); + comp.Layout.SetBitArray(comp.Genome, region, mutation.Genome.Bits); // this should probably vary depending on context if (activated) continue; From d7574f1c2bd4c49f5bb26466ee442fd3c4882405 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:23:33 +0300 Subject: [PATCH 11/13] RemoveMutationsReagentEffect Took 43 minutes --- .../Genetics/RemoveMutationsReagentEffect.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Content.Server/_White/Genetics/RemoveMutationsReagentEffect.cs diff --git a/Content.Server/_White/Genetics/RemoveMutationsReagentEffect.cs b/Content.Server/_White/Genetics/RemoveMutationsReagentEffect.cs new file mode 100644 index 00000000000..c293e301cc8 --- /dev/null +++ b/Content.Server/_White/Genetics/RemoveMutationsReagentEffect.cs @@ -0,0 +1,44 @@ +using System.Text.RegularExpressions; +using Content.Server._White.Genetics.Components; +using Content.Server._White.Genetics.Systems; +using Content.Shared.Body.Prototypes; +using Content.Shared.Chemistry.Reagent; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._White.Genetics +{ + [UsedImplicitly] + public sealed partial class RemoveMutationsReagentEffect : ReagentEffect + { + + // TODO: add probability + public override void Effect(ReagentEffectArgs args) + { + if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out var genome)) + return; + + var genetics = args.EntityManager.EntitySysManager.GetEntitySystem(); + + foreach (var mutation in genome.MutatedMutations) + { + genetics.CancelMutatorMutation(args.SolutionEntity, genome, mutation); + } + + foreach (var mutation in genome.ActivatedMutations) + { + genetics.CancelActivatorMutation(args.SolutionEntity, genome, mutation); + } + } + + // TODO + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + //return Loc.GetString("reagent-effect-guidebook-adjust-reagent-reagent",("chance", Probability), ("deltasign", MathF.Sign(Amount.Float())), ("reagent", reagentProto.LocalizedName), ("amount", MathF.Abs(Amount.Float()))); + //throw new NotImplementedException(); + return "Hello"; + } + } +} + From e00e08dcfa6954b5824c613107ed50bd39aa9643 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:40:39 +0300 Subject: [PATCH 12/13] tweaks Took 35 minutes --- .../Genetics/Systems/GeneticInjectorSystem.cs | 16 ++++++- .../Components/GeneticInjectorComponent.cs | 18 ++++++-- .../Genetics/SharedGeneticInjectorSystem.cs | 43 +++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) rename {Content.Server => Content.Shared}/_White/Genetics/Components/GeneticInjectorComponent.cs (60%) create mode 100644 Content.Shared/_White/Genetics/SharedGeneticInjectorSystem.cs diff --git a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs index bd8930c2cc7..14c208422eb 100644 --- a/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs +++ b/Content.Server/_White/Genetics/Systems/GeneticInjectorSystem.cs @@ -1,14 +1,16 @@ using Content.Server._White.Genetics.Components; using Content.Server.DoAfter; using Content.Server.Popups; +using Content.Shared._White.Genetics; using Content.Shared.DoAfter; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Mobs.Systems; +using Content.Shared._White.Genetics.Components; namespace Content.Server._White.Genetics.Systems; -public sealed class GeneticInjectorSystem : EntitySystem +public sealed class GeneticInjectorSystem : SharedGeneticInjectorSystem { [Dependency] private readonly GenomeSystem _genome = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -39,6 +41,12 @@ public void OnAfterInteract(Entity entity, ref AfterIn AddComp(args.Target.Value, targetGenome); } + if (!entity.Comp.Used) + return; + + if (entity.Comp.MutationProtos.Count + entity.Comp.ActivatorMutations.Count == 0) + return; + var delay = entity.Comp.UseDelay; if (args.User != args.Target.Value) @@ -94,6 +102,12 @@ public void OnInjectDoAfterComplete(Entity injector, r mutationList.Add(mutation); } + injector.Comp.MutationProtos.Clear(); // TODO: probably need another way to make injectors single-use + injector.Comp.ActivatorMutations.Clear(); + injector.Comp.Used = true; + + + // TODO: admin log here, use mutationList } } diff --git a/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs b/Content.Shared/_White/Genetics/Components/GeneticInjectorComponent.cs similarity index 60% rename from Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs rename to Content.Shared/_White/Genetics/Components/GeneticInjectorComponent.cs index 6a85341e48e..37c1f755414 100644 --- a/Content.Server/_White/Genetics/Components/GeneticInjectorComponent.cs +++ b/Content.Shared/_White/Genetics/Components/GeneticInjectorComponent.cs @@ -1,7 +1,7 @@ using Content.Shared.DoAfter; using Robust.Shared.Serialization; -namespace Content.Server._White.Genetics.Components; +namespace Content.Shared._White.Genetics.Components; public sealed partial class GeneticInjectorComponent : Component { @@ -17,8 +17,20 @@ public sealed partial class GeneticInjectorComponent : Component [DataField("useDelay")] public float UseDelay = 2.5f; - [DataField("name")] - public string? Name; + [DataField] + public bool Used = false; + + /// + /// Sprite state to use if Used = false + /// + [DataField] + public string NewState = "new"; + + /// + /// Sprite state to use if Used = true + /// + [DataField] + public string UsedState = "used"; } [Serializable, NetSerializable] diff --git a/Content.Shared/_White/Genetics/SharedGeneticInjectorSystem.cs b/Content.Shared/_White/Genetics/SharedGeneticInjectorSystem.cs new file mode 100644 index 00000000000..22ec9e82cbd --- /dev/null +++ b/Content.Shared/_White/Genetics/SharedGeneticInjectorSystem.cs @@ -0,0 +1,43 @@ +using Content.Shared._White.Genetics.Components; +using Content.Shared.Administration.Logs; +using Content.Shared.CombatMode; +using Content.Shared.DoAfter; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; + +namespace Content.Shared._White.Genetics; + +public partial class SharedGeneticInjectorSystem : EntitySystem +{ + [Dependency] protected readonly SharedPopupSystem _popup = default!; + [Dependency] protected readonly MobStateSystem _mobState = default!; + [Dependency] protected readonly SharedCombatModeSystem _combat = default!; + [Dependency] protected readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] protected readonly ISharedAdminLogManager _adminLogger = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInjectorStartup); + SubscribeLocalEvent(OnInjectorUse); + } + + private void OnInjectorStartup(Entity entity, ref ComponentStartup args) + { + // ???? why ????? + // TODO: wtf is this + Dirty(entity); + } + + private void OnInjectorUse(Entity entity, ref UseInHandEvent args) + { + if (args.Handled) + return; + + // inject yourself here + + args.Handled = true; + } + + +} From 6d606830f35f60f03e21757b48c58a6ed640fbf6 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Tue, 9 Jul 2024 20:36:42 +0300 Subject: [PATCH 13/13] erm Took 1 hour 17 minutes --- .../Genetics/DNAScannerBoundUserInterface.cs | 26 +++++ .../_White/Genetics/DNAScannerWindow.xaml | 5 + .../_White/Genetics/DNAScannerWindow.xaml.cs | 15 +++ .../Components/DNAScannerComponent.cs | 39 ++++++++ .../Genetics/Components/GenomeComponent.cs | 2 +- .../Genetics/Systems/DNAScannerSystem.cs | 96 +++++++++++++++++++ .../_White/Genetics/DNAScannerDoAfterEvent.cs | 9 ++ .../DNAScannerScannedGenomeMessage.cs | 35 +++++++ .../_White/Genetics/DNAScannerUiKey.cs | 12 +++ 9 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 Content.Client/_White/Genetics/DNAScannerBoundUserInterface.cs create mode 100644 Content.Client/_White/Genetics/DNAScannerWindow.xaml create mode 100644 Content.Client/_White/Genetics/DNAScannerWindow.xaml.cs create mode 100644 Content.Server/_White/Genetics/Components/DNAScannerComponent.cs create mode 100644 Content.Server/_White/Genetics/Systems/DNAScannerSystem.cs create mode 100644 Content.Shared/_White/Genetics/DNAScannerDoAfterEvent.cs create mode 100644 Content.Shared/_White/Genetics/DNAScannerScannedGenomeMessage.cs create mode 100644 Content.Shared/_White/Genetics/DNAScannerUiKey.cs diff --git a/Content.Client/_White/Genetics/DNAScannerBoundUserInterface.cs b/Content.Client/_White/Genetics/DNAScannerBoundUserInterface.cs new file mode 100644 index 00000000000..d9399c56cb0 --- /dev/null +++ b/Content.Client/_White/Genetics/DNAScannerBoundUserInterface.cs @@ -0,0 +1,26 @@ +namespace Content.Client._White.Genetics +{ + [UserImplicitly] + public sealed class DNAScannerBoundUserInterface : BoundUserInterface + { + [ViewVariables] + private DNAScannerWindow? _window; + + public DNAScannerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _window = new DNAScannerWindow + { + Title = EntMan.GetComponent(Owner).EntityName, + }; + _window.OnClose += Close; + _window.OpenCentered(); + } + + } + +} diff --git a/Content.Client/_White/Genetics/DNAScannerWindow.xaml b/Content.Client/_White/Genetics/DNAScannerWindow.xaml new file mode 100644 index 00000000000..fb78443dc19 --- /dev/null +++ b/Content.Client/_White/Genetics/DNAScannerWindow.xaml @@ -0,0 +1,5 @@ + + + diff --git a/Content.Client/_White/Genetics/DNAScannerWindow.xaml.cs b/Content.Client/_White/Genetics/DNAScannerWindow.xaml.cs new file mode 100644 index 00000000000..f7a1f4a4afe --- /dev/null +++ b/Content.Client/_White/Genetics/DNAScannerWindow.xaml.cs @@ -0,0 +1,15 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._White.Genetics; + +[GenerateTypedNameReferences] +public sealed partial class DNAScannerWindow : Control +{ + public DNAScannerWindow() + { + RobustXamlLoader.Load(this); + } +} + diff --git a/Content.Server/_White/Genetics/Components/DNAScannerComponent.cs b/Content.Server/_White/Genetics/Components/DNAScannerComponent.cs new file mode 100644 index 00000000000..19e816fb166 --- /dev/null +++ b/Content.Server/_White/Genetics/Components/DNAScannerComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.Audio; + +namespace Content.Server._White.Genetics.Components; + +/// +/// +/// +public sealed partial class DNAScannerComponent : Component +{ + /// + /// How long it takes to scan someone. + /// + [DataField("UseDelay")] + public TimeSpan ScanDelay = TimeSpan.FromSeconds(0.8); + + /// + /// The maximum range in tiles at which the analyzer can receive continuous updates + /// + [DataField] + public float MaxScanRange = 2.5f; + + /// + /// Sound played on scanning begin + /// + [DataField] + public SoundSpecifier? ScanningBeginSound; + + /// + /// Sound played on scanning end + /// + [DataField] + public SoundSpecifier? ScanningEndSound; + + /// + /// Genome that was scanned. + /// + [DataField] + public GenomeComponent? ScannedGenome; +} diff --git a/Content.Server/_White/Genetics/Components/GenomeComponent.cs b/Content.Server/_White/Genetics/Components/GenomeComponent.cs index d2ac57998bc..ada893dd21e 100644 --- a/Content.Server/_White/Genetics/Components/GenomeComponent.cs +++ b/Content.Server/_White/Genetics/Components/GenomeComponent.cs @@ -37,7 +37,7 @@ public sealed partial class GenomeComponent : Component public List MutatedMutations = new List(); /// - /// TODO: review + /// TODO: wtf is this /// [DataField("humanGenes")] public bool HumanGenes = false; diff --git a/Content.Server/_White/Genetics/Systems/DNAScannerSystem.cs b/Content.Server/_White/Genetics/Systems/DNAScannerSystem.cs new file mode 100644 index 00000000000..9c0b316747c --- /dev/null +++ b/Content.Server/_White/Genetics/Systems/DNAScannerSystem.cs @@ -0,0 +1,96 @@ +using Content.Server._White.Genetics.Components; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Forensics; +using Content.Server.PowerCell; +using Content.Shared._White.Genetics; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Mobs.Components; +using Content.Shared.PowerCell; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Player; + +namespace Content.Server._White.Genetics.Systems; + +/// +/// This handles... +/// +public sealed class DNAScannerSystem : EntitySystem +{ + [Dependency] private readonly PowerCellSystem _cell = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnDoAfter); + } + + /// + /// Trigger the doafter for scanning + /// + private void OnAfterInteract(Entity uid, ref AfterInteractEvent args) + { + if (args.Target == null || !args.CanReach || !HasComp(args.Target) || !_cell.HasDrawCharge(uid, user: args.User)) + return; + + _audio.PlayPvs(uid.Comp.ScanningBeginSound, uid); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, uid.Comp.ScanDelay, new DNAScannerDoAfterEvent(), uid, target: args.Target, used: uid) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + NeedHand = true + }); + } + + private void OnDoAfter(Entity uid, ref DNAScannerDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid, user: args.User)) + return; + + _audio.PlayPvs(uid.Comp.ScanningEndSound, uid); + + if (!TryComp(args.Target, out var genome)) + return; + + uid.Comp.ScannedGenome = genome; + + OpenUserInterface(args.User, uid); + + if (!_uiSystem.TryGetUi(uid.Owner, DNAScannerUiKey.Key, out var ui)) + return; + + string? fingerPrints = null; + if (TryComp(args.Target, out var dna)) + fingerPrints = dna.DNA; + + _uiSystem.SendUiMessage(ui, new DNAScannerScannedGenomeMessage( + GetNetEntity(args.Target), + genome.Genome, + genome.Layout, + genome.MutationRegions, + genome.MutatedMutations, + genome.ActivatedMutations, + fingerPrints + )); + + args.Handled = true; + } + + private void OpenUserInterface(EntityUid user, EntityUid scanner) + { + if (!TryComp(user, out var actor) || !_uiSystem.TryGetUi(scanner, DNAScannerUiKey.Key, out var ui)) + return; + + _uiSystem.OpenUi(ui, actor.PlayerSession); + } + + + +} diff --git a/Content.Shared/_White/Genetics/DNAScannerDoAfterEvent.cs b/Content.Shared/_White/Genetics/DNAScannerDoAfterEvent.cs new file mode 100644 index 00000000000..26a54f4e5a4 --- /dev/null +++ b/Content.Shared/_White/Genetics/DNAScannerDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Genetics; + +[Serializable, NetSerializable] +public sealed partial class DNAScannerDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/_White/Genetics/DNAScannerScannedGenomeMessage.cs b/Content.Shared/_White/Genetics/DNAScannerScannedGenomeMessage.cs new file mode 100644 index 00000000000..15ed80baa2d --- /dev/null +++ b/Content.Shared/_White/Genetics/DNAScannerScannedGenomeMessage.cs @@ -0,0 +1,35 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Genetics; + +/// +/// On interacting with an entity retrieves the entity UID for use with getting the current damage of the mob. +/// +[Serializable, NetSerializable] +public sealed class DNAScannerScannedGenomeMessage : BoundUserInterfaceMessage +{ + public readonly NetEntity? TargetEntity; + public Genome Genome; + public GenomeLayout Layout; + public Dictionary MutationRegions; + public List MutatedMutations; + public List ActivatedMutations; + public string? FingerPrints; + + public DNAScannerScannedGenomeMessage(NetEntity? targetEntity, + Genome genome, + GenomeLayout layout, + Dictionary mutationRegions, + List mutatedMutations, + List activatedMutations, + string? fingerPrints) + { + TargetEntity = targetEntity; + Genome = genome; + Layout = layout; + MutationRegions = mutationRegions; + MutatedMutations = mutatedMutations; + ActivatedMutations = activatedMutations; + FingerPrints = fingerPrints; + } +} diff --git a/Content.Shared/_White/Genetics/DNAScannerUiKey.cs b/Content.Shared/_White/Genetics/DNAScannerUiKey.cs new file mode 100644 index 00000000000..f3fd5699e4d --- /dev/null +++ b/Content.Shared/_White/Genetics/DNAScannerUiKey.cs @@ -0,0 +1,12 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Genetics; + +/// +/// This handles... +/// +[Serializable, NetSerializable] +public enum DNAScannerUiKey : byte +{ + Key +}