Merge branch 'master' into euphoriacardport
Signed-off-by: freezer dog <iamabanana372456@gmail.com>
This commit is contained in:
commit
af2b242976
|
|
@ -31,7 +31,7 @@ Examples of Pets include, but are not limited to:
|
|||
:* hand-held "personal AI" devices
|
||||
|
||||
== Synthetic ==
|
||||
Any Entity consisting primarily of non-organic matter, designed and/or constructed by NanoTrasen or its Employees for the purpose of performing tasks aboard a Space Station. Synthetics are bound by software limitations, typically referred to as Synthetic or Silicon Laws, and physically incorporated into their chassis. Any damage to a Synthetic- including un-authorized modification of their Laws- may leave the offender liable to charges of Sabotage. Irrevocable destruction of a Synthetic may leave the offender liable to the crime of Grand Sabotage.
|
||||
Any Entity consisting primarily of non-organic matter, designed and/or constructed by NanoTrasen or its Employees for the purpose of performing tasks aboard a Space Station. Synthetics are bound by software limitations, typically referred to as Synthetic or Silicon Laws, and physically incorporated into their chassis. Any damage to a Synthetic- including un-authorized modification of their Laws- may leave the offender liable to charges of Sabotage.
|
||||
Examples of Synthetics include, but are not limited to:
|
||||
:* Crew-constructed Cyborgs
|
||||
:* The Station AI
|
||||
|
|
@ -86,7 +86,7 @@ Any individual requiring any above item in the course of their job, or for regul
|
|||
Should the holder commit crimes through the use of such authorized items, the authorizing party may be found guilty of Accessory to said crimes, in addition to Endangerment and Abuse of Power where applicable. The Commanding Officer is also authorized to supervise the approval and revocation of documentation as they see fit.
|
||||
|
||||
== Controlled Substances ==
|
||||
Any individual requiring a Controlled Substance in the course of their job, or for regular function, must request access to a perscription or special dispensation from the Chief Medical Officer. The Psychologist is also permitted to grant prescriptions for psychoactive drugs. Any such document must state the substance(s) being prescribed, the amount(s) where applicable, and the stamp and signature of the authorizing party.
|
||||
Any individual requiring a Controlled Substance in the course of their job, or for regular function, must request access to a prescription or special dispensation from the Chief Medical Officer. Medical personnel are also permitted to [[Standard Operating Procedure#Prescribing Medication|prescribe medication]] for chronic illnesses or mental health. Any such document must state the substance(s) being prescribed, the amount(s) where applicable, and the stamp and signature of the authorizing party.
|
||||
|
||||
A list of such Substances follow:
|
||||
|
||||
|
|
@ -370,6 +370,38 @@ Given good medical reason and with documentation, the Chief Medical Officer of a
|
|||
|
||||
In order for a medical evaluation to be considered lawfully applicable, the CMO and relevant head of department must both agree that the specified crewmate is unfit for duty in order to proceed with the demotion. In the case of the Commanding Officer, Central Command should be contacted as soon as possible.
|
||||
|
||||
==Prescribing Medication==
|
||||
In the event that a patient requires stronger medication outside of medbay, Medical personnel have the ability to prescribe medicine to a patient. Prescriptions must be written on valid prescription paper, as well as signed and stamped with a valid medical personnel's stamp and signature. Any such document must state the substance(s) being prescribed as well as the amount(s) where applicable. The chief medical officer also has the right to revoke any prescription with valid medical reasoning. Prescriptions are not valid if they are both intended for and signed and stamped by the same person (e.g. You cannot prescribe something to yourself as a doctor).
|
||||
|
||||
=== Prescription Medicine ===
|
||||
Prescriptions that are valid with any medical personnel's stamp and signature are as follows:
|
||||
* diphenylmethylamine
|
||||
* equilibrazine
|
||||
* tranquinase
|
||||
* neurozenium
|
||||
* serenitol
|
||||
* soretizone
|
||||
|
||||
Highly-addictive mental health prescriptions that are only valid with the chief medical officer's or psychologist's stamp and signature are as follows:
|
||||
* blissifylovene
|
||||
* happiness
|
||||
|
||||
Highly-addictive or potentially-harmful prescriptions that are only valid with the chief medical officer's stamp and signature are as follows:
|
||||
* agonolexyne
|
||||
* desoxyephedrine
|
||||
* nocturine
|
||||
|
||||
=== Dangerous Chemical Authorizations ===
|
||||
|
||||
Other dangerous chemicals that may be authorized and given out ONLY with valid written permission with the chief medical officer's stamp are as follows:
|
||||
* mindbreaker, heartbreaker, and mute toxins
|
||||
* amatoxin and other dangerous poisons
|
||||
* phlogiston, napalm, and chlorine trifluoride
|
||||
* vestine and romerol
|
||||
* Any other [[Standard Operating Procedure#Controlled Substances|controlled substances]] not listed here
|
||||
|
||||
The chief medical officer is also required to inform and/or forward authorization of any dangerous chemical to Security.
|
||||
|
||||
==Quarantine==
|
||||
Medical staff are afforded the right to quarantine individuals that currently suffer from infectious or contagious diseases, and may confine them within pre-designated quarantine areas in the course of treatment, until the point that they are no longer infectious or harmful to the rest of the crew.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared._DV.MedicalRecords;
|
||||
using JetBrains.Annotations; // DeltaV - Medical Records
|
||||
using Content.Shared._DV.Medical; // DeltaV
|
||||
using Content.Shared._DV.MedicalRecords; // DeltaV
|
||||
using JetBrains.Annotations; // DeltaV
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
|
||||
namespace Content.Client.HealthAnalyzer.UI
|
||||
{
|
||||
[UsedImplicitly]
|
||||
|
|
@ -22,6 +24,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
|||
_window = this.CreateWindow<HealthAnalyzerWindow>();
|
||||
_window.HealthAnalyzer.OnTriageStatusChanged += SendTriageStatusMessage; // DeltaV - Medical Records
|
||||
_window.HealthAnalyzer.OnClaimPatient += SendTriageClaimMessage; // DeltaV - Medical Records
|
||||
_window.HealthAnalyzer.OnPrintMedTekRecord += () => SendMessage(new HealthAnalyzerPrintReportMessage()); // DeltaV - MedTekReports
|
||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<BoxContainer
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client._DV.UserInterfaces"
|
||||
xmlns:xe="clr-namespace:Content.Client.UserInterface.XamlExtensions"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical">
|
||||
Orientation="Vertical"> <!-- DeltaV - added ui and xe namespaces -->
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}" />
|
||||
|
|
@ -11,7 +13,8 @@
|
|||
Margin="0 0 0 5"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
|
||||
<TextureRect Name="NoDataTex" Access="Public" SetSize="96 96" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/> <!-- DeltaV - Size changed to 96-->
|
||||
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64" />
|
||||
<TextureRect Name="NoDataTex" Access="Public" SetSize="64 64" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/>
|
||||
<BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
|
||||
<RichTextLabel Name="NameLabel" SetWidth="150" />
|
||||
<Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />
|
||||
|
|
@ -22,6 +25,14 @@
|
|||
<Label Margin="0 0 5 0" HorizontalExpand="True" HorizontalAlignment="Right" VerticalExpand="True"
|
||||
VerticalAlignment="Top" Name="ScanModeLabel"
|
||||
Text="{Loc 'health-analyzer-window-entity-unknown-text'}" />
|
||||
<!-- BEGIN DeltaV - MedTek Reports -->
|
||||
<BoxContainer HorizontalExpand="False"
|
||||
Orientation="Vertical"
|
||||
Name="PrintButtonsContainer"
|
||||
Margin="0 0 5 0">
|
||||
<ui:IconButton Name="PrintReportButton" Icon="{xe:Tex '/Textures/_DV/Interface/VerbIcons/print.png'}" />
|
||||
</BoxContainer>
|
||||
<!-- END DeltaV -->
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
public event Action<TriageStatus>? OnTriageStatusChanged;
|
||||
public event Action? OnClaimPatient;
|
||||
// End DeltaV - Medical Records
|
||||
public Action? OnPrintMedTekRecord; // DeltaV - MedTek Reports
|
||||
|
||||
|
||||
public HealthAnalyzerControl()
|
||||
{
|
||||
|
|
@ -84,6 +86,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
StatusBox.Children.Last().RemoveStyleClass("ButtonSquare");
|
||||
ClaimButton.OnPressed += _ => OnClaimPatient?.Invoke();
|
||||
// End DeltaV - Medical Records
|
||||
PrintReportButton.OnPressed += (_) => OnPrintMedTekRecord?.Invoke(); // DeltaV - MedTek Reports
|
||||
}
|
||||
|
||||
public void Populate(HealthAnalyzerUiState state)
|
||||
|
|
@ -109,7 +112,12 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
|
||||
ScanModeLabel.FontColorOverride = state.ScanMode.HasValue && state.ScanMode.Value ? Color.Green : Color.Red;
|
||||
|
||||
PrintReportButton.Disabled = state is { ScanMode: false }; // DeltaV
|
||||
|
||||
// Patient Information
|
||||
SpriteView.SetEntity(target.Value);
|
||||
SpriteView.Visible = state.ScanMode.HasValue && state.ScanMode.Value;
|
||||
NoDataTex.Visible = !SpriteView.Visible;
|
||||
|
||||
var name = new FormattedMessage();
|
||||
name.PushColor(Color.White);
|
||||
|
|
@ -255,7 +263,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("damageGroup", _prototypes.Index(damageGroupId).LocalizedName),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
|
|
@ -270,7 +278,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
GroupsContainer.AddChild(groupContainer);
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
|
||||
var group = _prototypes.Index(damageGroupId);
|
||||
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ namespace Content.Client.Lobby.UI
|
|||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||
{
|
||||
// DeltaV - Ignore hidden species.
|
||||
if (character is HumanoidCharacterProfile hcp && SpeciesHiderSystem.IsHidden(hcp.Species.Id))
|
||||
if (character is { } hcp && SpeciesHiderSystem.IsHidden(hcp.Species.Id))
|
||||
continue;
|
||||
// End DeltaV
|
||||
numberOfFullSlots++;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._DV.Species;
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
|
|
@ -159,6 +160,8 @@ public sealed partial class HumanoidProfileEditor
|
|||
|
||||
for (var i = 0; i < _species.Count; i++)
|
||||
{
|
||||
if (SpeciesHiderSystem.IsHidden(_species[i].ID)) // Delta V - Don't add hidden species
|
||||
continue;
|
||||
var name = Loc.GetString(_species[i].Name);
|
||||
SpeciesButton.AddItem(name, i);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq; // DeltaV
|
||||
using Content.Client.Pinpointer.UI;
|
||||
using Content.Client.Station; // DeltaV
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player; // DeltaV
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
|
@ -10,6 +12,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||
{
|
||||
[Dependency] private IPlayerManager _playerManager = default!; // DeltaV
|
||||
private readonly SharedTransformSystem _xform; // DeltaV
|
||||
private readonly StationSystem _station; // DeltaV
|
||||
|
||||
public NetEntity? Focus;
|
||||
public Dictionary<NetEntity, string> LocalizedNames = new();
|
||||
|
|
@ -21,6 +24,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||
{
|
||||
IoCManager.InjectDependencies(this); // DeltaV - Gotta inject!
|
||||
_xform = EntManager.System<SharedTransformSystem>(); // DeltaV - Gotta inject!
|
||||
_station = EntManager.System<StationSystem>(); // DeltaV - Gotta Inject!
|
||||
|
||||
WallColor = new Color(192, 122, 196);
|
||||
TileColor = new(71, 42, 72);
|
||||
|
|
@ -97,7 +101,12 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||
if (!MapUid.HasValue && _playerManager.LocalEntity is { } player)
|
||||
{
|
||||
MapUid = _xform.GetGrid(player);
|
||||
forceMapUpdate = true;
|
||||
|
||||
// If it still doesn't have a value, just use the largest grid of the station.
|
||||
if (!MapUid.HasValue)
|
||||
MapUid = _station.GetLargestGrid(_station.GetStations().First());
|
||||
|
||||
forceMapUpdate = MapUid.HasValue;
|
||||
}
|
||||
|
||||
base.Draw(handle);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,21 @@
|
|||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<!-- BEGIN DeltaV - Station/Shuttle Map Buttons -->
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Button Name="StationMapButton"
|
||||
ToggleMode="True"
|
||||
Text="{Loc crew-monitoring-ui-button-station-map}"
|
||||
StyleClasses="OpenRight"
|
||||
HorizontalExpand="True" />
|
||||
<Button Name="ShuttleMapButton"
|
||||
ToggleMode="True"
|
||||
Text="{Loc crew-monitoring-ui-button-shuttle-map}"
|
||||
StyleClasses="OpenLeft"
|
||||
HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<!-- END DeltaV -->
|
||||
|
||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
||||
PlaceHolder="{Loc crew-monitoring-ui-filter-line-placeholder}" />
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||
|
||||
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
||||
ShuttleMap.NavMap = NavMap; // DeltaV - crew monitoring radar
|
||||
|
||||
StationMapButton.OnPressed += _ => EnableShuttleMap(false); // DeltaV - crew monitoring radar
|
||||
ShuttleMapButton.OnPressed += _ => EnableShuttleMap(true); // DeltaV - crew monitoring radar
|
||||
}
|
||||
|
||||
public void Set(string stationName, EntityUid? mapUid)
|
||||
|
|
@ -57,6 +60,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||
// Begin DeltaV - handle navmap visibility based on current position
|
||||
// else
|
||||
// NavMap.Visible = false;
|
||||
EnableShuttleMap(!NavMap.MapUid.HasValue);
|
||||
// End DeltaV - handle navmap visibility based on current position
|
||||
|
||||
StationName.AddStyleClass("LabelBig");
|
||||
|
|
@ -68,28 +72,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
// Begin DeltaV - handle navmap visibility based on current position
|
||||
var ent = ShuttleMap.Owner;
|
||||
|
||||
// If an entity is being tracked, use them as the focus, since they may be off-station
|
||||
// and we want to use the nav map if they are.
|
||||
if (_trackedEntity is { } trackedEntity)
|
||||
ent = _entManager.GetEntity(trackedEntity);
|
||||
|
||||
// We need to make sure the entity is valid because for some reason, it *could* be an
|
||||
// invalid entity and GetOwningStation will throw an exception.
|
||||
if (ent.HasValue && ent.Value.IsValid() && _station.GetOwningStation(ent) is not null)
|
||||
{
|
||||
NavMap.Visible = true;
|
||||
ShuttleMap.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
NavMap.Visible = false;
|
||||
ShuttleMap.Visible = true;
|
||||
}
|
||||
// End DeltaV - handle navmap visibility based on current position
|
||||
|
||||
if (_tryToScrollToListFocus)
|
||||
TryToScrollToFocus();
|
||||
}
|
||||
|
|
@ -390,6 +372,18 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||
UpdateSensorsTable(_trackedEntity, prevTrackedEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - If true, will show the shuttle map. If false, will show the normal station map.
|
||||
/// </summary>
|
||||
private void EnableShuttleMap(bool visible)
|
||||
{
|
||||
NavMap.Visible = !visible;
|
||||
StationMapButton.Pressed = !visible;
|
||||
|
||||
ShuttleMap.Visible = visible;
|
||||
ShuttleMapButton.Pressed = visible;
|
||||
}
|
||||
|
||||
private void UpdateSensorsTable(NetEntity? currTrackedEntity, NetEntity? prevTrackedEntity)
|
||||
{
|
||||
foreach (var sensor in SensorsTable.Children)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.StatusIcon;
|
|||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player; // DeltaV
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Shared.Maths.Color;
|
||||
|
|
@ -22,6 +23,7 @@ namespace Content.Client.Overlays;
|
|||
/// </summary>
|
||||
public sealed class EntityHealthBarOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!; // DeltaV
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
|
||||
|
|
@ -40,6 +42,8 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
|
||||
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
|
||||
{
|
||||
IoCManager.InjectDependencies(this); // DeltaV
|
||||
|
||||
_entManager = entManager;
|
||||
_prototype = prototype;
|
||||
_transform = _entManager.System<SharedTransformSystem>();
|
||||
|
|
@ -69,6 +73,11 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
out var damageableComponent,
|
||||
out var spriteComponent))
|
||||
{
|
||||
// BEGIN DeltaV - You can't see your own bar :godo:
|
||||
if (_player.LocalEntity is { } player && player.Id == uid.Id)
|
||||
continue;
|
||||
// END DeltaV
|
||||
|
||||
if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Client.UserInterface.RichText; // DeltaV - Limit what tags can be
|
|||
using Content.Shared.GameTicking;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Content.Shared._Goobstation.StationReport; // Goob
|
||||
using Robust.Client.UserInterface.RichText; // DeltaV - Limit what tags can be used in custom objective summaries
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
|
@ -35,6 +36,7 @@ namespace Content.Client.RoundEnd
|
|||
var roundEndTabs = new TabContainer();
|
||||
roundEndTabs.AddChild(MakeRoundEndSummaryTab(gm, roundEnd, roundTimeSpan, roundId));
|
||||
roundEndTabs.AddChild(MakePlayerManifestTab(info));
|
||||
roundEndTabs.AddChild(MakeStationReportTab()); //goob
|
||||
|
||||
ContentsContainer.AddChild(roundEndTabs);
|
||||
|
||||
|
|
@ -181,6 +183,38 @@ namespace Content.Client.RoundEnd
|
|||
|
||||
return playerManifestTab;
|
||||
}
|
||||
private BoxContainer MakeStationReportTab() //Goob edit start
|
||||
{
|
||||
//gets the stationreport varibible and sets the station report tab text to it if the map doesn't have a tablet will say No station report submitted
|
||||
var stationReportSystem = _entityManager.System<StationReportSystem>();
|
||||
string stationReportText = stationReportSystem.StationReportText ?? Loc.GetString("no-station-report-summited");
|
||||
var stationReportTab = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Name = Loc.GetString("round-end-summary-window-station-report-tab-title")
|
||||
};
|
||||
var StationReportContainerScrollbox = new ScrollContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Margin = new Thickness(10),
|
||||
HScrollEnabled = false,
|
||||
};
|
||||
var StationReportContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
var StationReportLabel = new RichTextLabel();
|
||||
var StationReportmessage = new FormattedMessage();
|
||||
StationReportmessage.AddMarkupOrThrow(stationReportText);
|
||||
StationReportLabel.SetMessage(StationReportmessage);
|
||||
StationReportContainer.AddChild(StationReportLabel);
|
||||
|
||||
|
||||
StationReportContainerScrollbox.AddChild(StationReportContainer);
|
||||
stationReportTab.AddChild(StationReportContainerScrollbox);
|
||||
return stationReportTab;
|
||||
}
|
||||
//Goob edit end
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,41 +85,40 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
case AsteroidOffering asteroid:
|
||||
option.Title = Loc.GetString($"dungeon-config-proto-{asteroid.Id}");
|
||||
break; // DeltaV: Skip ores since they aren't used with custom generation
|
||||
// var layerKeys = asteroid.MarkerLayers.Keys.ToList();
|
||||
// layerKeys.Sort();
|
||||
//
|
||||
// foreach (var resource in layerKeys)
|
||||
// {
|
||||
// var count = asteroid.MarkerLayers[resource];
|
||||
//
|
||||
// var container = new BoxContainer
|
||||
// {
|
||||
// Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
// HorizontalExpand = true,
|
||||
// };
|
||||
//
|
||||
// var resourceLabel = new Label
|
||||
// {
|
||||
// Text = Loc.GetString("salvage-magnet-resources",
|
||||
// ("resource", resource)),
|
||||
// HorizontalAlignment = Control.HAlignment.Left,
|
||||
// };
|
||||
//
|
||||
// var countLabel = new Label
|
||||
// {
|
||||
// Text = Loc.GetString("salvage-magnet-resources-count", ("count", count)),
|
||||
// HorizontalAlignment = Control.HAlignment.Right,
|
||||
// HorizontalExpand = true,
|
||||
// };
|
||||
//
|
||||
// container.AddChild(resourceLabel);
|
||||
// container.AddChild(countLabel);
|
||||
//
|
||||
// option.AddContent(container);
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
var layerKeys = asteroid.MarkerLayers.Keys.ToList();
|
||||
layerKeys.Sort();
|
||||
|
||||
foreach (var resource in layerKeys)
|
||||
{
|
||||
var count = asteroid.MarkerLayers[resource];
|
||||
|
||||
var container = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
|
||||
var resourceLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("salvage-magnet-resources",
|
||||
("resource", resource)),
|
||||
HorizontalAlignment = Control.HAlignment.Left,
|
||||
};
|
||||
|
||||
var countLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("salvage-magnet-resources-count", ("count", count)),
|
||||
HorizontalAlignment = Control.HAlignment.Right,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
|
||||
container.AddChild(resourceLabel);
|
||||
container.AddChild(countLabel);
|
||||
|
||||
option.AddContent(container);
|
||||
}
|
||||
|
||||
break;
|
||||
case DebrisOffering debris:
|
||||
option.Title = Loc.GetString($"salvage-magnet-debris-{debris.Id}");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:chassisSpriteControls="clr-namespace:Content.Client._CD.Silicons.Borgs.UI"
|
||||
Title="{Loc 'borg-select-type-menu-title'}"
|
||||
SetSize="550 300">
|
||||
SetSize="600 480">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<!-- Left pane: selection of borg type -->
|
||||
|
|
@ -29,6 +30,9 @@
|
|||
<RichTextLabel Name="DescriptionLabel" VerticalExpand="True" VerticalAlignment="Top" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
<chassisSpriteControls:ChassisSpriteSelection Name="ChassisSpriteSelection"/> <!-- CD change -->
|
||||
|
||||
<controls:ConfirmButton Name="ConfirmTypeButton" Text="{Loc 'borg-select-type-menu-confirm'}"
|
||||
Disabled="True" HorizontalAlignment="Right"
|
||||
MinWidth="200" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._CD.Silicons; // CosmicDrift
|
||||
using Content.Shared.Guidebook;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
|
|
@ -29,6 +30,7 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
|||
private BorgTypePrototype? _selectedBorgType;
|
||||
|
||||
public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
|
||||
public event Action<EntityPrototype?>? ConfirmedBorgSubtype; // CosmicDrift event - borg subtypes
|
||||
|
||||
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };
|
||||
|
||||
|
|
@ -56,6 +58,13 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
|||
|
||||
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
|
||||
HelpGuidebookIds = GuidebookEntries;
|
||||
|
||||
// Start CosmicDrift Changes - borg subtypes
|
||||
ChassisSpriteSelection.SubtypeSelected += () =>
|
||||
{
|
||||
ConfirmTypeButton.Disabled = false;
|
||||
};
|
||||
// End CosmicDrift Changes - borg subtypes
|
||||
}
|
||||
|
||||
private void UpdateInformation(BorgTypePrototype prototype)
|
||||
|
|
@ -87,6 +96,14 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
|||
NameLabel.Text = PrototypeName(prototype);
|
||||
DescriptionLabel.Text = Loc.GetString($"borg-type-{prototype.ID}-desc");
|
||||
ChassisView.SetPrototype(prototype.DummyPrototype);
|
||||
|
||||
// Start CosmicDrift Changes - borg subtypes
|
||||
if (_selectedBorgType != null)
|
||||
{
|
||||
ConfirmTypeButton.Disabled = true;
|
||||
ChassisSpriteSelection.Update(_selectedBorgType);
|
||||
}
|
||||
// End CosmicDrift Changes - borg subtypes
|
||||
}
|
||||
|
||||
private void ConfirmButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
|
|
@ -94,6 +111,7 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
|||
if (_selectedBorgType == null)
|
||||
return;
|
||||
|
||||
ConfirmedBorgSubtype?.Invoke(ChassisSpriteSelection.SubtypePrototype); // CosmicDrift
|
||||
ConfirmedBorgType?.Invoke(_selectedBorgType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared._CD.Silicons; // CosmicDrift
|
||||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
|
|
@ -26,5 +28,6 @@ public sealed class BorgSelectTypeUserInterface : BoundUserInterface
|
|||
|
||||
_menu = this.CreateWindow<BorgSelectTypeMenu>();
|
||||
_menu.ConfirmedBorgType += prototype => SendPredictedMessage(new BorgSelectTypeMessage(prototype));
|
||||
_menu.ConfirmedBorgSubtype += subtypePrototype => SendPredictedMessage(new BorgSelectSubtypeMessage(subtypePrototype?.ID)); // CosmicDrift - borg subtypes
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using Content.Client.PDA; // DeltaV
|
||||
using Content.Client.PDA; // DeltaV
|
||||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Serialization; // DeltaV
|
||||
using Robust.Client.ResourceManagement; // CosmicDrift
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations; // CosmicDrift
|
||||
using Robust.Shared.Timing; // CosmicDrift
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
|
|
@ -17,6 +20,7 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
|
|||
[Dependency] private readonly BorgSystem _borgSystem = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!; // CosmicDrift - borg subtypes
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -40,15 +44,20 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
|
|||
Entity<BorgSwitchableTypeComponent> entity,
|
||||
BorgTypePrototype prototype)
|
||||
{
|
||||
// Begin DeltaV Additions
|
||||
if (prototype.ClientComponents is {} add)
|
||||
EntityManager.AddComponents(entity, add);
|
||||
// End DeltaV Additions
|
||||
// Begin Afterlight Addition - added checks to stop sprite state errors
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (TryComp<BorgSwitchableSubtypeComponent>(entity, out var subtype) &&
|
||||
subtype.BorgSubtype != null)
|
||||
{
|
||||
var ev = new TypeTryingToUpdateVisualsEvent();
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
return;
|
||||
}
|
||||
// End Afterlight Additions - added checks to stop sprite state errors
|
||||
if (TryComp(entity, out SpriteComponent? sprite))
|
||||
{
|
||||
// Begin DeltaV Additions - work around engine bug with AddComponents
|
||||
((ISerializationHooks) sprite).AfterDeserialization();
|
||||
// End DeltaV Additions
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
|
||||
}
|
||||
|
|
@ -76,6 +85,26 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
|
|||
}
|
||||
// DeltaV - borg pdas
|
||||
|
||||
// Start CosmicDrift Changes - borg subtypes
|
||||
if (prototype.SpriteBodyMovementState is { } movementState)
|
||||
{
|
||||
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||
spriteMovement.NoMovementLayers.Clear();
|
||||
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = prototype.SpriteBodyState,
|
||||
};
|
||||
spriteMovement.MovementLayers.Clear();
|
||||
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = movementState,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<SpriteMovementComponent>(entity);
|
||||
}
|
||||
// End CosmicDrift Changes - borg subtypes
|
||||
base.UpdateEntityAppearance(entity, prototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,22 @@ public sealed class WelderStatusControl : PollingItemStatusControl<WelderStatusC
|
|||
protected override Data PollData()
|
||||
{
|
||||
var (fuel, capacity) = _toolSystem.GetWelderFuelAndCapacity(_parent, _parent.Comp);
|
||||
return new Data(fuel, capacity, _parent.Comp.Enabled);
|
||||
return new Data(fuel, capacity, _parent.Comp.Enabled, _parent.Comp.OnlyDisplayFuel); // Monolith - Nanite Applicators
|
||||
}
|
||||
|
||||
protected override void Update(in Data data)
|
||||
{
|
||||
// BEGIN Monolith - Nanite applicator
|
||||
if (data.OnlyDisplayFuel)
|
||||
{
|
||||
_label.SetMarkup(Loc.GetString("welder-component-on-examine-less-detailed-message",
|
||||
("colorName", data.Fuel < data.FuelCapacity / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", data.Fuel),
|
||||
("fuelCapacity", data.FuelCapacity)));
|
||||
return;
|
||||
}
|
||||
// END Monolith
|
||||
|
||||
_label.SetMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
|
||||
("colorName", data.Fuel < data.FuelCapacity / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", data.Fuel),
|
||||
|
|
@ -41,5 +52,5 @@ public sealed class WelderStatusControl : PollingItemStatusControl<WelderStatusC
|
|||
("status", Loc.GetString(data.Lit ? "welder-component-on-examine-welder-lit-message" : "welder-component-on-examine-welder-not-lit-message"))));
|
||||
}
|
||||
|
||||
public record struct Data(FixedPoint2 Fuel, FixedPoint2 FuelCapacity, bool Lit);
|
||||
public record struct Data(FixedPoint2 Fuel, FixedPoint2 FuelCapacity, bool Lit, bool OnlyDisplayFuel = false); // Monolith - Nanite Applicators
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
using Content.Client.Ghost;
|
||||
using Content.Client.UserInterface.Systems.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Ghost.Widgets;
|
||||
using Content.Shared._DV.Ghost.Roles; // DeltaV - freeform ghosties
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Shared.Console; // Frontier
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Prototypes; // DeltaV - freeform ghosties
|
||||
using Content.Shared._Corvax.Respawn; // Frontier
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost;
|
||||
|
|
@ -33,6 +35,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
|||
|
||||
// DeltaV
|
||||
SubscribeNetworkEvent<RespawnResetEvent>(OnRespawnReseted);
|
||||
SubscribeNetworkEvent<DVSpawnableGhostRoleCooldownUpdateEvent>(OnVentCritterCooldownUpdate); // DeltaV - freeform ghosties
|
||||
}
|
||||
|
||||
private void OnScreenLoad()
|
||||
|
|
@ -151,6 +154,8 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
|||
Gui.RequestWarpsPressed += RequestWarps;
|
||||
Gui.ReturnToBodyPressed += ReturnToBody;
|
||||
Gui.GhostRolesPressed += GhostRolesPressed;
|
||||
Gui.VentCritterPressed += OnVentCritterPressed; // DeltaV - freeform ghosties
|
||||
Gui.VentCritterSelected += OnVentCritterSelected; // DeltaV - freeform ghosties
|
||||
Gui.TargetWindow.WarpClicked += OnWarpClicked;
|
||||
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
|
||||
Gui.GhostRespawnPressed += GuiOnGhostRespawnPressed; // Frontier
|
||||
|
|
@ -173,6 +178,8 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
|||
Gui.RequestWarpsPressed -= RequestWarps;
|
||||
Gui.ReturnToBodyPressed -= ReturnToBody;
|
||||
Gui.GhostRolesPressed -= GhostRolesPressed;
|
||||
Gui.VentCritterPressed -= OnVentCritterPressed; // DeltaV - freeform ghosties
|
||||
Gui.VentCritterSelected -= OnVentCritterSelected; // DeltaV - freeform ghosties
|
||||
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
|
||||
Gui.GhostRespawnPressed -= GuiOnGhostRespawnPressed; // Frontier
|
||||
|
||||
|
|
@ -195,4 +202,21 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
|||
{
|
||||
_system?.OpenGhostRoles();
|
||||
}
|
||||
|
||||
// Begin DeltaV - freeform ghosties
|
||||
private void OnVentCritterPressed()
|
||||
{
|
||||
_net.SendSystemNetworkMessage(new DVSpawnableGhostRoleCooldownRequestEvent());
|
||||
}
|
||||
|
||||
private void OnVentCritterSelected(ProtoId<DVSpawnableGhostRolePrototype> role)
|
||||
{
|
||||
_net.SendSystemNetworkMessage(new DVSpawnableGhostRoleRequestEvent(role));
|
||||
}
|
||||
|
||||
private void OnVentCritterCooldownUpdate(DVSpawnableGhostRoleCooldownUpdateEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
Gui?.VentCritterWindow.SetCooldownEnd(msg.CooldownEnd);
|
||||
}
|
||||
// End DeltaV - freeform ghosties
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
|
||||
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
|
||||
<Button Name="GhostRolesButton" />
|
||||
<Button Name="VentCritterButton" Text="{Loc ghost-gui-spawn-vent-critter-button}" /> <!-- DeltaV: freeform ghosties -->
|
||||
<Button Name="GhostRespawnButton" Text="{Loc ghost-gui-respawn}" /> <!-- Frontier -->
|
||||
</BoxContainer>
|
||||
</widgets:GhostGui>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
using Content.Client._DV.Ghost; // DeltaV - freeform ghosties
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Ghost.Controls;
|
||||
using Content.Shared._DV.Ghost.Roles; // DeltaV - freeform ghosties
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes; // DeltaV - freeform ghosties
|
||||
using Robust.Shared.Timing; // Frontier
|
||||
using Content.Client._NF.UserInterface.Systems.Ghost.Controls; // Frontier
|
||||
|
||||
|
|
@ -17,10 +20,13 @@ public sealed partial class GhostGui : UIWidget
|
|||
|
||||
public GhostTargetWindow TargetWindow { get; }
|
||||
public GhostRespawnRulesWindow RulesWindow { get; } // Frontier
|
||||
public DVSpawnableGhostRoleWindow VentCritterWindow { get; } // DeltaV - freeform ghosties
|
||||
|
||||
public event Action? RequestWarpsPressed;
|
||||
public event Action? ReturnToBodyPressed;
|
||||
public event Action? GhostRolesPressed;
|
||||
public event Action? VentCritterPressed; // DeltaV - freeform ghosties
|
||||
public event Action<ProtoId<DVSpawnableGhostRolePrototype>>? VentCritterSelected; // DeltaV - freeform ghosties
|
||||
public event Action? GhostRespawnPressed; // Frontier
|
||||
private int _prevNumberRoles;
|
||||
|
||||
|
|
@ -31,6 +37,8 @@ public sealed partial class GhostGui : UIWidget
|
|||
TargetWindow = new GhostTargetWindow();
|
||||
RulesWindow = new GhostRespawnRulesWindow(); // Frontier
|
||||
RulesWindow.RespawnButton.OnPressed += _ => GhostRespawnPressed?.Invoke(); // Frontier
|
||||
VentCritterWindow = new DVSpawnableGhostRoleWindow(); // DeltaV - freeform ghosties
|
||||
VentCritterWindow.RoleSelected += role => VentCritterSelected?.Invoke(role); // DeltaV - freeform ghosties
|
||||
|
||||
MouseFilter = MouseFilterMode.Ignore;
|
||||
|
||||
|
|
@ -38,12 +46,18 @@ public sealed partial class GhostGui : UIWidget
|
|||
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
|
||||
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
|
||||
GhostRolesButton.OnPressed += _ => GhostRolesButton.StyleClasses.Remove(StyleClass.Negative);
|
||||
VentCritterButton.OnPressed += _ =>
|
||||
{
|
||||
VentCritterPressed?.Invoke();
|
||||
VentCritterWindow.OpenCentered();
|
||||
}; // DeltaV - freeform ghosties
|
||||
GhostRespawnButton.OnPressed += _ => RulesWindow.OpenCentered(); // Frontier
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
TargetWindow.Close();
|
||||
VentCritterWindow.Close(); // DeltaV - freeform ghosties
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +111,7 @@ public sealed partial class GhostGui : UIWidget
|
|||
if (disposing)
|
||||
{
|
||||
TargetWindow.Dispose();
|
||||
VentCritterWindow.Dispose(); // DeltaV - freeform ghosties
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Silicons.Borgs;
|
||||
using Content.Shared._CD.Silicons.Borgs;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._CD.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Primarily handles the appearance aspects of the borg subtype.
|
||||
/// </summary>
|
||||
public sealed class BorgSwitchableSubtypeSystem : SharedBorgSwitchableSubtypeSystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly BorgSystem _borg = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, AfterAutoHandleStateEvent>(OnAutoHandleEvent);
|
||||
}
|
||||
|
||||
private void OnAutoHandleEvent(Entity<BorgSwitchableSubtypeComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
SelectBorgSubtype(ent);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<BorgSwitchableSubtypeComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
SelectBorgSubtype(ent);
|
||||
}
|
||||
|
||||
protected override void UpdateEntityAppearance(Entity<BorgSwitchableSubtypeComponent> entity, EntityPrototype borgSubtypePrototype)
|
||||
{
|
||||
// LOT of copy pasted code from BorgSwitchableTypeSystem, but is probably necessary unless the upstream code
|
||||
// is refactored
|
||||
|
||||
if (!borgSubtypePrototype.TryGetComponent<BorgSubtypeDefinitionComponent>(out var borgSubtype, ComponentFactory))
|
||||
return;
|
||||
|
||||
// get our required components
|
||||
var (owner, _) = entity;
|
||||
if (!TryComp<SpriteComponent>(entity, out var chassisSprite))
|
||||
return;
|
||||
|
||||
// remove all existing layers
|
||||
for (int i = chassisSprite.AllLayers.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
_sprite.RemoveLayer((entity, chassisSprite), i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < borgSubtype.LayerData.Length; i++)
|
||||
{
|
||||
var layerData = borgSubtype.LayerData[i];
|
||||
|
||||
layerData.RsiPath = borgSubtype.SpritePath?.ToString();
|
||||
if (borgSubtype.Offset != null)
|
||||
layerData.Offset = borgSubtype.Offset;
|
||||
_sprite.AddLayer((owner, chassisSprite), layerData, i);
|
||||
}
|
||||
|
||||
if (TryComp<BorgChassisComponent>(entity, out var chassis))
|
||||
{
|
||||
_borg.SetMindStates(
|
||||
(entity.Owner, chassis),
|
||||
borgSubtype.SpriteHasMindState,
|
||||
borgSubtype.SpriteNoMindState);
|
||||
|
||||
if (TryComp(entity, out AppearanceComponent? appearance))
|
||||
{
|
||||
// Queue update so state changes apply.
|
||||
_appearance.QueueUpdate(entity, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
if (borgSubtype.SpriteBodyMovementState is { } movementState)
|
||||
{
|
||||
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||
spriteMovement.NoMovementLayers.Clear();
|
||||
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = borgSubtype.SpriteBodyState,
|
||||
};
|
||||
spriteMovement.MovementLayers.Clear();
|
||||
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = movementState,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<SpriteMovementComponent>(entity);
|
||||
}
|
||||
|
||||
base.UpdateEntityAppearance(entity, borgSubtypePrototype);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Name="MainContainer"
|
||||
Orientation="Vertical"
|
||||
SeparationOverride="4"
|
||||
Visible="False">
|
||||
<controls:StripeBack>
|
||||
<Label Text="{Loc 'cd-borg-select-subtype-flavour-text'}"
|
||||
StyleClasses="LabelSubText"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 12"/>
|
||||
</controls:StripeBack>
|
||||
<ScrollContainer Name="OptionsScrollContainer"
|
||||
HScrollEnabled="True"
|
||||
VScrollEnabled="False"
|
||||
MinHeight="96">
|
||||
<BoxContainer Name="OptionsContainer"
|
||||
Orientation="Horizontal"
|
||||
SeparationOverride="4"/>
|
||||
</ScrollContainer>
|
||||
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
using Content.Shared.Silicons.Borgs;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._CD.Silicons.Borgs.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ChassisSpriteSelection : Control
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public EntityPrototype? SubtypePrototype;
|
||||
public event Action? SubtypeSelected;
|
||||
|
||||
private const int PrototypeViewSize = 2;
|
||||
private static readonly ProtoId<EntityCategoryPrototype> _borgSubtypeCategory = "BorgSubtype";
|
||||
|
||||
public ChassisSpriteSelection()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void Update(BorgTypePrototype borgTypePrototype)
|
||||
{
|
||||
MainContainer.Visible = true;
|
||||
|
||||
OptionsContainer.RemoveAllChildren();
|
||||
|
||||
var buttonGroup = new ButtonGroup();
|
||||
List<Button> buttons = new List<Button>();
|
||||
buttons.Add(CreateDefaultSubtypeButton(borgTypePrototype, buttonGroup));
|
||||
|
||||
foreach (var ent in _prototype.Categories.GetValueRefOrNullRef(_borgSubtypeCategory))
|
||||
{
|
||||
if (!ent.TryGetComponent<Shared._CD.Silicons.Borgs.BorgSubtypeDefinitionComponent>(out var subtype, _componentFactory))
|
||||
continue;
|
||||
|
||||
// Only add subtypes of the current selected 'main' borg type (engineering, medical, etc.)
|
||||
if (subtype.ParentType != borgTypePrototype.ID)
|
||||
continue;
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
ToolTip = Loc.GetString($"al-borg-{borgTypePrototype.ID}-subtype-{ent.Name.Replace(' ', '-').ToLower()}-name"),
|
||||
Group = buttonGroup,
|
||||
MinHeight = 32,
|
||||
};
|
||||
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
SubtypePrototype = ent;
|
||||
SubtypeSelected?.Invoke();
|
||||
};
|
||||
|
||||
button.AddChild(CreateEntityPrototypeView(subtype.DummyPrototype));
|
||||
buttons.Add(button);
|
||||
}
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
OptionsContainer.AddChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
private Button CreateDefaultSubtypeButton(BorgTypePrototype borgTypePrototype, ButtonGroup group)
|
||||
{
|
||||
var button = new Button
|
||||
{
|
||||
ToolTip = "default",
|
||||
Group = group,
|
||||
MinHeight = 32,
|
||||
};
|
||||
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
SubtypePrototype = null;
|
||||
SubtypeSelected?.Invoke();
|
||||
};
|
||||
|
||||
button.AddChild(CreateEntityPrototypeView(borgTypePrototype.DummyPrototype));
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private EntityPrototypeView CreateEntityPrototypeView(EntProtoId entProtoId)
|
||||
{
|
||||
var entPrototypeView = new EntityPrototypeView();
|
||||
|
||||
entPrototypeView.SetPrototype(entProtoId);
|
||||
entPrototypeView.Scale *= PrototypeViewSize;
|
||||
|
||||
return entPrototypeView;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._DV.CosmicCult;
|
||||
using Content.Shared._DV.CosmicCult.Prototypes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._DV.CosmicCult.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class CosmicGlyphDrawBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private SimpleRadialMenu? _menu;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_menu = this.CreateWindow<SimpleRadialMenu>();
|
||||
_menu.OpenOverMouseScreenPosition();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not CosmicGlyphDrawBuiState glyphState || _menu == null)
|
||||
return;
|
||||
|
||||
var options = new List<RadialMenuOptionBase>();
|
||||
foreach (var glyphId in glyphState.Glyphs)
|
||||
{
|
||||
var glyph = _prototype.Index(glyphId);
|
||||
|
||||
options.Add(new RadialMenuActionOption<ProtoId<GlyphPrototype>>(SelectGlyph, glyph.ID)
|
||||
{
|
||||
IconSpecifier = RadialMenuIconSpecifier.With(glyph.Entity),
|
||||
ToolTip = $"{Loc.GetString(glyph.Name)}\n{Loc.GetString(glyph.Tooltip)}",
|
||||
});
|
||||
}
|
||||
|
||||
_menu.SetButtons(options);
|
||||
}
|
||||
|
||||
private void SelectGlyph(ProtoId<GlyphPrototype> glyphId)
|
||||
{
|
||||
SendMessage(new CosmicGlyphDrawSelectedMessage(glyphId));
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,6 @@ public sealed class MonumentBoundUserInterface(EntityUid owner, Enum uiKey) : Bo
|
|||
|
||||
_menu = this.CreateWindow<MonumentMenu>();
|
||||
|
||||
_menu.OnSelectGlyphButtonPressed += protoId => SendMessage(new GlyphSelectedMessage(protoId));
|
||||
_menu.OnRemoveGlyphButtonPressed += () => SendMessage(new GlyphRemovedMessage());
|
||||
|
||||
_menu.OnGainButtonPressed += OnInfluenceSelected;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@
|
|||
MinSize="650 600"
|
||||
SetSize="750 620">
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 0">
|
||||
<!-- Header -->
|
||||
<BoxContainer Margin="7 10 7 0">
|
||||
<!-- Progress bar -->
|
||||
<ProgressBar Name="CultProgressBar" SetHeight="30" MaxValue="100" HorizontalExpand="True">
|
||||
<Label Name="ProgressBarPercentage" HorizontalAlignment="Center"/>
|
||||
</ProgressBar>
|
||||
</BoxContainer>
|
||||
<!-- Main body -->
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True" Margin="10 20 10 10">
|
||||
<!-- Left section -->
|
||||
|
|
@ -31,27 +24,6 @@
|
|||
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-entropy-available-label'}" Margin="0 0 5 0" StyleClasses="LabelSmall" />
|
||||
<Label HorizontalAlignment="Center" Name="AvailableEntropy" FontColorOverride="#4CA7AD" StyleClasses="LabelSmall" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 5 0 0" HorizontalAlignment="Center">
|
||||
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-entropy-next-stage-title'}" Margin="0 0 5 0" StyleClasses="LabelSmall" />
|
||||
<Label HorizontalAlignment="Center" Name="EntropyUntilNextStage" FontColorOverride="#4CA7AD" StyleClasses="LabelSmall" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 5 0 0" HorizontalAlignment="Center">
|
||||
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-entropy-seperator'}" Margin="0 0 5 0" StyleClasses="LabelSmall" />
|
||||
<Label HorizontalAlignment="Center" Name="OrSeperator" FontColorOverride="#4CA7AD" StyleClasses="LabelSmall" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-entropy-crew-convert-title'}" Margin="0 0 5 0" StyleClasses="LabelSmall" />
|
||||
<Label HorizontalAlignment="Center" Name="CrewToConvertUntilNextStage" FontColorOverride="#4CA7AD" StyleClasses="LabelSmall" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Second section (Glyphs) -->
|
||||
<BoxContainer Name="GlyphArea" HorizontalExpand="True" MinHeight="75" Orientation="Vertical" Margin="0 20 0 0">
|
||||
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-glyphs-title'}" />
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<!-- Filled out programatically -->
|
||||
<GridContainer HorizontalAlignment="Center" Name="GlyphContainer" Columns="3" Margin="0 5" />
|
||||
<Button Name="SelectGlyphButton" Text="{Loc 'monument-interface-glyphs-button-scribe'}" SetHeight="50" Margin="0 10 0 0" StyleClasses="ButtonColorPurpleAndCool" />
|
||||
<Button Name="RemoveGlyphButton" Text="{Loc 'monument-interface-glyphs-button-unscribe'}" SetHeight="50" Margin="0 10 0 0" StyleClasses="ButtonColorPurpleAndCool" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Right section -->
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ using Content.Shared._DV.CosmicCult.Prototypes;
|
|||
using Content.Shared._DV.CosmicCult;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
|
@ -32,10 +31,6 @@ public sealed partial class MonumentMenu : FancyWindow
|
|||
// All influence prototypes
|
||||
private readonly IEnumerable<InfluencePrototype> _influencePrototypes;
|
||||
private readonly ButtonGroup _glyphButtonGroup;
|
||||
private ProtoId<GlyphPrototype> _selectedGlyphProtoId = string.Empty;
|
||||
private HashSet<ProtoId<GlyphPrototype>> _unlockedGlyphProtoIds = [];
|
||||
public Action<ProtoId<GlyphPrototype>>? OnSelectGlyphButtonPressed;
|
||||
public Action? OnRemoveGlyphButtonPressed;
|
||||
|
||||
public Action<ProtoId<InfluencePrototype>>? OnGainButtonPressed;
|
||||
private int _entropyPerCultist = 0;
|
||||
|
|
@ -52,9 +47,6 @@ public sealed partial class MonumentMenu : FancyWindow
|
|||
|
||||
_glyphButtonGroup = new ButtonGroup();
|
||||
|
||||
RemoveGlyphButton.OnPressed += _ => OnRemoveGlyphButtonPressed?.Invoke();
|
||||
SelectGlyphButton.OnPressed += _ => OnSelectGlyphButtonPressed?.Invoke(_selectedGlyphProtoId);
|
||||
|
||||
_cfg.OnValueChanged(DCCVars.CosmicCultistEntropyValue, entropy =>
|
||||
{
|
||||
_entropyPerCultist = entropy;
|
||||
|
|
@ -64,32 +56,10 @@ public sealed partial class MonumentMenu : FancyWindow
|
|||
|
||||
public void UpdateState(MonumentBuiState state)
|
||||
{
|
||||
_selectedGlyphProtoId = state.SelectedGlyph;
|
||||
_unlockedGlyphProtoIds = state.UnlockedGlyphs;
|
||||
|
||||
CultProgressBar.BackgroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = new Color(15, 17, 30) };
|
||||
CultProgressBar.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = new Color(91, 62, 124) };
|
||||
|
||||
UpdateBar(state);
|
||||
UpdateEntropy(state);
|
||||
UpdateGlyphs();
|
||||
UpdateInfluences(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the progress bar
|
||||
/// </summary>
|
||||
private void UpdateBar(MonumentBuiState state)
|
||||
{
|
||||
var percentComplete = 100f * ((float)state.CurrentProgress / state.TargetProgress);
|
||||
|
||||
percentComplete = Math.Min(percentComplete, 100f);
|
||||
|
||||
CultProgressBar.Value = percentComplete;
|
||||
|
||||
ProgressBarPercentage.Text = Loc.GetString("monument-interface-progress-bar", ("percentage", percentComplete.ToString("0")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the entropy fields
|
||||
/// </summary>
|
||||
|
|
@ -106,39 +76,6 @@ public sealed partial class MonumentMenu : FancyWindow
|
|||
var crewToNextStage = (int)Math.Max(Math.Round((double)entropyToNextStage / _entropyPerCultist, MidpointRounding.ToPositiveInfinity), min); //force it to be at least one
|
||||
|
||||
AvailableEntropy.Text = Loc.GetString("monument-interface-entropy-value", ("infused", availableEntropy));
|
||||
EntropyUntilNextStage.Text = Loc.GetString("monument-interface-entropy-value", ("infused", entropyToNextStage.ToString()));
|
||||
CrewToConvertUntilNextStage.Text = crewToNextStage.ToString();
|
||||
}
|
||||
|
||||
// Update all the glyph buttons
|
||||
private void UpdateGlyphs()
|
||||
{
|
||||
GlyphContainer.RemoveAllChildren();
|
||||
foreach (var glyph in _glyphPrototypes)
|
||||
{
|
||||
var boxContainer = new BoxContainer();
|
||||
var unlocked = _unlockedGlyphProtoIds.Contains(glyph.ID);
|
||||
var button = new Button
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
StyleClasses = { StyleClass.ButtonSquare },
|
||||
ToolTip = Loc.GetString(glyph.Tooltip),
|
||||
Group = _glyphButtonGroup,
|
||||
Pressed = glyph.ID == _selectedGlyphProtoId,
|
||||
Disabled = !unlocked,
|
||||
Modulate = !unlocked ? Color.Gray : Color.White,
|
||||
};
|
||||
button.OnPressed += _ => _selectedGlyphProtoId = glyph.ID;
|
||||
var glyphIcon = new TextureRect
|
||||
{
|
||||
Texture = _sprite.Frame0(glyph.Icon),
|
||||
TextureScale = new Vector2(2f, 2f),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered,
|
||||
};
|
||||
button.AddChild(glyphIcon);
|
||||
boxContainer.AddChild(button);
|
||||
GlyphContainer.AddChild(boxContainer);
|
||||
}
|
||||
}
|
||||
|
||||
// Update all the influence thingies
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ public sealed class CrewMonitoringShuttleControl : BaseShuttleControl
|
|||
if (blip.Blinks && !lit)
|
||||
continue;
|
||||
|
||||
if (!blip.Coordinates.IsValid(EntManager))
|
||||
continue;
|
||||
|
||||
var blipPos = _transform.ToMapCoordinates(blip.Coordinates);
|
||||
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Systems.Ghost.Controls.Roles;
|
||||
using Content.Shared._DV.Ghost.Roles;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._DV.Ghost;
|
||||
|
||||
public sealed class DVSpawnableGhostRoleWindow : FancyWindow
|
||||
{
|
||||
public event Action<ProtoId<DVSpawnableGhostRolePrototype>>? RoleSelected;
|
||||
|
||||
private GhostRoleRulesWindow? _windowRules;
|
||||
private readonly BoxContainer _entries;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
private readonly List<Button> _spawnButtons = new();
|
||||
private TimeSpan? _cooldownEnd;
|
||||
|
||||
public DVSpawnableGhostRoleWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = Loc.GetString("ghost-gui-spawn-vent-critter-window-title");
|
||||
MinSize = new Vector2(490, 400);
|
||||
SetSize = new Vector2(490, 500);
|
||||
|
||||
_entries = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
SeparationOverride = 8,
|
||||
Margin = new Thickness(8),
|
||||
};
|
||||
|
||||
var scroll = new ScrollContainer
|
||||
{
|
||||
HScrollEnabled = false,
|
||||
Children =
|
||||
{
|
||||
_entries,
|
||||
},
|
||||
};
|
||||
|
||||
ContentsContainer.AddChild(scroll);
|
||||
Populate();
|
||||
|
||||
OnClose += () => _windowRules?.Close();
|
||||
}
|
||||
|
||||
private void Populate()
|
||||
{
|
||||
var roles = _prototype.EnumeratePrototypes<DVSpawnableGhostRolePrototype>()
|
||||
.OrderBy(role => Loc.GetString(role.Name));
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
var info = new GhostRoleInfoBox(Loc.GetString(role.Name), Loc.GetString(role.Description));
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-button"),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
var id = new ProtoId<DVSpawnableGhostRolePrototype>(role.ID);
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
if (IsCooldownActive())
|
||||
return;
|
||||
|
||||
_windowRules?.Close();
|
||||
|
||||
_windowRules = new GhostRoleRulesWindow(Loc.GetString(role.Rules),
|
||||
_ =>
|
||||
{
|
||||
RoleSelected?.Invoke(id);
|
||||
Close();
|
||||
});
|
||||
_windowRules.OnClose += () =>
|
||||
{
|
||||
_windowRules = null;
|
||||
};
|
||||
_windowRules.OpenCentered();
|
||||
};
|
||||
|
||||
_entries.AddChild(info);
|
||||
_entries.AddChild(button);
|
||||
_spawnButtons.Add(button);
|
||||
}
|
||||
|
||||
if (_entries.ChildCount != 0)
|
||||
return;
|
||||
|
||||
_entries.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("ghost-gui-spawn-vent-critter-none"),
|
||||
});
|
||||
}
|
||||
|
||||
public void SetCooldownEnd(TimeSpan? cooldownEnd)
|
||||
{
|
||||
_cooldownEnd = cooldownEnd;
|
||||
UpdateCooldownButtons();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
UpdateCooldownButtons();
|
||||
}
|
||||
|
||||
[MemberNotNullWhen(true, nameof(_cooldownEnd))]
|
||||
private bool IsCooldownActive()
|
||||
{
|
||||
return _cooldownEnd > _timing.CurTime;
|
||||
}
|
||||
|
||||
private void UpdateCooldownButtons()
|
||||
{
|
||||
if (IsCooldownActive())
|
||||
{
|
||||
var seconds = Math.Ceiling((_cooldownEnd.Value - _timing.CurTime).TotalSeconds);
|
||||
foreach (var button in _spawnButtons)
|
||||
{
|
||||
button.Disabled = true;
|
||||
button.Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-cooldown", ("time", seconds));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in _spawnButtons)
|
||||
{
|
||||
button.Disabled = false;
|
||||
button.Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-button");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Client.Stylesheets.StylesheetHelpers;
|
||||
|
||||
namespace Content.Client._DV.Stylesheets.Sheetlets;
|
||||
|
||||
[CommonSheetlet]
|
||||
public sealed class SurgeryTextureButtonSheetlet <T> : Sheetlet<T> where T : PalettedStylesheet
|
||||
{
|
||||
private readonly Color _dollColor = Color.FromHex("#639bff");
|
||||
public override StyleRule[] GetRules(T sheet, object config)
|
||||
{
|
||||
return
|
||||
[
|
||||
E<TextureButton>()
|
||||
.Identifier("SurgeryTextureButton")
|
||||
.PseudoHovered()
|
||||
.Modulate(sheet.PositivePalette.HoveredElement),
|
||||
E<TextureButton>()
|
||||
.Identifier("SurgeryTextureButton")
|
||||
.PseudoPressed()
|
||||
.Modulate(sheet.PositivePalette.PressedElement),
|
||||
E<TextureButton>()
|
||||
.Identifier("SurgeryTextureButton")
|
||||
.PseudoNormal()
|
||||
.Modulate(_dollColor),
|
||||
|
||||
E<TextureButton>()
|
||||
.Identifier("OpenIncision")
|
||||
.PseudoHovered()
|
||||
.Modulate(sheet.PositivePalette.HoveredElement),
|
||||
E<TextureButton>()
|
||||
.Identifier("OpenIncision")
|
||||
.PseudoPressed()
|
||||
.Modulate(sheet.PositivePalette.PressedElement),
|
||||
E<TextureButton>()
|
||||
.Identifier("OpenIncision")
|
||||
.PseudoNormal()
|
||||
.Modulate(sheet.HighlightPalette.HoveredElement),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ using Content.Server.Mind;
|
|||
using Content.Server.Roles;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.StationEvents.Components; // DeltaV
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
|
|
@ -259,8 +260,12 @@ public sealed class NukeOpsTest : GameTest
|
|||
// Delete the last nukie and make sure the round ends.
|
||||
entMan.DeleteEntity(nukies[^1]);
|
||||
|
||||
Assert.That(roundEndSys.IsRoundEndRequested,
|
||||
"All nukies were deleted, but the round didn't end!");
|
||||
// BEGIN DeltaV - We convert to survival, so only check if the round ended if its ShuttleCall
|
||||
if (rule.Component.RoundEndBehavior == RoundEndBehavior.ShuttleCall)
|
||||
Assert.That(roundEndSys.IsRoundEndRequested, "All nukies were deleted, but the round didn't end!");
|
||||
if (rule.Component.RoundEndBehavior == RoundEndBehavior.BecomeSurvival)
|
||||
Assert.That(ticker.IsGameRuleAdded<RampingStationEventSchedulerComponent>(), "All nukies were deleted, but the round wasn't converted to survival.");
|
||||
// END DeltaV
|
||||
});
|
||||
|
||||
ticker.SetGamePreset((GamePresetPrototype?) null);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public abstract partial class InteractionTest
|
|||
protected const string Wrench = "Wrench";
|
||||
protected const string Screw = "Screwdriver";
|
||||
protected const string Weld = "WelderExperimental";
|
||||
protected const string Applicator = "NaniteApplicatorSyndicate"; // DeltaV
|
||||
protected const string Pry = "Crowbar";
|
||||
protected const string Cut = "Wirecutter";
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ public sealed class VendingInteractionTest : InteractionTest
|
|||
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "Opened BUI of broken vending machine.");
|
||||
|
||||
// Repair the vending machine
|
||||
await InteractUsing(Weld);
|
||||
await InteractUsing(Applicator); // DeltaV
|
||||
|
||||
// Make sure the BUI can open now that the machine has been repaired
|
||||
await Activate();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Shared.Damage.ForceSay;
|
|||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Player;
|
||||
|
|
@ -88,6 +89,9 @@ public sealed class DamageForceSaySystem : EntitySystem
|
|||
if (!args.FellAsleep)
|
||||
return;
|
||||
|
||||
if (Comp<MobStateComponent>(uid).CurrentState != MobState.Alive)
|
||||
return;
|
||||
|
||||
TryForceSay(uid, component);
|
||||
AllowNextSpeech(uid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public sealed partial class NukeopsRuleComponent : Component
|
|||
/// What will happen if all of the nuclear operatives will die. Used by LoneOpsSpawn event.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.ShuttleCall;
|
||||
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.BecomeSurvival; // DeltaV - Change from ShuttlCall To BecomeSurvival
|
||||
|
||||
/// <summary>
|
||||
/// Text for shuttle call if RoundEndBehavior is ShuttleCall.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Server.RoundEnd;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -25,4 +26,14 @@ public sealed partial class ZombieRuleComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public float ZombieShuttleCallPercentage = 0.7f;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The behavior of the round if all zombies are defeated.
|
||||
/// </summary>
|
||||
public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.BecomeSurvival;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall.
|
||||
/// </summary>
|
||||
public TimeSpan ZombieShuttleDelay = TimeSpan.FromMinutes(10);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Content.Server.Shuttles.Events;
|
|||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Cuffs.Components; // DeltaV
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
|
|
@ -61,6 +62,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
SubscribeLocalEvent<NukeOperativeComponent, ComponentRemove>(OnComponentRemove);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, EntityZombifiedEvent>(OnOperativeZombified);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, CuffedStateChangeEvent>(OnNukeOpCuffed); // DeltaV
|
||||
|
||||
SubscribeLocalEvent<NukeopsRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
|
||||
|
|
@ -318,6 +320,15 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
CheckRoundShouldEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Check if the round should end if a nuke op is cuffed. CheckRoundShouldEnd checks if theres any more
|
||||
/// alive and non-cuffed nukies remaining.
|
||||
/// </summary>
|
||||
private void OnNukeOpCuffed(EntityUid uid, NukeOperativeComponent component, CuffedStateChangeEvent ev)
|
||||
{
|
||||
CheckRoundShouldEnd();
|
||||
}
|
||||
|
||||
private void OnOperativeZombified(EntityUid uid, NukeOperativeComponent component, ref EntityZombifiedEvent args)
|
||||
{
|
||||
RemCompDeferred(uid, component);
|
||||
|
|
@ -462,10 +473,16 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
private void CheckRoundShouldEnd()
|
||||
{
|
||||
var query = QueryActiveRules();
|
||||
|
||||
// BEGIN DeltaV - Allow round to become survival
|
||||
// CheckRoundShouldEnd needs to be outside the query, because it might add an active rule
|
||||
List<Entity<NukeopsRuleComponent>> nukeOpsRules = new();
|
||||
while (query.MoveNext(out var uid, out _, out var nukeops, out _))
|
||||
{
|
||||
CheckRoundShouldEnd((uid, nukeops));
|
||||
nukeOpsRules.Add((uid, nukeops));
|
||||
}
|
||||
nukeOpsRules.ForEach(CheckRoundShouldEnd);
|
||||
// END DeltaV
|
||||
}
|
||||
|
||||
private void CheckRoundShouldEnd(Entity<NukeopsRuleComponent> ent)
|
||||
|
|
@ -500,12 +517,22 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
|||
// Check if there are nuke operatives still alive on the same map as the shuttle,
|
||||
// or on the same map as the station.
|
||||
// If there are, the round can continue.
|
||||
var operatives = EntityQuery<NukeOperativeComponent, MobStateComponent, TransformComponent>(true);
|
||||
|
||||
// BEGIN DeltaV - Detect Nukie Failure Better
|
||||
// We need to use a EntityQueryEnumerator instead of a EntityQuery, because we need the uid to check for cuffs
|
||||
var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent, TransformComponent>>();
|
||||
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent, TransformComponent>();
|
||||
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState, out var transform))
|
||||
{
|
||||
operatives.Add((uid, nukeOp, mobState, transform));
|
||||
}
|
||||
|
||||
var operativesAlive = operatives
|
||||
.Where(op =>
|
||||
op.Item3.MapID == shuttleMapId
|
||||
|| op.Item3.MapID == targetStationMap)
|
||||
.Any(op => op.Item2.CurrentState == MobState.Alive && op.Item1.Running);
|
||||
.Where(op => op.Comp3.MapID == shuttleMapId
|
||||
|| op.Comp3.MapID == targetStationMap)
|
||||
.Any(op => op.Comp2.CurrentState == MobState.Alive && op.Comp1.Running &&
|
||||
!TryComp<CuffableComponent>(op, out _)); // in the case the crew keeps one alive
|
||||
// END DeltaV
|
||||
|
||||
if (operativesAlive)
|
||||
return; // There are living operatives than can access the shuttle, or are still on the station's map.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using Content.Server.Chat.Systems;
|
|||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.RoundEnd; // DeltaV
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.Zombies;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
|
|
@ -18,6 +18,7 @@ using Content.Shared.Zombies;
|
|||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Globalization;
|
||||
using System.Linq; // DeltaV
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
|
|
@ -114,10 +115,32 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
|||
/// </summary>
|
||||
private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent)
|
||||
{
|
||||
// BEGIN DeltaV - Change mode to survival if zombies die
|
||||
// Zombies have failed to launch and the mode has been switched to survival
|
||||
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing)
|
||||
return;
|
||||
// END DeltaV
|
||||
|
||||
var healthy = GetHealthyHumans();
|
||||
if (healthy.Count == 1) // Only one human left. spooky
|
||||
_popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]);
|
||||
|
||||
|
||||
// BEGIN DeltaV - Change mode to survival if zombies die
|
||||
var anyInitialInfectedAlive = EntityQuery<InitialInfectedComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyLivingZombies = EntityQuery<ZombieComponent, MobStateComponent>(false)
|
||||
.Any(x => x.Item2.CurrentState != MobState.Dead);
|
||||
var anyPendingZombies = EntityQuery<PendingZombieComponent>(false).Any();
|
||||
|
||||
// All II turned/dead, all entities that have turned are dead, and there are no more pending zombies
|
||||
if (!anyInitialInfectedAlive && !anyLivingZombies && !anyPendingZombies)
|
||||
{
|
||||
_roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior, zombieRuleComponent.ZombieShuttleDelay);
|
||||
zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
if (GetInfectedFraction(false) > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested())
|
||||
{
|
||||
foreach (var station in _station.GetStations())
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ using Content.Shared._DV.MedicalRecords;
|
|||
|
||||
namespace Content.Server.Medical;
|
||||
|
||||
public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made Partial
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly PowerCellSystem _cell = default!;
|
||||
|
|
@ -55,6 +55,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||
subs.Event<HealthAnalyzerTriageClaimMessage>(OnHealthAnalyzerTriageClaimSelected);
|
||||
});
|
||||
// End DeltaV - Medical Records
|
||||
InitializeReportPrinting(); // DeltaV - MedTek Reports
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
|||
_audio.PlayPvs(comp.Sound, target);
|
||||
|
||||
_damageable.ChangeDamage(target, comp.StunDamage, origin: uid);
|
||||
_stun.TryAddParalyzeDuration(target, comp.StunTime);
|
||||
_stun.TryAddParalyzeDuration(target, comp.StunTime, true);
|
||||
|
||||
// short cooldown to prevent instant stunlocking
|
||||
_useDelay.SetLength((uid, useDelay), comp.Cooldown, id: comp.DelayId);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Research.Prototypes;
|
||||
|
|
@ -12,6 +13,7 @@ public sealed class ReverseEngineeringSystem : SharedReverseEngineeringSystem
|
|||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -146,5 +148,6 @@ public sealed class ReverseEngineeringSystem : SharedReverseEngineeringSystem
|
|||
disk.Recipes = new();
|
||||
foreach (var id in recipes)
|
||||
disk.Recipes.Add(id);
|
||||
_nameModifier.RefreshNameModifiers(uid);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,11 @@ namespace Content.Server.StationEvents.Components;
|
|||
/// Requires <c>AntagSelection</c>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class MidRoundAntagRuleComponent : Component;
|
||||
public sealed partial class MidRoundAntagRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// When set to true, this spawner will prefer vent spawns over midround spawnpoints.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PreferVentSpawns;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public sealed class MidRoundAntagRule : StationEventSystem<MidRoundAntagRuleComp
|
|||
if (!TryGetRandomStation(out var station))
|
||||
return;
|
||||
|
||||
var spawns = FindSpawns(station.Value);
|
||||
var spawns = FindSpawns(ent, station.Value);
|
||||
if (spawns.Count == 0)
|
||||
{
|
||||
Log.Warning($"Couldn't find any suitable midround antag spawners for {ToPrettyString(ent):rule}");
|
||||
|
|
@ -33,28 +33,48 @@ public sealed class MidRoundAntagRule : StationEventSystem<MidRoundAntagRuleComp
|
|||
args.Coordinates.AddRange(spawns);
|
||||
}
|
||||
|
||||
private List<MapCoordinates> FindSpawns(EntityUid station)
|
||||
private void FindSpawnLocations(EntityUid station, List<MapCoordinates> spawns)
|
||||
{
|
||||
var spawns = new List<MapCoordinates>();
|
||||
var query = EntityQueryEnumerator<MidRoundAntagSpawnLocationComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out _, out var xform))
|
||||
{
|
||||
if (StationSystem.GetOwningStation(uid, xform) == station && xform.GridUid != null)
|
||||
spawns.Add(_xform.GetMapCoordinates(xform));
|
||||
}
|
||||
}
|
||||
|
||||
// if there are any midround antag spawns mapped, use them
|
||||
if (spawns.Count > 0)
|
||||
return spawns;
|
||||
|
||||
// otherwise, fall back to vent critter spawns
|
||||
Log.Info($"Station {ToPrettyString(station):station} has no midround antag spawnpoints mapped, falling back. Please map them!");
|
||||
private void FindVentLocations(EntityUid station, List<MapCoordinates> spawns)
|
||||
{
|
||||
var fallbackQuery = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||
while (fallbackQuery.MoveNext(out var uid, out _, out var xform))
|
||||
{
|
||||
if (StationSystem.GetOwningStation(uid, xform) == station && xform.GridUid != null)
|
||||
spawns.Add(_xform.GetMapCoordinates(xform));
|
||||
}
|
||||
}
|
||||
|
||||
private List<MapCoordinates> FindSpawns(Entity<MidRoundAntagRuleComponent> rule, EntityUid station)
|
||||
{
|
||||
var spawns = new List<MapCoordinates>();
|
||||
|
||||
if (rule.Comp.PreferVentSpawns)
|
||||
FindVentLocations(station, spawns);
|
||||
else
|
||||
FindSpawnLocations(station, spawns);
|
||||
|
||||
// if there are any midround antag spawns mapped, use them
|
||||
if (spawns.Count > 0)
|
||||
return spawns;
|
||||
|
||||
if (rule.Comp.PreferVentSpawns)
|
||||
Log.Info($"Station {ToPrettyString(station):station} has no vent critter spawnpoints mapped, falling back. Please map them!");
|
||||
else
|
||||
Log.Info($"Station {ToPrettyString(station):station} has no midround antag spawnpoints mapped, falling back. Please map them!");
|
||||
|
||||
if (rule.Comp.PreferVentSpawns)
|
||||
FindSpawnLocations(station, spawns);
|
||||
else
|
||||
FindVentLocations(station, spawns);
|
||||
|
||||
return spawns;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,6 +334,11 @@ namespace Content.Server.RoundEnd
|
|||
{
|
||||
switch (behavior)
|
||||
{
|
||||
// BEGIN DeltaV - Convert Round To Survival
|
||||
case RoundEndBehavior.BecomeSurvival:
|
||||
_gameTicker.ConvertRoundToSurvival();
|
||||
break;
|
||||
// END DeltaV
|
||||
case RoundEndBehavior.InstantEnd:
|
||||
EndRound();
|
||||
break;
|
||||
|
|
@ -419,6 +424,11 @@ namespace Content.Server.RoundEnd
|
|||
/// <summary>
|
||||
/// Do nothing
|
||||
/// </summary>
|
||||
Nothing
|
||||
Nothing,
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Replace scheduler. Crew should call for evac.
|
||||
/// </summary>
|
||||
BecomeSurvival,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ public sealed class IonStormSystem : EntitySystem
|
|||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SiliconLawSystem _siliconLaw = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IonLawSystem _ionLaw = default!;
|
||||
// [Dependency] private readonly IonLawSystem _ionLaw = default!; // DeltaV - ion laws
|
||||
[Dependency] private readonly DVIonLawSystem _dvIonLaw = default!; // DeltaV - ion laws
|
||||
|
||||
/// <summary>
|
||||
/// Randomly alters the laws of an individual silicon.
|
||||
|
|
@ -72,7 +73,7 @@ public sealed class IonStormSystem : EntitySystem
|
|||
}
|
||||
|
||||
// generate a new law...
|
||||
var newLaw = _ionLaw.GetIonLaw();
|
||||
var newLaw = _dvIonLaw.GenerateLaw(_robustRandom); // DeltaV - new ion laws // _ionLaw.GetIonLaw();
|
||||
|
||||
if (string.IsNullOrEmpty(newLaw))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -66,10 +66,7 @@ public sealed class EventManagerSystem : EntitySystem
|
|||
/// </remarks>
|
||||
public EntProtoId? TryGenerateRandomEvent(EntityTableSelector limitedEventsTable, TimeSpan? eventRunTime = null)
|
||||
{
|
||||
var availableEvents = AvailableEvents(eventRunTime: eventRunTime); // handles the player counts and individual event restrictions.
|
||||
// Putting this here only makes any sense in the context of the toolshed commands in BasicStationEventScheduler. Kill me.
|
||||
|
||||
if (!TryBuildLimitedEvents(limitedEventsTable, out var limitedEvents))
|
||||
if (!TryBuildLimitedEvents(limitedEventsTable, out var limitedEvents, currentTime: eventRunTime))
|
||||
{
|
||||
Log.Warning("Provided event table could not build dict!");
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
using Content.Shared._CD.Silicons.Borgs;
|
||||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Server._CD.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Server-side logic that shouldn't be exposed to the client.
|
||||
/// </summary>
|
||||
public sealed class BorgSwitchableSubstypeSystem : SharedBorgSwitchableSubtypeSystem
|
||||
{
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
protected override void SelectBorgSubtype(Entity<BorgSwitchableSubtypeComponent> ent)
|
||||
{
|
||||
if (ent.Comp.BorgSubtype == null)
|
||||
return;
|
||||
|
||||
if (!Prototypes.Index(ent.Comp.BorgSubtype.Value)
|
||||
.TryGetComponent<BorgSubtypeDefinitionComponent>(out var borgSubtype, ComponentFactory))
|
||||
return;
|
||||
|
||||
// Configure special components
|
||||
if (Prototypes.TryIndex(ent.Comp.BorgSubtype, out var previousPrototype) &&
|
||||
previousPrototype.TryGetComponent<BorgSubtypeDefinitionComponent>(out var previousSubtype, ComponentFactory))
|
||||
{
|
||||
if (previousSubtype.AddComponents is { } removeComponents)
|
||||
EntityManager.RemoveComponents(ent, removeComponents);
|
||||
}
|
||||
|
||||
if (borgSubtype.AddComponents is { } addComponents)
|
||||
{
|
||||
EntityManager.AddComponents(ent, addComponents);
|
||||
}
|
||||
|
||||
// inventory template configuration (hats spacing)
|
||||
if (TryComp(ent, out InventoryComponent? inventory))
|
||||
{
|
||||
_inventorySystem.SetTemplateId((ent.Owner, inventory), borgSubtype.InventoryTemplateId);
|
||||
}
|
||||
|
||||
base.SelectBorgSubtype(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ using Content.Shared.Chat;
|
|||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
|
|
@ -16,22 +18,20 @@ public sealed class SynthSystem : EntitySystem
|
|||
// Begin DeltaV - make strings static readonly
|
||||
private static readonly ProtoId<TypingIndicatorPrototype> RobotTypingIndicator = "robot";
|
||||
private static readonly ProtoId<ReagentPrototype> SynthBloodReagent = "SynthBlood";
|
||||
private static readonly ProtoId<TagPrototype> SyntheticEmotesTag = "SyntheticEmotes";
|
||||
private static readonly ProtoId<EmoteSoundsPrototype> SyntheticEmoteSounds = "SyntheticEmoteSounds";
|
||||
private static readonly ProtoId<EmotePrototype>[] SiliconEmotes = ["Beep", "Chime", "Buzz", "Buzz-Two", "Ping"];
|
||||
// End DeltaV
|
||||
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
private EmoteSoundsPrototype? _syntheticEmoteSounds; // delta-v - proper synthe emote implementation
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SynthComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<SynthComponent, EmoteEvent>(OnEmote); // delta-v - proper synthe emote implementation
|
||||
SubscribeLocalEvent<SynthComponent, EmoteEvent>(OnEmote);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, SynthComponent component, ComponentStartup args)
|
||||
|
|
@ -42,26 +42,30 @@ public sealed class SynthSystem : EntitySystem
|
|||
Dirty(uid, indicator);
|
||||
}
|
||||
|
||||
// Begin DeltaV - Change blood amount according to original BloodstreamCompoent.ReferenceSolution volume
|
||||
if (TryComp<BloodstreamComponent>(uid, out var bloodstream))
|
||||
{
|
||||
// Give them synth blood. Ion storm notif is handled in that system
|
||||
_bloodstream.ChangeBloodReagents((uid, bloodstream), new([new(SynthBloodReagent, bloodstream.BloodReferenceSolution.Volume)]));
|
||||
}
|
||||
// End DeltaV
|
||||
|
||||
_tag.AddTag(uid, SyntheticEmotesTag); // delta-v - proper synthe emote implementation
|
||||
// Add the silicon emotes to the allow list so we can ignore the emote whitelist/blacklist. This just allows the emote event to
|
||||
// be sent but it does not associate a sound with the emote. We'll intercept that event in OnEmote and play the sound manually there
|
||||
// since we cannot add just add sounds on the fly to the emote sounds of a species (everyone will have a different sound).
|
||||
if (HasComp<VocalComponent>(uid) && TryComp<SpeechComponent>(uid, out var speech))
|
||||
{
|
||||
speech.AllowedEmotes.AddRange(SiliconEmotes);
|
||||
Dirty(uid, speech);
|
||||
}
|
||||
}
|
||||
|
||||
// Start DeltaV - proper synthe emote implementation
|
||||
private void OnEmote(EntityUid uid, SynthComponent component, ref EmoteEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
_syntheticEmoteSounds ??= _proto.Index(SyntheticEmoteSounds);
|
||||
|
||||
args.Handled = _chat.TryPlayEmoteSound(uid, _syntheticEmoteSounds, args.Emote);
|
||||
// If we make it this far, its an allowed emote, so just resolve the sound from
|
||||
// the SyntheticEmoteSounds prototype and play it.
|
||||
if (_proto.Resolve(SyntheticEmoteSounds, out var emoteSound))
|
||||
args.Handled = _chat.TryPlayEmoteSound(uid, emoteSound, args.Emote);
|
||||
}
|
||||
// End DeltaV
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using Content.Server.Players.PlayTimeTracking;
|
|||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Preferences;
|
||||
|
|
@ -83,10 +84,11 @@ public sealed class SpawnCharacter : LocalizedEntityCommands
|
|||
jobName = "Passenger"; // and if they fuck up, just default to passenger again
|
||||
}
|
||||
|
||||
var gameTicker = _entitySys.GetEntitySystem<GameTicker>();
|
||||
|
||||
var coordinates = player.AttachedEntity != null
|
||||
? _entityManager.GetComponent<TransformComponent>(player.AttachedEntity.Value).Coordinates
|
||||
: _entitySys.GetEntitySystem<GameTicker>().GetObserverSpawnPoint();
|
||||
: gameTicker.GetObserverSpawnPoint();
|
||||
|
||||
if (player.AttachedEntity == null ||
|
||||
!mindSystem.TryGetMind(player.AttachedEntity.Value, out var mindId, out var mind))
|
||||
|
|
@ -95,6 +97,16 @@ public sealed class SpawnCharacter : LocalizedEntityCommands
|
|||
var mobUid = _entityManager.System<StationSpawningSystem>().SpawnPlayerMob(coordinates, profile: character, entity: null, job: jobName, station: null);
|
||||
mindSystem.TransferTo(mindId, mobUid);
|
||||
|
||||
var spawnCompleteEv = new PlayerSpawnCompleteEvent(mobUid,
|
||||
player,
|
||||
jobExists ? jobName : null,
|
||||
lateJoin: false,
|
||||
silent: true,
|
||||
gameTicker.PlayersJoinedRoundNormally,
|
||||
station: EntityUid.Invalid,
|
||||
character);
|
||||
_entityManager.EventBus.RaiseLocalEvent(mobUid, spawnCompleteEv, true);
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-spawncharacter-complete"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Server.Station.Systems;
|
|||
using Content.Server.Traits;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Server.Player;
|
||||
|
|
@ -58,9 +59,11 @@ public sealed class SpawnPlayer : LocalizedEntityCommands
|
|||
jobName = "Passenger"; // and if they fuck up, just default to passenger again
|
||||
}
|
||||
|
||||
var gameTicker = _entitySys.GetEntitySystem<GameTicker>();
|
||||
|
||||
var coordinates = shell.Player != null && shell.Player.AttachedEntity != null
|
||||
? _entityManager.GetComponent<TransformComponent>(shell.Player.AttachedEntity.Value).Coordinates
|
||||
: _entitySys.GetEntitySystem<GameTicker>().GetObserverSpawnPoint();
|
||||
: gameTicker.GetObserverSpawnPoint();
|
||||
|
||||
if (player.AttachedEntity == null ||
|
||||
!mindSystem.TryGetMind(player.AttachedEntity.Value, out var mindId, out var mind))
|
||||
|
|
@ -76,6 +79,16 @@ public sealed class SpawnPlayer : LocalizedEntityCommands
|
|||
else
|
||||
shell.WriteLine(Loc.GetString("cmd-spawnplayer-info-mind-not-transferred", ("entId", mobUid.ToString()), ("player", args[0])));
|
||||
|
||||
var spawnCompleteEv = new PlayerSpawnCompleteEvent(mobUid,
|
||||
player,
|
||||
jobExists ? jobName : null,
|
||||
lateJoin: false,
|
||||
silent: true,
|
||||
gameTicker.PlayersJoinedRoundNormally,
|
||||
station: EntityUid.Invalid,
|
||||
character);
|
||||
_entityManager.EventBus.RaiseLocalEvent(mobUid, spawnCompleteEv, true);
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-spawnplayer-complete"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed partial class CosmicCultRuleComponent : Component
|
|||
/// What happens if all of the cultists die.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.ShuttleCall;
|
||||
public RoundEndBehavior RoundEndBehavior = RoundEndBehavior.BecomeSurvival;
|
||||
|
||||
/// <summary>
|
||||
/// Sender for shuttle call.
|
||||
|
|
@ -61,6 +61,45 @@ public sealed partial class CosmicCultRuleComponent : Component
|
|||
[DataField]
|
||||
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(5);
|
||||
|
||||
#region Progress Checking
|
||||
/// <summary>
|
||||
/// The next time the monument should check the progress of the cult.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan NextProgressCheck = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time between progress checks.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan TimeBetweenProgressChecks = TimeSpan.FromMinutes(15);
|
||||
|
||||
/// <summary>
|
||||
/// The progress value of the monument last time it was checked.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int LastProgress = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The number of times the the progress check as failed. Resets after some progress.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int ConsecutiveProgressFails = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The number of times the progress check has to fail for the round for round-end behavior to happen.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int ProgressFailureTolerance = 3;
|
||||
|
||||
[DataField]
|
||||
public LocId ProgressFailTextShuttleCall = "cosmiccult-progress-fail-shuttle-call";
|
||||
|
||||
[DataField]
|
||||
public LocId ProgressFailTextAnnouncement = "cosmiccult-progress-fail-announcement";
|
||||
|
||||
#endregion
|
||||
|
||||
[DataField]
|
||||
public HashSet<EntityUid> Cultists = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,15 @@ using Robust.Shared.Timing;
|
|||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Content.Shared.Body;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.SSDIndicator;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult;
|
||||
|
||||
/// <summary>
|
||||
/// Where all the main stuff for Cosmic Cultists happens.
|
||||
///
|
||||
/// This should probably be broken up into a partial class.
|
||||
/// </summary>
|
||||
public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponent>
|
||||
{
|
||||
|
|
@ -172,6 +176,31 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
|
|||
component.StewardVoteTimer = null;
|
||||
StewardVote();
|
||||
}
|
||||
|
||||
// Progress check. Is the cult trying to progress at all? Have they gotten to T3?
|
||||
if (component.MonumentPlaced && component.CurrentTier < 3 && _timing.CurTime > component.NextProgressCheck)
|
||||
{
|
||||
// We're going to assume one monument.
|
||||
var monument = EntityQuery<MonumentComponent>().First();
|
||||
if (component.LastProgress == monument.CurrentProgress)
|
||||
component.ConsecutiveProgressFails++;
|
||||
else
|
||||
component.ConsecutiveProgressFails = 0;
|
||||
|
||||
// They haven't progressed enough in X checks, convert to survival. They can still progress BUT
|
||||
// if they haven't done ANYTHING in a long time, I doubt they will be a threat all of a sudden.
|
||||
if (component.ConsecutiveProgressFails >= component.ProgressFailureTolerance)
|
||||
{
|
||||
SetWinType((uid, component), WinType.CrewMinor);
|
||||
|
||||
_roundEnd.DoRoundEndBehavior(component.RoundEndBehavior, component.EvacShuttleTime, component.RoundEndTextSender, component.ProgressFailTextShuttleCall, component.ProgressFailTextAnnouncement);
|
||||
component.RoundEndBehavior = RoundEndBehavior.Nothing;
|
||||
}
|
||||
|
||||
component.LastProgress = monument.CurrentProgress;
|
||||
component.NextProgressCheck = _timing.CurTime + component.TimeBetweenProgressChecks;
|
||||
}
|
||||
|
||||
if (component.ExtraRiftTimer is { } riftTimer && _timing.CurTime >= riftTimer && !component.RiftStop)
|
||||
{
|
||||
component.ExtraRiftTimer = _timing.CurTime + _rand.Next(TimeSpan.FromSeconds(230), TimeSpan.FromSeconds(360)); //3min50 to 6min between new rifts. Seconds instead of minutes for granularity.
|
||||
|
|
@ -442,17 +471,19 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
|
|||
}
|
||||
}
|
||||
|
||||
private bool CultistsAlive()
|
||||
private int GetCultistsAlive()
|
||||
{
|
||||
var query = EntityQueryEnumerator<CosmicCultComponent, MobStateComponent>();
|
||||
int cultistsAlive = 0;
|
||||
while (query.MoveNext(out var ent, out _, out var mobComp))
|
||||
{
|
||||
if (TryComp<CuffableComponent>(ent, out var cuffed) && cuffed.CuffedHandCount > 0) continue;
|
||||
if (TryComp<SSDIndicatorComponent>(ent, out var ssd) && ssd.IsSSD) continue; // TODO: Maybe check how long.
|
||||
if (mobComp.Running && mobComp.CurrentState != MobState.Dead)
|
||||
return true;
|
||||
cultistsAlive++;
|
||||
}
|
||||
|
||||
return false;
|
||||
return cultistsAlive;
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(Entity<CosmicCultComponent> ent, ref MobStateChangedEvent args)
|
||||
|
|
@ -467,21 +498,26 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
|
|||
|
||||
private void CheckForActiveCultists()
|
||||
{
|
||||
if (CultistsAlive())
|
||||
// A lone cultist can't do anything, so check for more than one.
|
||||
if (GetCultistsAlive() > 1)
|
||||
return;
|
||||
|
||||
var query = QueryActiveRules();
|
||||
List<Entity<CosmicCultRuleComponent>> cosCultRules = new();
|
||||
|
||||
// ConfirmWinState needs to be outside the query, because it might add an active rule
|
||||
while (query.MoveNext(out var ruleUid, out _, out var ruleComp, out _))
|
||||
{
|
||||
ConfirmWinState((ruleUid, ruleComp));
|
||||
cosCultRules.Add((ruleUid, ruleComp));
|
||||
}
|
||||
cosCultRules.ForEach(ConfirmWinState);
|
||||
}
|
||||
|
||||
private void ConfirmWinState(Entity<CosmicCultRuleComponent> ent)
|
||||
{
|
||||
var tier = ent.Comp.CurrentTier;
|
||||
var LeaderAtCentcom = false;
|
||||
var CultistsAtCentcom = 0;
|
||||
var leaderAtCentcom = false;
|
||||
var cultistsAtCentcom = 0;
|
||||
var centcomm = _emergency.GetCentcommMaps();
|
||||
var wrapup = AllEntityQuery<CosmicCultComponent, TransformComponent>();
|
||||
while (wrapup.MoveNext(out var cultist, out _, out var cultistLocation))
|
||||
|
|
@ -489,22 +525,22 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
|
|||
if (cultistLocation.MapUid != null && centcomm.Contains(cultistLocation.MapUid.Value))
|
||||
{
|
||||
if (TryComp<CuffableComponent>(cultist, out var cuffed) && cuffed.CuffedHandCount > 0) continue; // If they are cuffed, they should be deconverted soon, so we don't count them.
|
||||
CultistsAtCentcom++;
|
||||
cultistsAtCentcom++;
|
||||
if (HasComp<CosmicCultLeadComponent>(cultist))
|
||||
LeaderAtCentcom = true;
|
||||
leaderAtCentcom = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tier < 3)
|
||||
SetWinType(ent, WinType.CrewMinor); //The monument didn't even reach tier 3, which means that either cult had a skill issue, or crew evacuated early. Minor win.
|
||||
else if (LeaderAtCentcom) //If the monument reached tier 3, all cultists have glowing eyes now, so you shouldn't let them evacuate without cuffs on.
|
||||
else if (leaderAtCentcom) //If the monument reached tier 3, all cultists have glowing eyes now, so you shouldn't let them evacuate without cuffs on.
|
||||
SetWinType(ent, WinType.CultMajor); //The Monument wasn't completed, but the cult leader's alive and at Midpoint.
|
||||
else if (CultistsAtCentcom >= 2)
|
||||
else if (cultistsAtCentcom >= 2)
|
||||
SetWinType(ent, WinType.CultMinor); //The Monument wasn't completed, but at least two cultists are alive and at Midpoint.
|
||||
else
|
||||
SetWinType(ent, WinType.Neutral); //The monument wasn't completed, no cultists escaped to midpoint. Some cultists still remain on the station, though.
|
||||
|
||||
if (CultistsAlive()) return; //If there are no cultists alive, ignore all previous checks, crew alreay won.
|
||||
if (GetCultistsAlive() > 1) return; //If there are one or less cultists alive, ignore all previous checks, crew alreay won.
|
||||
|
||||
if (tier <= 1) //Prevent the cult getting cooked by accident before anyone even knows there's a cult.
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared._DV.CosmicCult.Components.Examine;
|
||||
using Content.Shared._DV.CosmicCult.Components;
|
||||
using Content.Shared._DV.CosmicCult.Prototypes;
|
||||
using Content.Shared._DV.CosmicCult;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._DV.CosmicCult;
|
||||
|
|
@ -21,8 +27,11 @@ public sealed class CosmicGlyphSystem : SharedCosmicGlyphSystem
|
|||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly CosmicCultRuleSystem _cultRule = default!;
|
||||
[Dependency] private readonly SharedCosmicCultSystem _cosmicCult = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly HashSet<Entity<CosmicCultComponent>> _cultists = [];
|
||||
|
|
@ -32,9 +41,72 @@ public sealed class CosmicGlyphSystem : SharedCosmicGlyphSystem
|
|||
{
|
||||
SubscribeLocalEvent<CosmicGlyphComponent, ActivateInWorldEvent>(OnUseGlyph);
|
||||
SubscribeLocalEvent<CosmicGlyphComponent, ComponentStartup>(OnGlyphCreated);
|
||||
SubscribeLocalEvent<CosmicGlyphComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAlternativeVerbs);
|
||||
SubscribeLocalEvent<CosmicCultComponent, EventCosmicDrawGlyph>(OnDrawGlyph);
|
||||
SubscribeLocalEvent<CosmicGlyphDrawComponent, CosmicGlyphDrawSelectedMessage>(OnGlyphSelected);
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
private void OnGetAlternativeVerbs(Entity<CosmicGlyphComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || !args.CanAccess || !_cosmicCult.EntityIsCultist(args.User) || ent.Comp.State == GlyphStatus.Despawning)
|
||||
return;
|
||||
|
||||
args.Verbs.Add(new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString("cult-glyph-verb-erase"),
|
||||
Act = () => EraseGlyph(ent),
|
||||
});
|
||||
}
|
||||
|
||||
private void OnDrawGlyph(Entity<CosmicCultComponent> ent, ref EventCosmicDrawGlyph args)
|
||||
{
|
||||
if (_cultRule.AssociatedGamerule(ent) is not { } cult ||
|
||||
!TryComp<CosmicGlyphDrawComponent>(args.Action, out var drawComp))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var glyphs = GetAvailableGlyphs(cult.Comp.CurrentTier);
|
||||
if (glyphs.Count == 0)
|
||||
{
|
||||
drawComp.Target = null;
|
||||
_ui.CloseUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
drawComp.Target = args.Target;
|
||||
_ui.OpenUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
|
||||
_ui.SetUiState(args.Action.Owner, CosmicGlyphDrawUiKey.Key, new CosmicGlyphDrawBuiState(glyphs));
|
||||
}
|
||||
|
||||
private void OnGlyphSelected(Entity<CosmicGlyphDrawComponent> ent, ref CosmicGlyphDrawSelectedMessage args)
|
||||
{
|
||||
if (ent.Comp.Target is not { } target || !TryComp<ActionComponent>(ent, out var action) || action.Container != args.Actor
|
||||
|| !TryComp<CosmicCultComponent>(args.Actor, out _) || _cultRule.AssociatedGamerule(args.Actor) is not { } cult
|
||||
|| !_prototype.TryIndex(args.GlyphProtoId, out var glyph) || glyph.Tier > cult.Comp.CurrentTier)
|
||||
{
|
||||
ent.Comp.Target = null;
|
||||
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
|
||||
return;
|
||||
}
|
||||
|
||||
var spawned = Spawn(glyph.Entity, target);
|
||||
_cultRule.TransferCultAssociation(args.Actor, spawned);
|
||||
|
||||
ent.Comp.Target = null;
|
||||
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
|
||||
}
|
||||
|
||||
private List<ProtoId<GlyphPrototype>> GetAvailableGlyphs(int currentTier)
|
||||
{
|
||||
return _prototype.EnumeratePrototypes<GlyphPrototype>()
|
||||
.Where(glyph => glyph.Tier <= currentTier)
|
||||
.OrderBy(glyph => Loc.GetString(glyph.Name))
|
||||
.Select(glyph => new ProtoId<GlyphPrototype>(glyph.ID))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#region Base trigger
|
||||
|
||||
private void OnGlyphCreated(Entity<CosmicGlyphComponent> ent, ref ComponentStartup args)
|
||||
|
|
|
|||
|
|
@ -324,12 +324,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
|
|||
|
||||
UpdateMonumentAppearance(uid, false);
|
||||
|
||||
//this is probably unnecessary but I have no idea where they get added to the list atm - ruddygreat
|
||||
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 1))
|
||||
{
|
||||
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
|
||||
}
|
||||
|
||||
//basically completely unnecessary, but putting this here for sanity & futureproofing - ruddygreat
|
||||
var query = EntityQueryEnumerator<CosmicCultComponent>();
|
||||
while (query.MoveNext(out var cultist, out var cultComp))
|
||||
|
|
@ -356,11 +350,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
|
|||
|
||||
UpdateMonumentAppearance(uid, true);
|
||||
|
||||
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 2))
|
||||
{
|
||||
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
|
||||
}
|
||||
|
||||
var objectiveQuery = EntityQueryEnumerator<CosmicTierConditionComponent>();
|
||||
while (objectiveQuery.MoveNext(out _, out var objectiveComp))
|
||||
{
|
||||
|
|
@ -398,11 +387,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
|
|||
if (_cosmicRule.AssociatedGamerule(uid) is not { } cult)
|
||||
return;
|
||||
|
||||
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 3))
|
||||
{
|
||||
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
|
||||
}
|
||||
|
||||
UpdateMonumentAppearance(uid, true);
|
||||
|
||||
var objectiveQuery = EntityQueryEnumerator<CosmicTierConditionComponent>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Localization;
|
||||
using Content.Shared.Fax;
|
||||
using Content.Server.StationEvents.Components;
|
||||
|
||||
namespace Content.Server.GameTicking;
|
||||
|
||||
/// <summary>
|
||||
/// Extends upstream's <see cref="GameTicker" />.
|
||||
/// </summary>
|
||||
public sealed partial class GameTicker
|
||||
{
|
||||
private static readonly EntProtoId RampingSchedulerProto = "RampingStationEventScheduler";
|
||||
|
||||
private static readonly TimeSpan GracePeriod = TimeSpan.FromMinutes(10);
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Removes the basic scheduler and adds a ramping scheduler to the round. Does nothing
|
||||
/// if there is already a ramping scheduler.
|
||||
/// </summary>
|
||||
/// <returns>True if the game rules either contain or added a RampingStationEventScheduler.</returns>
|
||||
[PublicAPI]
|
||||
public bool ConvertRoundToSurvival()
|
||||
{
|
||||
// Ramping scheduler is already added. Do nothing.
|
||||
if (IsGameRuleActive(RampingSchedulerProto))
|
||||
{
|
||||
_chatManager.SendAdminAlert("RampingStationEventScheduler detected. No rules added.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a ramping scheduler with a delay.
|
||||
var rampingScheduler = AddGameRule(RampingSchedulerProto);
|
||||
var rampingSchedulerStart = _gameTiming.CurTime.Add(GracePeriod);
|
||||
EnsureComp<DelayedStartRuleComponent>(rampingScheduler).RuleStartTime = rampingSchedulerStart;
|
||||
|
||||
_chatManager.SendAdminAlert($"Major antag defeated. Converting to survival at {rampingSchedulerStart}.");
|
||||
|
||||
// End Basic Rules
|
||||
var basicRules = EntityQueryEnumerator<BasicStationEventSchedulerComponent>();
|
||||
while (basicRules.MoveNext(out var uid, out var rule))
|
||||
EndGameRule(uid);
|
||||
|
||||
return IsGameRuleActive(RampingSchedulerProto);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared._DV.CCVars;
|
||||
using Content.Shared._DV.Ghost.Roles;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Station.Components;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._DV.Ghost.Roles;
|
||||
|
||||
public sealed class DVSpawnableGhostRoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly RoleSystem _role = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
|
||||
private readonly List<EntityCoordinates> _stationVents = new();
|
||||
private readonly List<EntityCoordinates> _allVents = new();
|
||||
private readonly Dictionary<NetUserId, TimeSpan> _cooldowns = new();
|
||||
private TimeSpan _spawnCooldown;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_player.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
|
||||
Subs.CVar(_cfg, DCCVars.VentCritterGhostRoleSpawnCooldown, value => _spawnCooldown = value < TimeSpan.Zero ? TimeSpan.Zero : value, true);
|
||||
|
||||
SubscribeNetworkEvent<DVSpawnableGhostRoleRequestEvent>(OnSpawnRequest);
|
||||
SubscribeNetworkEvent<DVSpawnableGhostRoleCooldownRequestEvent>(OnCooldownRequest);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_player.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs evt)
|
||||
{
|
||||
if (evt.NewStatus == SessionStatus.Connected)
|
||||
{
|
||||
SendCooldownUpdate(evt.Session);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSpawnRequest(DVSpawnableGhostRoleRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var session = args.SenderSession;
|
||||
if (session.AttachedEntity is not { Valid: true } attached
|
||||
|| !HasComp<GhostComponent>(attached))
|
||||
{
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{session:player} sent {nameof(DVSpawnableGhostRoleRequestEvent)} without being a ghost.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TestCooldown(session.UserId, out var cooldownEnd))
|
||||
{
|
||||
SendCooldownUpdate(session);
|
||||
var remaining = Math.Ceiling((cooldownEnd.Value - _timing.CurTime).TotalSeconds);
|
||||
_popup.PopupEntity(Loc.GetString("ghost-gui-spawn-vent-critter-cooldown-popup", ("time", remaining)), attached, attached);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_prototype.TryIndex(msg.Prototype, out var role)
|
||||
|| !_prototype.TryIndex<EntityPrototype>(role.Entity, out _))
|
||||
{
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{session:player} tried to spawn as invalid {nameof(DVSpawnableGhostRoleRequestEvent)} prototype {msg.Prototype}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryPickVent(out var coords))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("ghost-gui-spawn-vent-critter-no-vents"), attached, attached);
|
||||
return;
|
||||
}
|
||||
|
||||
var mob = SpawnAtPosition(role.Entity, coords);
|
||||
_transform.AttachToGridOrMap(mob);
|
||||
EnsureComp<MindContainerComponent>(mob);
|
||||
|
||||
DebugTools.AssertNotNull(session.ContentData());
|
||||
|
||||
if(_mind.TryGetMind(session.UserId, out _, out var mind) && !mind.IsVisitingEntity)
|
||||
_mind.WipeMind(session);
|
||||
|
||||
var newMind = _mind.CreateMind(session.UserId, Comp<MetaDataComponent>(mob).EntityName);
|
||||
|
||||
_mind.SetUserId(newMind, session.UserId);
|
||||
_mind.TransferTo(newMind, mob);
|
||||
_role.MindAddRoles(newMind, role.MindRoles, newMind);
|
||||
|
||||
_cooldowns[session.UserId] = _timing.CurTime + _spawnCooldown;
|
||||
SendCooldownUpdate(session);
|
||||
|
||||
_adminLog.Add(LogType.Action, LogImpact.Low, $"{session:player} spawned as a vent critter {msg.Prototype}");
|
||||
}
|
||||
|
||||
private void OnCooldownRequest(DVSpawnableGhostRoleCooldownRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
SendCooldownUpdate(args.SenderSession);
|
||||
}
|
||||
|
||||
/// <param name="userId">The user to test for</param>
|
||||
/// <param name="cooldownEndsAt">When the cooldown will end</param>
|
||||
/// <returns>If the user is on cooldown</returns>
|
||||
private bool TestCooldown(NetUserId userId, [NotNullWhen(true)] out TimeSpan? cooldownEndsAt)
|
||||
{
|
||||
if (_cooldowns.TryGetValue(userId, out var cooldownEnd))
|
||||
{
|
||||
cooldownEndsAt = cooldownEnd;
|
||||
if (_timing.CurTime < cooldownEnd)
|
||||
return true;
|
||||
|
||||
_cooldowns.Remove(userId);
|
||||
}
|
||||
|
||||
cooldownEndsAt = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SendCooldownUpdate(ICommonSession session)
|
||||
{
|
||||
TestCooldown(session.UserId, out var cooldownEnd);
|
||||
RaiseNetworkEvent(new DVSpawnableGhostRoleCooldownUpdateEvent(cooldownEnd), session.Channel);
|
||||
}
|
||||
|
||||
private bool TryPickVent(out EntityCoordinates coords)
|
||||
{
|
||||
_stationVents.Clear();
|
||||
_allVents.Clear();
|
||||
|
||||
var query = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||
while (query.MoveNext(out _, out _, out var transform))
|
||||
{
|
||||
if (!transform.Anchored || !transform.Coordinates.IsValid(EntityManager))
|
||||
continue;
|
||||
|
||||
_allVents.Add(transform.Coordinates);
|
||||
|
||||
if (transform.GridUid is { } grid && HasComp<StationMemberComponent>(grid))
|
||||
_stationVents.Add(transform.Coordinates);
|
||||
}
|
||||
|
||||
if (_stationVents.Count > 0)
|
||||
{
|
||||
coords = _random.Pick(_stationVents);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_allVents.Count > 0)
|
||||
{
|
||||
coords = _random.Pick(_allVents);
|
||||
return true;
|
||||
}
|
||||
|
||||
coords = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,12 +11,12 @@ public sealed class IonLawCommand : ToolshedCommand
|
|||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private IonLawSystem? _ionLaw;
|
||||
private DVIonLawSystem? _ionLaw;
|
||||
|
||||
[CommandImplementation("generate")]
|
||||
public IEnumerable<string> GenerateNames(int count)
|
||||
{
|
||||
_ionLaw ??= GetSys<IonLawSystem>();
|
||||
_ionLaw ??= GetSys<DVIonLawSystem>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
using Content.Shared.Paper;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Medical.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Extends upstream's HealthAnalyzerComponent.
|
||||
/// </summary>
|
||||
/// </remarks>
|
||||
public sealed partial class HealthAnalyzerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Time when the component can print again
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||
public TimeSpan PrintAllowedAfter = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Cooldown between individual prints to prevent entity and noise spam.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan PrintCooldown = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// Prototype of an entity to use as a template for printing. The paper may contain placeholders (wrapped in braces)
|
||||
/// which will be filled in during printing.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntProtoId<PaperComponent> PrintTemplate = "PaperWrittenMedicalMedtekPatientRecord";
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier PrintReportSound = new SoundPathSpecifier("/Audio/Machines/printer.ogg");
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Medical.Components;
|
||||
using Content.Shared._DV.Medical;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Medical;
|
||||
|
||||
public sealed partial class HealthAnalyzerSystem : EntitySystem
|
||||
{
|
||||
private static readonly Regex TemplateInsert = new(@"\{([\w.]+)\}", RegexOptions.Compiled);
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly LabelSystem _label = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly PaperSystem _paper = default!;
|
||||
[Dependency] private readonly SharedGameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
|
||||
public void InitializeReportPrinting()
|
||||
{
|
||||
SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerPrintReportMessage>(OnPrint);
|
||||
}
|
||||
|
||||
private void OnPrint(Entity<HealthAnalyzerComponent> entity, ref HealthAnalyzerPrintReportMessage args)
|
||||
{
|
||||
var printer = entity.Comp;
|
||||
// Prevent users from printing too quickly
|
||||
if (printer.PrintAllowedAfter >= _timing.CurTime)
|
||||
return;
|
||||
|
||||
HealthAnalyzerComponent? analyzer = null;
|
||||
if (!Resolve(entity.Owner, ref analyzer))
|
||||
return;
|
||||
|
||||
// The health analyzer UI disables the button when the patient is invalid or out of range
|
||||
if (analyzer.ScannedEntity is not { Valid: true } patient)
|
||||
return;
|
||||
|
||||
var user = args.Actor;
|
||||
if (!IsInRange(patient, user, analyzer.MaxScanRange))
|
||||
return;
|
||||
|
||||
// Create slip of paper according to template
|
||||
var paper = Spawn(printer.PrintTemplate, Transform(user).Coordinates);
|
||||
ComposePatientRecord(paper, user, patient);
|
||||
_label.Label(paper, GetEntityName(patient));
|
||||
_hands.PickupOrDrop(user, paper);
|
||||
_audio.PlayPvs(entity.Comp.PrintReportSound, user);
|
||||
|
||||
// Start cooldown
|
||||
printer.PrintAllowedAfter = _timing.CurTime + printer.PrintCooldown;
|
||||
}
|
||||
|
||||
private bool IsInRange(EntityUid patient, EntityUid user, float? maxScanRange)
|
||||
{
|
||||
if (maxScanRange == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _transformSystem.InRange(
|
||||
(patient, Transform(patient)),
|
||||
(user, Transform(user)),
|
||||
maxScanRange.Value
|
||||
);
|
||||
}
|
||||
|
||||
private void ComposePatientRecord(EntityUid uid, EntityUid responder, EntityUid patient)
|
||||
{
|
||||
PaperComponent? paper = null;
|
||||
DamageableComponent? damageable = null;
|
||||
if (!Resolve(uid, ref paper) || !Resolve(patient, ref damageable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var template = paper.Content;
|
||||
|
||||
// Anything in this dictionary can be interpolated into the print template
|
||||
Dictionary<string, Func<string>> inserts = new()
|
||||
{
|
||||
{ "patient.name", () => GetEntityName(patient) },
|
||||
{ "patient.species", () => GetEntitySpecies(patient) },
|
||||
{ "responder.name", () => GetEntityName(responder) },
|
||||
{ "roundTime", () => (_timing.CurTime - _gameTicker.RoundStartTimeSpan).ToString(@"hh\:mm") },
|
||||
{ "damageList", () => ComposeDamageList((patient, damageable)) },
|
||||
};
|
||||
|
||||
var content = TemplateInsert.Replace(template,
|
||||
match =>
|
||||
{
|
||||
var key = match.Groups[1].Value;
|
||||
if (inserts.TryGetValue(key, out var value))
|
||||
{
|
||||
return value.Invoke();
|
||||
}
|
||||
|
||||
return match.Value;
|
||||
});
|
||||
|
||||
_paper.SetContent((uid, paper), content);
|
||||
}
|
||||
|
||||
private string ComposeDamageList(Entity<DamageableComponent> ent)
|
||||
{
|
||||
var damages = _damageable.GetPositiveDamage(ent);
|
||||
if (damages.GetTotal() <= 0)
|
||||
{
|
||||
return Loc.GetString("health-analyzer-printout-damage-none");
|
||||
}
|
||||
|
||||
var report = new FormattedMessage();
|
||||
var groupDamages = damages.GetDamagePerGroup(_prototypes);
|
||||
|
||||
var groups = groupDamages.OrderByDescending(entry => entry.Value);
|
||||
var damage = damages.DamageDict;
|
||||
foreach (var (groupId, groupDamage) in groups)
|
||||
{
|
||||
if (groupDamage <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var group = _prototypes.Index(groupId);
|
||||
|
||||
// Group header
|
||||
var groupTitleText = Loc.GetString(
|
||||
"health-analyzer-printout-damage-group-text",
|
||||
("damageGroup", group.LocalizedName),
|
||||
("amount", groupDamage)
|
||||
);
|
||||
report.AddMarkupPermissive(groupTitleText);
|
||||
report.PushNewline();
|
||||
|
||||
// List individual damage types
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
var amount = damage.GetValueOrDefault(type, 0);
|
||||
if (amount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
report.AddMarkupPermissive(Loc.GetString(
|
||||
"health-analyzer-printout-damage-type-text",
|
||||
("damageType", _prototypes.Index(type).LocalizedName),
|
||||
("amount", amount)
|
||||
));
|
||||
report.PushNewline();
|
||||
}
|
||||
}
|
||||
|
||||
return report.ToMarkup();
|
||||
}
|
||||
|
||||
private string GetEntityName(EntityUid uid)
|
||||
{
|
||||
return HasComp<MetaDataComponent>(uid)
|
||||
? Identity.Name(uid, EntityManager)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
}
|
||||
|
||||
private string GetEntitySpecies(EntityUid uid)
|
||||
{
|
||||
return Loc.GetString(
|
||||
TryComp<HumanoidProfileComponent>(uid, out var appearance)
|
||||
? _prototypes.Index(appearance.Species).Name
|
||||
: "health-analyzer-window-entity-unknown-species-text"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
|
|
@ -6,11 +8,19 @@ using Content.Server.NodeContainer.Nodes;
|
|||
using Content.Shared._DV.NodeCrawl;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.NodeContainer;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._DV.NodeCrawl;
|
||||
|
||||
public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||
[Dependency] private readonly BarotraumaSystem _barotrauma = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflection = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -20,11 +30,99 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
|||
SubscribeLocalEvent<NodeCrawlerComponent, InhaleLocationEvent>(OnInhaleLocation);
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, ExhaleLocationEvent>(OnExhaleLocation);
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, AtmosExposedGetAirEvent>(OnGetAir);
|
||||
|
||||
SubscribeLocalEvent<NodeCrawlerComponent, EntityZombifiedEvent>(OnZombify);
|
||||
}
|
||||
|
||||
private GasMixture? GetExistingAir(Entity<NodeCrawlerMovementComponent> movement)
|
||||
{
|
||||
if (movement.Comp.Node is not { } node)
|
||||
return null;
|
||||
|
||||
if (!TryComp<NodeContainerComponent>(node, out var nodeContainer))
|
||||
return null;
|
||||
|
||||
foreach (var containedNode in nodeContainer.Nodes.Values)
|
||||
{
|
||||
if (containedNode is not PipeNode pipe)
|
||||
continue;
|
||||
|
||||
return pipe.Air;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void SetupAir(Entity<NodeCrawlerMovementComponent> movement)
|
||||
{
|
||||
base.SetupAir(movement);
|
||||
|
||||
if (movement.Comp.HeldCrawler is not { } heldCrawler ||
|
||||
!TryComp<BarotraumaComponent>(heldCrawler, out var barotrauma))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetExistingAir(movement) is { } existingAir)
|
||||
{
|
||||
var pressure = existingAir.Pressure switch
|
||||
{
|
||||
// Adjust pressure based on equipment. Works differently depending on if it's "high" or "low".
|
||||
<= Atmospherics.WarningLowPressure => _barotrauma.GetFeltLowPressure(heldCrawler, barotrauma, existingAir.Pressure),
|
||||
>= Atmospherics.WarningHighPressure => _barotrauma.GetFeltHighPressure(heldCrawler, barotrauma, existingAir.Pressure),
|
||||
_ => existingAir.Pressure,
|
||||
};
|
||||
|
||||
if (pressure is >= Atmospherics.HazardLowPressure and <= Atmospherics.HazardHighPressure)
|
||||
return;
|
||||
}
|
||||
|
||||
var xform = Transform(movement);
|
||||
var indices = _transform.GetGridTilePositionOrDefault((movement, xform));
|
||||
|
||||
if (_atmosphere.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment)
|
||||
{
|
||||
// we want to get one atmosphere's worth of pressure in the air volume of the component
|
||||
// we need to take an amount of moles from the gas, so
|
||||
// PV = nRT
|
||||
// (Atmospherics.OneAtmosphere) * (movement.Comp.AirVolume) = (amount of mols) * R * (environment.Temperature)
|
||||
// solve for amount of mols
|
||||
// amount of mols = (Atmospherics.OneAtmosphere) * (movement.Comp.AirVolume) / R * (environment.Temperature)
|
||||
var transferMoles = Atmospherics.OneAtmosphere * movement.Comp.AirVolume / (environment.Temperature * Atmospherics.R);
|
||||
|
||||
movement.Comp.Air = new(movement.Comp.AirVolume);
|
||||
_atmosphere.Merge(movement.Comp.Air, environment.Remove(transferMoles));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EjectAir(Entity<NodeCrawlerMovementComponent> movement)
|
||||
{
|
||||
base.EjectAir(movement);
|
||||
|
||||
if (movement.Comp.Air is not { } air)
|
||||
return;
|
||||
|
||||
var xform = Transform(movement);
|
||||
var indices = _transform.GetGridTilePositionOrDefault((movement, xform));
|
||||
|
||||
if (_atmosphere.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is not { } environment)
|
||||
return;
|
||||
|
||||
_atmosphere.Merge(environment, air);
|
||||
air.Clear();
|
||||
}
|
||||
|
||||
private Entity<NodeCrawlerMovementComponent>? GetMovement(Entity<NodeCrawlerComponent> crawler)
|
||||
{
|
||||
if (!TryComp<NodeCrawlerMovementComponent>(crawler.Comp.Mover, out var mover))
|
||||
return null;
|
||||
|
||||
return new(crawler.Comp.Mover.Value, mover);
|
||||
}
|
||||
|
||||
private Entity<NodeContainerComponent>? GetNodeContainer(Entity<NodeCrawlerComponent> crawler)
|
||||
{
|
||||
if (!TryComp<NodeCrawlerMovementComponent>(crawler.Comp.Mover, out var mover) || mover.Node is not { } node)
|
||||
if (GetMovement(crawler) is not { } mover || mover.Comp.Node is not { } node)
|
||||
return null;
|
||||
|
||||
if (!TryComp<NodeContainerComponent>(node, out var nodeContainer))
|
||||
|
|
@ -35,6 +133,9 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
|||
|
||||
private GasMixture? GetAir(Entity<NodeCrawlerComponent> crawler)
|
||||
{
|
||||
if (GetMovement(crawler)?.Comp.Air is { } air)
|
||||
return air;
|
||||
|
||||
if (GetNodeContainer(crawler) is not { } nodeContainer)
|
||||
return null;
|
||||
|
||||
|
|
@ -79,12 +180,23 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
|||
if (!TryComp<NodeContainerComponent>(ent, out var nodeContainer))
|
||||
return;
|
||||
|
||||
// ugly workaround for https://github.com/space-wizards/RobustToolbox/issues/6694 not letting List<Type>
|
||||
// get serialized properly
|
||||
var possibleTypes = ent.Comp.ReachableNodeTypes.Select(it => _reflection.GetType(it)).ToList();
|
||||
|
||||
ent.Comp.DeadEnd = false;
|
||||
var set = new HashSet<EntityUid>();
|
||||
foreach (var node in nodeContainer.Nodes.Values)
|
||||
{
|
||||
foreach (var reachable in node.ReachableNodes)
|
||||
{
|
||||
if (possibleTypes.Count != 0 &&
|
||||
!possibleTypes.TrueForAll(type => reachable.GetType() == type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(HasComp<CrawlableNodeComponent>(reachable.Owner), $"Node {ToPrettyString(reachable.Owner)} reachable from {ToPrettyString(ent)} should be a crawlable node, but wasn't");
|
||||
set.Add(reachable.Owner);
|
||||
}
|
||||
|
||||
|
|
@ -98,4 +210,16 @@ public sealed class NodeCrawlSystem : SharedNodeCrawlSystem
|
|||
ent.Comp.ReachableNodes = set;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lengthens the amount of time that it takes a zombified vent crawling mob to get back into the vent.
|
||||
/// They are really hard to kill otherwise.
|
||||
/// </summary>
|
||||
/// <param name="ent"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnZombify(Entity<NodeCrawlerComponent> ent, ref EntityZombifiedEvent args)
|
||||
{
|
||||
ent.Comp.EnterDelay = ent.Comp.ZombieEnterDelay;
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
using Content.Shared.Store;
|
||||
using Content.Shared.Store.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Store.Conditions;
|
||||
|
||||
/// <summary>
|
||||
/// XOR (eXclusive-OR) Buy Condition
|
||||
/// A condition where that blocks buying a listing if another listing
|
||||
/// specified is bought before it. Used to enforce that a traitor
|
||||
/// can only buy 1 of X options.
|
||||
///
|
||||
/// Primarily used for choose-your-own-adventure DAGD Implants.
|
||||
/// </summary>
|
||||
public sealed partial class XORBuyCondition : ListingCondition
|
||||
{
|
||||
/// <summary>
|
||||
/// Listing(s) that if bought, block this purchase, if any.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public HashSet<ProtoId<ListingPrototype>>? Listings;
|
||||
|
||||
public override bool Condition(ListingConditionArgs args)
|
||||
{
|
||||
if (!args.EntityManager.TryGetComponent<StoreComponent>(args.StoreEntity, out var storeComp))
|
||||
return false;
|
||||
|
||||
var allListings = storeComp.FullListingsCatalog;
|
||||
|
||||
if (Listings != null)
|
||||
{
|
||||
foreach (var blacklistListing in Listings)
|
||||
{
|
||||
foreach (var listing in allListings)
|
||||
{
|
||||
if (listing.ID == blacklistListing.Id && listing.PurchaseAmount > 0)
|
||||
return false; // it was purchased, so return false to fail the condition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // If no purchases were found, return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using Content.Server.GameTicking;
|
||||
using Content.Shared._Goobstation.StationReport;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.Text;
|
||||
|
||||
namespace Content.Server._Goobstation.StationReportSystem;
|
||||
|
||||
public sealed class StationReportSystem : EntitySystem
|
||||
{
|
||||
|
||||
//this is shitcode?
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
//subscribes to the endroundevent
|
||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndTextAppend);
|
||||
}
|
||||
|
||||
private void OnRoundEndTextAppend(RoundEndTextAppendEvent args)
|
||||
{
|
||||
//locates the first entity with StationReportComponent and combines them
|
||||
var stationReports = new StringBuilder();
|
||||
var reportNumber = 1;
|
||||
|
||||
var query = EntityQueryEnumerator<StationReportComponent>();
|
||||
while (query.MoveNext(out var uid, out _)) // finds every entity with StationReportComponent
|
||||
{
|
||||
if (!TryComp<PaperComponent>(uid, out var paper))
|
||||
continue;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(paper.Content))
|
||||
continue;
|
||||
|
||||
stationReports.AppendLine($"[bold]Station Report #{reportNumber}[/bold]");
|
||||
stationReports.AppendLine(paper.Content);
|
||||
stationReports.AppendLine();
|
||||
|
||||
reportNumber++;
|
||||
}
|
||||
var stationReportText = stationReports.Length > 0
|
||||
? stationReports.ToString()
|
||||
: null;
|
||||
BroadcastStationReport(stationReportText);
|
||||
}
|
||||
|
||||
//sends a networkevent to tell the client to update the stationreporttext when recived
|
||||
public void BroadcastStationReport(string? stationReportText)
|
||||
{
|
||||
RaiseNetworkEvent(new StationReportEvent(stationReportText));//to send to client
|
||||
RaiseLocalEvent(new StationReportEvent(stationReportText));//to send to discord intergration
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ public sealed partial class RevenantStasisComponent : Component
|
|||
public Entity<RevenantComponent> Revenant;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public TimeSpan StasisDuration = TimeSpan.FromSeconds(60);
|
||||
public TimeSpan StasisDuration = TimeSpan.FromSeconds(100); // DeltaV - change from 60 to 100s
|
||||
|
||||
public RevenantStasisComponent(TimeSpan stasisDuration, Entity<RevenantComponent> revenant)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Content.Server.Speech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Scraw!!
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class AllulaloAccentComponent : Component { }
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Speech;
|
||||
|
||||
namespace Content.Server._Impstation.Speech.EntitySystems;
|
||||
|
||||
public sealed class AllulaloAccentSystem : EntitySystem
|
||||
{
|
||||
private static readonly Regex RegexLowerslurR = new Regex("r{1,3}");
|
||||
private static readonly Regex RegexUpperslurR = new Regex("R{1,3}");
|
||||
private static readonly Regex RegexLowerdoubleCK = new Regex("ck{1,3}");
|
||||
private static readonly Regex RegexUpperdoubleCK = new Regex("CK{1,3}");
|
||||
private static readonly Regex RegexCasedoubleCK = new Regex("Ck{1,3}");
|
||||
private static readonly Regex RegexCameldoubleCK = new Regex("cK{1,3}");
|
||||
private static readonly Regex RegexLowerdoubleCKT = new Regex("ct{1,3}");
|
||||
private static readonly Regex RegexUpperdoubleCKT = new Regex("CT{1,3}");
|
||||
private static readonly Regex RegexCasedoubleCKT = new Regex("Ct{1,3}");
|
||||
private static readonly Regex RegexCameldoubleCKT = new Regex("cT{1,3}");
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AllulaloAccentComponent, AccentGetEvent>(OnAccent);
|
||||
}
|
||||
|
||||
private void OnAccent(EntityUid uid, AllulaloAccentComponent component, AccentGetEvent args)
|
||||
{
|
||||
var message = args.Message;
|
||||
|
||||
// Im not writing music in this one
|
||||
message = RegexLowerslurR.Replace(message, "rr");
|
||||
// Sorry to the snalienaccentsystem fans
|
||||
message = RegexUpperslurR.Replace(message, "RR");
|
||||
//
|
||||
message = RegexLowerdoubleCK.Replace(message, "ck-ck");
|
||||
//
|
||||
message = RegexUpperdoubleCK.Replace(message, "CK-CK");
|
||||
//
|
||||
message = RegexCasedoubleCK.Replace(message, "Ck-ck");
|
||||
//
|
||||
message = RegexCameldoubleCK.Replace(message, "cK-CK");
|
||||
//
|
||||
message = RegexLowerdoubleCKT.Replace(message, "ck-ct");
|
||||
//
|
||||
message = RegexUpperdoubleCKT.Replace(message, "CK-CT");
|
||||
//
|
||||
message = RegexCasedoubleCKT.Replace(message, "Ck-ct");
|
||||
//
|
||||
message = RegexCameldoubleCKT.Replace(message, "cK-CT");
|
||||
args.Message = message;
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +73,7 @@ public sealed partial class SleepingSystem : EntitySystem
|
|||
SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt);
|
||||
SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt);
|
||||
|
||||
SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectRelayedEvent<MobStateChangedEvent>>(OnStatusMobStateChanged);
|
||||
SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
|
||||
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
|
||||
//SubscribeLocalEvent<SleepingComponent, EmoteAttemptEvent>(OnEmoteAttempt); # DeltaV - Reverted "Block emotes for sleeping" (upstream PR #32779)
|
||||
|
|
@ -163,7 +164,6 @@ public sealed partial class SleepingSystem : EntitySystem
|
|||
|
||||
private void OnSpeakAttempt(Entity<SleepingComponent> ent, ref SpeakAttemptEvent args)
|
||||
{
|
||||
// TODO reduce duplication of this behavior with MobStateSystem somehow
|
||||
if (HasComp<AllowNextCritSpeechComponent>(ent))
|
||||
{
|
||||
RemCompDeferred<AllowNextCritSpeechComponent>(ent);
|
||||
|
|
@ -270,14 +270,19 @@ public sealed partial class SleepingSystem : EntitySystem
|
|||
/// </summary>
|
||||
private void OnMobStateChanged(Entity<SleepingComponent> ent, ref MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState == MobState.Dead)
|
||||
{
|
||||
RemComp<SpamEmitSoundComponent>(ent);
|
||||
RemComp<SleepingComponent>(ent);
|
||||
return;
|
||||
}
|
||||
if (TryComp<SpamEmitSoundComponent>(ent, out var spam))
|
||||
_emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive);
|
||||
|
||||
if (args.NewMobState == MobState.Dead)
|
||||
RemCompDeferred<SleepingComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnStatusMobStateChanged(Entity<ForcedSleepingStatusEffectComponent> ent, ref StatusEffectRelayedEvent<MobStateChangedEvent> args)
|
||||
{
|
||||
if (args.Args.NewMobState == MobState.Dead || HasComp<SleepingComponent>(args.Args.Target))
|
||||
return;
|
||||
|
||||
TrySleeping(args.Args.Target);
|
||||
}
|
||||
|
||||
private void OnStatusEffectApplied(Entity<ForcedSleepingStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Body.Components;
|
||||
using Content.Shared._DV.Traits.Assorted;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
|
|
@ -18,7 +19,7 @@ public sealed class BrainSystem : EntitySystem
|
|||
SubscribeLocalEvent<BrainComponent, OrganGotInsertedEvent>((uid, _, args) => HandleMind(args.Target, uid));
|
||||
SubscribeLocalEvent<BrainComponent, OrganGotRemovedEvent>((uid, _, args) => HandleMind(uid, args.Target));
|
||||
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
|
||||
SubscribeLocalEvent<BrainComponent, MakeBrainUnborgableEvent>(OnMakeUnborgable);
|
||||
SubscribeLocalEvent<BrainComponent, BodyRelayedEvent<MakeBrainUnborgableEvent>>(OnMakeUnborgable); // Delta V - Expanded for Delta V MMI Trait
|
||||
}
|
||||
|
||||
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
|
||||
|
|
@ -44,9 +45,10 @@ public sealed class BrainSystem : EntitySystem
|
|||
}
|
||||
|
||||
// Delta V - Begin Unborgable
|
||||
private void OnMakeUnborgable(Entity<BrainComponent> ent, ref MakeBrainUnborgableEvent args)
|
||||
private void OnMakeUnborgable(Entity<BrainComponent> ent, ref BodyRelayedEvent<MakeBrainUnborgableEvent> args)
|
||||
{
|
||||
EnsureComp<MindContainerComponent>(ent);
|
||||
EnsureComp<UnborgableComponent>(ent);
|
||||
}
|
||||
// Delta V - End Unborgable
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,8 +420,7 @@ public abstract partial class SharedStaminaSystem : EntitySystem
|
|||
component.Critical = true;
|
||||
component.StaminaDamage = component.CritThreshold;
|
||||
|
||||
if (StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime))
|
||||
StunSystem.TrySeeingStars(uid);
|
||||
StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime, true);
|
||||
|
||||
// Give them buffer before being able to be re-stunned
|
||||
component.NextUpdate = Timing.CurTime + component.StunTime + StamCritBufferTime;
|
||||
|
|
|
|||
|
|
@ -455,6 +455,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
|||
if (!SetState(uid, DoorState.Closing, door))
|
||||
return;
|
||||
|
||||
SetCollidable(uid, true, door); // DeltaV - doors are solid as soon as we start closing
|
||||
|
||||
if (predicted)
|
||||
Audio.PlayPredicted(door.CloseSound, uid, user, AudioParams.Default.WithVolume(-5));
|
||||
else if (_net.IsServer)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ public abstract class SharedDrunkSystem : EntitySystem
|
|||
|
||||
[Dependency] protected readonly StatusEffectsSystem Status = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<LightweightDrunkComponent, DrunkEvent>(OnLightweightDrinking);
|
||||
SubscribeLocalEvent<HeavyweightDrunkComponent, DrunkEvent>(OnHeavyweightDrinking); //imp
|
||||
}
|
||||
|
||||
public void TryApplyDrunkenness(EntityUid uid, TimeSpan boozePower)
|
||||
|
|
@ -38,6 +40,12 @@ public abstract class SharedDrunkSystem : EntitySystem
|
|||
args.Duration *= entity.Comp.BoozeStrengthMultiplier;
|
||||
}
|
||||
|
||||
//imp addition, repeated code bad, i know, but it seems excessive to make a whole other new method that both methods use
|
||||
private void OnHeavyweightDrinking(Entity<HeavyweightDrunkComponent> entity, ref DrunkEvent args)
|
||||
{
|
||||
args.Duration *= entity.Comp.BoozeStrengthMultiplier;
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct DrunkEvent(TimeSpan Duration);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace Content.Shared.Humanoid
|
|||
yield return HumanoidVisualLayers.FacialHair;
|
||||
yield return HumanoidVisualLayers.Snout;
|
||||
yield return HumanoidVisualLayers.SnoutCover;
|
||||
yield return HumanoidVisualLayers.NeckFluff;
|
||||
break;
|
||||
case HumanoidVisualLayers.Snout:
|
||||
yield return HumanoidVisualLayers.Snout;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public sealed partial class RepairableComponent : Component
|
|||
/// Tool quality necessary to repair this device.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<ToolQualityPrototype> QualityNeeded = "Welding";
|
||||
public ProtoId<ToolQualityPrototype> QualityNeeded = "Applicating"; // Monolith - Nanite Applicator
|
||||
|
||||
/// <summary>
|
||||
/// The base tool use delay (seconds). This will be modified by the tool's quality
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Shared.Database;
|
|||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Tools.Components; // Monolith - Nanite Applicators
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
|
|
@ -115,8 +116,26 @@ public sealed partial class RepairableSystem : EntitySystem
|
|||
delay *= ent.Comp.SelfRepairPenalty;
|
||||
}
|
||||
|
||||
// BEGIN DeltaV - Scale Repair Time with Damage
|
||||
// If its a self-repair, ignore it since they've already been penalized.
|
||||
if (args.User != args.Target && TryComp<DamageableComponent>(args.Target, out var damageComp))
|
||||
{
|
||||
// TODO: Scale with the destructible threshold if DestructibleSystem ever gets more prediction added.
|
||||
// For now, just scale up the delay per 100 damage, or reduce the delay if its less.
|
||||
var totalDamage = _damageableSystem.GetPositiveDamage((args.Target, damageComp)).GetTotal();
|
||||
delay *= Math.Clamp((float)totalDamage / 100.0f, 0.5f, 3.0f);
|
||||
}
|
||||
// END DeltaV
|
||||
|
||||
// BEGIN Monolith - Nanite Applicators
|
||||
if (!TryComp<ToolComponent>(args.Used, out var tool))
|
||||
return;
|
||||
// END Monolith
|
||||
|
||||
// Run the repairing doafter
|
||||
args.Handled = _toolSystem.UseTool(args.Used, args.User, ent.Owner, delay, ent.Comp.QualityNeeded, new RepairDoAfterEvent(), ent.Comp.FuelCost);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public sealed partial class RevenantComponent : Component
|
|||
public string SpawnOnDeathPrototype = "EctoplasmRevenant"; // DeltaV
|
||||
|
||||
[DataField("stasisTime"), ViewVariables(VVAccess.ReadWrite)] // Begin Imp Changes
|
||||
public TimeSpan StasisTime = TimeSpan.FromSeconds(60);
|
||||
public TimeSpan StasisTime = TimeSpan.FromSeconds(100); // DeltaV - change from 60 to 100s
|
||||
|
||||
/// <summary>
|
||||
/// If true, only bible users can exorcise this revenant
|
||||
|
|
@ -50,7 +50,7 @@ public sealed partial class RevenantComponent : Component
|
|||
/// grinder will explode.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool GrindingRequiresSalt = true; // End Imp Changes
|
||||
public bool GrindingRequiresSalt = false; // End Imp Changes - DeltaV - true to false
|
||||
|
||||
/// <summary>
|
||||
/// The entity's current max amount of essence. Can be increased
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift - borg subtypes
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
|
|
@ -96,6 +97,10 @@ public abstract partial class SharedBorgSwitchableTypeSystem : EntitySystem // D
|
|||
_userInterface.CloseUi((ent.Owner, null), BorgSwitchableTypeUiKey.SelectBorgType);
|
||||
|
||||
UpdateEntityAppearance(ent);
|
||||
|
||||
// AL - event for subtype system, always runs at end of borg type code
|
||||
var ev = new AfterBorgTypeSelectEvent();
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
}
|
||||
|
||||
protected void UpdateEntityAppearance(Entity<BorgSwitchableTypeComponent> entity)
|
||||
|
|
@ -120,24 +125,25 @@ public abstract partial class SharedBorgSwitchableTypeSystem : EntitySystem // D
|
|||
{
|
||||
footstepModifier.FootstepSoundCollection = prototype.FootstepCollection;
|
||||
}
|
||||
|
||||
if (prototype.SpriteBodyMovementState is { } movementState)
|
||||
{
|
||||
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||
spriteMovement.NoMovementLayers.Clear();
|
||||
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = prototype.SpriteBodyState,
|
||||
};
|
||||
spriteMovement.MovementLayers.Clear();
|
||||
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||
{
|
||||
State = movementState,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<SpriteMovementComponent>(entity);
|
||||
}
|
||||
// Start CosmicDrift Changes - Moved to BorgSwitchableTypeSystem.cs
|
||||
// if (prototype.SpriteBodyMovementState is { } movementState)
|
||||
// {
|
||||
// var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||
// spriteMovement.NoMovementLayers.Clear();
|
||||
// spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||
// {
|
||||
// State = prototype.SpriteBodyState,
|
||||
// };
|
||||
// spriteMovement.MovementLayers.Clear();
|
||||
// spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||
// {
|
||||
// State = movementState,
|
||||
// };
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// RemComp<SpriteMovementComponent>(entity);
|
||||
// }
|
||||
// End CosmicDrift Changes - Moved to BorgSwitchableTypeSystem.cs
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Content.Shared._DV.Psionics.Events; // DeltaV - Psionics Refactor
|
|||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Mobs.Events;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Rejuvenate;
|
||||
|
|
@ -23,6 +24,9 @@ public sealed partial class StatusEffectsSystem
|
|||
SubscribeLocalEvent<StatusEffectContainerComponent, RefreshMovementSpeedModifiersEvent>(RelayStatusEffectEvent);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, UpdateCanMoveEvent>(RelayStatusEffectEvent);
|
||||
|
||||
// By Ref Events
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, MobStateChangedEvent>(RefRelayStatusEffectEvent);
|
||||
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, RefreshFrictionModifiersEvent>(RefRelayStatusEffectEvent);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, TileFrictionEvent>(RefRelayStatusEffectEvent);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public abstract partial class SharedStunSystem
|
|||
|
||||
// Here so server can tell the client to do things
|
||||
// Don't dirty the component if we don't need to
|
||||
if (!Appearance.TryGetData<bool>(entity, StunVisuals.SeeingStars, out var stars, entity.Comp) && stars)
|
||||
if (Appearance.TryGetData<bool>(entity, StunVisuals.SeeingStars, out var stars, entity.Comp) && stars)
|
||||
return;
|
||||
|
||||
if (!Blocker.CanConsciouslyPerformAction(entity))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Globalization;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Alert;
|
||||
|
|
@ -128,25 +129,25 @@ public abstract partial class SharedStunSystem : EntitySystem
|
|||
}
|
||||
|
||||
// TODO STUN: Make events for different things. (Getting modifiers, attempt events, informative events...)
|
||||
public bool TryAddStunDuration(EntityUid uid, TimeSpan duration)
|
||||
public bool TryAddStunDuration(EntityUid uid, TimeSpan duration, bool visualized = false)
|
||||
{
|
||||
if (!_status.TryAddStatusEffectDuration(uid, StunId, duration))
|
||||
return false;
|
||||
|
||||
OnStunnedSuccessfully(uid, duration);
|
||||
OnStunnedSuccessfully(uid, duration, visualized);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryUpdateStunDuration(EntityUid uid, TimeSpan? duration)
|
||||
public bool TryUpdateStunDuration(EntityUid uid, TimeSpan? duration, bool visualized = false)
|
||||
{
|
||||
if (!_status.TryUpdateStatusEffectDuration(uid, StunId, duration))
|
||||
return false;
|
||||
|
||||
OnStunnedSuccessfully(uid, duration);
|
||||
OnStunnedSuccessfully(uid, duration, visualized);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnStunnedSuccessfully(EntityUid uid, TimeSpan? duration)
|
||||
private void OnStunnedSuccessfully(EntityUid uid, TimeSpan? duration, bool visualized)
|
||||
{
|
||||
var ev = new StunnedEvent(); // todo: rename event or change how it is raised - this event is raised each time duration of stun was externally changed
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
|
@ -154,8 +155,11 @@ public abstract partial class SharedStunSystem : EntitySystem
|
|||
var evDropHands = new DropHandItemsEvent();
|
||||
RaiseLocalEvent(uid, ref evDropHands);
|
||||
|
||||
if (visualized)
|
||||
TrySeeingStars(uid);
|
||||
|
||||
var timeForLogs = duration.HasValue
|
||||
? duration.Value.Seconds.ToString()
|
||||
? duration.Value.TotalSeconds.ToString(CultureInfo.CurrentCulture)
|
||||
: "Infinite";
|
||||
_adminLogger.Add(LogType.Stamina, LogImpact.Medium, $"{ToPrettyString(uid):user} stunned for {timeForLogs} seconds");
|
||||
}
|
||||
|
|
@ -283,7 +287,7 @@ public abstract partial class SharedStunSystem : EntitySystem
|
|||
if (time != null)
|
||||
{
|
||||
UpdateKnockdownTime((uid, component), time.Value, refresh);
|
||||
_adminLogger.Add(LogType.Stamina, LogImpact.Medium, $"{ToPrettyString(uid):user} was knocked down for {time.Value.Seconds} seconds");
|
||||
_adminLogger.Add(LogType.Stamina, LogImpact.Medium, $"{ToPrettyString(uid):user} was knocked down for {time.Value.TotalSeconds} seconds");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -292,7 +296,7 @@ public abstract partial class SharedStunSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
public bool TryAddParalyzeDuration(EntityUid uid, TimeSpan? duration)
|
||||
public bool TryAddParalyzeDuration(EntityUid uid, TimeSpan? duration, bool visualized = false)
|
||||
{
|
||||
if (duration == null)
|
||||
return TryUpdateParalyzeDuration(uid, duration);
|
||||
|
|
@ -302,19 +306,19 @@ public abstract partial class SharedStunSystem : EntitySystem
|
|||
|
||||
// We can't exit knockdown when we're stunned, so this prevents knockdown lasting longer than the stun.
|
||||
Knockdown(uid, null, false, true, true);
|
||||
OnStunnedSuccessfully(uid, duration);
|
||||
OnStunnedSuccessfully(uid, duration, visualized);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryUpdateParalyzeDuration(EntityUid uid, TimeSpan? duration)
|
||||
public bool TryUpdateParalyzeDuration(EntityUid uid, TimeSpan? duration, bool visualized = false)
|
||||
{
|
||||
if (!_status.TryUpdateStatusEffectDuration(uid, StunId, duration))
|
||||
return false;
|
||||
|
||||
// We can't exit knockdown when we're stunned, so this prevents knockdown lasting longer than the stun.
|
||||
Knockdown(uid, null, false, true, true);
|
||||
OnStunnedSuccessfully(uid, duration);
|
||||
OnStunnedSuccessfully(uid, duration, visualized);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,4 +73,10 @@ public sealed partial class WelderComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public bool TankSafe;
|
||||
|
||||
/// <summary>
|
||||
/// Monolith - This variable gets rid of the status display.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool OnlyDisplayFuel = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,19 +84,33 @@ public abstract partial class SharedToolSystem
|
|||
{
|
||||
using (args.PushGroup(nameof(WelderComponent)))
|
||||
{
|
||||
if (ItemToggle.IsActivated(entity.Owner))
|
||||
// BEGIN Monolith - Nanite Applicators
|
||||
if (!entity.Comp.OnlyDisplayFuel)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message"));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-not-lit-message"));
|
||||
var lit = Loc.GetString("welder-component-on-examine-welder-not-lit-message");
|
||||
|
||||
if (ItemToggle.IsActivated(entity.Owner))
|
||||
lit = Loc.GetString("welder-component-on-examine-welder-lit-message");
|
||||
|
||||
args.PushMarkup(lit);
|
||||
}
|
||||
// END Monolith
|
||||
|
||||
if (args.IsInDetailsRange)
|
||||
{
|
||||
var (fuel, capacity) = GetWelderFuelAndCapacity(entity.Owner, entity.Comp);
|
||||
|
||||
// BEGIN Monolith - Nanite Applicator
|
||||
if (entity.Comp.OnlyDisplayFuel)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("welder-component-on-examine-less-detailed-message",
|
||||
("colorName", fuel < capacity / FixedPoint2.New(4f) ? "darkorange" : "orange"),
|
||||
("fuelLeft", fuel),
|
||||
("fuelCapacity", capacity)));
|
||||
return;
|
||||
}
|
||||
// END Monolith
|
||||
|
||||
args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
|
||||
("colorName", fuel < capacity / FixedPoint2.New(4f) ? "darkorange" : "orange"),
|
||||
("fuelLeft", fuel),
|
||||
|
|
@ -120,6 +134,19 @@ public abstract partial class SharedToolSystem
|
|||
&& _whitelist.IsWhitelistPass(tank.FuelWhitelist, entity.Owner) //imp
|
||||
&& SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out var solutionComp, out var welderSolution))
|
||||
{
|
||||
// BEGIN Monolith - Nanite Applicators
|
||||
foreach (var reagent in targetSolution.Contents)
|
||||
{
|
||||
if (reagent.Reagent.Prototype != entity.Comp.FuelReagent.Id)
|
||||
{
|
||||
_popup.PopupClient(
|
||||
Loc.GetString("welder-component-incompatible-fuel", ("owner", args.Target)), entity, args.User);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// END Monolith
|
||||
|
||||
var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
|
||||
if (trans > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
using Content.Shared.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CD.Prototypes;
|
||||
|
||||
public sealed class ALPrototypeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _compFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public IEnumerable<(EntityPrototype Prototype, T Component)> EnumerateComponents<T>() where T : IComponent, new()
|
||||
{
|
||||
foreach (var entity in _prototype.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (entity.TryGetComponent(out T? comp, _compFactory))
|
||||
yield return (entity, comp);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EntityPrototype> EnumerateEntities<T>() where T : IComponent, new()
|
||||
{
|
||||
foreach (var entity in _prototype.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (entity.HasComponent<T>(_compFactory))
|
||||
yield return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._CD.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Information relating to a borg's subtype. Should be mostly cosmetic.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[EntityCategory("BorgSubtype")]
|
||||
public sealed partial class BorgSubtypeDefinitionComponent : Component
|
||||
{
|
||||
private static ProtoId<SoundCollectionPrototype> DefaultFootsteps = new("FootstepBorg"); // DeltaV - Removed Readonly
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="BorgTypePrototype.InventoryTemplateId"/>
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField] public ProtoId<InventoryTemplatePrototype> InventoryTemplateId = "borgShort";
|
||||
|
||||
/// <summary>
|
||||
/// The parent borg type of this subtype.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public string ParentType;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="BorgTypePrototype.AddComponents"/>
|
||||
/// </summary>
|
||||
[DataField] public ComponentRegistry? AddComponents;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite path that the prototype's layer data will reference.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField] public ResPath? SpritePath;
|
||||
|
||||
/// <summary>
|
||||
/// The visual layer data for the subtype.
|
||||
/// At the minimum should have definitions for each value of <see cref="BorgVisualLayers"/>.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public PrototypeLayerData[] LayerData;
|
||||
|
||||
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public string SpriteHasMindState;
|
||||
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public string SpriteNoMindState;
|
||||
|
||||
[DataField, AutoNetworkedField] public string? SpriteBodyState;
|
||||
|
||||
[DataField, AutoNetworkedField] public Vector2? Offset;
|
||||
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public EntProtoId DummyPrototype;
|
||||
|
||||
[DataField, AutoNetworkedField] public string PetSuccessString = "petting-success-generic-cyborg";
|
||||
[DataField, AutoNetworkedField] public string PetFailureString = "petting-failure-generic-cyborg";
|
||||
|
||||
/// <summary>
|
||||
/// Sound specifier for footstep sounds created by this subtype.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public SoundSpecifier FootstepCollection { get; set; } = new SoundCollectionSpecifier(DefaultFootsteps);
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="BorgTypePrototype.SpriteBodyMovementState"/>
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public string? SpriteBodyMovementState { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CD.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Component given to borgs that should be able to select subtypes inside of the borg type selection menu.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[AutoGenerateComponentState(true)]
|
||||
public sealed partial class BorgSwitchableSubtypeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="BorgSubtypeDefinitionComponent"/> of this chassis.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<EntityPrototype>? BorgSubtype;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CD.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Shared behaviour for borg switchable subtype logic.
|
||||
/// Essentially a reimplementation of <see cref="SharedBorgSwitchableTypeSystem"/> specifically for cosmetic functions.
|
||||
/// </summary>
|
||||
public abstract class SharedBorgSwitchableSubtypeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly InteractionPopupSystem _interactionPopup = default!;
|
||||
[Dependency] protected readonly IPrototypeManager Prototypes = default!;
|
||||
[Dependency] protected readonly IComponentFactory ComponentFactory = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, MapInitEvent>(OnMapInit); // make sure that our subtype is selected first
|
||||
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, AfterBorgTypeSelectEvent>(OnBorgTypeSelect);
|
||||
SubscribeLocalEvent<BorgSwitchableSubtypeComponent, TypeTryingToUpdateVisualsEvent>(OnBorgTypeUpdatingVisuals);
|
||||
|
||||
Subs.BuiEvents<BorgSwitchableTypeComponent>(BorgSwitchableTypeUiKey.SelectBorgType,
|
||||
sub =>
|
||||
{
|
||||
sub.Event<BorgSelectSubtypeMessage>(SelectSubtypeMessageHandler);
|
||||
});
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
private void OnBorgTypeUpdatingVisuals(Entity<BorgSwitchableSubtypeComponent> ent, ref TypeTryingToUpdateVisualsEvent args)
|
||||
{
|
||||
if (!ent.Comp.BorgSubtype.HasValue)
|
||||
return;
|
||||
|
||||
Dirty(ent);
|
||||
SelectBorgSubtype(ent);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<BorgSwitchableSubtypeComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
if (ent.Comp.BorgSubtype != null)
|
||||
{
|
||||
SelectBorgSubtype(ent);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBorgTypeSelect(Entity<BorgSwitchableSubtypeComponent> ent, ref AfterBorgTypeSelectEvent args)
|
||||
{
|
||||
if (!ent.Comp.BorgSubtype.HasValue)
|
||||
return;
|
||||
|
||||
UpdateEntityAppearance(ent);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
protected virtual void SelectBorgSubtype(Entity<BorgSwitchableSubtypeComponent> ent)
|
||||
{
|
||||
UpdateEntityAppearance(ent);
|
||||
}
|
||||
|
||||
private void UpdateEntityAppearance(Entity<BorgSwitchableSubtypeComponent> entity)
|
||||
{
|
||||
if (!Prototypes.TryIndex(entity.Comp.BorgSubtype, out var subtypePrototype))
|
||||
return;
|
||||
|
||||
UpdateEntityAppearance(entity, subtypePrototype);
|
||||
}
|
||||
|
||||
protected virtual void UpdateEntityAppearance(Entity<BorgSwitchableSubtypeComponent> entity,
|
||||
EntityPrototype borgSubtypePrototype)
|
||||
{
|
||||
if (!borgSubtypePrototype.TryGetComponent<BorgSubtypeDefinitionComponent>(out var borgSubtype, ComponentFactory))
|
||||
return;
|
||||
|
||||
if (TryComp(entity, out InteractionPopupComponent? popup))
|
||||
{
|
||||
_interactionPopup.SetInteractSuccessString((entity.Owner, popup), borgSubtype.PetSuccessString);
|
||||
_interactionPopup.SetInteractFailureString((entity.Owner, popup), borgSubtype.PetFailureString);
|
||||
}
|
||||
|
||||
if (TryComp(entity, out FootstepModifierComponent? footstepModifier))
|
||||
{
|
||||
footstepModifier.FootstepSoundCollection = borgSubtype.FootstepCollection;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectSubtypeMessageHandler(EntityUid uid, BorgSwitchableTypeComponent borgSwitchableTypeComponent, BorgSelectSubtypeMessage args)
|
||||
{
|
||||
if (!TryComp<BorgSwitchableSubtypeComponent>(uid, out var subtypeComp))
|
||||
return;
|
||||
|
||||
subtypeComp.BorgSubtype = args.Subtype;
|
||||
Dirty(uid, subtypeComp);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._CD.Silicons.Borgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BorgSelectSubtypeMessage(ProtoId<EntityPrototype>? subtype) : BoundUserInterfaceMessage
|
||||
{
|
||||
public ProtoId<EntityPrototype>? Subtype = subtype;
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct AfterBorgTypeSelectEvent;
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct TypeTryingToUpdateVisualsEvent;
|
||||
|
|
@ -222,6 +222,12 @@ public sealed partial class DCCVars
|
|||
public static readonly CVarDef<bool> EnablePresetCooldowns =
|
||||
CVarDef.Create("game.enable_preset_cooldowns", false, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time a player must wait after spawning as a vent critter ghost role before spawning as another one
|
||||
/// </summary>
|
||||
public static readonly CVarDef<TimeSpan> VentCritterGhostRoleSpawnCooldown =
|
||||
CVarDef.Create("ghost.vent_critter_spawn_cooldown", TimeSpan.FromMinutes(3), CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// A string containing a list of newline-separated strings to be highlighted in the chat. Use this instead of Wizden's CVar.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Content.Shared._DV.Carrying;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this component will override the number of free hands required to carry an entity, always requiring one hand instead.
|
||||
/// Used primarily for entities which only have one hand, but still need to be able to carry.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CarryingSystem))]
|
||||
public sealed partial class CarrierOneHandComponent : Component;
|
||||
|
|
@ -297,8 +297,10 @@ public sealed class CarryingSystem : EntitySystem
|
|||
if (_net.IsClient) // no spawning prediction
|
||||
return;
|
||||
|
||||
_virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
|
||||
_virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
|
||||
for (var x = 0; x < Comp<CarriableComponent>(carried).FreeHandsRequired; x++)
|
||||
{
|
||||
_virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryCarry(EntityUid carrier, Entity<CarriableComponent?> toCarry)
|
||||
|
|
@ -358,6 +360,10 @@ public sealed class CarryingSystem : EntitySystem
|
|||
|
||||
public bool CanCarry(EntityUid carrier, Entity<CarriableComponent> carried)
|
||||
{
|
||||
var handsRequired = carried.Comp.FreeHandsRequired; // imp
|
||||
if (TryComp<CarrierOneHandComponent>(carrier, out _))// && !carried.Comp.OneHandOverride)
|
||||
handsRequired = 1;
|
||||
|
||||
return
|
||||
carrier != carried.Owner &&
|
||||
// can't carry multiple people, even if you have 4 hands it will break invariants when removing carryingcomponent for first carried person
|
||||
|
|
@ -368,7 +374,7 @@ public sealed class CarryingSystem : EntitySystem
|
|||
!HasComp<BeingCarriedComponent>(carrier) &&
|
||||
!HasComp<BeingCarriedComponent>(carried) &&
|
||||
// finally check that there are enough free hands
|
||||
TryComp<HandsComponent>(carrier, out var hands) && _hands.CountFreeHands((carrier, hands)) >= carried.Comp.FreeHandsRequired;
|
||||
TryComp<HandsComponent>(carrier, out var hands) && _hands.CountFreeHands((carrier, hands)) >= handsRequired;
|
||||
}
|
||||
|
||||
private float MassContest(EntityUid roller, EntityUid target)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public sealed partial class CosmicCultComponent : Component
|
|||
[
|
||||
"ActionCosmicSiphon",
|
||||
"ActionCosmicBlank",
|
||||
"ActionCosmicDrawGlyph",
|
||||
];
|
||||
|
||||
[DataField]
|
||||
|
|
|
|||
|
|
@ -18,18 +18,6 @@ public sealed partial class MonumentComponent : Component
|
|||
[DataField]
|
||||
public SoundSpecifier InfusionSFX = new SoundPathSpecifier("/Audio/_DV/CosmicCult/insert_entropy.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// the list of glyphs that this monument is allowed to scribe
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public HashSet<ProtoId<GlyphPrototype>> UnlockedGlyphs = [];
|
||||
|
||||
/// <summary>
|
||||
/// the glyph that will be scribed when the button is pressed
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<GlyphPrototype> SelectedGlyph;
|
||||
|
||||
/// <summary>
|
||||
/// the total amount of entropy that has been inserted into the monument
|
||||
/// </summary>
|
||||
|
|
@ -120,15 +108,6 @@ public sealed class InfluenceSelectedMessage(ProtoId<InfluencePrototype> influen
|
|||
public ProtoId<InfluencePrototype> InfluenceProtoId = influenceProtoId;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GlyphSelectedMessage(ProtoId<GlyphPrototype> glyphProtoId) : BoundUserInterfaceMessage
|
||||
{
|
||||
public ProtoId<GlyphPrototype> GlyphProtoId = glyphProtoId;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class GlyphRemovedMessage : BoundUserInterfaceMessage;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MonumentVisuals : byte
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.CosmicCult;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CosmicCultActionComponent : Component;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CosmicGlyphDrawComponent : Component
|
||||
{
|
||||
public EntityCoordinates? Target;
|
||||
}
|
||||
|
||||
public sealed partial class EventCosmicSiphon : EntityTargetActionEvent;
|
||||
public sealed partial class EventCosmicBlank : EntityTargetActionEvent;
|
||||
public sealed partial class EventCosmicPlaceMonument : InstantActionEvent; //given to the cult leader on roundstart
|
||||
|
|
@ -16,6 +24,7 @@ public sealed partial class EventCosmicIngress : EntityTargetActionEvent;
|
|||
public sealed partial class EventCosmicImposition : InstantActionEvent;
|
||||
public sealed partial class EventCosmicNova : WorldTargetActionEvent;
|
||||
public sealed partial class EventCosmicFragmentation : EntityTargetActionEvent;
|
||||
public sealed partial class EventCosmicDrawGlyph : WorldTargetActionEvent;
|
||||
|
||||
// COLOSSUS ACTIONS
|
||||
public sealed partial class EventCosmicColossusSunder : WorldTargetActionEvent;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
using Content.Shared._DV.CosmicCult.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.CosmicCult;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum CosmicGlyphDrawUiKey : byte
|
||||
{
|
||||
Key,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CosmicGlyphDrawBuiState(List<ProtoId<GlyphPrototype>> glyphs) : BoundUserInterfaceState
|
||||
{
|
||||
public List<ProtoId<GlyphPrototype>> Glyphs = glyphs;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CosmicGlyphDrawSelectedMessage(ProtoId<GlyphPrototype> glyphProtoId) : BoundUserInterfaceMessage
|
||||
{
|
||||
public ProtoId<GlyphPrototype> GlyphProtoId = glyphProtoId;
|
||||
}
|
||||
|
|
@ -16,22 +16,16 @@ public sealed class MonumentBuiState : BoundUserInterfaceState
|
|||
{
|
||||
public int CurrentProgress;
|
||||
public int TargetProgress;
|
||||
public ProtoId<GlyphPrototype> SelectedGlyph;
|
||||
public HashSet<ProtoId<GlyphPrototype>> UnlockedGlyphs;
|
||||
|
||||
public MonumentBuiState(int currentProgress, int targetProgress, int progressOffset, ProtoId<GlyphPrototype> selectedGlyph, HashSet<ProtoId<GlyphPrototype>> unlockedGlyphs)
|
||||
public MonumentBuiState(int currentProgress, int targetProgress, int progressOffset)
|
||||
{
|
||||
CurrentProgress = currentProgress - progressOffset;
|
||||
TargetProgress = targetProgress - progressOffset;
|
||||
SelectedGlyph = selectedGlyph;
|
||||
UnlockedGlyphs = unlockedGlyphs;
|
||||
}
|
||||
|
||||
public MonumentBuiState(MonumentComponent comp)
|
||||
{
|
||||
CurrentProgress = comp.CurrentProgress - comp.ProgressOffset;
|
||||
TargetProgress = comp.TargetProgress - comp.ProgressOffset;
|
||||
SelectedGlyph = comp.SelectedGlyph;
|
||||
UnlockedGlyphs = comp.UnlockedGlyphs;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
using Content.Shared._DV.CosmicCult;
|
||||
using Content.Shared._DV.CosmicCult.Components;
|
||||
using Content.Shared._DV.CosmicCult.Prototypes;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Spawners;
|
||||
|
|
@ -21,16 +17,12 @@ public abstract class SharedMonumentSystem : EntitySystem
|
|||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedCosmicCultSystem _cosmicCult = default!;
|
||||
[Dependency] private readonly SharedCosmicGlyphSystem _glyph = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MonumentComponent, BoundUIOpenedEvent>(OnUIOpened);
|
||||
SubscribeLocalEvent<MonumentComponent, GlyphSelectedMessage>(OnGlyphSelected);
|
||||
SubscribeLocalEvent<MonumentComponent, GlyphRemovedMessage>(OnGlyphRemove);
|
||||
SubscribeLocalEvent<MonumentComponent, InfluenceSelectedMessage>(OnInfluenceSelected);
|
||||
SubscribeLocalEvent<MonumentOnDespawnComponent, TimedDespawnEvent>(OnTimedDespawn);
|
||||
SubscribeLocalEvent<MonumentCollisionComponent, PreventCollideEvent>(OnPreventCollide);
|
||||
|
|
@ -80,38 +72,6 @@ public abstract class SharedMonumentSystem : EntitySystem
|
|||
}
|
||||
|
||||
#region UI listeners
|
||||
private void OnGlyphSelected(Entity<MonumentComponent> ent, ref GlyphSelectedMessage args)
|
||||
{
|
||||
ent.Comp.SelectedGlyph = args.GlyphProtoId;
|
||||
|
||||
if (!_prototype.TryIndex(args.GlyphProtoId, out var proto))
|
||||
return;
|
||||
|
||||
var xform = Transform(ent);
|
||||
|
||||
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
|
||||
return;
|
||||
|
||||
var localTile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
var targetIndices = localTile.GridIndices + new Vector2i(0, -1);
|
||||
|
||||
if (ent.Comp.CurrentGlyph is { } curGlyph) _glyph.EraseGlyph(curGlyph);
|
||||
|
||||
var glyphEnt = Spawn(proto.Entity, _map.ToCenterCoordinates(xform.GridUid.Value, targetIndices, grid));
|
||||
ent.Comp.CurrentGlyph = glyphEnt;
|
||||
var evt = new CosmicCultAssociateRuleEvent(ent, glyphEnt);
|
||||
RaiseLocalEvent(ref evt);
|
||||
|
||||
_ui.SetUiState(ent.Owner, MonumentKey.Key, new MonumentBuiState(ent.Comp));
|
||||
}
|
||||
|
||||
private void OnGlyphRemove(Entity<MonumentComponent> ent, ref GlyphRemovedMessage args)
|
||||
{
|
||||
if (ent.Comp.CurrentGlyph is { } curGlyph) _glyph.EraseGlyph(curGlyph);
|
||||
|
||||
_ui.SetUiState(ent.Owner, MonumentKey.Key, new MonumentBuiState(ent.Comp));
|
||||
}
|
||||
|
||||
private void OnInfluenceSelected(Entity<MonumentComponent> ent, ref InfluenceSelectedMessage args)
|
||||
{
|
||||
if (!_prototype.TryIndex(args.InfluenceProtoId, out var proto) || !TryComp<ActivatableUIComponent>(ent, out var uiComp) || !TryComp<CosmicCultComponent>(args.Actor, out var cultComp))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Ghost.Roles;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVSpawnableGhostRoleRequestEvent(ProtoId<DVSpawnableGhostRolePrototype> prototype) : EntityEventArgs
|
||||
{
|
||||
public ProtoId<DVSpawnableGhostRolePrototype> Prototype = prototype;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVSpawnableGhostRoleCooldownRequestEvent : EntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DVSpawnableGhostRoleCooldownUpdateEvent(TimeSpan? cooldownEnd) : EntityEventArgs
|
||||
{
|
||||
public TimeSpan? CooldownEnd = cooldownEnd;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Ghost.Roles;
|
||||
|
||||
[Prototype("dvSpawnableGhostRole")]
|
||||
public sealed partial class DVSpawnableGhostRolePrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
public LocId Name => $"dv-spawnable-{ID}.name";
|
||||
|
||||
public LocId Description => $"dv-spawnable-{ID}.description";
|
||||
|
||||
[DataField(required: true)]
|
||||
public LocId Rules;
|
||||
|
||||
[DataField]
|
||||
public List<EntProtoId> MindRoles = new() { "MindRoleGhostRoleNeutral" };
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Entity;
|
||||
}
|
||||
|
|
@ -68,6 +68,7 @@ public sealed class ChargeHolosignSystem : EntitySystem
|
|||
|
||||
var holoUid = PredictedSpawnAtPosition(ent.Comp1.SignProto, args.ClickLocation.SnapToGrid(EntityManager));
|
||||
var xform = Transform(holoUid);
|
||||
xform.LocalRotation = Angle.Zero;
|
||||
if (!xform.Anchored)
|
||||
_transform.AnchorEntity(holoUid, xform); // anchor to prevent any tempering with (don't know what could even interact with it)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using Content.Shared.EntityTable.EntitySelectors;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.InnateTools;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(DVInnateToolsSystem))]
|
||||
public sealed partial class DVInnateToolsComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntityTableSelector Tools;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public int Provided;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.EntityTable;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Components;
|
||||
|
||||
namespace Content.Shared._DV.InnateTools;
|
||||
|
||||
public sealed class DVInnateToolsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly EntityTableSystem _entityTable = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVInnateToolsComponent, MapInitEvent>(OnInnateToolsInit);
|
||||
SubscribeLocalEvent<DVInnateToolsComponent, ComponentShutdown>(OnInnateToolsShutdown);
|
||||
}
|
||||
|
||||
private void OnInnateToolsInit(Entity<DVInnateToolsComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
var items = _entityTable.GetSpawns(ent.Comp.Tools).ToList();
|
||||
var xform = Transform(ent);
|
||||
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
var name = $"{GetNetEntity(ent)}-innate-{i}";
|
||||
_hands.AddHand(ent.Owner,
|
||||
name,
|
||||
i switch
|
||||
{
|
||||
0 => HandLocation.Right,
|
||||
_ when i < items.Count-1 => HandLocation.Middle,
|
||||
_ => HandLocation.Left,
|
||||
});
|
||||
|
||||
var spawned = PredictedSpawnAtPosition(items[i], xform.Coordinates);
|
||||
_hands.DoPickup(ent.Owner, name, spawned);
|
||||
|
||||
EnsureComp<UnremoveableComponent>(spawned);
|
||||
}
|
||||
|
||||
ent.Comp.Provided = items.Count;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnInnateToolsShutdown(Entity<DVInnateToolsComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
for (var i = 0; i < ent.Comp.Provided; i++)
|
||||
{
|
||||
var name = $"{GetNetEntity(ent)}-innate-{i}";
|
||||
if (_hands.TryGetHeldItem(ent.Owner, name, out var held))
|
||||
QueueDel(held);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ using Robust.Shared.Random;
|
|||
|
||||
namespace Content.Shared._DV.IonLaws;
|
||||
|
||||
public sealed class IonLawSystem : EntitySystem
|
||||
public sealed class DVIonLawSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ILocalizationManager _localization = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue