Merge branch 'master' into hristov-rework
This commit is contained in:
commit
93bacecb3e
|
|
@ -2,6 +2,7 @@ using Content.Client.Cargo.UI;
|
|||
using Content.Shared.Cargo.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
|
||||
|
||||
namespace Content.Client.Cargo.BUI;
|
||||
|
||||
|
|
@ -30,6 +31,18 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
SendMessage(new BountySkipMessage(id));
|
||||
};
|
||||
|
||||
// DeltaV: bounty claim stuff begins
|
||||
_menu.OnClaimButtonPressed += id =>
|
||||
{
|
||||
SendMessage(new BountyClaimedMessage(id));
|
||||
};
|
||||
|
||||
_menu.OnStatusOptionSelected += (id, status) =>
|
||||
{
|
||||
SendMessage(new BountySetStatusMessage(id, status));
|
||||
};
|
||||
// DeltaV: bounty claim stuff ends
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState message)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<RichTextLabel Name="RewardLabel"/>
|
||||
<RichTextLabel Name="ManifestLabel"/>
|
||||
<RichTextLabel Name="ClaimedBylabel"/> <!-- DeltaV bounty claim -->
|
||||
<RichTextLabel Name="StatusLabel"/> <!-- DeltaV bounty claim -->
|
||||
</BoxContainer>
|
||||
<Control MinWidth="10"/>
|
||||
<BoxContainer Orientation="Vertical" MinWidth="120">
|
||||
|
|
@ -17,14 +19,22 @@
|
|||
<Button Name="PrintButton"
|
||||
Text="{Loc 'bounty-console-label-button-text'}"
|
||||
HorizontalExpand="False"
|
||||
HorizontalAlignment="Right"
|
||||
StyleClasses="OpenRight"/>
|
||||
StyleClasses="OpenBoth"/>
|
||||
<Button Name="SkipButton"
|
||||
Text="{Loc 'bounty-console-skip-button-text'}"
|
||||
HorizontalExpand="False"
|
||||
HorizontalAlignment="Right"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenLeft"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="120"> <!-- Begin DeltaV bounty claim content -->
|
||||
<Button Name="ClaimButton"
|
||||
Text= "{Loc 'bounty-console-claim-button-text'}"
|
||||
HorizontalExpand="False"
|
||||
StyleClasses="OpenRight"/>
|
||||
<OptionButton Name="BountyStatusSelector"
|
||||
Access="Public"
|
||||
StyleClasses="OpenBoth"
|
||||
HorizontalExpand="True"/>
|
||||
</BoxContainer> <!-- End DeltaV content-->
|
||||
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ public sealed partial class BountyEntry : BoxContainer
|
|||
|
||||
public Action? OnLabelButtonPressed;
|
||||
public Action? OnSkipButtonPressed;
|
||||
public Action? OnClaimButtonPressed; // DeltaV
|
||||
public Action? OnStatusOptionSelected; // DeltaV
|
||||
|
||||
public TimeSpan EndTime;
|
||||
public TimeSpan UntilNextSkip;
|
||||
|
|
@ -45,6 +47,20 @@ public sealed partial class BountyEntry : BoxContainer
|
|||
|
||||
PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
|
||||
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
|
||||
|
||||
// Begin DeltaV bounty claiming
|
||||
ClaimButton.OnPressed += _ => OnClaimButtonPressed?.Invoke();
|
||||
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Undelivered)}"), 0);
|
||||
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Waiting)}"), 1);
|
||||
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.OnShuttle)}"), 2);
|
||||
|
||||
BountyStatusSelector.Select((int) bounty.Status);
|
||||
BountyStatusSelector.ToolTip = Loc.GetString($"bounty-console-status-tooltip-{bounty.Status.ToString()}");
|
||||
|
||||
var claimedByText = string.IsNullOrEmpty(bounty.ClaimedBy) ? Loc.GetString("bounty-console-claimed-by-none") : bounty.ClaimedBy;
|
||||
ClaimedBylabel.SetMarkup(Loc.GetString("bounty-console-claimed-by", ("claimant", claimedByText)));
|
||||
StatusLabel.SetMarkup(Loc.GetString($"bounty-console-formatted-status-{bounty.Status.ToString()}"));
|
||||
// End DeltaV bounty claiming
|
||||
}
|
||||
|
||||
private void UpdateSkipButton(float deltaSeconds)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
<!--DeltaV - SetSize was 550, now 750 to accomodate bounty claim UI -->
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'bounty-console-menu-title'}"
|
||||
SetSize="550 420"
|
||||
SetSize="750 420"
|
||||
MinSize="400 350">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public sealed partial class CargoBountyMenu : FancyWindow
|
|||
{
|
||||
public Action<string>? OnLabelButtonPressed;
|
||||
public Action<string>? OnSkipButtonPressed;
|
||||
public Action<string>? OnClaimButtonPressed; // DeltaV
|
||||
public Action<string, int>? OnStatusOptionSelected; // DeltaV
|
||||
|
||||
public CargoBountyMenu()
|
||||
{
|
||||
|
|
@ -31,6 +33,8 @@ public sealed partial class CargoBountyMenu : FancyWindow
|
|||
var entry = new BountyEntry(b, untilNextSkip);
|
||||
entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
|
||||
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
|
||||
entry.OnClaimButtonPressed += () => OnClaimButtonPressed?.Invoke(b.Id); // DeltaV
|
||||
entry.BountyStatusSelector.OnItemSelected += args => OnStatusOptionSelected?.Invoke(b.Id, args.Id); // DeltaV
|
||||
|
||||
BountyEntriesContainer.AddChild(entry);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,12 @@
|
|||
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" />
|
||||
<Label Name="TemperatureLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" />
|
||||
<Label Name="BloodLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" />
|
||||
<Label Name="DamageLabel" />
|
||||
<!-- Begin DeltaV - Health Analyzer Plus -->
|
||||
<Label Name="BloodLevelLabel" />
|
||||
<!-- Label Name="BloodLabel" / -->
|
||||
<!-- Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" / -->
|
||||
<!-- Label Name="DamageLabel" / -->
|
||||
<!-- End DeltaV - Health Analyzer Plus -->
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
|
||||
|
|
@ -73,10 +76,41 @@
|
|||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Margin="0 5 0 5"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
<!-- Begin DeltaV - Health Analyzer Plus -->
|
||||
<!-- BoxContainer Name="GroupsContainer" Margin="0 5 0 5" Orientation="Vertical" -->
|
||||
<!-- /BoxContainer -->
|
||||
|
||||
<GridContainer Margin="0 5 0 5" Columns="3" HorizontalAlignment="Center">
|
||||
<BoxContainer Name="DamageBox" Margin="5 5 5 5" Orientation="Vertical">
|
||||
<Label Name="TotalDamageLabel" HorizontalAlignment="Center" />
|
||||
<PanelContainer Name="DamageGroupsDivider" StyleClasses="LowDivider" />
|
||||
<Label
|
||||
Name="NoDamageLabel"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Loc 'health-analyzer-plus-window-no-damage-text'}"
|
||||
FontColorOverride="DeepSkyBlue"
|
||||
Margin="0 5 0 5" />
|
||||
<BoxContainer HorizontalAlignment="Center">
|
||||
<BoxContainer Name="DamageGroupsContainer" Margin="0 5 0 5" Orientation="Vertical" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer Name="BloodstreamBox" Margin="5 5 5 5" Orientation="Vertical">
|
||||
<Label Name="TotalReagentQuantityLabel" HorizontalAlignment="Center" />
|
||||
<PanelContainer Name="BloodstreamReagentsDivider" StyleClasses="LowDivider" />
|
||||
<Label
|
||||
Name="NoReagentsLabel"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Loc 'health-analyzer-plus-window-no-reagents-text'}"
|
||||
FontColorOverride="DeepSkyBlue"
|
||||
Margin="0 5 0 5" />
|
||||
<BoxContainer HorizontalAlignment="Center">
|
||||
<BoxContainer Name="BloodstreamReagentListContainer" Orientation="Vertical" Margin="0 5 0 5" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</GridContainer>
|
||||
<!-- End DeltaV - Health Analyzer Plus -->
|
||||
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -19,12 +19,25 @@ using Robust.Client.UserInterface.Controls;
|
|||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
// BEGIN DeltaV
|
||||
using Content.Client._DV.Traits.Assorted;
|
||||
using Content.Shared._DV.Traits.Assorted;
|
||||
using Content.Shared._DV.Medical; // Uncloneable
|
||||
using Content.Shared._DV.MedicalRecords; // Medical Records
|
||||
|
||||
// Health Analyzer Plus
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Client.Body.Systems;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Client.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Body.Components;
|
||||
// END DeltaV
|
||||
namespace Content.Client.HealthAnalyzer.UI;
|
||||
|
||||
using Content.Client._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared._DV.Medical; // DeltaV - Uncloneable
|
||||
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
||||
|
||||
|
||||
// Health analyzer UI is split from its window because it's used by both the
|
||||
// health analyzer item and the cryo pod UI.
|
||||
|
|
@ -32,6 +45,8 @@ using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerControl : BoxContainer
|
||||
{
|
||||
private const string REAGENT_GROUP_MEDICINE = "Medicine"; // DeltaV - Health Analyzer
|
||||
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly IPrototypeManager _prototypes;
|
||||
|
|
@ -41,6 +56,8 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
private readonly UnborgableSystem _unborgable; // DeltaV
|
||||
private readonly RedshirtSystem _redshirt; // DeltaV
|
||||
private readonly UncloneableSystem _uncloneable; // DeltaV
|
||||
private readonly SharedBloodstreamSystem _bloodstream; // DeltaV
|
||||
private readonly SharedSolutionContainerSystem _solutionContainer; // DeltaV
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
private readonly ButtonGroup _triageStatusGroup = new();
|
||||
|
|
@ -66,6 +83,8 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
_unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV
|
||||
_redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV
|
||||
_uncloneable = _entityManager.System<UncloneableSystem>(); // DeltaV
|
||||
_bloodstream = _entityManager.System<BloodstreamSystem>(); // DeltaV
|
||||
_solutionContainer = _entityManager.System<SolutionContainerSystem>(); // DeltaV
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
foreach (var item in Enum.GetValues<TriageStatus>())
|
||||
|
|
@ -138,9 +157,13 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
? $"{state.Temperature - Atmospherics.T0C:F1} °C ({state.Temperature:F1} K)"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
// Begin DeltaV - Health Analyzer Plus
|
||||
/*
|
||||
BloodLabel.Text = !float.IsNaN(state.BloodLevel)
|
||||
? $"{state.BloodLevel * 100:F1} %"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
*/
|
||||
// End DeltaV - Health Analyzer Plus
|
||||
|
||||
StatusLabel.Text =
|
||||
_entityManager.TryGetComponent<MobStateComponent>(target.Value, out var mobStateComponent)
|
||||
|
|
@ -149,7 +172,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
|
||||
// Total Damage
|
||||
|
||||
DamageLabel.Text = _damageable.GetTotalDamage(target.Value).ToString();
|
||||
// DamageLabel.Text = _damageable.GetTotalDamage(target.Value).ToString(); // DeltaV - Health Analyzer Plus
|
||||
|
||||
// Alerts
|
||||
// DeltaV traits - This is going to be horrid if we just keep adding things like this.
|
||||
|
|
@ -216,6 +239,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
var damagePerType = _damageable.GetAllDamage(target.Value).DamageDict;
|
||||
|
||||
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
|
||||
DrawBloodstreamInfo(target.Value, state.BloodSolution, state.BloodLevel); // End DeltaV - Health Analyzer Plus
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
if (state.MedicalRecord is not { } records)
|
||||
|
|
@ -254,7 +278,15 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> groups,
|
||||
IReadOnlyDictionary<ProtoId<DamageTypePrototype>, FixedPoint2> damageDict)
|
||||
{
|
||||
GroupsContainer.RemoveAllChildren();
|
||||
// Begin DeltaV - Health Analyzer Plus
|
||||
// GroupsContainer.RemoveAllChildren();
|
||||
DamageGroupsContainer.RemoveAllChildren();
|
||||
|
||||
// Begin DeltaV - Health Analyzer Plus
|
||||
var totalDamage = damageDict.Sum(x => x.Value.Double());
|
||||
TotalDamageLabel.Text = Loc.GetString("health-analyzer-plus-window-entity-total-damage-text", ("amount", Math.Round(totalDamage, 2)));
|
||||
NoDamageLabel.Visible = totalDamage == 0;
|
||||
// End DeltaV - Health Analyzer Plus
|
||||
|
||||
foreach (var (damageGroupId, damageAmount) in groups)
|
||||
{
|
||||
|
|
@ -275,7 +307,8 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
|
||||
|
||||
GroupsContainer.AddChild(groupContainer);
|
||||
// GroupsContainer.AddChild(groupContainer); // DeltaV - Health Analyzer Plus
|
||||
DamageGroupsContainer.AddChild(groupContainer); // DeltaV - Health Analyzer Plus
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
var group = _prototypes.Index(damageGroupId);
|
||||
|
|
@ -296,6 +329,116 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
|
|||
}
|
||||
}
|
||||
|
||||
// Begin DeltaV - Health Analyzer Plus
|
||||
private void DrawBloodstreamInfo(EntityUid target, Solution? bloodSolution, float bloodlevel)
|
||||
{
|
||||
BloodstreamReagentListContainer.RemoveAllChildren();
|
||||
|
||||
List<ReagentQuantity> bloodstream = new();
|
||||
double totalReagentQuantity = 0;
|
||||
double unknownReagents = 0;
|
||||
// Make sure we can access the bloodtype and reagents before trying to use them
|
||||
if (bloodSolution is not null)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent<BloodstreamComponent>(target, out var bloodstreamComp) ||
|
||||
!_solutionContainer.ResolveSolution(target, bloodstreamComp.BloodSolutionName, ref bloodstreamComp.BloodSolution, out var bloodReagentSolution))
|
||||
return;
|
||||
|
||||
// Get the blood reagent IDs to ignore in the scan
|
||||
var bloodReagentIds = bloodstreamComp.BloodReferenceSolution.Select(b => b.Reagent);
|
||||
|
||||
// Get all of the medicines in the bloodstream
|
||||
foreach (var reagent in bloodSolution!.Contents)
|
||||
{
|
||||
if (bloodReagentIds.Contains(reagent.Reagent))
|
||||
continue;
|
||||
|
||||
if (_prototypes.Index<ReagentPrototype>(reagent.Reagent.Prototype).Group == REAGENT_GROUP_MEDICINE)
|
||||
{
|
||||
bloodstream.Add(reagent);
|
||||
totalReagentQuantity += reagent.Quantity.Double();
|
||||
}
|
||||
else
|
||||
{
|
||||
unknownReagents += reagent.Quantity.Double();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bloodQuantityPercent = !float.IsNaN(bloodlevel)
|
||||
? $"{bloodlevel * 100:F1} %"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
// Display the percent and amount of blood in the system
|
||||
// Normal health scanner is percent only, this one adds the actual units too
|
||||
BloodLevelLabel.Text = Loc.GetString(
|
||||
"health-analyzer-plus-window-entity-blood-level-quantity-text",
|
||||
("percent", bloodQuantityPercent)
|
||||
);
|
||||
|
||||
// Display the total amount of reagents in the bloodstream, 0 is valid
|
||||
TotalReagentQuantityLabel.Text = Loc.GetString(
|
||||
"health-analyzer-plus-window-entity-total-reagents-text",
|
||||
("amount", Math.Round(totalReagentQuantity, 1).ToString("0.0"))
|
||||
);
|
||||
|
||||
// If the bloodstream was empty, display a message instead to fill the space with something
|
||||
NoReagentsLabel.Visible = bloodstream.Count == 0 && unknownReagents == 0;
|
||||
|
||||
// For each of the reagents in the bloodstream, add them to the list of reagents
|
||||
// If there are none, this loop will simply do nothing
|
||||
foreach (var (reagentID, reagentQuantity) in bloodstream)
|
||||
{
|
||||
var reagentPrototype = _prototypes.Index<ReagentPrototype>(reagentID.Prototype);
|
||||
|
||||
// Make a new container for each reagent we add to the list
|
||||
var reagentContainer = new BoxContainer
|
||||
{
|
||||
Align = AlignMode.Begin,
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
|
||||
// Add the string "[] <reagent_name>: <reagent_amount>u" for the current reagent
|
||||
// The [] is a unicode block character, and is colored with the reagent color
|
||||
// to make it easier to identify
|
||||
reagentContainer.AddChild(
|
||||
new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-plus-window-reagent-text",
|
||||
("reagentColor", reagentPrototype.SubstanceColor),
|
||||
("reagentName", reagentPrototype.LocalizedName),
|
||||
("amount", Math.Round(reagentQuantity.Double(), 1).ToString("0.0"))
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
BloodstreamReagentListContainer.AddChild(reagentContainer);
|
||||
}
|
||||
|
||||
// Add unknown reagents (if any)
|
||||
if (unknownReagents > 0)
|
||||
{
|
||||
var unknownReagentContainer = new BoxContainer
|
||||
{
|
||||
Align = AlignMode.Begin,
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
|
||||
unknownReagentContainer.AddChild(
|
||||
new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-plus-window-unknown-reagents-text",
|
||||
("amount", Math.Round(unknownReagents, 1).ToString("0.0"))
|
||||
)
|
||||
}
|
||||
);
|
||||
BloodstreamReagentListContainer.AddChild(unknownReagentContainer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// End DeltaV - Health Analyzer Plus
|
||||
|
||||
private Texture GetTexture(string texture)
|
||||
{
|
||||
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
<ScrollContainer
|
||||
Margin="5 5 5 5"
|
||||
ReturnMeasure="True"
|
||||
VerticalExpand="True">
|
||||
VerticalExpand="True"
|
||||
VScrollBarHidden="True"
|
||||
HScrollBarHidden="True"> <!-- DeltaV - Hide the scroll bars -->
|
||||
|
||||
<ui:HealthAnalyzerControl
|
||||
Name="HealthAnalyzer"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ namespace Content.Client.Input
|
|||
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
|
||||
common.AddFunction(ContentKeyFunctions.SaveItemLocation);
|
||||
common.AddFunction(ContentKeyFunctions.Point);
|
||||
// Floofstation section
|
||||
common.AddFunction(ContentKeyFunctions.OfferItem);
|
||||
// Floofstation section end
|
||||
common.AddFunction(ContentKeyFunctions.ZoomOut);
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
common.AddFunction(ContentKeyFunctions.ResetZoom);
|
||||
|
|
|
|||
|
|
@ -75,9 +75,11 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||
if (!LocalizedNames.TryGetValue(netEntity, out var name))
|
||||
name = Loc.GetString("navmap-unknown-entity");
|
||||
|
||||
var pos = _xform.ToMapCoordinates(blip.Coordinates); // DeltaV - map-coordinates
|
||||
|
||||
var message = name + "\n" + Loc.GetString("navmap-location",
|
||||
("x", MathF.Round(blip.Coordinates.X)),
|
||||
("y", MathF.Round(blip.Coordinates.Y)));
|
||||
("x", MathF.Round(pos.X)), // DeltaV - map-coordinates
|
||||
("y", MathF.Round(pos.Y))); // DeltaV - map-coordinates
|
||||
|
||||
_trackedEntityLabel.Text = message;
|
||||
_trackedEntityPanel.Visible = true;
|
||||
|
|
|
|||
|
|
@ -88,9 +88,9 @@ public sealed partial class CryoPodWindow : FancyWindow
|
|||
// Health analyzer
|
||||
var maybePatient = _entityManager.GetEntity(msg.Health.TargetEntity);
|
||||
var hasPatient = msg.Health.TargetEntity.HasValue;
|
||||
var hasDamage = hasPatient && msg.HasDamage;
|
||||
// var hasDamage = hasPatient && msg.HasDamage; // DeltaV - Health Analyzer Plus
|
||||
|
||||
NoDamageText.Visible = (hasPatient && !hasDamage);
|
||||
NoDamageText.Visible = false; //(hasPatient && !hasDamage); // DeltaV - Health Analyzer Plus
|
||||
HealthSection.Visible = hasPatient;
|
||||
EjectPatientButton.Disabled = !hasPatient;
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
AddButton(ContentKeyFunctions.MovePulledObject);
|
||||
AddButton(ContentKeyFunctions.ReleasePulledObject);
|
||||
AddButton(ContentKeyFunctions.Point);
|
||||
AddButton(ContentKeyFunctions.OfferItem); // Floofstation
|
||||
AddButton(ContentKeyFunctions.RotateObjectClockwise);
|
||||
AddButton(ContentKeyFunctions.RotateObjectCounterclockwise);
|
||||
AddButton(ContentKeyFunctions.FlipObject);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
<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="600 480">
|
||||
SetSize="550 300">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<!-- Left pane: selection of borg type -->
|
||||
|
|
@ -30,9 +29,6 @@
|
|||
<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,7 +1,6 @@
|
|||
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;
|
||||
|
|
@ -30,7 +29,6 @@ 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" };
|
||||
|
||||
|
|
@ -58,13 +56,6 @@ 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)
|
||||
|
|
@ -96,14 +87,6 @@ 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)
|
||||
|
|
@ -111,7 +94,6 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
|||
if (_selectedBorgType == null)
|
||||
return;
|
||||
|
||||
ConfirmedBorgSubtype?.Invoke(ChassisSpriteSelection.SubtypePrototype); // CosmicDrift
|
||||
ConfirmedBorgType?.Invoke(_selectedBorgType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using Content.Shared._CD.Silicons; // CosmicDrift
|
||||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
|
|
@ -28,6 +26,5 @@ 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,12 +1,9 @@
|
|||
using Content.Client.PDA; // DeltaV
|
||||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift
|
||||
using Content.Client.PDA; // DeltaV
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement; // CosmicDrift
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations; // CosmicDrift
|
||||
using Robust.Shared.Timing; // CosmicDrift
|
||||
using Robust.Shared.Serialization; // DeltaV
|
||||
|
||||
namespace Content.Client.Silicons.Borgs;
|
||||
|
||||
|
|
@ -20,7 +17,6 @@ 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()
|
||||
{
|
||||
|
|
@ -44,20 +40,15 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
|
|||
Entity<BorgSwitchableTypeComponent> entity,
|
||||
BorgTypePrototype prototype)
|
||||
{
|
||||
// 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
|
||||
// Begin DeltaV Additions
|
||||
if (prototype.ClientComponents is {} add)
|
||||
EntityManager.AddComponents(entity, add);
|
||||
// End DeltaV Additions
|
||||
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);
|
||||
}
|
||||
|
|
@ -85,26 +76,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
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,73 @@
|
|||
using System.Linq;
|
||||
using Content.Shared._DV.Forensics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._DV.Forensics;
|
||||
|
||||
public sealed class DVItemSlotVisualsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
private static readonly ResPath TextureRoot = new("/Textures");
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||
SubscribeLocalEvent<DVItemSlotVisualsComponent, EntRemovedFromContainerMessage>(OnRemovedFromContainer);
|
||||
}
|
||||
|
||||
private void RefreshVisuals(Entity<DVItemSlotVisualsComponent> ent)
|
||||
{
|
||||
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
|
||||
if (slot.ContainedEntity is { } contained)
|
||||
{
|
||||
_sprite.CopySprite(contained, ent.Owner);
|
||||
_sprite.AddLayer(ent.Owner, ent.Comp.FilledSprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sprite = Comp<SpriteComponent>(ent);
|
||||
var count = sprite.AllLayers.Count();
|
||||
for (var i = count - 1; i >= 0; i--)
|
||||
{
|
||||
_sprite.RemoveLayer((ent, sprite), i);
|
||||
}
|
||||
|
||||
if (ent.Comp.UnfilledSprite is not SpriteSpecifier.Rsi rsi)
|
||||
{
|
||||
throw new InvalidOperationException($"{ToPrettyString(ent)} has an unfilled sprite that's not an RSI");
|
||||
}
|
||||
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(TextureRoot / rsi.RsiPath, out var res))
|
||||
{
|
||||
throw new InvalidOperationException($"{ToPrettyString(ent)} has invalid RSI: {TextureRoot / rsi.RsiPath}");
|
||||
}
|
||||
|
||||
_sprite.SetBaseRsi(ent.Owner, res.RSI);
|
||||
_sprite.AddLayer(ent.Owner, ent.Comp.UnfilledSprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInsertedIntoContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != ent.Comp.ItemSlot)
|
||||
return;
|
||||
|
||||
RefreshVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnRemovedFromContainer(Entity<DVItemSlotVisualsComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != ent.Comp.ItemSlot)
|
||||
return;
|
||||
|
||||
RefreshVisuals(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._Floof.OfferItem;
|
||||
|
||||
public sealed class OfferItemIndicatorsOverlay : Overlay
|
||||
{
|
||||
private readonly IInputManager _inputManager;
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly IEyeManager _eye;
|
||||
private readonly OfferItemSystem _offer;
|
||||
|
||||
private readonly Texture _sight;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
private readonly Color _mainColor = Color.White.WithAlpha(0.3f);
|
||||
private readonly Color _strokeColor = Color.Black.WithAlpha(0.5f);
|
||||
private readonly float _scale = 0.6f; // 1 is a little big
|
||||
|
||||
public OfferItemIndicatorsOverlay(IInputManager input, IEntityManager entMan,
|
||||
IEyeManager eye, OfferItemSystem offerSys)
|
||||
{
|
||||
_inputManager = input;
|
||||
_entMan = entMan;
|
||||
_eye = eye;
|
||||
_offer = offerSys;
|
||||
|
||||
var spriteSys = _entMan.EntitySysManager.GetEntitySystem<SpriteSystem>();
|
||||
_sight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new("/Textures/_Floof/Interface/Misc/give_item.rsi"), "give_item"));
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_offer.IsInOfferMode())
|
||||
return false;
|
||||
|
||||
return base.BeforeDraw(in args);
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var mouseScreenPosition = _inputManager.MouseScreenPosition;
|
||||
var mousePosMap = _eye.PixelToMap(mouseScreenPosition);
|
||||
if (mousePosMap.MapId != args.MapId)
|
||||
return;
|
||||
|
||||
|
||||
var mousePos = mouseScreenPosition.Position;
|
||||
var uiScale = (args.ViewportControl as Control)?.UIScale ?? 1f;
|
||||
var limitedScale = uiScale > 1.25f ? 1.25f : uiScale;
|
||||
|
||||
DrawSight(_sight, args.ScreenHandle, mousePos, limitedScale * _scale);
|
||||
}
|
||||
|
||||
private void DrawSight(Texture sight, DrawingHandleScreen screen, Vector2 centerPos, float scale)
|
||||
{
|
||||
var sightSize = sight.Size * scale;
|
||||
var expandedSize = sightSize + new Vector2(7f, 7f);
|
||||
|
||||
screen.DrawTextureRect(sight,
|
||||
UIBox2.FromDimensions(centerPos - sightSize * 0.5f, sightSize), _strokeColor);
|
||||
screen.DrawTextureRect(sight,
|
||||
UIBox2.FromDimensions(centerPos - expandedSize * 0.5f, expandedSize), _mainColor);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Content.Shared._Floof.OfferItem;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
|
||||
namespace Content.Client._Floof.OfferItem;
|
||||
|
||||
public sealed class OfferItemSystem : SharedOfferItemSystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_overlayManager.AddOverlay(new OfferItemIndicatorsOverlay(
|
||||
_inputManager,
|
||||
EntityManager,
|
||||
_eye,
|
||||
this));
|
||||
}
|
||||
public override void Shutdown()
|
||||
{
|
||||
_overlayManager.RemoveOverlay<OfferItemIndicatorsOverlay>();
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
public bool IsInOfferMode()
|
||||
{
|
||||
var entity = _playerManager.LocalEntity;
|
||||
|
||||
if (entity == null)
|
||||
return false;
|
||||
|
||||
return IsInOfferMode(entity.Value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Access.Systems; // DeltaV
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.NameIdentifier;
|
||||
using Content.Shared.Access.Components;
|
||||
|
|
@ -20,6 +21,7 @@ using Robust.Shared.Prototypes;
|
|||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
|
||||
|
||||
namespace Content.Server.Cargo.Systems;
|
||||
|
||||
|
|
@ -28,6 +30,7 @@ public sealed partial class CargoSystem
|
|||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!;
|
||||
[Dependency] private readonly IdCardSystem _idCard = default!; // DeltaV
|
||||
|
||||
private static readonly ProtoId<NameIdentifierGroupPrototype> BountyNameIdentifierGroup = "Bounty";
|
||||
|
||||
|
|
@ -40,6 +43,8 @@ public sealed partial class CargoSystem
|
|||
SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened);
|
||||
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage);
|
||||
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySkipMessage>(OnSkipBountyMessage);
|
||||
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyClaimedMessage>(OnBountyClaimedMessage); // DeltaV
|
||||
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySetStatusMessage>(OnSetBountyStatusMessage); // DeltaV
|
||||
SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice);
|
||||
SubscribeLocalEvent<EntitySoldEvent>(OnSold);
|
||||
SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit);
|
||||
|
|
@ -111,6 +116,72 @@ public sealed partial class CargoSystem
|
|||
_audio.PlayPvs(component.SkipSound, uid);
|
||||
}
|
||||
|
||||
// Begin DeltaV bounty claim system
|
||||
private void OnSetBountyStatusMessage(Entity<CargoBountyConsoleComponent> ent, ref BountySetStatusMessage args)
|
||||
{
|
||||
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
|
||||
{
|
||||
var bounty = bountyDbComp.Bounties[i];
|
||||
|
||||
if (bounty.Id != args.BountyId)
|
||||
continue;
|
||||
|
||||
var newData = new CargoBountyData
|
||||
{
|
||||
Id = bounty.Id,
|
||||
Bounty = bounty.Bounty,
|
||||
ClaimedBy = bounty.ClaimedBy,
|
||||
Status = (CargoBountyStatus)args.Status,
|
||||
};
|
||||
|
||||
bountyDbComp.Bounties[i] = newData;
|
||||
}
|
||||
|
||||
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
|
||||
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
|
||||
}
|
||||
|
||||
private void OnBountyClaimedMessage(Entity<CargoBountyConsoleComponent> ent, ref BountyClaimedMessage args)
|
||||
{
|
||||
if (_station.GetOwningStation(ent.Owner) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDbComp))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < bountyDbComp.Bounties.Count; i++)
|
||||
{
|
||||
var bounty = bountyDbComp.Bounties[i];
|
||||
|
||||
if (bounty.Id != args.BountyId)
|
||||
continue;
|
||||
|
||||
string name;
|
||||
if (_idCard.TryFindIdCard(args.Actor, out var idCard) && idCard.Comp.FullName != null)
|
||||
{
|
||||
name = idCard.Comp.FullName;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = Loc.GetString("bounty-console-claimed-by-unknown");
|
||||
}
|
||||
|
||||
var newData = new CargoBountyData
|
||||
{
|
||||
Id = bounty.Id,
|
||||
Bounty = bounty.Bounty,
|
||||
ClaimedBy = name.Equals(bounty.ClaimedBy) ? string.Empty : name,
|
||||
Status = bounty.Status,
|
||||
};
|
||||
|
||||
bountyDbComp.Bounties[i] = newData;
|
||||
}
|
||||
|
||||
var untilNextSkip = bountyDbComp.NextSkipTime - Timing.CurTime;
|
||||
_uiSystem.SetUiState(ent.Owner, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDbComp.Bounties, bountyDbComp.History, untilNextSkip));
|
||||
}
|
||||
// End DeltaV bounty claim system
|
||||
|
||||
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
|
||||
{
|
||||
if (!Resolve(uid, ref paper, ref label) || !_protoMan.Resolve<CargoBountyPrototype>(bounty.Bounty, out var prototype))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ using Robust.Shared.Timing;
|
|||
using System.Linq;
|
||||
using Content.Shared.EntityEffects.Effects.Solution;
|
||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
||||
using Content.Server.Body.Components; // Delta V
|
||||
|
||||
namespace Content.Server.Fluids.EntitySystems;
|
||||
|
||||
|
|
@ -267,7 +268,8 @@ public sealed class SmokeSystem : EntitySystem
|
|||
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0)
|
||||
return;
|
||||
|
||||
var blockIngestion = _internals.AreInternalsWorking(entity);
|
||||
var blockIngestion = _internals.AreInternalsWorking(entity)
|
||||
|| !HasComp<RespiratorComponent>(entity); // Starlight - Shadekin does not breathe and "AreInternalsWorking" does not check for that
|
||||
|
||||
var cloneSolution = solution.Clone();
|
||||
var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ using Robust.Shared.Player;
|
|||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.Mobs; // Starlight
|
||||
|
||||
namespace Content.Server.Ghost
|
||||
{
|
||||
|
|
@ -619,7 +620,15 @@ namespace Content.Server.Ghost
|
|||
_damageable.GetTotalDamage((playerEntity.Value, damageable));
|
||||
}
|
||||
|
||||
DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
|
||||
// Starlight - Start
|
||||
//DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
|
||||
|
||||
var damageType = _prototypeManager.Index(AsphyxiationDamageType);
|
||||
if (TryComp<DeathgaspComponent>(playerEntity, out var deathgasp))
|
||||
damageType = _prototypeManager.Index(deathgasp.DamageType);
|
||||
|
||||
DamageSpecifier damage = new(damageType, dealtDamage);
|
||||
// Starlight - End
|
||||
|
||||
_damageable.ChangeDamage(playerEntity.Value, damage, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ using Content.Server.Body.Systems;
|
|||
// Begin DeltaV
|
||||
using Content.Server._DV.MedicalRecords;
|
||||
using Content.Shared._DV.MedicalRecords;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
// End DeltaV
|
||||
|
||||
namespace Content.Server.Medical;
|
||||
|
|
@ -269,9 +270,11 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
|
|||
var bleeding = false;
|
||||
var unrevivable = false;
|
||||
|
||||
Solution? bloodSolution = null; // DeltaV - Health Analyzer Plus
|
||||
|
||||
if (TryComp<BloodstreamComponent>(entity, out var bloodstream) &&
|
||||
_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName,
|
||||
ref bloodstream.BloodSolution, out var bloodSolution))
|
||||
ref bloodstream.BloodSolution, out bloodSolution)) // DeltaV - Health Analyzer Plus
|
||||
{
|
||||
bloodAmount = _bloodstreamSystem.GetBloodLevel(entity);
|
||||
bleeding = bloodstream.BleedAmount > 0;
|
||||
|
|
@ -287,6 +290,7 @@ public sealed partial class HealthAnalyzerSystem : EntitySystem // DeltaV - Made
|
|||
null,
|
||||
bleeding,
|
||||
unrevivable,
|
||||
bloodSolution, // DeltaV - Health Analyzer Plus
|
||||
_medicalRecords.GetMedicalRecords(entity) // DeltaV - Medical Records
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Chat.Prototypes; // Starlight
|
||||
using Content.Shared.Damage.Prototypes; // Starlight
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Mobs;
|
||||
|
|
@ -21,4 +23,10 @@ public sealed partial class DeathgaspComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public bool NeedsCritical = true;
|
||||
|
||||
/// <summary>
|
||||
/// Starlight - The damage that is taken when succumbing
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<DamageTypePrototype> DamageType = "Asphyxiation";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,9 +55,13 @@ public sealed class PseudoItemSystem : SharedPseudoItemSystem
|
|||
|
||||
protected override void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args)
|
||||
{
|
||||
// Floof - changed this a bit to actually start a do-after
|
||||
// Try to pick the entity up instead first
|
||||
if (args.User != args.Item && _carrying.TryCarry(args.User, uid))
|
||||
if (args.User != args.Item
|
||||
&& TryComp<CarriableComponent>(uid, out var carriable)
|
||||
&& _carrying.CanCarry(args.User, (uid, carriable)))
|
||||
{
|
||||
_carrying.StartCarryDoAfter(args.User, (uid, carriable));
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,20 +209,24 @@ namespace Content.Server.Psionics.Glimmer
|
|||
|
||||
private void OnDestroyed(EntityUid uid, SharedGlimmerReactiveComponent component, DestructionEventArgs args)
|
||||
{
|
||||
Spawn("MaterialBluespace1", Transform(uid).Coordinates);
|
||||
|
||||
var proberCoords = Transform(uid).Coordinates;
|
||||
var tier = _glimmerSystem.GetGlimmerTier();
|
||||
if (tier < GlimmerTier.High)
|
||||
return;
|
||||
|
||||
var totalIntensity = (float) (_glimmerSystem.Glimmer * 2);
|
||||
var slope = (float) (11 - _glimmerSystem.Glimmer / 100);
|
||||
var maxIntensity = 20;
|
||||
var explosionMultiplier = 2;
|
||||
if (_glimmerSystem.GetGlimmerTier() == GlimmerTier.Critical) // YOU DONE FUCKED UP
|
||||
explosionMultiplier = 3;
|
||||
|
||||
var removed = (float) _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
|
||||
_glimmerSystem.Glimmer -= (int) removed;
|
||||
var totalIntensity = (float)(_glimmerSystem.Glimmer * explosionMultiplier);
|
||||
var slope = (float)(11 - _glimmerSystem.Glimmer / 100);
|
||||
var maxIntensity = 75; // Same as syndicate bomb
|
||||
|
||||
var removed = _glimmerSystem.Glimmer * _random.NextFloat(0.06f, 0.08f);
|
||||
_glimmerSystem.Glimmer -= (int)removed;
|
||||
BeamRandomNearProber(uid, _glimmerSystem.Glimmer / 350, _glimmerSystem.Glimmer / 50);
|
||||
_explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity);
|
||||
_explosionSystem.QueueExplosion(uid, "Default", totalIntensity, slope, maxIntensity, addLog: true);
|
||||
Spawn("MaterialBluespace1", proberCoords); // Congrats on your bluespace!
|
||||
}
|
||||
|
||||
private void OnUnanchorAttempt(EntityUid uid, SharedGlimmerReactiveComponent component, UnanchorAttemptEvent args)
|
||||
|
|
@ -230,7 +234,7 @@ namespace Content.Server.Psionics.Glimmer
|
|||
if (component.Locked)
|
||||
{
|
||||
_sharedAudioSystem.PlayPvs(component.ShockNoises, args.User);
|
||||
_electrocutionSystem.TryDoElectrocution(args.User, null, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false);
|
||||
_electrocutionSystem.TryDoElectrocution(args.User, uid, _glimmerSystem.Glimmer / 200, TimeSpan.FromSeconds((float) _glimmerSystem.Glimmer / 100), false);
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ public sealed class OracleSystem : EntitySystem
|
|||
|
||||
while (i != 0)
|
||||
{
|
||||
Spawn("MaterialBluespace1", Transform(user).Coordinates);
|
||||
var entityToSpawn = _random.Next(0, 2) == 0 ? "MaterialBluespace1" : "CrystalNormality";
|
||||
Spawn(entityToSpawn, Transform(user).Coordinates);
|
||||
i--;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,12 +125,12 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||
RaiseLocalEvent(projectile, ref pierceEv);
|
||||
|
||||
// If the object won't be destroyed, it "tanks" the penetration hit.
|
||||
if (damage.GetTotal() < damageRequired)
|
||||
if (damage.GetTotal() < damageRequired && !pierceEv.Pierced) // DeltaV - Addition of the NT-3
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!projectile.Comp.ProjectileSpent)
|
||||
if (!projectile.Comp.ProjectileSpent && !pierceEv.Pierced) // DeltaV - Addition of the NT-3
|
||||
{
|
||||
projectile.Comp.PenetrationAmount += damageRequired;
|
||||
// The projectile has dealt enough damage to be spent.
|
||||
|
|
@ -139,7 +139,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||
return false;
|
||||
}
|
||||
|
||||
if (projectile.Comp.ProjectileSpent && pierceEv.Pierced) // DeltaV - Addition of the NT-3
|
||||
if (projectile.Comp.ProjectileSpent)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Projectiles.Components;
|
||||
|
|
@ -21,10 +22,14 @@ public sealed partial class PiercingProjectileComponent : Component
|
|||
public float PierceCounter;
|
||||
|
||||
/// <summary>
|
||||
/// The tag that will cause the piercing bullet to increment it's <see cref="PierceCounter"/>.
|
||||
/// The whitelist for checking what increments the <see cref="PierceCounter"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// If this has the tag "Wall" in it, any entity with the tag "Wall" will increment <see cref="PierceCounter"/>
|
||||
/// upon being hit.
|
||||
/// </example>
|
||||
[DataField]
|
||||
public List<ProtoId<TagPrototype>> PierceBlockTag = ["Wall", "Window"];
|
||||
public EntityWhitelist PierceCounterWhitelist;
|
||||
|
||||
/// <summary>
|
||||
/// The number of entities it is allowed to pierce before being deleted.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using Content.Shared.FixedPoint;
|
|||
namespace Content.Server._DV.Projectiles.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a piercing projectile hits an entity that doesn't follow upstream piercing rules.
|
||||
/// Raised when a piercing projectile that doesn't follow upstream piercing rules hits an entity.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ProjectilePierceEvent(EntityUid Target, FixedPoint2 RequiredDamage, bool Pierced = false);
|
||||
|
|
|
|||
|
|
@ -1,29 +1,33 @@
|
|||
using Content.Server._DV.Projectiles.Components;
|
||||
using Content.Server._DV.Projectiles.Events;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server._DV.Projectiles.Systems;
|
||||
|
||||
public sealed class PiercingProjectileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
// Mobs return a required Damage amount of Float.MaxValue. Therefore, we need to check for absurdly high values.
|
||||
private readonly int _indestructibleNumber = 20000000;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PiercingProjectileComponent, ProjectilePierceEvent>(OnPierce);
|
||||
}
|
||||
|
||||
private void OnPierce(Entity<PiercingProjectileComponent> bullet, ref ProjectilePierceEvent args)
|
||||
{
|
||||
// If the target doesn't have any tags to stop the bullet from piercing, it's automatically true.
|
||||
if (!_tagSystem.HasAnyTag(args.Target, bullet.Comp.PierceBlockTag))
|
||||
if (_whitelist.IsWhitelistFail(bullet.Comp.PierceCounterWhitelist, args.Target))
|
||||
{
|
||||
args.Pierced = true;
|
||||
return;
|
||||
}
|
||||
// If it does have the tag to stop it and enough health to count as "strongly armored", it'll block the bullet.
|
||||
if (bullet.Comp.HealthThreshold < args.RequiredDamage)
|
||||
if (bullet.Comp.HealthThreshold < args.RequiredDamage && args.RequiredDamage < _indestructibleNumber)
|
||||
return;
|
||||
|
||||
if (bullet.Comp.Direction == null) // Get the direction of the bullet to determine which walls count.
|
||||
|
|
|
|||
|
|
@ -109,8 +109,10 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
|
|||
var report = new FormattedMessage();
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-title"));
|
||||
report.PushNewline();
|
||||
report.PushNewline();
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-first-line"));
|
||||
report.PushNewline();
|
||||
report.PushNewline();
|
||||
|
||||
if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid))
|
||||
{
|
||||
|
|
@ -121,22 +123,28 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
|
|||
var species = PrototypeManager.Index(humanoid.Species);
|
||||
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-morphotype", ("species", Loc.GetString(species.Name))));
|
||||
report.PushNewline();
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-age", ("age", humanoid.Age)));
|
||||
report.PushNewline();
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-sex", ("sex", humanoid.Sex)));
|
||||
report.PushNewline();
|
||||
|
||||
if (TryComp<PhysicsComponent>(uid, out var physics))
|
||||
{
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-weight", ("weight", Math.Round(physics.FixturesMass))));
|
||||
|
||||
report.PushNewline();
|
||||
}
|
||||
// add a random identifying quality that officers can use to track them down
|
||||
report.AddMarkupOrThrow(RobustRandom.Next(0, 2) switch
|
||||
{
|
||||
0 => Loc.GetString("fugitive-report-detail-dna", ("dna", GetDNA(uid))),
|
||||
_ => Loc.GetString("fugitive-report-detail-prints", ("prints", GetPrints(uid)))
|
||||
});
|
||||
report.PushNewline();
|
||||
|
||||
report.PushNewline();
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crimes-header"));
|
||||
|
||||
report.PushNewline();
|
||||
// generate some random crimes to avoid this situation
|
||||
// "officer what are my charges?"
|
||||
// "uh i dunno a piece of paper said to arrest you thats it"
|
||||
|
|
@ -172,6 +180,7 @@ public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
|
|||
{
|
||||
var count = RobustRandom.Next(rule.MinCounts, rule.MaxCounts + 1);
|
||||
report.AddMarkupOrThrow(Loc.GetString("fugitive-report-crime", ("crime", Loc.GetString(crime)), ("count", count)));
|
||||
report.PushNewline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Errors;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._DV.Toolshed;
|
||||
|
||||
/// <summary>
|
||||
/// An extended version of the engine's <c>do</c> command. Runs a console command once per piped value,
|
||||
/// substituting <c>$NAME</c> tokens in the command string first.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On top of the built-in tokens that <c>do</c> supports (<c>$ID</c>, <c>$PID</c>, <c>$WX</c>, <c>$WY</c>,
|
||||
/// <c>$LX</c>, <c>$LY</c>, <c>$SELF</c>), any other <c>$name</c> is resolved as a toolshed variable and
|
||||
/// converted to a string. Tokens that don't resolve to anything are left untouched.
|
||||
///
|
||||
/// Where the command actually runs is explicit:
|
||||
/// <list type="bullet">
|
||||
/// <item><c>doext:client</c> sends it back down to the calling user's own client to run locally. This is what
|
||||
/// you want for client-side commands like <c>exec</c>, which resolve paths against the client's user data.</item>
|
||||
/// <item><c>doext:server</c> runs it server-side as the calling session, like <c>do</c> does.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example><code>
|
||||
/// self doext:client "exec /Script_$var.txt"
|
||||
/// i 5 => $count; self doext:server "somecommand $count"
|
||||
/// </code></example>
|
||||
[ToolshedCommand(Name = "doext"), AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class DoExtCommand : ToolshedCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Matches a <c>$name</c> token. Names use the same character set as toolshed variable names, so this
|
||||
/// matches whole identifiers - <c>$IDLE</c> resolves as "IDLE" rather than being mangled into <c>$ID</c>.
|
||||
/// </summary>
|
||||
private static readonly Regex TokenRegex = new(@"\$(\w+)", RegexOptions.Compiled);
|
||||
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private SharedTransformSystem? _xform;
|
||||
|
||||
/// <summary>
|
||||
/// Sends the command back down to the calling user's client, which runs it locally.
|
||||
/// </summary>
|
||||
[CommandImplementation("client"), TakesPipedTypeAsGeneric]
|
||||
public IEnumerable<T> Client<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
|
||||
{
|
||||
// No session means there's no client to hand this back to - e.g. the server console.
|
||||
if (ctx.Session is not { } session)
|
||||
{
|
||||
ctx.ReportError(new NoClientSessionError());
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var value in input)
|
||||
{
|
||||
_console.RemoteExecuteCommand(session, Substitute(command, value, ctx));
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command on the server as the calling session.
|
||||
/// </summary>
|
||||
[CommandImplementation("server"), TakesPipedTypeAsGeneric]
|
||||
public IEnumerable<T> Server<T>(IInvocationContext ctx, [PipedArgument] IEnumerable<T> input, string command)
|
||||
{
|
||||
foreach (var value in input)
|
||||
{
|
||||
_console.ExecuteCommand(ctx.Session, Substitute(command, value, ctx));
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
private string Substitute<T>(string command, T value, IInvocationContext ctx)
|
||||
{
|
||||
return TokenRegex.Replace(command, match =>
|
||||
{
|
||||
var name = match.Groups[1].Value;
|
||||
|
||||
if (TryGetBuiltin(name, value, ctx, out var builtin))
|
||||
return builtin;
|
||||
|
||||
// Anything else is looked up as a toolshed variable and implicitly stringified.
|
||||
if (ctx.ReadVar(name) is { } variable)
|
||||
return Stringify(variable);
|
||||
|
||||
// Unresolved, so leave the token alone rather than silently blanking it.
|
||||
return match.Value;
|
||||
});
|
||||
}
|
||||
|
||||
private bool TryGetBuiltin<T>(string name, T value, IInvocationContext ctx, [NotNullWhen(true)] out string? result)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "SELF":
|
||||
result = Stringify(value);
|
||||
return true;
|
||||
case "PID":
|
||||
result = (ctx.Session?.AttachedEntity ?? EntityUid.Invalid).ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
// The remaining tokens are all positional, so they only apply when piping entities.
|
||||
if (value is not EntityUid uid)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "ID":
|
||||
result = uid.ToString();
|
||||
return true;
|
||||
case "WX":
|
||||
result = Number(WorldPosition(uid).X);
|
||||
return true;
|
||||
case "WY":
|
||||
result = Number(WorldPosition(uid).Y);
|
||||
return true;
|
||||
case "LX":
|
||||
result = Number(Transform(uid).Coordinates.X);
|
||||
return true;
|
||||
case "LY":
|
||||
result = Number(Transform(uid).Coordinates.Y);
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Vector2 WorldPosition(EntityUid uid)
|
||||
{
|
||||
_xform ??= GetSys<SharedTransformSystem>();
|
||||
return _xform.GetWorldPosition(uid);
|
||||
}
|
||||
|
||||
private static string Number(float value) => value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
private static string Stringify(object? value) => value?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
public sealed class NoClientSessionError : IConError
|
||||
{
|
||||
public FormattedMessage DescribeInner()
|
||||
{
|
||||
return FormattedMessage.FromUnformatted("There is no client to run this on. doext:client must be run by a player, not the server console.");
|
||||
}
|
||||
|
||||
public string? Expression { get; set; }
|
||||
public Vector2i? IssueSpan { get; set; }
|
||||
public StackTrace? Trace { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Errors;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server._DV.Toolshed;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the value of a single field or property off of the piped input, by name.
|
||||
/// Member names are resolved the same way <c>vvread</c> does: the member must be exposed
|
||||
/// via <c>[ViewVariables]</c>, <c>[DataField]</c>, or <c>[IncludeDataField]</c>.
|
||||
/// </summary>
|
||||
/// <example><code>ent 12345 . Name</code></example>
|
||||
[ToolshedCommand(Name = "."), AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class FieldCommand : ToolshedCommand
|
||||
{
|
||||
[CommandImplementation]
|
||||
public object? Field([PipedArgument] object? value, [CommandArgument(typeof(FieldNameParser))] string field, IInvocationContext ctx)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
ctx.ReportError(new NullInputFieldError());
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = value.GetType();
|
||||
var member = GetSingleMember(type, field);
|
||||
|
||||
// Restrict to members that vvread would let you read, so this can't be used to peek at arbitrary internals.
|
||||
if (member == null || !ViewVariablesUtility.TryGetViewVariablesAccess(member, out _))
|
||||
{
|
||||
ctx.ReportError(new NoSuchFieldError(type, field));
|
||||
return null;
|
||||
}
|
||||
|
||||
return member switch
|
||||
{
|
||||
FieldInfo f => f.GetValue(value),
|
||||
PropertyInfo p => p.GetValue(value),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the field or property with the given name, mirroring the resolution used by <c>vvread</c>.
|
||||
/// </summary>
|
||||
private static MemberInfo? GetSingleMember(Type type, string member)
|
||||
{
|
||||
var members = type
|
||||
.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(m => m.Name == member && m is FieldInfo or PropertyInfo)
|
||||
.ToArray();
|
||||
|
||||
if (members.Length == 0)
|
||||
return null;
|
||||
|
||||
// In case there's member hiding going on, grab the one declared by the type of the object by default.
|
||||
return members.Length > 1
|
||||
? members.FirstOrDefault(m => m.DeclaringType == type) ?? members[0]
|
||||
: members[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a field name as a bare identifier (letters, digits, underscore) so that the <c>.</c> command doesn't
|
||||
/// require the name to be wrapped in quotes like the default <see cref="string"/> parser does.
|
||||
/// </summary>
|
||||
public sealed class FieldNameParser : CustomTypeParser<string>
|
||||
{
|
||||
public override bool TryParse(ParserContext ctx, [NotNullWhen(true)] out string? result)
|
||||
{
|
||||
ctx.ConsumeWhitespace();
|
||||
result = ctx.GetWord(ParserContext.IsToken);
|
||||
if (result != null)
|
||||
return true;
|
||||
|
||||
if (ctx.PeekRune() is null)
|
||||
ctx.Error = new OutOfInputError();
|
||||
else
|
||||
ctx.Error = new InvalidFieldNameError();
|
||||
|
||||
ctx.Error.Contextualize(ctx.Input, (ctx.Index, ctx.Index + 1));
|
||||
return false;
|
||||
}
|
||||
|
||||
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
|
||||
{
|
||||
return CompletionResult.FromHint(GetArgHint(arg));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InvalidFieldNameError : IConError
|
||||
{
|
||||
public FormattedMessage DescribeInner()
|
||||
{
|
||||
return FormattedMessage.FromUnformatted("Expected a field name (letters, digits, or underscores).");
|
||||
}
|
||||
|
||||
public string? Expression { get; set; }
|
||||
public Vector2i? IssueSpan { get; set; }
|
||||
public StackTrace? Trace { get; set; }
|
||||
}
|
||||
|
||||
public sealed class NullInputFieldError : IConError
|
||||
{
|
||||
public FormattedMessage DescribeInner()
|
||||
{
|
||||
return FormattedMessage.FromUnformatted("Cannot read a field off of a null input.");
|
||||
}
|
||||
|
||||
public string? Expression { get; set; }
|
||||
public Vector2i? IssueSpan { get; set; }
|
||||
public StackTrace? Trace { get; set; }
|
||||
}
|
||||
|
||||
public sealed class NoSuchFieldError(Type type, string field) : IConError
|
||||
{
|
||||
public FormattedMessage DescribeInner()
|
||||
{
|
||||
return FormattedMessage.FromUnformatted($"Type {type.Name} has no readable field or property named '{field}'. It must be exposed via [ViewVariables], [DataField], or [IncludeDataField].");
|
||||
}
|
||||
|
||||
public string? Expression { get; set; }
|
||||
public Vector2i? IssueSpan { get; set; }
|
||||
public StackTrace? Trace { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared._Floof.OfferItem;
|
||||
|
||||
namespace Content.Server._Floof.OfferItem;
|
||||
|
||||
public sealed partial class OfferItemSystem : SharedOfferItemSystem;
|
||||
|
|
@ -27,6 +27,7 @@ using Content.Shared.Mobs;
|
|||
using Robust.Shared.Timing;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Trigger.Components;
|
||||
using Content.Shared.Zombies; // DeltaV
|
||||
|
||||
namespace Content.Server._Impstation.Revenant.EntitySystems;
|
||||
|
||||
|
|
@ -196,6 +197,7 @@ public sealed partial class RevenantAnimatedSystem : EntitySystem
|
|||
animate.EndTime = _gameTiming.CurTime + duration.Value;
|
||||
else if (revenant != null)
|
||||
animate.EndTime = _gameTiming.CurTime + revenant.Value.Comp.AnimateTime;
|
||||
EnsureComp<ZombieImmuneComponent>(target); // DeltaV - ensure immune from zombies so we don't break clients
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,290 @@
|
|||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Server.Containers;
|
||||
using Content.Shared._Starlight;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared._Goobstation.Overlays;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.Body.Components;
|
||||
using System.Linq;
|
||||
using Content.Shared._Goobstation.Flashbang;
|
||||
using Content.Shared._Starlight.Flash.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Flash; // Delta V - Flash Work
|
||||
|
||||
|
||||
namespace Content.Server._Starlight;
|
||||
|
||||
public sealed class ShadekinSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _speed = default!;
|
||||
[Dependency] private readonly SharedFlashSystem _flashSystem = default!;
|
||||
|
||||
private sealed class LightCone
|
||||
{
|
||||
public float Direction { get; set; }
|
||||
public float InnerWidth { get; set; }
|
||||
public float OuterWidth { get; set; }
|
||||
}
|
||||
private readonly Dictionary<string, List<LightCone>> lightMasks = new()
|
||||
{
|
||||
["/Textures/Effects/LightMasks/cone.png"] = new List<LightCone>
|
||||
{
|
||||
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 }
|
||||
},
|
||||
["/Textures/Effects/LightMasks/double_cone.png"] = new List<LightCone>
|
||||
{
|
||||
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 },
|
||||
new LightCone { Direction = 180, InnerWidth = 30, OuterWidth = 60 }
|
||||
}
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ShadekinComponent, ComponentStartup>(OnInit);
|
||||
SubscribeLocalEvent<ShadekinComponent, EyeColorInitEvent>(OnEyeColorChange);
|
||||
SubscribeLocalEvent<ShadekinComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
|
||||
SubscribeLocalEvent<ShadekinComponent, AfterFlashedEvent>(OnShadekinFlashed); // Delta V - Prevent Chain Flashing
|
||||
SubscribeLocalEvent<ShadekinComponent, FlashDurationMultiplierEvent>(GetFlashModifier); // Delta V - Flash Modifier to Shadekin
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, ShadekinComponent component, ComponentStartup args)
|
||||
{
|
||||
UpdateAlert(uid, component, (short)component.CurrentState);
|
||||
RemComp<InternalsComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnEyeColorChange(EntityUid uid, ShadekinComponent component, EyeColorInitEvent args)
|
||||
{
|
||||
if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid))
|
||||
return;
|
||||
|
||||
// humanoid.EyeColor = Color.Black;
|
||||
Dirty(uid, humanoid);
|
||||
}
|
||||
|
||||
public void UpdateAlert(EntityUid uid, ShadekinComponent component, short state)
|
||||
{
|
||||
_alerts.ShowAlert(uid, component.ShadekinAlert, state);
|
||||
}
|
||||
|
||||
private Angle GetAngle(EntityUid lightUid, SharedPointLightComponent lightComp, EntityUid targetUid)
|
||||
{
|
||||
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(lightUid);
|
||||
lightPos += lightRot.RotateVec(lightComp.Offset);
|
||||
|
||||
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetUid);
|
||||
|
||||
var mapDiff = targetPos - lightPos;
|
||||
|
||||
var oppositeMapDiff = (-lightRot).RotateVec(mapDiff);
|
||||
var angle = oppositeMapDiff.ToWorldAngle();
|
||||
|
||||
if (angle == double.NaN && _transform.ContainsEntity(targetUid, lightUid) || _transform.ContainsEntity(lightUid, targetUid))
|
||||
{
|
||||
angle = 0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an illumination float value with is how many "energy" of light is hitting our ent.
|
||||
/// WARNING: This function might be expensive, Avoid calling it too much and CACHE THE RESULT!
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <returns></returns>
|
||||
public float GetLightExposure(EntityUid uid)
|
||||
{
|
||||
var illumination = 0f;
|
||||
|
||||
var lightQuery = _lookup.GetEntitiesInRange<PointLightComponent>(Transform(uid).Coordinates, 20, LookupFlags.Uncontained);
|
||||
|
||||
foreach (var light in lightQuery)
|
||||
{
|
||||
if (!light.Comp.Enabled
|
||||
|| light.Comp.Radius < 1
|
||||
|| light.Comp.Energy <= 0)
|
||||
continue;
|
||||
|
||||
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(light);
|
||||
lightPos += lightRot.RotateVec(light.Comp.Offset);
|
||||
|
||||
if (!_examine.InRangeUnOccluded(light, uid, light.Comp.Radius, null))
|
||||
continue;
|
||||
|
||||
Transform(uid).Coordinates.TryDistance(EntityManager, Transform(light).Coordinates, out var dist);
|
||||
|
||||
var denom = dist / light.Comp.Radius;
|
||||
var attenuation = 1 - (denom * denom);
|
||||
var calculatedLight = 0f;
|
||||
|
||||
if (light.Comp.MaskPath is not null)
|
||||
{
|
||||
var angleToTarget = GetAngle(light, light.Comp, uid);
|
||||
foreach (var cone in lightMasks[light.Comp.MaskPath])
|
||||
{
|
||||
var coneLight = 0f;
|
||||
var angleAttenuation = (float)Math.Min((float)Math.Max(cone.OuterWidth - angleToTarget, 0f), cone.InnerWidth) / cone.OuterWidth;
|
||||
|
||||
if (angleToTarget.Degrees - cone.Direction > cone.OuterWidth)
|
||||
continue;
|
||||
else if (angleToTarget.Degrees - cone.Direction > cone.InnerWidth
|
||||
&& angleToTarget.Degrees - cone.Direction < cone.OuterWidth)
|
||||
coneLight = light.Comp.Energy * attenuation * attenuation * angleAttenuation;
|
||||
else
|
||||
coneLight = light.Comp.Energy * attenuation * attenuation;
|
||||
|
||||
calculatedLight = Math.Max(calculatedLight, coneLight);
|
||||
}
|
||||
}
|
||||
else
|
||||
calculatedLight = light.Comp.Energy * attenuation * attenuation;
|
||||
|
||||
illumination += calculatedLight; //Math.Max(illumination, calculatedLight);
|
||||
}
|
||||
|
||||
return illumination;
|
||||
}
|
||||
|
||||
private void SetPassiveBuff(EntityUid uid, ShadekinState state)
|
||||
{
|
||||
if (!TryComp<PassiveDamageComponent>(uid, out var passive))
|
||||
return;
|
||||
|
||||
if (state == ShadekinState.Extreme || state == ShadekinState.Annoying || state == ShadekinState.High)
|
||||
{
|
||||
// passive.DamageCap = 1;
|
||||
}
|
||||
else if (state == ShadekinState.Low)
|
||||
{
|
||||
// passive.DamageCap = 20;
|
||||
passive.AllowedStates.Clear();
|
||||
passive.AllowedStates.Add(MobState.Alive);
|
||||
passive.Interval = 1f;
|
||||
}
|
||||
else if (state != ShadekinState.Dark)
|
||||
{
|
||||
// passive.DamageCap = 0;
|
||||
passive.AllowedStates.Clear();
|
||||
passive.AllowedStates.Add(MobState.Alive);
|
||||
passive.AllowedStates.Add(MobState.Critical);
|
||||
passive.AllowedStates.Add(MobState.Dead);
|
||||
passive.Interval = 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleNightVision(EntityUid uid, ShadekinState state)
|
||||
{
|
||||
if (state == ShadekinState.Dark)
|
||||
{
|
||||
var nightVisionComponent = EnsureComp<NightVisionComponent>(uid);
|
||||
nightVisionComponent.Color = Color.FromHex("#808080"); // Delta V - Change Night Vision Color
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryComp<NightVisionComponent>(uid, out var nightVision) && nightVision.IsActive)
|
||||
_flashSystem.Flash(uid, uid, uid, TimeSpan.FromSeconds(0.5 * (int)state), 0.5f);
|
||||
RemComp<NightVisionComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyLightDamage(EntityUid uid, float dmg)
|
||||
{
|
||||
var damage = new DamageSpecifier();
|
||||
damage.DamageDict.Add("Heat", dmg);
|
||||
_damageable.TryChangeDamage(uid, damage, true, false);
|
||||
|
||||
}
|
||||
|
||||
private void OnRefreshMovementSpeedModifiers(EntityUid uid, ShadekinComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
if (component.CurrentState == ShadekinState.Low || component.CurrentState == ShadekinState.Annoying ||
|
||||
component.CurrentState == ShadekinState.Dark || component.CurrentState == ShadekinState.Invalid)
|
||||
return;
|
||||
|
||||
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
|
||||
return;
|
||||
|
||||
var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed;
|
||||
args.ModifySpeed(1f, sprintDif);
|
||||
}
|
||||
|
||||
private ShadekinState GetStateByThreshold(ShadekinComponent component, float lightExposure)
|
||||
{
|
||||
var returnState = ShadekinState.Dark;
|
||||
|
||||
foreach (var (threshold, shadekinState) in component.Thresholds.Reverse())
|
||||
{
|
||||
if (threshold <= lightExposure)
|
||||
{
|
||||
returnState = shadekinState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnState;
|
||||
}
|
||||
|
||||
// Delta V - Begin Shadekin Flash Changes
|
||||
private void GetFlashModifier(EntityUid uid, ShadekinComponent comp, FlashDurationMultiplierEvent args)
|
||||
{
|
||||
if (!TryComp<FlashModifierComponent>(uid, out var flashModifier))
|
||||
return;
|
||||
|
||||
args.Multiplier = flashModifier.Modifier;
|
||||
}
|
||||
|
||||
private void OnShadekinFlashed(EntityUid uid, ShadekinComponent comp, AfterFlashedEvent ev)
|
||||
{
|
||||
RemComp<NightVisionComponent>(uid);
|
||||
}
|
||||
// Delta V - End
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<ShadekinComponent>();
|
||||
while (query.MoveNext(out var uid, out var component))
|
||||
{
|
||||
if (_timing.CurTime < component.NextUpdate)
|
||||
continue;
|
||||
|
||||
component.NextUpdate = _timing.CurTime + component.UpdateCooldown;
|
||||
|
||||
var lightExposure = 0f;
|
||||
|
||||
if (!_container.IsEntityInContainer(uid))
|
||||
lightExposure = GetLightExposure(uid);
|
||||
|
||||
component.CurrentState = GetStateByThreshold(component, lightExposure);
|
||||
|
||||
UpdateAlert(uid, component, (short)component.CurrentState);
|
||||
|
||||
SetPassiveBuff(uid, component.CurrentState);
|
||||
ToggleNightVision(uid, component.CurrentState);
|
||||
|
||||
_speed.RefreshMovementSpeedModifiers(uid);
|
||||
|
||||
if (component.CurrentState == ShadekinState.Extreme)
|
||||
ApplyLightDamage(uid, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,8 @@ using Robust.Shared.Timing;
|
|||
using Content.Shared._DV.Access.Systems;
|
||||
using Content.Shared._DV.Access.Components;
|
||||
using Content.Shared.Mind; // DeltaV - Subdermal ID Cards
|
||||
using Content.Shared.Ninja.Components; // DeltaV
|
||||
using Content.Shared.Revenant.Components; // DeltaV
|
||||
|
||||
namespace Content.Shared.Access.Systems;
|
||||
|
||||
|
|
@ -31,7 +33,6 @@ public sealed class AccessReaderSystem : EntitySystem
|
|||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly EmagSystem _emag = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly SharedGameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
|
|
@ -151,7 +152,7 @@ public sealed class AccessReaderSystem : EntitySystem
|
|||
|
||||
private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args)
|
||||
{
|
||||
if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) // DeltaV - emag for lockers etc instead of doorjack
|
||||
if (HasComp<SpaceNinjaComponent>(args.UserUid) || HasComp<RevenantComponent>(args.UserUid)) // DeltaV - Don't break access if its a ninja doing it
|
||||
return;
|
||||
|
||||
if (!reader.BreakOnAccessBreaker)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,12 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
|||
if (!other.Layer.Equals(ent.Comp.Layer))
|
||||
return;
|
||||
|
||||
// Delta V - Begin Fix Height for Cloning
|
||||
var height = other.Profile.Height;
|
||||
if (TryComp<HumanoidProfileComponent>(args.Body.Owner, out var component))
|
||||
ScaleBody((args.Body.Owner, component), height, height);
|
||||
// Delta V - End
|
||||
|
||||
SetOrganAppearance(ent, other.Data);
|
||||
}
|
||||
|
||||
|
|
@ -112,9 +118,14 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
|||
|
||||
var height = Math.Clamp(MathF.Round(args.Base.Value.Height, 2), speciesPrototype.MinHeight, speciesPrototype.MaxHeight);
|
||||
|
||||
ScaleBody(entity, speciesPrototype.ScaleHeight ? height : 1f, height);
|
||||
}
|
||||
|
||||
private void ScaleBody(Entity<HumanoidProfileComponent> entity, float heightX, float heightY)
|
||||
{
|
||||
_scaleVisualsSystem.SetSpriteScale(
|
||||
entity.Owner,
|
||||
new Vector2(speciesPrototype.ScaleHeight ? height : 1f, height)
|
||||
new Vector2(heightX, heightY)
|
||||
);
|
||||
}
|
||||
// Delta V - END
|
||||
|
|
@ -148,7 +159,7 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
|
|||
if (ent.Comp.Layer.Equals(HumanoidVisualLayers.Eyes))
|
||||
{
|
||||
// Delta V - Begin Add AppearanceLoaded Event
|
||||
var appearanceLoaded = new AppearanceLoadedEvent(ent.Comp.Profile.EyeColor);
|
||||
var appearanceLoaded = new AppearanceLoadedEvent(ent.Comp.Profile.EyeColor, ent.Comp.Profile.SkinColor);
|
||||
RaiseLocalEvent(args.Body.Owner, ref appearanceLoaded);
|
||||
// Delta V - End
|
||||
SetOrganColor(ent, ent.Comp.Profile.EyeColor);
|
||||
|
|
|
|||
|
|
@ -392,7 +392,6 @@ public abstract partial class SharedBuckleSystem
|
|||
if (TryComp<PhysicsComponent>(buckle, out var physics))
|
||||
_physics.ResetDynamics(buckle, physics);
|
||||
|
||||
// TOOD: DV - This fails when you try to buckle the entity you're carrying to something. Figure out why later.
|
||||
DebugTools.AssertEqual(xform.ParentUid, strap.Owner);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,4 +28,18 @@ public readonly partial record struct CargoBountyData
|
|||
Bounty = bounty.ID;
|
||||
Id = $"{bounty.IdPrefix}{uniqueIdentifier:D3}";
|
||||
}
|
||||
}
|
||||
// Begin DeltaV bounty claiming
|
||||
[DataField]
|
||||
public string ClaimedBy { get; init; } = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public CargoBountyStatus Status { get; init; } = CargoBountyStatus.Undelivered;
|
||||
}
|
||||
|
||||
public enum CargoBountyStatus
|
||||
{
|
||||
Undelivered,
|
||||
Waiting,
|
||||
OnShuttle,
|
||||
} // End DeltaV bounty claiming
|
||||
|
||||
|
|
|
|||
|
|
@ -94,3 +94,4 @@ public sealed class BountySkipMessage : BoundUserInterfaceMessage
|
|||
BountyId = bountyId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ using System.Linq;
|
|||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared._Starlight.Flash.Components; // Delta V - For Flash Duration
|
||||
|
||||
namespace Content.Shared.Flash;
|
||||
|
||||
|
|
@ -173,7 +174,7 @@ public abstract class SharedFlashSystem : EntitySystem
|
|||
// Goobstation end
|
||||
|
||||
// don't paralyze, slowdown or convert to rev if the target is immune to flashes
|
||||
if (!_statusEffectsSystem.TryAddStatusEffect<FlashedComponent>(target, FlashedKey, flashDuration, true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection
|
||||
if (!_statusEffectsSystem.TryAddStatusEffect<FlashedComponent>(target, FlashedKey, flashDuration * multiplier, true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection. Added Flashduration Multiplier
|
||||
return;
|
||||
|
||||
if (stunDuration != null)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ using Content.Shared.Armor; // DeltaV - Addition of HandHeldArmor
|
|||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.Cuffs;
|
||||
using Content.Shared.Damage; // DeltaV End - Addition of HandHeldArmor
|
||||
using Content.Shared.Damage.Systems; // DeltaV End - Addition of HandHeldArmor
|
||||
using Content.Shared.Damage.Systems; // DeltaV - Addition of HandHeldArmor
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Projectiles;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ namespace Content.Shared.Input
|
|||
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
|
||||
public static readonly BoundKeyFunction ToggleFullscreen = "ToggleFullscreen";
|
||||
public static readonly BoundKeyFunction Point = "Point";
|
||||
public static readonly BoundKeyFunction OfferItem = "OfferItem"; // Floofstation
|
||||
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
|
||||
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
||||
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Shared._DV.Overlays;
|
||||
using Content.Shared._DV.Overlays; // DeltaV
|
||||
using Content.Shared._DV.Psionics.Events; // DeltaV
|
||||
using Content.Shared.Armor;
|
||||
using Content.Shared.Atmos;
|
||||
|
|
@ -85,13 +85,15 @@ public partial class InventorySystem
|
|||
SubscribeLocalEvent<InventoryComponent, WieldAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, UnwieldAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(RefRelayInventoryEvent);
|
||||
// DeltaV Start - Psionic Events
|
||||
// DeltaV Start
|
||||
// Psionic Events
|
||||
SubscribeLocalEvent<InventoryComponent, DispelledEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, PsionicPowerUseAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, TargetedByPsionicPowerEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, NoosphericFryEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, WeightlessnessChangedEvent>(RefRelayInventoryEvent); // Heavy Clothing
|
||||
// DeltaV End - Psionic Events
|
||||
// Heavy Clothing
|
||||
SubscribeLocalEvent<InventoryComponent, WeightlessnessChangedEvent>(RefRelayInventoryEvent);
|
||||
// DeltaV End
|
||||
|
||||
// Eye/vision events
|
||||
SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);
|
||||
|
|
|
|||
|
|
@ -103,6 +103,22 @@ public abstract class SharedItemSystem : EntitySystem
|
|||
VisualsChanged(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - clear item visuals
|
||||
/// </summary>
|
||||
public void ClearVisuals(Entity<ItemComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
ent.Comp.RsiPath = null;
|
||||
ent.Comp.InhandVisuals = new();
|
||||
ent.Comp.HeldPrefix = null;
|
||||
|
||||
Dirty(ent, ent.Comp);
|
||||
VisualsChanged(ent.Owner);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnHandInteract(EntityUid uid, ItemComponent component, InteractHandEvent args)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
||||
using Content.Shared.Chemistry.Components; // DeltaV - Health Analyzer Plus
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.MedicalScanner;
|
||||
|
|
@ -29,11 +30,12 @@ public struct HealthAnalyzerUiState
|
|||
public bool? ScanMode;
|
||||
public bool? Bleeding;
|
||||
public bool? Unrevivable;
|
||||
public readonly Solution? BloodSolution; // DeltaV - Health Analyzer Plus
|
||||
public MedicalRecord? MedicalRecord; // DeltaV - Medical Records
|
||||
|
||||
public HealthAnalyzerUiState() {}
|
||||
|
||||
public HealthAnalyzerUiState(NetEntity? targetEntity, float temperature, float bloodLevel, bool? scanMode, bool? bleeding, bool? unrevivable, MedicalRecord? medicalRecord = null) // DeltaV - Medical Records
|
||||
public HealthAnalyzerUiState(NetEntity? targetEntity, float temperature, float bloodLevel, bool? scanMode, bool? bleeding, bool? unrevivable, Solution? bloodSolution, MedicalRecord? medicalRecord = null) // DeltaV - Health Analyzer Plus, Medical Records
|
||||
{
|
||||
TargetEntity = targetEntity;
|
||||
Temperature = temperature;
|
||||
|
|
@ -41,6 +43,7 @@ public struct HealthAnalyzerUiState
|
|||
ScanMode = scanMode;
|
||||
Bleeding = bleeding;
|
||||
Unrevivable = unrevivable;
|
||||
BloodSolution = bloodSolution; // DeltaV - Health Analyzer Plus
|
||||
MedicalRecord = medicalRecord; // DeltaV - Medical Records
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared._ST.Interaction; // Stellar - interaction particles
|
||||
using Content.Shared._Floof.OfferItem; // Floof
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Alert;
|
||||
|
|
@ -117,10 +118,11 @@ public sealed class PullingSystem : EntitySystem
|
|||
if (TryComp(args.PullerUid, out PullerComponent? pullerComp) && !pullerComp.NeedsHands)
|
||||
return;
|
||||
|
||||
if (!_virtual.TrySpawnVirtualItemInHand(args.PulledUid, uid))
|
||||
if (!_virtual.TrySpawnVirtualItemInHand(args.PulledUid, uid, out var virt)) // Floofstation - store item
|
||||
{
|
||||
DebugTools.Assert("Unable to find available hand when starting pulling??");
|
||||
}
|
||||
EnsureComp<OfferableVirtualItemComponent>(virt.Value); // Floofstation - add a special component to allow offering it
|
||||
}
|
||||
|
||||
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
using Content.Shared.Actions;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Item.PseudoItem;
|
||||
|
|
@ -24,7 +23,8 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
|
|||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!; // Floofstation
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!; // Floofstation
|
||||
|
||||
private readonly ProtoId<TagPrototype> PreventTag = "PreventLabel";
|
||||
private readonly EntProtoId SleepActionId = "ActionSleep"; // The action used for sleeping inside bags. Currently uses the default sleep action (same as beds)
|
||||
|
|
@ -116,17 +116,35 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
|
|||
protected virtual void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component,
|
||||
GettingPickedUpAttemptEvent args)
|
||||
{
|
||||
if (args.User == args.Item)
|
||||
return;
|
||||
args.Cancel(); // Floof - this is a terrible idea. This triggers every time ANY system checks if a pseudo-item can be picked up.
|
||||
// WHY DID YOU DO THAT, NYANOTRASEN???
|
||||
|
||||
_transform.AttachToGridOrMap(uid);
|
||||
args.Cancel();
|
||||
// if (args.User == args.Item)
|
||||
// return;
|
||||
//
|
||||
// _transform.AttachToGridOrMap(uid);
|
||||
// args.Cancel();
|
||||
}
|
||||
|
||||
private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args)
|
||||
{
|
||||
if (component.Active)
|
||||
args.Cancel();
|
||||
if (!component.Active)
|
||||
return;
|
||||
|
||||
// Floof - we try to get the containing container and try to drop it into it
|
||||
// If possible, we do it, since a bagged cat probably can put things back into the bag just like they can pick them up.
|
||||
string? failReason = null;
|
||||
if (_hands.GetActiveItem(uid) is { Valid: true, } droppedItem
|
||||
&& _container.TryGetContainingContainer(Transform(uid).ParentUid, uid, out var pseudoItemContainer)
|
||||
&& TryComp<StorageComponent>(pseudoItemContainer.Owner, out var targetStorage)
|
||||
&& _storage.CanInsert(pseudoItemContainer.Owner, droppedItem, out failReason, targetStorage, ignoreStacks: true)
|
||||
)
|
||||
_storage.Insert(pseudoItemContainer.Owner, droppedItem, out _, uid, targetStorage, stackAutomatically: false);
|
||||
|
||||
if (failReason != null)
|
||||
_popupSystem.PopupEntity(Loc.GetString(failReason), uid, uid);
|
||||
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component,
|
||||
|
|
@ -141,8 +159,9 @@ public abstract partial class SharedPseudoItemSystem : EntitySystem
|
|||
// Prevents moving within the bag :)
|
||||
private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args)
|
||||
{
|
||||
if (args.Uid == args.Target && component.Active)
|
||||
args.Cancelled = true;
|
||||
// Floof - why the fuck.
|
||||
// if (args.Uid == args.Target && component.Active)
|
||||
// args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems; // DeltaV
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Doors.Components;
|
||||
|
|
@ -19,6 +20,7 @@ namespace Content.Shared.Remotes.EntitySystems;
|
|||
public abstract class SharedDoorRemoteSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAirlockSystem _airlock = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!; // DeltaV
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly SharedElectrocutionSystem _electrify = default!;
|
||||
|
|
@ -96,6 +98,19 @@ public abstract class SharedDoorRemoteSystem : EntitySystem
|
|||
else if (entity.Comp.RequireTagWhitelist)
|
||||
return;
|
||||
|
||||
// Begin DeltaV - Emergency access only bypasses open/close; bolting and toggling emergency access still require actual access.
|
||||
if (entity.Comp.Mode != OperatingMode.OpenClose
|
||||
&& accessComponent != null
|
||||
&& !_accessReader.IsAllowed(accessTarget, args.Target.Value, accessComponent))
|
||||
{
|
||||
if (isAirlock)
|
||||
_doorSystem.Deny(args.Target.Value, doorComp, user: args.User, predicted: true);
|
||||
|
||||
_popup.PopupClient(Loc.GetString("door-remote-denied"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
// End DeltaV
|
||||
|
||||
switch (entity.Comp.Mode)
|
||||
{
|
||||
case OperatingMode.OpenClose:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using Content.Shared._CD.Silicons.Borgs; // CosmicDrift - borg subtypes
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
|
|
@ -97,10 +96,6 @@ 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)
|
||||
|
|
@ -125,25 +120,24 @@ public abstract partial class SharedBorgSwitchableTypeSystem : EntitySystem // D
|
|||
{
|
||||
footstepModifier.FootstepSoundCollection = prototype.FootstepCollection;
|
||||
}
|
||||
// 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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
|
|
|
|||
|
|
@ -188,12 +188,20 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
// Begin DeltaV - the grid check excludes the item being resized because it's already in the storage which defeats the purpose so yoink it out to remove that
|
||||
storage.StoredItems.Remove(itemEnt.Owner);
|
||||
UpdateOccupied((container.Owner, storage));
|
||||
|
||||
if (!ItemFitsInGridLocation((itemEnt.Owner, itemEnt.Comp), (container.Owner, storage), loc))
|
||||
if (ItemFitsInGridLocation((itemEnt.Owner, itemEnt.Comp), (container.Owner, storage), loc))
|
||||
{
|
||||
storage.StoredItems.Add(itemEnt.Owner, loc);
|
||||
UpdateOccupied((container.Owner, storage));
|
||||
}
|
||||
else
|
||||
{
|
||||
ContainerSystem.Remove(itemEnt.Owner, container, force: true);
|
||||
}
|
||||
// End DeltaV - the grid check excludes the item being resized because it's already in the storage which defeats the purpose so yoink it out to remove that
|
||||
}
|
||||
|
||||
private void OnNestedStorageCvar(bool obj)
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
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; }
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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;
|
||||
|
|
@ -75,4 +75,7 @@ public sealed partial class PreenableComponent : Component
|
|||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan? ReplenishTime;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public Color? Color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared._DV.Body.Components;
|
||||
using Content.Shared._DV.Body.Events;
|
||||
using Content.Shared.Buckle;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
|
|
@ -13,6 +14,7 @@ public sealed class CPRSystem : EntitySystem
|
|||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -85,7 +87,7 @@ public sealed class CPRSystem : EntitySystem
|
|||
{
|
||||
Act = () => StartCPR(user, target, cprComp.TimeLength),
|
||||
Text = Loc.GetString("cpr-verb-start"),
|
||||
Priority = 2,
|
||||
Priority = _buckle.IsBuckled(target) ? 3 : 1, // Higher priority if they are buckled. Otherwise, this conflicts with trying to carry.
|
||||
Disabled = alreadyAffected,
|
||||
Message = alreadyAffected ? Loc.GetString("cpr-verb-disabled-description") : Loc.GetString("cpr-verb-description"),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared._DV.Body.Components;
|
||||
using Content.Shared._DV.Body.Events;
|
||||
using Content.Shared._DV.Humanoid;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Damage;
|
||||
|
|
@ -45,6 +46,7 @@ public sealed class PreenableSystem : EntitySystem
|
|||
SubscribeLocalEvent<PreenableComponent, DamageChangedEvent>(OnDamaged);
|
||||
SubscribeLocalEvent<PreenableComponent, DamageModifyEvent>(OnDamageModify);
|
||||
SubscribeLocalEvent<PreenableComponent, ComponentInit>(OnCompInit);
|
||||
SubscribeLocalEvent<PreenableComponent, AppearanceLoadedEvent>(OnProfileLoadFinished);
|
||||
}
|
||||
|
||||
private void OnCompInit(Entity<PreenableComponent> ent, ref ComponentInit args)
|
||||
|
|
@ -185,7 +187,7 @@ public sealed class PreenableSystem : EntitySystem
|
|||
|
||||
if (TryComp<HumanoidProfileComponent>(ent, out var appearance))
|
||||
{
|
||||
_appearance.SetData(feather, FeatherVisuals.FeatherColor, Color.White); // TODO: Nubody Colors
|
||||
_appearance.SetData(feather, FeatherVisuals.FeatherColor, ent.Comp.Color ?? Color.White);
|
||||
}
|
||||
|
||||
// best be careful, no cleaning this
|
||||
|
|
@ -204,6 +206,11 @@ public sealed class PreenableSystem : EntitySystem
|
|||
return feather;
|
||||
}
|
||||
|
||||
private void OnProfileLoadFinished(Entity<PreenableComponent> ent, ref AppearanceLoadedEvent args)
|
||||
{
|
||||
ent.Comp.Color = args.SkinColor;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared._DV.Cargo.Components;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BountyClaimedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string BountyId;
|
||||
|
||||
public BountyClaimedMessage(string bountyId)
|
||||
{
|
||||
BountyId = bountyId;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BountySetStatusMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string BountyId;
|
||||
public readonly int Status;
|
||||
|
||||
public BountySetStatusMessage(string bountyId, int status)
|
||||
{
|
||||
BountyId = bountyId;
|
||||
Status = status;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,9 @@ using Robust.Shared.Network;
|
|||
using Robust.Shared.Physics.Components;
|
||||
using System.Numerics;
|
||||
using Content.Shared._DV.Polymorph;
|
||||
using Content.Shared._Floof.OfferItem;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Buckle;
|
||||
|
||||
namespace Content.Shared._DV.Carrying;
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ public sealed class CarryingSystem : EntitySystem
|
|||
SubscribeLocalEvent<BeingCarriedComponent, GettingInteractedWithAttemptEvent>(OnInteractedWith);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, PullAttemptEvent>(OnPullAttempt);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, StartClimbEvent>(OnDrop);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, BuckledEvent>(OnDrop);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, BuckledEvent>(OnBuckle);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, UnbuckledEvent>(OnDrop);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, StrappedEvent>(OnDrop);
|
||||
SubscribeLocalEvent<BeingCarriedComponent, UnstrappedEvent>(OnDrop);
|
||||
|
|
@ -181,14 +183,15 @@ public sealed class CarryingSystem : EntitySystem
|
|||
/// </summary>
|
||||
private void OnInteractionAttempt(Entity<BeingCarriedComponent> ent, ref InteractionAttemptEvent args)
|
||||
{
|
||||
if (args.Target is not {} target)
|
||||
return;
|
||||
|
||||
var targetParent = Transform(target).ParentUid;
|
||||
|
||||
var carrier = ent.Comp.Carrier;
|
||||
if (target != carrier && targetParent != carrier && targetParent != ent.Owner)
|
||||
args.Cancelled = true;
|
||||
// Floofstation - no - this prevents the person from escaping and more.
|
||||
// if (args.Target is not {} target)
|
||||
// return;
|
||||
//
|
||||
// var targetParent = Transform(target).ParentUid;
|
||||
//
|
||||
// var carrier = ent.Comp.Carrier;
|
||||
// if (target != carrier && targetParent != carrier && targetParent != ent.Owner)
|
||||
// args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnMoveAttempt(Entity<BeingCarriedComponent> ent, ref UpdateCanMoveEvent args)
|
||||
|
|
@ -203,8 +206,9 @@ public sealed class CarryingSystem : EntitySystem
|
|||
|
||||
private void OnInteractedWith(Entity<BeingCarriedComponent> ent, ref GettingInteractedWithAttemptEvent args)
|
||||
{
|
||||
if (args.Uid != ent.Comp.Carrier)
|
||||
args.Cancelled = true;
|
||||
// Floofstation - why?
|
||||
// if (args.Uid != ent.Comp.Carrier)
|
||||
// args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnPullAttempt(Entity<BeingCarriedComponent> ent, ref PullAttemptEvent args)
|
||||
|
|
@ -217,6 +221,13 @@ public sealed class CarryingSystem : EntitySystem
|
|||
DropCarried(ent.Comp.Carrier, ent);
|
||||
}
|
||||
|
||||
private void OnBuckle(Entity<BeingCarriedComponent> ent, ref BuckledEvent args)
|
||||
{
|
||||
// Buckling to a bed already handles the reparenting to the entity that the carried
|
||||
// entity is buckled to, and then relays the BuckledEvent, so don't reparent to the grid.
|
||||
DropCarried(ent.Comp.Carrier, ent, attachToGrid: false);
|
||||
}
|
||||
|
||||
private void OnRemoved(Entity<BeingCarriedComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
/*
|
||||
|
|
@ -242,7 +253,8 @@ public sealed class CarryingSystem : EntitySystem
|
|||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void StartCarryDoAfter(EntityUid carrier, Entity<CarriableComponent> carried)
|
||||
// Floofstation - made public
|
||||
public void StartCarryDoAfter(EntityUid carrier, Entity<CarriableComponent> carried)
|
||||
{
|
||||
TimeSpan length = GetPickupDuration(carrier, carried);
|
||||
|
||||
|
|
@ -299,7 +311,8 @@ public sealed class CarryingSystem : EntitySystem
|
|||
|
||||
for (var x = 0; x < Comp<CarriableComponent>(carried).FreeHandsRequired; x++)
|
||||
{
|
||||
_virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
|
||||
if (_virtualItem.TrySpawnVirtualItemInHand(carried, carrier, out var virtualItem))
|
||||
EnsureComp<OfferableVirtualItemComponent>(virtualItem.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,9 +335,9 @@ public sealed class CarryingSystem : EntitySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
public void DropCarried(EntityUid carrier, EntityUid carried)
|
||||
public void DropCarried(EntityUid carrier, EntityUid carried, bool attachToGrid = true)
|
||||
{
|
||||
Drop(carried);
|
||||
Drop(carried, attachToGrid);
|
||||
CleanupCarrier(carrier, carried);
|
||||
}
|
||||
|
||||
|
|
@ -336,12 +349,15 @@ public sealed class CarryingSystem : EntitySystem
|
|||
_movementSpeed.RefreshMovementSpeedModifiers(carrier);
|
||||
}
|
||||
|
||||
private void Drop(EntityUid carried)
|
||||
private void Drop(EntityUid carried, bool attachToGrid = true)
|
||||
{
|
||||
RemComp<BeingCarriedComponent>(carried);
|
||||
RemComp<KnockedDownComponent>(carried); // TODO SHITMED: make sure this doesnt let you make someone with no legs walk
|
||||
_actionBlocker.UpdateCanMove(carried);
|
||||
_transform.AttachToGridOrMap(carried);
|
||||
|
||||
// Some systems will handle re-parenting and then throw an event, and this changes the parent when it should not
|
||||
if (attachToGrid)
|
||||
_transform.AttachToGridOrMap(carried);
|
||||
_standingState.Stand(carried);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using Content.Shared.Item;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Forensics;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DVExpandToInsertedItemSizeComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public string ItemSlot;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<ItemSizePrototype> EmptySize;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using Content.Shared.Item;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared._DV.Forensics;
|
||||
|
||||
public sealed class DVExpandToInsertedItemSizeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVExpandToInsertedItemSizeComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
|
||||
SubscribeLocalEvent<DVExpandToInsertedItemSizeComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
|
||||
}
|
||||
|
||||
private void Refresh(Entity<DVExpandToInsertedItemSizeComponent> ent)
|
||||
{
|
||||
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
|
||||
if (slot.ContainedEntity is { } contained)
|
||||
{
|
||||
var item = Comp<ItemComponent>(contained);
|
||||
_item.SetSize(ent, item.Size);
|
||||
_item.SetShape(ent, item.Shape);
|
||||
}
|
||||
else
|
||||
{
|
||||
_item.SetSize(ent, ent.Comp.EmptySize);
|
||||
_item.SetShape(ent, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemInserted(Entity<DVExpandToInsertedItemSizeComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (ent.Comp.ItemSlot != args.Container.ID)
|
||||
return;
|
||||
|
||||
Refresh(ent);
|
||||
}
|
||||
|
||||
private void OnItemRemoved(Entity<DVExpandToInsertedItemSizeComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (ent.Comp.ItemSlot != args.Container.ID)
|
||||
return;
|
||||
|
||||
Refresh(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._DV.Forensics;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DVItemSlotVisualsComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public string ItemSlot;
|
||||
|
||||
[DataField(required: true)]
|
||||
public SpriteSpecifier FilledSprite;
|
||||
|
||||
[DataField(required: true)]
|
||||
public SpriteSpecifier UnfilledSprite;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Forensics;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DVSeenInsertedItemComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public string ItemSlot;
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.Forensics;
|
||||
|
||||
public sealed class DVSeenInsertedItemSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DVSeenInsertedItemComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<DVSeenInsertedItemComponent, RefreshNameModifiersEvent>(OnRefreshModifiers);
|
||||
SubscribeLocalEvent<DVSeenInsertedItemComponent, EntInsertedIntoContainerMessage>(OnItemInserted);
|
||||
SubscribeLocalEvent<DVSeenInsertedItemComponent, EntRemovedFromContainerMessage>(OnItemRemoved);
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<DVSeenInsertedItemComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
|
||||
if (slot.ContainedEntity is { } contained)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("dv-seen-inserted-item-examined.full", ("container", Identity.Entity(ent, EntityManager, args.Examiner)), ("contained", contained)));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("dv-seen-inserted-item-examined.empty", ("container", Identity.Entity(ent, EntityManager, args.Examiner))));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRefreshModifiers(Entity<DVSeenInsertedItemComponent> ent, ref RefreshNameModifiersEvent args)
|
||||
{
|
||||
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
|
||||
if (slot.ContainedEntity is not { } contained)
|
||||
return;
|
||||
|
||||
args.AddModifier("dv-seen-inserted-item-name-modifier", extraArgs: ("contained", contained));
|
||||
}
|
||||
|
||||
private void Refresh(Entity<DVSeenInsertedItemComponent> ent)
|
||||
{
|
||||
_nameModifier.RefreshNameModifiers(ent.Owner);
|
||||
|
||||
var slot = _container.EnsureContainer<ContainerSlot>(ent, ent.Comp.ItemSlot);
|
||||
if (slot.ContainedEntity is { } contained)
|
||||
{
|
||||
var item = Comp<ItemComponent>(contained);
|
||||
_item.CopyVisuals(ent.Owner, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
_item.ClearVisuals(ent.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemInserted(Entity<DVSeenInsertedItemComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != ent.Comp.ItemSlot || _timing.ApplyingState)
|
||||
return;
|
||||
|
||||
Refresh(ent);
|
||||
}
|
||||
|
||||
private void OnItemRemoved(Entity<DVSeenInsertedItemComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != ent.Comp.ItemSlot || _timing.ApplyingState)
|
||||
return;
|
||||
|
||||
Refresh(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,4 +4,4 @@ namespace Content.Shared._DV.Humanoid;
|
|||
/// Raised on an entity when their appearance is changed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AppearanceLoadedEvent(Color EyeColor);
|
||||
public record struct AppearanceLoadedEvent(Color EyeColor, Color SkinColor);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared.Alert;
|
||||
|
||||
namespace Content.Shared._Floof.OfferItem;
|
||||
|
||||
public sealed partial class AcceptOfferAlertEvent : BaseAlertEvent;
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using Content.Shared.Alert;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Floof.OfferItem;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
[Access(typeof(SharedOfferItemSystem))]
|
||||
public sealed partial class OfferItemComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Apparently this indicates whether the entity is currently choosing an entity to offer (right after pressing F).
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public bool IsInOfferMode;
|
||||
|
||||
/// <summary>
|
||||
/// If this is true, then someone is currently offering an item to this entity, and <see cref="ReceivingFrom"/>
|
||||
/// stores the ID of that entity.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsInReceiveMode;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string? Hand;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? Item;
|
||||
|
||||
/// <summary>
|
||||
/// Floofstation note. So, this is EE shitcode, so prepare for an emotional rollercoaster.
|
||||
/// This field can mean TWO things. It's either the target entity this entity is offering an item to,
|
||||
/// or an entity that is offering an item to this entity.
|
||||
/// Whether it's one or the other is distinguished by <see cref="IsInReceiveMode"/>.<br/><br/>
|
||||
///
|
||||
/// In rare cases it can be both. According to my research, if entity A offers an item to entity B, and entity B offers to entity A,
|
||||
/// then both entities will end up in receive mode, and they will have each other as targets. There's a check preventing offer loops
|
||||
/// of length more than 2.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? ReceivingFrom;
|
||||
|
||||
[DataField]
|
||||
public float MaxOfferDistance = 2f;
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AlertPrototype> OfferAlert = "Offer";
|
||||
|
||||
public EntityUid GetRealEntity(EntityManager entityManager) =>
|
||||
entityManager.GetComponentOrNull<VirtualItemComponent>(Item)?.BlockingEntity ?? Item ?? EntityUid.Invalid;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._Floof.OfferItem;
|
||||
|
||||
/// <summary>
|
||||
/// A marker component that, when applied to a virtual item, allows it to be offered using item offering.
|
||||
/// Implementors have to listen on ItemTransferredEvent.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
public sealed partial class OfferableVirtualItemComponent : Component;
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Shared._Floof.OfferItem;
|
||||
|
||||
public abstract partial class SharedOfferItemSystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
|
||||
private void InitializeInteractions()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.OfferItem, InputCmdHandler.FromDelegate(SetInOfferMode, handle: false, outsidePrediction: false))
|
||||
.Register<SharedOfferItemSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
CommandBinds.Unregister<SharedOfferItemSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This sets IsInOfferMode to true, allowing the player to select whom to offer an item to with interaction.
|
||||
/// </summary>
|
||||
private void SetInOfferMode(ICommonSession? offerer)
|
||||
{
|
||||
if (offerer is not { } playerSession)
|
||||
return;
|
||||
|
||||
if (playerSession.AttachedEntity is not { Valid: true } uid)
|
||||
return;
|
||||
|
||||
if (!Exists(uid))
|
||||
return;
|
||||
|
||||
if (!_actionBlocker.CanInteract(uid, null))
|
||||
return;
|
||||
|
||||
if (!TryComp<OfferItemComponent>(uid, out var offerItem))
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(uid, out var hands))
|
||||
return;
|
||||
|
||||
if (_hands.GetActiveHand((uid, hands)) is not { } activeHandName)
|
||||
return;
|
||||
|
||||
if (!_hands.TryGetHeldItem((uid, hands), activeHandName, out var heldItem))
|
||||
return;
|
||||
|
||||
offerItem.Item = heldItem;
|
||||
if (!offerItem.IsInOfferMode)
|
||||
{
|
||||
if (offerItem.Item == null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("offer-item-empty-hand"), uid, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offerItem.Hand == null || offerItem.ReceivingFrom == null)
|
||||
{
|
||||
offerItem.IsInOfferMode = true;
|
||||
offerItem.Hand = activeHandName;
|
||||
|
||||
Dirty(uid, offerItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're already offering an item to someone, cancel that offer
|
||||
if (offerItem.ReceivingFrom != null)
|
||||
{
|
||||
UnReceive(offerItem.ReceivingFrom.Value, offererComp: offerItem);
|
||||
offerItem.IsInOfferMode = false;
|
||||
Dirty(uid, offerItem);
|
||||
return;
|
||||
}
|
||||
|
||||
UnOffer(uid, offerItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
using Content.Shared._DV.Carrying;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Pulling.Components;
|
||||
using Content.Shared.Movement.Pulling.Systems;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
// Dear contributor.
|
||||
// This system is fucking unmaintainable.
|
||||
// If you ever happen to touch this again, please do your best to document your changes and try to resolve mysteries surrounding this code.
|
||||
// I did what I could to document the parts I managed to understand, but there is still more truth to be unveiled.
|
||||
//
|
||||
// HOURS_WASTED_HERE_FLOOFSTATION = 10
|
||||
// HOURS_WASTED_HERE_DELTAV = 1
|
||||
|
||||
namespace Content.Shared._Floof.OfferItem;
|
||||
|
||||
public abstract partial class SharedOfferItemSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
[Dependency] private readonly CarryingSystem _carrying = default!;
|
||||
[Dependency] private readonly PullingSystem _pulling = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<OfferItemComponent, AcceptOfferAlertEvent>(OnAcceptOffer);
|
||||
SubscribeLocalEvent<OfferItemComponent, InteractUsingEvent>(OnInteractWithReceiver, before: [typeof(IngestionSystem)]);
|
||||
SubscribeLocalEvent<OfferableVirtualItemComponent, BeforeRangedInteractEvent>(OnRangedInteractWithReceiver);
|
||||
SubscribeLocalEvent<OfferItemComponent, MoveEvent>(OnMove);
|
||||
|
||||
SubscribeLocalEvent<BeingCarriedComponent, ItemTransferredEvent>(OnCarryTransfer);
|
||||
SubscribeLocalEvent<PullableComponent, ItemTransferredEvent>(OnPulledTransfer);
|
||||
|
||||
InitializeInteractions();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<OfferItemComponent, HandsComponent>();
|
||||
while (query.MoveNext(out var uid, out var offerItem, out var hands))
|
||||
{
|
||||
// If the mob no longer holds an item in the original offering hand, clear offering mode
|
||||
if (offerItem.Hand != null && !_hands.TryGetHeldItem((uid, hands), offerItem.Hand, out _))
|
||||
{
|
||||
if (offerItem.ReceivingFrom != null)
|
||||
{
|
||||
UnReceive(offerItem.ReceivingFrom.Value, offererComp: offerItem);
|
||||
offerItem.IsInOfferMode = false;
|
||||
Dirty(uid, offerItem);
|
||||
}
|
||||
else
|
||||
UnOffer(uid, offerItem);
|
||||
}
|
||||
|
||||
if (!offerItem.IsInReceiveMode)
|
||||
{
|
||||
_alertsSystem.ClearAlert(uid, offerItem.OfferAlert);
|
||||
continue;
|
||||
}
|
||||
|
||||
_alertsSystem.ShowAlert(uid, offerItem.OfferAlert);
|
||||
}
|
||||
}
|
||||
|
||||
#region Events
|
||||
private void OnAcceptOffer(Entity<OfferItemComponent> ent, ref AcceptOfferAlertEvent args)
|
||||
{
|
||||
Receive((ent, ent.Comp));
|
||||
}
|
||||
|
||||
private void OnInteractWithReceiver(Entity<OfferItemComponent> receiver, ref InteractUsingEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted || _timing.ApplyingState || args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<OfferItemComponent>(args.User, out var offererComponent))
|
||||
return;
|
||||
|
||||
args.Handled = CreateOffer(receiver, (args.User, offererComponent));
|
||||
}
|
||||
|
||||
private void OnRangedInteractWithReceiver(Entity<OfferableVirtualItemComponent> virtItem, ref BeforeRangedInteractEvent args)
|
||||
{
|
||||
// If the entity being offered is a virtual item, InteractUsing will not be raised
|
||||
// because virtual items exclude themselves from being marked as used
|
||||
// If this is the case, InteractHand will be raised instead, which we can use anyway because OfferItem.Item stores the offered item
|
||||
//
|
||||
// We also can't check Handled here because VirtualItemSystem handles it, ffs
|
||||
// This won't lead you to accidentally offering someone your gun
|
||||
//
|
||||
// This is shitcode, this time my shitcode. My changes to the offering system allow you to transfer carrying and pulling,
|
||||
// but in order to handle these, we need to be able to intercept interactions with virtual items.
|
||||
//
|
||||
// Ideally this code should be rewritten to:
|
||||
// a) Have each different virtual item have a distinct component (e.g. CarryingVirtualItem) which would allow to distinguish them from the rest
|
||||
// b) Not rely on the InteractionSystem.
|
||||
// However, I'm not in the mood to do either. And I'm too deep into the rabbit hole of getting this shit to work.
|
||||
if (!_timing.IsFirstTimePredicted || _timing.ApplyingState)
|
||||
return;
|
||||
|
||||
var receiver = args.Target;
|
||||
if (!TryComp<OfferItemComponent>(receiver, out var receiverComponent))
|
||||
return;
|
||||
|
||||
var offerer = args.User;
|
||||
if (!TryComp<OfferItemComponent>(offerer, out var offererComponent) || offererComponent.Item == null)
|
||||
return;
|
||||
|
||||
// Since this is ranged, we must also check distance, because the interaction system wont check it for us in this case
|
||||
if (!Transform(offerer).Coordinates.TryDistance(EntityManager, _transform, Transform(receiver.Value).Coordinates, out var dst)
|
||||
|| dst > offererComponent.MaxOfferDistance)
|
||||
return;
|
||||
|
||||
args.Handled = CreateOffer((receiver.Value, receiverComponent), (offerer, offererComponent));
|
||||
}
|
||||
|
||||
private void OnMove(EntityUid uid, OfferItemComponent component, MoveEvent args)
|
||||
{
|
||||
if (_net.IsClient) // Client often mispredicts movement, we cant trust it here
|
||||
return;
|
||||
|
||||
if (component.ReceivingFrom == null)
|
||||
return;
|
||||
|
||||
if (_transform.InRange(args.NewPosition, Transform(component.ReceivingFrom.Value).Coordinates, component.MaxOfferDistance))
|
||||
return;
|
||||
|
||||
UnOffer(uid, component);
|
||||
}
|
||||
|
||||
private void OnCarryTransfer(Entity<BeingCarriedComponent> ent, ref ItemTransferredEvent args)
|
||||
{
|
||||
if (args.Handled
|
||||
|| args.PassedItem == args.RealItem // Means the entity is transferred NOT via carrying
|
||||
|| args.RealItem is not { Valid: true } carried
|
||||
|| ent.Comp.Carrier is not { Valid: true } oldCarrier)
|
||||
return;
|
||||
|
||||
_carrying.DropCarried(oldCarrier, ent);
|
||||
args.Handled = _carrying.TryCarry(args.Target, carried);
|
||||
}
|
||||
|
||||
private void OnPulledTransfer(Entity<PullableComponent> ent, ref ItemTransferredEvent args)
|
||||
{
|
||||
if (args.Handled
|
||||
|| args.PassedItem == args.RealItem // Means the entity is transferred NOT via pulling
|
||||
|| args.RealItem is not { Valid: true } pulled)
|
||||
return;
|
||||
|
||||
_pulling.TryStopPull(pulled, ent);
|
||||
args.Handled = _pulling.TryStartPull(args.Target, ent, null, ent.Comp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Offering / Recieving
|
||||
/// <summary>
|
||||
/// Attempts to create an offer. Expects offerer.Item to already be set to the offered item, offererComponent.InReceiveMode == true.
|
||||
/// Will fail if offerer == receiver or if receiver already has a set TargetOrOfferer, and that person is not the current offerer
|
||||
/// </summary>
|
||||
private bool CreateOffer(Entity<OfferItemComponent> receiver, Entity<OfferItemComponent> offerer)
|
||||
{
|
||||
var offererComponent = offerer.Comp;
|
||||
var receiverComponent = receiver.Comp;
|
||||
if (offerer == receiver || receiverComponent.IsInReceiveMode || !offererComponent.IsInOfferMode)
|
||||
return false;
|
||||
|
||||
if (offererComponent.IsInReceiveMode && offererComponent.ReceivingFrom != receiver)
|
||||
return false;
|
||||
|
||||
receiverComponent.IsInReceiveMode = true;
|
||||
receiverComponent.ReceivingFrom = offerer;
|
||||
|
||||
Dirty(receiver, receiverComponent);
|
||||
|
||||
offererComponent.ReceivingFrom = receiver; // TODO this is ee shitcode, may not be necessary?
|
||||
offererComponent.IsInOfferMode = false;
|
||||
|
||||
Dirty(offerer, offererComponent);
|
||||
|
||||
if (offererComponent.Item == null)
|
||||
return false;
|
||||
|
||||
// Sender popup (client-side only)
|
||||
_popup.PopupClient(
|
||||
Loc.GetString("offer-item-try-give",
|
||||
("item", Identity.Entity(offererComponent.GetRealEntity(EntityManager), EntityManager)),
|
||||
("target", Identity.Entity(receiver, EntityManager))),
|
||||
offerer,
|
||||
offerer);
|
||||
// Receiver popup (server side only, not predicted because recipient != local player)
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-try-give-target",
|
||||
("user", Identity.Entity(receiverComponent.ReceivingFrom.Value, EntityManager)),
|
||||
("item", Identity.Entity(offererComponent.GetRealEntity(EntityManager), EntityManager))),
|
||||
offerer,
|
||||
receiver,
|
||||
Popups.PopupType.Medium);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resets the <see cref="OfferItemComponent"/> of the user and the target
|
||||
/// </summary>
|
||||
protected void UnOffer(EntityUid thisEntity, OfferItemComponent offererComp)
|
||||
{
|
||||
if (!TryComp<HandsComponent>(thisEntity, out var hands) || _hands.GetActiveHand((thisEntity, hands)) is null)
|
||||
return;
|
||||
|
||||
if (offererComp.ReceivingFrom is { } otherEntity && TryComp<OfferItemComponent>(otherEntity, out var otherOfferer))
|
||||
{
|
||||
// So this tries to figure out which of these entities do what...
|
||||
// if A.OfferItemComponent.Item != null, then A is currently offering an item to A.OfferItemComponent.TargetOrOfferer
|
||||
// If it is null, then it is ONLY being offered an item TO.
|
||||
if (offererComp.Item != null && _net.IsServer)
|
||||
{
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-no-give",
|
||||
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
|
||||
("target", Identity.Entity(otherEntity, EntityManager))),
|
||||
thisEntity,
|
||||
thisEntity);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-no-give-target",
|
||||
("user", Identity.Entity(thisEntity, EntityManager)),
|
||||
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager))),
|
||||
thisEntity,
|
||||
otherEntity);
|
||||
}
|
||||
|
||||
else if (otherOfferer.Item != null && _net.IsServer)
|
||||
{
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-no-give",
|
||||
("item", Identity.Entity(otherOfferer.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
|
||||
("target", Identity.Entity(thisEntity, EntityManager))),
|
||||
otherEntity,
|
||||
otherEntity);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-no-give-target",
|
||||
("user", Identity.Entity(otherEntity, EntityManager)),
|
||||
("item", Identity.Entity(otherOfferer.GetRealEntity(EntityManager), EntityManager))),
|
||||
otherEntity,
|
||||
thisEntity);
|
||||
}
|
||||
|
||||
otherOfferer.IsInOfferMode = false;
|
||||
otherOfferer.IsInReceiveMode = false;
|
||||
otherOfferer.Hand = null;
|
||||
otherOfferer.ReceivingFrom = null;
|
||||
otherOfferer.Item = null;
|
||||
|
||||
Dirty(otherEntity, otherOfferer);
|
||||
}
|
||||
|
||||
offererComp.IsInOfferMode = false;
|
||||
offererComp.IsInReceiveMode = false;
|
||||
offererComp.Hand = null;
|
||||
offererComp.ReceivingFrom = null;
|
||||
offererComp.Item = null;
|
||||
|
||||
Dirty(thisEntity, offererComp);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the transfer of the item
|
||||
/// </summary>
|
||||
protected void UnReceive(EntityUid receiver, OfferItemComponent? receiverComp = null, OfferItemComponent? offererComp = null)
|
||||
{
|
||||
if (!Resolve(receiver, ref receiverComp)
|
||||
|| receiverComp.ReceivingFrom is not {} offerer
|
||||
|| !Resolve(offerer, ref offererComp))
|
||||
return;
|
||||
|
||||
// Idk why this check is here
|
||||
if (!TryComp<HandsComponent>(receiver, out var hands) || _hands.GetActiveHand((receiver, hands)) == null || receiverComp.ReceivingFrom == null)
|
||||
return;
|
||||
|
||||
// If offererComp.Item != null, then they are actively offering to TargetOrOfferer
|
||||
// Normally this method is called right after a transfer is done, but this part can be called from SetInOfferMode when the player presses F again to cancel an ongoing offer
|
||||
if (offererComp.Item != null)
|
||||
{
|
||||
_popup.PopupClient(
|
||||
Loc.GetString("offer-item-no-give",
|
||||
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager)), // Floof - resolve virtual items
|
||||
("target", Identity.Entity(receiver, EntityManager))),
|
||||
offerer,
|
||||
offerer);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-no-give-target",
|
||||
("user", Identity.Entity(receiverComp.ReceivingFrom.Value, EntityManager)), // Floof - resolve virtual items
|
||||
("item", Identity.Entity(offererComp.GetRealEntity(EntityManager), EntityManager))),
|
||||
offerer,
|
||||
receiver);
|
||||
}
|
||||
|
||||
if (!offererComp.IsInReceiveMode)
|
||||
{
|
||||
offererComp.ReceivingFrom = null;
|
||||
receiverComp.ReceivingFrom = null;
|
||||
}
|
||||
|
||||
offererComp.Item = null;
|
||||
offererComp.Hand = null;
|
||||
receiverComp.IsInReceiveMode = false;
|
||||
|
||||
Dirty(receiver, receiverComp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accepting the offer and receive item
|
||||
/// </summary>
|
||||
public void Receive(Entity<OfferItemComponent?> receiver)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (!Resolve(receiver, ref receiver.Comp))
|
||||
return;
|
||||
|
||||
if (!TryComp<OfferItemComponent>(receiver.Comp.ReceivingFrom, out var offererComponent) ||
|
||||
offererComponent.Hand == null ||
|
||||
receiver.Comp.ReceivingFrom is not {} sender ||
|
||||
!TryComp<HandsComponent>(receiver, out var hands))
|
||||
return;
|
||||
|
||||
if (offererComponent.Item != null)
|
||||
{
|
||||
// Floof - check if there's something else handling it first
|
||||
var realItem = offererComponent.GetRealEntity(EntityManager);
|
||||
if (!TryHandleExtendedTransfer(sender, receiver, offererComponent.Item.Value, realItem)
|
||||
&& !_hands.TryPickup(receiver, offererComponent.Item.Value, handsComp: hands))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("offer-item-full-hand"), receiver, receiver);
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-give",
|
||||
("item", Identity.Entity(realItem, EntityManager)), // FLoof - resolve virtual items
|
||||
("target", Identity.Entity(receiver, EntityManager))),
|
||||
sender,
|
||||
sender);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("offer-item-give-other",
|
||||
("user", Identity.Entity(receiver.Comp.ReceivingFrom.Value, EntityManager)),
|
||||
("item", Identity.Entity(realItem, EntityManager)), // FLoof - resolve virtual items
|
||||
("target", Identity.Entity(receiver, EntityManager))),
|
||||
sender,
|
||||
Filter.PvsExcept(sender, entityManager: EntityManager),
|
||||
true);
|
||||
}
|
||||
|
||||
offererComponent.Item = null;
|
||||
UnReceive(receiver, receiver.Comp, offererComponent);
|
||||
}
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Returns true if <see cref="OfferItemComponent.IsInOfferMode"/> = true
|
||||
/// </summary>
|
||||
protected bool IsInOfferMode(Entity<OfferItemComponent?> ent)
|
||||
{
|
||||
return Resolve(ent, ref ent.Comp, false) && ent.Comp.IsInOfferMode;
|
||||
}
|
||||
|
||||
private bool TryHandleExtendedTransfer(EntityUid user, EntityUid target, EntityUid offeredItem, EntityUid realItem)
|
||||
{
|
||||
var ev = new ItemTransferredEvent
|
||||
{
|
||||
User = user,
|
||||
Target = target,
|
||||
PassedItem = offeredItem,
|
||||
RealItem = realItem,
|
||||
};
|
||||
RaiseLocalEvent(realItem, ref ev);
|
||||
return ev.Handled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the entity that was transferred via item offering.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed class ItemTransferredEvent : HandledEntityEventArgs
|
||||
{
|
||||
public EntityUid User;
|
||||
public EntityUid Target;
|
||||
|
||||
/// <summary>
|
||||
/// The actual item being passed around. Can be a virtual item.
|
||||
/// </summary>
|
||||
public EntityUid PassedItem;
|
||||
/// <summary>
|
||||
/// If <see cref="PassedItem"/> is a virtual item, this field contains the real item that was transferred.
|
||||
/// </summary>
|
||||
public EntityUid? RealItem;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._Impstation.Clothing;
|
||||
|
||||
/// <summary>
|
||||
/// Adds examine text to the entity that wears item, for making things obvious.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(WearerGetsExamineTextSystem))]
|
||||
public sealed partial class WearerGetsExamineTextComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The LocId that specifies what category of object this is.
|
||||
/// i.e. "pin" or "scarf"
|
||||
/// Should be redefined on a per-category basis, naturally.
|
||||
/// </summary>
|
||||
[DataField("thing")]
|
||||
public LocId Category = "obvious-thing-default";
|
||||
|
||||
/// <summary>
|
||||
/// The LocId that specifies what member of the category this is.
|
||||
/// i.e. "lesbian pride"
|
||||
/// Can be used to define text colors that are copied to all things
|
||||
/// which share this specifier (i.e. the other items of the same pride).
|
||||
/// (And summarily, makes accessibility-based changes for these colors a cinch.)
|
||||
/// Should be defined by each thing that has this component.
|
||||
/// </summary>
|
||||
[DataField("thingType")]
|
||||
public LocId Specifier = "obvious-type-default";
|
||||
|
||||
/// <summary>
|
||||
/// The LocId that will be added to any wearing entity's examination.
|
||||
/// Typically only needs redefining on a per-category basis,
|
||||
/// but items that should have totally-unique obvious text can simply specify them here.
|
||||
/// </summary>
|
||||
[DataField("examineText", required: true)]
|
||||
public LocId ExamineOnWearer = "obvious-desc-default";
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the entity wearing this clothing.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? Wearer;
|
||||
/// <summary>
|
||||
/// The string that is attached to this item's ExamineOnWearer.
|
||||
/// Typically doesn't need to be redefined.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId PrefixExamineOnWearer = "obvious-prefix-wearing";
|
||||
|
||||
/// <summary>
|
||||
/// If true, an entity with this item in any slot (i.e. in pockets) will gain the examine text,
|
||||
/// instead of when just equipped as clothing.
|
||||
/// Should be used sparingly only when truly appropriate; this is effectively a half-measure for lack of a special pin slot.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PocketEvident;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the entity's description will inform examiners what others will see on the wearer (before they equip it).
|
||||
/// If the item is contraband, the item will also warn that displaying it may cause undue attention.
|
||||
/// Keep this false for good-natured jokes (i.e. the pride cloaks having funny, non-pride names)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool WarnExamine = true;
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Contraband;
|
||||
using Content.Shared._Impstation.Examine;
|
||||
using System.Text;
|
||||
|
||||
namespace Content.Shared._Impstation.Clothing;
|
||||
|
||||
/// <summary>
|
||||
/// Adds examine text to the entity that wears item, for making things obvious.
|
||||
/// </summary>
|
||||
public sealed class WearerGetsExamineTextSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WearerGetsExamineTextComponent, GotEquippedEvent>(OnEquipped);
|
||||
SubscribeLocalEvent<WearerGetsExamineTextComponent, GotUnequippedEvent>(OnUnequipped);
|
||||
SubscribeLocalEvent<WearerGetsExamineTextComponent, ExaminedEvent>(OnExamine);
|
||||
}
|
||||
|
||||
private void OnEquipped(Entity<WearerGetsExamineTextComponent> entity, ref GotEquippedEvent args)
|
||||
{
|
||||
if (!TryComp(entity, out ClothingComponent? clothing))
|
||||
return;
|
||||
var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
|
||||
if (!entity.Comp.PocketEvident) //if it can't be evident in our pockets
|
||||
{
|
||||
// Make sure the clothing item was equipped to the right slot, and not just held in a hand.
|
||||
if (!isCorrectSlot)
|
||||
return;
|
||||
}
|
||||
|
||||
entity.Comp.Wearer = args.Equipee;
|
||||
Dirty(entity);
|
||||
|
||||
//GIVE THEM INSPECT TEXT
|
||||
var obviousExamine = EnsureComp<ExtraExamineTextComponent>(args.Equipee);
|
||||
obviousExamine.Lines.TryAdd(entity.Owner, //using try so that we don't cause an error if we move something from slot to slot
|
||||
ConstructExamineText(entity, !isCorrectSlot, args.Equipee));
|
||||
}
|
||||
|
||||
|
||||
private string ConstructExamineText(Entity<WearerGetsExamineTextComponent> entity, bool prefixFallback, EntityUid affecting)
|
||||
{
|
||||
//parameters (these are the same between both constructions)
|
||||
var user = Identity.Entity(affecting, EntityManager);
|
||||
var nomen = Identity.Name(affecting, EntityManager);
|
||||
var thing = Loc.GetString(entity.Comp.Category);
|
||||
var type = Loc.GetString(entity.Comp.Specifier);
|
||||
var stringSpec = entity.Comp.Specifier.ToString();
|
||||
var shortType = stringSpec.Substring(stringSpec.LastIndexOf('-')); // necessary for working with colored text...
|
||||
|
||||
var prefix = Loc.GetString(prefixFallback ? "obvious-prefix-default" : entity.Comp.PrefixExamineOnWearer, // uses a different prefix if worn / displayed
|
||||
("user", user),
|
||||
("name", nomen),
|
||||
("thing", thing),
|
||||
("type", type));
|
||||
var suffix = Loc.GetString(entity.Comp.ExamineOnWearer,
|
||||
("user", user),
|
||||
("name", nomen),
|
||||
("thing", thing),
|
||||
("type", type),
|
||||
("short-type", shortType));
|
||||
return prefix + " " + suffix;
|
||||
}
|
||||
|
||||
private void OnUnequipped(Entity<WearerGetsExamineTextComponent> entity, ref GotUnequippedEvent args)
|
||||
{
|
||||
if (entity.Comp.Wearer is not { } wearer)
|
||||
return;
|
||||
|
||||
if (TryComp(wearer, out ExtraExamineTextComponent? obviousExamine))
|
||||
{
|
||||
obviousExamine.Lines.Remove(entity.Owner);
|
||||
}
|
||||
|
||||
entity.Comp.Wearer = null;
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<WearerGetsExamineTextComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
var currentlyWorn = entity.Comp.Wearer != null;
|
||||
var outString = new StringBuilder(Loc.GetString(currentlyWorn ? "obvious-on-item-currently" : "obvious-on-item",
|
||||
("used", Loc.GetString(entity.Comp.PocketEvident ? "obvious-reveal-pockets" : "obvious-reveal-default")),
|
||||
("thing", entity.Comp.Category),
|
||||
("me", Identity.Entity(entity, EntityManager))));
|
||||
|
||||
if (entity.Comp.WarnExamine)
|
||||
{
|
||||
if (!currentlyWorn && TryComp(entity, out ContrabandComponent? contra)) // if the item's contra and we're not wearing it yet
|
||||
{
|
||||
var contraLocId = "obvious-on-item-contra-" + contra.Severity; // apply additional text if the item is contraband to note that displaying it might be really bad
|
||||
if (Loc.HasString(contraLocId)) // saves us the trouble of making a switch block for this
|
||||
outString.Append(" " + Loc.GetString(contraLocId));
|
||||
}
|
||||
var affecting = currentlyWorn ? entity.Comp.Wearer.GetValueOrDefault() : args.Examiner;
|
||||
var testOut = ConstructExamineText(entity, false, affecting);
|
||||
|
||||
outString.Append("\n" + Loc.GetString("obvious-on-item-for-others",
|
||||
("will", currentlyWorn ? "can" : "will"), // i love hardcoding strings it's my favorite thing ever
|
||||
("output", testOut)));
|
||||
}
|
||||
|
||||
args.PushMarkup(outString.ToString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._Impstation.Examine;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DetailedInspectComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public LocId VerbText = "verbs-detailed-inspect";
|
||||
|
||||
[DataField]
|
||||
public LocId VerbMessage = "verbs-detailed-inspect-message";
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<LocId> ExamineText;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the entries in ExamineText are separated by linebreaks.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool LineBreak = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the entries in ExamineText are preceded by ticks.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool TickEntries = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not entries in the list are numbered.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool NumberedEntries = false;
|
||||
|
||||
/// <summary>
|
||||
/// Rooted directory of the icon for the verb.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Icon = "/Textures/Interface/VerbIcons/dot.svg.192dpi.png";
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._Impstation.Examine;
|
||||
|
||||
public sealed partial class DetailedInspectSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DetailedInspectComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerb);
|
||||
}
|
||||
|
||||
public void OnGetVerb(Entity<DetailedInspectComponent> ent, ref GetVerbsEvent<ExamineVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || !args.CanAccess || !_net.IsServer)
|
||||
return;
|
||||
|
||||
var msg = new FormattedMessage();
|
||||
var numberedIndex = 1;
|
||||
|
||||
foreach (var locId in ent.Comp.ExamineText)
|
||||
{
|
||||
if (ent.Comp.TickEntries)
|
||||
msg.AddMarkupOrThrow("- ");
|
||||
|
||||
if (ent.Comp.NumberedEntries)
|
||||
{
|
||||
msg.AddMarkupOrThrow($"{numberedIndex}. ");
|
||||
numberedIndex++;
|
||||
}
|
||||
|
||||
msg.AddMarkupOrThrow(Loc.GetString(locId));
|
||||
|
||||
if (ent.Comp.LineBreak)
|
||||
msg.PushNewline();
|
||||
else
|
||||
msg.AddMarkupOrThrow(" ");
|
||||
}
|
||||
|
||||
|
||||
_examine.AddDetailedExamineVerb(args, ent.Comp, msg, Loc.GetString(ent.Comp.VerbText), ent.Comp.Icon, Loc.GetString(ent.Comp.VerbMessage));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Robust.Shared.GameStates;
|
||||
using Content.Shared._Impstation.Clothing;
|
||||
|
||||
namespace Content.Shared._Impstation.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Adds examine text to the entity, intentionally "obvious details".
|
||||
/// Like, that's it. It's basic -- all it does is add the lines to the attached entity.
|
||||
/// This is particularly used for assigning players unique examine text.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(ExtraExamineTextSystem), typeof(WearerGetsExamineTextSystem))]
|
||||
public sealed partial class ExtraExamineTextComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The LocIds that will be added to the attached entity's examination.
|
||||
///
|
||||
/// The key is the source of the line, and the value is the text
|
||||
/// (built by the component that creates this one).
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Dictionary<EntityUid, LocId> Lines { get; set; } = new();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.IdentityManagement;
|
||||
|
||||
namespace Content.Shared._Impstation.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Adds examine text to the entity, intentionally "obvious details".
|
||||
/// Like, that's it. It's basic -- all it does is add the line to the attached entity.
|
||||
/// This is particularly used for assigning players unique examine text.
|
||||
/// </summary>
|
||||
public sealed class ExtraExamineTextSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExtraExamineTextComponent, ExaminedEvent>(OnExamine);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<ExtraExamineTextComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (entity.Comp.Lines.Count == 0)
|
||||
{
|
||||
RemCompDeferred<ExtraExamineTextComponent>(entity); // no more need for me!
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var l in entity.Comp.Lines)
|
||||
{
|
||||
args.PushMarkup(l.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Content.Shared._Starlight.Flash.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class FlashModifierComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float Modifier = 1f;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
namespace Content.Shared.Humanoid;
|
||||
|
||||
public static class EyeColor
|
||||
{
|
||||
public const float ShadekinBrightness = 0.251f;
|
||||
|
||||
public static bool VerifyShadekin(Color color)
|
||||
{
|
||||
var colorHsv = Color.ToHsv(color);
|
||||
|
||||
if (colorHsv.Z > ShadekinBrightness)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Color MakeShadekinValid(Color color)
|
||||
{
|
||||
var hsv = Color.ToHsv(color);
|
||||
|
||||
hsv.Z = Math.Clamp(hsv.Z, 0, ShadekinBrightness);
|
||||
|
||||
return Color.FromHsv(hsv);
|
||||
}
|
||||
|
||||
public static bool VerifyEyeColor(HumanoidEyeColor type, Color color)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
HumanoidEyeColor.Shadekin => VerifyShadekin(color),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static Color ValidEyeColor(HumanoidEyeColor type, Color color)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
HumanoidEyeColor.Shadekin => MakeShadekinValid(color),
|
||||
_ => color
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum HumanoidEyeColor : byte
|
||||
{
|
||||
Shadekin,
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct EyeColorInitEvent();
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using Content.Shared.Alert;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Starlight;
|
||||
|
||||
#region Shadekin
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause, AutoGenerateComponentState]
|
||||
public sealed partial class ShadekinComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public ProtoId<AlertPrototype> ShadekinAlert = "Shadekin";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly), AutoPausedField]
|
||||
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan UpdateCooldown = TimeSpan.FromSeconds(1f);
|
||||
|
||||
[AutoNetworkedField, ViewVariables]
|
||||
public ShadekinState CurrentState { get; set; } = ShadekinState.Dark;
|
||||
|
||||
[DataField("thresholds", required: true)]
|
||||
public SortedDictionary<FixedPoint2, ShadekinState> Thresholds = new();
|
||||
|
||||
/// <summary>
|
||||
/// whether to flicker lights or not. default on
|
||||
/// </summary>
|
||||
[DataField] public bool DoLightFlicker = true;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ShadekinState : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
Dark = 1,
|
||||
Low = 2,
|
||||
Annoying = 3,
|
||||
High = 4,
|
||||
Extreme = 5
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
- files: ["shadowkin_female_scream.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Edited and composited by MajorMoth. Re-encoded to Ogg Vorbis."
|
||||
source: "https://freesound.org/people/AlesiaDavina/sounds/722718/"
|
||||
|
||||
- files: ["shadowkin_male_scream.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Edited and composited by MajorMoth. Re-encoded to Ogg Vorbis."
|
||||
source: "https://freesound.org/people/AlesiaDavina/sounds/722718/"
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,14 @@
|
|||
- files: ["shadowkin_speak_say.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Edited and composited by MajorMoth. Re-encoded to Ogg Vorbis."
|
||||
source: "https://freesound.org/people/JKata/sounds/632414/"
|
||||
|
||||
- files: ["shadowkin_speak_ask.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Edited and composited by MajorMoth. Re-encoded to Ogg Vorbis."
|
||||
source: "https://freesound.org/people/templeofhades/sounds/95516/"
|
||||
|
||||
- files: ["shadowkin_speak_exclaim.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Edited and composited by MajorMoth. Re-encoded to Ogg Vorbis."
|
||||
source: "https://freesound.org/people/taure/sounds/376392/"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
- files: ["beep_500.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "Synthesized in Audacity by BasedUser."
|
||||
source: "https://github.com/ekrixi-14/ekrixi"
|
||||
|
||||
- files: ["whirr1.ogg", "whirr2.ogg", "whirr3.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "Taken from source, spectrally modified and clipped"
|
||||
source: "https://freesound.org/people/sad3d/sounds/500168/"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue