merge remote wizden/master

This commit is contained in:
Dmitry
2026-01-29 00:01:34 +07:00
117 changed files with 1572 additions and 1856 deletions
@@ -14,7 +14,6 @@ namespace Content.Client.Humanoid;
public sealed partial class OrganMarkingPicker : Control
{
[Dependency] private readonly MarkingManager _marking = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IEntityManager _entity = default!;
private readonly SpriteSystem _sprite;
@@ -1,5 +1,7 @@
using System.ComponentModel.Design;
using System.Linq;
using Content.Client.Light.Components;
using Content.Shared.Trigger.Components.Effects;
using Robust.Client.GameObjects;
using Robust.Client.Animations;
using Robust.Shared.Random;
@@ -36,6 +38,10 @@ public sealed class LightBehaviorSystem : EntitySystem
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
_player.Play(uid, container.Animation, container.FullKey);
}
else
{
StopLightBehaviour((uid, component), container.LightBehaviour.ID, resetToOriginalSettings: true);
}
}
private void OnLightStartup(Entity<LightBehaviourComponent> entity, ref ComponentStartup args)
@@ -53,7 +59,7 @@ public sealed class LightBehaviorSystem : EntitySystem
{
if (container.LightBehaviour.Enabled)
{
StartLightBehaviour(entity, container.LightBehaviour.ID);
StartLightBehaviour((entity, entity), container.LightBehaviour.ID);
}
}
}
@@ -82,12 +88,13 @@ public sealed class LightBehaviorSystem : EntitySystem
/// If specified light behaviours are already animating, calling this does nothing.
/// Multiple light behaviours can have the same ID.
/// </summary>
public void StartLightBehaviour(Entity<LightBehaviourComponent> entity, string id = "")
public void StartLightBehaviour(Entity<LightBehaviourComponent?> entity, string id = "")
{
if (!TryComp(entity, out AnimationPlayerComponent? animation))
{
if (!Resolve(entity, ref entity.Comp))
return;
if (!TryComp(entity, out AnimationPlayerComponent? animation))
return;
}
foreach (var container in entity.Comp.Animations)
{
@@ -95,7 +102,7 @@ public sealed class LightBehaviorSystem : EntitySystem
{
if (!_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key))
{
CopyLightSettings(entity, container.LightBehaviour.Property);
CopyLightSettings((entity, entity.Comp), container.LightBehaviour.Property);
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
_player.Play(entity, container.Animation, LightBehaviourComponent.KeyPrefix + container.Key);
}
@@ -118,11 +125,9 @@ public sealed class LightBehaviorSystem : EntitySystem
return;
}
var comp = entity.Comp;
var toRemove = new List<LightBehaviourComponent.AnimationContainer>();
foreach (var container in comp.Animations)
foreach (var container in entity.Comp.Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
@@ -140,18 +145,24 @@ public sealed class LightBehaviorSystem : EntitySystem
foreach (var container in toRemove)
{
comp.Animations.Remove(container);
entity.Comp.Animations.Remove(container);
}
if (resetToOriginalSettings && TryComp(entity, out PointLightComponent? light))
if (resetToOriginalSettings)
ResetToOriginalSettings(entity);
entity.Comp.OriginalPropertyValues.Clear();
}
private void ResetToOriginalSettings(Entity<LightBehaviourComponent, PointLightComponent?> entity)
{
if (!Resolve(entity, ref entity.Comp2))
return;
foreach (var (property, value) in entity.Comp1.OriginalPropertyValues)
{
foreach (var (property, value) in comp.OriginalPropertyValues)
{
AnimationHelper.SetAnimatableProperty(light, property, value);
}
AnimationHelper.SetAnimatableProperty(entity.Comp2, property, value);
}
comp.OriginalPropertyValues.Clear();
}
/// <summary>
@@ -194,7 +205,7 @@ public sealed class LightBehaviorSystem : EntitySystem
if (playImmediately)
{
StartLightBehaviour(entity, behaviour.ID);
StartLightBehaviour((entity, entity), behaviour.ID);
}
}
}
+2 -175
View File
@@ -39,7 +39,6 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly JobRequirementsManager _requirements = default!;
[Dependency] private readonly MarkingManager _markings = default!;
[UISystemDependency] private readonly VisualBodySystem _visualBody = default!;
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
@@ -183,13 +182,12 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
if (character is not HumanoidCharacterProfile humanoid)
{
PreviewPanel.SetSprite(EntityUid.Invalid);
PreviewPanel.ProfilePreviewSpriteView.ClearPreview();
PreviewPanel.SetSummaryText(string.Empty);
return;
}
var dummy = LoadProfileEntity(humanoid, null, true);
PreviewPanel.SetSprite(dummy);
PreviewPanel.ProfilePreviewSpriteView.LoadPreview(humanoid);
PreviewPanel.SetSummaryText(humanoid.Summary);
}
@@ -326,175 +324,4 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
return (_characterSetup, _profileEditor);
}
#region Helpers
/// <summary>
/// Applies the highest priority job's clothes to the dummy.
/// </summary>
public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile)
{
var job = jobProto ?? GetPreferredJob(profile);
GiveDummyJobClothes(dummy, profile, job);
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
{
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), _playerManager.LocalSession, profile.Species, EntityManager, _prototypeManager);
GiveDummyLoadout(dummy, loadout);
}
}
/// <summary>
/// Gets the highest priority job for the profile.
/// </summary>
public JobPrototype GetPreferredJob(HumanoidCharacterProfile profile)
{
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
return _prototypeManager.Index<JobPrototype>(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob);
}
public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
{
if (roleLoadout == null)
return;
foreach (var group in roleLoadout.SelectedLoadouts.Values)
{
foreach (var loadout in group)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
_spawn.EquipStartingGear(uid, loadoutProto);
}
}
}
/// <summary>
/// Applies the specified job's clothes to the dummy.
/// </summary>
public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile, JobPrototype job)
{
if (!_inventory.TryGetSlots(dummy, out var slots))
return;
// Apply loadout
if (profile.Loadouts.TryGetValue(job.ID, out var jobLoadout))
{
foreach (var loadouts in jobLoadout.SelectedLoadouts.Values)
{
foreach (var loadout in loadouts)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
// TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
// Try startinggear first
if (_prototypeManager.Resolve(loadoutProto.StartingGear, out var loadoutGear))
{
var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntityManager.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
}
}
else
{
var itemType = ((IEquipmentLoadout) loadoutProto).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntityManager.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
}
}
}
}
}
}
if (!_prototypeManager.Resolve(job.StartingGear, out var gear))
return;
foreach (var slot in slots)
{
var itemType = ((IEquipmentLoadout) gear).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntityManager.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
}
}
}
/// <summary>
/// Loads the profile onto a dummy entity.
/// </summary>
public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes)
{
EntityUid dummyEnt;
EntProtoId? previewEntity = null;
if (humanoid != null && jobClothes)
{
job ??= GetPreferredJob(humanoid);
previewEntity = job.JobPreviewEntity ?? (EntProtoId?)job?.JobEntity;
}
if (previewEntity != null)
{
// Special type like borg or AI, do not spawn a human just spawn the entity.
dummyEnt = EntityManager.SpawnEntity(previewEntity, MapCoordinates.Nullspace);
return dummyEnt;
}
else if (humanoid is not null)
{
var dummy = _prototypeManager.Index(humanoid.Species).DollPrototype;
dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
_visualBody.ApplyProfileTo(dummyEnt, humanoid);
}
else
{
dummyEnt = EntityManager.SpawnEntity(_prototypeManager.Index(HumanoidCharacterProfile.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
}
if (humanoid != null && jobClothes)
{
DebugTools.Assert(job != null);
GiveDummyJobClothes(dummyEnt, humanoid, job);
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
{
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), _playerManager.LocalSession, humanoid.Species, EntityManager, _prototypeManager);
GiveDummyLoadout(dummyEnt, loadout);
}
}
return dummyEnt;
}
#endregion
}
@@ -1,15 +1,16 @@
<ContainerButton xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:profile="clr-namespace:Content.Client.Lobby.UI.ProfileEditorControls"
xmlns:style="clr-namespace:Content.Client.Stylesheets">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
SeparationOverride="0"
Name="InternalHBox">
<SpriteView Scale="2 2"
Margin="0 4 4 4"
OverrideDirection="South"
Name="View"
SetSize="64 64"/>
<profile:ProfilePreviewSpriteView Scale="2 2"
Margin="0 4 4 4"
OverrideDirection="South"
Name="View"
SetSize="64 64"/>
<Label Name="DescriptionLabel"
ClipText="True"
HorizontalExpand="True"/>
@@ -10,6 +10,7 @@ using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Lobby.UI;
@@ -20,37 +21,28 @@ namespace Content.Client.Lobby.UI;
[GenerateTypedNameReferences]
public sealed partial class CharacterPickerButton : ContainerButton
{
private IEntityManager _entManager;
private EntityUid _previewDummy;
/// <summary>
/// Invoked if we should delete the attached character
/// </summary>
public event Action? OnDeletePressed;
public CharacterPickerButton(
IEntityManager entityManager,
IPrototypeManager prototypeManager,
ISharedPlayerManager playerMan,
ButtonGroup group,
ICharacterProfile profile,
bool isSelected)
{
RobustXamlLoader.Load(this);
_entManager = entityManager;
AddStyleClass(StyleClassButton);
ToggleMode = true;
Group = group;
var description = profile.Name;
if (profile is not HumanoidCharacterProfile humanoid)
View.LoadPreview(profile);
if (profile is HumanoidCharacterProfile humanoid)
{
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(HumanoidCharacterProfile.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
}
else
{
_previewDummy = UserInterfaceManager.GetUIController<LobbyUIController>()
.LoadProfileEntity(humanoid, null, true);
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
if (highPriorityJob != default)
@@ -63,7 +55,6 @@ public sealed partial class CharacterPickerButton : ContainerButton
Pressed = isSelected;
DeleteButton.Visible = !isSelected;
View.SetEntity(_previewDummy);
DescriptionLabel.Text = description;
ConfirmDeleteButton.OnPressed += _ =>
@@ -79,14 +70,4 @@ public sealed partial class CharacterPickerButton : ContainerButton
ConfirmDeleteButton.Visible = true;
};
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_entManager.DeleteEntity(_previewDummy);
_previewDummy = default;
}
}
@@ -11,6 +11,7 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Lobby.UI
@@ -22,10 +23,10 @@ namespace Content.Client.Lobby.UI
public sealed partial class CharacterSetupGui : Control
{
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _protomanager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
private readonly Button _createNewCharacterButton;
@@ -99,8 +100,8 @@ namespace Content.Client.Lobby.UI
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
{
numberOfFullSlots++;
var characterPickerButton = new CharacterPickerButton(_entManager,
_protomanager,
var characterPickerButton = new CharacterPickerButton(_protomanager,
_playerManager,
characterButtonsGroup,
character,
slot == selectedSlot);
@@ -3,6 +3,7 @@
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:ui="clr-namespace:Content.Client.Lobby.UI"
xmlns:profile="clr-namespace:Content.Client.Lobby.UI.ProfileEditorControls"
HorizontalExpand="True">
<!-- Left side -->
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
@@ -133,7 +134,7 @@
</BoxContainer>
<!-- Right side -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
<SpriteView Name="SpriteView" Scale="8 8" Margin="4" SizeFlagsStretchRatio="1" />
<profile:ProfilePreviewSpriteView Name="SpriteView" Scale="8 8" Margin="4" SizeFlagsStretchRatio="1" />
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
<Button Name="SpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
<cc:VSeparator Margin="2 0 3 0" />
@@ -77,11 +77,6 @@ namespace Content.Client.Lobby.UI
/// </summary>
public event Action? Save;
/// <summary>
/// Entity used for the profile editor preview
/// </summary>
public EntityUid PreviewDummy;
/// <summary>
/// Temporary override of their selected job, used to preview roles.
/// </summary>
@@ -683,15 +678,10 @@ namespace Content.Client.Lobby.UI
/// </remarks>
private void ReloadPreview()
{
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
if (Profile == null || !_prototypeManager.HasIndex(Profile.Species))
if (Profile == null)
return;
PreviewDummy = _controller.LoadProfileEntity(Profile, JobOverride, ShowClothes.Pressed);
SpriteView.SetEntity(PreviewDummy);
_entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, Profile.Name);
SpriteView.LoadPreview(Profile, JobOverride, ShowClothes.Pressed);
// Check and set the dirty flag to enable the save/reset buttons as appropriate.
SetDirty();
@@ -743,16 +733,15 @@ namespace Content.Client.Lobby.UI
}
}
/// <summary>
/// A slim reload that only updates the entity itself and not any of the job entities, etc.
/// </summary>
private void ReloadProfilePreview()
{
if (Profile == null || !_entManager.EntityExists(PreviewDummy))
if (Profile == null)
return;
_entManager.System<SharedVisualBodySystem>().ApplyProfileTo(PreviewDummy, Profile);
SpriteView.ReloadProfilePreview(Profile);
// Check and set the dirty flag to enable the save/reset buttons as appropriate.
SetDirty();
@@ -1104,13 +1093,6 @@ namespace Content.Client.Lobby.UI
ReloadPreview();
}
protected override void ExitedTree()
{
base.ExitedTree();
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
}
private void SetAge(int newAge)
{
Profile = Profile?.WithAge(newAge);
@@ -1177,7 +1159,7 @@ namespace Content.Client.Lobby.UI
if (!IsDirty)
return;
_entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, newName);
SpriteView.SetName(newName);
}
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
@@ -1397,7 +1379,7 @@ namespace Content.Client.Lobby.UI
// I tried disabling the button but it looks sorta goofy as it only takes a frame or two to save
_imaging = true;
await _entManager.System<ContentSpriteSystem>().Export(PreviewDummy, dir, includeId: false);
await _entManager.System<ContentSpriteSystem>().Export(SpriteView.PreviewDummy, dir, includeId: false);
_imaging = false;
}
@@ -1,7 +1,8 @@
<Control
xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:profile="clr-namespace:Content.Client.Lobby.UI.ProfileEditorControls">
<BoxContainer Name="VBox" Orientation="Vertical">
<controls:NanoHeading Name="Header" Text="{Loc 'lobby-character-preview-panel-header'}">
@@ -10,7 +11,12 @@
Visible="False">
<Label Name="Summary" HorizontalAlignment="Center" Margin="3 3"/>
<BoxContainer Name="ViewBox" Orientation="Horizontal" HorizontalAlignment="Center">
<profile:ProfilePreviewSpriteView Name="ProfilePreviewSpriteView"
OverrideDirection="South"
Scale="4 4"
MaxSize="112 112"
Stretch="Fill"
Access="Public" />
</BoxContainer>
<controls:VSpacer/>
<Button Name="CharacterSetup" Text="{Loc 'lobby-character-preview-panel-character-setup-button'}"
@@ -4,18 +4,16 @@ using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Lobby.UI;
[GenerateTypedNameReferences]
public sealed partial class LobbyCharacterPreviewPanel : Control
{
[Dependency] private readonly IEntityManager _entManager = default!;
public Button CharacterSetupButton => CharacterSetup;
private EntityUid? _previewDummy;
public LobbyCharacterPreviewPanel()
{
RobustXamlLoader.Load(this);
@@ -32,32 +30,4 @@ public sealed partial class LobbyCharacterPreviewPanel : Control
{
Summary.Text = value;
}
public void SetSprite(EntityUid uid)
{
if (_previewDummy != null)
{
_entManager.DeleteEntity(_previewDummy);
}
_previewDummy = uid;
ViewBox.RemoveAllChildren();
var spriteView = new SpriteView
{
OverrideDirection = Direction.South,
Scale = new Vector2(4f, 4f),
MaxSize = new Vector2(112, 112),
Stretch = SpriteView.StretchMode.Fill,
};
spriteView.SetEntity(uid);
ViewBox.AddChild(spriteView);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_entManager.DeleteEntity(_previewDummy);
_previewDummy = null;
}
}
@@ -0,0 +1,182 @@
using System.Linq;
using Content.Client.Humanoid;
using Content.Client.Station;
using Content.Shared.Body;
using Content.Shared.Clothing;
using Content.Shared.GameTicking;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Inventory;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Roles;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Lobby.UI.ProfileEditorControls;
public sealed partial class ProfilePreviewSpriteView
{
/// <summary>
/// A slim reload that only updates the entity itself and not any of the job entities, etc.
/// </summary>
private void ReloadHumanoidEntity(HumanoidCharacterProfile humanoid)
{
if (!EntMan.EntityExists(PreviewDummy) ||
!EntMan.HasComponent<VisualBodyComponent>(PreviewDummy))
return;
EntMan.System<SharedVisualBodySystem>().ApplyProfileTo(PreviewDummy, humanoid);
}
/// <summary>
/// Loads the profile onto a dummy entity.
/// </summary>
private void LoadHumanoidEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes)
{
EntProtoId? previewEntity = null;
if (humanoid != null && jobClothes)
{
job ??= GetPreferredJob(humanoid);
previewEntity = job.JobPreviewEntity ?? (EntProtoId?)job?.JobEntity;
}
if (previewEntity != null)
{
// Special type like borg or AI, do not spawn a human just spawn the entity.
PreviewDummy = EntMan.SpawnEntity(previewEntity, MapCoordinates.Nullspace);
}
else if (humanoid is not null)
{
var dummy = _prototypeManager.Index(humanoid.Species).DollPrototype;
PreviewDummy = EntMan.SpawnEntity(dummy, MapCoordinates.Nullspace);
EntMan.System<SharedVisualBodySystem>().ApplyProfileTo(PreviewDummy, humanoid);
}
else
{
PreviewDummy = EntMan.SpawnEntity(_prototypeManager.Index(HumanoidCharacterProfile.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
}
if (humanoid != null && jobClothes)
{
DebugTools.Assert(job != null);
GiveDummyJobClothes(humanoid, job);
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
{
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), _playerManager.LocalSession, humanoid.Species, EntMan, _prototypeManager);
GiveDummyLoadout(loadout);
}
}
}
/// <summary>
/// Gets the highest priority job for the profile.
/// </summary>
private JobPrototype GetPreferredJob(HumanoidCharacterProfile profile)
{
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
return _prototypeManager.Index<JobPrototype>(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob);
}
private void GiveDummyLoadout(RoleLoadout? roleLoadout)
{
if (roleLoadout == null)
return;
var spawnSys = EntMan.System<StationSpawningSystem>();
foreach (var group in roleLoadout.SelectedLoadouts.Values)
{
foreach (var loadout in group)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
spawnSys.EquipStartingGear(PreviewDummy, loadoutProto);
}
}
}
/// <summary>
/// Applies the specified job's clothes to the dummy.
/// </summary>
private void GiveDummyJobClothes(HumanoidCharacterProfile profile, JobPrototype job)
{
var inventorySys = EntMan.System<InventorySystem>();
if (!inventorySys.TryGetSlots(PreviewDummy, out var slots))
return;
// Apply loadout
if (profile.Loadouts.TryGetValue(job.ID, out var jobLoadout))
{
foreach (var loadouts in jobLoadout.SelectedLoadouts.Values)
{
foreach (var loadout in loadouts)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
// TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
// Try startinggear first
if (_prototypeManager.Resolve(loadoutProto.StartingGear, out var loadoutGear))
{
var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
if (inventorySys.TryUnequip(PreviewDummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntMan.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntMan.SpawnEntity(itemType, MapCoordinates.Nullspace);
inventorySys.TryEquip(PreviewDummy, item, slot.Name, true, true);
}
}
else
{
var itemType = ((IEquipmentLoadout) loadoutProto).GetGear(slot.Name);
if (inventorySys.TryUnequip(PreviewDummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntMan.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntMan.SpawnEntity(itemType, MapCoordinates.Nullspace);
inventorySys.TryEquip(PreviewDummy, item, slot.Name, true, true);
}
}
}
}
}
}
if (!_prototypeManager.Resolve(job.StartingGear, out var gear))
return;
foreach (var slot in slots)
{
var itemType = ((IEquipmentLoadout) gear).GetGear(slot.Name);
if (inventorySys.TryUnequip(PreviewDummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntMan.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntMan.SpawnEntity(itemType, MapCoordinates.Nullspace);
inventorySys.TryEquip(PreviewDummy, item, slot.Name, true, true);
}
}
}
}
@@ -0,0 +1,82 @@
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Lobby.UI.ProfileEditorControls;
public sealed partial class ProfilePreviewSpriteView : SpriteView
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
/// <summary>
/// Entity used for the profile editor preview
/// </summary>
public EntityUid PreviewDummy;
public ProfilePreviewSpriteView()
{
IoCManager.InjectDependencies(this);
}
/// <summary>
/// Reloads the entire dummy entity for preview.
/// </summary>
/// <remarks>
/// This is expensive so not recommended to run if you have a slider.
/// </remarks>
public void LoadPreview(ICharacterProfile profile, JobPrototype? jobOverride = null, bool showClothes = true)
{
EntMan.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
switch (profile)
{
case HumanoidCharacterProfile humanoid:
LoadHumanoidEntity(humanoid, jobOverride, showClothes);
break;
default:
throw new ArgumentException("Only humanoid profiles are implemented in ProfilePreviewSpriteView");
}
SetEntity(PreviewDummy);
SetName(profile.Name);
}
/// <summary>
/// Sets the preview entity's name without reloading anything else.
/// </summary>
public void SetName(string newName)
{
EntMan.System<MetaDataSystem>().SetEntityName(PreviewDummy, newName);
}
/// <summary>
/// A slim reload that only updates the entity itself and not any of the job entities, etc.
/// </summary>
public void ReloadProfilePreview(ICharacterProfile profile)
{
switch (profile)
{
case HumanoidCharacterProfile humanoid:
ReloadHumanoidEntity(humanoid);
break;
default:
throw new ArgumentException("Only humanoid profiles are implemented in ProfilePreviewSpriteView");
}
}
public void ClearPreview()
{
EntMan.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
}
protected override void ExitedTree()
{
base.ExitedTree();
ClearPreview();
}
}
@@ -15,8 +15,8 @@
<ui:OptionSlider Name="SpeechBubbleSpeakerOpacitySlider" Title="{Loc 'ui-options-speech-bubble-speaker-opacity'}" />
<ui:OptionSlider Name="SpeechBubbleBackgroundOpacitySlider" Title="{Loc 'ui-options-speech-bubble-background-opacity'}" />
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
<ui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
<ui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
Example="{Loc 'ui-options-highlights-color-example'}"/>
<Label Text="{Loc 'ui-options-accessability-header-content'}"
StyleClasses="LabelKeyText"/>
@@ -36,12 +36,6 @@ namespace Content.Client.Options.UI.Tabs
private readonly List<Action> _deferCommands = new();
private void HandleToggleUSQWERTYCheckbox(BaseButton.ButtonToggledEventArgs args)
{
_cfg.SetCVar(CVars.DisplayUSQWERTYHotkeys, args.Pressed);
_cfg.SaveToFile();
}
private void InitToggleWalk()
{
if (_cfg.GetCVar(CCVars.ToggleWalk))
@@ -150,8 +144,23 @@ namespace Content.Client.Options.UI.Tabs
KeybindsContainer.AddChild(newCheckBox);
}
void AddToggleCvarCheckBox(string checkBoxName, CVarDef<bool> cvar)
{
CheckBox newCheckBox = new CheckBox() { Text = Loc.GetString(checkBoxName) };
newCheckBox.Pressed = _cfg.GetCVar(cvar);
newCheckBox.OnToggled += (e) =>
{
_cfg.SetCVar(cvar, e.Pressed);
_cfg.SaveToFile();
};
KeybindsContainer.AddChild(newCheckBox);
}
AddHeader("ui-options-header-general");
AddCheckBox("ui-options-hotkey-keymap", _cfg.GetCVar(CVars.DisplayUSQWERTYHotkeys), HandleToggleUSQWERTYCheckbox);
AddToggleCvarCheckBox("ui-options-hotkey-keymap", CVars.DisplayUSQWERTYHotkeys);
AddToggleCvarCheckBox("ui-options-hold-to-attack-melee", CCVars.ControlHoldToAttackMelee);
AddToggleCvarCheckBox("ui-options-hold-to-attack-ranged", CCVars.ControlHoldToAttackRanged);
AddHeader("ui-options-header-movement");
AddButton(EngineKeyFunctions.MoveUp);
@@ -0,0 +1,21 @@
using Content.Client.Light.EntitySystems;
using Content.Shared.Trigger;
using Content.Shared.Trigger.Components.Effects;
using Robust.Shared.Timing;
namespace Content.Client.Trigger.Systems;
/// <summary>
/// This handles...
/// </summary>
public sealed class LightBehaviorOnTriggerSystem : XOnTriggerSystem<LightBehaviorOnTriggerComponent>
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly LightBehaviorSystem _light = default!;
protected override void OnTrigger(Entity<LightBehaviorOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
{
if (_timing.IsFirstTimePredicted)
_light.StartLightBehaviour(target, ent.Comp.Behavior);
}
}
@@ -1,16 +1,12 @@
using System.Numerics;
using Content.Client.Administration.Managers;
using Content.Client.Gameplay;
using Content.Client.Markers;
using Content.Client.Sandbox;
using Content.Client.SubFloor;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.DecalPlacer;
using Content.Client.UserInterface.Systems.Sandbox.Windows;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.Debugging;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.UserInterface;
@@ -30,14 +26,10 @@ namespace Content.Client.UserInterface.Systems.Sandbox;
public sealed class SandboxUIController : UIController, IOnStateChanged<GameplayState>, IOnSystemChanged<SandboxSystem>
{
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly IEyeManager _eye = default!;
[Dependency] private readonly IInputManager _input = default!;
[Dependency] private readonly ILightManager _light = default!;
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[UISystemDependency] private readonly DebugPhysicsSystem _debugPhysics = default!;
[UISystemDependency] private readonly MarkerSystem _marker = default!;
[UISystemDependency] private readonly SandboxSystem _sandbox = default!;
private SandboxWindow? _window;
@@ -117,13 +109,6 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
_window.OnOpen += () => { SandboxButton!.Pressed = true; };
_window.OnClose += () => { SandboxButton!.Pressed = false; };
// TODO: These need moving to opened so at least if they're not synced properly on open they work.
_window.ToggleLightButton.Pressed = !_light.Enabled;
_window.ToggleFovButton.Pressed = !_eye.CurrentEye.DrawFov;
_window.ToggleShadowsButton.Pressed = !_light.DrawShadows;
_window.ShowMarkersButton.Pressed = _marker.MarkersVisible;
_window.ShowBbButton.Pressed = (_debugPhysics.Flags & PhysicsDebugFlags.Shapes) != 0x0;
_window.AiOverlayButton.OnPressed += args =>
{
var player = _player.LocalEntity;
@@ -1,6 +1,11 @@
using Content.Client.SubFloor;
using Content.Client.Markers;
using Content.Client.SubFloor;
using Content.Client.Stylesheets;
using Content.Shared.Silicons.StationAi;
using Robust.Client.AutoGenerated;
using Robust.Client.Debugging;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -10,17 +15,33 @@ namespace Content.Client.UserInterface.Systems.Sandbox.Windows;
public sealed partial class SandboxWindow : DefaultWindow
{
[Dependency] private readonly IEntityManager _entManager = null!;
[Dependency] private readonly IEyeManager _eyeManager = null!;
[Dependency] private readonly ILightManager _lightManager = null!;
[Dependency] private readonly IPlayerManager _playerManager = null!;
private readonly DebugPhysicsSystem _debugPhysicsSystem;
private readonly MarkerSystem _markerSystem;
private readonly SubFloorHideSystem _subFloorSystem;
public SandboxWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_debugPhysicsSystem = _entManager.System<DebugPhysicsSystem>();
_markerSystem = _entManager.System<MarkerSystem>();
_subFloorSystem = _entManager.System<SubFloorHideSystem>();
}
protected override void Opened()
{
base.Opened();
// Make sure state is up to date.
ToggleSubfloorButton.Pressed = _entManager.System<SubFloorHideSystem>().ShowAll;
ToggleSubfloorButton.Pressed = _subFloorSystem.ShowAll;
ToggleLightButton.Pressed = !_lightManager.Enabled;
ToggleFovButton.Pressed = !_eyeManager.CurrentEye.DrawFov;
ToggleShadowsButton.Pressed = !_lightManager.DrawShadows;
ShowMarkersButton.Pressed = _markerSystem.MarkersVisible;
ShowBbButton.Pressed = (_debugPhysicsSystem.Flags & PhysicsDebugFlags.Shapes) != 0x0;
AiOverlayButton.Pressed = _playerManager.LocalEntity is { } player && _entManager.HasComponent<StationAiOverlayComponent>(player);
}
}
+15 -2
View File
@@ -12,18 +12,28 @@ namespace Content.Client.Voting.UI
{
[Dependency] private readonly IVoteManager _voteManager = default!;
private VoteCallMenu? _voteCallMenu;
public VoteCallMenuButton()
{
IoCManager.InjectDependencies(this);
Text = Loc.GetString("ui-vote-menu-button");
ToggleMode = true;
OnPressed += OnOnPressed;
}
private void OnOnPressed(ButtonEventArgs obj)
{
var menu = new VoteCallMenu();
menu.OpenCentered();
if (_voteCallMenu is { IsOpen: true })
{
_voteCallMenu.Close();
return;
}
_voteCallMenu = new VoteCallMenu();
_voteCallMenu.OnClose += () => Pressed = false;
_voteCallMenu.OpenCentered();
}
protected override void EnteredTree()
@@ -38,6 +48,9 @@ namespace Content.Client.Voting.UI
{
base.ExitedTree();
if (_voteCallMenu is { IsOpen: true })
_voteCallMenu.Close();
_voteManager.CanCallVoteChanged += UpdateCanCall;
}
@@ -1,5 +1,6 @@
using System.Linq;
using Content.Client.Gameplay;
using Content.Shared.CCVar;
using Content.Shared.CombatMode;
using Content.Shared.Effects;
using Content.Shared.Hands.Components;
@@ -14,6 +15,7 @@ using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.State;
using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Player;
@@ -31,6 +33,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
private EntityQuery<TransformComponent> _xformQuery;
@@ -76,7 +79,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
var useDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
var altDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.UseSecondary);
if (weapon.AutoAttack || useDown != BoundKeyState.Down && altDown != BoundKeyState.Down)
if (weapon.AutoAttack || useDown != BoundKeyState.Down && altDown != BoundKeyState.Down || _cfg.GetCVar(CCVars.ControlHoldToAttackMelee))
{
if (weapon.Attacking)
{
@@ -4,6 +4,7 @@ using Content.Client.Gameplay;
using Content.Client.Items;
using Content.Client.Weapons.Ranged.Components;
using Content.Shared.Camera;
using Content.Shared.CCVar;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
using Content.Shared.Weapons.Hitscan.Components;
@@ -19,6 +20,7 @@ using Robust.Client.Player;
using Robust.Client.State;
using Robust.Shared.Animations;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -38,6 +40,7 @@ public sealed partial class GunSystem : SharedGunSystem
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IStateManager _state = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedTransformSystem _xform = default!;
@@ -203,11 +206,13 @@ public sealed partial class GunSystem : SharedGunSystem
Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
RaisePredictiveEvent(new RequestShootEvent
{
Target = target,
Coordinates = GetNetCoordinates(coordinates),
Gun = GetNetEntity(gun),
Continuous = _cfg.GetCVar(CCVars.ControlHoldToAttackRanged),
});
}
@@ -240,14 +240,6 @@ namespace Content.Server.Database.Migrations.Postgres
column: "ban_id",
unique: true);
migrationBuilder.AddForeignKey(
name: "FK_server_ban_hit_ban_ban_id",
table: "server_ban_hit",
column: "ban_id",
principalTable: "ban",
principalColumn: "ban_id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.Sql("""
CREATE INDEX "IX_ban_address_address"
ON ban_address
@@ -490,6 +482,14 @@ namespace Content.Server.Database.Migrations.Postgres
AND round_id IS NOT NULL;
""");
migrationBuilder.AddForeignKey(
name: "FK_server_ban_hit_ban_ban_id",
table: "server_ban_hit",
column: "ban_id",
principalTable: "ban",
principalColumn: "ban_id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.DropForeignKey(
name: "FK_server_ban_hit_server_ban_ban_id",
table: "server_ban_hit");
@@ -21,7 +21,7 @@ public sealed class AddActionCommand : LocalizedEntityCommands
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString(Loc.GetString("cmd-addaction-invalid-args")));
shell.WriteError(Loc.GetString("cmd-addaction-invalid-args"));
return;
}
@@ -17,7 +17,7 @@ public sealed class RemoveActionCommand : LocalizedEntityCommands
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString(Loc.GetString("cmd-rmaction-invalid-args")));
shell.WriteError(Loc.GetString("cmd-rmaction-invalid-args"));
return;
}
@@ -16,13 +16,13 @@ namespace Content.Server.Administration.Commands
{
// ReSharper disable once ConvertIfStatementToSwitchStatement
if (args.Length == 1 && TimeSpan.TryParseExact(args[0], ContentLocalizationManager.TimeSpanMinutesFormats, LocalizationManager.DefaultCulture, out var timeSpan))
_roundEndSystem.RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false);
_roundEndSystem.RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, checkCooldown: false);
else if (args.Length == 1)
shell.WriteLine(Loc.GetString("shell-timespan-minutes-must-be-correct"));
else
_roundEndSystem.RequestRoundEnd(shell.Player?.AttachedEntity, false);
_roundEndSystem.RequestRoundEnd(shell.Player?.AttachedEntity, checkCooldown: false);
}
}
@@ -29,7 +29,6 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly ServerDbEntryManager _entryManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
@@ -48,7 +48,7 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem
protected override void DirtyUI(EntityUid uid, GasCanisterComponent? canister = null, NodeContainerComponent? nodeContainer = null)
{
if (!Resolve(uid, ref canister, ref nodeContainer))
if (!Resolve(uid, ref canister, ref nodeContainer, logMissing: false))
return;
var portStatus = false;
@@ -3,7 +3,6 @@ using Content.Server.Hands.Systems;
using Content.Shared.Administration;
using Content.Shared.Hands.Components;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
namespace Content.Server.Body.Commands
{
@@ -11,7 +10,6 @@ namespace Content.Server.Body.Commands
sealed class AddHandCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
private static int _handIdAccumulator;
@@ -312,7 +312,7 @@ namespace Content.Server.Communications
return;
}
_roundEndSystem.RequestRoundEnd(uid);
_roundEndSystem.RequestRoundEnd(mob, uid);
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(mob):player} has called the shuttle.");
}
@@ -321,13 +321,15 @@ namespace Content.Server.Communications
if (!CanCallOrRecall(comp))
return;
if (!CanUse(message.Actor, uid))
var mob = message.Actor;
if (!CanUse(mob, uid))
{
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, message.Actor);
return;
}
_roundEndSystem.CancelRoundEndCountdown(uid);
_roundEndSystem.CancelRoundEndCountdown(mob, uid);
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(message.Actor):player} has recalled the shuttle.");
}
}
@@ -410,7 +410,7 @@ namespace Content.Server.Construction
if ((!temperatureChangeStep.MinTemperature.HasValue || temp >= temperatureChangeStep.MinTemperature.Value) &&
(!temperatureChangeStep.MaxTemperature.HasValue || temp <= temperatureChangeStep.MaxTemperature.Value))
{
return HandleResult.True;
return validation ? HandleResult.Validated : HandleResult.True;
}
return HandleResult.False;
@@ -422,7 +422,7 @@ namespace Content.Server.Construction
break;
if (partAssemblyStep.Condition(uid, EntityManager))
return HandleResult.True;
return validation ? HandleResult.Validated : HandleResult.True;
return HandleResult.False;
}
+1 -1
View File
@@ -215,7 +215,7 @@ namespace Content.Server.Database
{
return document.Deserialize<TValue>();
}
catch (JsonException exception)
catch (JsonException)
{
return null;
}
+3
View File
@@ -34,6 +34,7 @@ using Robust.Server.ServerStatus;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Content.Shared.Emp;
@@ -91,6 +92,8 @@ namespace Content.Server.Entry
var cast = (ServerModuleTestingCallbacks)callback;
cast.ServerBeforeIoC?.Invoke();
}
Dependencies.Resolve<IRobustSerializer>().FloatFlags = SerializerFloatFlags.RemoveReadNan;
}
/// <inheritdoc />
@@ -197,8 +197,8 @@ namespace Content.Server.Forensics
{
Act = () => TryStartCleaning(entity, user, target),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/bubbles.svg.192dpi.png")),
Text = Loc.GetString(Loc.GetString("forensics-verb-text")),
Message = Loc.GetString(Loc.GetString("forensics-verb-message")),
Text = Loc.GetString("forensics-verb-text"),
Message = Loc.GetString("forensics-verb-message"),
// This is important because if its true using the cleaning device will count as touching the object.
DoContactInteraction = false
};
@@ -23,7 +23,7 @@ namespace Content.Server.GameTicking.Commands
{
if (args.Length != 1)
{
shell.WriteLine(Loc.GetString(Loc.GetString($"shell-need-exactly-one-argument")));
shell.WriteLine(Loc.GetString("shell-need-exactly-one-argument"));
return;
}
@@ -109,7 +109,7 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem<XenoborgsRuleComponent>
{
_chatSystem.DispatchStationAnnouncement(station, Loc.GetString("xenoborg-shuttle-call"), colorOverride: Color.BlueViolet);
}
_roundEnd.RequestRoundEnd(null, false, cantRecall: true);
_roundEnd.RequestRoundEnd(null, null, false, cantRecall: true);
xenoborgsRuleComponent.XenoborgShuttleCalled = true;
}
@@ -124,7 +124,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
{
_chat.DispatchStationAnnouncement(station, Loc.GetString("zombie-shuttle-call"), colorOverride: Color.Crimson);
}
_roundEnd.RequestRoundEnd(null, false);
_roundEnd.RequestRoundEnd(checkCooldown: false);
}
// we include dead for this count because we don't want to end the round
@@ -438,20 +438,22 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
if (Deleted(master))
{
Clean(uid, instrument);
continue;
}
var masterActive = activeQuery.CompOrNull(master);
if (masterActive == null)
{
Clean(uid, instrument);
continue;
}
var trans = transformQuery.GetComponent(uid);
var masterTrans = transformQuery.GetComponent(master);
if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f)
)
if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f))
{
Clean(uid, instrument);
continue;
}
}
@@ -64,7 +64,7 @@ public sealed class ToggleNukeCommand : LocalizedCommands
{
if (args.Length == 1)
{
return CompletionResult.FromHint(Loc.GetString(Loc.GetString("cmd-nukearm-1-help")));
return CompletionResult.FromHint(Loc.GetString("cmd-nukearm-1-help"));
}
if (args.Length == 2)
@@ -24,7 +24,7 @@ public sealed class AddObjectiveCommand : LocalizedEntityCommands
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString(Loc.GetString("cmd-addobjective-invalid-args")));
shell.WriteError(Loc.GetString("cmd-addobjective-invalid-args"));
return;
}
@@ -68,6 +68,6 @@ public sealed class AddObjectiveCommand : LocalizedEntityCommands
return CompletionResult.FromHintOptions(
_objectives.Objectives(),
Loc.GetString(Loc.GetString("cmd-add-objective-obj-completion")));
Loc.GetString("cmd-add-objective-obj-completion"));
}
}
@@ -19,7 +19,7 @@ namespace Content.Server.Objectives.Commands
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString(Loc.GetString("cmd-rmobjective-invalid-args")));
shell.WriteError(Loc.GetString("cmd-rmobjective-invalid-args"));
return;
}
@@ -1,118 +1,33 @@
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Interaction;
using Content.Shared.Power.EntitySystems;
using Content.Shared.PowerCell;
using Content.Shared.Radio.EntitySystems;
using Content.Shared.Radio.Components;
using Content.Shared.DeviceNetwork.Systems;
namespace Content.Server.Radio.EntitySystems;
public sealed class JammerSystem : SharedJammerSystem
{
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedDeviceNetworkJammerSystem _jammer = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadioJammerComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ActiveRadioJammerComponent, PowerCellChangedEvent>(OnPowerCellChanged);
SubscribeLocalEvent<RadioSendAttemptEvent>(OnRadioSendAttempt);
}
// TODO: Very important: Make this charge rate based instead of updating every single tick
// See BatteryComponent
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<ActiveRadioJammerComponent, RadioJammerComponent>();
while (query.MoveNext(out var uid, out var _, out var jam))
{
if (_powerCell.TryGetBatteryFromSlot(uid, out var battery))
{
if (!_battery.TryUseCharge(battery.Value.AsNullable(), GetCurrentWattage((uid, jam)) * frameTime))
{
ChangeLEDState(uid, false);
RemComp<ActiveRadioJammerComponent>(uid);
RemComp<DeviceNetworkJammerComponent>(uid);
}
else
{
var chargeFraction = _battery.GetChargeLevel(battery.Value.AsNullable());
var chargeLevel = chargeFraction switch
{
> 0.50f => RadioJammerChargeLevel.High,
< 0.15f => RadioJammerChargeLevel.Low,
_ => RadioJammerChargeLevel.Medium,
};
ChangeChargeLevel(uid, chargeLevel);
}
}
}
}
private void OnActivate(Entity<RadioJammerComponent> ent, ref ActivateInWorldEvent args)
{
if (args.Handled || !args.Complex)
return;
var activated = !HasComp<ActiveRadioJammerComponent>(ent) &&
_powerCell.TryGetBatteryFromSlot(ent.Owner, out var battery) &&
_battery.GetCharge(battery.Value.AsNullable()) > GetCurrentWattage(ent);
if (activated)
{
ChangeLEDState(ent.Owner, true);
EnsureComp<ActiveRadioJammerComponent>(ent);
EnsureComp<DeviceNetworkJammerComponent>(ent, out var jammingComp);
_jammer.SetRange((ent, jammingComp), GetCurrentRange(ent));
_jammer.AddJammableNetwork((ent, jammingComp), DeviceNetworkComponent.DeviceNetIdDefaults.Wireless.ToString());
// Add excluded frequencies using the system method
if (ent.Comp.FrequenciesExcluded != null)
{
foreach (var freq in ent.Comp.FrequenciesExcluded)
{
_jammer.AddExcludedFrequency((ent, jammingComp), (uint)freq);
}
}
}
else
{
ChangeLEDState(ent.Owner, false);
RemCompDeferred<ActiveRadioJammerComponent>(ent);
RemCompDeferred<DeviceNetworkJammerComponent>(ent);
}
var state = Loc.GetString(activated ? "radio-jammer-component-on-state" : "radio-jammer-component-off-state");
var message = Loc.GetString("radio-jammer-component-on-use", ("state", state));
Popup.PopupEntity(message, args.User, args.User);
args.Handled = true;
}
private void OnPowerCellChanged(Entity<ActiveRadioJammerComponent> ent, ref PowerCellChangedEvent args)
{
if (args.Ejected)
{
ChangeLEDState(ent.Owner, false);
RemCompDeferred<ActiveRadioJammerComponent>(ent);
}
SubscribeLocalEvent<RadioReceiveAttemptEvent>(OnRadioReceiveAttempt);
}
private void OnRadioSendAttempt(ref RadioSendAttemptEvent args)
{
if (ShouldCancelSend(args.RadioSource, args.Channel.Frequency))
{
if (ShouldCancel(args.RadioSource, args.Channel.Frequency))
args.Cancelled = true;
}
}
private bool ShouldCancelSend(EntityUid sourceUid, int frequency)
private void OnRadioReceiveAttempt(ref RadioReceiveAttemptEvent args)
{
if (ShouldCancel(args.RadioReceiver, args.Channel.Frequency))
args.Cancelled = true;
}
private bool ShouldCancel(EntityUid sourceUid, int frequency)
{
var source = Transform(sourceUid).Coordinates;
var query = EntityQueryEnumerator<ActiveRadioJammerComponent, RadioJammerComponent, TransformComponent>();
@@ -120,7 +35,7 @@ public sealed class JammerSystem : SharedJammerSystem
while (query.MoveNext(out var uid, out _, out var jam, out var transform))
{
// Check if this jammer excludes the frequency
if (jam.FrequenciesExcluded != null && jam.FrequenciesExcluded.Contains(frequency))
if (jam.FrequenciesExcluded.Contains(frequency))
continue;
if (_transform.InRange(source, transform.Coordinates, GetCurrentRange((uid, jam))))
+17 -21
View File
@@ -135,12 +135,13 @@ namespace Content.Server.RoundEnd
/// <summary>
/// Starts the process of ending the round by calling evac
/// </summary>
/// <param name="requester"></param>
/// <param name="requester">who called evac</param>
/// <param name="machine">machine used to call evac</param>
/// <param name="checkCooldown"></param>
/// <param name="text">text in the announcement of shuttle calling</param>
/// <param name="name">name in the announcement of shuttle calling</param>
/// <param name="cantRecall">if the station shouldn't be able to recall the shuttle</param>
public void RequestRoundEnd(EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
public void RequestRoundEnd(EntityUid? requester = null, EntityUid? machine = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
{
var duration = DefaultCountdownDuration;
@@ -155,19 +156,20 @@ namespace Content.Server.RoundEnd
}
}
RequestRoundEnd(duration, requester, checkCooldown, text, name, cantRecall);
RequestRoundEnd(duration, requester, machine, checkCooldown, text, name, cantRecall);
}
/// <summary>
/// Starts the process of ending the round by calling evac
/// </summary>
/// <param name="countdownTime">time for evac to arrive</param>
/// <param name="requester"></param>
/// <param name="requester">who called evac</param>
/// <param name="machine">machine used to call evac</param>
/// <param name="checkCooldown"></param>
/// <param name="text">text in the announcement of shuttle calling</param>
/// <param name="name">name in the announcement of shuttle calling</param>
/// <param name="cantRecall">if the station shouldn't be able to recall the shuttle</param>
public void RequestRoundEnd(TimeSpan countdownTime, EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
public void RequestRoundEnd(TimeSpan countdownTime, EntityUid? requester = null, EntityUid? machine = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
{
if (_gameTicker.RunLevel != GameRunLevel.InRound)
return;
@@ -181,14 +183,11 @@ namespace Content.Server.RoundEnd
_countdownTokenSource = new();
CantRecall = cantRecall;
var what = machine != null ? $" with {ToPrettyString(machine.Value):entity} " : "";
if (requester != null)
{
_adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called by {ToPrettyString(requester.Value):user}");
}
_adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called by {ToPrettyString(requester.Value):player}{what}");
else
{
_adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called");
}
_adminLogger.Add(LogType.ShuttleCalled, LogImpact.High, $"Shuttle called{what}");
// I originally had these set up here but somehow time gets passed as 0 to Loc so IDEK.
int time;
@@ -241,7 +240,7 @@ namespace Content.Server.RoundEnd
}
}
public void CancelRoundEndCountdown(EntityUid? requester = null, bool forceRecall = false)
public void CancelRoundEndCountdown(EntityUid? requester = null, EntityUid? machine = null, bool forceRecall = false)
{
if (_gameTicker.RunLevel != GameRunLevel.InRound)
return;
@@ -255,14 +254,11 @@ namespace Content.Server.RoundEnd
_countdownTokenSource.Cancel();
_countdownTokenSource = null;
var what = machine != null ? $" with {ToPrettyString(machine.Value):entity} " : "";
if (requester != null)
{
_adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled by {ToPrettyString(requester.Value):user}");
}
_adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled by {ToPrettyString(requester.Value):player}{what}");
else
{
_adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled");
}
_adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled{what}");
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("round-end-system-shuttle-recalled-announcement"),
Loc.GetString("round-end-system-shuttle-sender-announcement"), false, colorOverride: Color.Gold);
@@ -352,8 +348,8 @@ namespace Content.Server.RoundEnd
}
else
{
RequestRoundEnd(time, null, false, textCall,
Loc.GetString(sender));
RequestRoundEnd(time, checkCooldown: false, text: textCall,
name: Loc.GetString(sender));
}
break;
}
@@ -390,7 +386,7 @@ namespace Content.Server.RoundEnd
if (!_shuttle.EmergencyShuttleArrived && ExpectedCountdownEnd is null)
{
_autoCalledBefore = true; // Corvax-Announcements: Move before call RequestRoundEnd to play correct announcement sound type
RequestRoundEnd(null, false, "round-end-system-shuttle-auto-called-announcement");
RequestRoundEnd(checkCooldown: false, text: "round-end-system-shuttle-auto-called-announcement");
}
// Always reset auto-call in case of a recall.
@@ -270,8 +270,7 @@ public sealed partial class EmergencyShuttleSystem
return;
}
// TODO: This is fucking bad
if (!component.AuthorizedEntities.Remove(MetaData(idCard).EntityName))
if (!component.AuthorizedEntities.Remove(idCard.Owner))
return;
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle early launch REPEAL by {args.Actor:user}");
@@ -291,10 +290,13 @@ public sealed partial class EmergencyShuttleSystem
return;
}
// TODO: This is fucking bad
if (!component.AuthorizedEntities.Add(MetaData(idCard).EntityName))
var idCardUid = idCard.Owner;
if (component.AuthorizedEntities.ContainsKey(idCardUid))
return;
component.AuthorizedEntities[idCardUid] = MetaData(idCard).EntityName;
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle early launch AUTH by {args.Actor:user}");
var remaining = component.AuthorizationsRequired - component.AuthorizedEntities.Count;
@@ -337,7 +339,7 @@ public sealed partial class EmergencyShuttleSystem
{
var auths = new List<string>();
foreach (var auth in component.AuthorizedEntities)
foreach (var auth in component.AuthorizedEntities.Values)
{
auths.Add(auth);
}
@@ -7,41 +7,100 @@ namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(GasLeakRule))]
public sealed partial class GasLeakRuleComponent : Component
{
public readonly Gas[] LeakableGases =
/// <summary>
/// Gas types that can be selected for the leak event.
/// </summary>
[DataField]
public Gas[] LeakableGases =
{
Gas.Ammonia,
Gas.Plasma,
Gas.Tritium,
Gas.Frezon,
Gas.WaterVapor, // the fog
Gas.WaterVapor,
};
/// <summary>
/// Running cooldown of how much time until another leak.
/// Time remaining until the next gas addition to the leak tile.
/// </summary>
[DataField]
public float TimeUntilLeak;
/// <summary>
/// How long between more gas being added to the tile.
/// Fixed interval in seconds between gas additions to the leak tile.
/// </summary>
[DataField]
public float LeakCooldown = 1.0f;
// Event variables
/// <summary>
/// The station where the leak is located.
/// </summary>
[DataField]
public EntityUid TargetStation;
public EntityUid TargetGrid;
public Vector2i TargetTile;
public EntityCoordinates TargetCoords;
public bool FoundTile;
public Gas LeakGas;
public float MolesPerSecond;
public readonly int MinimumMolesPerSecond = 80;
/// <summary>
/// Don't want to make it too fast to give people time to flee.
/// The specific grid where the leak is located.
/// </summary>
[DataField]
public EntityUid TargetGrid;
/// <summary>
/// The tile coordinates where the leak is located.
/// </summary>
[DataField]
public Vector2i TargetTile;
/// <summary>
/// The world coordinates of the leak location.
/// </summary>
[DataField]
public EntityCoordinates TargetCoords;
/// <summary>
/// Whether a suitable tile for leaking has been found.
/// </summary>
[DataField]
public bool FoundTile;
/// <summary>
/// The specific gas type currently leaking.
/// </summary>
[DataField]
public Gas LeakGas;
/// <summary>
/// Current leak rate in moles per second.
/// </summary>
[DataField]
public float MolesPerSecond;
/// <summary>
/// Minimum leak rate in moles per second.
/// </summary>
[DataField]
public int MinimumMolesPerSecond = 80;
/// <summary>
/// Maximum leak rate in moles per second. Limited to give people time to flee.
/// </summary>
[DataField]
public int MaximumMolesPerSecond = 200;
/// <summary>
/// Minimum total amount of gas to leak over the entire event duration.
/// </summary>
[DataField]
public int MinimumGas = 1000;
/// <summary>
/// Maximum total amount of gas to leak over the entire event duration.
/// </summary>
[DataField]
public int MaximumGas = 4000;
/// <summary>
/// Chance to create an ignition spark when the event ends.
/// </summary>
[DataField]
public float SparkChance = 0.05f;
}
@@ -9,25 +9,59 @@ namespace Content.Server.StationEvents.Components;
public sealed partial class PowerGridCheckRuleComponent : Component
{
/// <summary>
/// Default sound of the announcement when power is back on.
/// Default sound for power restoration announcement.
/// </summary>
private static readonly ProtoId<SoundCollectionPrototype> DefaultPowerOn = new("PowerOn");
/// <summary>
/// Sound of the announcement to play when power is back on.
/// Sound to play when power is restored.
/// </summary>
[DataField]
public SoundSpecifier PowerOnSound = new SoundCollectionSpecifier(DefaultPowerOn, AudioParams.Default.WithVolume(-4f));
/// <summary>
/// Token source for cancelling the power restoration announcement.
/// </summary>
public CancellationTokenSource? AnnounceCancelToken;
/// <summary>
/// Station affected by the power grid event.
/// </summary>
[DataField]
public EntityUid AffectedStation;
public readonly List<EntityUid> Powered = new();
public readonly List<EntityUid> Unpowered = new();
/// <summary>
/// List of APC entities that will be sequentially turned off during the event.
/// </summary>
[DataField]
public List<EntityUid> Powered = new();
/// <summary>
/// List of APC entities that have been turned off.
/// </summary>
[DataField]
public List<EntityUid> Unpowered = new();
/// <summary>
/// Time delay in seconds before starting to turn off APCs.
/// </summary>
[DataField]
public float SecondsUntilOff = 30.0f;
/// <summary>
/// Number of APC toggles to process per second during the shutdown phase.
/// Dynamically calculated based on total APC count and <see cref="SecondsUntilOff"/>.
/// </summary>
public int NumberPerSecond = 0;
/// <summary>
/// Computed time interval between APC toggles.
/// </summary>
public float UpdateRate => 1.0f / NumberPerSecond;
/// <summary>
/// Accumulated frame time to track when to process the next APC toggle.
/// </summary>
[DataField]
public float FrameTimeAccumulator = 0.0f;
}
@@ -17,7 +17,7 @@ public sealed class BreakerFlipRule : StationEventSystem<BreakerFlipRuleComponen
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}"))));
var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")));
stationEvent.StartAnnouncement = str;
base.Added(uid, component, gameRule, args);
@@ -306,10 +306,15 @@ namespace Content.Server.Voting.Managers
var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
if (ticker.CanUpdateMap())
{
if (_gameMapManager.TrySelectMapIfEligible(picked.ID))
if (_gameMapManager.CheckMapExists(picked.ID))
{
_gameMapManager.SelectMap(picked.ID);
ticker.UpdateInfoText();
}
else
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-map-invalid", ("winner", maps[picked])));
}
}
else
{
+1 -1
View File
@@ -217,7 +217,7 @@ namespace Content.Shared.Atmos
/// <summary>
/// Amount of heat released per mole of burnt hydrogen or tritium (hydrogen isotope)
/// </summary>
public const float FireHydrogenEnergyReleased = 284e3f; // hydrogen is 284 kJ/mol
public const float FireHydrogenEnergyReleased = 284e4f;
public const float FireMinimumTemperatureToExist = T0C + 100f;
public const float FireMinimumTemperatureToSpread = T0C + 150f;
public const float FireSpreadRadiosityScale = 0.85f;
@@ -23,6 +23,8 @@ public abstract class SharedGasCanisterSystem : EntitySystem
SubscribeLocalEvent<GasCanisterComponent, EntRemovedFromContainerMessage>(OnCanisterContainerModified);
SubscribeLocalEvent<GasCanisterComponent, ItemSlotInsertAttemptEvent>(OnCanisterInsertAttempt);
SubscribeLocalEvent<GasCanisterComponent, ComponentStartup>(OnCanisterStartup);
SubscribeLocalEvent<GasCanisterComponent, MapInitEvent>(OnCanisterMapInit);
SubscribeLocalEvent<GasCanisterComponent, BoundUIOpenedEvent>(OnCanisterUIOpened);
// Bound UI subscriptions
SubscribeLocalEvent<GasCanisterComponent, GasCanisterHoldingTankEjectMessage>(OnHoldingTankEjectMessage);
@@ -30,6 +32,19 @@ public abstract class SharedGasCanisterSystem : EntitySystem
SubscribeLocalEvent<GasCanisterComponent, GasCanisterChangeReleaseValveMessage>(OnCanisterChangeReleaseValve);
}
private void OnCanisterUIOpened(Entity<GasCanisterComponent> ent, ref BoundUIOpenedEvent args)
{
// Fixes all canisters not populating UI elements before MapInit. Mappers rejoice
// We still need to DirtyUI after MapInit because this has latency, bad UX for players.
DirtyUI(ent.Owner, ent);
}
private void OnCanisterMapInit(Entity<GasCanisterComponent> ent, ref MapInitEvent args)
{
// Fixes empty canisters not populating UI elements
DirtyUI(ent.Owner, ent);
}
private void OnCanisterStartup(Entity<GasCanisterComponent> ent, ref ComponentStartup args)
{
// Ensure container
@@ -25,7 +25,6 @@ public abstract class SharedRottingSystem : EntitySystem
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnPerishableExamined);
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
@@ -63,14 +62,6 @@ public abstract class SharedRottingSystem : EntitySystem
args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(perishable, EntityManager))));
}
private void OnShutdown(Entity<RottingComponent> ent, ref ComponentShutdown args)
{
if (TryComp<PerishableComponent>(ent, out var perishable))
{
perishable.RotNextUpdate = TimeSpan.Zero;
}
}
private void OnRottingMobStateChanged(EntityUid uid, RottingComponent component, MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead)
-1
View File
@@ -7,7 +7,6 @@ namespace Content.Shared.Body;
public sealed class InitialBodySystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
@@ -70,4 +70,17 @@ public sealed partial class CCVars
/// </summary>
public static readonly CVarDef<bool> NestedStorage =
CVarDef.Create("control.nested_storage", true, CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// If enabled, melee weapons that have click-to-attack patterns (unarmed, slow weapons) will continue attacking if the button is held.
/// </summary>
public static readonly CVarDef<bool> ControlHoldToAttackMelee =
CVarDef.Create("control.hold_to_attack_melee", false, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If enabled, ranged weapons that have click-to-attack patterns (burst and semi-auto guns) will continue attacking if the button is held.
/// </summary>
public static readonly CVarDef<bool> ControlHoldToAttackRanged =
CVarDef.Create("control.hold_to_attack_ranged", false, CVar.CLIENTONLY | CVar.ARCHIVE);
}
@@ -7,7 +7,6 @@ using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
namespace Content.Shared.Chemistry.Reaction;
@@ -17,7 +16,6 @@ public sealed partial class ReactionMixerSystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly INetManager _net = default!;
public override void Initialize()
{
@@ -25,8 +25,8 @@ public sealed class InsulatedSystem : EntitySystem
_examine.AddHoverExamineVerb(args,
component,
Loc.GetString("identity-block-examinable-verb-text"),
Loc.GetString("identity-block-examinable-verb-text-message"),
Loc.GetString("insulated-examinable-verb-text"),
Loc.GetString("insulated-examinable-verb-text-message"),
iconTexture
);
}
@@ -104,11 +104,6 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
};
return new HumanoidCharacterAppearance(newEyeColor, newSkinColor, new());
float RandomizeColor(float channel)
{
return MathHelper.Clamp01(channel + random.Next(-25, 25) / 100f);
}
}
public static Color ClampColor(Color color)
@@ -9,7 +9,6 @@ using Content.Shared.Popups;
using Content.Shared.Stacks;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
namespace Content.Shared.Kitchen.EntitySystems;
@@ -22,7 +21,6 @@ internal sealed class HandheldGrinderSystem : EntitySystem
[Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedPuddleSystem _puddle = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
@@ -129,6 +129,11 @@ namespace Content.Shared.Maps
/// </summary>
[DataField("indestructible")] public bool Indestructible = false;
/// <summary>
/// Hide this tile in the tile placement editor.
/// </summary>
[DataField] public bool EditorHidden { get; private set; } = false;
public void AssignTileId(ushort id)
{
TileId = id;
@@ -46,6 +46,7 @@ public abstract class SharedChameleonProjectorSystem : EntitySystem
SubscribeLocalEvent<ChameleonDisguiseComponent, DamageChangedEvent>(OnDisguiseDamaged);
SubscribeLocalEvent<ChameleonDisguiseComponent, InsertIntoEntityStorageAttemptEvent>(OnDisguiseInsertAttempt);
SubscribeLocalEvent<ChameleonDisguiseComponent, ComponentShutdown>(OnDisguiseShutdown);
SubscribeLocalEvent<ChameleonDisguiseComponent, BeforeGettingEquippedHandEvent>(OnDisguiseBeforeEquippedHand);
SubscribeLocalEvent<ChameleonDisguisedComponent, EntGotInsertedIntoContainerMessage>(OnDisguisedInserted);
@@ -86,6 +87,12 @@ public abstract class SharedChameleonProjectorSystem : EntitySystem
_actions.RemoveProvidedActions(ent.Comp.User, ent.Comp.Projector);
}
private void OnDisguiseBeforeEquippedHand(Entity<ChameleonDisguiseComponent> ent, ref BeforeGettingEquippedHandEvent args)
{
args.Cancelled = true;
TryReveal(ent.Comp.User);
}
#endregion
#region Disguised player
@@ -11,11 +11,11 @@ public sealed partial class PowerCellSystem
[PublicAPI]
public void SetDrawEnabled(Entity<PowerCellDrawComponent?> ent, bool enabled)
{
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled)
return;
ent.Comp.Enabled = enabled;
Dirty(ent, ent.Comp);
if (Resolve(ent, ref ent.Comp, false) && ent.Comp.Enabled != enabled)
{
ent.Comp.Enabled = enabled;
Dirty(ent, ent.Comp);
}
if (TryGetBatteryFromSlot(ent.Owner, out var battery))
_battery.RefreshChargeRate(battery.Value.AsNullable());
@@ -36,9 +36,7 @@ public sealed class ToggleCellDrawSystem : EntitySystem
private void OnToggled(Entity<ToggleCellDrawComponent> ent, ref ItemToggledEvent args)
{
var uid = ent.Owner;
var draw = Comp<PowerCellDrawComponent>(uid);
_cell.SetDrawEnabled((uid, draw), args.Activated);
_cell.SetDrawEnabled(ent.Owner, args.Activated);
}
private void OnEmpty(Entity<ToggleCellDrawComponent> ent, ref PowerCellSlotEmptyEvent args)
@@ -1,25 +1,66 @@
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Examine;
using Content.Shared.Radio.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Item.ItemToggle;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Power;
namespace Content.Shared.Radio.EntitySystems;
public abstract class SharedJammerSystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly ItemToggleSystem _itemToggle = default!;
[Dependency] private readonly SharedDeviceNetworkJammerSystem _jammer = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadioJammerComponent, ItemToggledEvent>(OnItemToggle);
SubscribeLocalEvent<RadioJammerComponent, RefreshChargeRateEvent>(OnRefreshChargeRate);
SubscribeLocalEvent<RadioJammerComponent, GetVerbsEvent<Verb>>(OnGetVerb);
SubscribeLocalEvent<RadioJammerComponent, ExaminedEvent>(OnExamine);
}
private void OnItemToggle(Entity<RadioJammerComponent> entity, ref ItemToggledEvent args)
{
if (args.Activated)
{
EnsureComp<ActiveRadioJammerComponent>(entity);
EnsureComp<DeviceNetworkJammerComponent>(entity, out var jammingComp);
_jammer.SetRange((entity, jammingComp), GetCurrentRange(entity));
_jammer.AddJammableNetwork((entity, jammingComp), DeviceNetworkComponent.DeviceNetIdDefaults.Wireless.ToString());
// Add excluded frequencies using the system method
foreach (var freq in entity.Comp.FrequenciesExcluded)
{
_jammer.AddExcludedFrequency((entity, jammingComp), (uint)freq);
}
}
else
{
RemCompDeferred<ActiveRadioJammerComponent>(entity);
RemCompDeferred<DeviceNetworkJammerComponent>(entity);
}
if (args.User == null)
return;
var state = Loc.GetString(args.Activated ? "radio-jammer-component-on-state" : "radio-jammer-component-off-state");
var message = Loc.GetString("radio-jammer-component-on-use", ("state", state));
_popup.PopupPredicted(message, args.User.Value, args.User.Value);
}
private void OnRefreshChargeRate(Entity<RadioJammerComponent> entity, ref RefreshChargeRateEvent args)
{
if (_itemToggle.IsActivated(entity.Owner))
args.NewChargeRate -= GetCurrentWattage(entity);
}
private void OnGetVerb(Entity<RadioJammerComponent> entity, ref GetVerbsEvent<Verb> args)
{
if (!args.CanAccess || !args.CanInteract)
@@ -47,7 +88,7 @@ public abstract class SharedJammerSystem : EntitySystem
// The range should be updated when it turns on again!
_jammer.TrySetRange(entity.Owner, GetCurrentRange(entity));
Popup.PopupClient(Loc.GetString(setting.Message), user, user);
_popup.PopupClient(Loc.GetString(setting.Message), user, user);
},
Text = Loc.GetString(setting.Name),
};
@@ -58,37 +99,26 @@ public abstract class SharedJammerSystem : EntitySystem
private void OnExamine(Entity<RadioJammerComponent> ent, ref ExaminedEvent args)
{
if (args.IsInDetailsRange)
{
var powerIndicator = HasComp<ActiveRadioJammerComponent>(ent)
? Loc.GetString("radio-jammer-component-examine-on-state")
: Loc.GetString("radio-jammer-component-examine-off-state");
args.PushMarkup(powerIndicator);
if (!args.IsInDetailsRange)
return;
var powerLevel = Loc.GetString(ent.Comp.Settings[ent.Comp.SelectedPowerLevel].Name);
var switchIndicator = Loc.GetString("radio-jammer-component-switch-setting", ("powerLevel", powerLevel));
args.PushMarkup(switchIndicator);
}
var powerIndicator = _itemToggle.IsActivated(ent.Owner)
? Loc.GetString("radio-jammer-component-examine-on-state")
: Loc.GetString("radio-jammer-component-examine-off-state");
args.PushMarkup(powerIndicator);
var powerLevel = Loc.GetString(ent.Comp.Settings[ent.Comp.SelectedPowerLevel].Name);
var switchIndicator = Loc.GetString("radio-jammer-component-switch-setting", ("powerLevel", powerLevel));
args.PushMarkup(switchIndicator);
}
public float GetCurrentWattage(Entity<RadioJammerComponent> jammer)
private float GetCurrentWattage(Entity<RadioJammerComponent> jammer)
{
return jammer.Comp.Settings[jammer.Comp.SelectedPowerLevel].Wattage;
}
public float GetCurrentRange(Entity<RadioJammerComponent> jammer)
protected float GetCurrentRange(Entity<RadioJammerComponent> jammer)
{
return jammer.Comp.Settings[jammer.Comp.SelectedPowerLevel].Range;
}
protected void ChangeLEDState(Entity<AppearanceComponent?> ent, bool isLEDOn)
{
_appearance.SetData(ent, RadioJammerVisuals.LEDOn, isLEDOn, ent.Comp);
}
protected void ChangeChargeLevel(Entity<AppearanceComponent?> ent, RadioJammerChargeLevel chargeLevel)
{
_appearance.SetData(ent, RadioJammerVisuals.ChargeLevel, chargeLevel, ent.Comp);
}
}
@@ -5,13 +5,13 @@ namespace Content.Shared.Shuttles.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class EmergencyShuttleConsoleComponent : Component
{
// TODO: Okay doing it by string is kinda suss but also ID card tracking doesn't seem to be robust enough
/// <summary>
/// ID cards that have been used to authorize an early launch.
/// Key is the UID of the ID card,
/// value is the card's name at the time of authorization.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("authorized")]
public HashSet<string> AuthorizedEntities = new();
public Dictionary<EntityUid, string> AuthorizedEntities = new();
[ViewVariables(VVAccess.ReadWrite), DataField("authorizationsRequired")]
public int AuthorizationsRequired = 3;
@@ -33,7 +33,6 @@ public abstract class SharedPortalSystem : EntitySystem
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedGrapplingGunSystem _grappling = default!;
[Dependency] private readonly SharedJointSystem _joints = default!;
private const string PortalFixture = "portalFixture";
@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Trigger.Components.Effects;
/// <summary>
/// Plays a light behavior on the target when this trigger is activated, of note is that the entity needs a PointLightComponent
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class LightBehaviorOnTriggerComponent : BaseXOnTriggerComponent
{
/// <summary>
/// The light behavior we're triggering.
/// </summary>
[DataField(required: true)]
public string Behavior = string.Empty;
}
@@ -1,3 +1,4 @@
using System.Numerics;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
@@ -12,10 +13,10 @@ namespace Content.Shared.Trigger.Components.Effects;
public sealed partial class ScramOnTriggerComponent : BaseXOnTriggerComponent
{
/// <summary>
/// Up to how far to teleport the entity.
/// Up to how far to teleport the entity. Represented with X as Min Radius, and Y as Max Radius
/// </summary>
[DataField, AutoNetworkedField]
public float TeleportRadius = 100f;
public Vector2 TeleportRadius = new (10f, 15f);
/// <summary>
/// the sound to play when teleporting.
@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Shared.Maps;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
@@ -50,7 +51,7 @@ public sealed class ScramOnTriggerSystem : XOnTriggerSystem<ScramOnTriggerCompon
/// null if no tile is found within a certain number of tries.
/// </summary>
/// <remarks> Trends towards the outer radius. Compensates for small grids. </remarks>
private EntityCoordinates? SelectRandomTileInRange(EntityUid uid, float radius, int tries = 40, PhysicsComponent? physicsComponent = null)
private EntityCoordinates? SelectRandomTileInRange(EntityUid uid, Vector2 radius, int tries = 40, PhysicsComponent? physicsComponent = null)
{
var userCoords = Transform(uid).Coordinates;
EntityCoordinates? targetCoords = null;
@@ -68,7 +69,7 @@ public sealed class ScramOnTriggerSystem : XOnTriggerSystem<ScramOnTriggerCompon
// i = A percentage based on the current try count, which results in each
// subsequent try landing closer and closer towards the entity.
// Beneficial for smaller maps, especially when the radius is large.
var distance = radius * MathF.Sqrt(_random.NextFloat()) * (1 - (float)i / tries);
var distance = (radius.Y - radius.X) * MathF.Sqrt(_random.NextFloat()) * (1 - (float)i / tries) + radius.X;
// We then offset the user coords from a random angle * distance
var tempTargetCoords = userCoords.Offset(_random.NextAngle().ToVec() * distance);
@@ -9,7 +9,24 @@ namespace Content.Shared.Weapons.Ranged.Events;
[Serializable, NetSerializable]
public sealed class RequestShootEvent : EntityEventArgs
{
/// <summary>
/// The gun shooting.
/// </summary>
public NetEntity Gun;
/// <summary>
/// The location the player is shooting at.
/// </summary>
public NetCoordinates Coordinates;
/// <summary>
/// The target the player is shooting at, if any.
/// </summary>
public NetEntity? Target;
/// <summary>
/// If the client wants to continuously shoot.
/// If true, the gun will continue firing until a stop event is sent from the client.
/// </summary>
public bool Continuous;
}
@@ -158,6 +158,8 @@ public abstract partial class SharedGunSystem : EntitySystem
gun.Comp.ShootCoordinates = GetCoordinates(msg.Coordinates);
gun.Comp.Target = GetEntity(msg.Target);
AttemptShoot(user.Value, gun);
if (msg.Continuous)
gun.Comp.ShotCounter = 0;
}
private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args)
@@ -121,7 +121,7 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
crusher.Crushing = true;
crusher.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
crusher.CrushEndTime = _timing.CurTime + crusher.CrushDuration;
crusher.CrushingSoundEntity = AudioSystem.PlayPvs(crusher.CrushingSound, ent)?.Entity;
crusher.CrushingSoundEntity = AudioSystem.PlayPredicted(crusher.CrushingSound, ent, user)?.Entity ?? crusher.CrushingSoundEntity;
_appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, true);
Dirty(ent, ent.Comp1);
}
@@ -135,10 +135,7 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
_appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, false);
if (early)
{
AudioSystem.Stop(ent.Comp.CrushingSoundEntity);
ent.Comp.CrushingSoundEntity = null;
}
ent.Comp.CrushingSoundEntity = AudioSystem.Stop(ent.Comp.CrushingSoundEntity);
Dirty(ent, ent.Comp);
}
+8
View File
@@ -1605,5 +1605,13 @@ Entries:
id: 195
time: '2026-01-16T22:24:15.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42443
- author: Samuka
changes:
- message: shuttle called and recalled logs now show who recalled or called the
shuttle
type: Fix
id: 196
time: '2026-01-24T19:15:40.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/41557
Name: Admin
Order: 3
+129 -99
View File
@@ -1,103 +1,4 @@
Entries:
- author: imatsoup
changes:
- message: Butterflies can no longer be infected by Romerol or become Romerol zombies.
type: Tweak
id: 8948
time: '2025-09-10T21:48:53.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40265
- author: Princess-Cheeseballs
changes:
- message: Cardboard boxes can no longer freely move in space.
type: Fix
id: 8949
time: '2025-09-10T22:13:59.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40260
- author: TsjipTsjip LuckyShotPictures IProduceWidgets
changes:
- message: Admin shuttles have been added to the repo, and can be found in /Maps/Shuttles/AdminSpawn/..
type: Add
id: 8950
time: '2025-09-11T01:01:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32139
- author: ToastEnjoyer
changes:
- message: Fixed the laser carbine not being labeled as contraband
type: Fix
id: 8951
time: '2025-09-11T02:37:10.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40253
- author: IProduceWidgets
changes:
- message: Tennis balls! Found in arcade machines, maintenance and the cargo toy
crate!
type: Add
id: 8952
time: '2025-09-11T10:57:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40143
- author: 5tickman
changes:
- message: Food and ingredient item sizes have been adjusted.
type: Tweak
- message: Mimes now start with a Nutribrick instead of a Baguette.
type: Tweak
- message: The combat bakery kit is now a 4x4 sized box.
type: Tweak
id: 8953
time: '2025-09-11T11:37:25.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/39203
- author: aada
changes:
- message: Cups, bottles, mugs, and other drinks have had minor changes. Most are
now destructible.
type: Tweak
id: 8954
time: '2025-09-11T15:59:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/39221
- author: luegamer
changes:
- message: SmartFridge Circuitboards are now printable, SmartFridge destruction
no longer deletes all contents
type: Add
id: 8955
time: '2025-09-11T19:59:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/39879
- author: SurrealShibe
changes:
- message: Toilet seats are now displayed on the correct layer.
type: Fix
id: 8956
time: '2025-09-12T22:47:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40313
- author: Princess-Cheeseballs
changes:
- message: Chameleon Projector will no longer cause you to be permanently slowed
down
type: Fix
id: 8957
time: '2025-09-12T23:23:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/37960
- author: FungiFellow
changes:
- message: Cockroaches can Gib when Stepped on
type: Add
id: 8958
time: '2025-09-13T07:01:14.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40103
- author: Huaqas
changes:
- message: Vulpkanin now have Undergarments.
type: Add
id: 8959
time: '2025-09-13T17:36:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40321
- author: MissKay1994
changes:
- message: Greatly reduced lethality of Man-O-War shuttle
type: Tweak
id: 8960
time: '2025-09-14T05:44:32.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/40339
- author: ScarKy0
changes:
- message: Vulpkanin now use the corrent undergarments when "Censor character nudity"
@@ -3977,3 +3878,132 @@ Entries:
id: 9454
time: '2026-01-23T02:05:59.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42172
- author: Princess-Cheeseballs
changes:
- message: Added the Syndimov Lawboard to the Traitor Uplink.
type: Add
- message: You can now get Pythons and Cat Ears in the Syndicate Snack box.
type: Add
- message: Removed Whitehole Grenade, Necronomicon, Explosive Banana Peel and Super
Surplus crates from the uplink.
type: Remove
- message: Removed Singularity Beacon and Antimov Lawboard from the Traitor Uplink.
type: Remove
- message: Several less used uplink items have had their costs reduced.
type: Tweak
- message: Clustersoap and Cluster Bananas now explodes into soaplets when thrown
and no longer needs to be primed. The Clustersoap has been reduced to 1 TC as
well.
type: Tweak
- message: Martyr Cyborgs now start glowing red before exploding.
type: Tweak
- message: Scram Implanter's range has been crunched down to 10-15 tiles from 0-100.
type: Tweak
- message: Rigged Boxing Gloves have had their overall time to stun increased but
are also now available for all Traitors to purchase.
type: Tweak
- message: Combat bakery kit now comes with a flatpack microwave instead of a machine
board. The throwing croissants and baguette have been made equivalent in damage
to other Traitor uplink items as well.
type: Tweak
- message: Holoclowns no longer try to attack their host.
type: Fix
- message: Singularity grenade shouldn't throw you inside walls anymore
type: Fix
id: 9455
time: '2026-01-24T00:26:43.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42582
- author: alexalexmax
changes:
- message: Tasers no longer slow holoparasites down.
type: Fix
id: 9456
time: '2026-01-24T04:18:37.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42315
- author: Hitlinemoss
changes:
- message: The "Auto-fill the highlights with the character's information" accessibility
option has been renamed to "Automatically set the highlights list based on your
character's name and job".
type: Tweak
- message: More automatic job-text highlights have been added.
type: Tweak
id: 9457
time: '2026-01-24T21:06:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42630
- author: SlamBamActionman
changes:
- message: Added an toggle for "hold-to-attack" under the accessibility tab.
type: Add
id: 9458
time: '2026-01-25T22:30:52.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42596
- author: roryflowers
changes:
- message: Tritium's thermal output is increased to its previous value, to reenable
maxcap explosives and make the TEG easier to run.
type: Fix
id: 9459
time: '2026-01-26T00:39:55.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42641
- author: SlamBamActionman
changes:
- message: Crowbars are now consistent with other 1x2 items in terms of storage,
e.g. in pockets.
type: Tweak
id: 9460
time: '2026-01-26T01:35:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42585
- author: ScholarNZL
changes:
- message: Fixed a dev environment crash related to construction event validation.
type: Fix
id: 9461
time: '2026-01-26T06:12:02.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/41396
- author: B_Kirill
changes:
- message: Fixed being able to pick up chameleon projector disguises via context
menu.
type: Fix
id: 9462
time: '2026-01-26T14:03:39.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42656
- author: Ohelig
changes:
- message: Empty gas canisters now have a populated UI.
type: Fix
id: 9463
time: '2026-01-26T15:02:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42616
- author: notquitehadouken
changes:
- message: Doors close on clown spider webs now
type: Fix
id: 9464
time: '2026-01-26T15:19:39.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42589
- author: Marlyn
changes:
- message: Opporozidone no longer has instarot issues that get worse the longer
the server has been running
type: Fix
id: 9465
time: '2026-01-26T15:45:24.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42472
- author: PJB3005
changes:
- message: Job lizard plushies can be found in various lockers around the station
instead of being loadout items.
type: Tweak
id: 9466
time: '2026-01-26T16:15:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42545
- author: Princess-Cheeseballs
changes:
- message: Boxing gloves in the uplink are now the rigged variants instead of the
normal variants.
type: Fix
id: 9467
time: '2026-01-26T20:25:53.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/42662
@@ -3,6 +3,10 @@
[game]
hostname = "[EN][Testing] Wizard's Den Vulture [US East 2]"
desc = "Official English testing server for Space Station 14.\nVanilla, roleplay ruleset.\n\nYou can play with the newest changes to the game here, but these changes may not be final or stable, and may be reverted before the next stable release.\nPlease report bugs on our GitHub, forum, or community Discord."
panic_bunker.enabled = false
panic_bunker.disable_with_admins = false
panic_bunker.enable_without_admins = false
panic_bunker.custom_reason = ""
[hub]
tags = "lang:en,region:am_n_e,rp:low"
@@ -14,3 +18,6 @@ force_client_hud_version_watermark = true
[chat]
motd = "\n########################################################\n\n[font size=17]This is a test server. You can play with the newest changes to the game, but these [color=red]changes may not be final or stable[/color], and may be reverted. Please report bugs via our GitHub, forum, or community Discord.[/font]\n\n########################################################\n"
[feedback]
valid_origins = "wizden wizden_master"
@@ -58,3 +58,6 @@ max_explosion_range = 10
privacy_policy_link = "https://spacestation14.com/about/privacy/#game-server-privacy-policy"
privacy_policy_identifier = "wizden"
privacy_policy_version = "2025-01-19"
[feedback]
valid_origins = "wizden"
File diff suppressed because one or more lines are too long
+32 -31
View File
@@ -2,54 +2,55 @@
highlights-captain = Captain, "Cap", Bridge, Command
highlights-head-of-personnel = Head Of Personnel, "HoP", Service, Bridge, Command
highlights-chief-engineer = Chief Engineer, "CE", Engineering, Engineer, "Engi", Bridge, Command
highlights-chief-medical-officer = Chief Medical Officer, "CMO", MedBay, "Med", Bridge, Command
highlights-head-of-security = Head of Security, "HoS", Security, "Sec", Bridge, Command
highlights-quartermaster = Quartermaster, "QM", Cargo, Bridge, Command
highlights-research-director = Research Director, "RD", Science, "Sci", Bridge, Command
highlights-chief-medical-officer = Chief Medical Officer, "CMO", Medbay, Medical, "Med", Bridge, Command
highlights-head-of-security = Head of Security, "HoS", Armory, Security, "Sec", Bridge, Command
highlights-quartermaster = Quartermaster, "QM", Cargo, Supply, Bridge, Command
highlights-research-director = Research Director, "RD", Science, "Sci", "RND", "R&D", Bridge, Command
# Security
highlights-detective = Detective, "Det", Security, "Sec"
highlights-security-cadet = Security Cadet, Secoff, Cadet, Security, "Sec"
highlights-security-officer = Security Officer, Secoff, Officer, Security, "Sec"
highlights-warden = Warden, "Ward", Security, "Sec"
highlights-detective = Detective, "Det", Armory, Security, "Sec"
highlights-security-cadet = Security Cadet, Secoff, Cadet, Armory, Security, "Sec"
highlights-security-officer = Security Officer, Secoff, Officer, Armory, Security, "Sec"
highlights-warden = Warden, "Ward", Brig, Genpop, Jail, "Prison", Armory, Security, "Sec"
# Cargo
highlights-cargo-technician = Cargo Technician, Cargo Tech, "Cargo"
highlights-salvage-specialist = Salvage Specialist, Salvager, Salvage, "Salv", "Cargo", Miner
highlights-cargo-technician = Cargo Technician, Cargo Tech, "Cargo", Supply
highlights-salvage-specialist = Salvage Specialist, Salvager, Salvage, "Salv", Miner, "Cargo", Supply
# Engineering
highlights-atmospheric-technician = Atmospheric Technician, Atmos tech, Atmospheric, Engineering, "Atmos", "Engi"
highlights-atmospheric-technician = Atmospheric Technician, Atmos Tech, Atmospheric, Engineering, "Atmos", "Engi"
highlights-station-engineer = Station Engineer, Engineering, Engineer, "Engi"
highlights-technical-assistant = Technical Assistant, Tech Assistant, Engineering, Engineer, "Engi"
# Medical
highlights-chemist = Chemist, Chemistry, "Chem", MedBay, "Med"
highlights-medical-doctor = Medical Doctor, Doctor, "Doc", MedBay, "Med"
highlights-medical-intern = Medical Intern, "Doc", Intern, MedBay, "Med"
highlights-paramedic = Paramedic, "Para", MedBay, "Med"
highlights-chemist = Chemist, Chemistry, "Chem", Medbay, Medical, "Med"
highlights-medical-doctor = Medical Doctor, Doctor, "Doc", Medbay, Medical, "Med"
highlights-medical-intern = Medical Intern, Intern, Medbay, Medical, "Med"
highlights-paramedic = Paramedic, "Para", "Medic", Medbay, Medical, "Med"
# Science
highlights-scientist = Scientist, Science, "Sci"
highlights-research-assistant = Research Assistant, Science, "Sci"
highlights-scientist = Scientist, Science, "Sci", "RND", "R&D"
highlights-research-assistant = Research Assistant, Science, "Sci", "RND", "R&D"
# Civilian
highlights-bartender = Bartender, Barkeeper, Barkeep, "Bar"
highlights-botanist = Botanist, Botany, Hydroponics
highlights-chaplain = Chaplain, "Chap", Chapel
highlights-chef = Chef, "Cook", Kitchen
highlights-clown = Clown, Jester
highlights-janitor = Janitor, "Jani"
highlights-lawyer = Lawyer, Attorney
highlights-librarian = Librarian, Library
highlights-mime = Mime
highlights-passenger = Passenger, Greytider, "Tider"
highlights-service-worker = Service Worker
highlights-bartender = Bartender, Barkeeper, Barkeep, "Bar", Service, "Serv"
highlights-botanist = Botanist, Botany, Hydroponics, Service, "Serv"
highlights-chaplain = Chaplain, "Chap", Chapel, Service, "Serv"
highlights-chef = Chef, "Cook", Kitchen, Service, "Serv"
highlights-clown = Clown, Theatre, Theater, Service, "Serv"
highlights-janitor = Janitor, "Jani", Service, "Serv"
highlights-lawyer = Lawyer, Attorney, "Law", Service, "Serv"
highlights-librarian = Librarian, Library, Service, "Serv"
highlights-mime = Mime, Theatre, Theater, Service, "Serv"
highlights-musician = Musician, "Music", Theatre, Theater, Service, "Serv"
highlights-passenger = Passenger, Greytider, Graytider, "Tider", "Tide"
highlights-service-worker = Service Worker, Service, "Serv"
# Station-specific
highlights-reporter = Reporter, Journalist
highlights-psychologist = Psychologist, Psychology
highlights-reporter = Reporter, Journalist, Newsroom, News
highlights-psychologist = Psychologist, Psychology, "Psych", Medbay, Medical, "Med"
# Silicon
highlights-personal-ai = Personal AI, "pAI"
highlights-cyborg = Cyborg, Silicon, Borg
highlights-cyborg = Cyborg, Silicon, Borg, Robotics, "Robot"
highlights-station-ai = Station AI, Silicon, "AI", "sAI"
@@ -1,2 +1,2 @@
identity-block-examinable-verb-text = Insulatated
identity-block-examinable-verb-text-message = This item appears to be electrically insulated. It should protect the wearer from shocks.
insulated-examinable-verb-text = Insulated
insulated-examinable-verb-text-message = This item appears to be electrically insulated. It should protect the wearer from shocks.
@@ -49,7 +49,7 @@ ui-options-misc-label = Misc
ui-options-interface-label = Interface
ui-options-auto-fill-highlights = Auto-fill the highlights with the character's information
ui-options-auto-fill-highlights = Automatically set the highlights list based on your character's name and job
ui-options-highlights-color = Highlights color:
ui-options-highlights-color-example = This is highlighted text.
ui-options-show-held-item = Show held item next to cursor
@@ -108,6 +108,9 @@ ui-options-hud-layout = HUD layout:
## Controls menu
ui-options-hold-to-attack-melee = Hold to attack (melee)
ui-options-hold-to-attack-ranged = Hold to attack (ranged)
ui-options-binds-reset-all = Reset ALL keybinds
ui-options-binds-explanation = Click to change binding, right-click to clear
ui-options-unbound = Unbound
@@ -202,6 +202,9 @@ uplink-singularity-beacon-desc = A device that attracts singularities. Has to be
uplink-antimov-law-name = Antimov Law Circuit
uplink-antimov-law-desc = A very dangerous Lawset to use when you want to cause the A.I. to go haywire, use with caution.
uplink-syndimov-law-name = Syndi Law Circuit
uplink-syndimov-law-desc = A subversive Lawset to use when you want to turn the A.I. to your side, use as much as possible.
# Implants
uplink-storage-implanter-name = Storage Implanter
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
@@ -236,8 +239,8 @@ uplink-micro-bomb-implanter-desc = Explode on death or manual activation with th
uplink-radio-implanter-name = Radio Implanter
uplink-radio-implanter-desc = Implants a Syndicate radio, allowing covert communication without a headset.
uplink-voice-mask-implanter-name = Voice Mask Implanter
uplink-voice-mask-implanter-desc = Modifies your vocal cords to be able to sound like anyone you could imagine.
uplink-voice-mask-implanter-name = Identity Mask Implanter
uplink-voice-mask-implanter-desc = Modifies your vocal cords and facial structure to be able to mimic anyone you could imagine.
# Bundles
uplink-observation-kit-name = Observation Kit
@@ -295,7 +298,7 @@ uplink-starter-kit-desc = Contains 40 telecrystals of basic operative gear. For
uplink-toolbox-name = Toolbox
uplink-toolbox-desc = A full compliment of tools for the mechanically inclined traitor. Includes a pair of insulated combat gloves and a syndicate gas mask as well.
uplink-syndicate-jaws-of-life-name = Jaws Of Life
uplink-syndicate-jaws-of-life-name = Jaws Of Death
uplink-syndicate-jaws-of-life-desc = A combined prying and cutting tool. Useful for entering the station or its departments. Can even open bolted doors!
uplink-duffel-surgery-name = Surgical Duffel Bag
@@ -333,7 +336,7 @@ uplink-chimp-upgrade-kit-name = C.H.I.M.P. Handcannon Upgrade Chip
uplink-chimp-upgrade-kit-desc = Insert this chip into a standard C.H.I.M.P. handcannon to allow it to fire omega particles. Omega particles inflict severe burns and cause anomalies to go supercritical.
uplink-proximity-mine-name = Proximity Mine
uplink-proximity-mine-desc = A mine disguised as a wet floor sign.
uplink-proximity-mine-desc = A throwable mine disguised as a wet floor sign. Detonates on contact with almost anything, safety always off.
uplink-disposable-turret-name = Disposable Ballistic Turret
uplink-disposable-turret-desc = Looks and functions like a normal electrical toolbox. Upon hitting the toolbox it will transform into a ballistic turret, theoretically shooting at anyone except members of the syndicate. Can be turned back into a toolbox using a screwdriver and repaired using a wrench.
@@ -20,7 +20,7 @@ ui-vote-map-tie = Tie for map vote! Picking... { $picked }
ui-vote-map-win = { $winner } won the map vote!
ui-vote-map-notlobby = Voting for maps is only valid in the pre-round lobby!
ui-vote-map-notlobby-time = Voting for maps is only valid in the pre-round lobby with { $time } remaining!
ui-vote-map-invalid = { $winner } became invalid after the map vote! It will not be selected!
# Votekick votes
ui-vote-votekick-unknown-initiator = A player
+66 -185
View File
@@ -1,11 +1,11 @@
meta:
format: 7
category: Map
engineVersion: 266.0.0
engineVersion: 270.0.0
forkId: ""
forkVersion: ""
time: 08/31/2025 05:06:28
entityCount: 3156
time: 12/29/2025 08:52:17
entityCount: 3158
maps:
- 23
grids:
@@ -1580,143 +1580,47 @@ entities:
uniqueMixes:
- volume: 2500
immutable: True
moles:
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
moles: {}
- volume: 2500
temperature: 293.15
moles:
- 21.824879
- 82.10312
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 21.824879
Nitrogen: 82.10312
- volume: 2500
temperature: 235
moles:
- 21.824879
- 82.10312
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 21.824879
Nitrogen: 82.10312
- volume: 2500
temperature: 293.14975
moles:
- 20.078888
- 75.53487
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 20.078888
Nitrogen: 75.53487
- volume: 2500
temperature: 293.15
moles: {}
- volume: 2500
temperature: 293.15
moles:
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Nitrogen: 6666.982
- volume: 2500
temperature: 293.15
moles:
- 0
- 6666.982
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 6666.982
- volume: 2500
temperature: 293.15
moles:
- 6666.982
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- volume: 2500
temperature: 293.15
moles:
- 0
- 0
- 0
- 6666.982
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Plasma: 6666.982
- volume: 2500
temperature: 5000
moles:
- 6666.982
- 0
- 0
- 6666.982
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 6666.982
Plasma: 6666.982
chunkSize: 4
- type: BecomesStation
id: Dev
- type: ImplicitRoof
- type: ExplosionAirtightGrid
- uid: 23
components:
- type: MetaData
@@ -1809,37 +1713,16 @@ entities:
- volume: 2500
temperature: 293.15
moles:
- 21.824879
- 82.10312
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 21.824879
Nitrogen: 82.10312
- volume: 2500
immutable: True
moles:
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
moles: {}
chunkSize: 4
- type: GasTileOverlay
- type: RadiationGridResistance
- type: ImplicitRoof
- type: ExplosionAirtightGrid
- uid: 2869
components:
- type: MetaData
@@ -2106,14 +1989,6 @@ entities:
parent: 1
- type: Fixtures
fixtures: {}
- uid: 1220
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 51.5,37.5
parent: 1
- type: Fixtures
fixtures: {}
- uid: 1394
components:
- type: Transform
@@ -3064,6 +2939,15 @@ entities:
parent: 1
- type: Fixtures
fixtures: {}
- proto: BaseAPC
entities:
- uid: 2753
components:
- type: Transform
pos: -0.5,1.5
parent: 2709
- type: Fixtures
fixtures: {}
- proto: BaseBallBat
entities:
- uid: 2686
@@ -6404,6 +6288,11 @@ entities:
- type: Transform
pos: 77.5,11.5
parent: 1
- uid: 3158
components:
- type: Transform
pos: 52.5,37.5
parent: 1
- proto: CableApcStack
entities:
- uid: 3139
@@ -9831,18 +9720,8 @@ entities:
immutable: False
temperature: 293.14673
moles:
- 1.7459903
- 6.568249
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 1.7459903
Nitrogen: 6.568249
- type: ContainerContainer
containers:
entity_storage: !type:Container
@@ -9923,13 +9802,25 @@ entities:
- type: Transform
pos: 70.5,15.5
parent: 1
- proto: DebugAPC
- proto: DebugAPCRecharging
entities:
- uid: 2753
- uid: 1220
components:
- type: Transform
pos: -0.5,1.5
parent: 2709
rot: 3.141592653589793 rad
pos: 51.5,37.5
parent: 1
- type: AccessReader
accessListsOriginal:
- - Engineering
- type: Fixtures
fixtures: {}
- uid: 2115
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 52.5,37.5
parent: 1
- type: Fixtures
fixtures: {}
- proto: DebugGenerator
@@ -9943,15 +9834,6 @@ entities:
supplyRampRate: 500000
supplyRampTolerance: 5000
supplyRate: 3000000
- proto: DebugSubstation
entities:
- uid: 2296
components:
- type: Transform
pos: 43.5,32.5
parent: 1
- type: Battery
maxCharge: 25000000
- proto: DefaultStationBeaconMedical
entities:
- uid: 2882
@@ -11884,18 +11766,8 @@ entities:
immutable: False
temperature: 293.14673
moles:
- 1.7459903
- 6.568249
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
Oxygen: 1.7459903
Nitrogen: 6.568249
- proto: LockerChiefEngineerFilledHardsuit
entities:
- uid: 1332
@@ -15902,6 +15774,15 @@ entities:
- type: Transform
pos: 77.5,6.5
parent: 1
- proto: SubstationBasicEmpty
entities:
- uid: 2296
components:
- type: Transform
pos: 43.5,32.5
parent: 1
- type: Battery
maxCharge: 25000000
- proto: SubstationWallBasic
entities:
- uid: 2800
@@ -95,8 +95,8 @@
containers:
storagebase: !type:AllSelector
children:
- id: SyndicateMicrowaveFlatpack
- id: WeaponCroissant
amount: 2
- id: WeaponBaguette
- id: SyndicateMicrowaveMachineCircuitboard
- id: PaperWrittenCombatBakeryKit
@@ -21,6 +21,8 @@
prob: 0.3
rolls: !type:ConstantNumberSelector
value: 3
- id: PlushieLizardJobSalvagespecialist
prob: 0.02
- type: entity
id: LockerSalvageSpecialistFilledHardsuit
@@ -159,6 +159,8 @@
- id: RCD
- id: RCDAmmo
- id: AirGrenade
- id: PlushieLizardJobAtmospherictechnician
prob: 0.02
- type: entityTable
id: FillAtmosphericsHardsuit
@@ -202,6 +204,10 @@
- id: RCD
- id: RCDAmmo
- id: MetalFoamGrenade
- id: PlushieLizardJobTechnicalassistant
prob: 0.02
- id: PlushieLizardJobStationengineer
prob: 0.02
- type: entityTable
id: FillEngineerHardsuit
@@ -12,6 +12,8 @@
- id: DoorRemoteCargo
- id: AstroNavCartridge
- id: ClothingHandsKnuckleDustersQM
- id: PlushieLizardJobQuartermaster
prob: 0.02
- id: PrinterDocFlatpack # Corvax-Printer
- type: entity
@@ -50,6 +52,8 @@
conditions:
- !type:PlayerCountCondition
max: 15
- id: PlushieLizardJobCaptain
prob: 0.02
# Corvax-Resprite-Start
- id: ClothingHeadCaptainHat
- id: ClothingOuterCoatCaptain
@@ -135,6 +139,8 @@
- id: HoPIDCard
- id: WeaponDisabler
- id: ClothingEyesHudCommand
- id: PlushieLizardJobHeadofpersonnel
prob: 0.02
- id: ClothingOuterCoatHOP # Corvax-Resprite
- id: PrinterDocFlatpack # Corvax-Printer
@@ -167,6 +173,8 @@
- id: RCDAmmo
- id: RubberStampCE
- id: MetalFoamGrenade
- id: PlushieLizardJobChiefengineer
prob: 0.02
# Hardsuit table, used for suit storage as well
- type: entityTable
@@ -221,6 +229,8 @@
- id: DoorRemoteMedical
- id: BoxEncryptionKeyMedical
- id: BoxCMOCircuitboards
- id: PlushieLizardJobChiefmedicalofficer
prob: 0.02
# Hardsuit table, used for suit storage as well
- type: entityTable
@@ -270,6 +280,8 @@
- id: DoorRemoteResearch
- id: HandTeleporter
- id: RubberStampRd
- id: PlushieLizardJobResearchdirector
prob: 0.02
# Hardsuit table, used for suit storage as well
- type: entityTable
@@ -330,6 +342,8 @@
- id: WeaponTaser
- id: WantedListCartridge
- id: DrinkHosFlask
- id: PlushieLizardJobHeadofsecurity
prob: 0.02
# Corvax-Start
- id: ClothingHeadHatCapHoS
prob: 0.5
@@ -49,6 +49,12 @@
- id: HandheldHealthAnalyzer
- id: ChemistryBottleLaughter # Widely recognized as the best medicine :o)
prob: 0.01
- id: PlushieLizardJobMedicaldoctor
prob: 0.02
- id: PlushieLizardJobMedicalintern
prob: 0.02
- id: PlushieLizardJobPsychologist
prob: 0.02
- type: entity
parent: LockerMedical
@@ -94,6 +100,8 @@
- id: SprayBottle
- id: Bucket
- id: DrinkCartonMilk
- id: PlushieLizardJobChemist
prob: 0.02
- type: entity
parent: LockerChemistry
@@ -120,6 +128,8 @@
- id: RollerBedSpawnFolded
- id: CheapRollerBedSpawnFolded
- id: EmergencyRollerBedSpawnFolded
- id: PlushieLizardJobParamedic
prob: 0.02
- type: entity
parent: LockerParamedic
@@ -206,6 +206,10 @@
prob: 0.10
rolls: !type:RangeNumberSelector
range: 1, 2
- !type:NestedSelector
tableId: MaintenancePlushies
prob: 0.02
# Weapons
- !type:NestedSelector
tableId: MaintWeaponTable
@@ -218,6 +222,19 @@
- id: ClosetMaintenanceFilledRandom
prob: 0.01
- type: entityTable
id: MaintenancePlushies
table: !type:GroupSelector
children:
- id: PlushieLizardJobBoxer
- id: PlushieLizardJobClown
- id: PlushieLizardJobLibrarian
- id: PlushieLizardJobMime
- id: PlushieLizardJobMusician
- id: PlushieLizardJobPassenger
- id: PlushieLizardJobReporter
- id: PlushieLizardJobZookeeper
- type: entity
id: ClosetMaintenanceFilledRandom
suffix: Filled, Random
@@ -20,3 +20,7 @@
- id: NodeScanner
- id: NetworkConfigurator
prob: 0.5
- id: PlushieLizardJobResearchassistant
prob: 0.02
- id: PlushieLizardJobScientist
prob: 0.02
@@ -49,6 +49,8 @@
amount: 2
- id: NetworkConfigurator
- id: Binoculars
- id: PlushieLizardJobWarden
prob: 0.02
- type: entityTable
id: FillLockerWardenHarduit
@@ -93,6 +95,10 @@
prob: 0.6
- id: BookSpaceLaw
prob: 0.5
- id: PlushieLizardJobSecuritycadet
prob: 0.02
- id: PlushieLizardJobSecurityofficer
prob: 0.02
- id: ClothingOuterCoatSecurityOvercoat # Corvax-SecFashion
prob: 0.2
@@ -185,6 +191,8 @@
- id: HoloprojectorSecurity
- id: BoxEvidenceMarkers
- id: HandLabeler
- id: PlushieLizardJobDetective
prob: 0.02
- type: entity
id: ClosetBombFilled
@@ -25,6 +25,10 @@
children:
- id: BoxBeanbag
- id: RagItem
- id: PlushieLizardJobBartender
prob: 0.02
- id: PlushieLizardJobServiceworker
prob: 0.02
#- type: entity
# id: LockerFormalFilled
@@ -64,6 +68,10 @@
children:
- id: FoodCondimentPacketSalt
- id: FoodCondimentPacketPepper
- id: PlushieLizardJobChef
prob: 0.02
- id: PlushieLizardJobServiceworker
prob: 0.02
- type: entity
id: ClosetJanitorFilled
@@ -94,6 +102,8 @@
- id: FlashlightLantern
- id: Plunger
- id: WireBrush
- id: PlushieLizardJobJanitor
prob: 0.02
- type: entity
id: ClosetLegalFilled
@@ -112,6 +122,8 @@
- id: BriefcaseBrownFilled
prob: 0.80
- id: ClothingOuterRobesJudge
- id: PlushieLizardJobLawyer
prob: 0.02
- type: entity
id: LockerBotanistFilled
@@ -140,6 +152,10 @@
- id: ClothingUniformOveralls
- id: ClothingHeadHatTrucker
prob: 0.1
- id: PlushieLizardJobBotanist
prob: 0.02
- id: PlushieLizardJobServiceworker
prob: 0.02
- type: entity
id: LockerBotanistLoot
@@ -33,6 +33,8 @@
prob: 0.5
- id: ClothingOuterCoatBomber
prob: 0.3
- id: PlushieLizardJobPassenger
prob: 0.02
- type: entity
id: WardrobeMixedFilled
@@ -267,6 +267,8 @@
- id: ClothingUniformJumpsuitChaplain
- id: ClothingShoesColorBlack
- id: ClothingUniformJumpskirtChaplain
- id: PlushieLizardJobChaplain
prob: 0.02
- type: entity
id: WardrobeSecurityFilled
@@ -313,6 +315,8 @@
- id: ClothingUniformJumpskirtCargo
- id: ClothingHandsGlovesFingerless
- id: AppraisalTool
- id: PlushieLizardJobCargotechnician
prob: 0.02
- type: entity
id: WardrobeSalvageFilled
+110 -179
View File
@@ -124,24 +124,6 @@
categories:
- UplinkWeaponry
- type: listing
id: UplinkDisposableTurret
name: uplink-disposable-turret-name
description: uplink-disposable-turret-desc
productEntity: ToolboxElectricalTurretFilled
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 3
cost:
Telecrystal: 6
categories:
- UplinkWeaponry
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- type: listing
id: UplinkEshield
name: uplink-eshield-name
@@ -161,6 +143,20 @@
tags:
- NukeOpsUplink
- type: listing
id: uplinkRiggedBoxingGloves
name: uplink-rigged-boxing-gloves-name
description: uplink-rigged-boxing-gloves-desc
icon: { sprite: Clothing/Hands/Gloves/Boxing/boxingblue.rsi, state: icon }
productEntity: ClothingHandsGlovesBoxingRiggedBlue # TODO Replace this with the random spawner when it's not bugged
discountCategory: veryRareDiscounts
discountDownTo:
Telecrystal: 8
cost:
Telecrystal: 10
categories:
- UplinkWeaponry
- type: listing
id: UplinkSniperBundle
name: uplink-sniper-bundle-name
@@ -408,19 +404,6 @@
categories:
- UplinkDisruption
- type: listing
id: UplinkWhiteholeGrenade
name: uplink-whitehole-grenade-name
description: uplink-whitehole-grenade-desc
productEntity: WhiteholeGrenade
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
categories:
- UplinkDisruption
- type: listing
id: UplinkGrenadePenguin
name: uplink-penguin-grenade-name
@@ -1049,6 +1032,24 @@
categories:
- UplinkDisruption
- type: listing
id: UplinkDisposableTurret
name: uplink-disposable-turret-name
description: uplink-disposable-turret-desc
productEntity: ToolboxElectricalTurretFilled
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 2
cost:
Telecrystal: 4
categories:
- UplinkDisruption
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- type: listing
id: UplinkSyndicateMartyrModule
name: uplink-syndicate-martyr-module-name
@@ -1079,10 +1080,8 @@
description: uplink-slipocalypse-clustersoap-desc
productEntity: SlipocalypseClusterSoap
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
Telecrystal: 1
categories:
- UplinkDisruption
@@ -1137,23 +1136,18 @@
cost:
Telecrystal: 8
categories:
- UplinkDisruption
conditions:
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- UplinkExplosives
- type: listing
id: UplinkAntimovCircuitBoard
name: uplink-antimov-law-name
description: uplink-antimov-law-desc
productEntity: AntimovCircuitBoard
id: UplinkSyndimovCircuitBoard
name: uplink-syndimov-law-name
description: uplink-syndimov-law-desc
productEntity: SyndimovCircuitBoard
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 10
Telecrystal: 6
cost:
Telecrystal: 14
Telecrystal: 8
categories:
- UplinkDisruption
conditions:
@@ -1163,7 +1157,7 @@
- NukeOpsUplink
- type: listing
id: UplinkNukieAntimovCircuitBoard
id: UplinkAntimovCircuitBoard
name: uplink-antimov-law-name
description: uplink-antimov-law-desc
productEntity: AntimovCircuitBoard
@@ -1199,25 +1193,6 @@
components:
- SurplusBundle
- type: listing
id: UplinkSuperSurplusBundle
name: uplink-super-surplus-bundle-name
description: uplink-super-surplus-bundle-desc
productEntity: CrateSyndicateSuperSurplusBundle
cost:
Telecrystal: 40
categories:
- UplinkDisruption
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: UplinkStarterKit
name: uplink-starter-kit-name
@@ -1249,14 +1224,26 @@
Telecrystal: 12
categories:
- UplinkDisruption
conditions:
- !type:StoreWhitelistCondition
whitelist:
tags:
- NukeOpsUplink
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: UplinkCameraBug
name: uplink-cameraBug-name
description: uplink-cameraBug-desc
productEntity: CameraBug
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 4
Telecrystal: 2
categories:
- UplinkDisruption
@@ -1272,7 +1259,7 @@
discountDownTo:
Telecrystal: 8
cost:
Telecrystal: 14
Telecrystal: 12
categories:
- UplinkAllies
conditions:
@@ -1291,7 +1278,7 @@
discountDownTo:
Telecrystal: 7
cost:
Telecrystal: 14
Telecrystal: 11
categories:
- UplinkAllies
conditions:
@@ -1375,11 +1362,8 @@
name: uplink-carp-dehydrated-name
description: uplink-carp-dehydrated-desc
productEntity: DehydratedSpaceCarp
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
Telecrystal: 1
categories:
- UplinkAllies
conditions:
@@ -1445,9 +1429,9 @@
productEntity: FreedomImplanter
discountCategory: veryRareDiscounts
discountDownTo:
Telecrystal: 3
Telecrystal: 1
cost:
Telecrystal: 5
Telecrystal: 2
categories:
- UplinkImplants
@@ -1459,9 +1443,9 @@
productEntity: ScramImplanter
discountCategory: veryRareDiscounts
discountDownTo:
Telecrystal: 4
Telecrystal: 1
cost:
Telecrystal: 6 # it's a gamble that may kill you easily so 6 TC per 2 uses, second one more of a backup
Telecrystal: 2 # it's a gamble that may kill you easily so 1 TC per use.
categories:
- UplinkImplants
@@ -1475,7 +1459,7 @@
discountDownTo:
Telecrystal: 2
cost:
Telecrystal: 5
Telecrystal: 3
categories:
- UplinkImplants
@@ -1569,11 +1553,8 @@
description: uplink-uplink-implanter-desc
icon: { sprite: /Textures/Objects/Devices/communication.rsi, state: old-radio }
productEntity: UplinkImplanter
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
Telecrystal: 1
categories:
- UplinkImplants
conditions:
@@ -1818,16 +1799,6 @@
tags:
- NukeOpsUplink
- type: listing
id: UplinkClothingConductingGloves
name: uplink-clothing-conducting-gloves-name
description: uplink-clothing-conducting-gloves-desc
productEntity: ClothingHandsGlovesConducting
cost:
Telecrystal: 1
categories:
- UplinkPointless
- type: listing
id: UplinkBackpackSyndicate
name: uplink-backpack-syndicate-name
@@ -1860,16 +1831,23 @@
categories:
- UplinkPointless
- type: listing
id: UplinkClothingConductingGloves
name: uplink-clothing-conducting-gloves-name
description: uplink-clothing-conducting-gloves-desc
productEntity: ClothingHandsGlovesConducting
cost:
Telecrystal: 1
categories:
- UplinkPointless
- type: listing
id: UplinkRevolverCapGun
name: uplink-revolver-cap-gun-name
description: uplink-revolver-cap-gun-desc
productEntity: RevolverCapGun
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 2
cost:
Telecrystal: 4
Telecrystal: 1
categories:
- UplinkPointless
@@ -1878,13 +1856,11 @@
name: uplink-syndicate-stamp-name
description: uplink-syndicate-stamp-desc
productEntity: RubberStampSyndicate
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkCatEars
@@ -1895,16 +1871,22 @@
Telecrystal: 26
categories:
- UplinkPointless
conditions:
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: UplinkOutlawHat
name: uplink-outlaw-hat-name
description: uplink-outlaw-hat-desc
productEntity: ClothingHeadHatOutlawHat
cost:
Telecrystal: 1
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkOutlawGlasses
@@ -1922,7 +1904,7 @@
description: uplink-costume-pyjama-desc
productEntity: ClothingBackpackDuffelSyndicatePyjamaBundle
cost:
Telecrystal: 4
Telecrystal: 2
categories:
- UplinkPointless
@@ -1942,7 +1924,7 @@
description: uplink-carp-suit-bundle-desc
productEntity: ClothingBackpackDuffelSyndicateCarpSuit
cost:
Telecrystal: 4
Telecrystal: 2
categories:
- UplinkPointless
@@ -1951,20 +1933,22 @@
name: uplink-operative-suit-name
description: uplink-operative-suit-desc
productEntity: ClothingUniformJumpsuitOperative
cost:
Telecrystal: 1
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkOperativeSkirt
name: uplink-operative-skirt-name
description: uplink-operative-skirt-desc
productEntity: ClothingUniformJumpskirtOperative
cost:
Telecrystal: 1
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkBalloon
@@ -1975,26 +1959,33 @@
Telecrystal: 20
categories:
- UplinkPointless
conditions:
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: UplinkScarfSyndieRed
name: uplink-scarf-syndie-red-name
description: uplink-scarf-syndie-red-desc
productEntity: ClothingNeckScarfStripedSyndieRed
cost:
Telecrystal: 1
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkScarfSyndieGreen
name: uplink-scarf-syndie-green-name
description: uplink-scarf-syndie-green-desc
productEntity: ClothingNeckScarfStripedSyndieGreen
cost:
Telecrystal: 1
categories:
- UplinkPointless
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: UplinkSyndicateBusinessCard
@@ -2037,44 +2028,6 @@
whitelist:
- Botanist
- type: listing
id: uplinkRiggedBoxingGlovesPassenger
name: uplink-rigged-boxing-gloves-name
description: uplink-rigged-boxing-gloves-desc
productEntity: ClothingHandsGlovesBoxingRigged
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 3
cost:
Telecrystal: 6
categories:
- UplinkJob
conditions:
- !type:BuyerJobCondition
whitelist:
- Passenger
- type: listing
id: uplinkNecronomicon
name: uplink-necronomicon-name
description: uplink-necronomicon-desc
productEntity: BibleNecronomicon
discountCategory: usualDiscounts
discountDownTo:
Telecrystal: 2
cost:
Telecrystal: 4
categories:
- UplinkJob
conditions:
- !type:BuyerJobCondition
whitelist:
- Chaplain
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: uplinkHolyHandGrenade
name: uplink-holy-hand-grenade-name
@@ -2099,9 +2052,9 @@
productEntity: RevolverCapGunFake
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 3
Telecrystal: 2
cost:
Telecrystal: 5
Telecrystal: 3
categories:
- UplinkJob
conditions:
@@ -2110,24 +2063,6 @@
- Mime
- Clown
- type: listing
id: uplinkBananaPeelExplosive
name: uplink-banana-peel-explosive-name
description: uplink-banana-peel-explosive-desc
icon: { sprite: Objects/Specific/Hydroponics/banana.rsi, state: peel }
productEntity: TrashBananaPeelExplosiveUnarmed
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 1
cost:
Telecrystal: 2
categories:
- UplinkJob
conditions:
- !type:BuyerJobCondition
whitelist:
- Clown
- type: listing
id: UplinkClusterBananaPeel
name: uplink-cluster-banana-peel-name
@@ -2137,7 +2072,7 @@
discountDownTo:
Telecrystal: 3
cost:
Telecrystal: 6
Telecrystal: 5
categories:
- UplinkJob
conditions:
@@ -2228,10 +2163,10 @@
icon: { sprite: Objects/Misc/monkeycube.rsi, state: box}
discountCategory: rareDiscounts
discountDownTo:
Telecrystal: 4
Telecrystal: 2
productEntity: SyndicateSpongeBox
cost:
Telecrystal: 7
Telecrystal: 4
categories:
- UplinkJob
conditions:
@@ -2259,10 +2194,6 @@
- !type:BuyerJobCondition
whitelist:
- Librarian
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
- type: listing
id: UplinkCombatBakery
@@ -2349,7 +2280,7 @@
discountDownTo:
Telecrystal: 10
cost:
Telecrystal: 15
Telecrystal: 14
categories:
- UplinkJob
conditions:
@@ -1,13 +1,8 @@
- type: entity
abstract: true
parent: ClothingHandsBase
id: ClothingHandsGlovesBoxingRed
name: red boxing gloves
description: Red gloves for competitive boxing.
id: ClothingHandsGlovesBoxingBase
components:
- type: Sprite
sprite: Clothing/Hands/Gloves/Boxing/boxingred.rsi
- type: Clothing
sprite: Clothing/Hands/Gloves/Boxing/boxingred.rsi
- type: StaminaDamageOnHit
damage: 8 #Stam damage values seem a bit higher than regular damage because of the decay, etc
# This needs to be moved to boxinggloves
@@ -21,17 +16,28 @@
collection: BoxingHit
animation: WeaponArcFist
mustBeEquippedToUse: true
- type: Fiber
fiberMaterial: fibers-leather
fiberColor: fibers-red
- type: FingerprintMask
- type: Tag
tags:
- Kangaroo
- WhitelistChameleon
- type: entity
parent: ClothingHandsGlovesBoxingRed
parent: ClothingHandsGlovesBoxingBase
id: ClothingHandsGlovesBoxingRed
name: red boxing gloves
description: Red gloves for competitive boxing.
components:
- type: Sprite
sprite: Clothing/Hands/Gloves/Boxing/boxingred.rsi
- type: Clothing
sprite: Clothing/Hands/Gloves/Boxing/boxingred.rsi
- type: Fiber
fiberMaterial: fibers-leather
fiberColor: fibers-red
- type: FingerprintMask
- type: entity
parent: ClothingHandsGlovesBoxingBase
id: ClothingHandsGlovesBoxingBlue
name: blue boxing gloves
description: Blue gloves for competitive boxing.
@@ -47,7 +53,7 @@
- type: FingerprintMask
- type: entity
parent: ClothingHandsGlovesBoxingRed
parent: ClothingHandsGlovesBoxingBase
id: ClothingHandsGlovesBoxingGreen
name: green boxing gloves
description: Green gloves for competitive boxing.
@@ -63,7 +69,7 @@
- type: FingerprintMask
- type: entity
parent: ClothingHandsGlovesBoxingRed
parent: ClothingHandsGlovesBoxingBase
id: ClothingHandsGlovesBoxingYellow
name: yellow boxing gloves
description: Yellow gloves for competitive boxing.
@@ -79,19 +85,53 @@
- type: FingerprintMask
- type: entity
parent: ClothingHandsGlovesBoxingBlue
id: ClothingHandsGlovesBoxingRigged
abstract: true
parent: ClothingHandsGlovesBoxingBase
id: ClothingHandsGlovesBoxingRiggedBase
suffix: Rigged
components:
- type: StaminaDamageOnHit
damage: 25
- type: MeleeWeapon
attackRate: 1.4
damage:
types:
Blunt: 8
bluntStaminaDamageFactor: 0.0 # so blunt doesn't deal stamina damage at all
mustBeEquippedToUse: true
bluntStaminaDamageFactor: 2
- type: entity
parent: [ ClothingHandsGlovesBoxingRiggedBase, ClothingHandsGlovesBoxingRed ]
id: ClothingHandsGlovesBoxingRiggedRed
name: red boxing gloves
description: Red gloves for competitive boxing.
- type: entity
parent: [ ClothingHandsGlovesBoxingRiggedBase, ClothingHandsGlovesBoxingBlue ]
id: ClothingHandsGlovesBoxingRiggedBlue
name: blue boxing gloves
description: Blue gloves for competitive boxing.
- type: entity
parent: [ ClothingHandsGlovesBoxingRiggedBase, ClothingHandsGlovesBoxingGreen ]
id: ClothingHandsGlovesBoxingRiggedGreen
name: green boxing gloves
description: Green gloves for competitive boxing.
- type: entity
parent: [ ClothingHandsGlovesBoxingRiggedBase, ClothingHandsGlovesBoxingYellow ]
id: ClothingHandsGlovesBoxingRiggedYellow
name: yellow boxing gloves
description: Yellow gloves for competitive boxing.
- type: entity
id: GlovesBoxingRiggedRandomSpawner
categories: [ HideSpawnMenu ]
name: random rigged boxing glove spawner
components:
- type: EntityTableSpawner
table: !type:GroupSelector
children:
- id: ClothingHandsGlovesBoxingRiggedRed
- id: ClothingHandsGlovesBoxingRiggedBlue
- id: ClothingHandsGlovesBoxingRiggedGreen
- id: ClothingHandsGlovesBoxingRiggedYellow
- type: entity
parent: [ClothingHandsBase, BaseCommandContraband]
@@ -111,6 +111,8 @@
- type: Tag
tags:
- CannotSuicide
- StunImmune
- SlowImmune
# From the uplink injector
- type: entity
@@ -234,15 +236,6 @@
Hand:
location: Left
- type: ComplexInteraction
- type: Clumsy
gunShootFailDamage:
types:
Blunt: 5
Piercing: 4
Heat: 3
catchingFailDamage:
types:
Blunt: 1
- type: MeleeWeapon
angle: 30
animation: WeaponArcFist
@@ -257,9 +250,6 @@
- type: RandomMetadata
nameSegments:
- NamesClown
- type: NpcFactionMember
factions:
- Syndicate
- type: HTN
rootTask:
task: SimpleHumanoidHostileCompound
@@ -811,10 +811,6 @@
Quantity: 2
- ReagentId: Vitamin
Quantity: 1
- type: DamageOtherOnHit
damage:
types:
Blunt: 0 # so the damage stats icon doesn't immediately give away the syndie ones
- type: entity
parent: FoodBakedCroissant
@@ -583,7 +583,7 @@
children:
- id: FoodSaladValid
weight: 0.05
amount: 4
amount: 2
- id: FoodSnackSyndi
amount: 4
@@ -699,13 +699,17 @@
- type: entityTable # size: 1x2
id: HappyHonkToyUnsafeEntityTable
table: !type:GroupSelector
children:
children: # Total Weight 6
- id: ClothingHeadHatCatEars
weight: 0.25
- id: C4
weight: 0.02
weight: 0.05
- id: ToyMarauder
- id: ToyMauler
- id: ToyNuke
- id: ToySword
- id: WeaponRevolverPythonAP
weight: 0.4
- id: BalloonSyn
weight: 0.6
weight: 0.3
- id: PlushieNuke
@@ -124,6 +124,18 @@
- type: StaticPrice
price: 10000
- type: entity
id: SyndimovCircuitBoard
parent: [BaseSiliconLawboard, BaseSyndicateContraband]
name: law board (Syndimov)
description: An electronics board containing the Syndimov lawset.
components:
- type: SiliconLawProvider
laws: SyndicateStatic
lawUploadSound: /Audio/Ambience/Antag/emagged_borg.ogg # This should probably have its own sound but it's fine for now.
- type: StaticPrice
price: 5000
- type: entity
id: NutimovCircuitBoard
parent: BaseSiliconLawboard
@@ -266,3 +266,17 @@
guides:
- Botany
- Chemicals
- type: entity
parent: [ BaseFlatpack, BaseSyndicateContraband ]
id: SyndicateMicrowaveFlatpack
name: donk co. microwave flatpack
description: A flatpack used for constructing a microwave too hot for Nanotrasen to handle.
components:
- type: Item
size: Normal
- type: Flatpack
entity: SyndicateMicrowave
- type: GuideHelp
guides:
- FoodRecipes
@@ -259,7 +259,7 @@
- type: entity
id: VoiceMaskImplanter
name: voice mask implanter
name: identity mask implanter
parent: BaseImplantOnlyImplanterSyndi
components:
- type: Implanter
@@ -242,8 +242,8 @@
- type: entity
parent: BaseSubdermalImplant
id: VoiceMaskImplant
name: voice mask implant
description: This implant allows you to change your voice at will.
name: identity mask implant
description: This implant allows you to change your identity at will.
categories: [ HideSpawnMenu ]
components:
- type: SubdermalImplant
@@ -50,7 +50,7 @@
- type: ExaminableBattery
- type: PowerConsumer
voltage: High
drawRate: 1000000
drawRate: 10000000
- type: Sprite
sprite: Objects/Power/powersink.rsi
state: powersink

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