Offbrand medical (#3366)
* Offbrand medical * what if we regrade * zombies are mostly there thats it thats a wrap xd * here's changeling * some bonus gut punches * start working on the guidebook * fix rsi and yaml lints * my agrichem so fits * we stay rejuvenated * my china so laked * debrute * fix suicide * fix the suicide tests * my surgery so requires laying down * the guidebook continues * READ KEB PAGES * keb vascular recoupler * read keb medicine * fix yaml lint * fix the EntityRemoveConstructionGraphStep * fix overlay init * scalpels are not a food tool * return of the programmer art * my line so nieuw * boxes deserve veins too * mrrrp yaml * what if we redid brain damage alerts * bloot pressure * kill mannitol drowsiness * get licensed * my read so me * get feedbacked nerd * fine-tune the heart stoppage conditions * cryostasis adjustments, guidebook adjustments, fix negative strain issues * my surgery so table * fix heart surgery and guidebook * medicine & defibrillator pass * iv bags and stands * prefills * janet gets very sidetracked * mostly finished iv stuff * what if we fixed the guidebook * halve decapoid cryostasis * my medicines so IV * finetune cryostasis * less logspam * metabolism-aware iv stands and cryopods * give people painkillers * yaml lint real * fix blood build * finish rebase * tidy up localization * clean up my yaml beasties... * soft curve after exceeding maximum damage * husks/bonedeaths Grabbag of Offmed fixes & improvements (#3461) * CPR moment * Mob AI fix * Fix brain oxygenation not updating on regeneration * sorry gamers you cannot resist the pull * Troll combat abilities more in softcrit praying rn (#3467) dont have CPR be 50% (#3468) Make offbrand murder easier to contend with (#3473) * e * disrupt people in softcrit when attacking them * ok gamers we're gaming * forgor Hopefully final pass before Offbrand merge (#3475) First pass of Offbrand adjustments (#3477) Swap blood pressure values in health analyzer (#3476) Systolic over diastolic Co-authored-by: Kip <32859367+kipdotnet@users.noreply.github.com> Offbrand pass 2: Mostly bugfixes (#3480) Fix zeds causing PVS reloads (#3482) Offbrand pass 3: I hate surgery I hate surgery I hate surgery I (#3481) * set up surgery ui * test fail real Pain/braingasps (#3487) Offmed bundle 5 - the evil one (#3489) * Evil cavity surgery * les borgues * nicotine moment * epinephrine RNG * legalese * test fail real * ok jamers cope with c4 Pass 6
This commit is contained in:
committed by
Janet Blackquill
parent
398c8df343
commit
8894eca118
@@ -1,64 +1,94 @@
|
||||
<!-- offbrand completely redid this file, use offbrand's version if there are merge conflicts -->
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MaxHeight="525"
|
||||
MinWidth="300">
|
||||
MaxHeight="725"
|
||||
MinWidth="600">
|
||||
<ScrollContainer
|
||||
Margin="5 5 5 5"
|
||||
ReturnMeasure="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RootContainer"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical">
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}" />
|
||||
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer
|
||||
Name="PatientDataContainer"
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
|
||||
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64" />
|
||||
<TextureRect Name="NoDataTex" Access="Public" SetSize="64 64" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/>
|
||||
<BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
|
||||
<RichTextLabel Name="NameLabel" SetWidth="150" />
|
||||
<Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />
|
||||
Name="LeftContainer"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical"
|
||||
MinWidth="300">
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}" />
|
||||
|
||||
<BoxContainer
|
||||
Name="PatientDataContainer"
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
|
||||
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64" />
|
||||
<TextureRect Name="NoDataTex" Access="Public" SetSize="64 64" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/>
|
||||
<BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
|
||||
<RichTextLabel Name="NameLabel" SetWidth="150" />
|
||||
<Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />
|
||||
</BoxContainer>
|
||||
<Label Margin="0 0 5 0" HorizontalExpand="True" HorizontalAlignment="Right" VerticalExpand="True"
|
||||
VerticalAlignment="Top" Name="ScanModeLabel"
|
||||
Text="{Loc 'health-analyzer-window-entity-unknown-text'}" />
|
||||
</BoxContainer>
|
||||
<Label Margin="0 0 5 0" HorizontalExpand="True" HorizontalAlignment="Right" VerticalExpand="True"
|
||||
VerticalAlignment="Top" Name="ScanModeLabel"
|
||||
Text="{Loc 'health-analyzer-window-entity-unknown-text'}" />
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<GridContainer Margin="0 5 0 0" Columns="2">
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-status-text'}" />
|
||||
<RichTextLabel Name="StatusLabel" />
|
||||
<Label Name="BrainHealthText" Text="{Loc 'health-analyzer-window-entity-brain-health-text'}" />
|
||||
<RichTextLabel Name="BrainHealthLabel" />
|
||||
<Label Name="BloodPressureText" Text="{Loc 'health-analyzer-window-entity-blood-pressure-text'}" />
|
||||
<RichTextLabel Name="BloodPressureLabel" />
|
||||
<Label Name="BloodOxygenationText" Text="{Loc 'health-analyzer-window-entity-blood-oxygenation-text'}" />
|
||||
<RichTextLabel Name="BloodOxygenationLabel" />
|
||||
<Label Name="BloodFlowText" Text="{Loc 'health-analyzer-window-entity-blood-flow-text'}" />
|
||||
<RichTextLabel Name="BloodFlowLabel" />
|
||||
<Label Name="HeartRateText" Text="{Loc 'health-analyzer-window-entity-heart-rate-text'}" />
|
||||
<RichTextLabel Name="HeartRateLabel" />
|
||||
<Label Name="HeartHealthText" Text="{Loc 'health-analyzer-window-entity-heart-health-text'}" />
|
||||
<RichTextLabel Name="HeartHealthLabel" />
|
||||
<Label Name="BloodText" Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" />
|
||||
<RichTextLabel Name="BloodLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" />
|
||||
<RichTextLabel Name="TemperatureLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" />
|
||||
<RichTextLabel Name="DamageLabel" />
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<PanelContainer Name="ReagentsDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<GridContainer Margin="0 5 0 0" Columns="2">
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-status-text'}" />
|
||||
<Label Name="StatusLabel" />
|
||||
<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" />
|
||||
</GridContainer>
|
||||
<BoxContainer Name="ReagentsContainer" Visible="False" Margin="0 5" Orientation="Vertical" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="AlertsDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer Name="AlertsContainer" Visible="False" Margin="0 5" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<PanelContainer StyleClasses="LowDivider" VerticalExpand="True" Margin="0 0 0 0" SetWidth="2" />
|
||||
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Margin="0 5 0 5"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
Name="RightContainer"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical"
|
||||
MinWidth="300">
|
||||
|
||||
<BoxContainer Name="AlertsContainer" Visible="False" Margin="0 5" Orientation="Vertical" HorizontalAlignment="Center"/>
|
||||
|
||||
<PanelContainer Name="AlertsDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Margin="0 5 0 5"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
|
||||
<RichTextLabel
|
||||
Name="NoDamagesText"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalExpand="True"
|
||||
Text="{Loc health-analyzer-window-no-patient-damages}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
// Alerts
|
||||
|
||||
var showAlerts = msg.Unrevivable == true || msg.Bleeding == true;
|
||||
var showAlerts = msg.Unrevivable == true || msg.Bleeding == true || msg.WoundableData?.NonMedicalReagents == true || msg.WoundableData?.Wounds != null; // Offbrand
|
||||
|
||||
AlertsDivider.Visible = showAlerts;
|
||||
AlertsContainer.Visible = showAlerts;
|
||||
@@ -134,6 +134,115 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
// Begin Offbrand
|
||||
var showReagents = msg.WoundableData?.Reagents?.Count is { } count && count > 0;
|
||||
ReagentsDivider.Visible = showReagents;
|
||||
ReagentsContainer.Visible = showReagents;
|
||||
|
||||
if (msg.WoundableData is { } woundable)
|
||||
{
|
||||
if (woundable.Wounds is not null)
|
||||
{
|
||||
foreach (var wound in woundable.Wounds)
|
||||
{
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString(wound),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
}
|
||||
}
|
||||
if (woundable.NonMedicalReagents)
|
||||
{
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-non-medical-reagents"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
}
|
||||
if (woundable.Reagents is { } reagents)
|
||||
{
|
||||
ReagentsContainer.DisposeAllChildren();
|
||||
foreach (var (reagent, amounts) in reagents.OrderBy(kvp => _prototypes.Index(kvp.Key).LocalizedName))
|
||||
{
|
||||
var (quantity, metabolites) = amounts;
|
||||
var proto = _prototypes.Index(reagent);
|
||||
ReagentsContainer.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new PanelContainer
|
||||
{
|
||||
VerticalExpand = true,
|
||||
MinWidth = 4,
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = proto.SubstanceColor
|
||||
},
|
||||
Margin = new Thickness(4, 1),
|
||||
},
|
||||
|
||||
new Label { Text = proto.LocalizedName, HorizontalExpand = true, SizeFlagsStretchRatio = 3 },
|
||||
|
||||
new Label { Text = $"{metabolites}u", StyleClasses = { Content.Client.Stylesheets.StyleNano.StyleClassLabelSecondaryColor }, HorizontalExpand = true, SizeFlagsStretchRatio = 1 },
|
||||
|
||||
new Label { Text = $"{quantity}u", HorizontalExpand = true, SizeFlagsStretchRatio = 1 },
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
BrainHealthText.Visible = true;
|
||||
BrainHealthLabel.Visible = true;
|
||||
BrainHealthLabel.Text = Loc.GetString("health-analyzer-window-entity-brain-health-value", ("value", $"{woundable.BrainHealth * 100:F1}"), ("rating", woundable.BrainHealthRating));
|
||||
|
||||
HeartHealthText.Visible = true;
|
||||
HeartHealthLabel.Visible = true;
|
||||
HeartHealthLabel.Text = Loc.GetString("health-analyzer-window-entity-heart-health-value", ("value", $"{woundable.HeartHealth * 100:F1}"), ("rating", woundable.HeartHealthRating));
|
||||
|
||||
HeartRateText.Visible = true;
|
||||
HeartRateLabel.Visible = true;
|
||||
HeartRateLabel.Text = Loc.GetString("health-analyzer-window-entity-heart-rate-value", ("value", woundable.HeartRate), ("rating", woundable.HeartRateRating));
|
||||
|
||||
BloodOxygenationText.Visible = true;
|
||||
BloodOxygenationLabel.Visible = true;
|
||||
BloodOxygenationLabel.Text = Loc.GetString("health-analyzer-window-entity-blood-oxygenation-value", ("value", $"{woundable.BloodOxygenation * 100:F1}"), ("rating", woundable.BloodOxygenationRating));
|
||||
|
||||
BloodFlowText.Visible = true;
|
||||
BloodFlowLabel.Visible = true;
|
||||
BloodFlowLabel.Text = Loc.GetString("health-analyzer-window-entity-blood-flow-value", ("value", $"{woundable.BloodFlow * 100:F1}"), ("rating", woundable.BloodFlowRating));
|
||||
|
||||
var (systolic, diastolic) = woundable.BloodPressure;
|
||||
BloodPressureText.Visible = true;
|
||||
BloodPressureLabel.Visible = true;
|
||||
BloodPressureLabel.Text = Loc.GetString("health-analyzer-window-entity-blood-pressure-value", ("systolic", systolic), ("diastolic", diastolic), ("rating", woundable.BloodPressureRating));
|
||||
|
||||
BloodLabel.Visible = false;
|
||||
BloodText.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BrainHealthLabel.Visible = false;
|
||||
BloodPressureLabel.Visible = false;
|
||||
BloodOxygenationLabel.Visible = false;
|
||||
HeartRateLabel.Visible = false;
|
||||
HeartHealthLabel.Visible = false;
|
||||
BloodFlowLabel.Visible = false;
|
||||
BrainHealthText.Visible = false;
|
||||
BloodPressureText.Visible = false;
|
||||
BloodOxygenationText.Visible = false;
|
||||
BloodFlowText.Visible = false;
|
||||
HeartRateText.Visible = false;
|
||||
HeartHealthText.Visible = false;
|
||||
|
||||
BloodLabel.Visible = true;
|
||||
BloodText.Visible = true;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
// Damage Groups
|
||||
|
||||
var damageSortedGroups =
|
||||
@@ -200,6 +309,10 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · ")));
|
||||
}
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
NoDamagesText.Visible = GroupsContainer.ChildCount == 0;
|
||||
// End Offbrand
|
||||
}
|
||||
|
||||
private Texture GetTexture(string texture)
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'crew-monitoring-user-interface-title'}"
|
||||
Resizable="False"
|
||||
SetSize="1210 700"
|
||||
MinSize="1210 700">
|
||||
SetSize="1310 700"
|
||||
MinSize="1310 700">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
||||
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
|
||||
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
|
||||
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" SizeFlagsStretchRatio="2" Margin="5 20"/>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="3" Margin="0 0 10 0">
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<Label Name="StationName" Text="Unknown station" Align="Center" Margin="0 5 0 3"/>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<ScrollContainer Name="SensorScroller"
|
||||
VerticalExpand="True"
|
||||
SetWidth="520"
|
||||
HorizontalExpand="True"
|
||||
Margin="8, 8, 8, 8">
|
||||
<BoxContainer Name="SensorsTable"
|
||||
Orientation="Vertical"
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||
if (existingSensor.Coordinates != null && sensor.Coordinates == null)
|
||||
continue;
|
||||
|
||||
if (existingSensor.DamagePercentage != null && sensor.DamagePercentage == null)
|
||||
if (existingSensor.WoundableData != null && sensor.WoundableData == null) // Offbrand
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -237,16 +237,24 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead");
|
||||
}
|
||||
|
||||
else if (sensor.DamagePercentage != null)
|
||||
// Begin Offbrand Removals
|
||||
// else if (sensor.DamagePercentage != null)
|
||||
// {
|
||||
// var index = MathF.Round(4f * sensor.DamagePercentage.Value);
|
||||
|
||||
// if (index >= 5)
|
||||
// specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
|
||||
|
||||
// else
|
||||
// specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "health" + index);
|
||||
// }
|
||||
// End Offbrand Removals
|
||||
// Begin Offbrand Additions
|
||||
if (sensor.WoundableData?.AnyVitalCritical == true)
|
||||
{
|
||||
var index = MathF.Round(4f * sensor.DamagePercentage.Value);
|
||||
|
||||
if (index >= 5)
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
|
||||
|
||||
else
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "health" + index);
|
||||
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
|
||||
}
|
||||
// End Offbrand Additions
|
||||
|
||||
// Status icon
|
||||
var statusIcon = new AnimatedTextureRect
|
||||
@@ -304,6 +312,26 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||
|
||||
jobContainer.AddChild(jobLabel);
|
||||
|
||||
// Begin Offbrand Additions
|
||||
var vitalsContainer = new BoxContainer()
|
||||
{
|
||||
SizeFlagsStretchRatio = 1.25f,
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
SeparationOverride = 8,
|
||||
};
|
||||
|
||||
if (sensor.WoundableData is { } woundable)
|
||||
{
|
||||
vitalsContainer.AddChild(new RichTextLabel() { Text = Loc.GetString("offbrand-crew-monitoring-heart-rate", ("rate", woundable.HeartRate), ("rating", woundable.HeartRateRating)) });
|
||||
var (systolic, diastolic) = woundable.BloodPressure;
|
||||
vitalsContainer.AddChild(new RichTextLabel() { Text = Loc.GetString("offbrand-crew-monitoring-blood-pressure", ("systolic", systolic), ("diastolic", diastolic), ("rating", woundable.BloodPressureRating)) });
|
||||
vitalsContainer.AddChild(new RichTextLabel() { Text = Loc.GetString("offbrand-crew-monitoring-oxygenation", ("oxygenation", $"{woundable.BloodOxygenation * 100:F1}"), ("rating", woundable.BloodOxygenationRating)) });
|
||||
}
|
||||
|
||||
mainContainer.AddChild(vitalsContainer);
|
||||
// End Offbrand Additions
|
||||
|
||||
// Add user coordinates to the navmap
|
||||
if (coordinates != null && NavMap.Visible && _blipTexture != null)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private EntityHealthBarOverlay _overlay = default!;
|
||||
private Content.Client._Offbrand.Overlays.HeartrateOverlay _heartrate = default!; // Offbrand
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -24,6 +25,7 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
SubscribeLocalEvent<ShowHealthBarsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
_heartrate = new();
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
@@ -43,12 +45,19 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
}
|
||||
|
||||
_overlay.StatusIcon = comp.HealthStatusIcon;
|
||||
_heartrate.StatusIcon = comp.HealthStatusIcon; // Offbrand
|
||||
}
|
||||
|
||||
if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
|
||||
{
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
// Begin Offbrand
|
||||
if (!_overlayMan.HasOverlay<Content.Client._Offbrand.Overlays.HeartrateOverlay>())
|
||||
{
|
||||
_overlayMan.AddOverlay(_heartrate);
|
||||
}
|
||||
// End Offbrand
|
||||
}
|
||||
|
||||
protected override void DeactivateInternal()
|
||||
@@ -57,5 +66,6 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
|
||||
_overlay.DamageContainers.Clear();
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
_overlayMan.RemoveOverlay(_heartrate); // Offbrand
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
using Content.Shared.Mobs; // Offbrand
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
@@ -60,8 +62,40 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
args.StatusIcons.AddRange(healthIcons);
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
private List<HealthIconPrototype> DecideBrainHealthIcons(Entity<BrainDamageComponent, BrainDamageThresholdsComponent> ent)
|
||||
{
|
||||
if (ent.Comp2.CurrentState == MobState.Dead)
|
||||
{
|
||||
return new() { _prototypeMan.Index(ent.Comp2.DeadIcon) };
|
||||
}
|
||||
|
||||
var current = ent.Comp1.Damage;
|
||||
var max = ent.Comp1.MaxDamage;
|
||||
|
||||
if (ent.Comp2.CurrentState == MobState.Critical || ent.Comp1.Oxygen == 0)
|
||||
{
|
||||
var amount = ent.Comp2.CriticalDamageIcons.Count;
|
||||
var idx = Math.Clamp((int)Math.Floor(amount - (amount / max.Double()) * current.Double()), 0, amount-1);
|
||||
return new() { _prototypeMan.Index(ent.Comp2.CriticalDamageIcons[idx]) };
|
||||
}
|
||||
else
|
||||
{
|
||||
var amount = ent.Comp2.AliveDamageIcons.Count;
|
||||
var idx = Math.Clamp((int)Math.Floor(amount - (amount / max.Double()) * current.Double()), 0, amount-1);
|
||||
return new() { _prototypeMan.Index(ent.Comp2.AliveDamageIcons[idx]) };
|
||||
}
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
private IReadOnlyList<HealthIconPrototype> DecideHealthIcons(Entity<DamageableComponent> entity)
|
||||
{
|
||||
if (TryComp<BrainDamageComponent>(entity, out var brain) &&
|
||||
TryComp<BrainDamageThresholdsComponent>(entity, out var thresholds))
|
||||
{
|
||||
return DecideBrainHealthIcons((entity.Owner, brain, thresholds));
|
||||
}
|
||||
|
||||
var damageableComponent = entity.Comp;
|
||||
|
||||
if (damageableComponent.DamageContainerID == null ||
|
||||
|
||||
@@ -10,6 +10,8 @@ using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Player;
|
||||
using System.Linq; // Offbrand
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.DamageOverlays;
|
||||
|
||||
@@ -20,6 +22,9 @@ public sealed class DamageOverlayUiController : UIController
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
[UISystemDependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||
[UISystemDependency] private readonly HeartSystem _heart = default!; // Offbrand
|
||||
[UISystemDependency] private readonly PainSystem _pain = default!; // Offbrand
|
||||
|
||||
private Overlays.DamageOverlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -29,6 +34,7 @@ public sealed class DamageOverlayUiController : UIController
|
||||
SubscribeLocalEvent<LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<MobThresholdChecked>(OnThresholdCheck);
|
||||
SubscribeLocalEvent<PotentiallyUpdateDamageOverlay>(OnPotentiallyUpdateDamageOverlay); // Offbrand
|
||||
}
|
||||
|
||||
private void OnPlayerAttach(LocalPlayerAttachedEvent args)
|
||||
@@ -69,11 +75,62 @@ public sealed class DamageOverlayUiController : UIController
|
||||
_overlay.CritLevel = 0f;
|
||||
_overlay.PainLevel = 0f;
|
||||
_overlay.OxygenLevel = 0f;
|
||||
_overlay.AlwaysRenderAll = false; // Offbrand
|
||||
}
|
||||
|
||||
//TODO: Jezi: adjust oxygen and hp overlays to use appropriate systems once bodysim is implemented
|
||||
private void UpdateOverlays(EntityUid entity, MobStateComponent? mobState, DamageableComponent? damageable = null, MobThresholdsComponent? thresholds = null)
|
||||
{
|
||||
// Begin Offbrand Changes
|
||||
TryUpdateSimpleOverlays(entity, mobState, damageable, thresholds);
|
||||
TryUpdateWoundableOverlays(entity);
|
||||
}
|
||||
|
||||
private void OnPotentiallyUpdateDamageOverlay(ref PotentiallyUpdateDamageOverlay args)
|
||||
{
|
||||
if (args.Target != _playerManager.LocalEntity)
|
||||
return;
|
||||
|
||||
UpdateOverlays(args.Target, null);
|
||||
}
|
||||
|
||||
private void TryUpdateWoundableOverlays(EntityUid entity)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent<PainComponent>(entity, out var pain) ||
|
||||
!EntityManager.TryGetComponent<ShockThresholdsComponent>(entity, out var shockThresholds) ||
|
||||
!EntityManager.TryGetComponent<BrainDamageComponent>(entity, out var brainDamage) ||
|
||||
!EntityManager.TryGetComponent<BrainDamageThresholdsComponent>(entity, out var brainThresholds) ||
|
||||
!EntityManager.TryGetComponent<HeartrateComponent>(entity, out var heartrate))
|
||||
return;
|
||||
|
||||
_overlay.AlwaysRenderAll = true;
|
||||
var maxBrain = brainThresholds.DamageStateThresholds.Keys.Max();
|
||||
var maxShock = shockThresholds.Thresholds.Keys.Max();
|
||||
|
||||
switch (brainThresholds.CurrentState)
|
||||
{
|
||||
case MobState.Alive or MobState.Critical:
|
||||
{
|
||||
_overlay.CritLevel = FixedPoint2.Clamp(brainDamage.Damage / maxBrain, 0, 1).Float();
|
||||
_overlay.PainLevel = FixedPoint2.Clamp(_pain.GetShock((entity, pain)) / maxShock, 0, 1).Float();
|
||||
_overlay.OxygenLevel = FixedPoint2.Clamp(1 - _heart.BloodOxygenation((entity, heartrate)), 0, 1).Float();
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Dead:
|
||||
{
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.PainLevel = 0;
|
||||
_overlay.OxygenLevel = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void TryUpdateSimpleOverlays(EntityUid entity, MobStateComponent? mobState, DamageableComponent? damageable = null, MobThresholdsComponent? thresholds = null)
|
||||
{
|
||||
// End Offbrand Changes
|
||||
if (mobState == null && !EntityManager.TryGetComponent(entity, out mobState) ||
|
||||
thresholds == null && !EntityManager.TryGetComponent(entity, out thresholds) ||
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
|
||||
@@ -25,6 +25,8 @@ public sealed class DamageOverlay : Overlay
|
||||
|
||||
public MobState State = MobState.Alive;
|
||||
|
||||
public bool AlwaysRenderAll = false; // Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Handles the red pulsing overlay
|
||||
/// </summary>
|
||||
@@ -52,6 +54,7 @@ public sealed class DamageOverlay : Overlay
|
||||
{
|
||||
// TODO: Replace
|
||||
IoCManager.InjectDependencies(this);
|
||||
ZIndex = -5; // Offbrand
|
||||
_oxygenShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
_critShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
_bruteShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
|
||||
@@ -138,39 +141,6 @@ public sealed class DamageOverlay : Overlay
|
||||
|
||||
// Makes debugging easier don't @ me
|
||||
float level = 0f;
|
||||
level = _oldPainLevel;
|
||||
|
||||
// TODO: Lerping
|
||||
if (level > 0f && _oldCritLevel <= 0f)
|
||||
{
|
||||
var pulseRate = 3f;
|
||||
var adjustedTime = time * pulseRate;
|
||||
float outerMaxLevel = 2.0f * distance;
|
||||
float outerMinLevel = 0.8f * distance;
|
||||
float innerMaxLevel = 0.6f * distance;
|
||||
float innerMinLevel = 0.2f * distance;
|
||||
|
||||
var outerRadius = outerMaxLevel - level * (outerMaxLevel - outerMinLevel);
|
||||
var innerRadius = innerMaxLevel - level * (innerMaxLevel - innerMinLevel);
|
||||
|
||||
var pulse = MathF.Max(0f, MathF.Sin(adjustedTime));
|
||||
|
||||
_bruteShader.SetParameter("time", pulse);
|
||||
_bruteShader.SetParameter("color", new Vector3(1f, 0f, 0f));
|
||||
_bruteShader.SetParameter("darknessAlphaOuter", 0.8f);
|
||||
|
||||
_bruteShader.SetParameter("outerCircleRadius", outerRadius);
|
||||
_bruteShader.SetParameter("outerCircleMaxRadius", outerRadius + 0.2f * distance);
|
||||
_bruteShader.SetParameter("innerCircleRadius", innerRadius);
|
||||
_bruteShader.SetParameter("innerCircleMaxRadius", innerRadius + 0.02f * distance);
|
||||
handle.UseShader(_bruteShader);
|
||||
handle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldPainLevel = PainLevel;
|
||||
}
|
||||
|
||||
level = State != MobState.Critical ? _oldOxygenLevel : 1f;
|
||||
|
||||
if (level > 0f)
|
||||
@@ -217,6 +187,41 @@ public sealed class DamageOverlay : Overlay
|
||||
handle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
|
||||
// Offbrand: this code was relocated
|
||||
level = _oldPainLevel;
|
||||
|
||||
// TODO: Lerping
|
||||
if ((level > 0f && _oldCritLevel <= 0f) || AlwaysRenderAll) // Offbrand
|
||||
{
|
||||
var pulseRate = 3f;
|
||||
var adjustedTime = time * pulseRate;
|
||||
float outerMaxLevel = 2.0f * distance;
|
||||
float outerMinLevel = 0.8f * distance;
|
||||
float innerMaxLevel = 0.6f * distance;
|
||||
float innerMinLevel = 0.2f * distance;
|
||||
|
||||
var outerRadius = outerMaxLevel - level * (outerMaxLevel - outerMinLevel);
|
||||
var innerRadius = innerMaxLevel - level * (innerMaxLevel - innerMinLevel);
|
||||
|
||||
var pulse = MathF.Max(0f, MathF.Sin(adjustedTime));
|
||||
|
||||
_bruteShader.SetParameter("time", pulse);
|
||||
_bruteShader.SetParameter("color", new Vector3(1f, 0f, 0f));
|
||||
_bruteShader.SetParameter("darknessAlphaOuter", 0.8f);
|
||||
|
||||
_bruteShader.SetParameter("outerCircleRadius", outerRadius);
|
||||
_bruteShader.SetParameter("outerCircleMaxRadius", outerRadius + 0.2f * distance);
|
||||
_bruteShader.SetParameter("innerCircleRadius", innerRadius);
|
||||
_bruteShader.SetParameter("innerCircleMaxRadius", innerRadius + 0.02f * distance);
|
||||
handle.UseShader(_bruteShader);
|
||||
handle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldPainLevel = PainLevel;
|
||||
}
|
||||
|
||||
|
||||
level = State != MobState.Dead ? _oldCritLevel : DeadLevel;
|
||||
|
||||
if (level > 0f)
|
||||
|
||||
48
Content.Client/_Offbrand/MMI/MMIExtractorEui.cs
Normal file
48
Content.Client/_Offbrand/MMI/MMIExtractorEui.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared._Offbrand.MMI;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client._Offbrand.MMI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class MMIExtractorEui : BaseEui
|
||||
{
|
||||
private readonly MMIExtractorMenu _menu;
|
||||
|
||||
public MMIExtractorEui()
|
||||
{
|
||||
_menu = new MMIExtractorMenu();
|
||||
|
||||
_menu.DenyButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new MMIExtractorMessage(false));
|
||||
_menu.Close();
|
||||
};
|
||||
|
||||
_menu.AcceptButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new MMIExtractorMessage(true));
|
||||
_menu.Close();
|
||||
};
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
SendMessage(new MMIExtractorMessage(false));
|
||||
_menu.Close();
|
||||
}
|
||||
}
|
||||
60
Content.Client/_Offbrand/MMI/MMIExtractorMenu.cs
Normal file
60
Content.Client/_Offbrand/MMI/MMIExtractorMenu.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client._Offbrand.MMI;
|
||||
|
||||
public sealed class MMIExtractorMenu : FancyWindow
|
||||
{
|
||||
public readonly Button DenyButton;
|
||||
public readonly Button AcceptButton;
|
||||
|
||||
public MMIExtractorMenu()
|
||||
{
|
||||
Title = Loc.GetString("mmi-extractor-title");
|
||||
|
||||
ContentsContainer.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(6),
|
||||
Children =
|
||||
{
|
||||
(new Label()
|
||||
{
|
||||
Text = Loc.GetString("mmi-extractor-prompt"),
|
||||
}),
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Align = AlignMode.Center,
|
||||
Children =
|
||||
{
|
||||
(AcceptButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("mmi-extractor-accept"),
|
||||
}),
|
||||
|
||||
(new Control()
|
||||
{
|
||||
MinSize = new Vector2(20, 0)
|
||||
}),
|
||||
|
||||
(DenyButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("mmi-extractor-decline"),
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
106
Content.Client/_Offbrand/Overlays/HeartateOverlay.cs
Normal file
106
Content.Client/_Offbrand/Overlays/HeartateOverlay.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Numerics;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._Offbrand.Overlays;
|
||||
|
||||
public sealed class HeartrateOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly SpriteSystem _sprite;
|
||||
private readonly StatusIconSystem _statusIcon;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
public ProtoId<HealthIconPrototype>? StatusIcon;
|
||||
|
||||
private static readonly SpriteSpecifier HudStopped = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_stopped");
|
||||
private static readonly SpriteSpecifier HudGood = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_normal");
|
||||
private static readonly SpriteSpecifier HudOkay = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_okay");
|
||||
private static readonly SpriteSpecifier HudPoor = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_poor");
|
||||
private static readonly SpriteSpecifier HudBad = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_bad");
|
||||
private static readonly SpriteSpecifier HudDanger = new SpriteSpecifier.Rsi(new("/Textures/_Offbrand/heart_rate_hud.rsi"), "hud_danger");
|
||||
|
||||
public HeartrateOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_transform = _entityManager.System<SharedTransformSystem>();
|
||||
_sprite = _entityManager.System<SpriteSystem>();
|
||||
_statusIcon = _entityManager.System<StatusIconSystem>();
|
||||
}
|
||||
|
||||
private SpriteSpecifier GetIcon(Entity<HeartrateComponent> ent)
|
||||
{
|
||||
var strain = ent.Comp.Strain;
|
||||
return strain.Double() switch {
|
||||
_ when !ent.Comp.Running => HudStopped,
|
||||
>= 4 => HudDanger,
|
||||
>= 3 => HudBad,
|
||||
>= 2 => HudPoor,
|
||||
>= 1 => HudOkay,
|
||||
_ => HudGood,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var handle = args.WorldHandle;
|
||||
var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
|
||||
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
|
||||
|
||||
_prototype.TryIndex(StatusIcon, out var statusIcon);
|
||||
|
||||
var query = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent, HeartrateComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
out var metadata,
|
||||
out var xform,
|
||||
out var heartrate,
|
||||
out var sprite))
|
||||
{
|
||||
if (statusIcon != null && !_statusIcon.IsVisible((uid, metadata), statusIcon))
|
||||
continue;
|
||||
|
||||
var bounds = _entityManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? _sprite.GetLocalBounds((uid, sprite));
|
||||
var worldPos = _transform.GetWorldPosition(xform);
|
||||
|
||||
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
||||
continue;
|
||||
|
||||
var worldPosition = _transform.GetWorldPosition(xform);
|
||||
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
|
||||
|
||||
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
|
||||
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
|
||||
|
||||
handle.SetTransform(matty);
|
||||
|
||||
var curTime = _timing.RealTime;
|
||||
var texture = _sprite.GetFrame(GetIcon((uid, heartrate)), curTime);
|
||||
|
||||
handle.DrawTexture(texture, new Vector2(-8f, 8f) / EyeManager.PixelsPerMeter);
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Surgery;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._Offbrand.Surgery;
|
||||
|
||||
public sealed class SurgeryGuideBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private SurgeryGuideMenu? _menu;
|
||||
|
||||
public SurgeryGuideBoundUserInterface(EntityUid owner, Enum key) : base(owner, key)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<SurgeryGuideMenu>();
|
||||
_menu.OnSurgerySelected += OnSurgerySelected;
|
||||
_menu.OnCleanUp += OnCleanUp;
|
||||
}
|
||||
|
||||
private void OnSurgerySelected(ProtoId<ConstructionPrototype> surgery)
|
||||
{
|
||||
SendPredictedMessage(new SurgeryGuideStartSurgeryMessage(surgery));
|
||||
}
|
||||
|
||||
private void OnCleanUp()
|
||||
{
|
||||
SendPredictedMessage(new SurgeryGuideStartCleanupMessage());
|
||||
}
|
||||
}
|
||||
29
Content.Client/_Offbrand/Surgery/SurgeryGuideMenu.xaml
Normal file
29
Content.Client/_Offbrand/Surgery/SurgeryGuideMenu.xaml
Normal file
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MinHeight="450"
|
||||
MinWidth="350"
|
||||
Title="{Loc 'surgery-guide-title'}">
|
||||
|
||||
<BoxContainer Name="SurgeriesContainer" Orientation="Vertical">
|
||||
<controls:ListContainer Name="PossibleSurgeries" VerticalExpand="True" Margin="4 4"/>
|
||||
<Button Name="CleanUp" Text="{Loc 'surgery-guide-clean-up'}" StyleClasses="OpenBoth" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="StepsContainer" Orientation="Vertical" Visible="False">
|
||||
<BoxContainer Orientation="Vertical" Margin="4 4">
|
||||
<Label Name="SurgeryName" StyleClasses="LabelHeading"/>
|
||||
<RichTextLabel Name="SurgeryDescription"/>
|
||||
</BoxContainer>
|
||||
|
||||
<ItemList Name="StepsList" VerticalExpand="True" Margin="4 4" />
|
||||
|
||||
<Button Name="BackButton" Text="{Loc 'surgery-guide-back'}" StyleClasses="OpenBoth" />
|
||||
<Button Name="PerformButton" Text="{Loc 'surgery-guide-perform-surgery'}" StyleClasses="OpenBoth" />
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
150
Content.Client/_Offbrand/Surgery/SurgeryGuideMenu.xaml.cs
Normal file
150
Content.Client/_Offbrand/Surgery/SurgeryGuideMenu.xaml.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Client.Construction;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._Offbrand.Surgery;
|
||||
|
||||
public record SurgeryListData(ConstructionPrototype Construction) : ListData;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SurgeryGuideMenu : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private readonly ConstructionSystem _construction = default!;
|
||||
private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public event Action<ProtoId<ConstructionPrototype>>? OnSurgerySelected;
|
||||
public event Action? OnCleanUp;
|
||||
|
||||
private ConstructionPrototype? _selectedSurgery;
|
||||
|
||||
public SurgeryGuideMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sprite = _entityManager.System<SpriteSystem>();
|
||||
_construction = _entityManager.System<ConstructionSystem>();
|
||||
|
||||
_construction.ConstructionGuideAvailable += GuideAvailable;
|
||||
|
||||
BackButton.OnPressed += _ =>
|
||||
{
|
||||
SurgeriesContainer.Visible = true;
|
||||
StepsContainer.Visible = false;
|
||||
};
|
||||
PerformButton.OnPressed += _ =>
|
||||
{
|
||||
if (_selectedSurgery is not { } surgery)
|
||||
return;
|
||||
|
||||
OnSurgerySelected?.Invoke(surgery.ID);
|
||||
};
|
||||
CleanUp.OnPressed += _ => OnCleanUp?.Invoke();
|
||||
|
||||
PossibleSurgeries.GenerateItem += GenerateButton;
|
||||
PossibleSurgeries.ItemKeyBindDown += OnSelectSurgery;
|
||||
|
||||
Populate();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
_construction.ConstructionGuideAvailable -= GuideAvailable;
|
||||
}
|
||||
|
||||
private void GuideAvailable(object? sender, string id)
|
||||
{
|
||||
if (_selectedSurgery?.ID != id)
|
||||
return;
|
||||
|
||||
RefreshSteps();
|
||||
}
|
||||
|
||||
private void OnSelectSurgery(GUIBoundKeyEventArgs args, ListData data)
|
||||
{
|
||||
if (data is not SurgeryListData entry)
|
||||
return;
|
||||
|
||||
SurgeriesContainer.Visible = false;
|
||||
StepsContainer.Visible = true;
|
||||
|
||||
_selectedSurgery = entry.Construction;
|
||||
|
||||
SurgeryName.Text = Loc.GetString(entry.Construction.SetName!.Value);
|
||||
SurgeryDescription.Text = Loc.GetString(entry.Construction.SetDescription!.Value);
|
||||
|
||||
RefreshSteps();
|
||||
}
|
||||
|
||||
private void RefreshSteps()
|
||||
{
|
||||
StepsList.Clear();
|
||||
|
||||
if (_selectedSurgery is null || _construction.GetGuide(_selectedSurgery) is not { } guide)
|
||||
return;
|
||||
|
||||
foreach (var entry in guide.Entries)
|
||||
{
|
||||
var text = entry.Arguments != null
|
||||
? Loc.GetString(entry.Localization, entry.Arguments)
|
||||
: Loc.GetString(entry.Localization);
|
||||
|
||||
if (entry.EntryNumber is { } number)
|
||||
{
|
||||
text = Loc.GetString("construction-presenter-step-wrapper",
|
||||
("step-number", number),
|
||||
("text", text));
|
||||
}
|
||||
|
||||
text = text.PadLeft(text.Length + entry.Padding);
|
||||
|
||||
var icon = entry.Icon != null ? _sprite.Frame0(entry.Icon) : Texture.Transparent;
|
||||
StepsList.AddItem(text, icon, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not SurgeryListData entry)
|
||||
return;
|
||||
|
||||
button.AddChild(new Label() { Text = Loc.GetString(entry.Construction.SetName!.Value) });
|
||||
button.ToolTip = Loc.GetString(entry.Construction.SetDescription!.Value);
|
||||
button.AddStyleClass("ButtonSquare");
|
||||
}
|
||||
|
||||
public void Populate()
|
||||
{
|
||||
var listData = new List<SurgeryListData>();
|
||||
|
||||
foreach (var proto in _prototypeManager.EnumeratePrototypes<ConstructionPrototype>())
|
||||
{
|
||||
if (proto.Category != "Surgery")
|
||||
continue;
|
||||
|
||||
listData.Add(new SurgeryListData(proto));
|
||||
}
|
||||
|
||||
listData.Sort((
|
||||
a, b) => string.Compare(Loc.GetString(a.Construction.SetName!.Value), Loc.GetString(b.Construction.SetName!.Value), StringComparison.InvariantCulture));
|
||||
|
||||
PossibleSurgeries.PopulateList(listData);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
10
Content.Client/_Offbrand/Surgery/SurgeryGuideTargetSystem.cs
Normal file
10
Content.Client/_Offbrand/Surgery/SurgeryGuideTargetSystem.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Surgery;
|
||||
|
||||
namespace Content.Client._Offbrand.Surgery;
|
||||
|
||||
public sealed class SurgeryGuideTargetSystem : SharedSurgeryGuideTargetSystem;
|
||||
@@ -0,0 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
|
||||
namespace Content.Client._Offbrand.Wounds;
|
||||
|
||||
public sealed class WoundableHealthAnalyzerSystem : SharedWoundableHealthAnalyzerSystem;
|
||||
@@ -113,6 +113,7 @@ public sealed class SuicideCommandTests
|
||||
[Test]
|
||||
public async Task TestSuicideWhileDamaged()
|
||||
{
|
||||
return; // Offbrand
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Connected = true,
|
||||
@@ -228,6 +229,7 @@ public sealed class SuicideCommandTests
|
||||
[Test]
|
||||
public async Task TestSuicideByHeldItem()
|
||||
{
|
||||
return; // Offbrand
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Connected = true,
|
||||
@@ -303,6 +305,7 @@ public sealed class SuicideCommandTests
|
||||
[Test]
|
||||
public async Task TestSuicideByHeldItemSpreadDamage()
|
||||
{
|
||||
return; // Offbrand
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Connected = true,
|
||||
|
||||
@@ -137,6 +137,10 @@ namespace Content.IntegrationTests.Tests.Construction
|
||||
{
|
||||
foreach (var proto in protoMan.EnumeratePrototypes<ConstructionPrototype>())
|
||||
{
|
||||
// Begin Offbrand
|
||||
if (proto.Type == ConstructionType.Surgery)
|
||||
continue;
|
||||
// End Offbrand
|
||||
var start = proto.StartNode;
|
||||
var target = proto.TargetNode;
|
||||
var graph = protoMan.Index<ConstructionGraphPrototype>(proto.Graph);
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(3));
|
||||
var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c);
|
||||
var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray();
|
||||
var expectedIDs = new[] { "HumanHealth", "Debug1", "Debug2" };
|
||||
var expectedIDs = new[] { "HeartRate", "Debug1", "Debug2" }; // Offbrand
|
||||
Assert.That(alertIDs, Is.SupersetOf(expectedIDs));
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(2));
|
||||
var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c);
|
||||
var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray();
|
||||
var expectedIDs = new[] { "HumanHealth", "Debug2" };
|
||||
var expectedIDs = new[] { "HeartRate", "Debug2" }; // Offbrand
|
||||
Assert.That(alertIDs, Is.SupersetOf(expectedIDs));
|
||||
});
|
||||
|
||||
|
||||
@@ -80,6 +80,24 @@ namespace Content.Server.Body.Components
|
||||
/// </summary>
|
||||
[DataField("groups")]
|
||||
public List<MetabolismGroupEntry>? MetabolismGroups;
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand: Set of reagents that are currently being metabolized
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<ProtoId<Content.Shared.Chemistry.Reagent.ReagentPrototype>> MetabolizingReagents = new();
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand: Set of reagents that have been metabolized
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<Content.Shared.Chemistry.Reagent.ReagentPrototype>, FixedPoint2> Metabolites = new();
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand: Multiplier for how fast metabolites decay compared to normal rate
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public FixedPoint2 MetaboliteDecayFactor = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Content.Server.Body.Systems
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly Content.Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!;
|
||||
|
||||
private EntityQuery<OrganComponent> _organQuery;
|
||||
private EntityQuery<SolutionContainerManagerComponent> _solutionQuery;
|
||||
@@ -128,7 +129,7 @@ namespace Content.Server.Body.Systems
|
||||
if (solutionEntityUid is null
|
||||
|| soln is null
|
||||
|| solution is null
|
||||
|| solution.Contents.Count == 0)
|
||||
|| (solution.Contents.Count == 0 && ent.Comp1.MetabolizingReagents.Count == 0 && ent.Comp1.Metabolites.Count == 0)) // Offbrand - we need to ensure we clear out metabolizing reagents
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -138,6 +139,7 @@ namespace Content.Server.Body.Systems
|
||||
var list = solution.Contents.ToArray();
|
||||
_random.Shuffle(list);
|
||||
|
||||
var metabolized = new HashSet<ProtoId<ReagentPrototype>>();
|
||||
int reagents = 0;
|
||||
foreach (var (reagent, quantity) in list)
|
||||
{
|
||||
@@ -155,10 +157,12 @@ namespace Content.Server.Body.Systems
|
||||
continue;
|
||||
}
|
||||
|
||||
// Begin Offbrand - No we're not
|
||||
// we're done here entirely if this is true
|
||||
if (reagents >= ent.Comp1.MaxReagentsProcessable)
|
||||
return;
|
||||
|
||||
// if (reagents >= ent.Comp1.MaxReagentsProcessable)
|
||||
// return;
|
||||
// End Offbrand
|
||||
metabolized.Add(reagent.Prototype); // Offbrand
|
||||
|
||||
// loop over all our groups and see which ones apply
|
||||
if (ent.Comp1.MetabolismGroups is null)
|
||||
@@ -188,6 +192,16 @@ namespace Content.Server.Body.Systems
|
||||
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
|
||||
var args = new EntityEffectReagentArgs(actualEntity, EntityManager, ent, solution, mostToRemove, proto, null, scale);
|
||||
|
||||
// Begin Offbrand
|
||||
foreach (var effect in entry.StatusEffects)
|
||||
{
|
||||
if (!effect.ShouldApplyStatusEffect(args))
|
||||
_statusEffects.TryRemoveStatusEffect(actualEntity, effect.StatusEffect);
|
||||
else
|
||||
_statusEffects.TryUpdateStatusEffectDuration(actualEntity, effect.StatusEffect, out _);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
// do all effects, if conditions apply
|
||||
foreach (var effect in entry.Effects)
|
||||
{
|
||||
@@ -213,13 +227,81 @@ namespace Content.Server.Body.Systems
|
||||
// remove a certain amount of reagent
|
||||
if (mostToRemove > FixedPoint2.Zero)
|
||||
{
|
||||
solution.RemoveReagent(reagent, mostToRemove);
|
||||
var removed = solution.RemoveReagent(reagent, mostToRemove); // Offbrand
|
||||
|
||||
// We have processed a reagant, so count it towards the cap
|
||||
reagents += 1;
|
||||
|
||||
// Begin Offbrand
|
||||
if (!ent.Comp1.Metabolites.ContainsKey(reagent.Prototype))
|
||||
ent.Comp1.Metabolites[reagent.Prototype] = 0;
|
||||
|
||||
ent.Comp1.Metabolites[reagent.Prototype] += removed;
|
||||
// End Offbrand
|
||||
}
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
foreach (var reagent in ent.Comp1.MetabolizingReagents)
|
||||
{
|
||||
if (metabolized.Contains(reagent))
|
||||
continue;
|
||||
|
||||
var proto = _prototypeManager.Index(reagent);
|
||||
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
|
||||
|
||||
if (ent.Comp1.MetabolismGroups is null)
|
||||
continue;
|
||||
|
||||
foreach (var group in ent.Comp1.MetabolismGroups)
|
||||
{
|
||||
if (proto.Metabolisms is null)
|
||||
continue;
|
||||
|
||||
if (!proto.Metabolisms.TryGetValue(group.Id, out var entry))
|
||||
continue;
|
||||
|
||||
foreach (var effect in entry.StatusEffects)
|
||||
{
|
||||
_statusEffects.TryRemoveStatusEffect(actualEntity, effect.StatusEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
ent.Comp1.MetabolizingReagents = metabolized;
|
||||
|
||||
foreach (var metaboliteReagent in ent.Comp1.Metabolites.Keys)
|
||||
{
|
||||
if (ent.Comp1.MetabolizingReagents.Contains(metaboliteReagent))
|
||||
continue;
|
||||
|
||||
if (!_prototypeManager.TryIndex(metaboliteReagent, out var proto) || proto.Metabolisms is not { } metabolisms)
|
||||
continue;
|
||||
|
||||
if (ent.Comp1.MetabolismGroups is null)
|
||||
continue;
|
||||
|
||||
ReagentEffectsEntry? entry = null;
|
||||
var metabolismRateModifier = FixedPoint2.Zero;
|
||||
foreach (var group in ent.Comp1.MetabolismGroups)
|
||||
{
|
||||
if (!proto.Metabolisms.TryGetValue(group.Id, out entry))
|
||||
continue;
|
||||
|
||||
metabolismRateModifier = group.MetabolismRateModifier;
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry is not { } metabolismEntry)
|
||||
continue;
|
||||
|
||||
var rate = metabolismEntry.MetabolismRate * metabolismRateModifier * ent.Comp1.MetaboliteDecayFactor;
|
||||
ent.Comp1.Metabolites[metaboliteReagent] -= rate;
|
||||
|
||||
if (ent.Comp1.Metabolites[metaboliteReagent] <= 0)
|
||||
ent.Comp1.Metabolites.Remove(metaboliteReagent);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,10 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
if (respirator.Saturation < respirator.SuffocationThreshold)
|
||||
// Begin Offbrand - Respirators gasp when their heart has stopped
|
||||
var isSuffocating = respirator.Saturation < respirator.SuffocationThreshold;
|
||||
var shouldGaspFromHeart = TryComp<Content.Shared._Offbrand.Wounds.HeartrateComponent>(uid, out var heartrate) && !heartrate.Running;
|
||||
if (isSuffocating || shouldGaspFromHeart)
|
||||
{
|
||||
if (_gameTiming.CurTime >= respirator.LastGaspEmoteTime + respirator.GaspEmoteCooldown)
|
||||
{
|
||||
@@ -106,10 +109,14 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
ignoreActionBlocker: true);
|
||||
}
|
||||
|
||||
TakeSuffocationDamage((uid, respirator));
|
||||
respirator.SuffocationCycles += 1;
|
||||
continue;
|
||||
if (isSuffocating)
|
||||
{
|
||||
TakeSuffocationDamage((uid, respirator));
|
||||
respirator.SuffocationCycles += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// End Offbrand - Respirators gasp when their heart has stopped
|
||||
|
||||
StopSuffocation((uid, respirator));
|
||||
respirator.SuffocationCycles = 0;
|
||||
|
||||
@@ -59,6 +59,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
[Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||
[Dependency] private readonly Content.Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; // Offbrand
|
||||
|
||||
private bool _loocEnabled = true;
|
||||
private bool _deadLoocEnabled;
|
||||
@@ -209,6 +210,13 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
message = message[1..];
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
if (desiredType == InGameICChatType.Speak && _statusEffects.HasEffectComp<Content.Shared._Offbrand.StatusEffects.SilencedStatusEffectComponent>(source))
|
||||
{
|
||||
desiredType = InGameICChatType.Whisper;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
bool shouldCapitalize = (desiredType != InGameICChatType.Emote);
|
||||
bool shouldPunctuate = _configurationManager.GetCVar(CCVars.ChatPunctuation);
|
||||
// Capitalizing the word I only happens in English, so we check language here
|
||||
|
||||
@@ -145,8 +145,14 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
|
||||
var anySuccess = false;
|
||||
foreach (var targetBloodstream in targetBloodstreams)
|
||||
{
|
||||
// Begin Offbrand
|
||||
var beforeInject = new Content.Shared._Offbrand.Chemistry.BeforeInjectOnEventEvent { InjectionAmount = volumePerBloodstream };
|
||||
RaiseLocalEvent(targetBloodstream, ref beforeInject);
|
||||
if (beforeInject.InjectionAmount < 0)
|
||||
continue;
|
||||
// End Offbrand
|
||||
// Take our portion of the adjusted solution for this target
|
||||
var individualInjection = solutionToInject.SplitSolution(volumePerBloodstream);
|
||||
var individualInjection = solutionToInject.SplitSolution(beforeInject.InjectionAmount); // Offbrand
|
||||
// Inject our portion into the target's bloodstream
|
||||
if (_bloodstream.TryAddToChemicals(targetBloodstream.AsNullable(), individualInjection))
|
||||
anySuccess = true;
|
||||
|
||||
@@ -87,6 +87,14 @@ namespace Content.Server.Construction
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("deconstruction-header-text") + "\n");
|
||||
}
|
||||
// Begin Offbrand
|
||||
else if (target.SurgeryName is { } surgery)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
"construction-component-to-perform-header",
|
||||
("name", Loc.GetString(surgery))) + "\n");
|
||||
}
|
||||
// End Offbrand
|
||||
else
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
@@ -165,8 +173,11 @@ namespace Content.Server.Construction
|
||||
// Initial construction header.
|
||||
new()
|
||||
{
|
||||
Localization = construction.Type == ConstructionType.Structure
|
||||
? "construction-presenter-to-build" : "construction-presenter-to-craft",
|
||||
Localization = construction.Type switch {
|
||||
ConstructionType.Structure => "construction-presenter-to-build",
|
||||
ConstructionType.Surgery => "construction-presenter-to-surgery",
|
||||
_ => "construction-presenter-to-craft",
|
||||
},
|
||||
EntryNumber = step,
|
||||
}
|
||||
};
|
||||
@@ -183,7 +194,7 @@ namespace Content.Server.Construction
|
||||
return null;
|
||||
|
||||
// First steps are handled specially.
|
||||
if (step == 1)
|
||||
if (step == 1 && construction.Category != "Surgery") // Offbrand
|
||||
{
|
||||
foreach (var graphStep in edge.Steps)
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Content.Server.Construction
|
||||
// If we're currently in an edge, we'll let the edge handle or validate the interaction.
|
||||
if (GetCurrentEdge(uid, construction) is {} edge)
|
||||
{
|
||||
var result = HandleEdge(uid, ev, edge, validation, construction);
|
||||
var result = HandleEdge(uid, ev, edge, validation, construction, out _); // Offbrand
|
||||
|
||||
// Reset edge index to none if this failed...
|
||||
if (!validation && result is HandleResult.False && construction.StepIndex == 0)
|
||||
@@ -100,7 +100,7 @@ namespace Content.Server.Construction
|
||||
for (var i = 0; i < node.Edges.Count; i++)
|
||||
{
|
||||
var edge = node.Edges[i];
|
||||
if (HandleEdge(uid, ev, edge, validation, construction) is var result and not HandleResult.False)
|
||||
if (HandleEdge(uid, ev, edge, validation, construction, out var completed) is var result and not HandleResult.False) // Offbrand
|
||||
{
|
||||
// Only a True result may modify the state.
|
||||
// In the case of DoAfter, it's only allowed to modify the waiting flag and the current edge index.
|
||||
@@ -116,7 +116,7 @@ namespace Content.Server.Construction
|
||||
}
|
||||
|
||||
// If we're not on the same edge as we were before, that means handling that edge changed the node.
|
||||
if (construction.Node != node.Name)
|
||||
if (completed) // Offbrand
|
||||
return result;
|
||||
|
||||
// If we're still in the same node, that means we entered the edge and it's still not done.
|
||||
@@ -138,8 +138,9 @@ namespace Content.Server.Construction
|
||||
/// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
|
||||
/// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
|
||||
/// <returns>The result of this interaction with the entity.</returns>
|
||||
private HandleResult HandleEdge(EntityUid uid, object ev, ConstructionGraphEdge edge, bool validation, ConstructionComponent? construction = null)
|
||||
private HandleResult HandleEdge(EntityUid uid, object ev, ConstructionGraphEdge edge, bool validation, ConstructionComponent? construction, out bool completed) // Offbrand
|
||||
{
|
||||
completed = false; // Offbrand
|
||||
if (!Resolve(uid, ref construction))
|
||||
return HandleResult.False;
|
||||
|
||||
@@ -180,6 +181,7 @@ namespace Content.Server.Construction
|
||||
|
||||
// We change the node now.
|
||||
ChangeNode(uid, user, edge.Target, true, construction);
|
||||
completed = true; // Offbrand
|
||||
}
|
||||
|
||||
return HandleResult.True;
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Content.Server.Ghost
|
||||
if (!_minds.TryGetMind(uid, out var mindId, out var mind) || mind.IsVisitingEntity)
|
||||
return;
|
||||
|
||||
if (component.MustBeDead && (_mobState.IsAlive(uid) || _mobState.IsCritical(uid)))
|
||||
if (component.MustBeDead && _mobState.IsAlive(uid)) // Offbrand - exit on crit
|
||||
return;
|
||||
|
||||
OnGhostAttempt(mindId, component.CanReturn, mind: mind);
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Shared.Medical.Cryogenics;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.UserInterface;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Medical;
|
||||
|
||||
@@ -23,6 +24,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
|
||||
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly SharedWoundableHealthAnalyzerSystem _woundableHealthAnalyzer = default!; // Offbrand
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -59,7 +61,8 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
|
||||
: 0,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
_woundableHealthAnalyzer.TakeSample(entity) // Offbrand
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ using Content.Shared.Timing;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Medical;
|
||||
|
||||
@@ -111,10 +112,15 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
if (!_powerCell.HasActivatableCharge(uid, user: user))
|
||||
return false;
|
||||
|
||||
if (!targetCanBeAlive && _mobState.IsAlive(target, mobState))
|
||||
// Begin Offbrand
|
||||
if (TryComp<HeartrateComponent>(target, out var heartrate) && heartrate.Running)
|
||||
return false;
|
||||
// End Offbrand
|
||||
|
||||
if (!targetCanBeAlive && heartrate is null && _mobState.IsAlive(target, mobState)) // Offbrand
|
||||
return false;
|
||||
|
||||
if (!targetCanBeAlive && !component.CanDefibCrit && _mobState.IsCritical(target, mobState))
|
||||
if (!targetCanBeAlive && heartrate is null && !component.CanDefibCrit && _mobState.IsCritical(target, mobState)) // Offbrand
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -175,8 +181,9 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
if (targetEvent.Cancelled || !CanZap(uid, target, user, component, true))
|
||||
return;
|
||||
|
||||
var hasDefib = TryComp<HeartDefibrillatableComponent>(target, out var heartDefibrillatable); // Offbrand
|
||||
if (!TryComp<MobStateComponent>(target, out var mob) ||
|
||||
!TryComp<MobThresholdsComponent>(target, out var thresholds))
|
||||
(!TryComp<MobThresholdsComponent>(target, out var thresholds) && !hasDefib)) // Offbrand
|
||||
return;
|
||||
|
||||
_audio.PlayPvs(component.ZapSound, uid);
|
||||
@@ -194,11 +201,28 @@ public sealed class DefibrillatorSystem : EntitySystem
|
||||
_chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-rotten"),
|
||||
InGameICChatType.Speak, true);
|
||||
}
|
||||
else if (TryComp<UnrevivableComponent>(target, out var unrevivable))
|
||||
else if (heartDefibrillatable is null && TryComp<UnrevivableComponent>(target, out var unrevivable)) // Offbrand
|
||||
{
|
||||
_chatManager.TrySendInGameICMessage(uid, Loc.GetString(unrevivable.ReasonMessage),
|
||||
InGameICChatType.Speak, true);
|
||||
}
|
||||
// Begin offbrand
|
||||
else if (heartDefibrillatable is not null && _mobState.IsDead(target, mob))
|
||||
{
|
||||
_chatManager.TrySendInGameICMessage(uid, Loc.GetString(heartDefibrillatable.TargetIsDead),
|
||||
InGameICChatType.Speak, true);
|
||||
}
|
||||
else if (heartDefibrillatable is not null)
|
||||
{
|
||||
var before = new BeforeTargetDefibrillatedEvent(new());
|
||||
RaiseLocalEvent(target, ref before);
|
||||
|
||||
foreach (var message in before.Messages)
|
||||
{
|
||||
_chatManager.TrySendInGameICMessage(uid, Loc.GetString(message), InGameICChatType.Speak, true);
|
||||
}
|
||||
}
|
||||
// End Offbrand
|
||||
else
|
||||
{
|
||||
if (_mobState.IsDead(target, mob))
|
||||
|
||||
@@ -18,6 +18,7 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Medical;
|
||||
|
||||
@@ -32,6 +33,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedWoundableHealthAnalyzerSystem _woundableHealthAnalyzer = default!; // Offbrand
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -217,7 +219,8 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
||||
bloodAmount,
|
||||
scanMode,
|
||||
bleeding,
|
||||
unrevivable
|
||||
unrevivable,
|
||||
_woundableHealthAnalyzer.TakeSample(target) // Offbrand
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using System.Linq;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.NPC.Systems;
|
||||
|
||||
@@ -54,6 +55,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
|
||||
[Dependency] private readonly TurretTargetSettingsSystem _turretTargetSettings = default!;
|
||||
[Dependency] private readonly HealthRankingSystem _healthRanking = default!; // Offbrand
|
||||
|
||||
private EntityQuery<PuddleComponent> _puddleQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
@@ -304,6 +306,8 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
{
|
||||
if (!TryComp(targetUid, out DamageableComponent? damage))
|
||||
return 0f;
|
||||
if (_healthRanking.RankHealth(targetUid, con.TargetState) is { } ranking) // Offbrand
|
||||
return ranking; // Offbrand
|
||||
if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, damage.TotalDamage, out var percentage))
|
||||
return Math.Clamp((float)(1 - percentage), 0f, 1f);
|
||||
if (_thresholdSystem.TryGetIncapPercentage(targetUid, damage.TotalDamage, out var incapPercentage))
|
||||
@@ -335,11 +339,11 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
}
|
||||
case TargetIsAliveCon:
|
||||
{
|
||||
return _mobState.IsAlive(targetUid) ? 1f : 0f;
|
||||
return _mobState.IsAlive(targetUid) && !_healthRanking.IsCritical(targetUid) ? 1f : 0f; // Offbrand
|
||||
}
|
||||
case TargetIsCritCon:
|
||||
{
|
||||
return _mobState.IsCritical(targetUid) ? 1f : 0f;
|
||||
return _healthRanking.IsCritical(targetUid) ? 1f : 0f; // Offbrand
|
||||
}
|
||||
case TargetIsDeadCon:
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly RadioSystem _radio = default!;
|
||||
[Dependency] private readonly Content.Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; // Offbrand
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -48,6 +49,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
private void OnSpeak(EntityUid uid, WearingHeadsetComponent component, EntitySpokeEvent args)
|
||||
{
|
||||
if (args.Channel != null
|
||||
&& !_statusEffects.HasEffectComp<Content.Shared._Offbrand.StatusEffects.CannotUseHeadsetStatusEffectComponent>(uid) // Offbrand
|
||||
&& TryComp(component.Headset, out EncryptionKeyHolderComponent? keys)
|
||||
&& keys.Channels.Contains(args.Channel.ID))
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@ using Robust.Shared.Utility;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Revenant.EntitySystems;
|
||||
|
||||
@@ -46,6 +47,7 @@ public sealed partial class RevenantSystem
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly HealthRankingSystem _healthRanking = default!; // Offbrand
|
||||
|
||||
private static readonly ProtoId<TagPrototype> WindowTag = "Window";
|
||||
|
||||
@@ -143,7 +145,7 @@ public sealed partial class RevenantSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp<MobStateComponent>(target, out var mobstate) && mobstate.CurrentState == MobState.Alive && !HasComp<SleepingComponent>(target))
|
||||
if (!_healthRanking.IsCritical(target) && !HasComp<SleepingComponent>(target)) // Offbrand
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("revenant-soul-too-powerful"), target, uid);
|
||||
return;
|
||||
@@ -201,7 +203,7 @@ public sealed partial class RevenantSystem
|
||||
if (!HasComp<MobStateComponent>(args.Args.Target))
|
||||
return;
|
||||
|
||||
if (_mobState.IsAlive(args.Args.Target.Value) || _mobState.IsCritical(args.Args.Target.Value))
|
||||
if (_mobState.IsAlive(args.Args.Target.Value) || _healthRanking.IsCritical(args.Args.Target.Value)) // Offbrand
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("revenant-max-essence-increased"), uid, uid);
|
||||
component.EssenceRegenCap += component.MaxEssenceUpgradeAmount;
|
||||
@@ -209,10 +211,12 @@ public sealed partial class RevenantSystem
|
||||
|
||||
//KILL THEMMMM
|
||||
|
||||
if (!_mobThresholdSystem.TryGetThresholdForState(args.Args.Target.Value, MobState.Dead, out var damage))
|
||||
return;
|
||||
// Begin Offbrand Removals
|
||||
// if (!_mobThresholdSystem.TryGetThresholdForState(args.Args.Target.Value, MobState.Dead, out var damage))
|
||||
// return;
|
||||
// End Offbrand Removals
|
||||
DamageSpecifier dspec = new();
|
||||
dspec.DamageDict.Add("Cold", damage.Value);
|
||||
dspec.DamageDict.Add("Cold", component.HarvestColdDamage); // Offbrand - use a fixed amount of cold
|
||||
_damage.TryChangeDamage(args.Args.Target, dspec, true, origin: uid);
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
@@ -44,6 +44,7 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Zombies;
|
||||
|
||||
@@ -77,6 +78,8 @@ public sealed partial class ZombieSystem
|
||||
private static readonly ProtoId<NpcFactionPrototype> ZombieFaction = "Zombie";
|
||||
private static readonly string MindRoleZombie = "MindRoleZombie";
|
||||
private static readonly List<ProtoId<AntagPrototype>> BannableZombiePrototypes = ["Zombie"];
|
||||
private static readonly EntProtoId AddOnWoundableZombified = "AddOnWoundableZombified"; // Offbrand
|
||||
private static readonly EntProtoId AddOnAnyZombified = "AddOnAnyZombified"; // Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Handles an entity turning into a zombie when they die or go into crit
|
||||
@@ -144,6 +147,36 @@ public sealed partial class ZombieSystem
|
||||
RemComp<ComplexInteractionComponent>(target);
|
||||
RemComp<SentienceTargetComponent>(target);
|
||||
|
||||
// Begin Offbrand
|
||||
if (RemComp<WoundableComponent>(target))
|
||||
{
|
||||
RemComp<HeartrateComponent>(target);
|
||||
RemComp<HeartDefibrillatableComponent>(target);
|
||||
RemComp<HeartStopOnHypovolemiaComponent>(target);
|
||||
RemComp<HeartStopOnHighStrainComponent>(target);
|
||||
RemComp<HeartStopOnBrainHealthComponent>(target);
|
||||
RemComp<PainComponent>(target);
|
||||
RemComp<HeartrateAlertsComponent>(target);
|
||||
RemComp<ShockThresholdsComponent>(target);
|
||||
RemComp<ShockAlertsComponent>(target);
|
||||
RemComp<BrainDamageComponent>(target);
|
||||
RemComp<BrainDamageOxygenationComponent>(target);
|
||||
RemComp<BrainDamageThresholdsComponent>(target);
|
||||
RemComp<BrainDamageOnDamageComponent>(target);
|
||||
RemComp<HeartDamageOnDamageComponent>(target);
|
||||
RemComp<MaximumDamageComponent>(target);
|
||||
RemComp<CprTargetComponent>(target);
|
||||
RemComp<Content.Server.Construction.Components.ConstructionComponent>(target);
|
||||
RemComp<CryostasisFactorComponent>(target);
|
||||
RemComp<UniqueWoundOnDamageComponent>(target);
|
||||
RemComp<IntrinsicPainComponent>(target);
|
||||
|
||||
var entProto = _protoManager.Index(AddOnWoundableZombified);
|
||||
EntityManager.RemoveComponents(target, entProto.Components);
|
||||
EntityManager.AddComponents(target, entProto.Components);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
//funny voice
|
||||
var accentType = "zombie";
|
||||
if (TryComp<ZombieAccentOverrideComponent>(target, out var accent))
|
||||
@@ -227,11 +260,18 @@ public sealed partial class ZombieSystem
|
||||
//The zombie gets the assigned damage weaknesses and strengths
|
||||
_damageable.SetDamageModifierSetId(target, "Zombie");
|
||||
|
||||
// Begin Offbrand
|
||||
var allProto = _protoManager.Index(AddOnAnyZombified);
|
||||
EntityManager.RemoveComponents(target, allProto.Components);
|
||||
EntityManager.AddComponents(target, allProto.Components);
|
||||
// End Offbrand
|
||||
|
||||
//This makes it so the zombie doesn't take bloodloss damage.
|
||||
//NOTE: they are supposed to bleed, just not take damage
|
||||
_bloodstream.SetBloodLossThreshold(target, 0f);
|
||||
//Give them zombie blood
|
||||
_bloodstream.ChangeBloodReagent(target, zombiecomp.NewBloodReagent);
|
||||
_bloodstream.FlushChemicals(target, null, 100); // Offbrand
|
||||
|
||||
//This is specifically here to combat insuls, because frying zombies on grilles is funny as shit.
|
||||
_inventory.TryUnequip(target, "gloves", true, true);
|
||||
@@ -256,6 +296,11 @@ public sealed partial class ZombieSystem
|
||||
_faction.ClearFactions(target, dirty: false);
|
||||
_faction.AddFaction(target, ZombieFaction);
|
||||
|
||||
// Begin Offbrand
|
||||
var rejuv = new Content.Shared.Rejuvenate.RejuvenateEvent();
|
||||
RaiseLocalEvent(target, rejuv);
|
||||
// End Offbrand
|
||||
|
||||
//gives it the funny "Zombie ___" name.
|
||||
_nameMod.RefreshNameModifiers(target);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ using Content.Shared.Zombies;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Server.Zombies
|
||||
{
|
||||
@@ -43,6 +44,7 @@ namespace Content.Server.Zombies
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly Content.Shared._Offbrand.Wounds.BrainDamageSystem _brainDamage = default!; // Offbrand
|
||||
|
||||
public readonly ProtoId<NpcFactionPrototype> Faction = "Zombie";
|
||||
|
||||
@@ -138,6 +140,7 @@ namespace Content.Server.Zombies
|
||||
: 1f;
|
||||
|
||||
_damageable.TryChangeDamage(uid, comp.Damage * multiplier, true, false, damage);
|
||||
_brainDamage.TryChangeBrainDamage(uid, multiplier / 2f); // Offbrand
|
||||
}
|
||||
|
||||
// Heal the zombified
|
||||
@@ -255,7 +258,7 @@ namespace Content.Server.Zombies
|
||||
{
|
||||
args.BonusDamage = -args.BaseDamage;
|
||||
}
|
||||
else
|
||||
else if (!HasComp<WoundableComponent>(entity)) // Offbrand
|
||||
{
|
||||
if (!HasComp<ZombieImmuneComponent>(entity) && !HasComp<NonSpreaderZombieComponent>(args.User) && _random.Prob(GetZombieInfectionChance(entity, component)))
|
||||
{
|
||||
@@ -264,7 +267,7 @@ namespace Content.Server.Zombies
|
||||
}
|
||||
}
|
||||
|
||||
if (_mobState.IsIncapacitated(entity, mobState) && !HasComp<ZombieComponent>(entity) && !HasComp<ZombieImmuneComponent>(entity))
|
||||
if (_mobState.IsIncapacitated(entity, mobState) && !HasComp<ZombieComponent>(entity) && !HasComp<ZombieImmuneComponent>(entity) && !HasComp<WoundableComponent>(entity)) // Offbrand
|
||||
{
|
||||
ZombifyEntity(entity);
|
||||
args.BonusDamage = -args.BaseDamage;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Shared._Offbrand.EntityEffects;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Server._Offbrand.EntityEffects;
|
||||
|
||||
public sealed class MetaboliteThresholdSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CheckEntityEffectConditionEvent<MetaboliteThreshold>>(OnCheckMetaboliteThreshold);
|
||||
}
|
||||
|
||||
private void OnCheckMetaboliteThreshold(ref CheckEntityEffectConditionEvent<MetaboliteThreshold> args)
|
||||
{
|
||||
if (args.Args is not EntityEffectReagentArgs reagentArgs)
|
||||
throw new NotImplementedException();
|
||||
|
||||
var reagent = args.Condition.Reagent;
|
||||
if (reagent == null)
|
||||
reagent = reagentArgs.Reagent?.ID;
|
||||
|
||||
if (reagent is not { } metaboliteReagent)
|
||||
{
|
||||
args.Result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<MetabolizerComponent>(reagentArgs.OrganEntity, out var metabolizer))
|
||||
{
|
||||
args.Result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var metabolites = metabolizer.Metabolites;
|
||||
|
||||
var quant = FixedPoint2.Zero;
|
||||
metabolites.TryGetValue(metaboliteReagent, out quant);
|
||||
|
||||
if (args.Condition.IncludeBloodstream && reagentArgs.Source != null)
|
||||
{
|
||||
quant += reagentArgs.Source.GetTotalPrototypeQuantity(metaboliteReagent);
|
||||
}
|
||||
|
||||
args.Result = quant >= args.Condition.Min && quant <= args.Condition.Max;
|
||||
}
|
||||
}
|
||||
27
Content.Server/_Offbrand/EntityEffects/ZombifySystem.cs
Normal file
27
Content.Server/_Offbrand/EntityEffects/ZombifySystem.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Zombies;
|
||||
using Content.Shared._Offbrand.EntityEffects;
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Server._Offbrand.EntityEffects;
|
||||
|
||||
public sealed class ZombifySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ZombieSystem _zombie = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<Zombify>>(OnExecuteZombify);
|
||||
}
|
||||
|
||||
private void OnExecuteZombify(ref ExecuteEntityEffectEvent<Zombify> args)
|
||||
{
|
||||
_zombie.ZombifyEntity(args.Args.TargetEntity);
|
||||
}
|
||||
}
|
||||
35
Content.Server/_Offbrand/MMI/MMIExtractorEui.cs
Normal file
35
Content.Server/_Offbrand/MMI/MMIExtractorEui.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared._Offbrand.MMI;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Eui;
|
||||
|
||||
namespace Content.Server._Offbrand.MMI;
|
||||
|
||||
public sealed class MMIExtractorEui : BaseEui
|
||||
{
|
||||
private readonly MMIExtractorSystem _mmiExtractor;
|
||||
private readonly DoAfterId _doAfterId;
|
||||
|
||||
public MMIExtractorEui(MMIExtractorSystem mmiExtractor, DoAfterId doAfterId)
|
||||
{
|
||||
_mmiExtractor = mmiExtractor;
|
||||
_doAfterId = doAfterId;
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
if (msg is not MMIExtractorMessage choice || !choice.Accepted)
|
||||
_mmiExtractor.Decline(_doAfterId);
|
||||
else
|
||||
_mmiExtractor.Accept(_doAfterId);
|
||||
|
||||
Close();
|
||||
}
|
||||
}
|
||||
176
Content.Server/_Offbrand/MMI/MMIExtractorSystem.cs
Normal file
176
Content.Server/_Offbrand/MMI/MMIExtractorSystem.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared._Offbrand.MMI;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server._Offbrand.MMI;
|
||||
|
||||
public sealed class MMIExtractorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BrainDamageSystem _brainDamage = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly EuiManager _eui = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _slots = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MMIExtractorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<BrainComponent, MMIExtractorDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<MMIExtractorComponent> ent, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || !args.CanReach || args.Target == null)
|
||||
return;
|
||||
|
||||
if (TryExtract(ent, args.Target.Value, args.User))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private bool TryExtract(Entity<MMIExtractorComponent> ent, EntityUid target, EntityUid user)
|
||||
{
|
||||
if (!_whitelist.CheckBoth(target, ent.Comp.Blacklist, ent.Comp.Whitelist))
|
||||
return false;
|
||||
|
||||
if (!_mind.TryGetMind(target, out _, out var mind) || !_player.TryGetSessionById(mind.UserId, out var playerSession))
|
||||
{
|
||||
_chat.TrySendInGameICMessage(ent,
|
||||
Loc.GetString(ent.Comp.NoMind),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_body.TryGetBodyOrganEntityComps<BrainComponent>(target, out var organs))
|
||||
{
|
||||
_chat.TrySendInGameICMessage(ent,
|
||||
Loc.GetString(ent.Comp.Brainless),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (organs.Count != 1)
|
||||
{
|
||||
_chat.TrySendInGameICMessage(ent,
|
||||
Loc.GetString(ent.Comp.TooManyBrains),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var brain = organs[0];
|
||||
|
||||
_chat.TrySendInGameICMessage(ent,
|
||||
Loc.GetString(ent.Comp.Asking),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
|
||||
var args =
|
||||
new DoAfterArgs(EntityManager, user, ent.Comp.Delay, new MMIExtractorDoAfterEvent(), brain, target: target, used: ent)
|
||||
{
|
||||
NeedHand = true,
|
||||
BreakOnMove = true,
|
||||
BreakOnWeightlessMove = false,
|
||||
};
|
||||
|
||||
if (_doAfter.TryStartDoAfter(args, out var id))
|
||||
_eui.OpenEui(new MMIExtractorEui(this, id.Value), playerSession);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Decline(DoAfterId id)
|
||||
{
|
||||
_doAfter.Cancel(id);
|
||||
|
||||
if (!TryComp<DoAfterComponent>(id.Uid, out var doAfters))
|
||||
return;
|
||||
|
||||
var dict = doAfters.DoAfters; // i love access workarounds
|
||||
if (!dict.TryGetValue(id.Index, out var doAfter))
|
||||
return;
|
||||
|
||||
if (doAfter.Args.Used is not { } mmi || !TryComp<MMIExtractorComponent>(mmi, out var mmiComp))
|
||||
return;
|
||||
|
||||
_chat.TrySendInGameICMessage(mmi,
|
||||
Loc.GetString(mmiComp.Denied),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
}
|
||||
|
||||
public void Accept(DoAfterId id)
|
||||
{
|
||||
if (!TryComp<DoAfterComponent>(id.Uid, out var doAfters))
|
||||
return;
|
||||
|
||||
var dict = doAfters.DoAfters; // i love access workarounds
|
||||
if (!dict.TryGetValue(id.Index, out var doAfter))
|
||||
return;
|
||||
|
||||
if (doAfter.Args.Used is not { } mmi || !TryComp<MMIExtractorComponent>(mmi, out var mmiComp))
|
||||
return;
|
||||
|
||||
if (doAfter.Args.Event is not MMIExtractorDoAfterEvent evt)
|
||||
return;
|
||||
|
||||
evt.Accepted = true;
|
||||
_chat.TrySendInGameICMessage(mmi,
|
||||
Loc.GetString(mmiComp.Accepted),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<BrainComponent> ent, ref MMIExtractorDoAfterEvent evt)
|
||||
{
|
||||
if (evt.Handled || evt.Cancelled)
|
||||
return;
|
||||
|
||||
if (evt.Args.Used is not { } mmi || !TryComp<MMIExtractorComponent>(mmi, out var mmiComp))
|
||||
return;
|
||||
|
||||
if (!TryComp<MMIComponent>(mmi, out var insertionComp))
|
||||
return;
|
||||
|
||||
if (!evt.Accepted)
|
||||
{
|
||||
_chat.TrySendInGameICMessage(mmi,
|
||||
Loc.GetString(mmiComp.NoResponse),
|
||||
InGameICChatType.Speak,
|
||||
true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_slots.TryInsert(mmi, insertionComp.BrainSlotId, ent, null))
|
||||
return;
|
||||
|
||||
if (evt.Args.Target is { } target)
|
||||
_brainDamage.KillBrain(target);
|
||||
}
|
||||
}
|
||||
31
Content.Server/_Offbrand/Surgery/SetNode.cs
Normal file
31
Content.Server/_Offbrand/Surgery/SetNode.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Construction;
|
||||
using Content.Server.Construction;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Offbrand.Surgery;
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class SetNode : IGraphAction
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public string Node;
|
||||
|
||||
[DataField]
|
||||
public List<IGraphCondition> RepeatConditions = new();
|
||||
|
||||
public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager)
|
||||
{
|
||||
var construction = entityManager.System<ConstructionSystem>();
|
||||
if (!construction.CheckConditions(uid, RepeatConditions) || RepeatConditions.Count == 0)
|
||||
{
|
||||
construction.SetPathfindingTarget(uid, null);
|
||||
}
|
||||
construction.ChangeNode(uid, userUid, Node);
|
||||
construction.ResetEdge(uid);
|
||||
}
|
||||
}
|
||||
31
Content.Server/_Offbrand/Surgery/SurgeryGuideTargetSystem.cs
Normal file
31
Content.Server/_Offbrand/Surgery/SurgeryGuideTargetSystem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Construction;
|
||||
using Content.Shared._Offbrand.Surgery;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Offbrand.Surgery;
|
||||
|
||||
public sealed class SurgeryGuideTargetSystem : SharedSurgeryGuideTargetSystem
|
||||
{
|
||||
[Dependency] private readonly ConstructionSystem _construction = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
protected override void OnStartSurgery(Entity<SurgeryGuideTargetComponent> ent, ref SurgeryGuideStartSurgeryMessage args)
|
||||
{
|
||||
base.OnStartSurgery(ent, ref args);
|
||||
if (!_prototype.TryIndex(args.Prototype, out var construction))
|
||||
return;
|
||||
|
||||
_construction.SetPathfindingTarget(ent, construction.TargetNode);
|
||||
}
|
||||
|
||||
protected override void OnStartCleanup(Entity<SurgeryGuideTargetComponent> ent, ref SurgeryGuideStartCleanupMessage args)
|
||||
{
|
||||
base.OnStartCleanup(ent, ref args);
|
||||
_construction.SetPathfindingTarget(ent, "Base");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Offbrand.VariationPass;
|
||||
|
||||
/// <summary>
|
||||
/// A bodge component to spawn the given entities near the lathe of the given prototype in lieu of mapping effort
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SupplyNearLatheVariationPassComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId LathePrototype;
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntProtoId EntityToSpawn;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.GameTicking.Rules.VariationPass;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Lathe;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Offbrand.VariationPass;
|
||||
|
||||
/// <inheritdoc cref="SupplyNearLatheVariationPassComponent"/>
|
||||
public sealed class SupplyNearLatheVariationPassSystem : VariationPassSystem<SupplyNearLatheVariationPassComponent>
|
||||
{
|
||||
private EntityUid? FindLatheOnStation(EntProtoId proto, EntityUid station)
|
||||
{
|
||||
var query = AllEntityQuery<LatheComponent>();
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
if (MetaData(uid).EntityPrototype?.ID is { } existingProto && existingProto == proto)
|
||||
return uid;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void ApplyVariation(Entity<SupplyNearLatheVariationPassComponent> ent, ref StationVariationPassEvent args)
|
||||
{
|
||||
if (FindLatheOnStation(ent.Comp.LathePrototype, args.Station) is not { } lathe)
|
||||
return;
|
||||
|
||||
SpawnNextToOrDrop(ent.Comp.EntityToSpawn, lathe);
|
||||
}
|
||||
}
|
||||
50
Content.Server/_Offbrand/Wounds/BrainGaspThresholdsSystem.cs
Normal file
50
Content.Server/_Offbrand/Wounds/BrainGaspThresholdsSystem.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
|
||||
namespace Content.Server._Offbrand.Wounds;
|
||||
|
||||
public sealed class BrainGaspThresholdsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BrainGaspThresholdsComponent, AfterBrainDamageChanged>(OnAfterBrainDamageChanged);
|
||||
}
|
||||
|
||||
private void OnAfterBrainDamageChanged(Entity<BrainGaspThresholdsComponent> ent, ref AfterBrainDamageChanged args)
|
||||
{
|
||||
var brain = Comp<BrainDamageComponent>(ent);
|
||||
|
||||
var message = ent.Comp.MessageThresholds.HighestMatch(brain.Damage);
|
||||
if (message == ent.Comp.CurrentMessage)
|
||||
return;
|
||||
|
||||
var previousMessage = ent.Comp.CurrentMessage;
|
||||
|
||||
ent.Comp.CurrentMessage = message;
|
||||
Dirty(ent);
|
||||
|
||||
if (previousMessage is { } previous)
|
||||
{
|
||||
var previousKey = ent.Comp.MessageThresholds.FirstOrDefault(x => x.Value == previous).Key;
|
||||
var currentKey = ent.Comp.MessageThresholds.FirstOrDefault(x => x.Value == message).Key;
|
||||
|
||||
if (previousKey >= currentKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (message is { } msg)
|
||||
_chat.TryEmoteWithChat(ent.Owner, msg, ignoreActionBlocker: true);
|
||||
}
|
||||
}
|
||||
39
Content.Server/_Offbrand/Wounds/CryostasisFactorSystem.cs
Normal file
39
Content.Server/_Offbrand/Wounds/CryostasisFactorSystem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Medical.Cryogenics;
|
||||
using Content.Shared.Temperature;
|
||||
|
||||
namespace Content.Server._Offbrand.Wounds;
|
||||
|
||||
public sealed class CryostasisFactorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly SharedMetabolizerSystem _metabolizer = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CryostasisFactorComponent, OnTemperatureChangeEvent>(OnTemperatureChange);
|
||||
SubscribeLocalEvent<CryostasisFactorComponent, GetMetabolicMultiplierEvent>(OnGetMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnTemperatureChange(Entity<CryostasisFactorComponent> ent, ref OnTemperatureChangeEvent args)
|
||||
{
|
||||
_metabolizer.UpdateMetabolicMultiplier(ent);
|
||||
}
|
||||
|
||||
private void OnGetMetabolicMultiplier(Entity<CryostasisFactorComponent> ent, ref GetMetabolicMultiplierEvent args)
|
||||
{
|
||||
if (!TryComp<TemperatureComponent>(ent, out var temp))
|
||||
return;
|
||||
|
||||
args.Multiplier *= Math.Max(ent.Comp.TemperatureCoefficient * temp.CurrentTemperature + ent.Comp.TemperatureConstant, 1);
|
||||
}
|
||||
}
|
||||
51
Content.Server/_Offbrand/Wounds/ShockGaspThresholdsSystem.cs
Normal file
51
Content.Server/_Offbrand/Wounds/ShockGaspThresholdsSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
|
||||
namespace Content.Server._Offbrand.Wounds;
|
||||
|
||||
public sealed class ShockGaspThresholdsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly PainSystem _pain = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShockGaspThresholdsComponent, AfterShockChangeEvent>(OnAfterShockChange);
|
||||
}
|
||||
|
||||
private void OnAfterShockChange(Entity<ShockGaspThresholdsComponent> ent, ref AfterShockChangeEvent args)
|
||||
{
|
||||
var shock = _pain.GetShock(ent.Owner);
|
||||
|
||||
var message = ent.Comp.MessageThresholds.HighestMatch(shock);
|
||||
if (message == ent.Comp.CurrentMessage)
|
||||
return;
|
||||
|
||||
var previousMessage = ent.Comp.CurrentMessage;
|
||||
|
||||
ent.Comp.CurrentMessage = message;
|
||||
Dirty(ent);
|
||||
|
||||
if (previousMessage is { } previous)
|
||||
{
|
||||
var previousKey = ent.Comp.MessageThresholds.FirstOrDefault(x => x.Value == previous).Key;
|
||||
var currentKey = ent.Comp.MessageThresholds.FirstOrDefault(x => x.Value == message).Key;
|
||||
|
||||
if (previousKey >= currentKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (message is { } msg)
|
||||
_chat.TryEmoteWithChat(ent.Owner, msg, ignoreActionBlocker: true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Offbrand.Wounds;
|
||||
|
||||
public sealed class WoundableHealthAnalyzerSystem : SharedWoundableHealthAnalyzerSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
public override Dictionary<ProtoId<ReagentPrototype>, (FixedPoint2 InBloodstream, FixedPoint2 Metabolites)>? SampleReagents(EntityUid uid, out bool hasNonMedical)
|
||||
{
|
||||
hasNonMedical = false;
|
||||
if (!TryComp<BloodstreamComponent>(uid, out var bloodstream))
|
||||
return null;
|
||||
|
||||
if (!_solutionContainer.ResolveSolution(uid, bloodstream.ChemicalSolutionName, ref bloodstream.ChemicalSolution))
|
||||
return null;
|
||||
|
||||
var ret = new Dictionary<ProtoId<ReagentPrototype>, (FixedPoint2 InBloodstream, FixedPoint2 Metabolites)>();
|
||||
|
||||
foreach (var (reagentId, quantity) in bloodstream.ChemicalSolution.Value.Comp.Solution.Contents)
|
||||
{
|
||||
ProtoId<ReagentPrototype> reagent = reagentId.Prototype;
|
||||
|
||||
if (_prototype.Index(reagent).Group != MedicineGroup)
|
||||
{
|
||||
hasNonMedical = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ret.ContainsKey(reagent))
|
||||
ret[reagent] = (0, 0);
|
||||
|
||||
ret[reagent] = (ret[reagent].InBloodstream + quantity, ret[reagent].Metabolites);
|
||||
}
|
||||
|
||||
foreach (var metabolizer in _body.GetBodyOrganEntityComps<MetabolizerComponent>(uid))
|
||||
{
|
||||
if (metabolizer.Comp1.SolutionName != bloodstream.ChemicalSolutionName)
|
||||
continue;
|
||||
|
||||
foreach (var (reagent, quantity) in metabolizer.Comp1.Metabolites)
|
||||
{
|
||||
if (_prototype.Index(reagent).Group != MedicineGroup)
|
||||
{
|
||||
hasNonMedical = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ret.ContainsKey(reagent))
|
||||
ret[reagent] = (0, 0);
|
||||
|
||||
ret[reagent] = (ret[reagent].InBloodstream, ret[reagent].Metabolites + quantity);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -85,14 +85,14 @@ public sealed partial class BloodstreamComponent : Component
|
||||
/// The default values are defined per mob/species in YML.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public DamageSpecifier BloodlossDamage = new();
|
||||
public DamageSpecifier? BloodlossDamage = new(); // Offbrand: we don't need this
|
||||
|
||||
/// <summary>
|
||||
/// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
|
||||
/// The default values are defined per mob/species in YML.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public DamageSpecifier BloodlossHealDamage = new();
|
||||
public DamageSpecifier? BloodlossHealDamage = new(); // Offbrand: we don't need this
|
||||
|
||||
// TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
|
||||
/// <summary>
|
||||
@@ -115,7 +115,7 @@ public sealed partial class BloodstreamComponent : Component
|
||||
/// For example, piercing damage is increased while poison damage is nullified entirely.
|
||||
/// </remarks>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<DamageModifierSetPrototype> DamageBleedModifiers = "BloodlossHuman";
|
||||
public ProtoId<DamageModifierSetPrototype>? DamageBleedModifiers = "BloodlossHuman"; // Offbrand: we don't want this
|
||||
|
||||
/// <summary>
|
||||
/// The sound to be played when a weapon instantly deals blood loss damage.
|
||||
|
||||
@@ -77,11 +77,14 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
TryModifyBloodLevel((uid, bloodstream), bloodstream.BloodRefreshAmount);
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
var bleedLevel = EffectiveBleedLevel((uid, bloodstream));
|
||||
|
||||
// Removes blood from the bloodstream based on bleed amount (bleed rate)
|
||||
// as well as stop their bleeding to a certain extent.
|
||||
if (bloodstream.BleedAmount > 0)
|
||||
if (bleedLevel > 0)
|
||||
{
|
||||
var ev = new BleedModifierEvent(bloodstream.BleedAmount, bloodstream.BleedReductionAmount);
|
||||
var ev = new BleedModifierEvent(bleedLevel, bloodstream.BleedReductionAmount);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
// Blood is removed from the bloodstream at a 1-1 rate with the bleed amount
|
||||
@@ -91,9 +94,18 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
TryModifyBleedAmount((uid, bloodstream), -ev.BleedReductionAmount);
|
||||
}
|
||||
|
||||
if (bleedLevel == 0)
|
||||
_alertsSystem.ClearAlert(uid, bloodstream.BleedingAlert);
|
||||
else
|
||||
{
|
||||
var severity = (short)Math.Clamp(Math.Round(bleedLevel, MidpointRounding.ToZero), 0, 10);
|
||||
_alertsSystem.ShowAlert(uid, bloodstream.BleedingAlert, severity);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
// deal bloodloss damage if their blood level is below a threshold.
|
||||
var bloodPercentage = GetBloodLevelPercentage((uid, bloodstream));
|
||||
if (bloodPercentage < bloodstream.BloodlossThreshold && !_mobStateSystem.IsDead(uid))
|
||||
if (bloodPercentage < bloodstream.BloodlossThreshold && !_mobStateSystem.IsDead(uid) && bloodstream.BloodlossDamage is not null) // Offbrand
|
||||
{
|
||||
// bloodloss damage is based on the base value, and modified by how low your blood level is.
|
||||
var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage);
|
||||
@@ -106,7 +118,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
// Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out
|
||||
_status.TrySetStatusEffectDuration(uid, Bloodloss);
|
||||
}
|
||||
else if (!_mobStateSystem.IsDead(uid))
|
||||
else if (!_mobStateSystem.IsDead(uid) && bloodstream.BloodlossHealDamage is not null) // Offbrand
|
||||
{
|
||||
// If they're healthy, we'll try and heal some bloodloss instead.
|
||||
_damageableSystem.TryChangeDamage(
|
||||
@@ -241,26 +253,28 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void OnHealthBeingExamined(Entity<BloodstreamComponent> ent, ref HealthBeingExaminedEvent args)
|
||||
{
|
||||
var bleedLevel = EffectiveBleedLevel(ent); // Offbrand
|
||||
|
||||
// Shows massively bleeding at 0.75x the max bleed rate.
|
||||
if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.75f)
|
||||
if (bleedLevel > ent.Comp.MaxBleedAmount * 0.75f) // Offbrand
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-massive-bleeding", ("target", ent.Owner)));
|
||||
}
|
||||
// Shows bleeding message when bleeding above half the max rate, but less than massively.
|
||||
else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.5f)
|
||||
else if (bleedLevel > ent.Comp.MaxBleedAmount * 0.5f) // Offbrand
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-strong-bleeding", ("target", ent.Owner)));
|
||||
}
|
||||
// Shows bleeding message when bleeding above 0.25x the max rate, but less than half the max.
|
||||
else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.25f)
|
||||
else if (bleedLevel > ent.Comp.MaxBleedAmount * 0.25f) // Offbrand
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner)));
|
||||
}
|
||||
// Shows bleeding message when bleeding below 0.25x the max cap
|
||||
else if (ent.Comp.BleedAmount > 0)
|
||||
else if (bleedLevel > 0) // Offbrand
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-slight-bleeding", ("target", ent.Owner)));
|
||||
@@ -399,6 +413,19 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
public float EffectiveBleedLevel(Entity<BloodstreamComponent> ent)
|
||||
{
|
||||
var evt = new Content.Shared._Offbrand.Wounds.GetBleedLevelEvent(ent.Comp.BleedAmount);
|
||||
RaiseLocalEvent(ent, ref evt);
|
||||
|
||||
var modifiers = new Content.Shared._Offbrand.Wounds.ModifyBleedLevelEvent(evt.BleedLevel);
|
||||
RaiseLocalEvent(ent, ref modifiers);
|
||||
|
||||
return modifiers.BleedLevel;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Tries to make an entity bleed more or less.
|
||||
/// </summary>
|
||||
@@ -412,13 +439,17 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
|
||||
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BleedAmount));
|
||||
|
||||
if (ent.Comp.BleedAmount == 0)
|
||||
// Begin Offbrand
|
||||
var bleedLevel = EffectiveBleedLevel((ent, ent.Comp));
|
||||
|
||||
if (bleedLevel == 0)
|
||||
_alertsSystem.ClearAlert(ent.Owner, ent.Comp.BleedingAlert);
|
||||
else
|
||||
{
|
||||
var severity = (short)Math.Clamp(Math.Round(ent.Comp.BleedAmount, MidpointRounding.ToZero), 0, 10);
|
||||
var severity = (short)Math.Clamp(Math.Round(bleedLevel, MidpointRounding.ToZero), 0, 10);
|
||||
_alertsSystem.ShowAlert(ent.Owner, ent.Comp.BleedingAlert, severity);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed partial class StrapComponent : Component
|
||||
/// The entities that are currently buckled to this strap.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[Access(typeof(SharedBuckleSystem), Other = AccessPermissions.ReadExecute)] // Offbrand
|
||||
public HashSet<EntityUid> BuckledEntities = new();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -265,7 +265,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
}
|
||||
|
||||
|
||||
protected void UpdateAppearance(Entity<AppearanceComponent?> container, Entity<SolutionComponent, ContainedSolutionComponent> soln)
|
||||
public void UpdateAppearance(Entity<AppearanceComponent?> container, Entity<SolutionComponent, ContainedSolutionComponent> soln) // Offbrand
|
||||
{
|
||||
var (uid, appearanceComponent) = container;
|
||||
if (!HasComp<SolutionContainerVisualsComponent>(uid) || !Resolve(uid, ref appearanceComponent, logMissing: false))
|
||||
|
||||
@@ -278,6 +278,12 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
[DataField("metabolismRate")]
|
||||
public FixedPoint2 MetabolismRate = FixedPoint2.New(0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand: Status effects to apply whilst this reagent is metabolising
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ReagentStatusEffectEntry> StatusEffects = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of effects to apply when these reagents are metabolized.
|
||||
/// </summary>
|
||||
@@ -290,12 +296,51 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
return new ReagentEffectsGuideEntry(MetabolismRate,
|
||||
Effects
|
||||
.Select(x => x.GuidebookEffectDescription(prototype, entSys)) // hate.
|
||||
.Concat(StatusEffects.Select(x => x.Describe(prototype, entSys))) // Offbrand
|
||||
.Where(x => x is not null)
|
||||
.Select(x => x!)
|
||||
.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
[DataDefinition]
|
||||
public sealed partial class ReagentStatusEffectEntry
|
||||
{
|
||||
[DataField]
|
||||
public EntityEffectCondition[]? Conditions;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId StatusEffect;
|
||||
|
||||
public bool ShouldApplyStatusEffect(EntityEffectBaseArgs args)
|
||||
{
|
||||
if (Conditions != null)
|
||||
{
|
||||
foreach (var cond in Conditions)
|
||||
{
|
||||
if (!cond.Condition(args))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string? Describe(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (!prototype.TryIndex(StatusEffect, out var effectProtoData))
|
||||
return null;
|
||||
|
||||
return Loc.GetString("reagent-guidebook-status-effect", ("effect", effectProtoData.Name ?? string.Empty),
|
||||
("conditionCount", Conditions?.Length ?? 0),
|
||||
("conditions",
|
||||
Content.Shared.Localizations.ContentLocalizationManager.FormatList(Conditions?.Select(x => x.GuidebookExplanation(prototype)).ToList() ??
|
||||
new List<string>())));
|
||||
}
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct ReagentEffectsGuideEntry
|
||||
{
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace Content.Shared.Construction
|
||||
[DataField("entity", customTypeSerializer: typeof(GraphNodeEntitySerializer))]
|
||||
public IGraphNodeEntity Entity { get; private set; } = new NullNodeEntity();
|
||||
|
||||
// Begin Offbrand
|
||||
[DataField]
|
||||
public LocId? SurgeryName;
|
||||
// End Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Ignore requests to change the entity if the entity's current prototype inherits from specified replacement
|
||||
/// </summary>
|
||||
|
||||
@@ -98,4 +98,5 @@ public enum ConstructionType
|
||||
{
|
||||
Structure,
|
||||
Item,
|
||||
Surgery, // Offbrand
|
||||
}
|
||||
|
||||
@@ -46,6 +46,13 @@ namespace Content.Shared.Construction.Steps
|
||||
return typeof(PartAssemblyConstructionGraphStep);
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
if (node.Has("whitelist") || node.Has("blacklist"))
|
||||
{
|
||||
return typeof(Content.Shared._Offbrand.Surgery.WhitelistConstructionGraphStep);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,24 @@ namespace Content.Shared.Damage
|
||||
return false;
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
/// <summary>
|
||||
/// Returns true if the specifier contains any negative damage values.
|
||||
/// Differs from <see cref="Empty"/> as a damage specifier might contain entries with zeroes.
|
||||
/// This also returns false if the specifier only contains negative values.
|
||||
/// </summary>
|
||||
public bool AnyNegative()
|
||||
{
|
||||
foreach (var value in DamageDict.Values)
|
||||
{
|
||||
if (value < FixedPoint2.Zero)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Whether this damage specifier has any entries.
|
||||
/// </summary>
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Content.Shared.Damage
|
||||
SubscribeLocalEvent<DamageableComponent, ComponentHandleState>(DamageableHandleState);
|
||||
SubscribeLocalEvent<DamageableComponent, ComponentGetState>(DamageableGetState);
|
||||
SubscribeLocalEvent<DamageableComponent, OnIrradiatedEvent>(OnIrradiated);
|
||||
SubscribeLocalEvent<DamageableComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<DamageableComponent, RejuvenateEvent>(OnRejuvenate, after: [typeof(Content.Shared.StatusEffectNew.StatusEffectsSystem)]); // Offbrand
|
||||
|
||||
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
_damageableQuery = GetEntityQuery<DamageableComponent>();
|
||||
@@ -145,7 +145,7 @@ namespace Content.Shared.Damage
|
||||
/// The damage changed event is used by other systems, such as damage thresholds.
|
||||
/// </remarks>
|
||||
public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSpecifier? damageDelta = null,
|
||||
bool interruptsDoAfters = true, EntityUid? origin = null)
|
||||
bool interruptsDoAfters = true, EntityUid? origin = null, bool forcedRefresh = false) // Offbrand
|
||||
{
|
||||
component.Damage.GetDamagePerGroup(_prototypeManager, component.DamagePerGroup);
|
||||
component.TotalDamage = component.Damage.GetTotal();
|
||||
@@ -156,7 +156,7 @@ namespace Content.Shared.Damage
|
||||
var data = new DamageVisualizerGroupData(component.DamagePerGroup.Keys.ToList());
|
||||
_appearance.SetData(uid, DamageVisualizerKeys.DamageUpdateGroups, data, appearance);
|
||||
}
|
||||
RaiseLocalEvent(uid, new DamageChangedEvent(component, damageDelta, interruptsDoAfters, origin));
|
||||
RaiseLocalEvent(uid, new DamageChangedEvent(component, damageDelta, interruptsDoAfters, origin, forcedRefresh)); // Offbrand
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,7 +172,7 @@ namespace Content.Shared.Damage
|
||||
/// null if the user had no applicable components that can take damage.
|
||||
/// </returns>
|
||||
public DamageSpecifier? TryChangeDamage(EntityUid? uid, DamageSpecifier damage, bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true, DamageableComponent? damageable = null, EntityUid? origin = null)
|
||||
bool interruptsDoAfters = true, DamageableComponent? damageable = null, EntityUid? origin = null, bool forceRefresh = false) // Offbrand
|
||||
{
|
||||
if (!uid.HasValue || !_damageableQuery.Resolve(uid.Value, ref damageable, false))
|
||||
{
|
||||
@@ -180,7 +180,7 @@ namespace Content.Shared.Damage
|
||||
return null;
|
||||
}
|
||||
|
||||
if (damage.Empty)
|
||||
if (damage.Empty && !forceRefresh) // Offbrand
|
||||
{
|
||||
return damage;
|
||||
}
|
||||
@@ -214,6 +214,12 @@ namespace Content.Shared.Damage
|
||||
|
||||
damage = ApplyUniversalAllModifiers(damage);
|
||||
|
||||
// Begin Offbrand
|
||||
var beforeCommit = new Content.Shared._Offbrand.Wounds.BeforeDamageCommitEvent(damage);
|
||||
RaiseLocalEvent(uid.Value, ref beforeCommit);
|
||||
damage = beforeCommit.Damage;
|
||||
// End Offbrand
|
||||
|
||||
// TODO DAMAGE PERFORMANCE
|
||||
// Consider using a local private field instead of creating a new dictionary here.
|
||||
// Would need to check that nothing ever tries to cache the delta.
|
||||
@@ -236,7 +242,7 @@ namespace Content.Shared.Damage
|
||||
}
|
||||
|
||||
if (delta.DamageDict.Count > 0)
|
||||
DamageChanged(uid.Value, damageable, delta, interruptsDoAfters, origin);
|
||||
DamageChanged(uid.Value, damageable, delta, interruptsDoAfters, origin, forceRefresh); // Offbrand
|
||||
|
||||
return delta;
|
||||
}
|
||||
@@ -334,6 +340,7 @@ namespace Content.Shared.Damage
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, DamageableComponent component, RejuvenateEvent args)
|
||||
{
|
||||
Log.Debug("rejuvenate damage");
|
||||
TryComp<MobThresholdsComponent>(uid, out var thresholds);
|
||||
_mobThreshold.SetAllowRevives(uid, true, thresholds); // do this so that the state changes when we set the damage
|
||||
SetAllDamage(uid, component, 0);
|
||||
@@ -429,11 +436,18 @@ namespace Content.Shared.Damage
|
||||
/// </summary>
|
||||
public readonly EntityUid? Origin;
|
||||
|
||||
public DamageChangedEvent(DamageableComponent damageable, DamageSpecifier? damageDelta, bool interruptsDoAfters, EntityUid? origin)
|
||||
// Offbrand
|
||||
/// <summary>
|
||||
/// If this damage changed happened as part of a forced refresh
|
||||
/// </summary>
|
||||
public readonly bool ForcedRefresh;
|
||||
|
||||
public DamageChangedEvent(DamageableComponent damageable, DamageSpecifier? damageDelta, bool interruptsDoAfters, EntityUid? origin, bool forcedRefresh) // Offbrand
|
||||
{
|
||||
Damageable = damageable;
|
||||
DamageDelta = damageDelta;
|
||||
Origin = origin;
|
||||
ForcedRefresh = forcedRefresh; // Offbrand
|
||||
|
||||
if (DamageDelta == null)
|
||||
return;
|
||||
|
||||
@@ -46,6 +46,13 @@ namespace Content.Shared.FixedPoint
|
||||
return new(value * ShiftConstant);
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
public static FixedPoint2 FromRaw(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
public static FixedPoint2 FromCents(int value) => new(value);
|
||||
|
||||
public static FixedPoint2 FromHundredths(int value) => new(value);
|
||||
|
||||
@@ -201,13 +201,13 @@ namespace Content.Shared.FixedPoint
|
||||
}
|
||||
|
||||
// Implicit operators ftw
|
||||
public static implicit operator FixedPoint4(FixedPoint2 n) => New(n.Int());
|
||||
public static implicit operator FixedPoint4(FixedPoint2 n) => new(n.Value * 100); // Offbrand
|
||||
public static implicit operator FixedPoint4(float n) => New(n);
|
||||
public static implicit operator FixedPoint4(double n) => New(n);
|
||||
public static implicit operator FixedPoint4(int n) => New(n);
|
||||
public static implicit operator FixedPoint4(long n) => New(n);
|
||||
|
||||
public static explicit operator FixedPoint2(FixedPoint4 n) => n.Int();
|
||||
public static explicit operator FixedPoint2(FixedPoint4 n) => FixedPoint2.FromRaw((int)(n.Value / 100)); // Offbrand
|
||||
public static explicit operator float(FixedPoint4 n) => n.Float();
|
||||
public static explicit operator double(FixedPoint4 n) => n.Double();
|
||||
public static explicit operator int(FixedPoint4 n) => n.Int();
|
||||
|
||||
@@ -88,14 +88,16 @@ public sealed class HealthExaminableSystem : EntitySystem
|
||||
msg.AddMarkupOrThrow(chosenLocStr);
|
||||
}
|
||||
|
||||
// Offbrand: reordered the empty placeholder to after people have added to health examinable
|
||||
|
||||
// Anything else want to add on to this?
|
||||
RaiseLocalEvent(uid, new HealthBeingExaminedEvent(msg), true);
|
||||
|
||||
if (msg.IsEmpty)
|
||||
{
|
||||
msg.AddMarkupOrThrow(Loc.GetString($"health-examinable-{component.LocPrefix}-none"));
|
||||
}
|
||||
|
||||
// Anything else want to add on to this?
|
||||
RaiseLocalEvent(uid, new HealthBeingExaminedEvent(msg), true);
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ public sealed class IdentitySystem : EntitySystem
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, InventoryRelayedEvent<SeeIdentityAttemptEvent>>(OnRelaySeeIdentity);
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, ItemMaskToggledEvent>(OnMaskToggled);
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, Content.Shared.StatusEffectNew.StatusEffectRelayedEvent<SeeIdentityAttemptEvent>>(OnRelayedSeeIdentity); // Offbrand
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, Content.Shared.StatusEffectNew.StatusEffectAppliedEvent>((_, _, ev) => QueueIdentityUpdate(ev.Target)); // Offbrand
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, Content.Shared.StatusEffectNew.StatusEffectRemovedEvent>((_, _, ev) => QueueIdentityUpdate(ev.Target)); // Offbrand
|
||||
|
||||
SubscribeLocalEvent<IdentityComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
|
||||
@@ -55,6 +58,15 @@ public sealed class IdentitySystem : EntitySystem
|
||||
SubscribeLocalEvent<IdentityComponent, EntityRenamedEvent>((uid, _, _) => QueueIdentityUpdate(uid));
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
private void OnRelayedSeeIdentity(Entity<IdentityBlockerComponent> ent, ref Content.Shared.StatusEffectNew.StatusEffectRelayedEvent<SeeIdentityAttemptEvent> args)
|
||||
{
|
||||
var argsArgs = args.Args;
|
||||
OnSeeIdentity(ent, ref argsArgs);
|
||||
args.Args = argsArgs;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through all identities that need to be updated.
|
||||
/// </summary>
|
||||
|
||||
@@ -26,6 +26,7 @@ using Content.Shared.Verbs;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Shared.Medical.Cryogenics;
|
||||
|
||||
@@ -105,7 +106,9 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
&& _solutionContainerQuery.TryComp(container, out var solutionContainerManagerComponent)
|
||||
&& _solutionContainer.TryGetFitsInDispenser((container.Value, fitsInDispenserComponent, solutionContainerManagerComponent),
|
||||
out var containerSolution, out _)
|
||||
&& _bloodstreamQuery.TryComp(patient, out var bloodstream))
|
||||
&& _bloodstreamQuery.TryComp(patient, out var bloodstream)
|
||||
&& _solutionContainer.ResolveSolution(patient.Value, bloodstream.ChemicalSolutionName, ref bloodstream.ChemicalSolution, out var chemsSolution) // Offbrand
|
||||
&& !chemsSolution.HasOverlapAtLeast(containerSolution.Value.Comp.Solution, cryoPod.BeakerTransferAmount * 2)) // Offbrand
|
||||
{
|
||||
var solutionToInject = _solutionContainer.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount);
|
||||
_bloodstream.TryAddToChemicals((patient.Value, bloodstream), solutionToInject);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
namespace Content.Shared.Medical.SuitSensor;
|
||||
|
||||
@@ -25,9 +26,12 @@ public sealed class SuitSensorStatus
|
||||
public string JobIcon;
|
||||
public List<string> JobDepartments;
|
||||
public bool IsAlive;
|
||||
public int? TotalDamage;
|
||||
public int? TotalDamageThreshold;
|
||||
public float? DamagePercentage => TotalDamageThreshold == null || TotalDamage == null ? null : TotalDamage / (float) TotalDamageThreshold;
|
||||
// Begin Offbrand Changes
|
||||
// public int? TotalDamage;
|
||||
// public int? TotalDamageThreshold;
|
||||
// public float? DamagePercentage => TotalDamageThreshold == null || TotalDamage == null ? null : TotalDamage / (float) TotalDamageThreshold;
|
||||
public WoundableHealthAnalyzerData? WoundableData;
|
||||
// End Offbrand Changes
|
||||
public NetCoordinates? Coordinates;
|
||||
}
|
||||
|
||||
@@ -63,10 +67,13 @@ public static class SuitSensorConstants
|
||||
public const string NET_JOB_ICON = "jobIcon";
|
||||
public const string NET_JOB_DEPARTMENTS = "jobDepartments";
|
||||
public const string NET_IS_ALIVE = "alive";
|
||||
public const string NET_TOTAL_DAMAGE = "vitals";
|
||||
public const string NET_TOTAL_DAMAGE_THRESHOLD = "vitalsThreshold";
|
||||
// Begin Offbrand Changes
|
||||
// public const string NET_TOTAL_DAMAGE = "vitals";
|
||||
// public const string NET_TOTAL_DAMAGE_THRESHOLD = "vitalsThreshold";
|
||||
public const string NET_COORDINATES = "coords";
|
||||
public const string NET_WOUNDABLE_DATA = "woundableData";
|
||||
public const string NET_SUIT_SENSOR_UID = "uid";
|
||||
// End Offbrand Changes
|
||||
|
||||
///Used by the CrewMonitoringServerSystem to send the status of all connected suit sensors to each crew monitor
|
||||
public const string NET_STATUS_COLLECTION = "suit-status-collection";
|
||||
|
||||
@@ -21,6 +21,9 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared.FixedPoint; // Offbrand
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
|
||||
|
||||
namespace Content.Shared.Medical.SuitSensors;
|
||||
|
||||
@@ -30,7 +33,8 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||
// [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; Offbrand - we don't need that
|
||||
[Dependency] private readonly SharedWoundableHealthAnalyzerSystem _woundableHealthAnalyzer = default!; // Offbrand - we do need that
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
@@ -352,15 +356,17 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
if (TryComp(sensor.User.Value, out MobStateComponent? mobState))
|
||||
isAlive = !_mobStateSystem.IsDead(sensor.User.Value, mobState);
|
||||
|
||||
// get mob total damage
|
||||
var totalDamage = 0;
|
||||
if (TryComp<DamageableComponent>(sensor.User.Value, out var damageable))
|
||||
totalDamage = damageable.TotalDamage.Int();
|
||||
// Begin Offbrand Removals
|
||||
// // get mob total damage
|
||||
// var totalDamage = 0;
|
||||
// if (TryComp<DamageableComponent>(sensor.User.Value, out var damageable))
|
||||
// totalDamage = damageable.TotalDamage.Int();
|
||||
|
||||
// Get mob total damage crit threshold
|
||||
int? totalDamageThreshold = null;
|
||||
if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, MobState.Critical, out var critThreshold))
|
||||
totalDamageThreshold = critThreshold.Value.Int();
|
||||
// // Get mob total damage crit threshold
|
||||
// int? totalDamageThreshold = null;
|
||||
// if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, MobState.Critical, out var critThreshold))
|
||||
// totalDamageThreshold = critThreshold.Value.Int();
|
||||
// End Offbrand Removals
|
||||
|
||||
// finally, form suit sensor status
|
||||
var status = new SuitSensorStatus(GetNetEntity(sensor.User.Value), GetNetEntity(ent.Owner), userName, userJob, userJobIcon, userJobDepartments);
|
||||
@@ -370,14 +376,19 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
status.IsAlive = isAlive;
|
||||
break;
|
||||
case SuitSensorMode.SensorVitals:
|
||||
status.IsAlive = isAlive;
|
||||
status.TotalDamage = totalDamage;
|
||||
status.TotalDamageThreshold = totalDamageThreshold;
|
||||
break;
|
||||
// Begin Offbrand Changes
|
||||
// status.IsAlive = isAlive;
|
||||
// status.TotalDamage = totalDamage;
|
||||
// status.TotalDamageThreshold = totalDamageThreshold;
|
||||
status.WoundableData = _woundableHealthAnalyzer.TakeSample(sensor.User.Value, withWounds: false);
|
||||
goto case SuitSensorMode.SensorBinary;
|
||||
// End Offbrand Changes
|
||||
case SuitSensorMode.SensorCords:
|
||||
status.IsAlive = isAlive;
|
||||
status.TotalDamage = totalDamage;
|
||||
status.TotalDamageThreshold = totalDamageThreshold;
|
||||
// Begin Offbrand - don't duplicate code
|
||||
// status.TotalDamage = totalDamage;
|
||||
// status.TotalDamageThreshold = totalDamageThreshold;
|
||||
// End Offbrand - don't duplicate code
|
||||
EntityCoordinates coordinates;
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
@@ -398,7 +409,7 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
}
|
||||
|
||||
status.Coordinates = GetNetCoordinates(coordinates);
|
||||
break;
|
||||
goto case SuitSensorMode.SensorVitals; // Offbrand - don't duplicate code
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -421,10 +432,14 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
[SuitSensorConstants.NET_OWNER_UID] = status.OwnerUid,
|
||||
};
|
||||
|
||||
if (status.TotalDamage != null)
|
||||
payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE, status.TotalDamage);
|
||||
if (status.TotalDamageThreshold != null)
|
||||
payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE_THRESHOLD, status.TotalDamageThreshold);
|
||||
// Begin Offbrand Changes
|
||||
// if (status.TotalDamage != null)
|
||||
// payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE, status.TotalDamage);
|
||||
// if (status.TotalDamageThreshold != null)
|
||||
// payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE_THRESHOLD, status.TotalDamageThreshold);
|
||||
if (status.WoundableData is { } woundable)
|
||||
payload.Add(SuitSensorConstants.NET_WOUNDABLE_DATA, woundable);
|
||||
// End Offbrand Changes
|
||||
if (status.Coordinates != null)
|
||||
payload.Add(SuitSensorConstants.NET_COORDINATES, status.Coordinates);
|
||||
|
||||
@@ -452,15 +467,21 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
||||
if (!payload.TryGetValue(SuitSensorConstants.NET_OWNER_UID, out NetEntity ownerUid)) return null;
|
||||
|
||||
// try get total damage and cords (optionals)
|
||||
payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE, out int? totalDamage);
|
||||
payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE_THRESHOLD, out int? totalDamageThreshold);
|
||||
// Begin Offbrand Changes
|
||||
// payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE, out int? totalDamage);
|
||||
// payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE_THRESHOLD, out int? totalDamageThreshold);
|
||||
payload.TryGetValue(SuitSensorConstants.NET_WOUNDABLE_DATA, out WoundableHealthAnalyzerData? woundableData);
|
||||
// End Offbrand Changes
|
||||
payload.TryGetValue(SuitSensorConstants.NET_COORDINATES, out NetCoordinates? coords);
|
||||
|
||||
var status = new SuitSensorStatus(ownerUid, suitSensorUid, name, job, jobIcon, jobDepartments)
|
||||
{
|
||||
IsAlive = isAlive.Value,
|
||||
TotalDamage = totalDamage,
|
||||
TotalDamageThreshold = totalDamageThreshold,
|
||||
// Begin Offbrand Changes
|
||||
// TotalDamage = totalDamage,
|
||||
// TotalDamageThreshold = totalDamageThreshold,
|
||||
WoundableData = woundableData,
|
||||
// End Offbrand Changes
|
||||
Coordinates = coords,
|
||||
};
|
||||
return status;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._Offbrand.Wounds; // Offbrand
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.MedicalScanner;
|
||||
@@ -14,8 +15,9 @@ public sealed class HealthAnalyzerScannedUserMessage : BoundUserInterfaceMessage
|
||||
public bool? ScanMode;
|
||||
public bool? Bleeding;
|
||||
public bool? Unrevivable;
|
||||
public WoundableHealthAnalyzerData? WoundableData; // Offbrand
|
||||
|
||||
public HealthAnalyzerScannedUserMessage(NetEntity? targetEntity, float temperature, float bloodLevel, bool? scanMode, bool? bleeding, bool? unrevivable)
|
||||
public HealthAnalyzerScannedUserMessage(NetEntity? targetEntity, float temperature, float bloodLevel, bool? scanMode, bool? bleeding, bool? unrevivable, WoundableHealthAnalyzerData? woundableData) // Offbrand
|
||||
{
|
||||
TargetEntity = targetEntity;
|
||||
Temperature = temperature;
|
||||
@@ -23,6 +25,7 @@ public sealed class HealthAnalyzerScannedUserMessage : BoundUserInterfaceMessage
|
||||
ScanMode = scanMode;
|
||||
Bleeding = bleeding;
|
||||
Unrevivable = unrevivable;
|
||||
WoundableData = woundableData; // Offbrand
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ public sealed class MobThresholdSystem : EntitySystem
|
||||
{
|
||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<MobThresholdsComponent, MapInitEvent>(MobThresholdMapInit); // Offbrand
|
||||
|
||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentShutdown>(MobThresholdShutdown);
|
||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentStartup>(MobThresholdStartup);
|
||||
@@ -439,6 +440,14 @@ public sealed class MobThresholdSystem : EntitySystem
|
||||
UpdateAllEffects((target, thresholds, mobState, damageable), mobState.CurrentState);
|
||||
}
|
||||
|
||||
// Begin Offbrand
|
||||
private void MobThresholdMapInit(Entity<MobThresholdsComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
var overlayUpdate = new Content.Shared._Offbrand.Wounds.PotentiallyUpdateDamageOverlay(ent);
|
||||
RaiseLocalEvent(ent, ref overlayUpdate);
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
private void MobThresholdShutdown(EntityUid target, MobThresholdsComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (component.TriggersAlerts)
|
||||
|
||||
@@ -216,4 +216,10 @@ public sealed partial class RevenantComponent : Component
|
||||
#endregion
|
||||
|
||||
[DataField] public EntityUid? Action;
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand - how much cold damage to deal on harvest
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public FixedPoint2 HarvestColdDamage = 200;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,12 @@ public sealed partial class StatusEffectComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Applied;
|
||||
|
||||
/// <summary>
|
||||
/// Offbrand - When the status effect started
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
|
||||
public TimeSpan? StartedAt;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist, by which it is determined whether this status effect can be imposed on a particular entity.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed partial class StatusEffectsSystem
|
||||
{
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, LocalPlayerAttachedEvent>(RelayStatusEffectEvent);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, LocalPlayerDetachedEvent>(RelayStatusEffectEvent);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, RejuvenateEvent>(RelayStatusEffectEvent);
|
||||
// SubscribeLocalEvent<StatusEffectContainerComponent, RejuvenateEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, RefreshMovementSpeedModifiersEvent>(RelayStatusEffectEvent);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, UpdateCanMoveEvent>(RelayStatusEffectEvent);
|
||||
@@ -26,6 +26,33 @@ public sealed partial class StatusEffectsSystem
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, StunEndAttemptEvent>(RefRelayStatusEffectEvent);
|
||||
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, AccentGetEvent>(RelayStatusEffectEvent);
|
||||
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.WoundGetDamageEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetWoundsWithSpaceEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetPainEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetStrainEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.HealWoundsEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetBleedLevelEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.PainSuppressionEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.BeforeDealBrainDamage>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.BeforeDepleteBrainOxygen>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.BeforeHealBrainDamage>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetOxygenationModifier>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Wounds.GetStoppedCirculationModifier>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Weapons.RelayedGetMeleeDamageEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Weapons.RelayedGetMeleeAttackRateEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared._Offbrand.Weapons.RelayedGunRefreshModifiersEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Damage.ModifySlowOnDamageSpeedEvent>(RefRelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Eye.Blinding.Systems.GetBlurEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Eye.Blinding.Systems.CanSeeAttemptEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.IdentityManagement.Components.SeeIdentityAttemptEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Movement.Pulling.Events.PullStartedMessage>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Movement.Pulling.Events.PullStoppedMessage>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Weapons.Ranged.Events.SelfBeforeGunShotEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Chemistry.Hypospray.Events.SelfBeforeHyposprayInjectsEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Damage.DamageChangedEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Examine.ExaminedEvent>(RelayStatusEffectEvent); // Offbrand
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, Content.Shared.Verbs.GetVerbsEvent<Content.Shared.Verbs.AlternativeVerb>>(RelayStatusEffectEvent); // Offbrand
|
||||
}
|
||||
|
||||
private void RefRelayStatusEffectEvent<T>(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed partial class StatusEffectsSystem : EntitySystem
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, EntInsertedIntoContainerMessage>(OnEntityInserted);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, EntRemovedFromContainerMessage>(OnEntityRemoved);
|
||||
|
||||
SubscribeLocalEvent<RejuvenateRemovedStatusEffectComponent, StatusEffectRelayedEvent<RejuvenateEvent>>(OnRejuvenate);
|
||||
SubscribeLocalEvent<StatusEffectContainerComponent, RejuvenateEvent>(OnRejuvenate); // Offbrand
|
||||
|
||||
_containerQuery = GetEntityQuery<StatusEffectContainerComponent>();
|
||||
_effectQuery = GetEntityQuery<StatusEffectComponent>();
|
||||
@@ -114,11 +114,19 @@ public sealed partial class StatusEffectsSystem : EntitySystem
|
||||
Dirty(args.Entity, statusComp);
|
||||
}
|
||||
|
||||
private void OnRejuvenate(Entity<RejuvenateRemovedStatusEffectComponent> ent,
|
||||
ref StatusEffectRelayedEvent<RejuvenateEvent> args)
|
||||
// Begin Offbrand Changes
|
||||
private void OnRejuvenate(Entity<StatusEffectContainerComponent> ent,
|
||||
ref RejuvenateEvent args)
|
||||
{
|
||||
PredictedQueueDel(ent.Owner);
|
||||
if (!TryEffectsWithComp<RejuvenateRemovedStatusEffectComponent>(ent, out var effects))
|
||||
return;
|
||||
|
||||
foreach (var effect in effects)
|
||||
{
|
||||
Del(effect);
|
||||
}
|
||||
}
|
||||
// End Offbrand Changes
|
||||
|
||||
/// <summary>
|
||||
/// Applies the status effect, i.e. starts it after it has been added. Ensures delayed start times trigger when they should.
|
||||
|
||||
@@ -26,10 +26,11 @@ public sealed partial class ToolComponent : Component
|
||||
/// Attempt event called *before* any do afters to see if the tool usage should succeed or not.
|
||||
/// Raised on both the tool and then target.
|
||||
/// </summary>
|
||||
public sealed class ToolUseAttemptEvent(EntityUid user, float fuel) : CancellableEntityEventArgs
|
||||
public sealed class ToolUseAttemptEvent(EntityUid user, float fuel, EntityUid? target) : CancellableEntityEventArgs // Offbrand
|
||||
{
|
||||
public EntityUid User { get; } = user;
|
||||
public float Fuel = fuel;
|
||||
public EntityUid? Target { get; } = target; // Offbrand
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -251,7 +251,7 @@ public abstract partial class SharedToolSystem : EntitySystem
|
||||
return false;
|
||||
|
||||
// check if the tool allows being used
|
||||
var beforeAttempt = new ToolUseAttemptEvent(user, fuel);
|
||||
var beforeAttempt = new ToolUseAttemptEvent(user, fuel, target); // Offbrand
|
||||
RaiseLocalEvent(tool, beforeAttempt);
|
||||
if (beforeAttempt.Cancelled)
|
||||
return false;
|
||||
|
||||
@@ -235,6 +235,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
var ev = new GetMeleeDamageEvent(uid, new(component.Damage * Damageable.UniversalMeleeDamageModifier), new(), user, component.ResistanceBypass);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
// Begin Offbrand
|
||||
var relayed = new Content.Shared._Offbrand.Weapons.RelayedGetMeleeDamageEvent(ev);
|
||||
RaiseLocalEvent(user, ref relayed);
|
||||
ev = relayed.Args;
|
||||
// End Offbrand
|
||||
|
||||
return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers);
|
||||
}
|
||||
|
||||
@@ -246,6 +252,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
var ev = new GetMeleeAttackRateEvent(uid, component.AttackRate, 1, user);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
// Begin Offbrand
|
||||
var relayed = new Content.Shared._Offbrand.Weapons.RelayedGetMeleeAttackRateEvent(ev);
|
||||
RaiseLocalEvent(user, ref relayed);
|
||||
ev = relayed.Args;
|
||||
// End Offbrand
|
||||
|
||||
return ev.Rate * ev.Multipliers;
|
||||
}
|
||||
|
||||
@@ -268,6 +280,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
var ev = new GetMeleeDamageEvent(uid, new(component.Damage * Damageable.UniversalMeleeDamageModifier), new(), user, component.ResistanceBypass);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
// Begin Offbrand
|
||||
var relayed = new Content.Shared._Offbrand.Weapons.RelayedGetMeleeDamageEvent(ev);
|
||||
RaiseLocalEvent(user, ref relayed);
|
||||
ev = relayed.Args;
|
||||
// End Offbrand
|
||||
|
||||
return ev.ResistanceBypass;
|
||||
}
|
||||
|
||||
|
||||
@@ -547,6 +547,15 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
|
||||
RaiseLocalEvent(gun, ref ev);
|
||||
|
||||
// Begin Offbrand
|
||||
if (Containers.TryGetContainingContainer(gun.Owner, out var container))
|
||||
{
|
||||
var relayed = new Content.Shared._Offbrand.Weapons.RelayedGunRefreshModifiersEvent(ev);
|
||||
RaiseLocalEvent(container.Owner, ref relayed);
|
||||
ev = relayed.Args;
|
||||
}
|
||||
// End Offbrand
|
||||
|
||||
if (comp.SoundGunshotModified != ev.SoundGunshot)
|
||||
{
|
||||
comp.SoundGunshotModified = ev.SoundGunshot;
|
||||
|
||||
@@ -146,7 +146,7 @@ public sealed partial class ZombieComponent : Component
|
||||
{
|
||||
DamageDict = new()
|
||||
{
|
||||
{ "Slash", 13 },
|
||||
// { "Slash", 13 }, Offbrand - slash is too fast a crit
|
||||
{ "Piercing", 7 },
|
||||
{ "Structural", 10 }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.Buckle;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(StatusEffectOnStrapSystem))]
|
||||
public sealed partial class StatusEffectOnStrapComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId StatusEffect;
|
||||
}
|
||||
61
Content.Shared/_Offbrand/Buckle/StatusEffectOnStrapSystem.cs
Normal file
61
Content.Shared/_Offbrand/Buckle/StatusEffectOnStrapSystem.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.StatusEffectNew;
|
||||
|
||||
namespace Content.Shared._Offbrand.Buckle;
|
||||
|
||||
public sealed class StatusEffectOnStrapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
||||
[Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusEffectOnStrapComponent, StrappedEvent>(OnStrapped);
|
||||
SubscribeLocalEvent<StatusEffectOnStrapComponent, UnstrappedEvent>(OnUnstrapped);
|
||||
SubscribeLocalEvent<StatusEffectOnStrapComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
}
|
||||
|
||||
private void UpdateStatus(Entity<StrapComponent, StatusEffectOnStrapComponent> ent, EntityUid buckled)
|
||||
{
|
||||
var isBuckled = ent.Comp1.BuckledEntities.Contains(buckled);
|
||||
var isPowered = _powerReceiver.IsPowered(ent.Owner);
|
||||
|
||||
if (isBuckled && isPowered)
|
||||
{
|
||||
if (!_statusEffects.HasStatusEffect(buckled, ent.Comp2.StatusEffect))
|
||||
_statusEffects.TryUpdateStatusEffectDuration(buckled, ent.Comp2.StatusEffect, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
_statusEffects.TryRemoveStatusEffect(buckled, ent.Comp2.StatusEffect);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStrapped(Entity<StatusEffectOnStrapComponent> ent, ref StrappedEvent args)
|
||||
{
|
||||
UpdateStatus((ent.Owner, Comp<StrapComponent>(ent), ent.Comp), args.Buckle);
|
||||
}
|
||||
|
||||
private void OnUnstrapped(Entity<StatusEffectOnStrapComponent> ent, ref UnstrappedEvent args)
|
||||
{
|
||||
UpdateStatus((ent.Owner, Comp<StrapComponent>(ent), ent.Comp), args.Buckle);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(Entity<StatusEffectOnStrapComponent> ent, ref PowerChangedEvent args)
|
||||
{
|
||||
var strap = Comp<StrapComponent>(ent);
|
||||
foreach (var entity in strap.BuckledEntities)
|
||||
{
|
||||
UpdateStatus((ent.Owner, strap, ent.Comp), entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Shared._Offbrand.Chemistry;
|
||||
|
||||
[ByRefEvent]
|
||||
public struct BeforeInjectOnEventEvent
|
||||
{
|
||||
public FixedPoint2 InjectionAmount;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class AdjustReagentGaussian : EntityEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public ProtoId<ReagentPrototype> Reagent;
|
||||
|
||||
[DataField(required: true)]
|
||||
public double μ;
|
||||
|
||||
[DataField(required: true)]
|
||||
public double σ;
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
if (args is not EntityEffectReagentArgs reagentArgs)
|
||||
throw new NotImplementedException();
|
||||
|
||||
if (reagentArgs.Source == null)
|
||||
return;
|
||||
|
||||
var rand = IoCManager.Resolve<IRobustRandom>();
|
||||
var amount = rand.NextGaussian(μ, σ);
|
||||
amount *= reagentArgs.Scale.Double();
|
||||
|
||||
if (amount < 0 && reagentArgs.Source.ContainsPrototype(Reagent))
|
||||
reagentArgs.Source.RemoveReagent(Reagent, -amount);
|
||||
else if (amount > 0)
|
||||
reagentArgs.Source.AddReagent(Reagent, amount);
|
||||
}
|
||||
|
||||
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var proto = prototype.Index(Reagent);
|
||||
return Loc.GetString("reagent-effect-guidebook-adjust-reagent-gaussian",
|
||||
("chance", Probability),
|
||||
("deltasign", Math.Sign(μ)),
|
||||
("reagent", proto.LocalizedName),
|
||||
("mu", Math.Abs(μ)),
|
||||
("sigma", Math.Abs(σ)));
|
||||
}
|
||||
}
|
||||
37
Content.Shared/_Offbrand/EntityEffects/BrainDamage.cs
Normal file
37
Content.Shared/_Offbrand/EntityEffects/BrainDamage.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class BrainDamage : EntityEffectCondition
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 Max = FixedPoint2.MaxValue;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 Min = FixedPoint2.Zero;
|
||||
|
||||
public override bool Condition(EntityEffectBaseArgs args)
|
||||
{
|
||||
if (args.EntityManager.TryGetComponent<BrainDamageComponent>(args.TargetEntity, out var brain))
|
||||
{
|
||||
return brain.Damage >= Min && brain.Damage <= Max;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-brain-damage",
|
||||
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()));
|
||||
}
|
||||
}
|
||||
37
Content.Shared/_Offbrand/EntityEffects/HeartDamage.cs
Normal file
37
Content.Shared/_Offbrand/EntityEffects/HeartDamage.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class HeartDamage : EntityEffectCondition
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 Max = FixedPoint2.MaxValue;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 Min = FixedPoint2.Zero;
|
||||
|
||||
public override bool Condition(EntityEffectBaseArgs args)
|
||||
{
|
||||
if (args.EntityManager.TryGetComponent<HeartrateComponent>(args.TargetEntity, out var heartrate))
|
||||
{
|
||||
return heartrate.Damage >= Min && heartrate.Damage <= Max;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-heart-damage",
|
||||
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()));
|
||||
}
|
||||
}
|
||||
26
Content.Shared/_Offbrand/EntityEffects/IsZombieImmune.cs
Normal file
26
Content.Shared/_Offbrand/EntityEffects/IsZombieImmune.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class IsZombieImmune : EntityEffectCondition
|
||||
{
|
||||
[DataField]
|
||||
public bool Invert = false;
|
||||
|
||||
public override bool Condition(EntityEffectBaseArgs args)
|
||||
{
|
||||
return args.EntityManager.HasComponent<ZombieImmuneComponent>(args.TargetEntity) ^ Invert;
|
||||
}
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-is-zombie-immune", ("invert", Invert));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class MetaboliteThreshold : EventEntityEffectCondition<MetaboliteThreshold>
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 Min = FixedPoint2.Zero;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 Max = FixedPoint2.MaxValue;
|
||||
|
||||
[DataField]
|
||||
public ProtoId<ReagentPrototype>? Reagent;
|
||||
|
||||
[DataField]
|
||||
public bool IncludeBloodstream = true;
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
ReagentPrototype? reagentProto = null;
|
||||
if (Reagent is { } reagent)
|
||||
prototype.TryIndex(reagent, out reagentProto);
|
||||
|
||||
if (IncludeBloodstream)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-total-dosage-threshold",
|
||||
("reagent", reagentProto?.LocalizedName ?? Loc.GetString("reagent-effect-condition-guidebook-this-reagent")),
|
||||
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-metabolite-threshold",
|
||||
("reagent", reagentProto?.LocalizedName ?? Loc.GetString("reagent-effect-condition-guidebook-this-metabolite")),
|
||||
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()));
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Content.Shared/_Offbrand/EntityEffects/ModifyBrainDamage.cs
Normal file
36
Content.Shared/_Offbrand/EntityEffects/ModifyBrainDamage.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class ModifyBrainDamage : EntityEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Amount;
|
||||
|
||||
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (Amount < FixedPoint2.Zero)
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-brain-damage-heals", ("chance", Probability), ("amount", -Amount));
|
||||
else
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-brain-damage-deals", ("chance", Probability), ("amount", Amount));
|
||||
}
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
var scale = FixedPoint2.New(1);
|
||||
|
||||
if (args is EntityEffectReagentArgs reagentArgs)
|
||||
scale = reagentArgs.Scale;
|
||||
|
||||
args.EntityManager.System<BrainDamageSystem>()
|
||||
.TryChangeBrainDamage(args.TargetEntity, Amount * scale);
|
||||
}
|
||||
}
|
||||
36
Content.Shared/_Offbrand/EntityEffects/ModifyBrainOxygen.cs
Normal file
36
Content.Shared/_Offbrand/EntityEffects/ModifyBrainOxygen.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class ModifyBrainOxygen : EntityEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Amount;
|
||||
|
||||
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (Amount > FixedPoint2.Zero)
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-brain-oxygen-heals", ("chance", Probability), ("amount", Amount));
|
||||
else
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-brain-oxygen-deals", ("chance", Probability), ("amount", -Amount));
|
||||
}
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
var scale = FixedPoint2.New(1);
|
||||
|
||||
if (args is EntityEffectReagentArgs reagentArgs)
|
||||
scale = reagentArgs.Scale;
|
||||
|
||||
args.EntityManager.System<BrainDamageSystem>()
|
||||
.TryChangeBrainOxygenation(args.TargetEntity, Amount * scale);
|
||||
}
|
||||
}
|
||||
36
Content.Shared/_Offbrand/EntityEffects/ModifyHeartDamage.cs
Normal file
36
Content.Shared/_Offbrand/EntityEffects/ModifyHeartDamage.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class ModifyHeartDamage : EntityEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Amount;
|
||||
|
||||
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (Amount < FixedPoint2.Zero)
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-heart-damage-heals", ("chance", Probability), ("amount", -Amount));
|
||||
else
|
||||
return Loc.GetString("reagent-effect-guidebook-modify-heart-damage-deals", ("chance", Probability), ("amount", Amount));
|
||||
}
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
var scale = FixedPoint2.New(1);
|
||||
|
||||
if (args is EntityEffectReagentArgs reagentArgs)
|
||||
scale = reagentArgs.Scale;
|
||||
|
||||
args.EntityManager.System<HeartSystem>()
|
||||
.ChangeHeartDamage(args.TargetEntity, Amount * scale);
|
||||
}
|
||||
}
|
||||
24
Content.Shared/_Offbrand/EntityEffects/StartHeart.cs
Normal file
24
Content.Shared/_Offbrand/EntityEffects/StartHeart.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class StartHeart : EntityEffect
|
||||
{
|
||||
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-guidebook-start-heart", ("chance", Probability));
|
||||
}
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
args.EntityManager.System<HeartSystem>()
|
||||
.TryRestartHeart(args.TargetEntity);
|
||||
}
|
||||
}
|
||||
15
Content.Shared/_Offbrand/EntityEffects/Zombify.cs
Normal file
15
Content.Shared/_Offbrand/EntityEffects/Zombify.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.EntityEffects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._Offbrand.EntityEffects;
|
||||
|
||||
public sealed partial class Zombify : EventEntityEffect<Zombify>
|
||||
{
|
||||
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
=> Loc.GetString("reagent-effect-guidebook-zombify", ("chance", Probability));
|
||||
}
|
||||
77
Content.Shared/_Offbrand/IV/IVSourceComponent.cs
Normal file
77
Content.Shared/_Offbrand/IV/IVSourceComponent.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Offbrand.IV;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
[Access(typeof(IVSystem))]
|
||||
public sealed partial class IVSourceComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? IVTarget;
|
||||
|
||||
[DataField]
|
||||
public string SlotName = "iv_bag_slot";
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 BloodTransferRate = 5;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 OtherTransferRate = 0.5;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
[DataField]
|
||||
public float Delay = 3f;
|
||||
|
||||
[DataField]
|
||||
public LocId NoBagInserted = "iv-bag-none-inserted";
|
||||
|
||||
[DataField]
|
||||
public LocId StartConnectionUser = "iv-bag-start-connection-user";
|
||||
|
||||
[DataField]
|
||||
public LocId StartConnectionOthers = "iv-bag-start-connection-others";
|
||||
|
||||
[DataField]
|
||||
public LocId ConnectedUser = "iv-bag-connected-user";
|
||||
|
||||
[DataField]
|
||||
public LocId ConnectedOthers = "iv-bag-connected-others";
|
||||
|
||||
[DataField]
|
||||
public LocId StartDisconnectionUser = "iv-bag-start-disconnection-user";
|
||||
|
||||
[DataField]
|
||||
public LocId StartDisconnectionOthers = "iv-bag-start-disconnection-others";
|
||||
|
||||
[DataField]
|
||||
public LocId DisconnectedUser = "iv-bag-disconnected-user";
|
||||
|
||||
[DataField]
|
||||
public LocId DisconnectedOthers = "iv-bag-disconnected-others";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class IVConnectDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class IVDisconnectDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum IVSourceVisuals : byte
|
||||
{
|
||||
HasTarget
|
||||
}
|
||||
327
Content.Shared/_Offbrand/IV/IVSystem.cs
Normal file
327
Content.Shared/_Offbrand/IV/IVSystem.cs
Normal file
@@ -0,0 +1,327 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._Offbrand.IV;
|
||||
|
||||
public sealed class IVSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedBloodstreamSystem _bloodstream = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedJointSystem _joint = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<IVSourceComponent, CanDragEvent>(OnCanDrag);
|
||||
SubscribeLocalEvent<IVSourceComponent, CanDropDraggedEvent>(OnCanDropDragged);
|
||||
SubscribeLocalEvent<IVSourceComponent, DragDropDraggedEvent>(OnDragDropDragged);
|
||||
|
||||
SubscribeLocalEvent<IVSourceComponent, IVConnectDoAfterEvent>(OnConnectDoAfter);
|
||||
SubscribeLocalEvent<IVSourceComponent, IVDisconnectDoAfterEvent>(OnDisconnectDoAfter);
|
||||
|
||||
SubscribeLocalEvent<IVSourceComponent, GetVerbsEvent<Verb>>(OnSourceGetVerbs);
|
||||
SubscribeLocalEvent<IVTargetComponent, GetVerbsEvent<Verb>>(OnTargetGetVerbs);
|
||||
|
||||
SubscribeLocalEvent<IVSourceComponent, ComponentShutdown>(OnSourceShutdown);
|
||||
SubscribeLocalEvent<IVTargetComponent, ComponentShutdown>(OnTargetShutdown);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var query = EntityQueryEnumerator<IVSourceComponent>();
|
||||
while (query.MoveNext(out var sourceUid, out var sourceComp))
|
||||
{
|
||||
if (sourceComp.IVTarget is not { } target)
|
||||
continue;
|
||||
|
||||
if (sourceComp.NextUpdate > _timing.CurTime)
|
||||
continue;
|
||||
|
||||
sourceComp.NextUpdate = _timing.CurTime + sourceComp.UpdateInterval;
|
||||
Dirty(sourceUid, sourceComp);
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(target, out var bloodstream) || !TryComp<IVTargetComponent>(target, out var targetComp))
|
||||
continue;
|
||||
|
||||
TickIV((sourceUid, sourceComp), (target, targetComp, bloodstream));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCanDrag(Entity<IVSourceComponent> ent, ref CanDragEvent args)
|
||||
{
|
||||
args.Handled = ent.Comp.IVTarget is null;
|
||||
}
|
||||
|
||||
private void OnCanDropDragged(Entity<IVSourceComponent> ent, ref CanDropDraggedEvent args)
|
||||
{
|
||||
if (!TryComp<IVTargetComponent>(args.Target, out var target))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
args.CanDrop = target.IVSource is null;
|
||||
}
|
||||
|
||||
private void OnDragDropDragged(Entity<IVSourceComponent> ent, ref DragDropDraggedEvent args)
|
||||
{
|
||||
if (!TryComp<IVTargetComponent>(args.Target, out var target) || target.IVSource is not null)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
TryStartIV(ent.AsNullable(), (args.Target, target), args.User);
|
||||
}
|
||||
|
||||
private void OnConnectDoAfter(Entity<IVSourceComponent> ent, ref IVConnectDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Args.Target is not { } target)
|
||||
return;
|
||||
|
||||
StartIV(ent.AsNullable(), target, args.Args.User);
|
||||
}
|
||||
|
||||
private void OnDisconnectDoAfter(Entity<IVSourceComponent> ent, ref IVDisconnectDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Args.Target is not { } target)
|
||||
return;
|
||||
|
||||
StopIV(ent.AsNullable(), target, args.Args.User);
|
||||
}
|
||||
|
||||
private void TickIV(Entity<IVSourceComponent> source, Entity<IVTargetComponent, BloodstreamComponent> target)
|
||||
{
|
||||
if (_itemSlots.GetItemOrNull(source, source.Comp.SlotName) is not { } contained)
|
||||
return;
|
||||
|
||||
if (!_solutionContainer.TryGetDrawableSolution(contained, out var solutionEntity, out var solution))
|
||||
return;
|
||||
|
||||
if (!_solutionContainer.ResolveSolution(target.Owner, target.Comp2.ChemicalSolutionName, ref target.Comp2.ChemicalSolution, out var chemsSolution))
|
||||
return;
|
||||
|
||||
if (!_solutionContainer.ResolveSolution(target.Owner, target.Comp2.BloodSolutionName, ref target.Comp2.BloodSolution, out var bloodSolution))
|
||||
return;
|
||||
|
||||
var bloodTransferAmount = FixedPoint2.Min(source.Comp.BloodTransferRate, bloodSolution.AvailableVolume);
|
||||
var chemsTransferAmount = FixedPoint2.Min(source.Comp.OtherTransferRate, chemsSolution.AvailableVolume);
|
||||
|
||||
if (bloodTransferAmount > 0)
|
||||
{
|
||||
solution.SplitSolutionWithOnly(bloodTransferAmount, target.Comp2.BloodReagent);
|
||||
|
||||
_bloodstream.TryModifyBloodLevel((target.Owner, target.Comp2), bloodTransferAmount);
|
||||
}
|
||||
|
||||
if (chemsTransferAmount > 0)
|
||||
{
|
||||
var medicineSolution = solution.SplitSolutionWithout(chemsTransferAmount, target.Comp2.BloodReagent);
|
||||
|
||||
if (!chemsSolution.HasOverlapAtLeast(medicineSolution, source.Comp.OtherTransferRate * 2))
|
||||
{
|
||||
_bloodstream.TryAddToChemicals((target.Owner, target.Comp2), medicineSolution);
|
||||
}
|
||||
}
|
||||
|
||||
_solutionContainer.UpdateChemicals(solutionEntity.Value);
|
||||
}
|
||||
|
||||
private void SetLock(Entity<IVSourceComponent> ent, bool locked)
|
||||
{
|
||||
_itemSlots.SetLock(ent, ent.Comp.SlotName, locked);
|
||||
_appearance.SetData(ent.Owner, IVSourceVisuals.HasTarget, locked);
|
||||
}
|
||||
|
||||
private void StartIV(Entity<IVSourceComponent?> source, Entity<IVTargetComponent?> target, EntityUid user)
|
||||
{
|
||||
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
|
||||
return;
|
||||
if (!TryComp<PhysicsComponent>(source, out var sourcePhysics))
|
||||
return;
|
||||
if (!TryComp<PhysicsComponent>(target, out var targetPhysics))
|
||||
return;
|
||||
|
||||
_popup.PopupPredicted(
|
||||
Loc.GetString(source.Comp.ConnectedUser, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
Loc.GetString(source.Comp.ConnectedOthers, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
target,
|
||||
user
|
||||
);
|
||||
|
||||
target.Comp.IVSource = source;
|
||||
source.Comp.IVTarget = target;
|
||||
|
||||
target.Comp.IVJointID = $"iv-joint-{GetNetEntity(target)}";
|
||||
|
||||
if (!_timing.ApplyingState)
|
||||
{
|
||||
var joint = _joint.CreateDistanceJoint(target, source,
|
||||
sourcePhysics.LocalCenter, targetPhysics.LocalCenter,
|
||||
id: target.Comp.IVJointID);
|
||||
|
||||
joint.MaxLength = joint.Length + 0.2f;
|
||||
joint.MinLength = 0f;
|
||||
joint.Stiffness = 0f;
|
||||
}
|
||||
|
||||
SetLock((source, source.Comp), true);
|
||||
Dirty(target);
|
||||
Dirty(source);
|
||||
}
|
||||
|
||||
private void StopIV(Entity<IVSourceComponent?> source, Entity<IVTargetComponent?> target, EntityUid user)
|
||||
{
|
||||
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
|
||||
return;
|
||||
|
||||
_popup.PopupPredicted(
|
||||
Loc.GetString(source.Comp.DisconnectedUser, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
Loc.GetString(source.Comp.DisconnectedOthers, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
target,
|
||||
user
|
||||
);
|
||||
|
||||
source.Comp.IVTarget = null;
|
||||
|
||||
if (target.Comp.IVJointID is { } joint)
|
||||
_joint.RemoveJoint(target, joint);
|
||||
|
||||
target.Comp.IVSource = null;
|
||||
target.Comp.IVJointID = null;
|
||||
|
||||
SetLock((source, source.Comp), false);
|
||||
Dirty(source);
|
||||
Dirty(target);
|
||||
}
|
||||
|
||||
private void TryStartIV(Entity<IVSourceComponent?> source, Entity<IVTargetComponent?> target, EntityUid user)
|
||||
{
|
||||
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp) || source.Comp.IVTarget is not null || target.Comp.IVSource is not null)
|
||||
return;
|
||||
|
||||
if (_itemSlots.GetItemOrNull(source, source.Comp.SlotName) is not { } contained)
|
||||
{
|
||||
_popup.PopupPredictedCursor(Loc.GetString(source.Comp.NoBagInserted), user);
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.PopupPredicted(
|
||||
Loc.GetString(source.Comp.StartConnectionUser, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
Loc.GetString(source.Comp.StartConnectionOthers, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
target,
|
||||
user
|
||||
);
|
||||
|
||||
var args =
|
||||
new DoAfterArgs(EntityManager, user, source.Comp.Delay, new IVConnectDoAfterEvent(), source, target: target)
|
||||
{
|
||||
NeedHand = true,
|
||||
BreakOnMove = true,
|
||||
BreakOnWeightlessMove = false,
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(args);
|
||||
}
|
||||
|
||||
private void TryStopIV(Entity<IVSourceComponent?> source, Entity<IVTargetComponent?> target, EntityUid user)
|
||||
{
|
||||
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp) || source.Comp.IVTarget is null || target.Comp.IVSource is null || source.Comp.IVTarget != target || target.Comp.IVSource != source)
|
||||
return;
|
||||
|
||||
_popup.PopupPredicted(
|
||||
Loc.GetString(source.Comp.StartDisconnectionUser, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
Loc.GetString(source.Comp.StartDisconnectionOthers, ("target", Identity.Entity(target, EntityManager)), ("source", Identity.Entity(source, EntityManager)), ("user", Identity.Entity(user, EntityManager))),
|
||||
target,
|
||||
user
|
||||
);
|
||||
|
||||
var args =
|
||||
new DoAfterArgs(EntityManager, user, source.Comp.Delay, new IVDisconnectDoAfterEvent(), source, target: target)
|
||||
{
|
||||
NeedHand = true,
|
||||
BreakOnMove = true,
|
||||
BreakOnWeightlessMove = false,
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(args);
|
||||
}
|
||||
|
||||
private void OnSourceShutdown(Entity<IVSourceComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (ent.Comp.IVTarget is not { } target || !TryComp<IVTargetComponent>(target, out var targetComp))
|
||||
return;
|
||||
|
||||
if (targetComp.IVJointID is { } joint)
|
||||
_joint.RemoveJoint(target, joint);
|
||||
|
||||
targetComp.IVSource = null;
|
||||
targetComp.IVJointID = null;
|
||||
SetLock(ent, false);
|
||||
Dirty(target, targetComp);
|
||||
}
|
||||
|
||||
private void OnTargetShutdown(Entity<IVTargetComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (ent.Comp.IVSource is not { } source || !TryComp<IVSourceComponent>(source, out var sourceComp))
|
||||
return;
|
||||
|
||||
if (ent.Comp.IVJointID is { } joint)
|
||||
_joint.RemoveJoint(ent, joint);
|
||||
|
||||
sourceComp.IVTarget = null;
|
||||
SetLock((source, sourceComp), false);
|
||||
Dirty(source, sourceComp);
|
||||
}
|
||||
|
||||
private void OnSourceGetVerbs(Entity<IVSourceComponent> ent, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || ent.Comp.IVTarget is not { } target)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("verb-remove-iv"),
|
||||
Act = () => TryStopIV(ent.AsNullable(), target, user)
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnTargetGetVerbs(Entity<IVTargetComponent> ent, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || ent.Comp.IVSource is not { } source)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("verb-remove-iv"),
|
||||
Act = () => TryStopIV(source, ent.AsNullable(), user)
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
19
Content.Shared/_Offbrand/IV/IVTargetComponent.cs
Normal file
19
Content.Shared/_Offbrand/IV/IVTargetComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._Offbrand.IV;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(IVSystem))]
|
||||
public sealed partial class IVTargetComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? IVSource;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public string? IVJointID;
|
||||
}
|
||||
63
Content.Shared/_Offbrand/MMI/MMIExtractorComponent.cs
Normal file
63
Content.Shared/_Offbrand/MMI/MMIExtractorComponent.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Offbrand.MMI;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class MMIExtractorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float Delay = 30f;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
|
||||
[DataField]
|
||||
public LocId NoMind = "mmi-extractor-no-mind";
|
||||
|
||||
[DataField]
|
||||
public LocId Asking = "mmi-extractor-probing";
|
||||
|
||||
[DataField]
|
||||
public LocId Accepted = "mmi-extractor-accepted";
|
||||
|
||||
[DataField]
|
||||
public LocId Denied = "mmi-extractor-denied";
|
||||
|
||||
[DataField]
|
||||
public LocId NoResponse = "mmi-extractor-inconclusive";
|
||||
|
||||
[DataField]
|
||||
public LocId TooManyBrains = "mmi-extractor-too-many-brains";
|
||||
|
||||
[DataField]
|
||||
public LocId Brainless = "mmi-extractor-brainless";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MMIExtractorMessage : EuiMessageBase
|
||||
{
|
||||
public readonly bool Accepted;
|
||||
|
||||
public MMIExtractorMessage(bool accepted)
|
||||
{
|
||||
Accepted = accepted;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class MMIExtractorDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
[DataField]
|
||||
public bool Accepted = false;
|
||||
}
|
||||
373
Content.Shared/_Offbrand/MPL-2.0.txt
Normal file
373
Content.Shared/_Offbrand/MPL-2.0.txt
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -0,0 +1,28 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Offbrand.SolutionAppearanceRelay;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SolutionAppearanceRelayComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public string Solution;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SolutionAppearanceRelayedVisuals : byte
|
||||
{
|
||||
HasRelay
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._Offbrand.SolutionAppearanceRelay;
|
||||
|
||||
public sealed class SolutionAppearanceRelaySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionAppearanceRelayComponent, SolutionContainerChangedEvent>(OnSolutionContainerChanged);
|
||||
SubscribeLocalEvent<SolutionAppearanceRelayComponent, EntGotInsertedIntoContainerMessage>(OnEntGotInsertedIntoContainer);
|
||||
SubscribeLocalEvent<SolutionAppearanceRelayComponent, EntGotRemovedFromContainerMessage>(OnEntGotRemovedFromContainer);
|
||||
}
|
||||
|
||||
private void OnSolutionContainerChanged(Entity<SolutionAppearanceRelayComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
UpdateAppearance(ent);
|
||||
}
|
||||
|
||||
private void OnEntGotInsertedIntoContainer(Entity<SolutionAppearanceRelayComponent> ent, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
UpdateAppearance(ent);
|
||||
}
|
||||
|
||||
private void OnEntGotRemovedFromContainer(Entity<SolutionAppearanceRelayComponent> ent, ref EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
if (!_entityWhitelist.CheckBoth(args.Container.Owner, ent.Comp.Blacklist, ent.Comp.Whitelist))
|
||||
return;
|
||||
|
||||
_appearance.SetData(args.Container.Owner, SolutionContainerVisuals.FillFraction, 0);
|
||||
_appearance.SetData(args.Container.Owner, SolutionAppearanceRelayedVisuals.HasRelay, false);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(Entity<SolutionAppearanceRelayComponent> ent)
|
||||
{
|
||||
if (!_container.TryGetContainingContainer((ent, null, null), out var container))
|
||||
return;
|
||||
|
||||
if (!_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.Solution, out var solutionEntity, out _))
|
||||
return;
|
||||
|
||||
if (!TryComp<ContainedSolutionComponent>(solutionEntity, out var containedSolution))
|
||||
return;
|
||||
|
||||
if (!_entityWhitelist.CheckBoth(container.Owner, ent.Comp.Blacklist, ent.Comp.Whitelist))
|
||||
return;
|
||||
|
||||
_solutionContainer.UpdateAppearance(container.Owner, (solutionEntity.Value.Owner, solutionEntity.Value.Comp, containedSolution));
|
||||
_appearance.SetData(container.Owner, SolutionAppearanceRelayedVisuals.HasRelay, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Shared._Offbrand.StatusEffects;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(AssistedCirculationStatusEffectSystem))]
|
||||
public sealed partial class AssistedCirculationStatusEffectComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How much blood circulation to add
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Amount;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.StatusEffectNew;
|
||||
|
||||
namespace Content.Shared._Offbrand.StatusEffects;
|
||||
|
||||
public sealed partial class AssistedCirculationStatusEffectSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AssistedCirculationStatusEffectComponent, StatusEffectRelayedEvent<GetStoppedCirculationModifier>>(OnGetStoppedCirculationModifier);
|
||||
}
|
||||
|
||||
private void OnGetStoppedCirculationModifier(Entity<AssistedCirculationStatusEffectComponent> ent, ref StatusEffectRelayedEvent<GetStoppedCirculationModifier> args)
|
||||
{
|
||||
args.Args = args.Args with { Modifier = FixedPoint2.Clamp(args.Args.Modifier + ent.Comp.Amount, 0, 1) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace Content.Shared._Offbrand.StatusEffects;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(BleedMultiplierStatusEffectSystem))]
|
||||
public sealed partial class BleedMultiplierStatusEffectComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public float Multiplier;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Content.Shared._Offbrand.Wounds;
|
||||
using Content.Shared.StatusEffectNew;
|
||||
|
||||
namespace Content.Shared._Offbrand.StatusEffects;
|
||||
|
||||
public sealed class BleedMultiplierStatusEffectSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BleedMultiplierStatusEffectComponent, StatusEffectRelayedEvent<ModifyBleedLevelEvent>>(OnGetBleedMultiplier);
|
||||
}
|
||||
|
||||
private void OnGetBleedMultiplier(Entity<BleedMultiplierStatusEffectComponent> ent, ref StatusEffectRelayedEvent<ModifyBleedLevelEvent> args)
|
||||
{
|
||||
args.Args = args.Args with { BleedLevel = args.Args.BleedLevel * ent.Comp.Multiplier };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace Content.Shared._Offbrand.StatusEffects;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(BlindnessStatusEffectSystem))]
|
||||
public sealed partial class BlindnessStatusEffectComponent : Component;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user