Merge pull request #542 from crystallpunk-14/ed-06-11-2024-upstream

Ed 06 11 2024 upstream
This commit is contained in:
Ed
2024-11-06 17:06:40 +03:00
committed by GitHub
207 changed files with 2180 additions and 1314 deletions

View File

@@ -1,6 +1,8 @@
using Content.Shared.Damage.Prototypes;
using Content.Shared.Overlays;
using Robust.Client.Player;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Client.Commands;
@@ -34,7 +36,7 @@ public sealed class ShowHealthBarsCommand : LocalizedCommands
{
var showHealthBarsComponent = new ShowHealthBarsComponent
{
DamageContainers = args.ToList(),
DamageContainers = args.Select(arg => new ProtoId<DamageContainerPrototype>(arg)).ToList(),
HealthStatusIcon = null,
NetSyncEnabled = false
};

View File

@@ -22,6 +22,6 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
return;
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype!);
ev.StatusIcons.Add(iconPrototype);
}
}

View File

@@ -0,0 +1,9 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Vertical"
Margin="8 0 8 0">
<BoxContainer Name="Buttons"
Orientation="Vertical"
SeparationOverride="5">
<!-- Buttons are added here by code -->
</BoxContainer>
</BoxContainer>

View File

@@ -10,20 +10,17 @@ using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{
[GenerateTypedNameReferences]
public sealed partial class GhostRolesEntry : BoxContainer
public sealed partial class GhostRoleButtonsBox : BoxContainer
{
private SpriteSystem _spriteSystem;
public event Action<GhostRoleInfo>? OnRoleSelected;
public event Action<GhostRoleInfo>? OnRoleFollow;
public GhostRolesEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
public GhostRoleButtonsBox(bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
{
RobustXamlLoader.Load(this);
_spriteSystem = spriteSystem;
Title.Text = name;
Description.SetMessage(description);
foreach (var role in roles)
{
var button = new GhostRoleEntryButtons(role);

View File

@@ -1,15 +1,15 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal">
Orientation="Horizontal"
HorizontalAlignment="Stretch">
<Button Name="RequestButton"
Access="Public"
Text="{Loc 'ghost-roles-window-request-role-button'}"
StyleClasses="OpenRight"
HorizontalAlignment="Left"
SetWidth="300"/>
HorizontalExpand="True"
SizeFlagsStretchRatio="3"/>
<Button Name="FollowButton"
Access="Public"
Text="{Loc 'ghost-roles-window-follow-role-button'}"
StyleClasses="OpenLeft"
HorizontalAlignment="Right"
SetWidth="150"/>
HorizontalExpand="True"/>
</BoxContainer>

View File

@@ -0,0 +1,8 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Vertical">
<Label Name="Title"
StyleClasses="LabelKeyText"/>
<PanelContainer StyleClasses="HighDivider" />
<RichTextLabel Name="Description"
Margin="0 4"/>
</BoxContainer>

View File

@@ -0,0 +1,18 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{
[GenerateTypedNameReferences]
public sealed partial class GhostRoleInfoBox : BoxContainer
{
public GhostRoleInfoBox(string name, string description)
{
RobustXamlLoader.Load(this);
Title.Text = name;
Description.SetMessage(description);
}
}
}

View File

@@ -1,16 +0,0 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Vertical"
HorizontalExpand="True"
Margin="0 0 8 8">
<Label Name="Title"
StyleClasses="LabelKeyText"/>
<PanelContainer StyleClasses="HighDivider" />
<RichTextLabel Name="Description"
Margin="0 4"/>
<BoxContainer Name="Buttons"
HorizontalAlignment="Left"
Orientation="Vertical"
SeparationOverride="5">
<!-- Buttons are added here by code -->
</BoxContainer>
</BoxContainer>

View File

@@ -5,7 +5,6 @@ using Content.Shared.Eui;
using Content.Shared.Ghost.Roles;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
{
@@ -77,6 +76,13 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
if (state is not GhostRolesEuiState ghostState)
return;
// We must save BodyVisible state, so all Collapsible boxes will not close
// on adding new ghost role.
// Save the current state of each Collapsible box being visible or not
_window.SaveCollapsibleBoxesStates();
// Clearing the container before adding new roles
_window.ClearEntries();
var entityManager = IoCManager.Resolve<IEntityManager>();
@@ -84,28 +90,32 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
var spriteSystem = sysManager.GetEntitySystem<SpriteSystem>();
var requirementsManager = IoCManager.Resolve<JobRequirementsManager>();
// TODO: role.Requirements value doesn't work at all as an equality key, this must be fixed
// Grouping roles
var groupedRoles = ghostState.GhostRoles.GroupBy(
role => (role.Name, role.Description, role.Requirements));
// Add a new entry for each role group
foreach (var group in groupedRoles)
{
var name = group.Key.Name;
var description = group.Key.Description;
bool hasAccess = true;
FormattedMessage? reason;
if (!requirementsManager.CheckRoleRequirements(group.Key.Requirements, null, out reason))
{
hasAccess = false;
}
var hasAccess = requirementsManager.CheckRoleRequirements(
group.Key.Requirements,
null,
out var reason);
// Adding a new role
_window.AddEntry(name, description, hasAccess, reason, group, spriteSystem);
}
// Restore the Collapsible box state if it is saved
_window.RestoreCollapsibleBoxesStates();
// Close the rules window if it is no longer needed
var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId);
if (closeRulesWindow)
{
_windowRules?.Close();
}
}
}
}

View File

@@ -1,7 +1,10 @@
using System.Linq;
using Content.Shared.Ghost.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
@@ -12,20 +15,86 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
public event Action<GhostRoleInfo>? OnRoleRequestButtonClicked;
public event Action<GhostRoleInfo>? OnRoleFollow;
private Dictionary<(string name, string description), Collapsible> _collapsibleBoxes = new();
private HashSet<(string name, string description)> _uncollapsedStates = new();
public GhostRolesWindow()
{
RobustXamlLoader.Load(this);
}
public void ClearEntries()
{
NoRolesMessage.Visible = true;
EntryContainer.DisposeAllChildren();
_collapsibleBoxes.Clear();
}
public void SaveCollapsibleBoxesStates()
{
_uncollapsedStates.Clear();
foreach (var (key, collapsible) in _collapsibleBoxes)
{
if (collapsible.BodyVisible)
{
_uncollapsedStates.Add(key);
}
}
}
public void RestoreCollapsibleBoxesStates()
{
foreach (var (key, collapsible) in _collapsibleBoxes)
{
collapsible.BodyVisible = _uncollapsedStates.Contains(key);
}
}
public void AddEntry(string name, string description, bool hasAccess, FormattedMessage? reason, IEnumerable<GhostRoleInfo> roles, SpriteSystem spriteSystem)
{
NoRolesMessage.Visible = false;
var entry = new GhostRolesEntry(name, description, hasAccess, reason, roles, spriteSystem);
entry.OnRoleSelected += OnRoleRequestButtonClicked;
entry.OnRoleFollow += OnRoleFollow;
EntryContainer.AddChild(entry);
var ghostRoleInfos = roles.ToList();
var rolesCount = ghostRoleInfos.Count;
var info = new GhostRoleInfoBox(name, description);
var buttons = new GhostRoleButtonsBox(hasAccess, reason, ghostRoleInfos, spriteSystem);
buttons.OnRoleSelected += OnRoleRequestButtonClicked;
buttons.OnRoleFollow += OnRoleFollow;
EntryContainer.AddChild(info);
if (rolesCount > 1)
{
var buttonHeading = new CollapsibleHeading(Loc.GetString("ghost-roles-window-available-button", ("rolesCount", rolesCount)));
buttonHeading.AddStyleClass(ContainerButton.StyleClassButton);
buttonHeading.Label.HorizontalAlignment = HAlignment.Center;
buttonHeading.Label.HorizontalExpand = true;
var body = new CollapsibleBody
{
Margin = new Thickness(0, 5, 0, 0),
};
// TODO: Add Requirements to this key when it'll be fixed and work as an equality key in GhostRolesEui
var key = (name, description);
var collapsible = new Collapsible(buttonHeading, body)
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Margin = new Thickness(0, 0, 0, 8),
};
body.AddChild(buttons);
EntryContainer.AddChild(collapsible);
_collapsibleBoxes.Add(key, collapsible);
}
else
{
EntryContainer.AddChild(buttons);
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Linq;
using System.Numerics;
using Content.Client.Gameplay;
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Content.Shared.CCVar;
@@ -8,6 +9,7 @@ using Content.Shared.Voting;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.State;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -28,6 +30,7 @@ namespace Content.Client.Voting.UI
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntityNetworkManager _entNetManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IStateManager _state = default!;
private VotingSystem _votingSystem;
@@ -62,7 +65,7 @@ namespace Content.Client.Voting.UI
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
CloseButton.OnPressed += _ => Close();
VoteNotTrustedLabel.Text = Loc.GetString("ui-vote-trusted-users-notice", ("timeReq", _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime) / 60));
VoteNotTrustedLabel.Text = Loc.GetString("ui-vote-trusted-users-notice", ("timeReq", _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime)));
foreach (StandardVoteType voteType in Enum.GetValues<StandardVoteType>())
{
@@ -70,6 +73,7 @@ namespace Content.Client.Voting.UI
VoteTypeButton.AddItem(Loc.GetString(option.Name), (int)voteType);
}
_state.OnStateChanged += OnStateChanged;
VoteTypeButton.OnItemSelected += VoteTypeSelected;
CreateButton.OnPressed += CreatePressed;
FollowButton.OnPressed += FollowSelected;
@@ -101,6 +105,14 @@ namespace Content.Client.Voting.UI
UpdateVoteTimeout();
}
private void OnStateChanged(StateChangedEventArgs obj)
{
if (obj.NewState is not GameplayState)
return;
Close();
}
private void CanCallVoteChanged(bool obj)
{
if (!obj)

View File

@@ -19,11 +19,6 @@ public sealed class VotingSystem : EntitySystem
private void OnVotePlayerListResponseEvent(VotePlayerListResponseEvent msg)
{
if (!_ghostSystem.IsGhost)
{
return;
}
VotePlayerListResponse?.Invoke(msg);
}

View File

@@ -35,8 +35,10 @@ using Robust.Shared.Toolshed;
using Robust.Shared.Utility;
using System.Linq;
using Content.Server.Silicons.Laws;
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Laws.Components;
using Robust.Server.Player;
using Content.Shared.Silicons.StationAi;
using Robust.Shared.Physics.Components;
using static Content.Shared.Configurable.ConfigurationComponent;
@@ -345,7 +347,30 @@ namespace Content.Server.Administration.Systems
Impact = LogImpact.Low
});
if (TryComp<SiliconLawBoundComponent>(args.Target, out var lawBoundComponent))
// This logic is needed to be able to modify the AI's laws through its core and eye.
EntityUid? target = null;
SiliconLawBoundComponent? lawBoundComponent = null;
if (TryComp(args.Target, out lawBoundComponent))
{
target = args.Target;
}
// When inspecting the core we can find the entity with its laws by looking at the AiHolderComponent.
else if (TryComp<StationAiHolderComponent>(args.Target, out var holder) && holder.Slot.Item != null
&& TryComp(holder.Slot.Item, out lawBoundComponent))
{
target = holder.Slot.Item.Value;
// For the eye we can find the entity with its laws as the source of the movement relay since the eye
// is just a proxy for it to move around and look around the station.
}
else if (TryComp<MovementRelayTargetComponent>(args.Target, out var relay)
&& TryComp(relay.Source, out lawBoundComponent))
{
target = relay.Source;
}
if (lawBoundComponent != null && target != null)
{
args.Verbs.Add(new Verb()
{
@@ -359,7 +384,7 @@ namespace Content.Server.Administration.Systems
return;
}
_euiManager.OpenEui(ui, session);
ui.UpdateLaws(lawBoundComponent, args.Target);
ui.UpdateLaws(lawBoundComponent, target.Value);
},
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Actions/actions_borg.rsi"), "state-laws"),
});

View File

@@ -47,20 +47,23 @@ namespace Content.Server.Administration.Systems
[GeneratedRegex(@"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")]
private static partial Regex DiscordRegex();
private ISawmill _sawmill = default!;
private readonly HttpClient _httpClient = new();
private string _webhookUrl = string.Empty;
private WebhookData? _webhookData;
private string _onCallUrl = string.Empty;
private WebhookData? _onCallData;
private ISawmill _sawmill = default!;
private readonly HttpClient _httpClient = new();
private string _footerIconUrl = string.Empty;
private string _avatarUrl = string.Empty;
private string _serverName = string.Empty;
private readonly
Dictionary<NetUserId, (string? id, string username, string description, string? characterName, GameRunLevel
lastRunLevel)> _relayMessages = new();
private readonly Dictionary<NetUserId, DiscordRelayInteraction> _relayMessages = new();
private Dictionary<NetUserId, string> _oldMessageIds = new();
private readonly Dictionary<NetUserId, Queue<string>> _messageQueues = new();
private readonly Dictionary<NetUserId, Queue<DiscordRelayedData>> _messageQueues = new();
private readonly HashSet<NetUserId> _processingChannels = new();
private readonly Dictionary<NetUserId, (TimeSpan Timestamp, bool Typing)> _typingUpdateTimestamps = new();
private string _overrideClientName = string.Empty;
@@ -82,12 +85,16 @@ namespace Content.Server.Administration.Systems
public override void Initialize()
{
base.Initialize();
Subs.CVar(_config, CCVars.DiscordOnCallWebhook, OnCallChanged, true);
Subs.CVar(_config, CCVars.DiscordAHelpWebhook, OnWebhookChanged, true);
Subs.CVar(_config, CCVars.DiscordAHelpFooterIcon, OnFooterIconChanged, true);
Subs.CVar(_config, CCVars.DiscordAHelpAvatar, OnAvatarChanged, true);
Subs.CVar(_config, CVars.GameHostName, OnServerNameChanged, true);
Subs.CVar(_config, CCVars.AdminAhelpOverrideClientName, OnOverrideChanged, true);
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("AHELP");
var defaultParams = new AHelpMessageParams(
string.Empty,
string.Empty,
@@ -96,7 +103,7 @@ namespace Content.Server.Administration.Systems
_gameTicker.RunLevel,
playedSound: false
);
_maxAdditionalChars = GenerateAHelpMessage(defaultParams).Length;
_maxAdditionalChars = GenerateAHelpMessage(defaultParams).Message.Length;
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnGameRunLevelChanged);
@@ -111,6 +118,33 @@ namespace Content.Server.Administration.Systems
);
}
private async void OnCallChanged(string url)
{
_onCallUrl = url;
if (url == string.Empty)
return;
var match = DiscordRegex().Match(url);
if (!match.Success)
{
Log.Error("On call URL does not appear to be valid.");
return;
}
if (match.Groups.Count <= 2)
{
Log.Error("Could not get webhook ID or token for on call URL.");
return;
}
var webhookId = match.Groups[1].Value;
var webhookToken = match.Groups[2].Value;
_onCallData = await GetWebhookData(webhookId, webhookToken);
}
private void PlayerRateLimitedAction(ICommonSession obj)
{
RaiseNetworkEvent(
@@ -259,13 +293,13 @@ namespace Content.Server.Administration.Systems
// Store the Discord message IDs of the previous round
_oldMessageIds = new Dictionary<NetUserId, string>();
foreach (var message in _relayMessages)
foreach (var (user, interaction) in _relayMessages)
{
var id = message.Value.id;
var id = interaction.Id;
if (id == null)
return;
_oldMessageIds[message.Key] = id;
_oldMessageIds[user] = id;
}
_relayMessages.Clear();
@@ -330,10 +364,10 @@ namespace Content.Server.Administration.Systems
var webhookToken = match.Groups[2].Value;
// Fire and forget
await SetWebhookData(webhookId, webhookToken);
_webhookData = await GetWebhookData(webhookId, webhookToken);
}
private async Task SetWebhookData(string id, string token)
private async Task<WebhookData?> GetWebhookData(string id, string token)
{
var response = await _httpClient.GetAsync($"https://discord.com/api/v10/webhooks/{id}/{token}");
@@ -342,10 +376,10 @@ namespace Content.Server.Administration.Systems
{
_sawmill.Log(LogLevel.Error,
$"Discord returned bad status code when trying to get webhook data (perhaps the webhook URL is invalid?): {response.StatusCode}\nResponse: {content}");
return;
return null;
}
_webhookData = JsonSerializer.Deserialize<WebhookData>(content);
return JsonSerializer.Deserialize<WebhookData>(content);
}
private void OnFooterIconChanged(string url)
@@ -358,14 +392,14 @@ namespace Content.Server.Administration.Systems
_avatarUrl = url;
}
private async void ProcessQueue(NetUserId userId, Queue<string> messages)
private async void ProcessQueue(NetUserId userId, Queue<DiscordRelayedData> messages)
{
// Whether an embed already exists for this player
var exists = _relayMessages.TryGetValue(userId, out var existingEmbed);
// Whether the message will become too long after adding these new messages
var tooLong = exists && messages.Sum(msg => Math.Min(msg.Length, MessageLengthCap) + "\n".Length)
+ existingEmbed.description.Length > DescriptionMax;
var tooLong = exists && messages.Sum(msg => Math.Min(msg.Message.Length, MessageLengthCap) + "\n".Length)
+ existingEmbed?.Description.Length > DescriptionMax;
// If there is no existing embed, or it is getting too long, we create a new embed
if (!exists || tooLong)
@@ -385,10 +419,10 @@ namespace Content.Server.Administration.Systems
// If we have all the data required, we can link to the embed of the previous round or embed that was too long
if (_webhookData is { GuildId: { } guildId, ChannelId: { } channelId })
{
if (tooLong && existingEmbed.id != null)
if (tooLong && existingEmbed?.Id != null)
{
linkToPrevious =
$"**[Go to previous embed of this round](https://discord.com/channels/{guildId}/{channelId}/{existingEmbed.id})**\n";
$"**[Go to previous embed of this round](https://discord.com/channels/{guildId}/{channelId}/{existingEmbed.Id})**\n";
}
else if (_oldMessageIds.TryGetValue(userId, out var id) && !string.IsNullOrEmpty(id))
{
@@ -398,13 +432,22 @@ namespace Content.Server.Administration.Systems
}
var characterName = _minds.GetCharacterName(userId);
existingEmbed = (null, lookup.Username, linkToPrevious, characterName, _gameTicker.RunLevel);
existingEmbed = new DiscordRelayInteraction()
{
Id = null,
CharacterName = characterName,
Description = linkToPrevious,
Username = lookup.Username,
LastRunLevel = _gameTicker.RunLevel,
};
_relayMessages[userId] = existingEmbed;
}
// Previous message was in another RunLevel, so show that in the embed
if (existingEmbed.lastRunLevel != _gameTicker.RunLevel)
if (existingEmbed!.LastRunLevel != _gameTicker.RunLevel)
{
existingEmbed.description += _gameTicker.RunLevel switch
existingEmbed.Description += _gameTicker.RunLevel switch
{
GameRunLevel.PreRoundLobby => "\n\n:arrow_forward: _**Pre-round lobby started**_\n",
GameRunLevel.InRound => "\n\n:arrow_forward: _**Round started**_\n",
@@ -413,26 +456,35 @@ namespace Content.Server.Administration.Systems
$"{_gameTicker.RunLevel} was not matched."),
};
existingEmbed.lastRunLevel = _gameTicker.RunLevel;
existingEmbed.LastRunLevel = _gameTicker.RunLevel;
}
// If last message of the new batch is SOS then relay it to on-call.
// ... as long as it hasn't been relayed already.
var discordMention = messages.Last();
var onCallRelay = !discordMention.Receivers && !existingEmbed.OnCall;
// Add available messages to the embed description
while (messages.TryDequeue(out var message))
{
// In case someone thinks they're funny
if (message.Length > MessageLengthCap)
message = message[..(MessageLengthCap - TooLongText.Length)] + TooLongText;
string text;
existingEmbed.description += $"\n{message}";
// In case someone thinks they're funny
if (message.Message.Length > MessageLengthCap)
text = message.Message[..(MessageLengthCap - TooLongText.Length)] + TooLongText;
else
text = message.Message;
existingEmbed.Description += $"\n{text}";
}
var payload = GeneratePayload(existingEmbed.description,
existingEmbed.username,
existingEmbed.characterName);
var payload = GeneratePayload(existingEmbed.Description,
existingEmbed.Username,
existingEmbed.CharacterName);
// If there is no existing embed, create a new one
// Otherwise patch (edit) it
if (existingEmbed.id == null)
if (existingEmbed.Id == null)
{
var request = await _httpClient.PostAsync($"{_webhookUrl}?wait=true",
new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
@@ -455,11 +507,11 @@ namespace Content.Server.Administration.Systems
return;
}
existingEmbed.id = id.ToString();
existingEmbed.Id = id.ToString();
}
else
{
var request = await _httpClient.PatchAsync($"{_webhookUrl}/messages/{existingEmbed.id}",
var request = await _httpClient.PatchAsync($"{_webhookUrl}/messages/{existingEmbed.Id}",
new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
if (!request.IsSuccessStatusCode)
@@ -474,6 +526,43 @@ namespace Content.Server.Administration.Systems
_relayMessages[userId] = existingEmbed;
// Actually do the on call relay last, we just need to grab it before we dequeue every message above.
if (onCallRelay &&
_onCallData != null)
{
existingEmbed.OnCall = true;
var roleMention = _config.GetCVar(CCVars.DiscordAhelpMention);
if (!string.IsNullOrEmpty(roleMention))
{
var message = new StringBuilder();
message.AppendLine($"<@&{roleMention}>");
message.AppendLine("Unanswered SOS");
// Need webhook data to get the correct link for that channel rather than on-call data.
if (_webhookData is { GuildId: { } guildId, ChannelId: { } channelId })
{
message.AppendLine(
$"**[Go to ahelp](https://discord.com/channels/{guildId}/{channelId}/{existingEmbed.Id})**");
}
payload = GeneratePayload(message.ToString(), existingEmbed.Username, existingEmbed.CharacterName);
var request = await _httpClient.PostAsync($"{_onCallUrl}?wait=true",
new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
var content = await request.Content.ReadAsStringAsync();
if (!request.IsSuccessStatusCode)
{
_sawmill.Log(LogLevel.Error, $"Discord returned bad status code when posting relay message (perhaps the message is too long?): {request.StatusCode}\nResponse: {content}");
}
}
}
else
{
existingEmbed.OnCall = false;
}
_processingChannels.Remove(userId);
}
@@ -652,7 +741,7 @@ namespace Content.Server.Administration.Systems
if (sendsWebhook)
{
if (!_messageQueues.ContainsKey(msg.UserId))
_messageQueues[msg.UserId] = new Queue<string>();
_messageQueues[msg.UserId] = new Queue<DiscordRelayedData>();
var str = message.Text;
var unameLength = senderSession.Name.Length;
@@ -701,7 +790,7 @@ namespace Content.Server.Administration.Systems
.ToList();
}
private static string GenerateAHelpMessage(AHelpMessageParams parameters)
private static DiscordRelayedData GenerateAHelpMessage(AHelpMessageParams parameters)
{
var stringbuilder = new StringBuilder();
@@ -718,13 +807,57 @@ namespace Content.Server.Administration.Systems
stringbuilder.Append($" **{parameters.RoundTime}**");
if (!parameters.PlayedSound)
stringbuilder.Append(" **(S)**");
if (parameters.Icon == null)
stringbuilder.Append($" **{parameters.Username}:** ");
else
stringbuilder.Append($" **{parameters.Username}** ");
stringbuilder.Append(parameters.Message);
return stringbuilder.ToString();
return new DiscordRelayedData()
{
Receivers = !parameters.NoReceivers,
Message = stringbuilder.ToString(),
};
}
private record struct DiscordRelayedData
{
/// <summary>
/// Was anyone online to receive it.
/// </summary>
public bool Receivers;
/// <summary>
/// What's the payload to send to discord.
/// </summary>
public string Message;
}
/// <summary>
/// Class specifically for holding information regarding existing Discord embeds
/// </summary>
private sealed class DiscordRelayInteraction
{
public string? Id;
public string Username = String.Empty;
public string? CharacterName;
/// <summary>
/// Contents for the discord message.
/// </summary>
public string Description = string.Empty;
/// <summary>
/// Run level of the last interaction. If different we'll link to the last Id.
/// </summary>
public GameRunLevel LastRunLevel;
/// <summary>
/// Did we relay this interaction to OnCall previously.
/// </summary>
public bool OnCall;
}
}

View File

@@ -55,7 +55,7 @@ public sealed partial class AmeControllerComponent : SharedAmeControllerComponen
/// </summary>
[DataField("injectSound")]
[ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier InjectSound = new SoundCollectionSpecifier("MetalThud");
public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Machines/ame_fuelinjection.ogg");
/// <summary>
/// The last time this could have injected fuel into the AME.

View File

@@ -680,7 +680,10 @@ public sealed class PlantHolderSystem : EntitySystem
if (TryComp<HandsComponent>(user, out var hands))
{
if (!_botany.CanHarvest(component.Seed, hands.ActiveHandEntity))
{
_popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user);
return false;
}
}
else if (!_botany.CanHarvest(component.Seed))
{

View File

@@ -39,7 +39,7 @@ public sealed class ExaminableDamageSystem : EntitySystem
var level = GetDamageLevel(uid, component);
var msg = Loc.GetString(messages[level]);
args.PushMarkup(msg);
args.PushMarkup(msg,-99);
}
private int GetDamageLevel(EntityUid uid, ExaminableDamageComponent? component = null,

View File

@@ -46,8 +46,8 @@ public sealed class DoorSystem : SharedDoorSystem
SetBoltsDown(ent, true);
}
UpdateBoltLightStatus(ent);
ent.Comp.Powered = args.Powered;
Dirty(ent, ent.Comp);
UpdateBoltLightStatus(ent);
}
}

View File

@@ -172,7 +172,8 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
// TODO: AntagCodewordsComponent
private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args)
{
args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
if(comp.GiveCodewords)
args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
}
// TODO: figure out how to handle this? add priority to briefing event?

View File

@@ -49,12 +49,9 @@ namespace Content.Server.Nutrition.EntitySystems
{
_puddle.TrySpillAt(uid, solution, out _, false);
}
if (foodComp.Trash.Count == 0)
foreach (var trash in foodComp.Trash)
{
foreach (var trash in foodComp.Trash)
{
EntityManager.SpawnEntity(trash, Transform(uid).Coordinates);
}
EntityManager.SpawnEntity(trash, Transform(uid).Coordinates);
}
}
ActivatePayload(uid);

View File

@@ -10,6 +10,7 @@ using Content.Server.Shuttles.Events;
using Content.Server.Shuttles.Systems;
using Content.Shared.Atmos;
using Content.Shared.Decals;
using Content.Shared.Ghost;
using Content.Shared.Gravity;
using Content.Shared.Parallax.Biomes;
using Content.Shared.Parallax.Biomes.Layers;
@@ -51,6 +52,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
private EntityQuery<BiomeComponent> _biomeQuery;
private EntityQuery<FixturesComponent> _fixturesQuery;
private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<TransformComponent> _xformQuery;
private readonly HashSet<EntityUid> _handledEntities = new();
@@ -81,6 +83,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
Log.Level = LogLevel.Debug;
_biomeQuery = GetEntityQuery<BiomeComponent>();
_fixturesQuery = GetEntityQuery<FixturesComponent>();
_ghostQuery = GetEntityQuery<GhostComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
SubscribeLocalEvent<BiomeComponent, MapInitEvent>(OnBiomeMapInit);
SubscribeLocalEvent<FTLStartedEvent>(OnFTLStarted);
@@ -315,6 +318,11 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
}
}
private bool CanLoad(EntityUid uid)
{
return !_ghostQuery.HasComp(uid);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -332,7 +340,8 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
if (_xformQuery.TryGetComponent(pSession.AttachedEntity, out var xform) &&
_handledEntities.Add(pSession.AttachedEntity.Value) &&
_biomeQuery.TryGetComponent(xform.MapUid, out var biome) &&
biome.Enabled)
biome.Enabled &&
CanLoad(pSession.AttachedEntity.Value))
{
var worldPos = _transform.GetWorldPosition(xform);
AddChunksInRange(biome, worldPos);
@@ -349,7 +358,8 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
if (!_handledEntities.Add(viewer) ||
!_xformQuery.TryGetComponent(viewer, out xform) ||
!_biomeQuery.TryGetComponent(xform.MapUid, out biome) ||
!biome.Enabled)
!biome.Enabled ||
!CanLoad(viewer))
{
continue;
}

View File

@@ -30,7 +30,7 @@ public sealed class RadioDeviceSystem : EntitySystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
// Used to prevent a shitter from using a bunch of radios to spam chat.
private HashSet<(string, EntityUid)> _recentlySent = new();
private HashSet<(string, EntityUid, RadioChannelPrototype)> _recentlySent = new();
public override void Initialize()
{
@@ -114,7 +114,7 @@ public sealed class RadioDeviceSystem : EntitySystem
{
if (args.Powered)
return;
SetMicrophoneEnabled(uid, null, false, true, component);
SetMicrophoneEnabled(uid, null, false, true, component);
}
public void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
@@ -191,8 +191,9 @@ public sealed class RadioDeviceSystem : EntitySystem
if (HasComp<RadioSpeakerComponent>(args.Source))
return; // no feedback loops please.
if (_recentlySent.Add((args.Message, args.Source)))
_radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel), uid);
var channel = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel)!;
if (_recentlySent.Add((args.Message, args.Source, channel)))
_radio.SendRadioMessage(args.Source, args.Message, channel, uid);
}
private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args)
@@ -279,7 +280,7 @@ public sealed class RadioDeviceSystem : EntitySystem
if (TryComp<RadioMicrophoneComponent>(ent, out var mic))
mic.BroadcastChannel = channel;
if (TryComp<RadioSpeakerComponent>(ent, out var speaker))
speaker.Channels = new(){ channel };
speaker.Channels = new() { channel };
Dirty(ent);
}
}

View File

@@ -1,4 +1,4 @@
using Content.Server.Administration.Managers;
using Content.Server.Administration.Managers;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Eui;
@@ -52,8 +52,8 @@ public sealed class SiliconLawEui : BaseEui
return;
var player = _entityManager.GetEntity(message.Target);
_siliconLawSystem.SetLaws(message.Laws, player);
if (_entityManager.TryGetComponent<SiliconLawProviderComponent>(player, out var playerProviderComp))
_siliconLawSystem.SetLaws(message.Laws, player, playerProviderComp.LawUploadSound);
}
private bool IsAllowed()

View File

@@ -21,6 +21,8 @@ using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
namespace Content.Server.Silicons.Laws;
@@ -113,7 +115,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
component.Lawset = args.Lawset;
// gotta tell player to check their laws
NotifyLawsChanged(uid);
NotifyLawsChanged(uid, component.LawUploadSound);
// new laws may allow antagonist behaviour so make it clear for admins
if (TryComp<EmagSiliconLawComponent>(uid, out var emag))
@@ -149,14 +151,11 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
return;
base.OnGotEmagged(uid, component, ref args);
NotifyLawsChanged(uid);
NotifyLawsChanged(uid, component.EmaggedSound);
EnsureEmaggedRole(uid, component);
_stunSystem.TryParalyze(uid, component.StunTime, true);
if (!_mind.TryGetMind(uid, out var mindId, out _))
return;
_roles.MindPlaySound(mindId, component.EmaggedSound);
}
private void OnEmagMindAdded(EntityUid uid, EmagSiliconLawComponent component, MindAddedMessage args)
@@ -237,7 +236,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
return ev.Laws;
}
public void NotifyLawsChanged(EntityUid uid)
public void NotifyLawsChanged(EntityUid uid, SoundSpecifier? cue = null)
{
if (!TryComp<ActorComponent>(uid, out var actor))
return;
@@ -245,6 +244,9 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
var msg = Loc.GetString("laws-update-notify");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
if (cue != null && _mind.TryGetMind(uid, out var mindId, out _))
_roles.MindPlaySound(mindId, cue);
}
/// <summary>
@@ -269,7 +271,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
/// <summary>
/// Set the laws of a silicon entity while notifying the player.
/// </summary>
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target)
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null)
{
if (!TryComp<SiliconLawProviderComponent>(target, out var component))
return;
@@ -278,7 +280,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
component.Lawset = new SiliconLawset();
component.Lawset.Laws = newLaws;
NotifyLawsChanged(target);
NotifyLawsChanged(target, cue);
}
protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
@@ -292,9 +294,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
while (query.MoveNext(out var update))
{
SetLaws(lawset, update);
if (provider.LawUploadSound != null && _mind.TryGetMind(update, out var mindId, out _))
_roles.MindPlaySound(mindId, provider.LawUploadSound);
SetLaws(lawset, update, provider.LawUploadSound);
}
}
}

View File

@@ -32,6 +32,7 @@ namespace Content.Server.Voting.Managers
private VotingSystem? _votingSystem;
private RoleSystem? _roleSystem;
private GameTicker? _gameTicker;
private static readonly Dictionary<StandardVoteType, CVarDef<bool>> _voteTypesToEnableCVars = new()
{
@@ -70,8 +71,8 @@ namespace Content.Server.Voting.Managers
default:
throw new ArgumentOutOfRangeException(nameof(voteType), voteType, null);
}
var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
ticker.UpdateInfoText();
_gameTicker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
_gameTicker.UpdateInfoText();
if (timeoutVote)
TimeoutStandardVote(voteType);
}
@@ -346,8 +347,14 @@ namespace Content.Server.Voting.Managers
return;
}
var voterEligibility = _cfg.GetCVar(CCVars.VotekickVoterGhostRequirement) ? VoterEligibility.GhostMinimumPlaytime : VoterEligibility.MinimumPlaytime;
if (_cfg.GetCVar(CCVars.VotekickIgnoreGhostReqInLobby) && _gameTicker!.RunLevel == GameRunLevel.PreRoundLobby)
voterEligibility = VoterEligibility.MinimumPlaytime;
var eligibleVoterNumberRequirement = _cfg.GetCVar(CCVars.VotekickEligibleNumberRequirement);
var eligibleVoterNumber = _cfg.GetCVar(CCVars.VotekickVoterGhostRequirement) ? CalculateEligibleVoterNumber(VoterEligibility.GhostMinimumPlaytime) : CalculateEligibleVoterNumber(VoterEligibility.MinimumPlaytime);
var eligibleVoterNumber = CalculateEligibleVoterNumber(voterEligibility);
string target = args[0];
string reason = args[1];
@@ -441,7 +448,7 @@ namespace Content.Server.Voting.Managers
},
Duration = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VotekickTimer)),
InitiatorTimeout = TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.VotekickTimeout)),
VoterEligibility = _cfg.GetCVar(CCVars.VotekickVoterGhostRequirement) ? VoterEligibility.GhostMinimumPlaytime : VoterEligibility.MinimumPlaytime,
VoterEligibility = voterEligibility,
DisplayVotes = false,
TargetEntity = targetNetEntity
};

View File

@@ -24,7 +24,6 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Voting.Managers
{
public sealed partial class VoteManager : IVoteManager
@@ -40,7 +39,7 @@ namespace Content.Server.Voting.Managers
[Dependency] private readonly IGameMapManager _gameMapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly ISharedPlaytimeManager _playtimeManager = default!;
[Dependency] private readonly ISharedPlaytimeManager _playtimeManager = default!;
private int _nextVoteId = 1;
@@ -282,7 +281,7 @@ namespace Content.Server.Voting.Managers
}
// Admin always see the vote count, even if the vote is set to hide it.
if (_adminMgr.HasAdminFlag(player, AdminFlags.Moderator))
if (v.DisplayVotes || _adminMgr.HasAdminFlag(player, AdminFlags.Moderator))
{
msg.DisplayVotes = true;
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Administration.Managers;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.Ghost;
using Content.Server.Roles.Jobs;
using Content.Shared.CCVar;
@@ -12,6 +13,7 @@ using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using System.Threading.Tasks;
using Content.Shared.Players.PlayTimeTracking;
namespace Content.Server.Voting;
@@ -24,6 +26,8 @@ public sealed class VotingSystem : EntitySystem
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly ISharedPlaytimeManager _playtimeManager = default!;
public override void Initialize()
{
@@ -34,8 +38,7 @@ public sealed class VotingSystem : EntitySystem
private async void OnVotePlayerListRequestEvent(VotePlayerListRequestEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { Valid: true } entity
|| !await CheckVotekickInitEligibility(args.SenderSession))
if (!await CheckVotekickInitEligibility(args.SenderSession))
{
var deniedResponse = new VotePlayerListResponseEvent(new (NetUserId, NetEntity, string)[0], true);
RaiseNetworkEvent(deniedResponse, args.SenderSession.Channel);
@@ -46,17 +49,23 @@ public sealed class VotingSystem : EntitySystem
foreach (var player in _playerManager.Sessions)
{
if (player.AttachedEntity is not { Valid: true } attached)
continue;
if (attached == entity) continue;
if (args.SenderSession == player) continue;
if (_adminManager.IsAdmin(player, false)) continue;
var playerName = GetPlayerVoteListName(attached);
var netEntity = GetNetEntity(attached);
if (player.AttachedEntity is not { Valid: true } attached)
{
var playerName = player.Name;
var netEntity = NetEntity.Invalid;
players.Add((player.UserId, netEntity, playerName));
}
else
{
var playerName = GetPlayerVoteListName(attached);
var netEntity = GetNetEntity(attached);
players.Add((player.UserId, netEntity, playerName));
players.Add((player.UserId, netEntity, playerName));
}
}
var response = new VotePlayerListResponseEvent(players.ToArray(), false);
@@ -86,22 +95,29 @@ public sealed class VotingSystem : EntitySystem
if (initiator.AttachedEntity != null && _adminManager.IsAdmin(initiator.AttachedEntity.Value, false))
return true;
if (_cfg.GetCVar(CCVars.VotekickInitiatorGhostRequirement))
// If cvar enabled, skip the ghost requirement in the preround lobby
if (!_cfg.GetCVar(CCVars.VotekickIgnoreGhostReqInLobby) || (_cfg.GetCVar(CCVars.VotekickIgnoreGhostReqInLobby) && _gameTicker.RunLevel != GameRunLevel.PreRoundLobby))
{
// Must be ghost
if (!TryComp(initiator.AttachedEntity, out GhostComponent? ghostComp))
return false;
if (_cfg.GetCVar(CCVars.VotekickInitiatorGhostRequirement))
{
// Must be ghost
if (!TryComp(initiator.AttachedEntity, out GhostComponent? ghostComp))
return false;
// Must have been dead for x seconds
if ((int)_gameTiming.RealTime.Subtract(ghostComp.TimeOfDeath).TotalSeconds < _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime))
return false;
// Must have been dead for x seconds
if ((int)_gameTiming.RealTime.Subtract(ghostComp.TimeOfDeath).TotalSeconds < _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime))
return false;
}
}
// Must be whitelisted
if (!await _dbManager.GetWhitelistStatusAsync(initiator.UserId))
if (!await _dbManager.GetWhitelistStatusAsync(initiator.UserId) && _cfg.GetCVar(CCVars.VotekickInitiatorWhitelistedRequirement))
return false;
return true;
// Must be eligible to vote
var playtime = _playtimeManager.GetPlayTimes(initiator);
return playtime.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out TimeSpan overallTime) && (overallTime >= TimeSpan.FromHours(_cfg.GetCVar(CCVars.VotekickEligibleVoterPlaytime))
|| !_cfg.GetCVar(CCVars.VotekickInitiatorTimeRequirement));
}
/// <summary>

View File

@@ -461,6 +461,18 @@ namespace Content.Shared.CCVar
* Discord
*/
/// <summary>
/// The role that will get mentioned if a new SOS ahelp comes in.
/// </summary>
public static readonly CVarDef<string> DiscordAhelpMention =
CVarDef.Create("discord.on_call_ping", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
/// <summary>
/// URL of the discord webhook to relay unanswered ahelp messages.
/// </summary>
public static readonly CVarDef<string> DiscordOnCallWebhook =
CVarDef.Create("discord.on_call_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
/// <summary>
/// URL of the Discord webhook which will relay all ahelp messages.
/// </summary>
@@ -1472,7 +1484,7 @@ namespace Content.Shared.CCVar
/// Config for when the votekick should be allowed to be called based on number of eligible voters.
/// </summary>
public static readonly CVarDef<int> VotekickEligibleNumberRequirement =
CVarDef.Create("votekick.eligible_number", 10, CVar.SERVERONLY);
CVarDef.Create("votekick.eligible_number", 5, CVar.SERVERONLY);
/// <summary>
/// Whether a votekick initiator must be a ghost or not.
@@ -1480,6 +1492,18 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<bool> VotekickInitiatorGhostRequirement =
CVarDef.Create("votekick.initiator_ghost_requirement", true, CVar.SERVERONLY);
/// <summary>
/// Should the initiator be whitelisted to initiate a votekick?
/// </summary>
public static readonly CVarDef<bool> VotekickInitiatorWhitelistedRequirement =
CVarDef.Create("votekick.initiator_whitelist_requirement", true, CVar.SERVERONLY);
/// <summary>
/// Should the initiator be able to start a votekick if they are bellow the votekick.voter_playtime requirement?
/// </summary>
public static readonly CVarDef<bool> VotekickInitiatorTimeRequirement =
CVarDef.Create("votekick.initiator_time_requirement", false, CVar.SERVERONLY);
/// <summary>
/// Whether a votekick voter must be a ghost or not.
/// </summary>
@@ -1496,7 +1520,7 @@ namespace Content.Shared.CCVar
/// Config for how many seconds a player must have been dead to initiate a votekick / be able to vote on a votekick.
/// </summary>
public static readonly CVarDef<int> VotekickEligibleVoterDeathtime =
CVarDef.Create("votekick.voter_deathtime", 180, CVar.REPLICATED | CVar.SERVER);
CVarDef.Create("votekick.voter_deathtime", 30, CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// The required ratio of eligible voters that must agree for a votekick to go through.
@@ -1540,6 +1564,12 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<int> VotekickBanDuration =
CVarDef.Create("votekick.ban_duration", 180, CVar.SERVERONLY);
/// <summary>
/// Whether the ghost requirement settings for votekicks should be ignored for the lobby.
/// </summary>
public static readonly CVarDef<bool> VotekickIgnoreGhostReqInLobby =
CVarDef.Create("votekick.ignore_ghost_req_in_lobby", true, CVar.SERVERONLY);
/*
* BAN
*/

View File

@@ -33,7 +33,7 @@ public sealed partial class CartridgeLoaderComponent : Component
/// The maximum amount of programs that can be installed on the cartridge loader entity
/// </summary>
[DataField]
public int DiskSpace = 5;
public int DiskSpace = 8;
/// <summary>
/// Controls whether the cartridge loader will play notifications if it supports it at all

View File

@@ -0,0 +1,21 @@
namespace Content.Shared.Chemistry.Components;
/// <summary>
/// Represents a container that also contains a solution.
/// This means that reactive entities react when inserted into the container.
/// </summary>
[RegisterComponent]
public sealed partial class ReactiveContainerComponent : Component
{
/// <summary>
/// The container that holds the solution.
/// </summary>
[DataField(required: true)]
public string Container = default!;
/// <summary>
/// The solution in the container.
/// </summary>
[DataField(required: true)]
public string Solution = default!;
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Robust.Shared.Containers;
namespace Content.Shared.Chemistry.EntitySystems;
public sealed class ReactiveContainerSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReactiveContainerComponent, EntInsertedIntoContainerMessage>(OnInserted);
SubscribeLocalEvent<ReactiveContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
}
private void OnInserted(EntityUid uid, ReactiveContainerComponent comp, EntInsertedIntoContainerMessage args)
{
// Only reactive entities can react with the solution
if (!HasComp<ReactiveComponent>(args.Entity))
return;
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
return;
if (solution.Volume == 0)
return;
_reactiveSystem.DoEntityReaction(args.Entity, solution, ReactionMethod.Touch);
}
private void OnSolutionChange(EntityUid uid, ReactiveContainerComponent comp, SolutionContainerChangedEvent args)
{
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
return;
if (solution.Volume == 0)
return;
if (!TryComp<ContainerManagerComponent>(uid, out var manager))
return;
if (!_containerSystem.TryGetContainer(uid, comp.Container, out var container))
return;
foreach (var entity in container.ContainedEntities)
{
if (!HasComp<ReactiveComponent>(entity))
continue;
_reactiveSystem.DoEntityReaction(entity, solution, ReactionMethod.Touch);
}
}
}

View File

@@ -60,6 +60,7 @@ namespace Content.Shared.Containers.ItemSlots
}
#region ComponentManagement
/// <summary>
/// Spawn in starting items for any item slots that should have one.
/// </summary>
@@ -70,7 +71,8 @@ namespace Content.Shared.Containers.ItemSlots
if (slot.HasItem || string.IsNullOrEmpty(slot.StartingItem))
continue;
var item = EntityManager.SpawnEntity(slot.StartingItem, EntityManager.GetComponent<TransformComponent>(uid).Coordinates);
var item = Spawn(slot.StartingItem, Transform(uid).Coordinates);
if (slot.ContainerSlot != null)
_containers.Insert(item, slot.ContainerSlot);
}
@@ -99,7 +101,8 @@ namespace Content.Shared.Containers.ItemSlots
if (itemSlots.Slots.TryGetValue(id, out var existing))
{
if (existing.Local)
Log.Error($"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(uid).EntityName} ({uid}), key: {id}");
Log.Error(
$"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(uid).EntityName} ({uid}), key: {id}");
else
// server state takes priority
slot.CopyFrom(existing);
@@ -134,7 +137,10 @@ namespace Content.Shared.Containers.ItemSlots
Dirty(uid, itemSlots);
}
public bool TryGetSlot(EntityUid uid, string slotId, [NotNullWhen(true)] out ItemSlot? itemSlot, ItemSlotsComponent? component = null)
public bool TryGetSlot(EntityUid uid,
string slotId,
[NotNullWhen(true)] out ItemSlot? itemSlot,
ItemSlotsComponent? component = null)
{
itemSlot = null;
@@ -143,9 +149,11 @@ namespace Content.Shared.Containers.ItemSlots
return component.Slots.TryGetValue(slotId, out itemSlot);
}
#endregion
#region Interactions
/// <summary>
/// Attempt to take an item from a slot, if any are set to EjectOnInteract.
/// </summary>
@@ -201,20 +209,50 @@ namespace Content.Shared.Containers.ItemSlots
if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands))
return;
if (itemSlots.Slots.Count == 0)
return;
// If any slot can be inserted into don't show popup.
// If any whitelist passes, but slot is locked, then show locked.
// If whitelist fails all, show whitelist fail.
// valid, insertable slots (if any)
var slots = new List<ItemSlot>();
string? whitelistFailPopup = null;
string? lockedFailPopup = null;
foreach (var slot in itemSlots.Slots.Values)
{
if (!slot.InsertOnInteract)
continue;
if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User))
continue;
if (CanInsert(uid, args.Used, args.User, slot, slot.Swap))
{
slots.Add(slot);
}
else
{
var allowed = CanInsertWhitelist(args.Used, slot);
if (lockedFailPopup == null && slot.LockedFailPopup != null && allowed && slot.Locked)
lockedFailPopup = slot.LockedFailPopup;
slots.Add(slot);
if (whitelistFailPopup == null && slot.WhitelistFailPopup != null)
whitelistFailPopup = slot.WhitelistFailPopup;
}
}
if (slots.Count == 0)
{
// it's a bit weird that the popupMessage is stored with the item slots themselves, but in practice
// the popup messages will just all be the same, so it's probably fine.
//
// doing a check to make sure that they're all the same or something is probably frivolous
if (lockedFailPopup != null)
_popupSystem.PopupClient(Loc.GetString(lockedFailPopup), uid, args.User);
else if (whitelistFailPopup != null)
_popupSystem.PopupClient(Loc.GetString(whitelistFailPopup), uid, args.User);
return;
}
// Drop the held item onto the floor. Return if the user cannot drop.
if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands))
@@ -236,23 +274,31 @@ namespace Content.Shared.Containers.ItemSlots
return;
}
}
#endregion
#region Insert
/// <summary>
/// Insert an item into a slot. This does not perform checks, so make sure to also use <see
/// cref="CanInsert"/> or just use <see cref="TryInsert"/> instead.
/// </summary>
/// <param name="excludeUserAudio">If true, will exclude the user when playing sound. Does nothing client-side.
/// Useful for predicted interactions</param>
private void Insert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user, bool excludeUserAudio = false)
private void Insert(EntityUid uid,
ItemSlot slot,
EntityUid item,
EntityUid? user,
bool excludeUserAudio = false)
{
bool? inserted = slot.ContainerSlot != null ? _containers.Insert(item, slot.ContainerSlot) : null;
// ContainerSlot automatically raises a directed EntInsertedIntoContainerMessage
// Logging
if (inserted != null && inserted.Value && user != null)
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user.Value)} inserted {ToPrettyString(item)} into {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(user.Value)} inserted {ToPrettyString(item)} into {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_audioSystem.PlayPredicted(slot.InsertSound, uid, excludeUserAudio ? user : null);
}
@@ -261,46 +307,53 @@ namespace Content.Shared.Containers.ItemSlots
/// Check whether a given item can be inserted into a slot. Unless otherwise specified, this will return
/// false if the slot is already filled.
/// </summary>
/// <remarks>
/// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to
/// pass the whitelist or due to slot being locked, then this will generate an appropriate popup.
/// </remarks>
public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlot slot, bool swap = false, EntityUid? popup = null)
public bool CanInsert(EntityUid uid,
EntityUid usedUid,
EntityUid? user,
ItemSlot slot,
bool swap = false)
{
if (slot.ContainerSlot == null)
return false;
if (_whitelistSystem.IsWhitelistFail(slot.Whitelist, usedUid) || _whitelistSystem.IsBlacklistPass(slot.Blacklist, usedUid))
{
if (popup.HasValue && slot.WhitelistFailPopup.HasValue)
_popupSystem.PopupClient(Loc.GetString(slot.WhitelistFailPopup), uid, popup.Value);
if (slot.HasItem && (!swap || swap && !CanEject(uid, user, slot)))
return false;
if (!CanInsertWhitelist(usedUid, slot))
return false;
}
if (slot.Locked)
{
if (popup.HasValue && slot.LockedFailPopup.HasValue)
_popupSystem.PopupClient(Loc.GetString(slot.LockedFailPopup), uid, popup.Value);
return false;
}
if (slot.HasItem && (!swap || (swap && !CanEject(uid, user, slot))))
return false;
var ev = new ItemSlotInsertAttemptEvent(uid, usedUid, user, slot);
RaiseLocalEvent(uid, ref ev);
RaiseLocalEvent(usedUid, ref ev);
if (ev.Cancelled)
{
return false;
}
return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: swap);
}
private bool CanInsertWhitelist(EntityUid usedUid, ItemSlot slot)
{
if (_whitelistSystem.IsWhitelistFail(slot.Whitelist, usedUid)
|| _whitelistSystem.IsBlacklistPass(slot.Blacklist, usedUid))
return false;
return true;
}
/// <summary>
/// Tries to insert item into a specific slot.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsert(EntityUid uid, string id, EntityUid item, EntityUid? user, ItemSlotsComponent? itemSlots = null, bool excludeUserAudio = false)
public bool TryInsert(EntityUid uid,
string id,
EntityUid item,
EntityUid? user,
ItemSlotsComponent? itemSlots = null,
bool excludeUserAudio = false)
{
if (!Resolve(uid, ref itemSlots))
return false;
@@ -315,7 +368,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Tries to insert item into a specific slot.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user, bool excludeUserAudio = false)
public bool TryInsert(EntityUid uid,
ItemSlot slot,
EntityUid item,
EntityUid? user,
bool excludeUserAudio = false)
{
if (!CanInsert(uid, item, user, slot))
return false;
@@ -329,7 +386,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Does not check action blockers.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, HandsComponent? hands = null, bool excludeUserAudio = false)
public bool TryInsertFromHand(EntityUid uid,
ItemSlot slot,
EntityUid user,
HandsComponent? hands = null,
bool excludeUserAudio = false)
{
if (!Resolve(user, ref hands, false))
return false;
@@ -443,6 +504,7 @@ namespace Content.Shared.Containers.ItemSlots
return 1;
}
#endregion
#region Eject
@@ -462,7 +524,7 @@ namespace Content.Shared.Containers.ItemSlots
return false;
}
if (slot.ContainerSlot?.ContainedEntity is not {} item)
if (slot.ContainerSlot?.ContainedEntity is not { } item)
return false;
var ev = new ItemSlotEjectAttemptEvent(uid, item, user, slot);
@@ -487,7 +549,9 @@ namespace Content.Shared.Containers.ItemSlots
// Logging
if (ejected != null && ejected.Value && user != null)
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user.Value)} ejected {ToPrettyString(item)} from {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(user.Value)} ejected {ToPrettyString(item)} from {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_audioSystem.PlayPredicted(slot.EjectSound, uid, excludeUserAudio ? user : null);
}
@@ -496,7 +560,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Try to eject an item from a slot.
/// </summary>
/// <returns>False if item slot is locked or has no item inserted</returns>
public bool TryEject(EntityUid uid, ItemSlot slot, EntityUid? user, [NotNullWhen(true)] out EntityUid? item, bool excludeUserAudio = false)
public bool TryEject(EntityUid uid,
ItemSlot slot,
EntityUid? user,
[NotNullWhen(true)] out EntityUid? item,
bool excludeUserAudio = false)
{
item = null;
@@ -518,8 +586,12 @@ namespace Content.Shared.Containers.ItemSlots
/// Try to eject item from a slot.
/// </summary>
/// <returns>False if the id is not valid, the item slot is locked, or it has no item inserted</returns>
public bool TryEject(EntityUid uid, string id, EntityUid? user,
[NotNullWhen(true)] out EntityUid? item, ItemSlotsComponent? itemSlots = null, bool excludeUserAudio = false)
public bool TryEject(EntityUid uid,
string id,
EntityUid? user,
[NotNullWhen(true)] out EntityUid? item,
ItemSlotsComponent? itemSlots = null,
bool excludeUserAudio = false)
{
item = null;
@@ -550,12 +622,16 @@ namespace Content.Shared.Containers.ItemSlots
return true;
}
#endregion
#region Verbs
private void AddAlternativeVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent<AlternativeVerb> args)
private void AddAlternativeVerbs(EntityUid uid,
ItemSlotsComponent itemSlots,
GetVerbsEvent<AlternativeVerb> args)
{
if (args.Hands == null || !args.CanAccess ||!args.CanInteract)
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
{
return;
}
@@ -649,7 +725,9 @@ namespace Content.Shared.Containers.ItemSlots
}
}
private void AddInteractionVerbsVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent<InteractionVerb> args)
private void AddInteractionVerbsVerbs(EntityUid uid,
ItemSlotsComponent itemSlots,
GetVerbsEvent<InteractionVerb> args)
{
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
return;
@@ -708,7 +786,7 @@ namespace Content.Shared.Containers.ItemSlots
new SpriteSpecifier.Texture(
new ResPath("/Textures/Interface/VerbIcons/insert.svg.192dpi.png"));
}
else if(slot.EjectOnInteract)
else if (slot.EjectOnInteract)
{
// Inserting/ejecting is a primary interaction for this entity. Instead of using the insert
// category, we will use a single "Place <item>" verb.
@@ -727,9 +805,11 @@ namespace Content.Shared.Containers.ItemSlots
args.Verbs.Add(insertVerb);
}
}
#endregion
#region BUIs
private void HandleButtonPressed(EntityUid uid, ItemSlotsComponent component, ItemSlotButtonPressedEvent args)
{
if (!component.Slots.TryGetValue(args.SlotId, out var slot))
@@ -740,6 +820,7 @@ namespace Content.Shared.Containers.ItemSlots
else if (args.TryInsert && !slot.HasItem)
TryInsertFromHand(uid, slot, args.Actor);
}
#endregion
/// <summary>

View File

@@ -47,7 +47,7 @@ public abstract partial class InventorySystem
private void OnEntRemoved(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
var unequippedEvent = new DidUnequipEvent(uid, args.Entity, slotDef);
@@ -59,8 +59,8 @@ public abstract partial class InventorySystem
private void OnEntInserted(EntityUid uid, InventoryComponent component, EntInsertedIntoContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
var equippedEvent = new DidEquipEvent(uid, args.Entity, slotDef);
RaiseLocalEvent(uid, equippedEvent, true);
@@ -118,7 +118,7 @@ public abstract partial class InventorySystem
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter:true);
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter: true);
}
public bool TryEquip(EntityUid uid, EntityUid itemUid, string slot, bool silent = false, bool force = false, bool predicted = false,
@@ -365,6 +365,25 @@ public abstract partial class InventorySystem
ClothingComponent? clothing = null,
bool reparent = true,
bool checkDoafter = false)
{
var itemsDropped = 0;
return TryUnequip(actor, target, slot, out removedItem, ref itemsDropped,
silent, force, predicted, inventory, clothing, reparent, checkDoafter);
}
private bool TryUnequip(
EntityUid actor,
EntityUid target,
string slot,
[NotNullWhen(true)] out EntityUid? removedItem,
ref int itemsDropped,
bool silent = false,
bool force = false,
bool predicted = false,
InventoryComponent? inventory = null,
ClothingComponent? clothing = null,
bool reparent = true,
bool checkDoafter = false)
{
removedItem = null;
@@ -423,17 +442,27 @@ public abstract partial class InventorySystem
return false;
}
if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent))
return false;
// this is in order to keep track of whether this is the first instance of a recursion call
var firstRun = itemsDropped == 0;
++itemsDropped;
foreach (var slotDef in inventory.Slots)
{
if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name)
{
//this recursive call might be risky
TryUnequip(actor, target, slotDef.Name, true, true, predicted, inventory, reparent: reparent);
TryUnequip(actor, target, slotDef.Name, out _, ref itemsDropped, true, true, predicted, inventory, reparent: reparent);
}
}
if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent))
return false;
// we check if any items were dropped, and make a popup if they were.
// the reason we check for > 1 is because the first item is always the one we are trying to unequip,
// whereas we only want to notify for extra dropped items.
if (!silent && _gameTiming.IsFirstTimePredicted && firstRun && itemsDropped > 1)
_popup.PopupClient(Loc.GetString("inventory-component-dropped-from-unequip", ("items", itemsDropped - 1)), target, target);
// TODO: Inventory needs a hot cleanup hoo boy
// Check if something else (AKA toggleable) dumped it into a container.
@@ -466,7 +495,7 @@ public abstract partial class InventorySystem
if ((containerSlot == null || slotDefinition == null) && !TryGetSlotContainer(target, slot, out containerSlot, out slotDefinition, inventory))
return false;
if (containerSlot.ContainedEntity is not {} itemUid)
if (containerSlot.ContainedEntity is not { } itemUid)
return false;
if (!_containerSystem.CanRemove(itemUid, containerSlot))

View File

@@ -81,6 +81,24 @@ public sealed partial class NpcFactionSystem : EntitySystem
return ent.Comp.Factions.Contains(faction);
}
/// <summary>
/// Returns whether an entity is a member of any listed faction.
/// If the list is empty this returns false.
/// </summary>
public bool IsMemberOfAny(Entity<NpcFactionMemberComponent?> ent, IEnumerable<ProtoId<NpcFactionPrototype>> factions)
{
if (!Resolve(ent, ref ent.Comp, false))
return false;
foreach (var faction in factions)
{
if (ent.Comp.Factions.Contains(faction))
return true;
}
return false;
}
/// <summary>
/// Adds this entity to the particular faction.
/// </summary>

View File

@@ -33,18 +33,10 @@ public sealed class HungerSystem : EntitySystem
[ValidatePrototypeId<SatiationIconPrototype>]
private const string HungerIconStarvingId = "HungerIconStarving";
private SatiationIconPrototype? _hungerIconOverfed;
private SatiationIconPrototype? _hungerIconPeckish;
private SatiationIconPrototype? _hungerIconStarving;
public override void Initialize()
{
base.Initialize();
DebugTools.Assert(_prototype.TryIndex(HungerIconOverfedId, out _hungerIconOverfed) &&
_prototype.TryIndex(HungerIconPeckishId, out _hungerIconPeckish) &&
_prototype.TryIndex(HungerIconStarvingId, out _hungerIconStarving));
SubscribeLocalEvent<HungerComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<HungerComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<HungerComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
@@ -221,13 +213,13 @@ public sealed class HungerSystem : EntitySystem
switch (component.CurrentThreshold)
{
case HungerThreshold.Overfed:
prototype = _hungerIconOverfed;
_prototype.TryIndex(HungerIconOverfedId, out prototype);
break;
case HungerThreshold.Peckish:
prototype = _hungerIconPeckish;
_prototype.TryIndex(HungerIconPeckishId, out prototype);
break;
case HungerThreshold.Starving:
prototype = _hungerIconStarving;
_prototype.TryIndex(HungerIconStarvingId, out prototype);
break;
default:
prototype = null;

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Nutrition.EntitySystems;
@@ -31,18 +32,10 @@ public sealed class ThirstSystem : EntitySystem
[ValidatePrototypeId<SatiationIconPrototype>]
private const string ThirstIconParchedId = "ThirstIconParched";
private SatiationIconPrototype? _thirstIconOverhydrated = null;
private SatiationIconPrototype? _thirstIconThirsty = null;
private SatiationIconPrototype? _thirstIconParched = null;
public override void Initialize()
{
base.Initialize();
DebugTools.Assert(_prototype.TryIndex(ThirstIconOverhydratedId, out _thirstIconOverhydrated) &&
_prototype.TryIndex(ThirstIconThirstyId, out _thirstIconThirsty) &&
_prototype.TryIndex(ThirstIconParchedId, out _thirstIconParched));
SubscribeLocalEvent<ThirstComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<ThirstComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ThirstComponent, RejuvenateEvent>(OnRejuvenate);
@@ -128,26 +121,28 @@ public sealed class ThirstSystem : EntitySystem
}
}
public bool TryGetStatusIconPrototype(ThirstComponent component, out SatiationIconPrototype? prototype)
public bool TryGetStatusIconPrototype(ThirstComponent component, [NotNullWhen(true)] out SatiationIconPrototype? prototype)
{
switch (component.CurrentThirstThreshold)
{
case ThirstThreshold.OverHydrated:
prototype = _thirstIconOverhydrated;
return true;
_prototype.TryIndex(ThirstIconOverhydratedId, out prototype);
break;
case ThirstThreshold.Thirsty:
prototype = _thirstIconThirsty;
return true;
_prototype.TryIndex(ThirstIconThirstyId, out prototype);
break;
case ThirstThreshold.Parched:
prototype = _thirstIconParched;
return true;
_prototype.TryIndex(ThirstIconParchedId, out prototype);
break;
default:
prototype = null;
return false;
break;
}
return prototype != null;
}
private void UpdateEffects(EntityUid uid, ThirstComponent component)

View File

@@ -2,7 +2,6 @@ using Content.Shared.Damage.Prototypes;
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Overlays;
@@ -15,8 +14,11 @@ public sealed partial class ShowHealthBarsComponent : Component
/// <summary>
/// Displays health bars of the damage containers.
/// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
public List<string> DamageContainers = new();
[DataField]
public List<ProtoId<DamageContainerPrototype>> DamageContainers = new()
{
"Biological"
};
[DataField]
public ProtoId<HealthIconPrototype>? HealthStatusIcon = "HealthIconFine";

View File

@@ -1,6 +1,6 @@
using Content.Shared.Damage.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Prototypes;
namespace Content.Shared.Overlays;
@@ -13,6 +13,9 @@ public sealed partial class ShowHealthIconsComponent : Component
/// <summary>
/// Displays health status icons of the damage containers.
/// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
public List<string> DamageContainers = new();
[DataField]
public List<ProtoId<DamageContainerPrototype>> DamageContainers = new()
{
"Biological"
};
}

View File

@@ -24,7 +24,7 @@ public sealed partial class SiliconLawProviderComponent : Component
/// <summary>
/// The sound that plays for the Silicon player
/// when the particular lawboard has been inserted.
/// when the law change is processed for the provider.
/// </summary>
[DataField]
public SoundSpecifier? LawUploadSound = new SoundPathSpecifier("/Audio/Misc/cryo_warning.ogg");

View File

@@ -324,6 +324,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
if (TryComp(user, out EyeComponent? eyeComp))
{
_eye.SetDrawFov(user, false, eyeComp);
_eye.SetTarget(user, ent.Comp.RemoteEntity.Value, eyeComp);
}
@@ -356,6 +357,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
if (TryComp(args.Entity, out EyeComponent? eyeComp))
{
_eye.SetDrawFov(args.Entity, true, eyeComp);
_eye.SetTarget(args.Entity, null, eyeComp);
}
ClearEye(ent);

View File

@@ -3,6 +3,7 @@ using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
@@ -125,7 +126,7 @@ public sealed class WieldableSystem : EntitySystem
private void OnExamine(EntityUid uid, GunWieldBonusComponent component, ref ExaminedEvent args)
{
if (HasComp<GunRequiresWieldComponent>(uid))
if (HasComp<GunRequiresWieldComponent>(uid))
return;
if (component.WieldBonusExamineMessage != null)
@@ -253,7 +254,7 @@ public sealed class WieldableSystem : EntitySystem
return false;
var selfMessage = Loc.GetString("wieldable-component-successful-wield", ("item", used));
var othersMessage = Loc.GetString("wieldable-component-successful-wield-other", ("user", user), ("item", used));
var othersMessage = Loc.GetString("wieldable-component-successful-wield-other", ("user", Identity.Entity(user, EntityManager)), ("item", used));
_popupSystem.PopupPredicted(selfMessage, othersMessage, user, user);
var targEv = new ItemWieldedEvent();
@@ -298,7 +299,7 @@ public sealed class WieldableSystem : EntitySystem
_audioSystem.PlayPredicted(component.UnwieldSound, uid, args.User);
var selfMessage = Loc.GetString("wieldable-component-failed-wield", ("item", uid));
var othersMessage = Loc.GetString("wieldable-component-failed-wield-other", ("user", args.User.Value), ("item", uid));
var othersMessage = Loc.GetString("wieldable-component-failed-wield-other", ("user", Identity.Entity(args.User.Value, EntityManager)), ("item", uid));
_popupSystem.PopupPredicted(selfMessage, othersMessage, args.User.Value, args.User.Value);
}

Binary file not shown.

View File

@@ -181,3 +181,8 @@
license: "CC0-1.0"
copyright: "by ScarKy0"
source: "https://github.com/space-wizards/space-station-14/pull/32012"
- files: ["ame_fuelinjection.ogg"]
license: "CC0-1.0"
copyright: "by AftrLite (Github). Uses audio from hypospray.ogg and hiss.ogg (Found in Resources/Audio/Items)"
source: "https://github.com/space-wizards/space-station-14/pull/33097"

View File

@@ -567,5 +567,28 @@ Entries:
id: 70
time: '2024-10-16T22:24:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32844
- author: BramvanZijp
changes:
- message: CC, ERT, Admin, and Deathsquad PDA's now have all departmental programs
pre-installed.
type: Tweak
id: 71
time: '2024-10-31T14:53:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32601
- author: metalgearsloth
changes:
- message: Added on-call functionality for discord relay to get notified on unanswered
ahelps.
type: Add
id: 72
time: '2024-11-02T09:29:16.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30443
- author: nikthechampiongr
changes:
- message: It is now possible to edit the AI's laws through its core and eye.
type: Fix
id: 73
time: '2024-11-02T13:21:10.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32461
Name: Admin
Order: 1

View File

@@ -1,364 +1,4 @@
Entries:
- author: Errant
changes:
- message: Medical Mask sprite now works on vox.
type: Fix
id: 7052
time: '2024-08-07T03:41:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30702
- author: Lyroth001
changes:
- message: Dragons are immune to flashes
type: Tweak
id: 7053
time: '2024-08-07T07:42:00.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30658
- author: ShadowCommander
changes:
- message: Rollerbeds can now be dragged to the player to fold and pick them up.
type: Add
id: 7054
time: '2024-08-07T09:19:10.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30002
- author: Errant
changes:
- message: Survivors arriving via the Unknown Shuttle event, ERT and CBURN agents,
and Death Squad members are now equipped with the appropriate species-specific
survival gear.
type: Fix
- message: Unknown Shuttle event can once again spawn vox characters.
type: Tweak
id: 7055
time: '2024-08-07T09:26:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29746
- author: IProduceWidgets
changes:
- message: butter is slippery
type: Tweak
id: 7056
time: '2024-08-07T21:47:03.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29772
- author: Mervill
changes:
- message: Gas Miners now have detailed examine text
type: Tweak
id: 7057
time: '2024-08-08T02:14:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30480
- author: BackeTako
changes:
- message: "!, \u203D and multiple punctuations now work for Spanish."
type: Tweak
id: 7058
time: '2024-08-08T03:08:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30551
- author: strO0pwafel
changes:
- message: Fixed inconsistent naming of CentComm.
type: Fix
id: 7059
time: '2024-08-08T10:04:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29217
- author: Plykiya
changes:
- message: Buffed the range of EMP implants from a radius of 1.75 tiles to 2.75
tiles.
type: Tweak
- message: Buffed the range of EMP grenades from a radius of 4 tiles to 5.5 tiles.
type: Tweak
id: 7060
time: '2024-08-08T10:04:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30660
- author: Plykiya
changes:
- message: You can drop food or drinks from your hands to interrupt eating it, again.
type: Fix
- message: Barber scissors are now interrupted if the item is dropped or if the
user changes hands.
type: Tweak
id: 7061
time: '2024-08-08T11:39:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30361
- author: TheShuEd
changes:
- message: Add "thieving beacon" to Thief antag - a device that counts objects within
a radius of itself as stolen.
type: Add
- message: Return thief structures stealing objectives.
type: Add
- message: Animal theft objectives can no longer appear if the animals are not on
the station.
type: Fix
id: 7062
time: '2024-08-08T13:17:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29997
- author: lzk228
changes:
- message: RD labcoat added in RD's dresser.
type: Add
id: 7063
time: '2024-08-08T22:50:57.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30671
- author: SlamBamActionman
changes:
- message: Animal DNA now shows up as "unknown DNA" in the Forensic Scanner.
type: Tweak
- message: Forensic Scanner can now scan fluid containers for DNA in reagents.
type: Tweak
- message: Fluids keep their DNA data when moved.
type: Fix
- message: Fluids now stain containers they're in with DNA. Make sure to scrub your
blood bucket after use!
type: Add
- message: Vomit now includes DNA!
type: Add
id: 7064
time: '2024-08-08T23:27:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/26699
- author: themias
changes:
- message: Butter can be sliced, cookie and toast recipes now use butter slices.
type: Tweak
- message: Chefvend butter reduced from 4 to 3.
type: Tweak
id: 7065
time: '2024-08-08T23:32:42.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30789
- author: EmoGarbage404
changes:
- message: You should have significantly less friction when moving in space.
type: Tweak
id: 7066
time: '2024-08-09T04:52:25.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29383
- author: Plykiya
changes:
- message: Hardsuits and EVA suits now count as protection for unscrewing lightbulbs.
type: Add
- message: More gloves were given the ability to unscrew light bulbs.
type: Add
- message: Behonkers no longer hurt you when melee attacking them or interacting
with them.
type: Remove
id: 7067
time: '2024-08-09T05:32:41.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30244
- author: Ian321
changes:
- message: The warden is now an important job.
type: Tweak
id: 7068
time: '2024-08-09T05:45:51.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30745
- author: slarticodefast
changes:
- message: Added tooltips to the agent ID job icons
type: Add
id: 7069
time: '2024-08-09T06:14:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28575
- author: stalengd
changes:
- message: Head bandana no longer blocks food eating.
type: Fix
id: 7070
time: '2024-08-09T06:17:51.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28910
- author: Ubaser
changes:
- message: Oxygen and nitrogen canisters now have new sprites when worn.
type: Add
id: 7071
time: '2024-08-09T10:32:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30809
- author: Plykiya
changes:
- message: Buckling someone now triggers a short do-after.
type: Tweak
id: 7072
time: '2024-08-09T15:43:02.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29621
- author: Ubaser
changes:
- message: Normal crowbars cannot be placed in pockets, but can now fit in belts.
type: Tweak
- message: Depending on where you obtained the crowbars, they will now have different
colours.
type: Tweak
id: 7073
time: '2024-08-09T19:29:00.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28988
- author: lzk228
changes:
- message: Hotplate works again.
type: Fix
id: 7074
time: '2024-08-10T01:10:39.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30830
- author: EmoGarbage404
changes:
- message: Maintenance closets have more variety in what they can contain.
type: Tweak
id: 7075
time: '2024-08-10T02:12:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30579
- author: Ko4erga
changes:
- message: Changed chemistry airlock color.
type: Tweak
id: 7076
time: '2024-08-10T03:23:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30666
- author: Blackern5000
changes:
- message: Bent pipes now deal 8 thrown damage instead of 3.
type: Tweak
id: 7077
time: '2024-08-10T03:30:43.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30634
- author: shampunj
changes:
- message: Rat king can now wideswing
type: Tweak
id: 7078
time: '2024-08-10T12:47:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30808
- author: BackeTako
changes:
- message: Added a suitskirt for the psychologist
type: Add
id: 7079
time: '2024-08-10T12:51:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30709
- author: Unkn0wnGh0st333
changes:
- message: ERT Chaplain starting gear was fixed and will no longer give the ERT
Engineer gear
type: Fix
id: 7080
time: '2024-08-10T12:55:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30855
- author: Ubaser
changes:
- message: Light tube structures now have new sprites.
type: Tweak
id: 7081
time: '2024-08-10T15:00:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29091
- author: lzk228
changes:
- message: Standartized some clothing recipes.
type: Tweak
id: 7082
time: '2024-08-10T18:16:56.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29315
- author: TheShuEd
changes:
- message: You cat cut burger bun into two halfs, and make custom burgers now!
type: Add
id: 7083
time: '2024-08-10T19:31:32.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30755
- author: thetolbean
changes:
- message: Updated Core's boxing ring beacon label to be correct
type: Tweak
id: 7084
time: '2024-08-10T19:50:08.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30800
- author: Flareguy
changes:
- message: Added vox sprites for most of the remaining common mask items.
type: Add
id: 7085
time: '2024-08-10T21:06:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30838
- author: Scribbles0, Plykiya, nikthechampiongr
changes:
- message: You can now execute incapacitated/cuffed entities or yourself with certain
sharp weapons. Executions require a do-after, deal 9x the damage of a normal
attack, and ignore damage resistances.
type: Add
id: 7086
time: '2024-08-11T03:05:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30104
- author: Plykiya
changes:
- message: Cooking shelves have been renamed to Kitchen shelves, you can now put
more drinks and cutlery in them.
type: Tweak
id: 7087
time: '2024-08-11T06:21:34.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30858
- author: BombasterDS
changes:
- message: You can now change the suit's sensors on incapacitated people without
taking it off
type: Tweak
id: 7088
time: '2024-08-11T09:04:43.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29668
- author: lzk228
changes:
- message: RCD UI can be activated only in hand.
type: Tweak
id: 7089
time: '2024-08-11T09:15:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30861
- author: TokenStyle
changes:
- message: Pax reagent now refresh pacified.
type: Tweak
- message: Pax reagent pacified duration has been changed from 2 seconds to 4 seconds.
type: Tweak
id: 7090
time: '2024-08-11T09:19:01.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30778
- author: osjarw
changes:
- message: You can once again pull and buckle pets.
type: Fix
id: 7091
time: '2024-08-11T09:22:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30066
- author: slarticodefast
changes:
- message: The sentient plant mutation can no longer be propagated to other trays
via seeds or swabs. It has also been made more rare.
type: Tweak
id: 7092
time: '2024-08-11T09:23:14.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29133
- author: Moomoobeef
changes:
- message: Added The Throngler (Plushie) to the grand lottery!!
type: Add
id: 7093
time: '2024-08-11T10:26:37.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29978
- author: Plykiya
changes:
- message: Energy sword weapons are now execution-capable.
type: Add
id: 7094
time: '2024-08-11T22:12:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30909
- author: Blackern5000
changes:
- message: Added diamond-tipped mining drills, a science printed item found under
T2 industrial.
type: Add
- message: Standard mining drills now attack faster and break rocks in two hits.
type: Tweak
- message: Moved ore bags of holding to T2 industrial tech.
type: Tweak
- message: Grappling guns are now researched with Salvage Equipment.
type: Tweak
- message: Diamonds are now worth 2,000 spesos each.
type: Tweak
id: 7095
time: '2024-08-12T01:49:09.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/30814
- author: mirrorcult
changes:
- message: With space law being overhauled, you can now examine items to see their
@@ -3959,3 +3599,348 @@
id: 7551
time: '2024-10-24T03:41:03.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32965
- author: slarticodefast
changes:
- message: Mix 1u aluminium, 1u potassium and 1u sulfur for a flash reaction effect.
The radius scales with the reagent amount.
type: Add
id: 7552
time: '2024-10-25T22:47:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32377
- author: BramvanZijp
changes:
- message: Fixed the Lone Nuclear Operative mid-round antagonist being extremely
rare.
type: Fix
id: 7553
time: '2024-10-26T02:16:45.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32942
- author: Moomoobeef
changes:
- message: Bowls no longer make an eating sound when drinking from them.
type: Fix
id: 7554
time: '2024-10-26T04:00:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32819
- author: SaphireLattice
changes:
- message: Added a warning about unrevivability in the health analyzer UI.
type: Add
id: 7555
time: '2024-10-26T17:22:09.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32636
- author: slarticodefast
changes:
- message: Fixed pie throwing sound not playing.
type: Fix
id: 7556
time: '2024-10-27T04:25:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33017
- author: stalengd
changes:
- message: Fixed playtime labels not being able to correctly display time greater
than 24 hours
type: Fix
id: 7557
time: '2024-10-28T18:00:00.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32974
- author: august-sun
changes:
- message: Extended the minimum round time for meteor swarm events.
type: Tweak
id: 7558
time: '2024-10-28T21:25:34.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32876
- author: deltanedas
changes:
- message: Fixed lava planet expeditions not working.
type: Fix
id: 7559
time: '2024-10-29T05:00:29.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33042
- author: metalgearsloth
changes:
- message: Fix separated game screen bumping slightly.
type: Fix
id: 7560
time: '2024-10-29T05:07:57.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33046
- author: Blackern5000
changes:
- message: Proto-kitentic crushers, glaives, and daggers now have more accurate
inhand sprites.
type: Tweak
id: 7561
time: '2024-10-30T07:38:19.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32212
- author: Blackern5000
changes:
- message: Security belts now contain a holobarrier projector and a handheld security
radio by default rather than tear gas and a flashbang.
type: Tweak
id: 7562
time: '2024-10-30T07:40:33.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32291
- author: Blackern5000
changes:
- message: Added three bottle boxes to the nanomed plus inventory for doctors to
carry small amounts of chemicals on their person
type: Add
id: 7563
time: '2024-10-30T07:41:51.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33018
- author: Blackern5000
changes:
- message: Added the interdyne defibrillator, a black-and-red defibrillator that
can be used as a melee weapon.
type: Add
- message: The syndicate medical bundle now contains an interdyne defibrillator,
a collection of various instant injectors, tourniquets, and several combat kits.
The price has been raised to 24 tc.
type: Tweak
id: 7564
time: '2024-10-30T09:15:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32720
- author: Boaz1111
changes:
- message: Pill bottles can now only store pills.
type: Tweak
id: 7565
time: '2024-10-31T10:56:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33074
- author: Jarmer123
changes:
- message: You can now find a spare bible in the PietyVend
type: Add
id: 7566
time: '2024-10-31T13:26:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32363
- author: justinbrick
changes:
- message: Added a pop-up notification when extra items are dropped while unequipping
something.
type: Tweak
id: 7567
time: '2024-10-31T14:12:26.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33078
- author: BramvanZijp
changes:
- message: The maximum amount of programs that can be installed on a PDA has been
increased from 5 to 8
type: Tweak
- message: The Detective and Head of Security now get the logprobe program pre-installed
on their PDA.
type: Tweak
id: 7568
time: '2024-10-31T14:53:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32601
- author: Psychpsyo
changes:
- message: Carp plushies can now be placed in mop buckets, along with other rehydratable
things like monkey cubes.
type: Add
id: 7569
time: '2024-10-31T18:46:19.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33079
- author: Bhijn and Myr
changes:
- message: Tail thumping has been downmixed to mono to fix the sound lacking any
sort of positioning. They're now capable of having a presence in the actual
soundspace, in turn meaning lizards are no longer occupying your headset at
all times of day.
type: Fix
id: 7570
time: '2024-10-31T21:30:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33092
- author: reesque
changes:
- message: pie not dropping tin on thrown
type: Fix
id: 7571
time: '2024-11-01T01:43:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33013
- author: SlamBamActionman
changes:
- message: Votekicks can now be initiated during the pregame lobby.
type: Fix
id: 7572
time: '2024-11-01T01:52:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32528
- author: PopGamer46
changes:
- message: Fixed bolt lights of recently unpowered bolted doors
type: Fix
id: 7573
time: '2024-11-01T02:04:09.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33063
- author: RumiTiger
changes:
- message: A chocolate and banana muffin has been added to the game. The berry and
cherry muffins have also been resprited!
type: Add
- message: Now you can make a regular, chocolate, banana, and berry muffin!
type: Tweak
- message: Muffin tins have been added to the game. They are needed for making muffins!
type: Add
id: 7574
time: '2024-11-01T02:06:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/29318
- author: ScarKy0
changes:
- message: AI can no longer toggle seeing jobs off.
type: Tweak
- message: Borgs can no longer see mindshield status.
type: Fix
id: 7575
time: '2024-11-01T02:32:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33069
- author: Minemoder5000
changes:
- message: The cargo shuttle's cargo pallets can no longer sell or buy.
type: Fix
id: 7576
time: '2024-11-01T06:22:39.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33022
- author: aspiringLich
changes:
- message: Fixed the logic triggering popups when inserting items into machines.
type: Fix
id: 7577
time: '2024-11-02T01:33:26.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/28856
- author: K-Dynamic
changes:
- message: Pills are explosion resistant.
type: Tweak
id: 7578
time: '2024-11-02T09:51:45.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32458
- author: K-Dynamic
changes:
- message: Handcrafted gauze now takes 3 seconds instead of 10 seconds of crafting
type: Tweak
- message: Medical techfab gauze recipe now takes 1 cloth instead of 2
type: Tweak
id: 7579
time: '2024-11-02T09:53:19.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32744
- author: Ubaser
changes:
- message: Service workers can now be antagonists.
type: Fix
id: 7580
time: '2024-11-02T10:07:52.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31359
- author: AftrLite
changes:
- message: The AME now has a new sound effect for fuel injection!
type: Add
id: 7581
time: '2024-11-02T13:19:33.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33097
- author: deltanedas
changes:
- message: Printing cables in an autolathe is now 20 times faster.
type: Tweak
id: 7582
time: '2024-11-02T13:24:08.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31521
- author: Centronias
changes:
- message: Fixed a bug where attempting to speak into a handheld radio and an intercom
simultaneously would lead to only one device transmitting the message.
type: Fix
id: 7583
time: '2024-11-02T15:04:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32737
- author: joshepvodka
changes:
- message: Headphones are now selectable in loadouts.
type: Add
id: 7584
time: '2024-11-02T15:12:26.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33067
- author: Plykiya
changes:
- message: Wielding weapons no longer displays your character's real identity when
your identity is hidden.
type: Fix
id: 7585
time: '2024-11-03T11:25:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33134
- author: MilenVolf
changes:
- message: Ghost roles menu is now collapsible! This makes it much easier to find
a specific role that you want.
type: Tweak
id: 7586
time: '2024-11-04T00:49:42.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32717
- author: Tr1bute
changes:
- message: A popup now appears when attempting to harvest mutated plants that require
sharp tools.
type: Add
id: 7587
time: '2024-11-04T00:56:41.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33136
- author: Boolean-Buckeye
changes:
- message: Emergency lights can be constructed and deconstructed
type: Add
id: 7588
time: '2024-11-04T03:24:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32945
- author: thetolbean
changes:
- message: The web coat and boots can now be broken down into silk.
type: Fix
id: 7589
time: '2024-11-04T03:33:51.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33121
- author: Kickguy223
changes:
- message: Ion Storms now notify a Borg that their laws have changed via a sound
cue.
type: Add
id: 7590
time: '2024-11-04T11:23:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32887
- author: DrSmugleaf
changes:
- message: Fixed not being able to see vote counts for non-votekick votes.
type: Fix
id: 7591
time: '2024-11-05T00:12:29.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33170
- author: kirus59
changes:
- message: Fixed display of icons when using hunger or thirst huds
type: Fix
- message: Fixed errors spam when using beer goggles
type: Fix
id: 7592
time: '2024-11-05T16:58:23.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32832
- author: Errant
changes:
- message: Syndicate Reinforcements no longer list their (hidden and unused) codewords
on the Round End window.
type: Fix
id: 7593
time: '2024-11-05T18:18:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33181
- author: Baptr0b0t
changes:
- message: Fixed Intelicard can see through walls.
type: Fix
id: 7594
time: '2024-11-06T03:28:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33177
- author: slarticodefast
changes:
- message: Removed the wanted list cartridge from the captain's PDA.
type: Remove
id: 7595
time: '2024-11-06T13:20:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33084

File diff suppressed because one or more lines are too long

View File

@@ -31,3 +31,4 @@ plant-holder-component-heat-improper-warning = The [color=orange]improper temper
plant-holder-component-pressure-improper-warning = The [color=lightblue]improper environment pressure alert[/color] is blinking.
plant-holder-component-gas-missing-warning = The [color=cyan]improper gas environment alert[/color] is blinking.
plant-holder-component-early-sample-message = The plant hasn't grown enough to take a sample yet.
plant-holder-component-ligneous-cant-harvest-message = The plant is too tough to harvest with your bare hands.

View File

@@ -14,6 +14,7 @@ ghost-target-window-current-button = Warp: {$name}
ghost-target-window-warp-to-most-followed = Warp to Most Followed
ghost-roles-window-title = Ghost Roles
ghost-roles-window-available-button = Available ({$rolesCount})
ghost-roles-window-join-raffle-button = Join raffle
ghost-roles-window-raffle-in-progress-button =
Join raffle ({$time} left, { $players ->

View File

@@ -2,3 +2,9 @@ inventory-component-can-equip-cannot = You can't equip this!
inventory-component-can-equip-does-not-fit = This doesn't fit!
inventory-component-can-unequip-cannot = You can't unequip this!
inventory-component-dropped-from-unequip =
You dropped {$items ->
[1] an item!
*[other] some items!
}

View File

@@ -1,5 +1,6 @@
# mop bucket
mop-bucket-slot-component-slot-name-shark = Shark
mop-bucket-slot-component-slot-name-item = Item
mop-bucket-slot-component-eject-verb = Take out
# janitorial trolley
janitorial-trolley-slot-component-slot-name-plunger = Plunger
janitorial-trolley-slot-component-slot-name-sign = Sign

View File

@@ -216,8 +216,8 @@ uplink-chemistry-kit-desc = A starter kit for the aspiring chemist, includes tox
uplink-knives-kit-name = Throwing Knives Kit
uplink-knives-kit-desc = A set of 4 syndicate branded throwing knives, perfect for embedding into the body of your victims.
uplink-meds-bundle-name = Medical Bundle
uplink-meds-bundle-desc = All you need to get your comrades back in the fight: mainly a combat medkit, a defibrillator and three combat medipens.
uplink-meds-bundle-name = Interdyne Medical Bundle
uplink-meds-bundle-desc = An assortment of autoinjectors and premium medical equipment to cover for every possible situation. Contains an elite compact defibrillator that can be used as a weapon.
uplink-ammo-bundle-name = Ammo Bundle
uplink-ammo-bundle-desc = Reloading! Contains 4 magazines for the C-20r, 4 drums for the Bulldog, and 2 ammo boxes for the L6 SAW.

View File

@@ -25,8 +25,8 @@ ui-vote-type-not-available = This vote type has been disabled
# Vote option only available for specific users.
ui-vote-trusted-users-notice =
This vote option is only available to whitelisted players.
In addition, you must have been a ghost for { $timeReq } minutes.
This vote option is only available to players who have enough playtime or are whitelisted.
In addition, you must have been a ghost for { $timeReq } seconds.
# Warning to not abuse a specific vote option.
ui-vote-abuse-warning =

View File

@@ -2,13 +2,14 @@
# Shown when examining the window. Each entry represents the window's health condition
comp-window-damaged-1 = It looks fully intact.
comp-window-damaged-2 = It has a few scratches
comp-window-damaged-2 = It has a few scratches.
comp-window-damaged-3 = It has a few small cracks.
comp-window-damaged-4 = It has several big cracks running along its surface.
comp-window-damaged-5 = It has deep cracks across multiple layers.
comp-window-damaged-6 = It's extremely cracked and on the verge of shattering.
comp-window-damaged-4 = [color=yellow]It has several big cracks running along its surface.[/color]
comp-window-damaged-5 = [color=orange]It has deep cracks across multiple layers.[/color]
comp-window-damaged-6 = [color=red]It's extremely cracked and on the verge of shattering.[/color]
### Interaction Messages
# Shown when knocking on a window
comp-window-knock = *knock knock*

View File

@@ -6576,7 +6576,7 @@ entities:
- uid: 2031
components:
- type: Transform
pos: 3.6114278,-10.732791
pos: 4.0109396,-12.223828
parent: 104
- uid: 2095
components:
@@ -10993,6 +10993,13 @@ entities:
- type: Transform
pos: 11.5,-6.5
parent: 104
- proto: HandLabeler
entities:
- uid: 1826
components:
- type: Transform
pos: 3.4542694,-10.616036
parent: 104
- proto: KitchenKnife
entities:
- uid: 1061

View File

@@ -5,35 +5,20 @@
description: Sends your eye back to the core.
components:
- type: InstantAction
priority: -10
priority: -9
itemIconStyle: BigAction
icon:
sprite: Interface/Actions/actions_ai.rsi
state: ai_core
event: !type:JumpToCoreEvent
- type: entity
id: ActionShowJobIcons
name: Show job icons
description: Shows job icons for crew members.
components:
- type: InstantAction
priority: -5
itemIconStyle: BigAction
icon:
sprite: Interface/Actions/actions_ai.rsi
state: job_view
event: !type:ActionComponentChangeEvent
components:
- type: ShowJobIcons
- type: entity
id: ActionSurvCameraLights
name: Toggle camera lights
description: Enable surveillance camera lights near wherever you're viewing.
components:
- type: InstantAction
priority: -6
priority: -5
itemIconStyle: BigAction
icon:
sprite: Interface/Actions/actions_ai.rsi

View File

@@ -302,13 +302,19 @@
components:
- type: StorageFill
contents:
- id: DefibrillatorSyndicate
- id: MedkitCombatFilled
- id: Defibrillator
amount: 4
- id: Tourniquet
amount: 4
- id: CombatMedipen
amount: 3
- id: ClothingHandsGlovesNitrile
- id: SyringeTranexamicAcid
- id: SyringeHyronalin
amount: 4
- id: PunctAutoInjector
amount: 4
- id: PyraAutoInjector
amount: 4
- id: AirlossAutoInjector
amount: 4
- type: entity
parent: ClothingBackpackDuffelSyndicateBundle

View File

@@ -46,10 +46,10 @@
table: !type:AllSelector
children:
- id: Stunbaton
- id: GrenadeFlashBang
- id: TearGasGrenade
- id: Handcuffs
- id: Handcuffs
- id: HoloprojectorSecurity
- id: RadioHandheldSecurity
- type: entity
id: ClothingBeltSecurityFilled

View File

@@ -20,6 +20,7 @@
BoxCandle: 2
BoxCandleSmall: 2
Urn: 5
Bible: 1
emaggedInventory:
ClothingOuterArmorCult: 1
ClothingHeadHelmetCult: 1

View File

@@ -11,6 +11,7 @@
FoodPlate: 10
FoodPlateSmall: 10
FoodPlateTin: 5
FoodPlateMuffinTin: 5
FoodKebabSkewer: 5
DrinkGlass: 5
Beaker: 5

View File

@@ -7,6 +7,7 @@
Bloodpack: 5
EpinephrineChemistryBottle: 3
Syringe: 5
BoxBottle: 3
ClothingEyesHudMedical: 2
ClothingEyesEyepatchHudMedical: 2

View File

@@ -699,9 +699,9 @@
productEntity: ClothingBackpackDuffelSyndicateMedicalBundleFilled
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 12
Telecrystal: 16
cost:
Telecrystal: 20
Telecrystal: 24
categories:
- UplinkChemicals
conditions:

View File

@@ -487,7 +487,6 @@
- CartridgeAmmo
- DoorRemote
- Whistle
- HolosignProjector
- BalloonPopper
- type: ItemMapper
mapLayers:

View File

@@ -13,11 +13,7 @@
categories: [ HideSpawnMenu ]
components:
- type: ShowHealthBars
damageContainers:
- Biological
- type: ShowHealthIcons
damageContainers:
- Biological
- type: entity
parent: ClothingEyesBase
@@ -223,8 +219,6 @@
sprite: Clothing/Eyes/Hud/syndagent.rsi
- type: ShowSyndicateIcons
- type: ShowHealthBars
damageContainers:
- Biological
- type: entity
parent: [ClothingEyesGlassesSunglasses, ShowSecurityIcons]

View File

@@ -853,6 +853,10 @@
- type: Construction
graph: WebObjects
node: coat
- type: Butcherable
spawned:
- id: MaterialWebSilk1
amount: 5
- type: FlavorProfile
flavors:
- cobwebs

View File

@@ -187,6 +187,10 @@
reagents:
- ReagentId: Fiber
Quantity: 10
- type: Butcherable
spawned:
- id: MaterialWebSilk1
amount: 2
- type: Construction
graph: WebObjects
node: boots

View File

@@ -46,6 +46,8 @@
- FoodBakedMuffinBerry
- FoodBakedMuffinCherry
- FoodBakedMuffinBluecherry
- FoodBakedMuffinChocolate
- FoodBakedMuffinBanana
- FoodBakedBunHoney
- FoodBakedBunHotX
- FoodBakedBunMeat

View File

@@ -285,7 +285,6 @@
- type: AccessReader
access: [["Command"], ["Research"]]
- type: ShowJobIcons
- type: ShowMindShieldIcons
- type: entity
id: BaseBorgChassisSyndicate

View File

@@ -391,8 +391,6 @@
- type: Construction
node: syndicatemedical
- type: ShowHealthBars
damageContainers:
- Biological
- type: InteractionPopup
interactSuccessString: petting-success-syndicate-cyborg
interactFailureString: petting-failure-syndicate-cyborg

View File

@@ -127,7 +127,7 @@
icon: { sprite: Interface/Actions/actions_ai.rsi, state: mass_scanner }
iconOn: Interface/Actions/actions_ai.rsi/mass_scanner.png
keywords: [ "AI", "console", "interface" ]
priority: -7
priority: -6
event: !type:ToggleIntrinsicUIEvent { key: enum.RadarConsoleUiKey.Key }
- type: entity
@@ -151,7 +151,7 @@
icon: { sprite: Interface/Actions/actions_ai.rsi, state: crew_monitor }
iconOn: Interface/Actions/actions_ai.rsi/crew_monitor.png
keywords: [ "AI", "console", "interface" ]
priority: -9
priority: -8
event: !type:ToggleIntrinsicUIEvent { key: enum.CrewMonitoringUIKey.Key }
- type: entity
@@ -163,5 +163,5 @@
icon: { sprite: Interface/Actions/actions_ai.rsi, state: station_records }
iconOn: Interface/Actions/actions_ai.rsi/station_records.png
keywords: [ "AI", "console", "interface" ]
priority: -8
priority: -7
event: !type:ToggleIntrinsicUIEvent { key: enum.GeneralStationRecordConsoleKey.Key }

View File

@@ -33,7 +33,6 @@
- type: ActionGrant
actions:
- ActionJumpToCore
- ActionShowJobIcons
- ActionSurvCameraLights
- ActionAIViewLaws
- type: UserInterface
@@ -70,6 +69,9 @@
canShuttle: false
title: comms-console-announcement-title-station-ai
color: "#2ed2fd"
- type: Speech
speechVerb: Robotic
- type: ShowJobIcons
- type: entity
id: AiHeldIntellicard

View File

@@ -19,44 +19,84 @@
- type: Item
size: Tiny
# Muffins/Buns
# Muffins
- type: entity
name: muffin
parent: FoodBakedBase
parent: FoodInjectableBase
id: FoodBakedMuffin
description: A delicious and spongy little cake.
components:
- type: Food
trash:
- FoodPlateMuffinTin
- type: Sprite
sprite: Objects/Consumable/Food/Baked/misc.rsi
state: muffin
- type: SolutionContainerManager
solutions:
food:
maxVol: 10
reagents:
- ReagentId: Nutriment
Quantity: 6
- ReagentId: Vitamin
Quantity: 2
- type: FlavorProfile
flavors:
- sweet
- type: Item
size: Tiny
- type: entity
name: berry muffin
parent: FoodBakedBase
parent: FoodBakedMuffin
id: FoodBakedMuffinBerry
description: A delicious and spongy little cake, with berries.
components:
- type: Sprite
state: muffin-berry
- type: SolutionContainerManager
solutions:
food:
maxVol: 12
reagents:
- ReagentId: Nutriment
Quantity: 6
- ReagentId: Vitamin
Quantity: 2
- ReagentId: JuiceBerry
Quantity: 2
- type: Tag
tags:
- Fruit
- type: entity
name: cherry muffin
parent: FoodBakedBase
parent: FoodBakedMuffin
id: FoodBakedMuffinCherry
description: A sweet muffin with cherry bits.
components:
- type: Sprite
state: muffin-cherry
- type: SolutionContainerManager
solutions:
food:
maxVol: 12
reagents:
- ReagentId: Nutriment
Quantity: 6
- ReagentId: Vitamin
Quantity: 2
- ReagentId: JuiceCherry
Quantity: 2
- type: Tag
tags:
- Fruit
- type: entity
name: bluecherry muffin
parent: FoodBakedBase
parent: FoodBakedMuffin
id: FoodBakedMuffinBluecherry
description: Blue cherries inside a delicious muffin.
components:
@@ -66,6 +106,51 @@
tags:
- Fruit
- type: entity
name: chocolate muffin
parent: FoodBakedMuffin
id: FoodBakedMuffinChocolate
description: A delicious and spongy chocolate muffin.
components:
- type: Sprite
state: muffin-chocolate
- type: SolutionContainerManager
solutions:
food:
maxVol: 12
reagents:
- ReagentId: Nutriment
Quantity: 6
- ReagentId: Vitamin
Quantity: 2
- ReagentId: CocoaPowder
Quantity: 2
- type: entity
name: banana muffin
parent: FoodBakedMuffin
id: FoodBakedMuffinBanana
description: A delicious and spongy banana muffin.
components:
- type: Sprite
state: muffin-banana
- type: SolutionContainerManager
solutions:
food:
maxVol: 12
reagents:
- ReagentId: Nutriment
Quantity: 6
- ReagentId: Vitamin
Quantity: 2
- ReagentId: JuiceBanana
Quantity: 2
- type: Tag
tags:
- Fruit
# Buns
- type: entity
name: honey bun #TODO honey
parent: FoodBakedBase
@@ -685,4 +770,4 @@
- type: DamageOtherOnHit
damage:
types:
Blunt: 0 # so the damage stats icon doesn't immediately give away the syndie ones
Blunt: 0 # so the damage stats icon doesn't immediately give away the syndie ones

View File

@@ -178,3 +178,27 @@
materialComposition:
Steel: 60
- type: SpaceGarbage
# Muffin Tin
- type: entity
name: muffin tin
parent: BaseItem
id: FoodPlateMuffinTin
description: A cheap foil tin for muffins.
components:
- type: Sprite
sprite: Objects/Consumable/Food/plates.rsi
state: muffin-tin
- type: Item
size: Small
shape:
- 0,0,1,0
storedOffset: 0,-3
- type: Tag
tags:
- Trash
- type: PhysicalComposition
materialComposition:
Steel: 30
- type: SpaceGarbage

View File

@@ -12,6 +12,7 @@
components:
- Anchorable
- Item
tags:
- Bot # for funny bot moments
blacklist:
components:

View File

@@ -121,6 +121,7 @@
- type: Tag
tags:
- HolofanProjector
- SecBeltEquip
- type: StaticPrice
price: 50

View File

@@ -496,7 +496,7 @@
state: pda-janitor
- type: entity
parent: BaseSecurityPDA
parent: BasePDA
id: CaptainPDA
name: captain PDA
description: Surprisingly no different from your PDA.
@@ -684,6 +684,13 @@
accentHColor: "#447987"
- type: Icon
state: pda-hos
- type: CartridgeLoader
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- WantedListCartridge
- LogProbeCartridge
- type: entity
parent: BaseSecurityPDA
@@ -732,6 +739,16 @@
borderColor: "#00842e"
- type: Icon
state: pda-centcom
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- MedTekCartridge
- WantedListCartridge
- LogProbeCartridge
- AstroNavCartridge
- type: entity
parent: CentcomPDA
@@ -754,6 +771,8 @@
- NewsReaderCartridge
- LogProbeCartridge
- WantedListCartridge
- MedTekCartridge
- AstroNavCartridge
- type: entity
parent: CentcomPDA
@@ -845,14 +864,6 @@
uiKey: enum.PdaUiKey.Key
preinstalled:
- NotekeeperCartridge
cartridgeSlot:
priority: -1
name: Cartridge
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/id_insert.ogg
whitelist:
components:
- Cartridge
- type: entity
parent: BaseSecurityPDA
@@ -870,6 +881,16 @@
accentVColor: "#447987"
- type: Icon
state: pda-ert
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- MedTekCartridge
- WantedListCartridge
- LogProbeCartridge
- AstroNavCartridge
- type: entity
parent: ERTLeaderPDA
@@ -910,14 +931,6 @@
components:
- type: Pda
id: ERTMedicIDCard
- type: CartridgeLoader
uiKey: enum.PdaUiKey.Key
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- MedTekCartridge
- WantedListCartridge
- type: entity
parent: ERTLeaderPDA
@@ -1019,6 +1032,13 @@
borderColor: "#774705"
- type: Icon
state: pda-detective
- type: CartridgeLoader
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- WantedListCartridge
- LogProbeCartridge
- type: entity
parent: BaseMedicalPDA
@@ -1035,6 +1055,13 @@
accentVColor: "#d7d7d0"
- type: Icon
state: pda-brigmedic
- type: CartridgeLoader
preinstalled:
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
- WantedListCartridge
- MedTekCartridge
- type: entity
parent: ClownPDA
@@ -1151,11 +1178,3 @@
preinstalled:
- NotekeeperCartridge
- MedTekCartridge
cartridgeSlot:
priority: -1
name: Cartridge
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/id_insert.ogg
whitelist:
components:
- Cartridge

View File

@@ -35,6 +35,7 @@
damage:
types:
Blunt: 0
hidden: true
- type: PhysicalComposition
materialComposition:
Cloth: 100
@@ -598,6 +599,11 @@
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcBite # Rrrr!
- type: Tag
tags:
- Payload
- ClothMade
- PlushieCarp
- type: entity
parent: PlushieCarp

View File

@@ -20,8 +20,6 @@
density: 15
mask:
- MachineMask
- type: CargoPallet
palletType: All
- type: StaticPrice
price: 100
- type: Sprite

View File

@@ -71,3 +71,69 @@
id: DefibrillatorOneHandedUnpowered
parent: BaseDefibrillator
suffix: One-Handed, Unpowered
- type: entity
id: DefibrillatorCompact # This should be a research item at some point
parent: [ BaseDefibrillator, PowerCellSlotMediumItem ]
name: compact defibrillator
description: Now in fun size!
components:
- type: Sprite
sprite: Objects/Specific/Medical/defibsmall.rsi
layers:
- state: icon
- state: screen
map: [ "enum.ToggleVisuals.Layer" ]
visible: false
shader: unshaded
- state: ready
map: ["enum.PowerDeviceVisualLayers.Powered"]
shader: unshaded
- type: Item
size: Normal
- type: ToggleCellDraw
- type: PowerCellDraw
useRate: 100
- type: Defibrillator
zapHeal:
types:
Asphyxiation: -40
doAfterDuration: 6
- type: DoAfter
- type: UseDelay
- type: entity
id: DefibrillatorSyndicate
parent: DefibrillatorCompact
name: interdyne defibrillator
description: Doubles as a self-defense weapon against war-crime inclined tiders.
components:
- type: Sprite
sprite: Objects/Specific/Medical/defibsyndi.rsi
layers:
- state: icon
- state: screen
map: [ "enum.ToggleVisuals.Layer" ]
visible: false
shader: unshaded
- state: ready
map: ["enum.PowerDeviceVisualLayers.Powered"]
shader: unshaded
- type: MeleeWeapon
damage:
types:
Blunt: 8
- type: ItemToggleMeleeWeapon
activatedSoundOnHit:
path: /Audio/Items/Defib/defib_zap.ogg
params:
variation: 0.250
activatedSoundOnHitNoDamage:
path: /Audio/Items/Defib/defib_zap.ogg
params:
variation: 0.250
volume: -10
activatedDamage:
types:
Blunt: 8
Shock: 16

View File

@@ -271,7 +271,6 @@
transferAmount: 20
onlyAffectsMobs: false
injectOnly: true
- type: SolutionContainerManager
solutions:
pen:
@@ -284,6 +283,102 @@
- type: Tag
tags: []
- type: entity
name: puncturase auto-injector
parent: ChemicalMedipen
id: PunctAutoInjector
description: A rapid dose of puncturase and tranexamic acid, intended for combat applications.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
layers:
- state: punctpen
map: ["enum.SolutionContainerLayers.Fill"]
- type: SolutionContainerVisuals
maxFillLevels: 1
changeColor: false
emptySpriteName: punctpen_empty
- type: Hypospray
solutionName: pen
transferAmount: 15
onlyAffectsMobs: false
injectOnly: true
- type: SolutionContainerManager
solutions:
pen:
maxVol: 15
reagents:
- ReagentId: Puncturase
Quantity: 10
- ReagentId: TranexamicAcid
Quantity: 5
- type: Tag
tags: []
- type: entity
name: pyrazine auto-injector
parent: ChemicalMedipen
id: PyraAutoInjector
description: A rapid dose of pyrazine and dermaline, intended for combat applications.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
layers:
- state: pyrapen
map: ["enum.SolutionContainerLayers.Fill"]
- type: SolutionContainerVisuals
maxFillLevels: 1
changeColor: false
emptySpriteName: pyrapen_empty
- type: Hypospray
solutionName: pen
transferAmount: 20
onlyAffectsMobs: false
injectOnly: true
- type: SolutionContainerManager
solutions:
pen:
maxVol: 20
reagents:
- ReagentId: Pyrazine
Quantity: 10
- ReagentId: Dermaline
Quantity: 10
- type: Tag
tags: []
- type: entity
name: airloss auto-injector
parent: ChemicalMedipen
id: AirlossAutoInjector
description: A rapid dose of saline and dexalin plus, intended to get someone up quickly.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
layers:
- state: dexpen
map: ["enum.SolutionContainerLayers.Fill"]
- type: SolutionContainerVisuals
maxFillLevels: 1
changeColor: false
emptySpriteName: dexpen_empty
- type: Hypospray
solutionName: pen
transferAmount: 40
onlyAffectsMobs: false
injectOnly: true
- type: SolutionContainerManager
solutions:
pen:
maxVol: 40
reagents:
- ReagentId: Saline
Quantity: 20
- ReagentId: DexalinPlus
Quantity: 20
- type: Tag
tags: []
- type: entity
name: space medipen
parent: ChemicalMedipen

View File

@@ -5,60 +5,65 @@
description: A strange alien device.
abstract: true
components:
- type: Sprite
drawdepth: SmallObjects
sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi
noRot: true
layers:
- state: ano30
map: [ "enum.ArtifactsVisualLayers.Base" ]
- state: ano30_on
map: [ "enum.ArtifactsVisualLayers.Effect" ]
visible: false
- type: Damageable
- type: Physics
bodyType: Dynamic
- type: Transform
noRot: true
- type: UserInterface #needs to be here for certain effects
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
enum.InstrumentUiKey.Key:
type: InstrumentBoundUserInterface
enum.IntercomUiKey.Key:
type: IntercomBoundUserInterface
- type: Reactive
groups:
Acidic: [Touch]
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.45
density: 75
layer: # doesn't collide with artifact storage
- Opaque
mask:
- MachineMask
- type: InteractionOutline
- type: Artifact
- type: RandomArtifactSprite
maxSprite: 36
- type: RandomSprite
available:
- enum.ArtifactsVisualLayers.Effect:
ano01_on: Rainbow
- type: Appearance
- type: Actions
- type: GuideHelp
guides:
- Xenoarchaeology
- type: StealTarget
stealGroup: XenoArtifact
- type: Sprite
drawdepth: SmallObjects
sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi
noRot: true
layers:
- state: ano30
map: [ "enum.ArtifactsVisualLayers.Base" ]
- state: ano30_on
map: [ "enum.ArtifactsVisualLayers.Effect" ]
visible: false
- type: Damageable
- type: Physics
bodyType: Dynamic
- type: Transform
noRot: true
- type: UserInterface #needs to be here for certain effects
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
enum.InstrumentUiKey.Key:
type: InstrumentBoundUserInterface
enum.IntercomUiKey.Key:
type: IntercomBoundUserInterface
- type: Reactive
groups:
Acidic: [Touch]
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.45
density: 75
layer: # doesn't collide with artifact storage
- Opaque
mask:
- MachineMask
- type: InteractionOutline
- type: Artifact
- type: RandomArtifactSprite
maxSprite: 36
- type: RandomSprite
available:
- enum.ArtifactsVisualLayers.Effect:
ano01_on: Rainbow
- type: Appearance
- type: Actions
- type: GuideHelp
guides:
- Xenoarchaeology
- type: StealTarget
stealGroup: XenoArtifact
- type: ContainerContainer
containers:
storagebase: !type:Container # needed for the EffectStorage artifactEffect
ents: [ ]
revolver-ammo: !type:Container # needed for the EffectBigIron artifactEffect
- type: entity
parent: BaseXenoArtifact

View File

@@ -543,12 +543,8 @@
solutions:
food:
maxVol: 20
- type: SolutionSpiker
sourceSolution: food
- type: Extractable
grindableSolutionName: food
- type: StaticPrice
price: 0
- type: ExplosionResistance
damageCoefficient: 0.025 # survives conventional explosives but not minibombs and nukes
- type: Damageable
damageContainer: Inorganic
- type: Destructible
@@ -561,6 +557,12 @@
solution: food
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: SolutionSpiker
sourceSolution: food
- type: Extractable
grindableSolutionName: food
- type: StaticPrice
price: 0
- type: Tag
tags:
- Pill
@@ -588,4 +590,7 @@
areaInsertRadius: 1
storageInsertSound: /Audio/Effects/pill_insert.ogg
storageRemoveSound: /Audio/Effects/pill_remove.ogg
whitelist:
tags:
- Pill
- type: Dumpable

View File

@@ -1,4 +1,4 @@
- type: entity
- type: entity
id: WeaponWaterGunBase
abstract: true
parent: BaseItem
@@ -71,7 +71,7 @@
id: WeaponWaterBlaster
parent: WeaponWaterGunBase
name: water blaster
description: With this bad boy, you'll be the cooleste kid at the summer barbecue.
description: With this bad boy, you'll be the coolest kid at the summer barbecue.
components:
- type: Gun
cameraRecoilScalar: 0 #no recoil

View File

@@ -103,6 +103,9 @@
Slash: 10
- type: Item
sprite: Objects/Weapons/Melee/combat_knife.rsi
storedSprite:
state: storage
sprite: Objects/Weapons/Melee/combat_knife.rsi
- type: DisarmMalus
malus: 0.225
@@ -117,6 +120,9 @@
state: icon
- type: Item
sprite: Objects/Weapons/Melee/survival_knife.rsi
storedSprite:
state: storage
sprite: Objects/Weapons/Melee/survival_knife.rsi
- type: entity
name: kukri knife

View File

@@ -149,6 +149,7 @@
- type: GunRequiresWield
- type: Item
size: Ginormous
sprite: Objects/Weapons/Melee/crusher-inhands.rsi
- type: DisarmMalus
- type: Prying
@@ -191,3 +192,6 @@
- type: Tag
tags:
- Pickaxe
- type: Item
size: Ginormous
sprite: Objects/Weapons/Melee/crusher_glaive-inhands.rsi

View File

@@ -43,7 +43,7 @@
- type: RCDDeconstructable
cost: 4
delay: 2
fx: EffectRCDDeconstruct2
fx: EffectRCDDeconstruct2
- type: Destructible
thresholds:
- trigger:
@@ -74,7 +74,7 @@
mode: SnapgridCenter
snap:
- Wallmount
- type: entity
name: light
description: "A light fixture. Draws power and produces light when equipped with a light tube."
@@ -400,6 +400,37 @@
shader: "unshaded"
visible: false
- type: Appearance
- type: Construction
graph: LightFixture
node: emergencyLight
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 100
behaviors: #excess damage, don't spawn entities.
- !type:DoActsBehavior
acts: [ "Destruction" ]
- !type:PlaySoundBehavior
sound:
collection: GlassBreak
- trigger:
!type:DamageTrigger
damage: 25
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
SheetSteel1:
min: 1
max: 1
ShardGlass:
min: 1
max: 1
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: GlassBreak
placement:
mode: SnapgridCenter
snap:

View File

@@ -163,6 +163,7 @@
- FoodPlateSmallPlastic
- FoodBowlBig
- FoodPlateTin
- FoodPlateMuffinTin
- FoodKebabSkewer
- SprayBottle
- MopItem

View File

@@ -47,19 +47,30 @@
whitelist:
tags:
- PlushieSharkGrey
sprite: Objects/Fun/sharkplush.rsi
mopbucket_carpplush:
whitelist:
tags:
- PlushieCarp
sprite: Objects/Specific/Janitorial/janitorial.rsi
- type: Transform
noRot: true
- type: ItemSlots
slots:
shark_slot:
name: mop-bucket-slot-component-slot-name-shark
item_slot:
name: mop-bucket-slot-component-slot-name-item
ejectVerbText: mop-bucket-slot-component-eject-verb
whitelist:
tags:
- PlushieSharkBlue
- PlushieSharkPink
- PlushieSharkGrey
- PlushieCarp
components:
- Rehydratable
priority: 3 # Higher than drinking priority
- type: ReactiveContainer
solution: bucket
container: item_slot
- type: Drink
solution: bucket
- type: Appearance
@@ -70,7 +81,7 @@
containers:
storagebase: !type:Container
ents: []
shark_slot: !type:ContainerSlot {}
item_slot: !type:ContainerSlot {}
- type: GuideHelp
guides:
- Janitorial

View File

@@ -29,7 +29,7 @@
isCollidableWhenOpen: false
openOnMove: false
airtight: false
capacity: 4 #4 Entities seems like a nice comfy fit for a cardboard box.
capacity: 5 #5 entity capacity to fit all of your friends (or teammates on your nuclear operation without reinforcements).
- type: ContainerContainer
containers:
entity_storage: !type:Container

View File

@@ -64,8 +64,8 @@
canEditLabel: true
- type: TextScreenVisuals
color: FloralWhite
textOffset: 0,8
timerOffset: 0,8
textOffset: 0,6
timerOffset: 0,6
textLength: 5
rows: 1
- type: Sprite

View File

@@ -19,6 +19,7 @@
baseOdds: 0.036
appliesToProduce: false
effect: !type:PlantSpeciesChange
persists: false
- name: Unviable
baseOdds: 0.109
persists: false

View File

@@ -21,6 +21,13 @@
back:
- ClothingHeadHatHairflower
# Headphones
- type: loadout
id: Headphones
storage:
back:
- ClothingNeckHeadphones
# Plushies
- type: loadout
id: PlushieLizard
@@ -165,7 +172,7 @@
!type:OverallPlaytimeRequirement
time: 36000 # 10hr
storage:
back:
back:
- TowelColorWhite
- type: loadout
@@ -176,9 +183,9 @@
!type:OverallPlaytimeRequirement
time: 1800000 # 500hr
storage:
back:
back:
- TowelColorSilver
- type: loadout
id: TowelColorGold
effects:
@@ -187,7 +194,7 @@
!type:OverallPlaytimeRequirement
time: 3600000 # 1000hr
storage:
back:
back:
- TowelColorGold
- type: loadout
@@ -199,7 +206,7 @@
department: Cargo
time: 360000 # 100hr
storage:
back:
back:
- TowelColorLightBrown
- type: loadout
@@ -211,7 +218,7 @@
department: Civilian
time: 360000 # 100hr
storage:
back:
back:
- TowelColorGreen
- type: loadout
@@ -223,7 +230,7 @@
department: Command
time: 360000 # 100hr
storage:
back:
back:
- TowelColorDarkBlue
- type: loadout
@@ -235,9 +242,9 @@
department: Engineering
time: 360000 # 100hr
storage:
back:
back:
- TowelColorOrange
- type: loadout
id: TowelColorLightBlue
effects:
@@ -247,7 +254,7 @@
department: Medical
time: 360000 # 100hr
storage:
back:
back:
- TowelColorLightBlue
- type: loadout
@@ -259,7 +266,7 @@
department: Science
time: 360000 # 100hr
storage:
back:
back:
- TowelColorPurple
- type: loadout
@@ -271,7 +278,7 @@
department: Security
time: 360000 # 100hr
storage:
back:
back:
- TowelColorRed
- type: loadout
@@ -283,7 +290,7 @@
role: JobPassenger
time: 360000 # 100hr
storage:
back:
back:
- TowelColorGray
- type: loadout
@@ -295,7 +302,7 @@
role: JobChaplain
time: 360000 # 100hr
storage:
back:
back:
- TowelColorBlack
- type: loadout
@@ -307,7 +314,7 @@
role: JobLibrarian
time: 360000 # 100hr
storage:
back:
back:
- TowelColorDarkGreen
- type: loadout
@@ -319,7 +326,7 @@
role: JobLawyer
time: 360000 # 100hr
storage:
back:
back:
- TowelColorMaroon
- type: loadout
@@ -331,7 +338,7 @@
role: JobClown
time: 360000 # 100hr
storage:
back:
back:
- TowelColorYellow
- type: loadout
@@ -343,5 +350,5 @@
role: JobMime
time: 360000 # 100hr
storage:
back:
- TowelColorMime
back:
- TowelColorMime

View File

@@ -7,6 +7,7 @@
loadouts:
- FlowerWreath
- Hairflower
- Headphones
- PlushieLizard
- PlushieSpaceLizard
- Lighter
@@ -42,7 +43,6 @@
- TowelColorPurple
- TowelColorRed
- TowelColorSilver
- TowelColorLightBlue
- TowelColorWhite
- TowelColorYellow

View File

@@ -24,6 +24,14 @@
- material: Steel
amount: 1
doAfter: 2.0
- to: emergencyLight
steps:
- material: Steel
amount: 1
doAfter: 1.0
- material: Glass
amount: 1
doAfter: 1.0
- node: tubeLight
entity: PoweredlightEmpty
edges:
@@ -83,4 +91,19 @@
- !type:SpawnPrototype
prototype: SheetSteel1
amount: 1
- !type:DeleteEntity {}
- !type:DeleteEntity {}
- node: emergencyLight
entity: EmergencyLight
edges:
- to: start
steps:
- tool: Screwing
doAfter: 2.0
completed:
- !type:SpawnPrototype
prototype: SheetSteel1
amount: 1
- !type:SpawnPrototype
prototype: SheetGlass1
amount: 1
- !type:DeleteEntity {}

View File

@@ -1456,6 +1456,24 @@
# Same here. - 20kdc
- !type:TileNotBlocked
- type: construction
name: emergency light
id: EmergencyLightFixture
graph: LightFixture
startNode: start
targetNode: emergencyLight
category: construction-category-structures
description: An emergency light.
icon:
sprite: Structures/Wallmounts/Lighting/emergency_light.rsi
state: base
objectType: Structure
placementMode: SnapgridCenter
canRotate: true
canBuildInImpassable: false
conditions:
- !type:TileNotBlocked
- type: construction
name: ground light post
id: LightGroundFixture

View File

@@ -1779,6 +1779,67 @@
FoodOrange: 1
FoodAmbrosiaVulgaris: 1
# Muffins
- type: microwaveMealRecipe
id: RecipeMuffin
name: muffin recipe
result: FoodBakedMuffin
time: 15
solids:
FoodPlateMuffinTin: 1
FoodDoughSlice: 1
reagents:
Sugar: 10
- type: microwaveMealRecipe
id: RecipeMuffinChocolate
name: chocolate muffin recipe
result: FoodBakedMuffinChocolate
time: 15
solids:
FoodPlateMuffinTin: 1
FoodDoughSlice: 1
FoodSnackChocolateBar: 1
reagents:
Sugar: 10
- type: microwaveMealRecipe
id: RecipeMuffinBerry
name: berry muffin recipe
result: FoodBakedMuffinBerry
time: 15
solids:
FoodPlateMuffinTin: 1
FoodDoughSlice: 1
FoodBerries: 1
reagents:
Sugar: 10
- type: microwaveMealRecipe
id: RecipeMuffinBanana
name: banana muffin recipe
result: FoodBakedMuffinBanana
time: 15
solids:
FoodPlateMuffinTin: 1
FoodDoughSlice: 1
FoodBanana: 1
reagents:
Sugar: 10
- type: microwaveMealRecipe
id: RecipeMuffinCherry
name: cherry muffin recipe
result: FoodBakedMuffinCherry
time: 15
solids:
FoodPlateMuffinTin: 1
FoodDoughSlice: 1
FoodCherry: 3
reagents:
Sugar: 10
# NOT ACTUAL FOOD
- type: microwaveMealRecipe

View File

@@ -8,6 +8,6 @@
steps:
- material: Cloth
amount: 2
doAfter: 10
doAfter: 3
- node: gauze
entity: Gauze1

View File

@@ -41,13 +41,13 @@
- material: WebSilk
amount: 12
doAfter: 6
- to: boots
steps:
- material: WebSilk
amount: 2
doAfter: 4
# Deconstruction
- node: tile
entity: FloorTileItemWeb

View File

@@ -1,39 +1,39 @@
# Base prototypes
- type: latheRecipe
id: MiniHoe
result: HydroponicsToolMiniHoe
completetime: 2
abstract: true
parent: BaseToolRecipe
id: BaseHydroToolRecipe
materials:
Steel: 200
Plastic: 100
# Recipes
- type: latheRecipe
parent: BaseHydroToolRecipe
id: HydroponicsToolMiniHoe
result: HydroponicsToolMiniHoe
- type: latheRecipe
parent: BaseHydroToolRecipe
id: HydroponicsToolScythe
result: HydroponicsToolScythe
completetime: 2
materials:
Steel: 300
Plastic: 200
- type: latheRecipe
parent: BaseHydroToolRecipe
id: HydroponicsToolHatchet
result: HydroponicsToolHatchet
completetime: 2
materials:
Steel: 200
Plastic: 100
- type: latheRecipe
id: Spade
parent: BaseHydroToolRecipe
id: HydroponicsToolSpade
result: HydroponicsToolSpade
completetime: 2
materials:
Steel: 200
Plastic: 100
- type: latheRecipe
id: Clippers
parent: BaseHydroToolRecipe
id: HydroponicsToolClippers
result: HydroponicsToolClippers
completetime: 2
materials:
Steel: 200
Plastic: 100

View File

@@ -1,67 +1,65 @@
- type: latheRecipe
parent: BaseToolRecipe
id: ButchCleaver
result: ButchCleaver
completetime: 2
materials:
Steel: 300
Plastic: 50
- type: latheRecipe
parent: BaseToolRecipe
id: KitchenKnife
result: KitchenKnife
completetime: 2
materials:
Steel: 200
Plastic: 50
- type: latheRecipe
id: DrinkMug
result: DrinkMug
abstract: true
id: BaseGlasswareRecipe
completetime: 0.8
materials:
Glass: 100
- type: latheRecipe
parent: BaseGlasswareRecipe
id: DrinkMug
result: DrinkMug
- type: latheRecipe
parent: DrinkMug
id: DrinkMugMetal
result: DrinkMugMetal
completetime: 0.8
materials:
Steel: 100
- type: latheRecipe
parent: DrinkMug
id: DrinkGlass
result: DrinkGlass
completetime: 0.8
materials:
Glass: 100
- type: latheRecipe
parent: DrinkMug
id: DrinkShotGlass
result: DrinkShotGlass
completetime: 0.4
materials:
Glass: 100
- type: latheRecipe
parent: DrinkMug
id: DrinkGlassCoupeShaped
result: DrinkGlassCoupeShaped
completetime: 0.8
materials:
Glass: 100
- type: latheRecipe
id: CustomDrinkJug
result: CustomDrinkJug
completetime: 2
materials:
Plastic: 200
- type: latheRecipe
id: CustomDrinkJug
result: CustomDrinkJug
completetime: 2
materials:
Plastic: 200
- type: latheRecipe
parent: BaseGlasswareRecipe
id: FoodPlate
result: FoodPlate
completetime: 0.8
materials:
Glass: 100
- type: latheRecipe
id: FoodPlateSmall
@@ -71,25 +69,23 @@
Glass: 50
- type: latheRecipe
parent: FoodPlate
id: FoodPlatePlastic
result: FoodPlatePlastic
completetime: 0.8
materials:
Plastic: 100
- type: latheRecipe
parent: FoodPlateSmall
id: FoodPlateSmallPlastic
result: FoodPlateSmallPlastic
completetime: 0.4
materials:
Plastic: 50
- type: latheRecipe
parent: FoodPlate
id: FoodBowlBig
result: FoodBowlBig
completetime: 0.8
materials:
Glass: 100
- type: latheRecipe
id: FoodPlateTin
@@ -98,6 +94,13 @@
materials:
Steel: 100
- type: latheRecipe
parent: FoodPlateTin
id: FoodPlateMuffinTin
result: FoodPlateMuffinTin
materials:
Steel: 50
- type: latheRecipe
id: FoodKebabSkewer
result: FoodKebabSkewer

Some files were not shown because too many files have changed in this diff Show More