Merge remote-tracking branch 'refs/remotes/upstream/master' into ed-17-05-2024-upstream

This commit is contained in:
Ed
2024-05-17 12:57:04 +03:00
58 changed files with 3724 additions and 4985 deletions

View File

@@ -1,7 +1,7 @@
<Control xmlns="https://spacestation14.io">
<Control xmlns="https://spacestation14.io" HorizontalExpand="True">
<BoxContainer Name="MainContainer"
Orientation="Horizontal"
SetWidth="160">
HorizontalExpand="True">
<PanelContainer Name="ColorPanel"
VerticalExpand="True"
SetWidth="7"
@@ -16,8 +16,7 @@
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
<Label Name="ReagentNameLabel"
StyleClasses="LabelSubText" />
<Label Name="ReagentNameLabel" />
<Label Name="FillLabel"
StyleClasses="LabelSubText"
Margin="0 -5 0 0" />

View File

@@ -20,10 +20,12 @@ public sealed partial class ReagentCardControl : Control
StorageSlotId = item.StorageSlotId;
ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = item.ReagentColor };
ReagentNameLabel.Text = item.ReagentLabel;
ReagentNameLabel.FontColorOverride = Color.White;
FillLabel.Text = item.StoredAmount;
FillLabel.Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", item.Quantity));;
EjectButtonIcon.Text = Loc.GetString("reagent-dispenser-window-eject-container-button");
if (item.Quantity == 0.0)
MainButton.Disabled = true;
MainButton.OnPressed += args => OnPressed?.Invoke(StorageSlotId);
EjectButton.OnPressed += args => OnEjectButtonPressed?.Invoke(StorageSlotId);
}

View File

@@ -4,7 +4,8 @@
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:ui="clr-namespace:Content.Client.Chemistry.UI"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
MinSize="680 460">
MinSize="600 300"
SetSize="800 500">
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical" MinWidth="170">
<Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}" HorizontalAlignment="Center" />

View File

@@ -121,28 +121,4 @@ namespace Content.Client.Chemistry.UI
}
}
}
public sealed class DispenseReagentButton : Button
{
public string ReagentId { get; }
public DispenseReagentButton(string reagentId, string text, string amount)
{
AddStyleClass("OpenRight");
ReagentId = reagentId;
Text = text + " " + amount;
}
}
public sealed class EjectJugButton : Button
{
public string ReagentId { get; }
public EjectJugButton(string reagentId)
{
AddStyleClass("OpenLeft");
ReagentId = reagentId;
Text = "⏏";
}
}
}

View File

@@ -2,6 +2,4 @@ using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem
{
}
public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleSystem;

View File

@@ -2,6 +2,4 @@ using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem
{
}
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem;

View File

@@ -0,0 +1,5 @@
using Content.Shared.CriminalRecords.Systems;
namespace Content.Client.CriminalRecords.Systems;
public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem;

View File

@@ -153,7 +153,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
}
}
else if (state == VisualState.OverlayCharging)
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, new RSI.StateId("disposal-charging"));
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
else
_animationSystem.Stop(uid, AnimationKey);

View File

@@ -7,7 +7,7 @@
<!-- Left side -->
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
<!-- Middle container -->
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" SeparationOverride="10" HorizontalExpand="True">
<!-- Name box-->
<BoxContainer Orientation="Vertical">
<ui:HighlightedContainer>
@@ -31,13 +31,13 @@
</ui:HighlightedContainer>
</BoxContainer>
<!-- Import/Export -->
<BoxContainer Orientation="Vertical">
<ui:HighlightedContainer>
<BoxContainer Orientation="Horizontal" SeparationOverride="5">
<BoxContainer Orientation="Vertical" MinSize="60 0" HorizontalExpand="True" HorizontalAlignment="Right">
<ui:HighlightedContainer Name="ProfileHighlight">
<BoxContainer Orientation="Vertical">
<Button Name="SaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}"/>
<Button Name="ResetButton" Disabled="True" Text="{Loc 'humanoid-profile-editor-reset-button'}"/>
<Button Name="ImportButton" Text="{Loc 'humanoid-profile-editor-import-button'}"/>
<Button Name="ExportButton" Text="{Loc 'humanoid-profile-editor-export-button'}"/>
<Button Name="SaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}" />
<Button Name="ResetButton" Disabled="True" Text="{Loc 'humanoid-profile-editor-reset-button'}"/>
</BoxContainer>
</ui:HighlightedContainer>
</BoxContainer>

View File

@@ -68,7 +68,7 @@ public sealed partial class RCDMenu : RadialMenu
tooltip = Loc.GetString(entProto.Name);
}
tooltip = char.ToUpper(tooltip[0]) + tooltip.Remove(0, 1);
tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1));
var button = new RCDMenuButton()
{
@@ -119,6 +119,12 @@ public sealed partial class RCDMenu : RadialMenu
SendRCDSystemMessageAction += bui.SendRCDSystemMessage;
}
private static string OopsConcat(string a, string b)
{
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
return a + b;
}
private void AddRCDMenuButtonOnClickActions(Control control)
{
var radialContainer = control as RadialContainer;

View File

@@ -20,10 +20,14 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_window = new OfferingWindow();
_window.Title = Loc.GetString("salvage-magnet-window-title");
_window.OnClose += Close;
_window.OpenCenteredLeft();
if (_window is null)
{
_window = new OfferingWindow();
_window.Title = Loc.GetString("salvage-magnet-window-title");
_window.OnClose += Close;
_window.OpenCenteredLeft();
}
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -108,4 +112,15 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
_window.AddOption(option);
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window?.Close();
_window?.Dispose();
}
}
}

View File

@@ -254,7 +254,7 @@ public sealed class StorageContainer : BaseWindow
//todo. at some point, we may want to only rebuild the pieces that have actually received new data.
_pieceGrid.Children.Clear();
_pieceGrid.RemoveAllChildren();
_pieceGrid.Rows = boundingGrid.Height + 1;
_pieceGrid.Columns = boundingGrid.Width + 1;
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
@@ -275,18 +275,29 @@ public sealed class StorageContainer : BaseWindow
if (_entity.TryGetComponent<ItemComponent>(itemEnt, out var itemEntComponent))
{
var gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity)
ItemGridPiece gridPiece;
if (_storageController.CurrentlyDragging?.Entity is { } dragging
&& dragging == itemEnt)
{
MinSize = size,
Marked = Array.IndexOf(containedEntities, itemEnt) switch
_storageController.CurrentlyDragging.Orphan();
gridPiece = _storageController.CurrentlyDragging;
}
else
{
gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity)
{
0 => ItemGridPieceMarks.First,
1 => ItemGridPieceMarks.Second,
_ => null,
}
};
gridPiece.OnPiecePressed += OnPiecePressed;
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
MinSize = size,
Marked = Array.IndexOf(containedEntities, itemEnt) switch
{
0 => ItemGridPieceMarks.First,
1 => ItemGridPieceMarks.Second,
_ => null,
}
};
gridPiece.OnPiecePressed += OnPiecePressed;
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
}
control.AddChild(gridPiece);
}

View File

@@ -314,15 +314,16 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
_entity.GetNetEntity(storageEnt)));
}
_menuDragHelper.EndDrag();
_container?.BuildItemPieces();
}
else //if we just clicked, then take it out of the bag.
{
_menuDragHelper.EndDrag();
_entity.RaisePredictiveEvent(new StorageInteractWithItemEvent(
_entity.GetNetEntity(control.Entity),
_entity.GetNetEntity(storageEnt)));
}
_menuDragHelper.EndDrag();
args.Handle();
}

View File

@@ -16,8 +16,6 @@ using Robust.Client.State;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Weapons.Melee;
@@ -66,7 +64,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
if (!TryGetWeapon(entity, out var weaponUid, out var weapon))
return;
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity))
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity, weapon: (weaponUid, weapon)))
{
weapon.Attacking = false;
return;

View File

@@ -27,6 +27,9 @@ public sealed class IdCardSystem : SharedIdCardSystem
private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
{
if (!component.CanMicrowave)
return;
if (TryComp<AccessComponent>(uid, out var access))
{
float randomPick = _random.NextFloat();

View File

@@ -205,47 +205,30 @@ namespace Content.Server.Atmos.EntitySystems
if (!flammable.OnFire && !otherFlammable.OnFire)
return; // Neither are on fire
if (flammable.OnFire && otherFlammable.OnFire)
// Both are on fire -> equalize fire stacks.
// Weight each thing's firestacks by its mass
var mass1 = 1f;
var mass2 = 1f;
if (_physicsQuery.TryComp(uid, out var physics) && _physicsQuery.TryComp(otherUid, out var otherPhys))
{
// Both are on fire -> equalize fire stacks.
// Weight each thing's firestacks by its mass
var mass1 = 1f;
var mass2 = 1f;
if (_physicsQuery.TryComp(uid, out var physics) && _physicsQuery.TryComp(otherUid, out var otherPhys))
{
mass1 = physics.Mass;
mass2 = otherPhys.Mass;
}
var total = mass1 + mass2;
var avg = (flammable.FireStacks * mass1 + otherFlammable.FireStacks * mass2) / total;
flammable.FireStacks = flammable.CanExtinguish ? avg : Math.Max(flammable.FireStacks, avg);
otherFlammable.FireStacks = otherFlammable.CanExtinguish ? avg : Math.Max(otherFlammable.FireStacks, avg);
UpdateAppearance(uid, flammable);
UpdateAppearance(otherUid, otherFlammable);
return;
mass1 = physics.Mass;
mass2 = otherPhys.Mass;
}
// Only one is on fire -> attempt to spread the fire.
var (srcUid, srcFlammable, destUid, destFlammable) = flammable.OnFire
? (uid, flammable, otherUid, otherFlammable)
: (otherUid, otherFlammable, uid, flammable);
// when the thing on fire is more massive than the other, the following happens:
// - the thing on fire loses a small number of firestacks
// - the other thing gains a large number of firestacks
// so a person on fire engulfs a mouse, but an engulfed mouse barely does anything to a person
var total = mass1 + mass2;
var avg = (flammable.FireStacks + otherFlammable.FireStacks) / total;
// if the thing on fire has less mass, spread less firestacks and vice versa
var ratio = 0.5f;
if (_physicsQuery.TryComp(srcUid, out var srcPhysics) && _physicsQuery.TryComp(destUid, out var destPhys))
{
ratio *= srcPhysics.Mass / destPhys.Mass;
}
var lost = srcFlammable.FireStacks * ratio;
destFlammable.FireStacks += lost;
Ignite(destUid, srcUid, destFlammable);
if (srcFlammable.CanExtinguish)
{
srcFlammable.FireStacks -= lost;
UpdateAppearance(srcUid, srcFlammable);
}
// swap the entity losing stacks depending on whichever has the most firestack kilos
var (src, dest) = flammable.FireStacks * mass1 > otherFlammable.FireStacks * mass2
? (-1f, 1f)
: (1f, -1f);
// bring each entity to the same firestack mass, firestacks being scaled by the other's mass
AdjustFireStacks(uid, src * avg * mass2, flammable, ignite: true);
AdjustFireStacks(otherUid, dest * avg * mass1, otherFlammable, ignite: true);
}
private void OnIsHot(EntityUid uid, FlammableComponent flammable, IsHotEvent args)
@@ -283,17 +266,30 @@ namespace Content.Server.Atmos.EntitySystems
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, flammable.OnFire, appearance);
}
public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableComponent? flammable = null)
public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableComponent? flammable = null, bool ignite = false)
{
if (!Resolve(uid, ref flammable))
return;
flammable.FireStacks = MathF.Min(MathF.Max(flammable.MinimumFireStacks, flammable.FireStacks + relativeFireStacks), flammable.MaximumFireStacks);
SetFireStacks(uid, flammable.FireStacks + relativeFireStacks, flammable, ignite);
}
if (flammable.OnFire && flammable.FireStacks <= 0)
public void SetFireStacks(EntityUid uid, float stacks, FlammableComponent? flammable = null, bool ignite = false)
{
if (!Resolve(uid, ref flammable))
return;
flammable.FireStacks = MathF.Min(MathF.Max(flammable.MinimumFireStacks, stacks), flammable.MaximumFireStacks);
if (flammable.FireStacks <= 0)
{
Extinguish(uid, flammable);
}
else
{
flammable.OnFire = ignite;
UpdateAppearance(uid, flammable);
}
}
public void Extinguish(EntityUid uid, FlammableComponent? flammable = null)

View File

@@ -1,15 +1,9 @@
using Content.Shared.Damage;
using Content.Shared.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Temperature.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Rejuvenate;
using Content.Shared.Damage;
using Robust.Server.Containers;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
@@ -22,83 +16,16 @@ public sealed class RottingSystem : SharedRottingSystem
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PerishableComponent, MapInitEvent>(OnPerishableMapInit);
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnPerishableExamined);
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
SubscribeLocalEvent<RottingComponent, BeingGibbedEvent>(OnGibbed);
SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<TemperatureComponent, IsRottingEvent>(OnTempIsRotting);
}
private void OnPerishableMapInit(EntityUid uid, PerishableComponent component, MapInitEvent args)
{
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
private void OnMobStateChanged(EntityUid uid, PerishableComponent component, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Dead && args.OldMobState != MobState.Dead)
return;
if (HasComp<RottingComponent>(uid))
return;
component.RotAccumulator = TimeSpan.Zero;
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
private void OnShutdown(EntityUid uid, RottingComponent component, ComponentShutdown args)
{
if (TryComp<PerishableComponent>(uid, out var perishable))
{
perishable.RotNextUpdate = TimeSpan.Zero;
}
}
private void OnRottingMobStateChanged(EntityUid uid, RottingComponent component, MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead)
return;
RemCompDeferred(uid, component);
}
public bool IsRotProgressing(EntityUid uid, PerishableComponent? perishable)
{
// things don't perish by default.
if (!Resolve(uid, ref perishable, false))
return false;
// only dead things or inanimate objects can rot
if (TryComp<MobStateComponent>(uid, out var mobState) && !_mobState.IsDead(uid, mobState))
return false;
if (_container.TryGetOuterContainer(uid, Transform(uid), out var container) &&
HasComp<AntiRottingContainerComponent>(container.Owner))
{
return false;
}
var ev = new IsRottingEvent();
RaiseLocalEvent(uid, ref ev);
return !ev.Handled;
}
public bool IsRotten(EntityUid uid, RottingComponent? rotting = null)
{
return Resolve(uid, ref rotting, false);
}
private void OnGibbed(EntityUid uid, RottingComponent component, BeingGibbedEvent args)
{
if (!TryComp<PhysicsComponent>(uid, out var physics))
@@ -112,36 +39,6 @@ public sealed class RottingSystem : SharedRottingSystem
tileMix?.AdjustMoles(Gas.Ammonia, molsToDump);
}
private void OnPerishableExamined(Entity<PerishableComponent> perishable, ref ExaminedEvent args)
{
int stage = PerishStage(perishable, MaxStages);
if (stage < 1 || stage > MaxStages)
{
// We dont push an examined string if it hasen't started "perishing" or it's already rotting
return;
}
var isMob = HasComp<MobStateComponent>(perishable);
var description = "perishable-" + stage + (!isMob ? "-nonmob" : string.Empty);
args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(perishable, EntityManager))));
}
/// <summary>
/// Return an integer from 0 to maxStage representing how close to rotting an entity is. Used to
/// generate examine messages for items that are starting to rot.
/// </summary>
public int PerishStage(Entity<PerishableComponent> perishable, int maxStages)
{
if (perishable.Comp.RotAfter.TotalSeconds == 0 || perishable.Comp.RotAccumulator.TotalSeconds == 0)
return 0;
return (int)(1 + maxStages * perishable.Comp.RotAccumulator.TotalSeconds / perishable.Comp.RotAfter.TotalSeconds);
}
private void OnRejuvenate(EntityUid uid, RottingComponent component, RejuvenateEvent args)
{
RemCompDeferred<RottingComponent>(uid);
}
private void OnTempIsRotting(EntityUid uid, TemperatureComponent component, ref IsRottingEvent args)
{
if (args.Handled)
@@ -149,29 +46,6 @@ public sealed class RottingSystem : SharedRottingSystem
args.Handled = component.CurrentTemperature < Atmospherics.T0C + 0.85f;
}
public void ReduceAccumulator(EntityUid uid, TimeSpan time)
{
if (!TryComp<PerishableComponent>(uid, out var perishable))
return;
if (!TryComp<RottingComponent>(uid, out var rotting))
{
perishable.RotAccumulator -= time;
return;
}
var total = (rotting.TotalRotTime + perishable.RotAccumulator) - time;
if (total < perishable.RotAfter)
{
RemCompDeferred(uid, rotting);
perishable.RotAccumulator = total;
}
else
rotting.TotalRotTime = total - perishable.RotAfter;
}
/// <summary>
/// Is anything speeding up the decay?
/// e.g. buried in a grave

View File

@@ -107,9 +107,8 @@ namespace Content.Server.Chemistry.EntitySystems
quantity = sol.Volume;
reagentColor = sol.GetColor(_prototypeManager);
}
var storedAmount = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity));
inventory.Add(new ReagentInventoryItem(storageSlotId, reagentLabel, storedAmount, reagentColor));
inventory.Add(new ReagentInventoryItem(storageSlotId, reagentLabel, quantity, reagentColor));
}
return inventory;

View File

@@ -27,7 +27,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly SharedIdCardSystem _idCard = default!;
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
[Dependency] private readonly StationRecordsSystem _records = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
@@ -80,7 +80,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
if (!CheckSelected(ent, msg.Actor, out var mob, out var key))
return;
if (!_stationRecords.TryGetRecord<CriminalRecord>(key.Value, out var record) || record.Status == msg.Status)
if (!_records.TryGetRecord<CriminalRecord>(key.Value, out var record) || record.Status == msg.Status)
return;
// validate the reason
@@ -106,7 +106,7 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
// will probably never fail given the checks above
_criminalRecords.TryChangeStatus(key.Value, msg.Status, msg.Reason);
var name = RecordName(key.Value);
var name = _records.RecordName(key.Value);
var officer = Loc.GetString("criminal-records-console-unknown-officer");
if (_idCard.TryFindIdCard(mob.Value, out var id) && id.Comp.FullName is { } fullName)
officer = fullName;
@@ -145,7 +145,6 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
ent.Comp.SecurityChannel, ent);
UpdateUserInterface(ent);
UpdateCriminalIdentity(name, msg.Status);
}
private void OnAddHistory(Entity<CriminalRecordsConsoleComponent> ent, ref CriminalRecordAddHistory msg)
@@ -189,15 +188,15 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
return;
}
var listing = _stationRecords.BuildListing((owningStation.Value, stationRecords), console.Filter);
var listing = _records.BuildListing((owningStation.Value, stationRecords), console.Filter);
var state = new CriminalRecordsConsoleState(listing, console.Filter);
if (console.ActiveKey is { } id)
{
// get records to display when a crewmember is selected
var key = new StationRecordKey(id, owningStation.Value);
_stationRecords.TryGetRecord(key, out state.StationRecord, stationRecords);
_stationRecords.TryGetRecord(key, out state.CriminalRecord, stationRecords);
_records.TryGetRecord(key, out state.StationRecord, stationRecords);
_records.TryGetRecord(key, out state.CriminalRecord, stationRecords);
state.SelectedKey = id;
}
@@ -232,17 +231,6 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
return true;
}
/// <summary>
/// Gets the name from a record, or empty string if this somehow fails.
/// </summary>
private string RecordName(StationRecordKey key)
{
if (!_stationRecords.TryGetRecord<GeneralStationRecord>(key, out var record))
return "";
return record.Name;
}
/// <summary>
/// Checks if the new identity's name has a criminal record attached to it, and gives the entity the icon that
/// belongs to the status if it does.
@@ -255,14 +243,14 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
// TODO use the entity's station? Not the station of the map that it happens to currently be on?
var station = _station.GetStationInMap(xform.MapID);
if (station != null && _stationRecords.GetRecordByName(station.Value, name) is { } id)
if (station != null && _records.GetRecordByName(station.Value, name) is { } id)
{
if (_stationRecords.TryGetRecord<CriminalRecord>(new StationRecordKey(id, station.Value),
if (_records.TryGetRecord<CriminalRecord>(new StationRecordKey(id, station.Value),
out var record))
{
if (record.Status != SecurityStatus.None)
{
SetCriminalIcon(name, record.Status, uid);
_criminalRecords.SetCriminalIcon(name, record.Status, uid);
return;
}
}

View File

@@ -6,6 +6,7 @@ using Content.Shared.CriminalRecords.Components;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Dataset;
using Content.Shared.Security;
using Content.Shared.StationRecords;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -14,6 +15,7 @@ namespace Content.Server.CriminalRecords.Systems;
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly CriminalRecordsSystem _criminalRecords = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StationSystem _station = default!;
@@ -38,8 +40,7 @@ public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSys
foreach (var (key, record) in _records.GetRecordsOfType<CriminalRecord>(station))
{
var reason = _random.Pick(reasons.Values);
record.Status = SecurityStatus.Wanted;
record.Reason = reason;
_criminalRecords.OverwriteStatus(new StationRecordKey(key, station), record, SecurityStatus.Wanted, reason);
// no radio message since spam
// no history since lazy and its easy to remove anyway
// main damage with this is existing arrest warrants are lost and to anger beepsky

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.StationRecords.Systems;
using Content.Shared.CriminalRecords;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Security;
using Content.Shared.StationRecords;
using Content.Server.GameTicking;
@@ -15,10 +16,10 @@ namespace Content.Server.CriminalRecords.Systems;
/// - See security officers' actions in Criminal Records in the radio
/// - See reasons for any action with no need to ask the officer personally
/// </summary>
public sealed class CriminalRecordsSystem : EntitySystem
public sealed class CriminalRecordsSystem : SharedCriminalRecordsSystem
{
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
[Dependency] private readonly StationRecordsSystem _records = default!;
public override void Initialize()
{
@@ -29,28 +30,40 @@ public sealed class CriminalRecordsSystem : EntitySystem
private void OnGeneralRecordCreated(AfterGeneralRecordCreatedEvent ev)
{
_stationRecords.AddRecordEntry(ev.Key, new CriminalRecord());
_stationRecords.Synchronize(ev.Key);
_records.AddRecordEntry(ev.Key, new CriminalRecord());
_records.Synchronize(ev.Key);
}
/// <summary>
/// Tries to change the status of the record found by the StationRecordKey.
/// Reason should only be passed if status is Wanted.
/// Reason should only be passed if status is Wanted, nullability isn't checked.
/// </summary>
/// <returns>True if the status is changed, false if not</returns>
public bool TryChangeStatus(StationRecordKey key, SecurityStatus status, string? reason)
{
// don't do anything if its the same status
if (!_stationRecords.TryGetRecord<CriminalRecord>(key, out var record)
if (!_records.TryGetRecord<CriminalRecord>(key, out var record)
|| status == record.Status)
return false;
OverwriteStatus(key, record, status, reason);
return true;
}
/// <summary>
/// Sets the status without checking previous status or reason nullability.
/// </summary>
public void OverwriteStatus(StationRecordKey key, CriminalRecord record, SecurityStatus status, string? reason)
{
record.Status = status;
record.Reason = reason;
_stationRecords.Synchronize(key);
var name = _records.RecordName(key);
if (name != string.Empty)
UpdateCriminalIdentity(name, status);
return true;
_records.Synchronize(key);
}
/// <summary>
@@ -59,7 +72,7 @@ public sealed class CriminalRecordsSystem : EntitySystem
/// <returns>True if adding succeeded, false if not</returns>
public bool TryAddHistory(StationRecordKey key, CrimeHistory entry)
{
if (!_stationRecords.TryGetRecord<CriminalRecord>(key, out var record))
if (!_records.TryGetRecord<CriminalRecord>(key, out var record))
return false;
record.History.Add(entry);
@@ -81,7 +94,7 @@ public sealed class CriminalRecordsSystem : EntitySystem
/// <returns>True if the line was removed, false if not</returns>
public bool TryDeleteHistory(StationRecordKey key, uint index)
{
if (!_stationRecords.TryGetRecord<CriminalRecord>(key, out var record))
if (!_records.TryGetRecord<CriminalRecord>(key, out var record))
return false;
if (index >= record.History.Count)

View File

@@ -36,7 +36,7 @@ public sealed class IgnitionSourceSystem : EntitySystem
/// </summary>
public void SetIgnited(Entity<IgnitionSourceComponent?> ent, bool ignited = true)
{
if (!Resolve(ent, ref ent.Comp))
if (!Resolve(ent, ref ent.Comp, false))
return;
ent.Comp.Ignited = ignited;

View File

@@ -39,6 +39,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
@@ -119,6 +120,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
SetSeed(uid, component, _random.Next());
}
if (_proto.TryIndex(component.Template, out var biome))
SetTemplate(uid, component, biome);
var xform = Transform(uid);
var mapId = xform.MapID;
@@ -151,6 +155,15 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
}
}
public void SetEnabled(Entity<BiomeComponent?> ent, bool enabled = true)
{
if (!Resolve(ent, ref ent.Comp) || ent.Comp.Enabled == enabled)
return;
ent.Comp.Enabled = enabled;
Dirty(ent, ent.Comp);
}
public void SetSeed(EntityUid uid, BiomeComponent component, int seed, bool dirty = true)
{
component.Seed = seed;

View File

@@ -223,6 +223,17 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
return null;
}
/// <summary>
/// Get the name for a record, or an empty string if it has no record.
/// </summary>
public string RecordName(StationRecordKey key)
{
if (!TryGetRecord<GeneralStationRecord>(key, out var record))
return string.Empty;
return record.Name;
}
/// <summary>
/// Gets all records of a specific type from a station.
/// </summary>

View File

@@ -496,7 +496,7 @@ namespace Content.Server.Strip
if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp))
{
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", target)), user);
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Name(target, EntityManager, user))), user);
return false;
}
@@ -511,7 +511,7 @@ namespace Content.Server.Strip
if (!_handsSystem.CanDropHeld(target, handSlot, false))
{
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", target)), user);
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Name(target, EntityManager, user))), user);
return false;
}

View File

@@ -46,4 +46,7 @@ public sealed partial class IdCardComponent : Component
[DataField]
public LocId FullNameLocId = "access-id-card-component-owner-full-name-job-title-text";
[DataField]
public bool CanMicrowave = true;
}

View File

@@ -1,29 +1,85 @@
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Rejuvenate;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
namespace Content.Shared.Atmos.Rotting;
public abstract class SharedRottingSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
public const int MaxStages = 3;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PerishableComponent, MapInitEvent>(OnPerishableMapInit);
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnPerishableExamined);
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
}
/// <summary>
/// Return the rot stage, usually from 0 to 2 inclusive.
/// </summary>
public int RotStage(EntityUid uid, RottingComponent? comp = null, PerishableComponent? perishable = null)
private void OnPerishableMapInit(EntityUid uid, PerishableComponent component, MapInitEvent args)
{
if (!Resolve(uid, ref comp, ref perishable))
return 0;
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
return (int) (comp.TotalRotTime.TotalSeconds / perishable.RotAfter.TotalSeconds);
private void OnMobStateChanged(EntityUid uid, PerishableComponent component, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Dead && args.OldMobState != MobState.Dead)
return;
if (HasComp<RottingComponent>(uid))
return;
component.RotAccumulator = TimeSpan.Zero;
component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
private void OnPerishableExamined(Entity<PerishableComponent> perishable, ref ExaminedEvent args)
{
int stage = PerishStage(perishable, MaxStages);
if (stage < 1 || stage > MaxStages)
{
// We dont push an examined string if it hasen't started "perishing" or it's already rotting
return;
}
var isMob = HasComp<MobStateComponent>(perishable);
var description = "perishable-" + stage + (!isMob ? "-nonmob" : string.Empty);
args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(perishable, EntityManager))));
}
private void OnShutdown(EntityUid uid, RottingComponent component, ComponentShutdown args)
{
if (TryComp<PerishableComponent>(uid, out var perishable))
{
perishable.RotNextUpdate = TimeSpan.Zero;
}
}
private void OnRottingMobStateChanged(EntityUid uid, RottingComponent component, MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead)
return;
RemCompDeferred(uid, component);
}
private void OnRejuvenate(EntityUid uid, RottingComponent component, RejuvenateEvent args)
{
RemCompDeferred<RottingComponent>(uid);
}
private void OnExamined(EntityUid uid, RottingComponent component, ExaminedEvent args)
@@ -41,4 +97,75 @@ public abstract class SharedRottingSystem : EntitySystem
args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(uid, EntityManager))));
}
/// <summary>
/// Return an integer from 0 to maxStage representing how close to rotting an entity is. Used to
/// generate examine messages for items that are starting to rot.
/// </summary>
public int PerishStage(Entity<PerishableComponent> perishable, int maxStages)
{
if (perishable.Comp.RotAfter.TotalSeconds == 0 || perishable.Comp.RotAccumulator.TotalSeconds == 0)
return 0;
return (int)(1 + maxStages * perishable.Comp.RotAccumulator.TotalSeconds / perishable.Comp.RotAfter.TotalSeconds);
}
public bool IsRotProgressing(EntityUid uid, PerishableComponent? perishable)
{
// things don't perish by default.
if (!Resolve(uid, ref perishable, false))
return false;
// only dead things or inanimate objects can rot
if (TryComp<MobStateComponent>(uid, out var mobState) && !_mobState.IsDead(uid, mobState))
return false;
if (_container.TryGetOuterContainer(uid, Transform(uid), out var container) &&
HasComp<AntiRottingContainerComponent>(container.Owner))
{
return false;
}
var ev = new IsRottingEvent();
RaiseLocalEvent(uid, ref ev);
return !ev.Handled;
}
public bool IsRotten(EntityUid uid, RottingComponent? rotting = null)
{
return Resolve(uid, ref rotting, false);
}
public void ReduceAccumulator(EntityUid uid, TimeSpan time)
{
if (!TryComp<PerishableComponent>(uid, out var perishable))
return;
if (!TryComp<RottingComponent>(uid, out var rotting))
{
perishable.RotAccumulator -= time;
return;
}
var total = (rotting.TotalRotTime + perishable.RotAccumulator) - time;
if (total < perishable.RotAfter)
{
RemCompDeferred(uid, rotting);
perishable.RotAccumulator = total;
}
else
rotting.TotalRotTime = total - perishable.RotAfter;
}
/// <summary>
/// Return the rot stage, usually from 0 to 2 inclusive.
/// </summary>
public int RotStage(EntityUid uid, RottingComponent? comp = null, PerishableComponent? perishable = null)
{
if (!Resolve(uid, ref comp, ref perishable))
return 0;
return (int) (comp.TotalRotTime.TotalSeconds / perishable.RotAfter.TotalSeconds);
}
}

View File

@@ -573,13 +573,13 @@ namespace Content.Shared.CCVar
/// When a mob is walking should its X / Y movement be relative to its parent (true) or the map (false).
/// </summary>
public static readonly CVarDef<bool> RelativeMovement =
CVarDef.Create("physics.relative_movement", true, CVar.ARCHIVE | CVar.REPLICATED);
CVarDef.Create("physics.relative_movement", true, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
public static readonly CVarDef<float> TileFrictionModifier =
CVarDef.Create("physics.tile_friction", 40.0f, CVar.ARCHIVE | CVar.REPLICATED);
CVarDef.Create("physics.tile_friction", 40.0f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
public static readonly CVarDef<float> StopSpeed =
CVarDef.Create("physics.stop_speed", 0.1f, CVar.ARCHIVE | CVar.REPLICATED);
CVarDef.Create("physics.stop_speed", 0.1f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// Whether mobs can push objects like lockers.
@@ -588,7 +588,7 @@ namespace Content.Shared.CCVar
/// Technically client doesn't need to know about it but this may prevent a bug in the distant future so it stays.
/// </remarks>
public static readonly CVarDef<bool> MobPushing =
CVarDef.Create("physics.mob_pushing", false, CVar.REPLICATED);
CVarDef.Create("physics.mob_pushing", false, CVar.REPLICATED | CVar.SERVER);
/*
* Music
@@ -1547,10 +1547,10 @@ namespace Content.Shared.CCVar
CVarDef.Create("viewport.scale_render", true, CVar.CLIENTONLY | CVar.ARCHIVE);
public static readonly CVarDef<int> ViewportMinimumWidth =
CVarDef.Create("viewport.minimum_width", 15, CVar.REPLICATED);
CVarDef.Create("viewport.minimum_width", 15, CVar.REPLICATED | CVar.SERVER);
public static readonly CVarDef<int> ViewportMaximumWidth =
CVarDef.Create("viewport.maximum_width", 21, CVar.REPLICATED);
CVarDef.Create("viewport.maximum_width", 21, CVar.REPLICATED | CVar.SERVER);
public static readonly CVarDef<int> ViewportWidth =
CVarDef.Create("viewport.width", 21, CVar.CLIENTONLY | CVar.ARCHIVE);
@@ -1655,7 +1655,7 @@ namespace Content.Shared.CCVar
CVarDef.Create("chat.chat_sanitizer_enabled", true, CVar.SERVERONLY);
public static readonly CVarDef<bool> ChatShowTypingIndicator =
CVarDef.Create("chat.show_typing_indicator", true, CVar.CLIENTONLY);
CVarDef.Create("chat.show_typing_indicator", true, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
public static readonly CVarDef<bool> ChatEnableFancyBubbles =
CVarDef.Create("chat.enable_fancy_bubbles", true, CVar.CLIENTONLY | CVar.ARCHIVE, "Toggles displaying fancy speech bubbles, which display the speaking character's name.");

View File

@@ -152,10 +152,16 @@ public abstract class SharedChatSystem : EntitySystem
if (string.IsNullOrEmpty(message))
return message;
// Capitalize first letter
message = char.ToUpper(message[0]) + message.Remove(0, 1);
message = OopsConcat(char.ToUpper(message[0]).ToString(), message.Remove(0, 1));
return message;
}
private static string OopsConcat(string a, string b)
{
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
return a + b;
}
public string SanitizeMessageCapitalizeTheWordI(string message, string theWordI = "i")
{
if (string.IsNullOrEmpty(message))

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry
@@ -93,11 +94,11 @@ namespace Content.Shared.Chemistry
}
[Serializable, NetSerializable]
public sealed class ReagentInventoryItem(string storageSlotId, string reagentLabel, string storedAmount, Color reagentColor)
public sealed class ReagentInventoryItem(string storageSlotId, string reagentLabel, FixedPoint2 quantity, Color reagentColor)
{
public string StorageSlotId = storageSlotId;
public string ReagentLabel = reagentLabel;
public string StoredAmount = storedAmount;
public FixedPoint2 Quantity = quantity;
public Color ReagentColor = reagentColor;
}

View File

@@ -1,52 +1,6 @@
using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Security;
using Content.Shared.Security.Components;
namespace Content.Shared.CriminalRecords.Systems;
public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem
{
/// <summary>
/// Any entity that has a the name of the record that was just changed as their visible name will get their icon
/// updated with the new status, if the record got removed their icon will be removed too.
/// </summary>
public void UpdateCriminalIdentity(string name, SecurityStatus status)
{
var query = EntityQueryEnumerator<IdentityComponent>();
while (query.MoveNext(out var uid, out var identity))
{
if (!Identity.Name(uid, EntityManager).Equals(name))
continue;
if (status == SecurityStatus.None)
RemComp<CriminalRecordComponent>(uid);
else
SetCriminalIcon(name, status, uid);
}
}
/// <summary>
/// Decides the icon that should be displayed on the entity based on the security status
/// </summary>
public void SetCriminalIcon(string name, SecurityStatus status, EntityUid characterUid)
{
EnsureComp<CriminalRecordComponent>(characterUid, out var record);
var previousIcon = record.StatusIcon;
record.StatusIcon = status switch
{
SecurityStatus.Paroled => "SecurityIconParoled",
SecurityStatus.Wanted => "SecurityIconWanted",
SecurityStatus.Detained => "SecurityIconIncarcerated",
SecurityStatus.Discharged => "SecurityIconDischarged",
SecurityStatus.Suspected => "SecurityIconSuspected",
_ => record.StatusIcon
};
if(previousIcon != record.StatusIcon)
Dirty(characterUid, record);
}
}
/// <summary>
/// Station records aren't predicted, just exists for access.
/// </summary>
public abstract class SharedCriminalRecordsConsoleSystem : EntitySystem;

View File

@@ -0,0 +1,52 @@
using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Security;
using Content.Shared.Security.Components;
namespace Content.Shared.CriminalRecords.Systems;
public abstract class SharedCriminalRecordsSystem : EntitySystem
{
/// <summary>
/// Any entity that has a the name of the record that was just changed as their visible name will get their icon
/// updated with the new status, if the record got removed their icon will be removed too.
/// </summary>
public void UpdateCriminalIdentity(string name, SecurityStatus status)
{
var query = EntityQueryEnumerator<IdentityComponent>();
while (query.MoveNext(out var uid, out var identity))
{
if (!Identity.Name(uid, EntityManager).Equals(name))
continue;
if (status == SecurityStatus.None)
RemComp<CriminalRecordComponent>(uid);
else
SetCriminalIcon(name, status, uid);
}
}
/// <summary>
/// Decides the icon that should be displayed on the entity based on the security status
/// </summary>
public void SetCriminalIcon(string name, SecurityStatus status, EntityUid characterUid)
{
EnsureComp<CriminalRecordComponent>(characterUid, out var record);
var previousIcon = record.StatusIcon;
record.StatusIcon = status switch
{
SecurityStatus.Paroled => "SecurityIconParoled",
SecurityStatus.Wanted => "SecurityIconWanted",
SecurityStatus.Detained => "SecurityIconIncarcerated",
SecurityStatus.Discharged => "SecurityIconDischarged",
SecurityStatus.Suspected => "SecurityIconSuspected",
_ => record.StatusIcon
};
if (previousIcon != record.StatusIcon)
Dirty(characterUid, record);
}
}

View File

@@ -30,14 +30,13 @@ public sealed partial class BiomeComponent : Component
public List<IBiomeLayer> Layers = new();
/// <summary>
/// Templates to use for <see cref="Layers"/>. Optional as this can be set elsewhere.
/// Templates to use for <see cref="Layers"/>.
/// If this is set on mapinit, it will fill out layers automatically.
/// If not set, use <c>BiomeSystem</c> to do it.
/// Prototype reloading will also use this.
/// </summary>
/// <remarks>
/// This is really just here for prototype reload support.
/// </remarks>
[ViewVariables(VVAccess.ReadWrite),
DataField("template", customTypeSerializer: typeof(PrototypeIdSerializer<BiomeTemplatePrototype>))]
public string? Template;
[DataField]
public ProtoId<BiomeTemplatePrototype>? Template;
/// <summary>
/// If we've already generated a tile and couldn't deload it then we won't ever reload it in future.

View File

@@ -6,8 +6,13 @@ namespace Content.Shared.Weapons.Ranged.Components;
/// <summary>
/// Indicates that this gun requires wielding to be useable.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(WieldableSystem))]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(WieldableSystem))]
public sealed partial class GunRequiresWieldComponent : Component
{
[DataField, AutoNetworkedField]
public TimeSpan LastPopup;
[DataField, AutoNetworkedField]
public TimeSpan PopupCooldown = TimeSpan.FromSeconds(1);
}

View File

@@ -1,3 +1,5 @@
using Content.Shared.Weapons.Ranged.Components;
namespace Content.Shared.Weapons.Ranged.Events;
/// <summary>
@@ -15,7 +17,7 @@ public record struct ShotAttemptedEvent
/// <summary>
/// The gun being shot.
/// </summary>
public EntityUid Used;
public Entity<GunComponent> Used;
public bool Cancelled { get; private set; }

View File

@@ -239,7 +239,7 @@ public abstract partial class SharedGunSystem : EntitySystem
var prevention = new ShotAttemptedEvent
{
User = user,
Used = gunUid
Used = (gunUid, gun)
};
RaiseLocalEvent(gunUid, ref prevention);
if (prevention.Cancelled)

View File

@@ -16,7 +16,7 @@ using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Shared.Wieldable;
@@ -30,6 +30,7 @@ public sealed class WieldableSystem : EntitySystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly UseDelaySystem _delay = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
@@ -42,7 +43,7 @@ public sealed class WieldableSystem : EntitySystem
SubscribeLocalEvent<WieldableComponent, GetVerbsEvent<InteractionVerb>>(AddToggleWieldVerb);
SubscribeLocalEvent<MeleeRequiresWieldComponent, AttemptMeleeEvent>(OnMeleeAttempt);
SubscribeLocalEvent<GunRequiresWieldComponent, AttemptShootEvent>(OnShootAttempt);
SubscribeLocalEvent<GunRequiresWieldComponent, ShotAttemptedEvent>(OnShootAttempt);
SubscribeLocalEvent<GunWieldBonusComponent, ItemWieldedEvent>(OnGunWielded);
SubscribeLocalEvent<GunWieldBonusComponent, ItemUnwieldedEvent>(OnGunUnwielded);
SubscribeLocalEvent<GunWieldBonusComponent, GunRefreshModifiersEvent>(OnGunRefreshModifiers);
@@ -61,16 +62,21 @@ public sealed class WieldableSystem : EntitySystem
}
}
private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref AttemptShootEvent args)
private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref ShotAttemptedEvent args)
{
if (TryComp<WieldableComponent>(uid, out var wieldable) &&
!wieldable.Wielded)
{
args.Cancelled = true;
args.Cancel();
if (!HasComp<MeleeWeaponComponent>(uid) && !HasComp<MeleeRequiresWieldComponent>(uid))
var time = _timing.CurTime;
if (time > component.LastPopup + component.PopupCooldown &&
!HasComp<MeleeWeaponComponent>(uid) &&
!HasComp<MeleeRequiresWieldComponent>(uid))
{
args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
component.LastPopup = time;
var message = Loc.GetString("wieldable-component-requires", ("item", uid));
_popupSystem.PopupClient(message, args.Used, args.User);
}
}
}

View File

@@ -1,78 +1,4 @@
Entries:
- author: Brandon-Huu
changes:
- message: Removed the ability for command or any antag-safe role to be initial
infected in zombie mode
type: Remove
id: 6090
time: '2024-03-05T04:34:18.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25785
- author: Whisper
changes:
- message: Glass shards can be added to flatcaps to create bladed flatcaps!
type: Add
id: 6091
time: '2024-03-05T20:57:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25780
- author: MACMAN2003
changes:
- message: Added clockwork structures and brass to build them with.
type: Add
id: 6092
time: '2024-03-06T00:25:59.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/24673
- author: deepdarkdepths
changes:
- message: Added the paramedic's cap to the uniform printer.
type: Add
id: 6093
time: '2024-03-06T01:17:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25861
- author: lzk228
changes:
- message: Livestock crate and artifact container now can go under plastic flaps.
type: Tweak
id: 6094
time: '2024-03-06T01:33:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25862
- author: PJB3005
changes:
- message: Saving paper is on ctrl+enter again. Also there's a visible "save" button
now.
type: Tweak
id: 6095
time: '2024-03-06T01:35:26.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25870
- author: ps3moira
changes:
- message: Changed brick, wood, and web walls to be unable to conjoin with different
walls.
type: Tweak
id: 6096
time: '2024-03-06T01:36:04.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25674
- author: Froffy
changes:
- message: Water can now be scooped from water tiles, such as planetary rivers.
type: Add
id: 6097
time: '2024-03-06T01:37:24.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25825
- author: Ko4erga
changes:
- message: '"Post light" updated.'
type: Tweak
id: 6098
time: '2024-03-06T01:39:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25814
- author: Ilya246
changes:
- message: Gas leak events are now 4 times as strong, and may leak water vapor.
The fog is coming.
type: Tweak
id: 6099
time: '2024-03-06T01:41:04.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25864
- author: Just_Art
changes:
- message: Added bob 5 hair!
@@ -3870,3 +3796,75 @@
id: 6589
time: '2024-05-13T00:03:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27962
- author: lzk228
changes:
- message: Fixed color jumpskirts female mask.
type: Fix
id: 6590
time: '2024-05-13T19:25:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27984
- author: aspiringLich
changes:
- message: Tweaked the Chemical, Booze, and Soda Dispenser UIs to aid readability.
type: Tweak
id: 6591
time: '2024-05-13T21:46:17.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27958
- author: deltanedas
changes:
- message: Fixed fire spreading being insane.
type: Fix
id: 6592
time: '2024-05-13T21:49:44.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27986
- author: DrSmugleaf
changes:
- message: Failing to fire a gun that requires wielding will no longer delay the
next shot.
type: Tweak
id: 6593
time: '2024-05-14T04:53:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27973
- author: lzk228
changes:
- message: Grill and hotplate can be anchored on the table.
type: Fix
id: 6594
time: '2024-05-14T23:57:23.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28026
- author: ShadowCommander
changes:
- message: Fixed salvage magnet UI opening multiple times when activated.
type: Fix
id: 6595
time: '2024-05-14T23:59:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28010
- author: Vermidia
changes:
- message: Shuttle roles get PDAs and headsets now
type: Fix
id: 6596
time: '2024-05-15T10:30:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28045
- author: EdenTheLiznerd
changes:
- message: Removed the throngler from the grand lottery
type: Tweak
id: 6597
time: '2024-05-15T21:02:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28060
- author: deltanedas
changes:
- message: Fixed ninja criminal records hack not changing sechud icons.
type: Fix
id: 6598
time: '2024-05-16T20:51:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28021
- author: EmoGarbage404
changes:
- message: Fixed a bug that would cause items in your inventory to get stuck to
your cursor.
type: Fix
id: 6599
time: '2024-05-17T07:51:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27960

View File

@@ -428,7 +428,7 @@ entities:
- type: Physics
canCollide: False
- type: InsideEntityStorage
- proto: ClothingHeadHelmetHardsuitBasic
- proto: ClothingHeadHelmetEVALarge
entities:
- uid: 82
components:

File diff suppressed because it is too large Load Diff

View File

@@ -110,9 +110,6 @@
- id: WeaponRifleAk
prob: 0.0001
orGroup: Weapons
- id: Throngler
prob: 0.0001
orGroup: Weapons
- id: WeaponLauncherPirateCannon
prob: 0.001
orGroup: Weapons

View File

@@ -133,6 +133,5 @@
- "You can weld glass shards into glass sheets."
- "By right clicking on a player, and then clicking the heart icon, you can quickly examine them to check for injuries or how badly they're bleeding. You can also do this to yourself."
- "Monkeys and kobolds have a rare chance to be sentient. Ook!"
- "Lottery crates can very rarely contain The Throngler."
- "You can tell if an area with firelocks up is spaced by looking to see if the firelocks have lights beside them."
- "Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands."

View File

@@ -1,6 +1,6 @@
# White Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorWhite
name: white jumpskirt
description: A generic white jumpskirt with no rank markings.
@@ -27,7 +27,7 @@
# Grey Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorGrey
name: grey jumpskirt
description: A tasteful grey jumpskirt that reminds you of the good old days.
@@ -58,7 +58,7 @@
# Black Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorBlack
name: black jumpskirt
description: A generic black jumpskirt with no rank markings.
@@ -89,7 +89,7 @@
# Blue Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorBlue
name: blue jumpskirt
description: A generic blue jumpskirt with no rank markings.
@@ -120,7 +120,7 @@
# Dark Blue Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorDarkBlue
name: dark blue jumpskirt
description: A generic dark blue jumpskirt with no rank markings.
@@ -151,7 +151,7 @@
# Teal Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorTeal
name: teal jumpskirt
description: A generic teal jumpskirt with no rank markings.
@@ -182,7 +182,7 @@
# Green Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorGreen
name: green jumpskirt
description: A generic green jumpskirt with no rank markings.
@@ -213,7 +213,7 @@
# Dark Green Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorDarkGreen
name: dark green jumpskirt
description: A generic dark green jumpskirt with no rank markings.
@@ -244,7 +244,7 @@
# Orange Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorOrange
name: orange jumpskirt
description: Don't wear this near paranoid security officers.
@@ -275,7 +275,7 @@
# Pink Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorPink
name: pink jumpskirt
description: Just looking at this makes you feel <i>fabulous</i>.
@@ -306,7 +306,7 @@
# Red Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorRed
name: red jumpskirt
description: A generic red jumpskirt with no rank markings.
@@ -337,7 +337,7 @@
# Yellow Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorYellow
name: yellow jumpskirt
description: A generic yellow jumpskirt with no rank markings.
@@ -368,7 +368,7 @@
# Purple Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorPurple
name: purple jumpskirt
description: A generic light purple jumpskirt with no rank markings.
@@ -399,7 +399,7 @@
# Light Brown Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorLightBrown
name: light brown jumpskirt
description: A generic light brown jumpskirt with no rank markings.
@@ -430,7 +430,7 @@
# Brown Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorBrown
name: brown jumpskirt
description: A generic brown jumpskirt with no rank markings.
@@ -461,7 +461,7 @@
# Maroon Jumpskirt
- type: entity
parent: ClothingUniformBase
parent: ClothingUniformSkirtBase
id: ClothingUniformJumpskirtColorMaroon
name: maroon jumpskirt
description: A generic maroon jumpskirt with no rank markings.

View File

@@ -629,7 +629,7 @@
settings: short
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobCargoTechnician ]
prototypes: [ LostCargoTechGearSuit, LostCargoTechGearCoat ]
- type: RandomMetadata
nameSegments:
- names_first
@@ -684,7 +684,7 @@
settings: short
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobClown ]
prototypes: [ ClownTroupe ]
- type: RandomMetadata
nameSegments:
- names_clown
@@ -742,7 +742,7 @@
settings: short
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobChef ]
prototypes: [ TravelingChef ]
- type: RandomMetadata
nameSegments:
- names_first
@@ -805,7 +805,7 @@
settings: default
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobResearchDirector ]
prototypes: [ DisasterVictimRD, DisasterVictimRDAlt ]
- type: RandomMetadata
nameSegments:
- names_first
@@ -823,7 +823,7 @@
settings: default
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobChiefMedicalOfficer ]
prototypes: [ DisasterVictimCMO, DisasterVictimCMOAlt ]
- type: RandomMetadata
nameSegments:
- names_first
@@ -841,7 +841,7 @@
settings: default
- type: GhostTakeoverAvailable
- type: Loadout
roleLoadout: [ JobCaptain ]
prototypes: [ DisasterVictimCaptain, DisasterVictimCaptainAlt ]
- type: RandomMetadata
nameSegments:
- names_first
@@ -888,7 +888,7 @@
settings: short
- type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ SyndicateOperativeGearCivilian ]
prototypes: [ SyndicateOperativeGearDisasterVictim ]
- type: RandomMetadata
nameSegments:
- names_first

View File

@@ -553,7 +553,7 @@
- type: Sprite
state: medical
- type: MachineBoard
prototype: chem_master
prototype: ChemMaster
requirements:
Capacitor: 1
materialRequirements:

View File

@@ -16,6 +16,17 @@
- ItemMask
restitution: 0.3 # fite me
friction: 0.2
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: StrongMetallic # We only want explosions breaking them (for the most part)
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 75
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: Tag
tags:
- Cartridge

View File

@@ -1,9 +1,9 @@
- type: entity
id: soda_dispenser
name: soda dispenser
suffix: Filled
parent: ReagentDispenserBase
id: SodaDispenser
name: soda dispenser
description: A beverage dispenser with a selection of soda and several other common beverages. Has a single fill slot for containers.
suffix: Filled
components:
- type: Rotatable
- type: Sprite
@@ -24,9 +24,9 @@
- Bartender
- type: entity
parent: SodaDispenser
id: SodaDispenserEmpty
suffix: Empty
parent: soda_dispenser
components:
- type: ReagentDispenser
storageWhitelist:

View File

@@ -51,10 +51,6 @@
disposals: !type:Container
- type: Physics
bodyType: Static
- type: Fixtures
fixtures:
fix1:
hard: false
- type: Construction
graph: Toilet
node: toilet

View File

@@ -1,5 +1,5 @@
- type: entity
id: chem_master
id: ChemMaster
parent: [ BaseMachinePowered, ConstructibleMachine ]
name: ChemMaster 4000
description: An industrial grade chemical manipulator with pill and bottle production included.

View File

@@ -15,9 +15,7 @@
mask:
- TabletopMachineMask
layer:
- Impassable
- MidImpassable
- LowImpassable
- TabletopMachineLayer
hard: false
- type: ApcPowerReceiver
powerLoad: 300

View File

@@ -58,6 +58,7 @@
- TabletopMachineMask
layer:
- TabletopMachineLayer
hard: false
- type: Sprite
sprite: Structures/Machines/microwave.rsi
drawdepth: SmallObjects

View File

@@ -32,6 +32,7 @@
- TabletopMachineMask
layer:
- TabletopMachineLayer
hard: false
- type: Sprite
sprite: Structures/Machines/juicer.rsi
drawdepth: SmallObjects

View File

@@ -80,6 +80,18 @@
shoes: ClothingShoesBootsCombat
gloves: ClothingHandsGlovesColorBlack
# Syndicate Operative Outfit - Disaster Victim
- type: startingGear
id: SyndicateOperativeGearDisasterVictim
equipment:
jumpsuit: ClothingUniformJumpsuitSyndieFormal
back: ClothingBackpackDuffelSyndicate
shoes: ClothingShoesBootsCombat
gloves: ClothingHandsGlovesColorBlack
id: SyndiPDA
ears: ClothingHeadsetAltSyndicate
#Syndicate Operative Outfit - Basic
- type: startingGear
id: SyndicateOperativeGearBasic
@@ -318,6 +330,140 @@
- type: startingGear
id: BananaClown
equipment:
id: ClownPDA
back: ClothingBackpackClown
shoes: ClothingShoesClownBanana
jumpsuit: ClothingUniformJumpsuitClownBanana
mask: ClothingMaskClownBanana
ears: ClothingHeadsetService
pocket1: BikeHorn
pocket2: ClownRecorder
#Clown Troupe
- type: startingGear
id: ClownTroupe
equipment:
jumpsuit: ClothingUniformJumpsuitClown
shoes: ClothingShoesClown
id: ClownPDA
back: ClothingBackpackClown
ears: ClothingHeadsetService
mask: ClothingMaskClown
pocket1: BikeHorn
pocket2: ClownRecorder
#Lost Cargo Tech
- type: startingGear
id: LostCargoTechGearSuit
equipment:
jumpsuit: ClothingUniformJumpsuitCargo
shoes: ClothingShoesColorBlack
head: ClothingHeadHatCargosoft
id: CargoPDA
back: ClothingBackpackCargo
ears: ClothingHeadsetCargo
pocket1: AppraisalTool
- type: startingGear
id: LostCargoTechGearCoat
equipment:
jumpsuit: ClothingUniformJumpsuitCargo
shoes: ClothingShoesBootsWinterCargo
head: ClothingHeadHatCargosoft
id: CargoPDA
back: ClothingBackpackDuffelCargo
ears: ClothingHeadsetCargo
pocket1: AppraisalTool
outerClothing: ClothingOuterWinterCargo
#Traveling Chef
- type: startingGear
id: TravelingChef
equipment:
jumpsuit: ClothingUniformJumpsuitChef
shoes: ClothingShoesColorWhite
id: ChefPDA
back: ClothingBackpackSatchel
ears: ClothingHeadsetService
belt: ClothingBeltChef
#CMO Disaster Victim
- type: startingGear
id: DisasterVictimCMO
equipment:
jumpsuit: ClothingUniformJumpsuitCMO
shoes: ClothingShoesColorBrown
head: ClothingHeadMirror
neck: ClothingCloakCmo
id: CMOPDA
back: ClothingBackpackMedical
ears: ClothingHeadsetCMO
belt: ClothingBeltMedical
outerClothing: ClothingOuterCoatLabCmo
- type: startingGear
id: DisasterVictimCMOAlt
equipment:
jumpsuit: ClothingUniformJumpsuitCMOTurtle
shoes: ClothingShoesColorBrown
head: ClothingHeadHatBeretCmo
neck: ClothingNeckMantleCMO
id: CMOPDA
back: ClothingBackpackSatchelMedical
ears: ClothingHeadsetCMO
belt: ClothingBeltMedical
outerClothing: ClothingOuterCoatLabCmo
#Captain Disaster Victim
- type: startingGear
id: DisasterVictimCaptain
equipment:
jumpsuit: ClothingUniformJumpsuitCaptain
shoes: ClothingShoesBootsLaceup
eyes: ClothingEyesGlassesSunglasses
gloves: ClothingHandsGlovesCaptain
head: ClothingHeadHatCaptain
neck: ClothingNeckCloakCap
id: CaptainPDA
back: ClothingBackpackCaptain
ears: ClothingHeadsetAltCommand
outerClothing: ClothingOuterArmorCaptainCarapace
- type: startingGear
id: DisasterVictimCaptainAlt
equipment:
jumpsuit: ClothingUniformJumpsuitCapFormal
shoes: ClothingShoesBootsLaceup
eyes: ClothingEyesGlassesSunglasses
gloves: ClothingHandsGlovesCaptain
head: ClothingHeadHatCapcap
neck: ClothingNeckMantleCap
id: CaptainPDA
back: ClothingBackpackSatchelCaptain
ears: ClothingHeadsetAltCommand
outerClothing: ClothingOuterArmorCaptainCarapace
#RD Disaster Victim
- type: startingGear
id: DisasterVictimRD
equipment:
jumpsuit: ClothingUniformJumpsuitResearchDirector
shoes: ClothingShoesColorBrown
head: ClothingHeadHatBeretRND
neck: ClothingNeckCloakRd
id: RnDPDA
back: ClothingBackpackScience
ears: ClothingHeadsetRD
outerClothing: ClothingOuterCoatRD
- type: startingGear
id: DisasterVictimRDAlt
equipment:
jumpsuit: ClothingUniformJumpsuitResearchDirector
shoes: ClothingShoesColorBrown
head: ClothingHeadHatBeretRND
neck: ClothingNeckMantleRD
id: RnDPDA
back: ClothingBackpackSatchelScience
ears: ClothingHeadsetRD
outerClothing: ClothingOuterCoatRD

View File

@@ -10,7 +10,7 @@ While chemists primarily make medications, there's a very wide variety of chemic
<GuideEntityEmbed Entity="ChemDispenser"/>
<GuideEntityEmbed Entity="ChemBag"/>
<GuideEntityEmbed Entity="KitchenReagentGrinder"/>
<GuideEntityEmbed Entity="chem_master"/>
<GuideEntityEmbed Entity="ChemMaster"/>
</Box>
<Box>
<GuideEntityEmbed Entity="LargeBeaker"/>

View File

@@ -17,6 +17,6 @@ Right now, medbay is divided into two different sets of care:
<GuideEntityEmbed Entity="ChemDispenser"/>
<GuideEntityEmbed Entity="LargeBeaker"/>
<GuideEntityEmbed Entity="PillCanister"/>
<GuideEntityEmbed Entity="chem_master"/>
<GuideEntityEmbed Entity="ChemMaster"/>
</Box>
</Document>

View File

@@ -7,7 +7,7 @@ The materials for these drinks can be found in various places:
</Box>
<Box>
<GuideEntityEmbed Entity="BoozeDispenser"/>
<GuideEntityEmbed Entity="soda_dispenser"/>
<GuideEntityEmbed Entity="SodaDispenser"/>
<GuideEntityEmbed Entity="VendingMachineBooze"/>
</Box>

View File

@@ -348,3 +348,7 @@ ReinforcementRadioSyndicateMonkeyNukeops: ReinforcementRadioSyndicateAncestorNuk
# 2024-05-01
DrinkBottleGoldschlager: DrinkBottleGildlager
# 2024-05-14
soda_dispenser: SodaDispenser
chem_master: ChemMaster