diff --git a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
index 44c40143d83..04075000f5b 100644
--- a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
+++ b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
@@ -39,6 +39,6 @@ protected override void UpdateState(BoundUserInterfaceState message)
if (message is not CargoBountyConsoleState state)
return;
- _menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
+ _menu?.UpdateEntries(state.Bounties, state.History, state.UntilNextSkip);
}
}
diff --git a/Content.Client/Cargo/UI/BountyHistoryEntry.xaml b/Content.Client/Cargo/UI/BountyHistoryEntry.xaml
new file mode 100644
index 00000000000..eee8c5cc165
--- /dev/null
+++ b/Content.Client/Cargo/UI/BountyHistoryEntry.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Cargo/UI/BountyHistoryEntry.xaml.cs b/Content.Client/Cargo/UI/BountyHistoryEntry.xaml.cs
new file mode 100644
index 00000000000..f3c9bbfafb1
--- /dev/null
+++ b/Content.Client/Cargo/UI/BountyHistoryEntry.xaml.cs
@@ -0,0 +1,54 @@
+using Content.Client.Message;
+using Content.Shared.Cargo;
+using Content.Shared.Cargo.Prototypes;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Cargo.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class BountyHistoryEntry : BoxContainer
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public BountyHistoryEntry(CargoBountyHistoryData bounty)
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ if (!_prototype.TryIndex(bounty.Bounty, out var bountyPrototype))
+ return;
+
+ var items = new List();
+ foreach (var entry in bountyPrototype.Entries)
+ {
+ items.Add(Loc.GetString("bounty-console-manifest-entry",
+ ("amount", entry.Amount),
+ ("item", Loc.GetString(entry.Name))));
+ }
+ ManifestLabel.SetMarkup(Loc.GetString("bounty-console-manifest-label", ("item", string.Join(", ", items))));
+ RewardLabel.SetMarkup(Loc.GetString("bounty-console-reward-label", ("reward", bountyPrototype.Reward)));
+ IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id)));
+
+ var stationTime = bounty.Timestamp.ToString("hh\\:mm\\:ss");
+ if (bounty.ActorName == null)
+ {
+ StatusLabel.SetMarkup(Loc.GetString("bounty-console-history-completed-label"));
+ NoticeLabel.SetMarkup(Loc.GetString("bounty-console-history-notice-completed-label", ("time", stationTime)));
+ }
+ else
+ {
+ StatusLabel.SetMarkup(Loc.GetString("bounty-console-history-skipped-label"));
+ NoticeLabel.SetMarkup(Loc.GetString("bounty-console-history-notice-skipped-label",
+ ("id", bounty.ActorName),
+ ("time", stationTime)));
+ }
+ }
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+ }
+}
diff --git a/Content.Client/Cargo/UI/CargoBountyMenu.xaml b/Content.Client/Cargo/UI/CargoBountyMenu.xaml
index bb263ff6c4a..0f093d5f8e7 100644
--- a/Content.Client/Cargo/UI/CargoBountyMenu.xaml
+++ b/Content.Client/Cargo/UI/CargoBountyMenu.xaml
@@ -11,15 +11,26 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Cargo/UI/CargoBountyMenu.xaml.cs b/Content.Client/Cargo/UI/CargoBountyMenu.xaml.cs
index 3767b45e4be..0717aacc5e6 100644
--- a/Content.Client/Cargo/UI/CargoBountyMenu.xaml.cs
+++ b/Content.Client/Cargo/UI/CargoBountyMenu.xaml.cs
@@ -17,8 +17,11 @@ public CargoBountyMenu()
RobustXamlLoader.Load(this);
}
- public void UpdateEntries(List bounties, TimeSpan untilNextSkip)
+ public void UpdateEntries(List bounties, List history, TimeSpan untilNextSkip)
{
+ MasterTabContainer.SetTabTitle(0, Loc.GetString("bounty-console-tab-available-label"));
+ MasterTabContainer.SetTabTitle(1, Loc.GetString("bounty-console-tab-history-label"));
+
BountyEntriesContainer.Children.Clear();
foreach (var b in bounties)
{
@@ -32,5 +35,12 @@ public void UpdateEntries(List bounties, TimeSpan untilNextSkip
{
MinHeight = 10
});
+
+ BountyHistoryContainer.Children.Clear();
+ foreach (var h in history)
+ {
+ var entry = new BountyHistoryEntry(h);
+ BountyHistoryContainer.AddChild(entry);
+ }
}
}
diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs
index 7e681aeba3e..f57f4d4e170 100644
--- a/Content.Client/Stack/StackSystem.cs
+++ b/Content.Client/Stack/StackSystem.cs
@@ -8,7 +8,7 @@
namespace Content.Client.Stack
{
[UsedImplicitly]
- public sealed class StackSystem : SharedStackSystem
+ public sealed partial class StackSystem : SharedStackSystem // Frontier: add partial to class definition
{
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly ItemCounterSystem _counterSystem = default!;
@@ -44,7 +44,7 @@ public override void SetCount(EntityUid uid, int amount, StackComponent? compone
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0 && !component.Lingering)
{
- Xform.DetachEntity(uid, Transform(uid));
+ Xform.DetachParentToNull(uid, Transform(uid));
return;
}
@@ -56,20 +56,25 @@ private void OnAppearanceChange(EntityUid uid, StackComponent comp, ref Appearan
if (args.Sprite == null || comp.LayerStates.Count < 1)
return;
+ StackLayerData data = new StackLayerData(); // Frontier: use structure to store StackLayerData
+
// Skip processing if no actual
- if (!_appearanceSystem.TryGetData(uid, StackVisuals.Actual, out var actual, args.Component))
+ if (!_appearanceSystem.TryGetData(uid, StackVisuals.Actual, out data.Actual, args.Component))
return;
- if (!_appearanceSystem.TryGetData(uid, StackVisuals.MaxCount, out var maxCount, args.Component))
- maxCount = comp.LayerStates.Count;
+ if (!_appearanceSystem.TryGetData(uid, StackVisuals.MaxCount, out data.MaxCount, args.Component))
+ data.MaxCount = comp.LayerStates.Count;
+
+ if (!_appearanceSystem.TryGetData(uid, StackVisuals.Hide, out data.Hidden, args.Component))
+ data.Hidden = false;
- if (!_appearanceSystem.TryGetData(uid, StackVisuals.Hide, out var hidden, args.Component))
- hidden = false;
+ if (comp.LayerFunction != StackLayerFunction.None) // Frontier: use stack layer function to modify appearance if provided.
+ ApplyLayerFunction(uid, comp, ref data); // Frontier: definition in _NF/Stack/StackSystem.Layers.cs
if (comp.IsComposite)
- _counterSystem.ProcessCompositeSprite(uid, actual, maxCount, comp.LayerStates, hidden, sprite: args.Sprite);
+ _counterSystem.ProcessCompositeSprite(uid, data.Actual, data.MaxCount, comp.LayerStates, data.Hidden, sprite: args.Sprite);
else
- _counterSystem.ProcessOpaqueSprite(uid, comp.BaseLayer, actual, maxCount, comp.LayerStates, hidden, sprite: args.Sprite);
+ _counterSystem.ProcessOpaqueSprite(uid, comp.BaseLayer, data.Actual, data.MaxCount, comp.LayerStates, data.Hidden, sprite: args.Sprite);
}
}
}
diff --git a/Content.Client/_NF/Stack/StackSystem.Layers.cs b/Content.Client/_NF/Stack/StackSystem.Layers.cs
new file mode 100644
index 00000000000..2893d32d3f5
--- /dev/null
+++ b/Content.Client/_NF/Stack/StackSystem.Layers.cs
@@ -0,0 +1,56 @@
+using Content.Shared.Stacks.Components;
+using Content.Shared.Stacks;
+
+namespace Content.Client.Stack
+{
+ ///
+ /// Data used to determine which layers of a stack's sprite are visible.
+ ///
+ public struct StackLayerData
+ {
+ public int Actual;
+ public int MaxCount;
+ public bool Hidden;
+ }
+
+ public sealed partial class StackSystem : SharedStackSystem
+ {
+ // Modifies a given stack component to adjust the layers to display.
+ private bool ApplyLayerFunction(EntityUid uid, StackComponent comp, ref StackLayerData data)
+ {
+ switch (comp.LayerFunction)
+ {
+ case StackLayerFunction.Threshold:
+ if (TryComp(uid, out var threshold))
+ {
+ ApplyThreshold(threshold, ref data);
+ return true;
+ }
+ break;
+ }
+ // No function applied.
+ return false;
+ }
+
+ ///
+ /// Sets Actual to the number of thresholds that Actual exceeds from the beginning of the list.
+ /// Sets MaxCount to the total number of thresholds plus one (for values under thresholds).
+ ///
+ private static void ApplyThreshold(StackLayerThresholdComponent comp, ref StackLayerData data)
+ {
+ // We must stop before we run out of thresholds or layers, whichever's smaller.
+ data.MaxCount = Math.Min(comp.Thresholds.Count + 1, data.MaxCount);
+ int newActual = 0;
+ foreach (var threshold in comp.Thresholds)
+ {
+ //If our value exceeds threshold, the next layer should be displayed.
+ //Note: we must ensure actual <= MaxCount.
+ if (data.Actual >= threshold && newActual < data.MaxCount)
+ newActual++;
+ else
+ break;
+ }
+ data.Actual = newActual;
+ }
+ }
+}
diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs
index 72af4d9e63a..256c6d67298 100644
--- a/Content.Server/Body/Components/LungComponent.cs
+++ b/Content.Server/Body/Components/LungComponent.cs
@@ -34,4 +34,11 @@ public sealed partial class LungComponent : Component
///
[DataField]
public ProtoId Alert = "LowOxygen";
+
+ ///
+ /// DeltaV: Multiplier on saturation passively lost.
+ /// Higher values require more air, lower require less.
+ ///
+ [DataField]
+ public float SaturationLoss = 1f;
}
diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs
index 1f95295d2f8..6cb575a6888 100644
--- a/Content.Server/Body/Systems/RespiratorSystem.cs
+++ b/Content.Server/Body/Systems/RespiratorSystem.cs
@@ -78,14 +78,15 @@ public override void Update(float frameTime)
if (_mobState.IsDead(uid))
continue;
- //_bodySystem.GetBodyOrgans<(EntityUid euid, LungComponent lc)>(uid, body);
- //_bodySystem.
- /* var organs = _bodySystem.GetBodyOrganComponents(uid, body);
- if(organs==null){
- continue;
- }*/
-
- UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator);
+ // Begin DeltaV Code: Addition:
+ var organs = _bodySystem.GetBodyOrganEntityComps((uid, body));
+ var multiplier = -1f;
+ foreach (var (_, lung, _) in organs)
+ {
+ multiplier *= lung.SaturationLoss;
+ }
+ // End DeltaV Code
+ UpdateSaturation(uid, multiplier * (float) respirator.UpdateInterval.TotalSeconds, respirator); // DeltaV: use multiplier instead of negating
if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
{
diff --git a/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs b/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs
index a7735787cbb..f58b5450ba4 100644
--- a/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs
+++ b/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs
@@ -21,6 +21,13 @@ public sealed partial class StationCargoBountyDatabaseComponent : Component
[DataField, ViewVariables(VVAccess.ReadWrite)]
public List Bounties = new();
+ ///
+ /// A list of all the bounties that have been completed or
+ /// skipped for a station.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public List History = new();
+
///
/// Used to determine unique order IDs
///
diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs
index 373e8e243ba..236b018a9da 100644
--- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs
+++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs
@@ -8,6 +8,7 @@
using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes;
using Content.Shared.Database;
+using Content.Shared.IdentityManagement;
using Content.Shared.NameIdentifier;
using Content.Shared.Paper;
using Content.Shared.Stacks;
@@ -16,6 +17,7 @@
using Robust.Server.Containers;
using Robust.Shared.Containers;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Cargo.Systems;
@@ -25,6 +27,7 @@ public sealed partial class CargoSystem
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSys = default!;
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
[ValidatePrototypeId]
private const string BountyNameIdentifierGroup = "Bounty";
@@ -54,7 +57,7 @@ private void OnBountyConsoleOpened(EntityUid uid, CargoBountyConsoleComponent co
return;
var untilNextSkip = bountyDb.NextSkipTime - _timing.CurTime;
- _uiSystem.SetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDb.Bounties, untilNextSkip));
+ _uiSystem.SetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDb.Bounties, bountyDb.History, untilNextSkip));
}
private void OnPrintLabelMessage(EntityUid uid, CargoBountyConsoleComponent component, BountyPrintLabelMessage args)
@@ -95,13 +98,13 @@ private void OnSkipBountyMessage(EntityUid uid, CargoBountyConsoleComponent comp
return;
}
- if (!TryRemoveBounty(station, bounty.Value))
+ if (!TryRemoveBounty(station, bounty.Value, null, args.Actor))
return;
FillBountyDatabase(station);
db.NextSkipTime = _timing.CurTime + db.SkipDelay;
var untilNextSkip = db.NextSkipTime - _timing.CurTime;
- _uiSystem.SetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, untilNextSkip));
+ _uiSystem.SetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, db.History, untilNextSkip));
_audio.PlayPvs(component.SkipSound, uid);
}
@@ -434,15 +437,15 @@ public bool TryAddBounty(EntityUid uid, CargoBountyPrototype bounty, StationCarg
}
[PublicAPI]
- public bool TryRemoveBounty(EntityUid uid, string dataId, StationCargoBountyDatabaseComponent? component = null)
+ public bool TryRemoveBounty(EntityUid uid, string dataId, StationCargoBountyDatabaseComponent? component = null, EntityUid? actor = null)
{
if (!TryGetBountyFromId(uid, dataId, out var data, component))
return false;
- return TryRemoveBounty(uid, data.Value, component);
+ return TryRemoveBounty(uid, data.Value, component, actor);
}
- public bool TryRemoveBounty(EntityUid uid, CargoBountyData data, StationCargoBountyDatabaseComponent? component = null)
+ public bool TryRemoveBounty(EntityUid uid, CargoBountyData data, StationCargoBountyDatabaseComponent? component = null, EntityUid? actor = null)
{
if (!Resolve(uid, ref component))
return false;
@@ -451,6 +454,15 @@ public bool TryRemoveBounty(EntityUid uid, CargoBountyData data, StationCargoBou
{
if (component.Bounties[i].Id == data.Id)
{
+ string? actorName = default;
+ if (actor != null)
+ {
+ var getIdentityEvent = new TryGetIdentityShortInfoEvent(uid, actor.Value);
+ RaiseLocalEvent(getIdentityEvent);
+ actorName = getIdentityEvent.Title;
+ }
+
+ component.History.Add(new CargoBountyHistoryData(data, _gameTiming.CurTime, actorName));
component.Bounties.RemoveAt(i);
return true;
}
@@ -492,7 +504,7 @@ public void UpdateBountyConsoles()
}
var untilNextSkip = db.NextSkipTime - _timing.CurTime;
- _uiSystem.SetUiState((uid, ui), CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, untilNextSkip));
+ _uiSystem.SetUiState((uid, ui), CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, db.History, untilNextSkip));
}
}
diff --git a/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs b/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs
index ec800db2a30..079ec77bbf3 100644
--- a/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs
+++ b/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs
@@ -1,5 +1,6 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
+using Content.Shared.DeltaV.Carrying;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Systems;
@@ -11,7 +12,6 @@
using Content.Shared.Verbs;
using Content.Shared.Whitelist;
using Content.Server.NPC.Components;
-using Content.Server.Carrying;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Utility;
diff --git a/Content.Server/DeltaV/Station/Components/CaptainStateComponent.cs b/Content.Server/DeltaV/Station/Components/CaptainStateComponent.cs
index 96d7c441071..d432e9ea6d0 100644
--- a/Content.Server/DeltaV/Station/Components/CaptainStateComponent.cs
+++ b/Content.Server/DeltaV/Station/Components/CaptainStateComponent.cs
@@ -18,7 +18,7 @@ public sealed partial class CaptainStateComponent : Component
/// Assume no captain unless specified
///
[DataField]
- public bool HasCaptain;
+ public bool HasCaptain = false;
///
/// The localization ID used for announcing the cancellation of ACO requests
@@ -42,13 +42,13 @@ public sealed partial class CaptainStateComponent : Component
/// Set after ACO has been requested to avoid duplicate calls
///
[DataField]
- public bool IsACORequestActive;
+ public bool IsACORequestActive = false;
///
/// Used to denote that AA has been brought into the round either from captain or safe.
///
[DataField]
- public bool IsAAInPlay;
+ public bool IsAAInPlay = false;
///
/// The localization ID for announcing that AA has been unlocked for ACO
diff --git a/Content.Server/DeltaV/Station/Systems/CaptainStateSystem.cs b/Content.Server/DeltaV/Station/Systems/CaptainStateSystem.cs
index 38475da89be..c790b22f782 100644
--- a/Content.Server/DeltaV/Station/Systems/CaptainStateSystem.cs
+++ b/Content.Server/DeltaV/Station/Systems/CaptainStateSystem.cs
@@ -46,6 +46,12 @@ public override void Update(float frameTime)
var query = EntityQueryEnumerator();
while (query.MoveNext(out var station, out var captainState))
{
+ if (currentTime < _acoDelay && captainState.IsACORequestActive == true) // Avoid timing issues. No need to run before _acoDelay is reached anyways.
+ {
+ Log.Error($"{captainState} IsACORequestActive true before ACO request time.");
+ captainState.IsACORequestActive = false;
+ }
+
if (captainState.HasCaptain)
HandleHasCaptain(station, captainState);
else
diff --git a/Content.Server/DeltaV/Weather/WeatherEffectsSystem.cs b/Content.Server/DeltaV/Weather/WeatherEffectsSystem.cs
new file mode 100644
index 00000000000..11a29e05cc8
--- /dev/null
+++ b/Content.Server/DeltaV/Weather/WeatherEffectsSystem.cs
@@ -0,0 +1,82 @@
+using Content.Shared.Damage;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Weather;
+using Content.Shared.Whitelist;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.DeltaV.Weather;
+
+///
+/// Handles weather damage for exposed entities.
+///
+public sealed partial class WeatherEffectsSystem : EntitySystem
+{
+ [Dependency] private readonly DamageableSystem _damageable = default!;
+ [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
+ [Dependency] private readonly SharedWeatherSystem _weather = default!;
+
+ private EntityQuery _gridQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _gridQuery = GetEntityQuery();
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var now = _timing.CurTime;
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var map, out var weather))
+ {
+ if (now < weather.NextUpdate)
+ continue;
+
+ weather.NextUpdate = now + weather.UpdateDelay;
+
+ foreach (var (id, data) in weather.Weather)
+ {
+ // start and end do no damage
+ if (data.State != WeatherState.Running)
+ continue;
+
+ UpdateDamage(map, id);
+ }
+ }
+ }
+
+ private void UpdateDamage(EntityUid map, ProtoId id)
+ {
+ var weather = _proto.Index(id);
+ if (weather.Damage is not {} damage)
+ return;
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var mob, out var xform))
+ {
+ // don't give dead bodies 10000 burn, that's not fun for anyone
+ if (xform.MapUid != map || mob.CurrentState == MobState.Dead)
+ continue;
+
+ // if not in space, check for being indoors
+ if (xform.GridUid is {} gridUid && _gridQuery.TryComp(gridUid, out var grid))
+ {
+ var tile = _map.GetTileRef((gridUid, grid), xform.Coordinates);
+ if (!_weather.CanWeatherAffect(gridUid, grid, tile))
+ continue;
+ }
+
+ if (_whitelist.IsBlacklistFailOrNull(weather.DamageBlacklist, uid))
+ _damageable.TryChangeDamage(uid, damage, interruptsDoAfters: false);
+ }
+ }
+}
diff --git a/Content.Server/DeltaV/Weather/WeatherSchedulerComponent.cs b/Content.Server/DeltaV/Weather/WeatherSchedulerComponent.cs
new file mode 100644
index 00000000000..ac69c957057
--- /dev/null
+++ b/Content.Server/DeltaV/Weather/WeatherSchedulerComponent.cs
@@ -0,0 +1,58 @@
+using Content.Shared.Destructible.Thresholds;
+using Content.Shared.Weather;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Server.DeltaV.Weather;
+
+///
+/// Makes weather randomly happen every so often.
+///
+[RegisterComponent, Access(typeof(WeatherSchedulerSystem))]
+[AutoGenerateComponentPause]
+public sealed partial class WeatherSchedulerComponent : Component
+{
+ ///
+ /// Weather stages to schedule.
+ ///
+ [DataField(required: true)]
+ public List Stages = new();
+
+ ///
+ /// The index of to use next, wraps back to the start.
+ ///
+ [DataField]
+ public int Stage;
+
+ ///
+ /// When to go to the next step of the schedule.
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
+ public TimeSpan NextUpdate;
+}
+
+///
+/// A stage in a weather schedule.
+///
+[Serializable, DataDefinition]
+public partial struct WeatherStage
+{
+ ///
+ /// A range of how long the stage can last for, in seconds.
+ ///
+ [DataField(required: true)]
+ public MinMax Duration = new(0, 0);
+
+ ///
+ /// The weather prototype to add, or null for clear weather.
+ ///
+ [DataField]
+ public ProtoId? Weather;
+
+ ///
+ /// Alert message to send in chat for players on the map when it starts.
+ ///
+ [DataField]
+ public LocId? Message;
+}
diff --git a/Content.Server/DeltaV/Weather/WeatherSchedulerSystem.cs b/Content.Server/DeltaV/Weather/WeatherSchedulerSystem.cs
new file mode 100644
index 00000000000..a19a2f3787d
--- /dev/null
+++ b/Content.Server/DeltaV/Weather/WeatherSchedulerSystem.cs
@@ -0,0 +1,75 @@
+using Content.Server.Chat.Managers;
+using Content.Shared.Chat;
+using Content.Shared.Weather;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Timing;
+
+namespace Content.Server.DeltaV.Weather;
+
+public sealed class WeatherSchedulerSystem : EntitySystem
+{
+ [Dependency] private readonly IChatManager _chat = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly SharedWeatherSystem _weather = default!;
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var now = _timing.CurTime;
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var map, out var comp))
+ {
+ if (now < comp.NextUpdate)
+ continue;
+
+ if (comp.Stage >= comp.Stages.Count)
+ comp.Stage = 0;
+
+ var stage = comp.Stages[comp.Stage++];
+ var duration = stage.Duration.Next(_random);
+ comp.NextUpdate = now + TimeSpan.FromSeconds(duration);
+
+ var mapId = Comp(map).MapId;
+ if (stage.Weather is {} weather)
+ {
+ var ending = comp.NextUpdate;
+ // crossfade weather so as one ends the next starts
+ if (HasWeather(comp, comp.Stage - 1))
+ ending += WeatherComponent.ShutdownTime;
+ if (HasWeather(comp, comp.Stage + 1))
+ ending += WeatherComponent.StartupTime;
+ _weather.SetWeather(mapId, _proto.Index(weather), ending);
+ }
+
+ if (stage.Message is {} message)
+ {
+ var msg = Loc.GetString(message);
+ _chat.ChatMessageToManyFiltered(
+ Filter.BroadcastMap(mapId),
+ ChatChannel.Radio,
+ msg,
+ msg,
+ map,
+ false,
+ true,
+ null);
+ }
+ }
+ }
+
+ private bool HasWeather(WeatherSchedulerComponent comp, int stage)
+ {
+ if (stage < 0)
+ stage = comp.Stages.Count + stage;
+ else if (stage >= comp.Stages.Count)
+ stage %= comp.Stages.Count;
+
+ return comp.Stages[stage].Weather != null;
+ }
+}
diff --git a/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs b/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs
deleted file mode 100644
index afc78978c86..00000000000
--- a/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Content.Server.Carrying
-{
- ///
- /// Stores the carrier of an entity being carried.
- ///
- [RegisterComponent]
- public sealed partial class BeingCarriedComponent : Component
- {
- public EntityUid Carrier = default!;
- }
-}
diff --git a/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs b/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs
deleted file mode 100644
index f4fd1fa6d56..00000000000
--- a/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Threading;
-
-namespace Content.Server.Carrying
-{
- [RegisterComponent]
- public sealed partial class CarriableComponent : Component
- {
- ///
- /// Number of free hands required
- /// to carry the entity
- ///
- [DataField("freeHandsRequired")]
- public int FreeHandsRequired = 2;
-
- public CancellationTokenSource? CancelToken;
- }
-}
diff --git a/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs b/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs
deleted file mode 100644
index e79460595b9..00000000000
--- a/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Content.Server.Carrying
-{
- ///
- /// Added to an entity when they are carrying somebody.
- ///
- [RegisterComponent]
- public sealed partial class CarryingComponent : Component
- {
- public EntityUid Carried = default!;
- }
-}
diff --git a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs b/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs
deleted file mode 100644
index 0faff7d8078..00000000000
--- a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs
+++ /dev/null
@@ -1,432 +0,0 @@
-using System.Numerics;
-using System.Threading;
-using Content.Server.DoAfter;
-using Content.Server.Body.Systems;
-using Content.Server.Hands.Systems;
-using Content.Server.Resist;
-using Content.Server.Popups;
-using Content.Server.Inventory;
-using Content.Server.Nyanotrasen.Item.PseudoItem;
-using Content.Shared.Climbing; // Shared instead of Server
-using Content.Shared.Mobs;
-using Content.Shared.DoAfter;
-using Content.Shared.Buckle.Components;
-using Content.Shared.Hands.Components;
-using Content.Shared.Hands;
-using Content.Shared.Stunnable;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Verbs;
-using Content.Shared.Climbing.Events; // Added this.
-using Content.Shared.Carrying;
-using Content.Shared.Movement.Events;
-using Content.Shared.Movement.Systems;
-using Content.Shared.Pulling;
-using Content.Shared.Standing;
-using Content.Shared.ActionBlocker;
-using Content.Shared.Inventory.VirtualItem;
-using Content.Shared.Item;
-using Content.Shared.Throwing;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Movement.Pulling.Components;
-using Content.Shared.Movement.Pulling.Events;
-using Content.Shared.Movement.Pulling.Systems;
-using Content.Shared.Nyanotrasen.Item.PseudoItem;
-using Content.Shared.Storage;
-using Robust.Shared.Map.Components;
-using Robust.Shared.Physics.Components;
-
-namespace Content.Server.Carrying
-{
- public sealed class CarryingSystem : EntitySystem
- {
- [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!;
- [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!;
- [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly StandingStateSystem _standingState = default!;
- [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
- [Dependency] private readonly PullingSystem _pullingSystem = default!;
- [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
- [Dependency] private readonly EscapeInventorySystem _escapeInventorySystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
- [Dependency] private readonly RespiratorSystem _respirator = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly PseudoItemSystem _pseudoItem = default!; // Needed for fitting check
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent>(AddCarryVerb);
- SubscribeLocalEvent>(AddInsertCarriedVerb);
- SubscribeLocalEvent(OnVirtualItemDeleted);
- SubscribeLocalEvent(OnThrow);
- SubscribeLocalEvent(OnParentChanged);
- SubscribeLocalEvent(OnMobStateChanged);
- SubscribeLocalEvent(OnInteractionAttempt);
- SubscribeLocalEvent(OnMoveInput);
- SubscribeLocalEvent(OnMoveAttempt);
- SubscribeLocalEvent(OnStandAttempt);
- SubscribeLocalEvent(OnInteractedWith);
- SubscribeLocalEvent(OnPullAttempt);
- SubscribeLocalEvent(OnStartClimb);
- SubscribeLocalEvent(OnBuckleChange);
- SubscribeLocalEvent(OnBuckleChange);
- SubscribeLocalEvent(OnBuckleChange);
- SubscribeLocalEvent(OnBuckleChange);
- SubscribeLocalEvent(OnDoAfter);
- }
-
- private void AddCarryVerb(EntityUid uid, CarriableComponent component, GetVerbsEvent args)
- {
- if (!args.CanInteract || !args.CanAccess)
- return;
-
- if (!CanCarry(args.User, uid, component))
- return;
-
- if (HasComp(args.User)) // yeah not dealing with that
- return;
-
- if (HasComp(args.User) || HasComp(args.Target))
- return;
-
- if (!_mobStateSystem.IsAlive(args.User))
- return;
-
- if (args.User == args.Target)
- return;
-
- AlternativeVerb verb = new()
- {
- Act = () =>
- {
- StartCarryDoAfter(args.User, uid, component);
- },
- Text = Loc.GetString("carry-verb"),
- Priority = 2
- };
- args.Verbs.Add(verb);
- }
-
- private void AddInsertCarriedVerb(EntityUid uid, CarryingComponent component, GetVerbsEvent args)
- {
- // If the person is carrying someone, and the carried person is a pseudo-item, and the target entity is a storage,
- // then add an action to insert the carried entity into the target
- var toInsert = args.Using;
- if (toInsert is not { Valid: true } || !args.CanAccess || !TryComp(toInsert, out var pseudoItem))
- return;
-
- if (!TryComp(args.Target, out var storageComp))
- return;
-
- if (!_pseudoItem.CheckItemFits((toInsert.Value, pseudoItem), (args.Target, storageComp)))
- return;
-
- InnateVerb verb = new()
- {
- Act = () =>
- {
- DropCarried(uid, toInsert.Value);
- _pseudoItem.TryInsert(args.Target, toInsert.Value, pseudoItem, storageComp);
- },
- Text = Loc.GetString("action-name-insert-other", ("target", toInsert)),
- Priority = 2
- };
- args.Verbs.Add(verb);
- }
-
- ///
- /// Since the carried entity is stored as 2 virtual items, when deleted we want to drop them.
- ///
- private void OnVirtualItemDeleted(EntityUid uid, CarryingComponent component, VirtualItemDeletedEvent args)
- {
- if (!HasComp(args.BlockingEntity))
- return;
-
- DropCarried(uid, args.BlockingEntity);
- }
-
- ///
- /// Basically using virtual item passthrough to throw the carried person. A new age!
- /// Maybe other things besides throwing should use virt items like this...
- ///
- private void OnThrow(EntityUid uid, CarryingComponent component, BeforeThrowEvent args)
- {
- if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity))
- return;
-
- args.ItemUid = virtItem.BlockingEntity;
-
- var multiplier = MassContest(uid, virtItem.BlockingEntity);
- args.ThrowSpeed = 5f * multiplier;
- }
-
- private void OnParentChanged(EntityUid uid, CarryingComponent component, ref EntParentChangedMessage args)
- {
- var xform = Transform(uid);
- if (xform.MapUid != args.OldMapId)
- return;
-
- // Do not drop the carried entity if the new parent is a grid
- if (xform.ParentUid == xform.GridUid)
- return;
-
- DropCarried(uid, component.Carried);
- }
-
- private void OnMobStateChanged(EntityUid uid, CarryingComponent component, MobStateChangedEvent args)
- {
- DropCarried(uid, component.Carried);
- }
-
- ///
- /// Only let the person being carried interact with their carrier and things on their person.
- ///
- private void OnInteractionAttempt(EntityUid uid, BeingCarriedComponent component, InteractionAttemptEvent args)
- {
- if (args.Target == null)
- return;
-
- var targetParent = Transform(args.Target.Value).ParentUid;
-
- if (args.Target.Value != component.Carrier && targetParent != component.Carrier && targetParent != uid)
- args.Cancelled = true;
- }
-
- ///
- /// Try to escape via the escape inventory system.
- ///
- private void OnMoveInput(EntityUid uid, BeingCarriedComponent component, ref MoveInputEvent args)
- {
- if (!TryComp(uid, out var escape))
- return;
-
- if (!args.HasDirectionalMovement)
- return;
-
- if (_actionBlockerSystem.CanInteract(uid, component.Carrier))
- {
- // Note: the mass contest is inverted because weaker entities are supposed to take longer to escape
- _escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape, MassContest(component.Carrier, uid));
- }
- }
-
- private void OnMoveAttempt(EntityUid uid, BeingCarriedComponent component, UpdateCanMoveEvent args)
- {
- args.Cancel();
- }
-
- private void OnStandAttempt(EntityUid uid, BeingCarriedComponent component, StandAttemptEvent args)
- {
- args.Cancel();
- }
-
- private void OnInteractedWith(EntityUid uid, BeingCarriedComponent component, GettingInteractedWithAttemptEvent args)
- {
- if (args.Uid != component.Carrier)
- args.Cancelled = true;
- }
-
- private void OnPullAttempt(EntityUid uid, BeingCarriedComponent component, PullAttemptEvent args)
- {
- args.Cancelled = true;
- }
-
- private void OnStartClimb(EntityUid uid, BeingCarriedComponent component, ref StartClimbEvent args)
- {
- DropCarried(component.Carrier, uid);
- }
-
- private void OnBuckleChange(EntityUid uid, BeingCarriedComponent component, TEvent args) // Augh
- {
- DropCarried(component.Carrier, uid);
- }
-
- private void OnDoAfter(EntityUid uid, CarriableComponent component, CarryDoAfterEvent args)
- {
- component.CancelToken = null;
- if (args.Handled || args.Cancelled)
- return;
-
- if (!CanCarry(args.Args.User, uid, component))
- return;
-
- Carry(args.Args.User, uid);
- args.Handled = true;
- }
- private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component)
- {
- TimeSpan length = GetPickupDuration(carrier, carried);
-
- if (length >= TimeSpan.FromSeconds(9))
- {
- _popupSystem.PopupEntity(Loc.GetString("carry-too-heavy"), carried, carrier, Shared.Popups.PopupType.SmallCaution);
- return;
- }
-
- if (!HasComp(carried))
- length *= 2f;
-
- component.CancelToken = new CancellationTokenSource();
-
- var ev = new CarryDoAfterEvent();
- var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried)
- {
- BreakOnMove = true,
- NeedHand = true
- };
-
- _doAfterSystem.TryStartDoAfter(args);
-
- // Show a popup to the person getting picked up
- _popupSystem.PopupEntity(Loc.GetString("carry-started", ("carrier", carrier)), carried, carried);
- }
-
- private void Carry(EntityUid carrier, EntityUid carried)
- {
- if (TryComp(carried, out var pullable))
- _pullingSystem.TryStopPull(carried, pullable);
-
- var carrierXform = Transform(carrier);
- var xform = Transform(carried);
- _transform.AttachToGridOrMap(carrier, carrierXform);
- _transform.AttachToGridOrMap(carried, xform);
- xform.Coordinates = carrierXform.Coordinates;
- _transform.SetParent(carried, xform, carrier, carrierXform);
-
- _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier);
- _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier);
- var carryingComp = EnsureComp(carrier);
- ApplyCarrySlowdown(carrier, carried);
- var carriedComp = EnsureComp(carried);
- EnsureComp(carried);
-
- carryingComp.Carried = carried;
- carriedComp.Carrier = carrier;
-
- _actionBlockerSystem.UpdateCanMove(carried);
- }
-
- public bool TryCarry(EntityUid carrier, EntityUid toCarry, CarriableComponent? carriedComp = null)
- {
- if (!Resolve(toCarry, ref carriedComp, false))
- return false;
-
- if (!CanCarry(carrier, toCarry, carriedComp))
- return false;
-
- // The second one means that carrier is a pseudo-item and is inside a bag.
- if (HasComp(carrier) || HasComp(carrier))
- return false;
-
- if (GetPickupDuration(carrier, toCarry) > TimeSpan.FromSeconds(9))
- return false;
-
- Carry(carrier, toCarry);
-
- return true;
- }
-
- public void DropCarried(EntityUid carrier, EntityUid carried)
- {
- RemComp(carrier); // get rid of this first so we don't recusrively fire that event
- RemComp(carrier);
- RemComp(carried);
- RemComp(carried);
- _actionBlockerSystem.UpdateCanMove(carried);
- _virtualItemSystem.DeleteInHandsMatching(carrier, carried);
- Transform(carried).AttachToGridOrMap();
- _standingState.Stand(carried);
- _movementSpeed.RefreshMovementSpeedModifiers(carrier);
- }
-
- private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried)
- {
- var massRatio = MassContest(carrier, carried);
-
- if (massRatio == 0)
- massRatio = 1;
-
- var massRatioSq = Math.Pow(massRatio, 2);
- var modifier = (1 - (0.15 / massRatioSq));
- modifier = Math.Max(0.1, modifier);
- var slowdownComp = EnsureComp(carrier);
- _slowdown.SetModifier(carrier, (float) modifier, (float) modifier, slowdownComp);
- }
-
- public bool CanCarry(EntityUid carrier, EntityUid carried, CarriableComponent? carriedComp = null)
- {
- if (!Resolve(carried, ref carriedComp, false))
- return false;
-
- if (carriedComp.CancelToken != null)
- return false;
-
- if (!HasComp(Transform(carrier).ParentUid))
- return false;
-
- if (HasComp(carrier) || HasComp(carried))
- return false;
-
- // if (_respirator.IsReceivingCPR(carried))
- // return false;
-
- if (!TryComp(carrier, out var hands))
- return false;
-
- if (hands.CountFreeHands() < carriedComp.FreeHandsRequired)
- return false;
-
- return true;
- }
-
- private float MassContest(EntityUid roller, EntityUid target, PhysicsComponent? rollerPhysics = null, PhysicsComponent? targetPhysics = null)
- {
- if (!Resolve(roller, ref rollerPhysics, false) || !Resolve(target, ref targetPhysics, false))
- return 1f;
-
- if (targetPhysics.FixturesMass == 0)
- return 1f;
-
- return rollerPhysics.FixturesMass / targetPhysics.FixturesMass;
- }
-
- private TimeSpan GetPickupDuration(EntityUid carrier, EntityUid carried)
- {
- var length = TimeSpan.FromSeconds(3);
-
- var mod = MassContest(carrier, carried);
- if (mod != 0)
- length /= mod;
-
- return length;
- }
-
- public override void Update(float frameTime)
- {
- var query = EntityQueryEnumerator();
- while (query.MoveNext(out var carried, out var comp))
- {
- var carrier = comp.Carrier;
- if (carrier is not { Valid: true } || carried is not { Valid: true })
- continue;
-
- // SOMETIMES - when an entity is inserted into disposals, or a cryosleep chamber - it can get re-parented without a proper reparent event
- // when this happens, it needs to be dropped because it leads to weird behavior
- if (Transform(carried).ParentUid != carrier)
- {
- DropCarried(carrier, carried);
- continue;
- }
-
- // Make sure the carried entity is always centered relative to the carrier, as gravity pulls can offset it otherwise
- var xform = Transform(carried);
- if (!xform.LocalPosition.Equals(Vector2.Zero))
- {
- xform.LocalPosition = Vector2.Zero;
- }
- }
- query.Dispose();
- }
- }
-}
diff --git a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs
index 6df387e6ba8..7437d293da6 100644
--- a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs
+++ b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs
@@ -1,9 +1,9 @@
-using Content.Server.Carrying;
using Content.Server.DoAfter;
using Content.Server.Item;
using Content.Server.Popups;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Bed.Sleep;
+using Content.Shared.DeltaV.Carrying;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Item;
diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
index d77bec6b1ae..faf27484ce9 100644
--- a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
+++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
@@ -11,230 +11,184 @@
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-namespace Content.Server.Nyanotrasen.Kitchen.Components
+namespace Content.Server.Nyanotrasen.Kitchen.Components;
+
+// TODO: move to shared and get rid of SharedDeepFryerComponent
+[RegisterComponent, Access(typeof(SharedDeepfryerSystem))]
+public sealed partial class DeepFryerComponent : SharedDeepFryerComponent
{
- [RegisterComponent]
- [Access(typeof(SharedDeepfryerSystem))]
- // This line appears to be depracted: [ComponentReference(typeof(SharedDeepFryerComponent))]
- public sealed partial class DeepFryerComponent : SharedDeepFryerComponent
- {
- // There are three levels to how the deep fryer treats entities.
- //
- // 1. An entity can be rejected by the blacklist and be untouched by
- // anything other than heat damage.
- //
- // 2. An entity can be deep-fried but not turned into an edible. The
- // change will be mostly cosmetic. Any entity that does not match
- // the blacklist will fall into this category.
- //
- // 3. An entity can be deep-fried and turned into something edible. The
- // change will permit the item to be permanently destroyed by eating
- // it.
-
- ///
- /// When will the deep fryer layer on the next stage of crispiness?
- ///
- [DataField("nextFryTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
- public TimeSpan NextFryTime { get; set; }
-
- ///
- /// How much waste needs to be added at the next update interval?
- ///
- public FixedPoint2 WasteToAdd { get; set; } = FixedPoint2.Zero;
-
- ///
- /// How often are items in the deep fryer fried?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("fryInterval")]
- public TimeSpan FryInterval { get; set; } = TimeSpan.FromSeconds(5);
-
- ///
- /// What entities cannot be deep-fried no matter what?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("blacklist")]
- public EntityWhitelist? Blacklist { get; set; }
-
- ///
- /// What entities can be deep-fried into being edible?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("whitelist")]
- public EntityWhitelist? Whitelist { get; set; }
-
- ///
- /// What are over-cooked and burned entities turned into?
- ///
- ///
- /// To prevent unwanted destruction of items, only food can be turned
- /// into this.
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("charredPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string? CharredPrototype { get; set; }
-
- ///
- /// What reagents are considered valid cooking oils?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("fryingOils", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
- public HashSet FryingOils { get; set; } = new();
-
- ///
- /// What reagents are added to tasty deep-fried food?
- /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("goodReagents")]
- public List GoodReagents { get; set; } = new();
-
- ///
- /// What reagents are added to terrible deep-fried food?
- /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("badReagents")]
- public List BadReagents { get; set; } = new();
-
- ///
- /// What reagents replace every 1 unit of oil spent on frying?
- /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("wasteReagents")]
- public List WasteReagents { get; set; } = new();
-
- ///
- /// What flavors go well with deep frying?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("goodFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
- public HashSet GoodFlavors { get; set; } = new();
-
- ///
- /// What flavors don't go well with deep frying?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("badFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
- public HashSet BadFlavors { get; set; } = new();
-
- ///
- /// How much is the price coefficiency of a food changed for each good flavor?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("goodFlavorPriceBonus")]
- public float GoodFlavorPriceBonus { get; set; } = 0.2f;
-
- ///
- /// How much is the price coefficiency of a food changed for each bad flavor?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("badFlavorPriceMalus")]
- public float BadFlavorPriceMalus { get; set; } = -0.3f;
-
- ///
- /// What is the name of the solution container for the fryer's oil?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("solution")]
- public string SolutionName { get; set; } = "vat_oil";
-
- public Solution Solution { get; set; } = default!;
-
- ///
- /// What is the name of the entity container for items inside the deep fryer?
- ///
- [DataField("storage")]
- public string StorageName { get; set; } = "vat_entities";
-
- public BaseContainer Storage { get; set; } = default!;
-
- ///
- /// How much solution should be imparted based on an item's size?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("solutionSizeCoefficient")]
- public FixedPoint2 SolutionSizeCoefficient { get; set; } = 1f;
-
- ///
- /// What's the maximum amount of solution that should ever be imparted?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("solutionSplitMax")]
- public FixedPoint2 SolutionSplitMax { get; set; } = 10f;
-
- ///
- /// What percent of the fryer's solution has to be oil in order for it to fry?
- ///
- ///
- /// The chef will have to clean it out occasionally, and if too much
- /// non-oil reagents are added, the vat will have to be drained.
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("fryingOilThreshold")]
- public FixedPoint2 FryingOilThreshold { get; set; } = 0.5f;
-
- ///
- /// What is the bare minimum number of oil units to prevent the fryer
- /// from unsafe operation?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("safeOilVolume")]
- public FixedPoint2 SafeOilVolume { get; set; } = 10f;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("unsafeOilVolumeEffects")]
- public List UnsafeOilVolumeEffects = new();
-
- ///
- /// What is the temperature of the vat when the deep fryer is powered?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("poweredTemperature")]
- public float PoweredTemperature = 550.0f;
-
- ///
- /// How many entities can this deep fryer hold?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- public int StorageMaxEntities = 4;
-
- ///
- /// How many entities can be held, at a minimum?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("baseStorageMaxEntities")]
- public int BaseStorageMaxEntities = 4;
-
- // ///
- // /// What upgradeable machine part dictates the quality of the storage size?
- // ///
- // [DataField("machinePartStorageMax", customTypeSerializer: typeof(PrototypeIdSerializer))]
- // public string MachinePartStorageMax = "MatterBin";
-
- ///
- /// How much extra storage is added per part rating?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("storagePerPartRating")]
- public int StoragePerPartRating = 4;
-
- ///
- /// What sound is played when an item is inserted into hot oil?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("soundInsertItem")]
- public SoundSpecifier SoundInsertItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg");
-
- ///
- /// What sound is played when an item is removed?
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("soundRemoveItem")]
- public SoundSpecifier SoundRemoveItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg");
- }
+ // There are three levels to how the deep fryer treats entities.
+ //
+ // 1. An entity can be rejected by the blacklist and be untouched by
+ // anything other than heat damage.
+ //
+ // 2. An entity can be deep-fried but not turned into an edible. The
+ // change will be mostly cosmetic. Any entity that does not match
+ // the blacklist will fall into this category.
+ //
+ // 3. An entity can be deep-fried and turned into something edible. The
+ // change will permit the item to be permanently destroyed by eating
+ // it.
+
+ ///
+ /// When will the deep fryer layer on the next stage of crispiness?
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextFryTime;
+
+ ///
+ /// How much waste needs to be added at the next update interval?
+ ///
+ [DataField]
+ public FixedPoint2 WasteToAdd = FixedPoint2.Zero;
+
+ ///
+ /// How often are items in the deep fryer fried?
+ ///
+ [DataField]
+ public TimeSpan FryInterval = TimeSpan.FromSeconds(5);
+
+ ///
+ /// What entities cannot be deep-fried no matter what?
+ ///
+ [DataField]
+ public EntityWhitelist? Blacklist;
+
+ ///
+ /// What entities can be deep-fried into being edible?
+ ///
+ [DataField]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// What are over-cooked and burned entities turned into?
+ ///
+ ///
+ /// To prevent unwanted destruction of items, only food can be turned
+ /// into this.
+ ///
+ [DataField]
+ public EntProtoId? CharredPrototype;
+
+ ///
+ /// What reagents are considered valid cooking oils?
+ ///
+ [DataField]
+ public HashSet> FryingOils = new();
+
+ ///
+ /// What reagents are added to tasty deep-fried food?
+ ///
+ [DataField]
+ public List GoodReagents = new();
+
+ ///
+ /// What reagents are added to terrible deep-fried food?
+ ///
+ [DataField]
+ public List BadReagents = new();
+
+ ///
+ /// What reagents replace every 1 unit of oil spent on frying?
+ ///
+ [DataField]
+ public List WasteReagents = new();
+
+ ///
+ /// What flavors go well with deep frying?
+ ///
+ [DataField]
+ public HashSet> GoodFlavors = new();
+
+ ///
+ /// What flavors don't go well with deep frying?
+ ///
+ [DataField]
+ public HashSet> BadFlavors = new();
+
+ ///
+ /// How much is the price coefficiency of a food changed for each good flavor?
+ ///
+ [DataField]
+ public float GoodFlavorPriceBonus = 0.2f;
+
+ ///
+ /// How much is the price coefficiency of a food changed for each bad flavor?
+ ///
+ [DataField]
+ public float BadFlavorPriceMalus = -0.3f;
+
+ ///
+ /// What is the name of the solution container for the fryer's oil?
+ ///
+ [DataField]
+ public string SolutionName = "vat_oil";
+
+ // TODO: Entity
+ public Solution Solution = default!;
+
+ ///
+ /// What is the name of the entity container for items inside the deep fryer?
+ ///
+ [DataField("storage")]
+ public string StorageName = "vat_entities";
+
+ public BaseContainer Storage = default!;
+
+ ///
+ /// How much solution should be imparted based on an item's size?
+ ///
+ [DataField]
+ public FixedPoint2 SolutionSizeCoefficient = 1f;
+
+ ///
+ /// What's the maximum amount of solution that should ever be imparted?
+ ///
+ [DataField]
+ public FixedPoint2 SolutionSplitMax = 10f;
+
+ ///
+ /// What percent of the fryer's solution has to be oil in order for it to fry?
+ ///
+ ///
+ /// The chef will have to clean it out occasionally, and if too much
+ /// non-oil reagents are added, the vat will have to be drained.
+ ///
+ [DataField]
+ public FixedPoint2 FryingOilThreshold = 0.5f;
+
+ ///
+ /// What is the bare minimum number of oil units to prevent the fryer
+ /// from unsafe operation?
+ ///
+ [DataField]
+ public FixedPoint2 SafeOilVolume = 10f;
+
+ [DataField]
+ public List UnsafeOilVolumeEffects = new();
+
+ ///
+ /// What is the temperature of the vat when the deep fryer is powered?
+ ///
+ [DataField]
+ public float PoweredTemperature = 550.0f;
+
+ ///
+ /// How many entities can this deep fryer hold?
+ ///
+ [DataField]
+ public int StorageMaxEntities = 4;
+
+ ///
+ /// What sound is played when an item is inserted into hot oil?
+ ///
+ [DataField]
+ public SoundSpecifier SoundInsertItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg");
+
+ ///
+ /// What sound is played when an item is removed?
+ ///
+ [DataField]
+ public SoundSpecifier SoundRemoveItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg");
}
diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs
index 3f93787934c..fa2807509a6 100644
--- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs
+++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs
@@ -11,15 +11,20 @@
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.NPC;
+using Content.Shared.Nutrition;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nyanotrasen.Kitchen.Components;
using Content.Shared.Paper;
+using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems;
public sealed partial class DeepFryerSystem
{
+ private HashSet> _badFlavors = new();
+ private HashSet> _goodFlavors = new();
+
///
/// Make an item look deep-fried.
///
@@ -129,33 +134,33 @@ private void MakeEdible(EntityUid uid, DeepFryerComponent component, EntityUid i
var extraSolution = new Solution();
if (TryComp(item, out FlavorProfileComponent? flavorProfileComponent))
{
- HashSet goodFlavors = new(flavorProfileComponent.Flavors);
- goodFlavors.IntersectWith(component.GoodFlavors);
+ _goodFlavors.Clear();
+ _goodFlavors.IntersectWith(component.GoodFlavors);
- HashSet badFlavors = new(flavorProfileComponent.Flavors);
- badFlavors.IntersectWith(component.BadFlavors);
+ _badFlavors.Clear();
+ _badFlavors.IntersectWith(component.BadFlavors);
deepFriedComponent.PriceCoefficient = Math.Max(0.01f,
1.0f
- + goodFlavors.Count * component.GoodFlavorPriceBonus
- - badFlavors.Count * component.BadFlavorPriceMalus);
+ + _goodFlavors.Count * component.GoodFlavorPriceBonus
+ - _badFlavors.Count * component.BadFlavorPriceMalus);
- if (goodFlavors.Count > 0)
+ if (_goodFlavors.Count > 0)
{
foreach (var reagent in component.GoodReagents)
{
- extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * goodFlavors.Count);
+ extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * _goodFlavors.Count);
// Mask the taste of "medicine."
flavorProfileComponent.IgnoreReagents.Add(reagent.Reagent.ToString());
}
}
- if (badFlavors.Count > 0)
+ if (_badFlavors.Count > 0)
{
foreach (var reagent in component.BadReagents)
{
- extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * badFlavors.Count);
+ extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * _badFlavors.Count);
}
}
}
diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
index 40a658f0c44..4ac8684a7a3 100644
--- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
+++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
@@ -105,7 +105,6 @@ public override void Initialize()
SubscribeLocalEvent(OnInitDeepFryer);
SubscribeLocalEvent(OnPowerChange);
- // SubscribeLocalEvent(OnRefreshParts);
SubscribeLocalEvent(OnDeconstruct);
SubscribeLocalEvent(OnDestruction);
SubscribeLocalEvent(OnThrowHitBy);
@@ -437,14 +436,6 @@ private void OnDestruction(EntityUid uid, DeepFryerComponent component, Destruct
_containerSystem.EmptyContainer(component.Storage, true);
}
- // private void OnRefreshParts(EntityUid uid, DeepFryerComponent component, RefreshPartsEvent args)
- // {
- // var ratingStorage = args.PartRatings[component.MachinePartStorageMax];
- //
- // component.StorageMaxEntities = component.BaseStorageMaxEntities +
- // (int) (component.StoragePerPartRating * (ratingStorage - 1));
- // }
-
///
/// Allow thrown items to land in a basket.
///
diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs
index 93a83465861..eec8ebb5072 100644
--- a/Content.Server/Resist/EscapeInventorySystem.cs
+++ b/Content.Server/Resist/EscapeInventorySystem.cs
@@ -1,6 +1,5 @@
using Content.Server.Popups;
using Content.Shared.Storage;
-using Content.Server.Carrying; // Carrying system from Nyanotrasen.
using Content.Shared.Inventory;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Storage.Components;
@@ -25,7 +24,6 @@ public sealed class EscapeInventorySystem : EntitySystem
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
- [Dependency] private readonly CarryingSystem _carryingSystem = default!; // Carrying system from Nyanotrasen.
[Dependency] private readonly SharedActionsSystem _actions = default!; // DeltaV
///
@@ -109,13 +107,6 @@ private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, Esca
if (args.Handled || args.Cancelled)
return;
- if (TryComp(uid, out var carried)) // Start of carrying system of nyanotrasen.
- {
- _carryingSystem.DropCarried(carried.Carrier, uid);
- return;
- } // End of carrying system of nyanotrasen.
-
-
_containerSystem.AttachParentToContainerOrGrid((uid, Transform(uid)));
args.Handled = true;
}
diff --git a/Content.Shared/Cargo/CargoBountyHistoryData.cs b/Content.Shared/Cargo/CargoBountyHistoryData.cs
new file mode 100644
index 00000000000..43da42d5587
--- /dev/null
+++ b/Content.Shared/Cargo/CargoBountyHistoryData.cs
@@ -0,0 +1,46 @@
+using Robust.Shared.Serialization;
+using Content.Shared.Cargo.Prototypes;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Cargo;
+
+///
+/// A data structure for storing historical information about bounties.
+///
+[DataDefinition, NetSerializable, Serializable]
+public readonly partial record struct CargoBountyHistoryData
+{
+ ///
+ /// A unique id used to identify the bounty
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public string Id { get; init; } = string.Empty;
+
+ ///
+ /// Optional name of the actor that skipped the bounty.
+ /// Only set when the bounty has been skipped.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public string? ActorName { get; init; } = default;
+
+ ///
+ /// Time when this bounty was completed or skipped
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan Timestamp { get; init; } = TimeSpan.MinValue;
+
+ ///
+ /// The prototype containing information about the bounty.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField(required: true)]
+ public ProtoId Bounty { get; init; } = string.Empty;
+
+ public CargoBountyHistoryData(CargoBountyData bounty, TimeSpan timestamp, string? actorName)
+ {
+ Bounty = bounty.Bounty;
+ Id = bounty.Id;
+ ActorName = actorName;
+ Timestamp = timestamp;
+ }
+}
diff --git a/Content.Shared/Cargo/Components/CargoBountyConsoleComponent.cs b/Content.Shared/Cargo/Components/CargoBountyConsoleComponent.cs
index bf82a08127e..8c78312be19 100644
--- a/Content.Shared/Cargo/Components/CargoBountyConsoleComponent.cs
+++ b/Content.Shared/Cargo/Components/CargoBountyConsoleComponent.cs
@@ -50,11 +50,13 @@ public sealed partial class CargoBountyConsoleComponent : Component
public sealed class CargoBountyConsoleState : BoundUserInterfaceState
{
public List Bounties;
+ public List History;
public TimeSpan UntilNextSkip;
- public CargoBountyConsoleState(List bounties, TimeSpan untilNextSkip)
+ public CargoBountyConsoleState(List bounties, List history, TimeSpan untilNextSkip)
{
Bounties = bounties;
+ History = history;
UntilNextSkip = untilNextSkip;
}
}
diff --git a/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs b/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs
new file mode 100644
index 00000000000..7e519e7e04b
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+///
+/// Stores the carrier of an entity being carried.
+///
+[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))]
+[AutoGenerateComponentState]
+public sealed partial class BeingCarriedComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public EntityUid Carrier;
+}
diff --git a/Content.Shared/DeltaV/Carrying/CarriableComponent.cs b/Content.Shared/DeltaV/Carrying/CarriableComponent.cs
new file mode 100644
index 00000000000..ad1968aec62
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarriableComponent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))]
+public sealed partial class CarriableComponent : Component
+{
+ ///
+ /// Number of free hands required
+ /// to carry the entity
+ ///
+ [DataField]
+ public int FreeHandsRequired = 2;
+}
diff --git a/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs b/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs
new file mode 100644
index 00000000000..7ea0375518a
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs
@@ -0,0 +1,7 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+[Serializable, NetSerializable]
+public sealed partial class CarryDoAfterEvent : SimpleDoAfterEvent;
diff --git a/Content.Shared/DeltaV/Carrying/CarryingComponent.cs b/Content.Shared/DeltaV/Carrying/CarryingComponent.cs
new file mode 100644
index 00000000000..e6661da0e04
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarryingComponent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+///
+/// Added to an entity when they are carrying somebody.
+///
+[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))]
+[AutoGenerateComponentState]
+public sealed partial class CarryingComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public EntityUid Carried;
+}
diff --git a/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs b/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs
new file mode 100644
index 00000000000..9e1be89370c
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSlowdownSystem))]
+[AutoGenerateComponentState]
+public sealed partial class CarryingSlowdownComponent : Component
+{
+ ///
+ /// Modifier for both walk and sprint speed.
+ ///
+ [DataField, AutoNetworkedField]
+ public float Modifier = 1.0f;
+}
diff --git a/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs b/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs
new file mode 100644
index 00000000000..677c53eedc0
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs
@@ -0,0 +1,29 @@
+using Content.Shared.Movement.Systems;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+public sealed class CarryingSlowdownSystem : EntitySystem
+{
+ [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnRefreshMoveSpeed);
+ }
+
+ public void SetModifier(Entity ent, float modifier)
+ {
+ ent.Comp ??= EnsureComp(ent);
+ ent.Comp.Modifier = modifier;
+ Dirty(ent, ent.Comp);
+
+ _movementSpeed.RefreshMovementSpeedModifiers(ent);
+ }
+
+ private void OnRefreshMoveSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args)
+ {
+ args.ModifySpeed(ent.Comp.Modifier, ent.Comp.Modifier);
+ }
+}
diff --git a/Content.Shared/DeltaV/Carrying/CarryingSystem.cs b/Content.Shared/DeltaV/Carrying/CarryingSystem.cs
new file mode 100644
index 00000000000..2b47c49abd1
--- /dev/null
+++ b/Content.Shared/DeltaV/Carrying/CarryingSystem.cs
@@ -0,0 +1,384 @@
+using Content.Shared.ActionBlocker;
+using Content.Shared.Buckle.Components;
+using Content.Shared.Climbing.Events;
+using Content.Shared.DoAfter;
+using Content.Shared.Hands;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Inventory.VirtualItem;
+using Content.Shared.Item;
+using Content.Shared.Mobs;
+using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Events;
+using Content.Shared.Movement.Pulling.Systems;
+using Content.Shared.Movement.Systems;
+using Content.Shared.Nyanotrasen.Item.PseudoItem;
+using Content.Shared.Popups;
+using Content.Shared.Pulling;
+using Content.Shared.Resist;
+using Content.Shared.Standing;
+using Content.Shared.Storage;
+using Content.Shared.Stunnable;
+using Content.Shared.Throwing;
+using Content.Shared.Verbs;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Network;
+using Robust.Shared.Physics.Components;
+using System.Numerics;
+
+namespace Content.Shared.DeltaV.Carrying;
+
+public sealed class CarryingSystem : EntitySystem
+{
+ [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
+ [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!;
+ [Dependency] private readonly INetManager _net = default!;
+ [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedPseudoItemSystem _pseudoItem = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly StandingStateSystem _standingState = default!;
+ [Dependency] private readonly SharedVirtualItemSystem _virtualItem = default!;
+
+ private EntityQuery _physicsQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _physicsQuery = GetEntityQuery();
+
+ SubscribeLocalEvent>(AddCarryVerb);
+ SubscribeLocalEvent>(AddInsertCarriedVerb);
+ SubscribeLocalEvent(OnVirtualItemDeleted);
+ SubscribeLocalEvent(OnThrow);
+ SubscribeLocalEvent(OnParentChanged);
+ SubscribeLocalEvent(OnMobStateChanged);
+ SubscribeLocalEvent(OnInteractionAttempt);
+ SubscribeLocalEvent(OnMoveAttempt);
+ SubscribeLocalEvent(OnStandAttempt);
+ SubscribeLocalEvent(OnInteractedWith);
+ SubscribeLocalEvent(OnPullAttempt);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDrop);
+ SubscribeLocalEvent(OnDoAfter);
+ }
+
+ private void AddCarryVerb(Entity ent, ref GetVerbsEvent args)
+ {
+ var user = args.User;
+ var target = args.Target;
+ if (!args.CanInteract || !args.CanAccess || user == target)
+ return;
+
+ if (!CanCarry(user, ent))
+ return;
+
+ args.Verbs.Add(new AlternativeVerb()
+ {
+ Act = () => StartCarryDoAfter(user, ent),
+ Text = Loc.GetString("carry-verb"),
+ Priority = 2
+ });
+ }
+
+ private void AddInsertCarriedVerb(Entity ent, ref GetVerbsEvent args)
+ {
+ // If the person is carrying someone, and the carried person is a pseudo-item, and the target entity is a storage,
+ // then add an action to insert the carried entity into the target
+ // AKA put carried felenid into a duffelbag
+ if (args.Using is not {} carried || !args.CanAccess || !TryComp(carried, out var pseudoItem))
+ return;
+
+ var target = args.Target;
+ if (!TryComp(target, out var storageComp))
+ return;
+
+ if (!_pseudoItem.CheckItemFits((carried, pseudoItem), (target, storageComp)))
+ return;
+
+ args.Verbs.Add(new InnateVerb()
+ {
+ Act = () =>
+ {
+ DropCarried(ent, carried);
+ _pseudoItem.TryInsert(target, carried, pseudoItem, storageComp);
+ },
+ Text = Loc.GetString("action-name-insert-other", ("target", carried)),
+ Priority = 2
+ });
+ }
+
+ ///
+ /// Since the carried entity is stored as 2 virtual items, when deleted we want to drop them.
+ ///
+ private void OnVirtualItemDeleted(Entity ent, ref VirtualItemDeletedEvent args)
+ {
+ if (HasComp(args.BlockingEntity))
+ DropCarried(ent, args.BlockingEntity);
+ }
+
+ ///
+ /// Basically using virtual item passthrough to throw the carried person. A new age!
+ /// Maybe other things besides throwing should use virt items like this...
+ ///
+ private void OnThrow(Entity ent, ref BeforeThrowEvent args)
+ {
+ if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity))
+ return;
+
+ var carried = virtItem.BlockingEntity;
+ args.ItemUid = carried;
+
+ args.ThrowSpeed = 5f * MassContest(ent, carried);
+ }
+
+ private void OnParentChanged(Entity ent, ref EntParentChangedMessage args)
+ {
+ var xform = Transform(ent);
+ if (xform.MapUid != args.OldMapId)
+ return;
+
+ // Do not drop the carried entity if the new parent is a grid
+ if (xform.ParentUid == xform.GridUid)
+ return;
+
+ DropCarried(ent, ent.Comp.Carried);
+ }
+
+ private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args)
+ {
+ DropCarried(ent, ent.Comp.Carried);
+ }
+
+ ///
+ /// Only let the person being carried interact with their carrier and things on their person.
+ ///
+ private void OnInteractionAttempt(Entity ent, ref InteractionAttemptEvent args)
+ {
+ if (args.Target is not {} target)
+ return;
+
+ var targetParent = Transform(target).ParentUid;
+
+ var carrier = ent.Comp.Carrier;
+ if (target != carrier && targetParent != carrier && targetParent != ent.Owner)
+ args.Cancelled = true;
+ }
+
+ private void OnMoveAttempt(Entity ent, ref UpdateCanMoveEvent args)
+ {
+ args.Cancel();
+ }
+
+ private void OnStandAttempt(Entity ent, ref StandAttemptEvent args)
+ {
+ args.Cancel();
+ }
+
+ private void OnInteractedWith(Entity ent, ref GettingInteractedWithAttemptEvent args)
+ {
+ if (args.Uid != ent.Comp.Carrier)
+ args.Cancelled = true;
+ }
+
+ private void OnPullAttempt(Entity ent, ref PullAttemptEvent args)
+ {
+ args.Cancelled = true;
+ }
+
+ private void OnDrop(Entity ent, ref TEvent args) // Augh
+ {
+ DropCarried(ent.Comp.Carrier, ent);
+ }
+
+ private void OnDoAfter(Entity ent, ref CarryDoAfterEvent args)
+ {
+ if (args.Handled || args.Cancelled)
+ return;
+
+ if (!CanCarry(args.Args.User, ent))
+ return;
+
+ Carry(args.Args.User, ent);
+ args.Handled = true;
+ }
+
+ private void StartCarryDoAfter(EntityUid carrier, Entity carried)
+ {
+ TimeSpan length = GetPickupDuration(carrier, carried);
+
+ if (length.TotalSeconds >= 9f)
+ {
+ _popup.PopupClient(Loc.GetString("carry-too-heavy"), carried, carrier, PopupType.SmallCaution);
+ return;
+ }
+
+ if (!HasComp(carried))
+ length *= 2f;
+
+ var ev = new CarryDoAfterEvent();
+ var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried)
+ {
+ BreakOnMove = true,
+ NeedHand = true
+ };
+
+ _doAfter.TryStartDoAfter(args);
+
+ // Show a popup to the person getting picked up
+ _popup.PopupEntity(Loc.GetString("carry-started", ("carrier", carrier)), carried, carried);
+ }
+
+ private void Carry(EntityUid carrier, EntityUid carried)
+ {
+ if (TryComp(carried, out var pullable))
+ _pulling.TryStopPull(carried, pullable);
+
+ var carrierXform = Transform(carrier);
+ var xform = Transform(carried);
+ _transform.AttachToGridOrMap(carrier, carrierXform);
+ _transform.AttachToGridOrMap(carried, xform);
+ _transform.SetParent(carried, xform, carrier, carrierXform);
+
+ var carryingComp = EnsureComp(carrier);
+ carryingComp.Carried = carried;
+ Dirty(carrier, carryingComp);
+ var carriedComp = EnsureComp(carried);
+ carriedComp.Carrier = carrier;
+ Dirty(carried, carriedComp);
+ EnsureComp(carried);
+
+ ApplyCarrySlowdown(carrier, carried);
+
+ _actionBlocker.UpdateCanMove(carried);
+
+ if (_net.IsClient) // no spawning prediction
+ return;
+
+ _virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
+ _virtualItem.TrySpawnVirtualItemInHand(carried, carrier);
+ }
+
+ public bool TryCarry(EntityUid carrier, Entity toCarry)
+ {
+ if (!Resolve(toCarry, ref toCarry.Comp, false))
+ return false;
+
+ if (!CanCarry(carrier, (toCarry, toCarry.Comp)))
+ return false;
+
+ // The second one means that carrier is a pseudo-item and is inside a bag.
+ if (HasComp(carrier) || HasComp(carrier))
+ return false;
+
+ if (GetPickupDuration(carrier, toCarry).TotalSeconds > 9f)
+ return false;
+
+ Carry(carrier, toCarry);
+ return true;
+ }
+
+ public void DropCarried(EntityUid carrier, EntityUid carried)
+ {
+ Drop(carried);
+ RemComp(carrier); // get rid of this first so we don't recursively fire that event
+ RemComp(carrier);
+ _virtualItem.DeleteInHandsMatching(carrier, carried);
+ _movementSpeed.RefreshMovementSpeedModifiers(carrier);
+ }
+
+ private void Drop(EntityUid carried)
+ {
+ RemComp(carried);
+ RemComp(carried); // TODO SHITMED: make sure this doesnt let you make someone with no legs walk
+ _actionBlocker.UpdateCanMove(carried);
+ Transform(carried).AttachToGridOrMap();
+ _standingState.Stand(carried);
+ }
+
+ private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried)
+ {
+ var massRatio = MassContest(carrier, carried);
+
+ if (massRatio == 0)
+ massRatio = 1;
+
+ var massRatioSq = Math.Pow(massRatio, 2);
+ var modifier = (1 - (0.15 / massRatioSq));
+ modifier = Math.Max(0.1, modifier);
+ _slowdown.SetModifier(carrier, (float) modifier);
+ }
+
+ public bool CanCarry(EntityUid carrier, Entity carried)
+ {
+ return
+ carrier != carried.Owner &&
+ // can't carry multiple people, even if you have 4 hands it will break invariants when removing carryingcomponent for first carried person
+ !HasComp(carrier) &&
+ // can't carry someone in a locker, buckled, etc
+ HasComp(Transform(carrier).ParentUid) &&
+ // no tower of spacemen or stack overflow
+ !HasComp(carrier) &&
+ !HasComp(carried) &&
+ // finally check that there are enough free hands
+ TryComp(carrier, out var hands) &&
+ hands.CountFreeHands() >= carried.Comp.FreeHandsRequired;
+ }
+
+ private float MassContest(EntityUid roller, EntityUid target)
+ {
+ if (!_physicsQuery.TryComp(roller, out var rollerPhysics) || !_physicsQuery.TryComp(target, out var targetPhysics))
+ return 1f;
+
+ if (targetPhysics.FixturesMass == 0)
+ return 1f;
+
+ return rollerPhysics.FixturesMass / targetPhysics.FixturesMass;
+ }
+
+ private TimeSpan GetPickupDuration(EntityUid carrier, EntityUid carried)
+ {
+ var length = TimeSpan.FromSeconds(3);
+
+ var mod = MassContest(carrier, carried);
+ if (mod != 0)
+ length /= mod;
+
+ return length;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var carried, out var comp, out var xform))
+ {
+ var carrier = comp.Carrier;
+ if (TerminatingOrDeleted(carrier))
+ {
+ RemCompDeferred(carried);
+ continue;
+ }
+
+ // SOMETIMES - when an entity is inserted into disposals, or a cryosleep chamber - it can get re-parented without a proper reparent event
+ // when this happens, it needs to be dropped because it leads to weird behavior
+ if (xform.ParentUid != carrier)
+ {
+ DropCarried(carrier, carried);
+ continue;
+ }
+
+ // Make sure the carried entity is always centered relative to the carrier, as gravity pulls can offset it otherwise
+ _transform.SetLocalPosition(carried, Vector2.Zero);
+ }
+ }
+}
diff --git a/Content.Shared/DeltaV/Item/ItemToggle/Components/ItemToggleExamineComponent.cs b/Content.Shared/DeltaV/Item/ItemToggle/Components/ItemToggleExamineComponent.cs
new file mode 100644
index 00000000000..e8064d79456
--- /dev/null
+++ b/Content.Shared/DeltaV/Item/ItemToggle/Components/ItemToggleExamineComponent.cs
@@ -0,0 +1,17 @@
+using Content.Shared.DeltaV.Item.ItemToggle.Systems;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Item.ItemToggle.Components;
+
+///
+/// Adds examine text when the item is on or off.
+///
+[RegisterComponent, NetworkedComponent, Access(typeof(ItemToggleExamineSystem))]
+public sealed partial class ItemToggleExamineComponent : Component
+{
+ [DataField(required: true)]
+ public LocId On;
+
+ [DataField(required: true)]
+ public LocId Off;
+}
diff --git a/Content.Shared/DeltaV/Item/ItemToggle/Systems/ItemToggleExamineSystem.cs b/Content.Shared/DeltaV/Item/ItemToggle/Systems/ItemToggleExamineSystem.cs
new file mode 100644
index 00000000000..5abb0aec78b
--- /dev/null
+++ b/Content.Shared/DeltaV/Item/ItemToggle/Systems/ItemToggleExamineSystem.cs
@@ -0,0 +1,23 @@
+using Content.Shared.DeltaV.Item.ItemToggle.Components;
+using Content.Shared.Examine;
+using Content.Shared.Item.ItemToggle;
+
+namespace Content.Shared.DeltaV.Item.ItemToggle.Systems;
+
+public sealed class ItemToggleExamineSystem : EntitySystem
+{
+ [Dependency] private readonly ItemToggleSystem _toggle = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnExamined);
+ }
+
+ private void OnExamined(Entity ent, ref ExaminedEvent args)
+ {
+ var msg = _toggle.IsActivated(ent.Owner) ? ent.Comp.On : ent.Comp.Off;
+ args.PushMarkup(Loc.GetString(msg));
+ }
+}
diff --git a/Content.Shared/DeltaV/Weather/Components/AshStormImmuneComponent.cs b/Content.Shared/DeltaV/Weather/Components/AshStormImmuneComponent.cs
new file mode 100644
index 00000000000..ec2c272695b
--- /dev/null
+++ b/Content.Shared/DeltaV/Weather/Components/AshStormImmuneComponent.cs
@@ -0,0 +1,9 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeltaV.Weather.Components;
+
+///
+/// Makes an entity not take damage from ash storms.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class AshStormImmuneComponent : Component;
diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs
deleted file mode 100644
index 6acd6b775f3..00000000000
--- a/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Robust.Shared.Serialization;
-using Content.Shared.DoAfter;
-
-namespace Content.Shared.Carrying
-{
- [Serializable, NetSerializable]
- public sealed partial class CarryDoAfterEvent : SimpleDoAfterEvent
- {
- }
-}
diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs
deleted file mode 100644
index aabde66af0d..00000000000
--- a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Carrying
-{
- [RegisterComponent, NetworkedComponent, Access(typeof(CarryingSlowdownSystem))]
-
- public sealed partial class CarryingSlowdownComponent : Component
- {
- [DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
- public float WalkModifier = 1.0f;
-
- [DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
- public float SprintModifier = 1.0f;
- }
-
- [Serializable, NetSerializable]
- public sealed class CarryingSlowdownComponentState : ComponentState
- {
- public float WalkModifier;
- public float SprintModifier;
- public CarryingSlowdownComponentState(float walkModifier, float sprintModifier)
- {
- WalkModifier = walkModifier;
- SprintModifier = sprintModifier;
- }
- }
-}
diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs
deleted file mode 100644
index 9b9c8cec10f..00000000000
--- a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Content.Shared.Movement.Systems;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Carrying
-{
- public sealed class CarryingSlowdownSystem : EntitySystem
- {
- [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnGetState);
- SubscribeLocalEvent(OnHandleState);
- SubscribeLocalEvent(OnRefreshMoveSpeed);
- }
-
- public void SetModifier(EntityUid uid, float walkSpeedModifier, float sprintSpeedModifier, CarryingSlowdownComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return;
-
- component.WalkModifier = walkSpeedModifier;
- component.SprintModifier = sprintSpeedModifier;
- _movementSpeed.RefreshMovementSpeedModifiers(uid);
- }
- private void OnGetState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentGetState args)
- {
- args.State = new CarryingSlowdownComponentState(component.WalkModifier, component.SprintModifier);
- }
-
- private void OnHandleState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentHandleState args)
- {
- if (args.Current is CarryingSlowdownComponentState state)
- {
- component.WalkModifier = state.WalkModifier;
- component.SprintModifier = state.SprintModifier;
-
- _movementSpeed.RefreshMovementSpeedModifiers(uid);
- }
- }
- private void OnRefreshMoveSpeed(EntityUid uid, CarryingSlowdownComponent component, RefreshMovementSpeedModifiersEvent args)
- {
- args.ModifySpeed(component.WalkModifier, component.SprintModifier);
- }
- }
-}
diff --git a/Content.Shared/Stacks/StackComponent.cs b/Content.Shared/Stacks/StackComponent.cs
index 7137f8c0c22..b18f9b0d051 100644
--- a/Content.Shared/Stacks/StackComponent.cs
+++ b/Content.Shared/Stacks/StackComponent.cs
@@ -24,7 +24,7 @@ public sealed partial class StackComponent : Component
///
[ViewVariables(VVAccess.ReadOnly)]
[DataField("maxCountOverride")]
- public int? MaxCountOverride { get; set; }
+ public int? MaxCountOverride { get; set; }
///
/// Set to true to not reduce the count when used.
@@ -78,6 +78,14 @@ public sealed partial class StackComponent : Component
[DataField("layerStates")]
[ViewVariables(VVAccess.ReadWrite)]
public List LayerStates = new();
+
+ // Frontier: transforming Amount, MaxCount in speso stacks
+ ///
+ /// An optional function to adjust the layers used for a stack's appearance.
+ ///
+ [DataField]
+ public StackLayerFunction LayerFunction = StackLayerFunction.None;
+ // End Frontier
}
[Serializable, NetSerializable]
diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs
index 7a8961485d6..5cc24992d31 100644
--- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs
+++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs
@@ -1,5 +1,6 @@
using Content.Server.Storage.Components;
using Content.Shared.Inventory;
+using Content.Shared.Item.ItemToggle; // DeltaV
using Content.Shared.Whitelist;
using Robust.Shared.Map;
using Robust.Shared.Physics.Components;
@@ -15,6 +16,7 @@ public sealed class MagnetPickupSystem : EntitySystem
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
+ [Dependency] private readonly ItemToggleSystem _toggle = default!; // DeltaV
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
@@ -49,11 +51,18 @@ public override void Update(float frameTime)
comp.NextScan += ScanDelay;
- if (!_inventory.TryGetContainingSlot((uid, xform, meta), out var slotDef))
+ // Begin DeltaV Addition: Make ore bags use ItemToggle
+ if (!_toggle.IsActivated(uid))
continue;
+ // End DeltaV Addition
- if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0)
- continue;
+ // Begin DeltaV Removals: Allow ore bags to work inhand
+ //if (!_inventory.TryGetContainingSlot((uid, xform, meta), out var slotDef))
+ // continue;
+
+ //if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0)
+ // continue;
+ // End DeltaV Removals
// No space
if (!_storage.HasSpace((uid, storage)))
diff --git a/Content.Shared/Weather/SharedWeatherSystem.cs b/Content.Shared/Weather/SharedWeatherSystem.cs
index acd43055388..95c089fa10e 100644
--- a/Content.Shared/Weather/SharedWeatherSystem.cs
+++ b/Content.Shared/Weather/SharedWeatherSystem.cs
@@ -36,6 +36,7 @@ private void OnWeatherUnpaused(EntityUid uid, WeatherComponent component, ref En
if (weather.EndTime != null)
weather.EndTime = weather.EndTime.Value + args.PausedTime;
}
+ component.NextUpdate += args.PausedTime; // DeltaV
}
public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef)
diff --git a/Content.Shared/Weather/WeatherComponent.cs b/Content.Shared/Weather/WeatherComponent.cs
index eaf901fb424..e16fe978fa6 100644
--- a/Content.Shared/Weather/WeatherComponent.cs
+++ b/Content.Shared/Weather/WeatherComponent.cs
@@ -14,6 +14,18 @@ public sealed partial class WeatherComponent : Component
[DataField]
public Dictionary, WeatherData> Weather = new();
+ ///
+ /// DeltaV: How long to wait between updating weather effects.
+ ///
+ [DataField]
+ public TimeSpan UpdateDelay = TimeSpan.FromSeconds(1);
+
+ ///
+ /// DeltaV: When to next update weather effects (damage).
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate = TimeSpan.Zero;
+
public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15);
public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15);
}
diff --git a/Content.Shared/Weather/WeatherPrototype.cs b/Content.Shared/Weather/WeatherPrototype.cs
index 3803c37d4ce..af585d8d7c4 100644
--- a/Content.Shared/Weather/WeatherPrototype.cs
+++ b/Content.Shared/Weather/WeatherPrototype.cs
@@ -1,3 +1,5 @@
+using Content.Shared.Damage;
+using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -20,4 +22,17 @@ public sealed partial class WeatherPrototype : IPrototype
///
[ViewVariables(VVAccess.ReadWrite), DataField("sound")]
public SoundSpecifier? Sound;
+
+ ///
+ /// DeltaV: Damage you can take from being in this weather.
+ /// Only applies when weather has fully set in.
+ ///
+ [DataField]
+ public DamageSpecifier? Damage;
+
+ ///
+ /// DeltaV: Don't damage entities that match this blacklist.
+ ///
+ [DataField]
+ public EntityWhitelist? DamageBlacklist;
}
diff --git a/Content.Shared/_NF/Stacks/Components/StackLayerThresholdComponent.cs b/Content.Shared/_NF/Stacks/Components/StackLayerThresholdComponent.cs
new file mode 100644
index 00000000000..98d3bb0e61f
--- /dev/null
+++ b/Content.Shared/_NF/Stacks/Components/StackLayerThresholdComponent.cs
@@ -0,0 +1,13 @@
+namespace Content.Shared.Stacks.Components;
+
+[RegisterComponent]
+public sealed partial class StackLayerThresholdComponent : Component
+{
+ ///
+ /// A list of thresholds to check against the number of things in the stack.
+ /// Each exceeded threshold will cause the next layer to be displayed.
+ /// Should be sorted in ascending order.
+ ///
+ [DataField(required: true)]
+ public List Thresholds = new List();
+}
diff --git a/Content.Shared/_NF/Stacks/StackLayerFunction.cs b/Content.Shared/_NF/Stacks/StackLayerFunction.cs
new file mode 100644
index 00000000000..c655f3f76c2
--- /dev/null
+++ b/Content.Shared/_NF/Stacks/StackLayerFunction.cs
@@ -0,0 +1,7 @@
+namespace Content.Shared.Stacks;
+
+public enum StackLayerFunction
+{
+ None,
+ Threshold
+}
diff --git a/Resources/Changelog/DeltaVChangelog.yml b/Resources/Changelog/DeltaVChangelog.yml
index 80c31975690..db32d37ed23 100644
--- a/Resources/Changelog/DeltaVChangelog.yml
+++ b/Resources/Changelog/DeltaVChangelog.yml
@@ -1,62 +1,4 @@
Entries:
-- author: DangerRevolution
- changes:
- - message: Moths can now eat skirts
- type: Fix
- id: 283
- time: '2024-03-17T17:45:11.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/976
-- author: UnicornOnLSD
- changes:
- - message: pebble minor patch 3
- type: Tweak
- id: 284
- time: '2024-03-18T18:35:38.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/977
-- author: DangerRevolution and Noctis
- changes:
- - message: Added lunchboxes to the chefvend and dinnerwarevend for the Chef to fill
- or hand out pre-made!
- type: Add
- id: 285
- time: '2024-03-19T11:39:39.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/980
-- author: rosieposieeee
- changes:
- - message: Added Corpsman-only access and airlocks.
- type: Add
- id: 286
- time: '2024-03-19T12:13:13.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/892
-- author: rosieposieeee
- changes:
- - message: Submarine has received another batch of design tweaks and revisions.
- The deepest recesses of the station are shifting...
- type: Tweak
- id: 287
- time: '2024-03-19T23:50:03.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/809
-- author: Rose
- changes:
- - message: Added Submarine Station back to the map pool (for real this time)
- type: Add
- id: 288
- time: '2024-03-20T22:49:59.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/984
-- author: rosieposieeee
- changes:
- - message: More fixes for Submarine! Thank you everyone for your feedback.
- type: Fix
- id: 289
- time: '2024-03-21T06:10:41.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/987
-- author: DangerRevolution
- changes:
- - message: Added lunchboxes to various staff lockers
- type: Add
- id: 290
- time: '2024-03-21T18:46:44.0000000+00:00'
- url: https://github.com/DeltaV-Station/Delta-v/pull/989
- author: Velcroboy
changes:
- message: Reverted AME buff. AME will require more than 1 core again to function
@@ -3818,3 +3760,63 @@
id: 782
time: '2024-12-17T12:49:40.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/2378
+- author: deltanedas
+ changes:
+ - message: Plasma and diamonds are more abundant on lavaland.
+ type: Tweak
+ id: 783
+ time: '2024-12-17T18:17:41.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2441
+- author: BlitzTheSquishy & Yuukitten
+ changes:
+ - message: Brand new drink from the people that brought you Dr. Gibb, Dr Gibb Blood-Red!
+ In a Dr. Gibb vendor near you!
+ type: Add
+ id: 784
+ time: '2024-12-18T01:12:44.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2250
+- author: Stop-Signs
+ changes:
+ - message: A new loadout is available in the fugitives stash
+ type: Add
+ id: 785
+ time: '2024-12-18T01:26:19.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2374
+- author: deltanedas
+ changes:
+ - message: Ash storms can now happen on lavaland, run for shelter if you get caught
+ in one!
+ type: Add
+ id: 786
+ time: '2024-12-18T01:27:23.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2445
+- author: paige404
+ changes:
+ - message: Rodentia with bat snouts now look correct no matter their facing.
+ type: Fix
+ id: 787
+ time: '2024-12-18T07:03:37.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2470
+- author: deltanedas
+ changes:
+ - message: Fixed not being able to throw felenids...
+ type: Fix
+ id: 788
+ time: '2024-12-18T11:22:19.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2466
+- author: Hell_Cat, deltanedas
+ changes:
+ - message: Ore bag magnet can now be turned on without being worn as a belt. Mining
+ borgs rejoice!
+ type: Tweak
+ id: 789
+ time: '2024-12-18T11:33:37.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2467
+- author: BarryNorfolk
+ changes:
+ - message: Added a history tab to the bounty computer, showing all past completed
+ and skipped orders (and who skipped them).
+ type: Add
+ id: 790
+ time: '2024-12-18T13:36:06.0000000+00:00'
+ url: https://github.com/DeltaV-Station/Delta-v/pull/2473
diff --git a/Resources/Locale/en-US/cargo/cargo-bounty-console.ftl b/Resources/Locale/en-US/cargo/cargo-bounty-console.ftl
index 4314cbf4496..4d849c5bdab 100644
--- a/Resources/Locale/en-US/cargo/cargo-bounty-console.ftl
+++ b/Resources/Locale/en-US/cargo/cargo-bounty-console.ftl
@@ -18,3 +18,10 @@ bounty-console-flavor-right = v1.4
bounty-manifest-header = [font size=14][bold]Official cargo bounty manifest[/bold] (ID#{$id})[/font]
bounty-manifest-list-start = Item manifest:
+
+bounty-console-tab-available-label = Available
+bounty-console-tab-history-label = History
+bounty-console-history-notice-completed-label = {$time} - Completed
+bounty-console-history-notice-skipped-label = {$time} - Skipped by {$id}
+bounty-console-history-completed-label = [color=limegreen]Completed[/color]
+bounty-console-history-skipped-label = [color=red]Skipped[/color]
diff --git a/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl
index 6c93c98ed3c..51b345aa339 100644
--- a/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl
+++ b/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl
@@ -29,6 +29,7 @@ flavor-complex-blellow = like an impossible color
flavor-complex-candy-strawberry = like strawberries
flavor-complex-candy-bubblegum = like bubble gum
flavor-complex-double-ice-cream = like ice cream, twice
+flavor-complex-drgibbbloodred = like severe malpractice
candy-flavor-profile = This one is supposed to taste {$flavor}.
candy-flavor-profile-multiple = This one is supposed to taste {$flavors} and {$lastFlavor}.
diff --git a/Resources/Locale/en-US/deltav/fugitive/sets.ftl b/Resources/Locale/en-US/deltav/fugitive/sets.ftl
index bc1361dc352..e353fd38de7 100644
--- a/Resources/Locale/en-US/deltav/fugitive/sets.ftl
+++ b/Resources/Locale/en-US/deltav/fugitive/sets.ftl
@@ -22,3 +22,8 @@ fugitive-set-infiltrator-name = infiltrator's kit
fugitive-set-infiltrator-description =
Use an Agent ID to steal access from others and go anywhere.
Your freedom implanter can be used as a plan B if all else fails.
+
+fugitive-set-disruptor-name = disruptor's kit
+fugitive-set-disruptor-description =
+ Hack the stations various systems and use them to your advantage.
+ Comes with a cryptographic sequencer and camera bug.
diff --git a/Resources/Locale/en-US/deltav/reagents/meta/consumable/drink/soda.ftl b/Resources/Locale/en-US/deltav/reagents/meta/consumable/drink/soda.ftl
new file mode 100644
index 00000000000..156f8aaa73a
--- /dev/null
+++ b/Resources/Locale/en-US/deltav/reagents/meta/consumable/drink/soda.ftl
@@ -0,0 +1,2 @@
+reagent-name-dr-gibb-blood-red = Dr. Gibb Blood Red
+reagent-desc-dr-gibb-blood-red = A drink to quench YOUR blood thirst.
diff --git a/Resources/Locale/en-US/deltav/weather/ashstorm.ftl b/Resources/Locale/en-US/deltav/weather/ashstorm.ftl
new file mode 100644
index 00000000000..49778522502
--- /dev/null
+++ b/Resources/Locale/en-US/deltav/weather/ashstorm.ftl
@@ -0,0 +1,3 @@
+ash-storm-telegraph = [color=red][bold]An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.[/bold][/color]
+ash-storm-alert = [color=red][bolditalic]Smoldering clouds of scorching ash billow down around you! Get inside![/bolditalic][/color]
+ash-storm-clearing = [color=red]The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.[/color]
diff --git a/Resources/Migrations/deltaMigrations.yml b/Resources/Migrations/deltaMigrations.yml
index 4edcc461900..18a7472447d 100644
--- a/Resources/Migrations/deltaMigrations.yml
+++ b/Resources/Migrations/deltaMigrations.yml
@@ -122,4 +122,9 @@ BoxMagazinePistolSubMachineGunRubber: MagazineBoxPistolRubber
VendingMachineAutomatrobe: null
# 2024-11-08
-SuitStorageSecDeltaV: SuitStorageSec # Stray revert 24-11-2024
+SuitStorageSec: SuitStorageSecDeltaV
+
+# 2024-12-17
+LightBulbMaintenanceRed: DimLightBulb
+PoweredSmallLightMaintenanceRed: PoweredDimSmallLight
+AlwaysPoweredSmallLightMaintenanceRed: PoweredDimSmallLight
diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml
index 98513a48a4a..171eb196f77 100644
--- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml
+++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/gib.yml
@@ -2,6 +2,7 @@
id: DrGibbInventory
startingInventory:
DrinkDrGibbCan: 4
+ DrinkDrGibbBloodRedCan: 2 # Delta-V
DrinkGrapeCan: 2
DrinkRootBeerCan: 2
DrinkIcedTeaCan: 2
diff --git a/Resources/Prototypes/DeltaV/Body/Organs/ashwalker.yml b/Resources/Prototypes/DeltaV/Body/Organs/ashwalker.yml
new file mode 100644
index 00000000000..c0388ffd0ee
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Body/Organs/ashwalker.yml
@@ -0,0 +1,9 @@
+- type: entity
+ parent: OrganAnimalLungs
+ id: OrganAshWalkerLungs
+ name: ashwalker lungs
+ description: These lungs are adapted from isolation in lavaland, capable of withstanding the low oxygen content and ash storms.
+ components:
+ - type: Lung
+ saturationLoss: 0.5
+ # TODO SHITMED: add AshStormImmune when transplanted
diff --git a/Resources/Prototypes/DeltaV/Body/Prototypes/ashwalker.yml b/Resources/Prototypes/DeltaV/Body/Prototypes/ashwalker.yml
new file mode 100644
index 00000000000..59c55e89f4e
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Body/Prototypes/ashwalker.yml
@@ -0,0 +1,50 @@
+# TODO: will need updating with shitmed
+- type: body
+ name: ashwalker
+ id: AshWalker
+ root: torso
+ slots:
+ head:
+ part: HeadReptilian
+ connections:
+ - torso
+ organs:
+ brain: OrganHumanBrain
+ eyes: OrganHumanEyes
+ torso:
+ part: TorsoReptilian
+ organs:
+ heart: OrganAnimalHeart
+ lungs: OrganAshWalkerLungs
+ stomach: OrganReptilianStomach
+ liver: OrganAnimalLiver
+ kidneys: OrganHumanKidneys
+ connections:
+ - right arm
+ - left arm
+ - right leg
+ - left leg
+ right arm:
+ part: RightArmReptilian
+ connections:
+ - right hand
+ left arm:
+ part: LeftArmReptilian
+ connections:
+ - left hand
+ right hand:
+ part: RightHandReptilian
+ left hand:
+ part: LeftHandReptilian
+ right leg:
+ part: RightLegReptilian
+ connections:
+ - right foot
+ left leg:
+ part: LeftLegReptilian
+ connections:
+ - left foot
+ right foot:
+ part: RightFootReptilian
+ left foot:
+ part: LeftFootReptilian
diff --git a/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml b/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml
index 7c4e08cfcf0..325eb6e078a 100644
--- a/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml
+++ b/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml
@@ -87,4 +87,3 @@
cost: 500
category: Food
group: market
-
\ No newline at end of file
diff --git a/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml b/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml
index 4dc38e493c2..b2e2328693a 100644
--- a/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml
+++ b/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml
@@ -59,3 +59,14 @@
- AgentIDCard
- FreedomImplanter
- ClothingMaskGasSyndicate
+
+- type: thiefBackpackSet
+ id: FugitiveDisruptor
+ name: fugitive-set-disruptor-name
+ description: fugitive-set-disruptor-description
+ sprite:
+ sprite: Objects/Tools/emag.rsi
+ state: icon
+ content:
+ - Emag
+ - CameraBug
diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/ashwalker.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/ashwalker.yml
new file mode 100644
index 00000000000..4a397caaa37
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/ashwalker.yml
@@ -0,0 +1,11 @@
+- type: entity
+ save: false
+ parent: MobReptilian
+ id: MobAshWalker
+ name: Urist McAsh
+ suffix: ""
+ components:
+ - type: Body
+ prototype: AshWalker
+ - type: AshStormImmune
+ # TODO: shitmed stuff so you can steal ashwalker lungs
diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Drinks/drinks_cans.yml
new file mode 100644
index 00000000000..a7e16b8c33c
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Drinks/drinks_cans.yml
@@ -0,0 +1,15 @@
+- type: entity
+ parent: DrinkCanBaseFull
+ id: DrinkDrGibbBloodRedCan
+ name: Dr. Gibb Blood Red can
+ description: A drink to quench YOUR bloodthirst.
+ components:
+ - type: SolutionContainerManager
+ solutions:
+ drink:
+ maxVol: 30
+ reagents:
+ - ReagentId: DrGibbBloodRed
+ Quantity: 30
+ - type: Sprite
+ sprite: DeltaV/Objects/Consumable/Drinks/drgibbbloodred.rsi
diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml
index 305a9a801d3..b23f4635421 100644
--- a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml
@@ -13,3 +13,4 @@
- FugitiveGhost
- FugitiveLeverage
- FugitiveInfiltrator
+ - FugitiveDisruptor
diff --git a/Resources/Prototypes/DeltaV/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/DeltaV/Entities/Structures/Storage/Closets/Lockers/lockers.yml
index d442b0cf3f5..bb388fe66d4 100644
--- a/Resources/Prototypes/DeltaV/Entities/Structures/Storage/Closets/Lockers/lockers.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Structures/Storage/Closets/Lockers/lockers.yml
@@ -1,19 +1,30 @@
+- type: entity
+ parent: LockerBase
+ id: LockerBaseDeltaV
+ components:
+ - type: Sprite
+ sprite: DeltaV/Structures/Storage/closet.rsi
+
+- type: entity
+ parent: [ LockerBaseDeltaV, LockerBaseSecure ]
+ id: LockerBaseSecureDeltaV
+
- type: entity
id: LockerChiefJustice
- parent: LockerBaseSecure
+ parent: LockerBaseSecureDeltaV
name: chief justice's locker
components:
- type: Appearance
- type: EntityStorageVisuals
- stateBaseClosed: cj
+ stateBaseClosed: cj
stateDoorOpen: cj_open
stateDoorClosed: cj_door
- type: AccessReader
access: [["ChiefJustice"]]
-
+
- type: entity
id: LockerClerk
- parent: LockerBaseSecure
+ parent: LockerBaseSecureDeltaV
name: clerk's locker
components:
- type: Appearance
@@ -25,12 +36,10 @@
access: [["Clerk"]]
- type: entity
- parent: LockerBaseSecure
+ parent: LockerBaseSecureDeltaV
id: LockerPsychologist
name: psychologist's locker
components:
- - type: Sprite
- sprite: DeltaV/Structures/Storage/closet.rsi
- type: EntityStorageVisuals
stateBaseClosed: psych
stateDoorOpen: psych_open
diff --git a/Resources/Prototypes/DeltaV/Flavors/flavors.yml b/Resources/Prototypes/DeltaV/Flavors/flavors.yml
index 8d0d01efa0b..9ed9b3003f4 100644
--- a/Resources/Prototypes/DeltaV/Flavors/flavors.yml
+++ b/Resources/Prototypes/DeltaV/Flavors/flavors.yml
@@ -121,6 +121,11 @@
flavorType: Complex
description: flavor-complex-blellow
+- type: flavor
+ id: drgibbbloodred
+ flavorType: Complex
+ description: flavor-complex-drgibbbloodred
+
# this is prefixed with "candy" to avoid clashes with potential future strawberries upstream
- type: flavor
id: candystrawberry
diff --git a/Resources/Prototypes/DeltaV/Procedural/biome_ore_templates.yml b/Resources/Prototypes/DeltaV/Procedural/biome_ore_templates.yml
new file mode 100644
index 00000000000..b264f014844
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Procedural/biome_ore_templates.yml
@@ -0,0 +1,46 @@
+# these have higher group size so more ores spawns
+
+# Plasma
+- type: biomeMarkerLayer
+ id: OrePlasma3
+ entityMask:
+ AsteroidRock: AsteroidRockPlasma
+ WallRock: WallRockPlasma
+ WallRockBasalt: WallRockBasaltPlasma
+ WallRockChromite: WallRockChromitePlasma
+ WallRockSand: WallRockSandPlasma
+ WallRockSnow: WallRockSnowPlasma
+ maxCount: 12
+ minGroupSize: 12
+ maxGroupSize: 24
+ radius: 4
+
+# Uranium
+- type: biomeMarkerLayer
+ id: OreUranium2
+ entityMask:
+ AsteroidRock: AsteroidRockUranium
+ WallRock: WallRockUranium
+ WallRockBasalt: WallRockBasaltUranium
+ WallRockChromite: WallRockChromiteUranium
+ WallRockSand: WallRockSandUranium
+ WallRockSnow: WallRockSnowUranium
+ maxCount: 15
+ minGroupSize: 8
+ maxGroupSize: 16
+ radius: 4
+
+# Diamond
+- type: biomeMarkerLayer
+ id: OreDiamond2
+ entityMask:
+ AsteroidRock: AsteroidRockDiamond
+ WallRock: WallRockDiamond
+ WallRockBasalt: WallRockBasaltDiamond
+ WallRockChromite: WallRockChromiteDiamond
+ WallRockSand: WallRockSandDiamond
+ WallRockSnow: WallRockSnowDiamond
+ maxCount: 6
+ minGroupSize: 2
+ maxGroupSize: 4
+ radius: 4
diff --git a/Resources/Prototypes/DeltaV/Reagents/Consumable/Drink/soda.yml b/Resources/Prototypes/DeltaV/Reagents/Consumable/Drink/soda.yml
index f42d5e62356..b22a929f515 100644
--- a/Resources/Prototypes/DeltaV/Reagents/Consumable/Drink/soda.yml
+++ b/Resources/Prototypes/DeltaV/Reagents/Consumable/Drink/soda.yml
@@ -1,3 +1,26 @@
+- type: reagent
+ id: DrGibbBloodRed
+ name: reagent-name-dr-gibb-blood-red
+ parent: BaseSoda
+ desc: reagent-desc-dr-gibb-blood-red
+ physicalDesc: reagent-physical-desc-fizzy
+ flavor: drgibbbloodred
+ color: "#570303"
+ metabolisms:
+ Drink:
+ effects:
+ - !type:SatiateThirst
+ factor: 2.0
+ conditions:
+ - !type:OrganType
+ type: Human
+ shouldHave: false
+ Food:
+ effects:
+ - !type:AdjustReagent
+ reagent: UncookedAnimalProteins
+ amount: 0.05
+
- type: reagent
id: Tarragon
name: reagent-name-tarragon
diff --git a/Resources/Prototypes/DeltaV/Recipes/Reactions/drinks.yml b/Resources/Prototypes/DeltaV/Recipes/Reactions/drinks.yml
index 60bb26eaa2f..e64c347e445 100644
--- a/Resources/Prototypes/DeltaV/Recipes/Reactions/drinks.yml
+++ b/Resources/Prototypes/DeltaV/Recipes/Reactions/drinks.yml
@@ -100,3 +100,15 @@
amount: 1
products:
DoubleIceCream: 3
+
+- type: reaction
+ id: DrGibbBloodRed
+ reactants:
+ DrGibb:
+ amount: 3
+ Blood:
+ amount: 2
+ Sugar:
+ amount: 1
+ products:
+ DrGibbBloodRed: 6
diff --git a/Resources/Prototypes/DeltaV/planets.yml b/Resources/Prototypes/DeltaV/planets.yml
index 9491c621a89..7229f5a7917 100644
--- a/Resources/Prototypes/DeltaV/planets.yml
+++ b/Resources/Prototypes/DeltaV/planets.yml
@@ -8,6 +8,26 @@
whitelist:
components:
- MiningShuttle
+ - type: WeatherScheduler # Regular ash storms
+ stages:
+ - duration: # 5-10 minutes of calm
+ min: 300
+ max: 600
+ - weather: AshfallLight # ash starts to fall, 30 second warning
+ message: ash-storm-telegraph
+ duration:
+ min: 30
+ max: 30
+ - weather: Ashfall # 1-2 minutes of damaging storm
+ message: ash-storm-alert
+ duration:
+ min: 60
+ max: 120
+ - weather: AshfallLight # ash clears away for 30 seconds
+ message: ash-storm-clearing
+ duration:
+ min: 30
+ max: 30
atmosphere:
volume: 2500
temperature: 353.15 # 80C
@@ -20,9 +40,9 @@
- OreCoal
- OreGold
- OreSilver
- - OrePlasma
+ - OrePlasma3
- OreUranium
- - OreDiamond
+ - OreDiamond2
- OreArtifactFragment
- type: planet
@@ -47,7 +67,7 @@
- OreCoal
- OreGold
- OreSilver
- - OrePlasma
- - OreUranium
+ - OrePlasma3
+ - OreUranium2
- OreDiamond
- OreArtifactFragment
diff --git a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
index 57dfb400984..5321a20fa72 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml
@@ -25,9 +25,17 @@
- cash_100
- cash_500
- cash_1000
- - cash_1000000
+ - cash_5000 # Frontier: larger denominations
+ - cash_10000 # Frontier: larger denominations
+ - cash_25000 # Frontier: larger denominations
+ - cash_50000 # Frontier: larger denominations
+ - cash_100000 # Frontier: larger denominations
+ - cash_250000 # Frontier: larger denominations (cash_1000000