Merge remote-tracking branch 'space-wizards/master'
# Conflicts: # Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs # Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs # Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml # Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml
This commit is contained in:
commit
f71f04558f
47 changed files with 622 additions and 375 deletions
|
|
@ -15,7 +15,6 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||
<TargetFramework>net4.7.2</TargetFramework>
|
||||
<RestorePackages>false</RestorePackages>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from typing import List
|
|||
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||
# If this doesn't match the saved version we overwrite them all.
|
||||
CURRENT_HOOKS_VERSION = "2"
|
||||
CURRENT_HOOKS_VERSION = "3"
|
||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,4 @@ gitroot=`git rev-parse --show-toplevel`
|
|||
|
||||
cd "$gitroot/BuildChecker"
|
||||
|
||||
if [[ `uname` == MINGW* || `uname` == CYGWIN* ]]; then
|
||||
# Windows
|
||||
py -3 git_helper.py --quiet
|
||||
else
|
||||
# Not Windows, so probably some other Unix thing.
|
||||
python3 git_helper.py --quiet
|
||||
fi
|
||||
python3 git_helper.py --quiet
|
||||
|
|
|
|||
|
|
@ -75,10 +75,9 @@ public sealed partial class CargoSystem
|
|||
switch (state)
|
||||
{
|
||||
case CargoTelepadState.Teleporting:
|
||||
if (_player.HasRunningAnimation(uid, TelepadBeamKey))
|
||||
return;
|
||||
_player.Stop(uid, player, TelepadIdleKey);
|
||||
_player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey);
|
||||
_player.Stop((uid, player), TelepadIdleKey);
|
||||
if (!_player.HasRunningAnimation(uid, TelepadBeamKey))
|
||||
_player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey);
|
||||
break;
|
||||
case CargoTelepadState.Unpowered:
|
||||
sprite.LayerSetVisible(CargoTelepadLayers.Beam, false);
|
||||
|
|
|
|||
|
|
@ -213,52 +213,14 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
|
|||
return;
|
||||
}
|
||||
|
||||
var entries = listing.ToList();
|
||||
entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal));
|
||||
// `entries` now contains the definitive list of items which should be in
|
||||
// our list of records and is in the order we want to present those items.
|
||||
|
||||
// Walk through the existing items in RecordListing and in the updated listing
|
||||
// in parallel to synchronize the items in RecordListing with `entries`.
|
||||
int i = RecordListing.Count - 1;
|
||||
int j = entries.Count - 1;
|
||||
while (i >= 0 && j >= 0)
|
||||
{
|
||||
var strcmp = string.Compare(RecordListing[i].Text, entries[j].Value, StringComparison.Ordinal);
|
||||
if (strcmp == 0)
|
||||
{
|
||||
// This item exists in both RecordListing and `entries`. Nothing to do.
|
||||
i--;
|
||||
j--;
|
||||
}
|
||||
else if (strcmp > 0)
|
||||
{
|
||||
// Item exists in RecordListing, but not in `entries`. Remove it.
|
||||
RecordListing.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
else if (strcmp < 0)
|
||||
{
|
||||
// A new entry which doesn't exist in RecordListing. Create it.
|
||||
RecordListing.Insert(i + 1, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key});
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
// Any remaining items in RecordListing don't exist in `entries`, so remove them
|
||||
while (i >= 0)
|
||||
{
|
||||
RecordListing.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
|
||||
// And finally, any remaining items in `entries`, don't exist in RecordListing. Create them.
|
||||
while (j >= 0)
|
||||
{
|
||||
RecordListing.Insert(0, new ItemList.Item(RecordListing){ Text = entries[j].Value, Metadata = entries[j].Key });
|
||||
j--;
|
||||
}
|
||||
var entries = listing.Select(i => new ItemList.Item(RecordListing) {
|
||||
Text = i.Value,
|
||||
Metadata = i.Key
|
||||
}).ToList();
|
||||
entries.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
|
||||
RecordListing.SetItems(entries, (a,b) => string.Compare(a.Text, b.Text));
|
||||
}
|
||||
|
||||
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
|
||||
{
|
||||
var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Misc/job_icons.rsi"), "Unknown");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Materials;
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Materials.UI;
|
||||
|
|
@ -17,7 +18,7 @@ public sealed partial class MaterialStorageControl : ScrollContainer
|
|||
|
||||
private EntityUid? _owner;
|
||||
|
||||
private Dictionary<string, int> _currentMaterials = new();
|
||||
private Dictionary<ProtoId<MaterialPrototype>, int> _currentMaterials = new();
|
||||
|
||||
public MaterialStorageControl()
|
||||
{
|
||||
|
|
@ -44,7 +45,7 @@ public sealed partial class MaterialStorageControl : ScrollContainer
|
|||
}
|
||||
|
||||
var canEject = materialStorage.CanEjectStoredMaterials;
|
||||
var mats = materialStorage.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary();
|
||||
var mats = materialStorage.Storage;
|
||||
if (_currentMaterials.Equals(mats))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -632,8 +632,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
if (args.Function != EngineKeyFunctions.UIClick && args.Function != EngineKeyFunctions.Use)
|
||||
return;
|
||||
|
||||
_menuDragHelper.MouseDown(action);
|
||||
args.Handle();
|
||||
HandleActionPressed(args, action);
|
||||
}
|
||||
|
||||
private void OnWindowActionUnPressed(GUIBoundKeyEventArgs args, ActionButton dragged)
|
||||
|
|
@ -641,8 +640,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
if (args.Function != EngineKeyFunctions.UIClick && args.Function != EngineKeyFunctions.Use)
|
||||
return;
|
||||
|
||||
DragAction();
|
||||
args.Handle();
|
||||
HandleActionUnpressed(args, dragged);
|
||||
}
|
||||
|
||||
private void OnWindowActionFocusExisted(ActionButton button)
|
||||
|
|
@ -662,6 +660,11 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
HandleActionPressed(args, button);
|
||||
}
|
||||
|
||||
private void HandleActionPressed(GUIBoundKeyEventArgs args, ActionButton button)
|
||||
{
|
||||
args.Handle();
|
||||
if (button.ActionId != null)
|
||||
{
|
||||
|
|
@ -677,7 +680,15 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
|
||||
private void OnActionUnpressed(GUIBoundKeyEventArgs args, ActionButton button)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick || _actionsSystem == null)
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
HandleActionUnpressed(args, button);
|
||||
}
|
||||
|
||||
private void HandleActionUnpressed(GUIBoundKeyEventArgs args, ActionButton button)
|
||||
{
|
||||
if (_actionsSystem == null)
|
||||
return;
|
||||
|
||||
args.Handle();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Configuration;
|
||||
|
|
@ -9,6 +10,7 @@ using Robust.Shared.Log;
|
|||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
|
|
@ -274,70 +276,108 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
// We consider only non-audio entities, as some entities will just play sounds when they spawn.
|
||||
int Count(IEntityManager ent) => ent.EntityCount - ent.Count<AudioComponent>();
|
||||
IEnumerable<EntityUid> Entities(IEntityManager entMan) => entMan.GetEntities().Where(entMan.HasComponent<AudioComponent>);
|
||||
|
||||
foreach (var protoId in protoIds)
|
||||
await Assert.MultipleAsync(async () =>
|
||||
{
|
||||
// TODO fix ninja
|
||||
// Currently ninja fails to equip their own loadout.
|
||||
if (protoId == "MobHumanSpaceNinja")
|
||||
continue;
|
||||
|
||||
var count = Count(server.EntMan);
|
||||
var clientCount = Count(client.EntMan);
|
||||
EntityUid uid = default;
|
||||
await server.WaitPost(() => uid = server.EntMan.SpawnEntity(protoId, coords));
|
||||
await pair.RunTicksSync(3);
|
||||
|
||||
// If the entity deleted itself, check that it didn't spawn other entities
|
||||
if (!server.EntMan.EntityExists(uid))
|
||||
foreach (var protoId in protoIds)
|
||||
{
|
||||
if (Count(server.EntMan) != count)
|
||||
// TODO fix ninja
|
||||
// Currently ninja fails to equip their own loadout.
|
||||
if (protoId == "MobHumanSpaceNinja")
|
||||
continue;
|
||||
|
||||
var count = Count(server.EntMan);
|
||||
var clientCount = Count(client.EntMan);
|
||||
var serverEntities = new HashSet<EntityUid>(Entities(server.EntMan));
|
||||
var clientEntities = new HashSet<EntityUid>(Entities(client.EntMan));
|
||||
EntityUid uid = default;
|
||||
await server.WaitPost(() => uid = server.EntMan.SpawnEntity(protoId, coords));
|
||||
await pair.RunTicksSync(3);
|
||||
|
||||
// If the entity deleted itself, check that it didn't spawn other entities
|
||||
if (!server.EntMan.EntityExists(uid))
|
||||
{
|
||||
Assert.Fail($"Server prototype {protoId} failed on deleting itself");
|
||||
Assert.That(Count(server.EntMan), Is.EqualTo(count), $"Server prototype {protoId} failed on deleting itself\n" +
|
||||
BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan));
|
||||
Assert.That(Count(client.EntMan), Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deleting itself\n" +
|
||||
$"Expected {clientCount} and found {client.EntMan.EntityCount}.\n" +
|
||||
$"Server count was {count}.\n" +
|
||||
BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Count(client.EntMan) != clientCount)
|
||||
{
|
||||
Assert.Fail($"Client prototype {protoId} failed on deleting itself\n" +
|
||||
$"Expected {clientCount} and found {Count(client.EntMan)}.\n" +
|
||||
$"Server was {count}.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Check that the number of entities has increased.
|
||||
Assert.That(Count(server.EntMan), Is.GreaterThan(count), $"Server prototype {protoId} failed on spawning as entity count didn't increase\n" +
|
||||
BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan));
|
||||
Assert.That(Count(client.EntMan), Is.GreaterThan(clientCount), $"Client prototype {protoId} failed on spawning as entity count didn't increase\n" +
|
||||
$"Expected at least {clientCount} and found {client.EntMan.EntityCount}. " +
|
||||
$"Server count was {count}.\n" +
|
||||
BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan));
|
||||
|
||||
// Check that the number of entities has increased.
|
||||
if (Count(server.EntMan) <= count)
|
||||
{
|
||||
Assert.Fail($"Server prototype {protoId} failed on spawning as entity count didn't increase");
|
||||
}
|
||||
await server.WaitPost(() => server.EntMan.DeleteEntity(uid));
|
||||
await pair.RunTicksSync(3);
|
||||
|
||||
if (Count(client.EntMan) <= clientCount)
|
||||
{
|
||||
Assert.Fail($"Client prototype {protoId} failed on spawning as entity count didn't increase" +
|
||||
$"Expected at least {clientCount} and found {Count(client.EntMan)}. " +
|
||||
$"Server was {count}");
|
||||
// Check that the number of entities has gone back to the original value.
|
||||
Assert.That(Count(server.EntMan), Is.EqualTo(count), $"Server prototype {protoId} failed on deletion: count didn't reset properly\n" +
|
||||
BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan));
|
||||
Assert.That(client.EntMan.EntityCount, Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deletion: count didn't reset properly:\n" +
|
||||
$"Expected {clientCount} and found {client.EntMan.EntityCount}.\n" +
|
||||
$"Server count was {count}.\n" +
|
||||
BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan));
|
||||
}
|
||||
|
||||
await server.WaitPost(() => server.EntMan.DeleteEntity(uid));
|
||||
await pair.RunTicksSync(3);
|
||||
|
||||
// Check that the number of entities has gone back to the original value.
|
||||
if (Count(server.EntMan) != count)
|
||||
{
|
||||
Assert.Fail($"Server prototype {protoId} failed on deletion count didn't reset properly");
|
||||
}
|
||||
|
||||
if (Count(client.EntMan) != clientCount)
|
||||
{
|
||||
Assert.Fail($"Client prototype {protoId} failed on deletion count didn't reset properly:\n" +
|
||||
$"Expected {clientCount} and found {Count(client.EntMan)}.\n" +
|
||||
$"Server was {count}.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
private static string BuildDiffString(IEnumerable<EntityUid> oldEnts, IEnumerable<EntityUid> newEnts, IEntityManager entMan)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var addedEnts = newEnts.Except(oldEnts);
|
||||
var removedEnts = oldEnts.Except(newEnts);
|
||||
if (addedEnts.Any())
|
||||
sb.AppendLine("Listing new entities:");
|
||||
foreach (var addedEnt in addedEnts)
|
||||
{
|
||||
sb.AppendLine(entMan.ToPrettyString(addedEnt));
|
||||
}
|
||||
if (removedEnts.Any())
|
||||
sb.AppendLine("Listing removed entities:");
|
||||
foreach (var removedEnt in removedEnts)
|
||||
{
|
||||
sb.AppendLine("\t" + entMan.ToPrettyString(removedEnt));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static bool HasRequiredDataField(Component component)
|
||||
{
|
||||
foreach (var field in component.GetType().GetFields())
|
||||
{
|
||||
foreach (var attribute in field.GetCustomAttributes(true))
|
||||
{
|
||||
if (attribute is not DataFieldAttribute dataField)
|
||||
continue;
|
||||
|
||||
if (dataField.Required)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach (var property in component.GetType().GetProperties())
|
||||
{
|
||||
foreach (var attribute in property.GetCustomAttributes(true))
|
||||
{
|
||||
if (attribute is not DataFieldAttribute dataField)
|
||||
continue;
|
||||
|
||||
if (dataField.Required)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AllComponentsOneToOneDeleteTest()
|
||||
{
|
||||
|
|
@ -362,9 +402,6 @@ namespace Content.IntegrationTests.Tests
|
|||
"ActivatableUI", // Requires enum key
|
||||
};
|
||||
|
||||
// TODO TESTS
|
||||
// auto ignore any components that have a "required" data field.
|
||||
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
|
|
@ -382,9 +419,12 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
foreach (var type in componentFactory.AllRegisteredTypes)
|
||||
{
|
||||
var component = (Component) componentFactory.GetComponent(type);
|
||||
var component = (Component)componentFactory.GetComponent(type);
|
||||
var name = componentFactory.GetComponentName(type);
|
||||
|
||||
if (HasRequiredDataField(component))
|
||||
continue;
|
||||
|
||||
// If this component is ignored
|
||||
if (skipComponents.Contains(name))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Construction.Completions;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Destructible;
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Server.Lathe;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Construction.Steps;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
|
@ -14,10 +15,9 @@ using Content.Shared.Lathe;
|
|||
using Content.Shared.Materials;
|
||||
using Content.Shared.Research.Prototypes;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests;
|
||||
|
||||
|
|
@ -28,6 +28,21 @@ namespace Content.IntegrationTests.Tests;
|
|||
[TestFixture]
|
||||
public sealed class MaterialArbitrageTest
|
||||
{
|
||||
// These recipes are currently broken and need fixing. You should not be adding to these sets.
|
||||
private readonly HashSet<string> _destructionArbitrageIgnore =
|
||||
[
|
||||
"BaseChemistryEmptyVial", "DrinkShotGlass", "Beaker", "SodiumLightTube", "DrinkGlassCoupeShaped",
|
||||
"LedLightBulb", "ExteriorLightTube", "LightTube", "DrinkGlass", "DimLightBulb", "LightBulb", "LedLightTube",
|
||||
"SheetRGlass1", "ChemistryEmptyBottle01", "WarmLightBulb",
|
||||
];
|
||||
|
||||
private readonly HashSet<string> _compositionArbitrageIgnore =
|
||||
[
|
||||
"FoodPlateSmall", "AirTank", "FoodPlateTin", "FoodPlateMuffinTin", "WeaponCapacitorRechargerCircuitboard",
|
||||
"WeaponCapacitorRechargerCircuitboard", "BorgChargerCircuitboard", "BorgChargerCircuitboard", "FoodPlate",
|
||||
"CellRechargerCircuitboard", "CellRechargerCircuitboard",
|
||||
];
|
||||
|
||||
[Test]
|
||||
public async Task NoMaterialArbitrage()
|
||||
{
|
||||
|
|
@ -38,13 +53,12 @@ public sealed class MaterialArbitrageTest
|
|||
await server.WaitIdleAsync();
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
var pricing = entManager.System<PricingSystem>();
|
||||
var stackSys = entManager.System<StackSystem>();
|
||||
var mapSystem = server.System<SharedMapSystem>();
|
||||
var latheSys = server.System<SharedLatheSystem>();
|
||||
var latheSys = server.System<LatheSystem>();
|
||||
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||
|
||||
Assert.That(mapSystem.IsInitialized(testMap.MapId));
|
||||
|
|
@ -53,13 +67,24 @@ public sealed class MaterialArbitrageTest
|
|||
var compositionName = compFact.GetComponentName(typeof(PhysicalCompositionComponent));
|
||||
var materialName = compFact.GetComponentName(typeof(MaterialComponent));
|
||||
var destructibleName = compFact.GetComponentName(typeof(DestructibleComponent));
|
||||
var refinableName = compFact.GetComponentName(typeof(ToolRefinableComponent));
|
||||
|
||||
// get the inverted lathe recipe dictionary
|
||||
var latheRecipes = latheSys.InverseRecipes;
|
||||
|
||||
// Lets assume the possible lathe for resource multipliers:
|
||||
// TODO: each recipe can technically have its own cost multiplier associated with it, so this test needs redone to factor that in.
|
||||
var multiplier = MathF.Pow(0.85f, 3);
|
||||
// Find the lowest multiplier / optimal lathe that can be used to construct a recipie.
|
||||
var minMultiplier = new Dictionary<ProtoId<LatheRecipePrototype>, float>();
|
||||
|
||||
foreach (var (_, lathe) in pair.GetPrototypesWithComponent<LatheComponent>())
|
||||
{
|
||||
foreach (var recipe in latheSys.GetAllPossibleRecipes(lathe))
|
||||
{
|
||||
if (!minMultiplier.TryGetValue(recipe, out var min))
|
||||
min = 1;
|
||||
|
||||
minMultiplier[recipe] = Math.Min(min, lathe.MaterialUseMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
// create construction dictionary
|
||||
Dictionary<string, ConstructionComponent> constructionRecipes = new();
|
||||
|
|
@ -122,6 +147,65 @@ public sealed class MaterialArbitrageTest
|
|||
|
||||
Dictionary<string, (Dictionary<string, int> Ents, Dictionary<string, int> Mats)> spawnedOnDestroy = new();
|
||||
|
||||
// cache the compositions of entities
|
||||
// If the entity is refineable (i.e. glass shared can be turned into glass, we take the greater of the two compositions.
|
||||
Dictionary<EntProtoId, Dictionary<string, int>> compositions = new();
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
Dictionary<string, int>? baseComposition = null;
|
||||
|
||||
if (proto.Components.ContainsKey(materialName)
|
||||
&& proto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
{
|
||||
var compositionComp = (PhysicalCompositionComponent)compositionReg.Component;
|
||||
baseComposition = compositionComp.MaterialComposition;
|
||||
|
||||
}
|
||||
|
||||
if (!proto.Components.TryGetValue(refinableName, out var refinableReg))
|
||||
{
|
||||
if (baseComposition != null)
|
||||
compositions[proto.ID] = new(baseComposition);
|
||||
continue;
|
||||
}
|
||||
|
||||
var composition = new Dictionary<string, int>();
|
||||
compositions.Add(proto.ID, composition);
|
||||
|
||||
var refinable = (ToolRefinableComponent)refinableReg.Component;
|
||||
foreach (var refineResult in refinable.RefineResult)
|
||||
{
|
||||
if (refineResult.PrototypeId == null)
|
||||
continue;
|
||||
|
||||
var refineProto = protoManager.Index(refineResult.PrototypeId.Value);
|
||||
if (!refineProto.Components.ContainsKey(materialName))
|
||||
continue;
|
||||
|
||||
if (!refineProto.Components.TryGetValue(compositionName, out var refinedCompositionReg))
|
||||
continue;
|
||||
|
||||
var refinedCompositionComp = (PhysicalCompositionComponent)refinedCompositionReg.Component;
|
||||
|
||||
// This assumes refine results do not have complex spawn behaviours like exclusive groups.
|
||||
var quantity = refineResult.MaxAmount;
|
||||
|
||||
foreach (var (matId, amount) in refinedCompositionComp.MaterialComposition)
|
||||
{
|
||||
composition[matId] = quantity * amount + composition.GetValueOrDefault(matId);
|
||||
}
|
||||
}
|
||||
|
||||
if (baseComposition == null)
|
||||
continue;
|
||||
|
||||
// If the un-refined material quantity is greater than the refined quantity, we use that instead.
|
||||
foreach (var (matId, amount) in baseComposition)
|
||||
{
|
||||
composition[matId] = Math.Max(amount, composition.GetValueOrDefault(matId));
|
||||
}
|
||||
}
|
||||
|
||||
// Here we get the set of entities/materials spawned when destroying an entity.
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
|
|
@ -151,16 +235,10 @@ public sealed class MaterialArbitrageTest
|
|||
{
|
||||
spawnedEnts[key] = spawnedEnts.GetValueOrDefault(key) + value.Max;
|
||||
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(key);
|
||||
|
||||
// get the amount of each material included in the entity
|
||||
|
||||
if (!spawnProto.Components.ContainsKey(materialName) ||
|
||||
!spawnProto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
if (!compositions.TryGetValue(key, out var composition))
|
||||
continue;
|
||||
|
||||
var mat = (PhysicalCompositionComponent) compositionReg.Component;
|
||||
foreach (var (matId, amount) in mat.MaterialComposition)
|
||||
foreach (var (matId, amount) in composition)
|
||||
{
|
||||
spawnedMats[matId] = value.Max * amount + spawnedMats.GetValueOrDefault(matId);
|
||||
}
|
||||
|
|
@ -173,10 +251,13 @@ public sealed class MaterialArbitrageTest
|
|||
}
|
||||
|
||||
// This is the main loop where we actually check for destruction arbitrage
|
||||
Assert.Multiple(async () =>
|
||||
await Assert.MultipleAsync(async () =>
|
||||
{
|
||||
foreach (var (id, (spawnedEnts, spawnedMats)) in spawnedOnDestroy)
|
||||
{
|
||||
if (_destructionArbitrageIgnore.Contains(id))
|
||||
continue;
|
||||
|
||||
// Check cargo sell price
|
||||
// several constructible entities have no sell price
|
||||
// also this test only really matters if the entity is also purchaseable.... eh..
|
||||
|
|
@ -190,6 +271,11 @@ public sealed class MaterialArbitrageTest
|
|||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
if (!minMultiplier.TryGetValue(recipe, out var multiplier))
|
||||
{
|
||||
server.Log.Info($"Unused lathe recipe? {recipe.ID}?");
|
||||
continue;
|
||||
}
|
||||
foreach (var (matId, amount) in recipe.Materials)
|
||||
{
|
||||
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
|
||||
|
|
@ -231,6 +317,9 @@ public sealed class MaterialArbitrageTest
|
|||
var edge = cur.GetEdge(node.Name);
|
||||
cur = node;
|
||||
|
||||
if (edge == null)
|
||||
continue;
|
||||
|
||||
foreach (var completion in edge.Completed)
|
||||
{
|
||||
if (completion is not SpawnPrototype spawnCompletion)
|
||||
|
|
@ -253,9 +342,9 @@ public sealed class MaterialArbitrageTest
|
|||
}
|
||||
|
||||
// This is functionally the same loop as before, but now testing deconstruction rather than destruction.
|
||||
// This is pretty braindead. In principle construction graphs can have loops and whatnot.
|
||||
// This is pretty brain-dead. In principle construction graphs can have loops and whatnot.
|
||||
|
||||
Assert.Multiple(async () =>
|
||||
await Assert.MultipleAsync(async () =>
|
||||
{
|
||||
foreach (var (id, deconstructedMats) in deconstructionMaterials)
|
||||
{
|
||||
|
|
@ -270,6 +359,11 @@ public sealed class MaterialArbitrageTest
|
|||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
if (!minMultiplier.TryGetValue(recipe, out var multiplier))
|
||||
{
|
||||
server.Log.Info($"Unused lathe recipe? {recipe.ID}?");
|
||||
continue;
|
||||
}
|
||||
foreach (var (matId, amount) in recipe.Materials)
|
||||
{
|
||||
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
|
||||
|
|
@ -291,7 +385,7 @@ public sealed class MaterialArbitrageTest
|
|||
}
|
||||
});
|
||||
|
||||
// create phyiscal composition dictionary
|
||||
// create physical composition dictionary
|
||||
// this doesn't account for the chemicals in the composition
|
||||
Dictionary<string, PhysicalCompositionComponent> physicalCompositions = new();
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
|
|
@ -308,10 +402,13 @@ public sealed class MaterialArbitrageTest
|
|||
|
||||
// This is functionally the same loop as before, but now testing composition rather than destruction or deconstruction.
|
||||
// This doesn't take into account chemicals generated when deconstructing. Maybe it should.
|
||||
Assert.Multiple(async () =>
|
||||
await Assert.MultipleAsync(async () =>
|
||||
{
|
||||
foreach (var (id, compositionComponent) in physicalCompositions)
|
||||
{
|
||||
if (_compositionArbitrageIgnore.Contains(id))
|
||||
continue;
|
||||
|
||||
// Check cargo sell price
|
||||
var materialPrice = await GetDeconstructedPrice(compositionComponent.MaterialComposition);
|
||||
var chemicalPrice = await GetChemicalCompositionPrice(compositionComponent.ChemicalComposition);
|
||||
|
|
@ -325,6 +422,11 @@ public sealed class MaterialArbitrageTest
|
|||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
if (!minMultiplier.TryGetValue(recipe, out var multiplier))
|
||||
{
|
||||
server.Log.Info($"Unused lathe recipe? {recipe.ID}?");
|
||||
continue;
|
||||
}
|
||||
foreach (var (matId, amount) in recipe.Materials)
|
||||
{
|
||||
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ namespace Content.MapRenderer
|
|||
var mapIds = pair.Server
|
||||
.ResolveDependency<IPrototypeManager>()
|
||||
.EnumeratePrototypes<GameMapPrototype>()
|
||||
.Where(map => !pair.IsTestPrototype(map))
|
||||
.Select(map => map.ID)
|
||||
.ToArray();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Server.AlertLevel.Systems;
|
||||
|
||||
namespace Content.Server.AlertLevel;
|
||||
/// <summary>
|
||||
/// This component is for changing the alert level of the station when triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AlertLevelChangeOnTriggerSystem))]
|
||||
public sealed partial class AlertLevelChangeOnTriggerComponent : Component
|
||||
{
|
||||
///<summary>
|
||||
///The alert level to change to when triggered.
|
||||
///</summary>
|
||||
[DataField]
|
||||
public string Level = "blue";
|
||||
|
||||
/// <summary>
|
||||
///Whether to play the sound when the alert level changes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PlaySound = true;
|
||||
|
||||
/// <summary>
|
||||
///Whether to say the announcement when the alert level changes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Announce = true;
|
||||
|
||||
/// <summary>
|
||||
///Force the alert change. This applies if the alert level is not selectable or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Force = false;
|
||||
}
|
||||
|
|
@ -118,6 +118,20 @@ public sealed class AlertLevelSystem : EntitySystem
|
|||
return alert.CurrentDelay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default alert level for a station entity.
|
||||
/// Returns an empty string if the station has no alert levels defined.
|
||||
/// </summary>
|
||||
/// <param name="station">The station entity.</param>
|
||||
public string GetDefaultLevel(Entity<AlertLevelComponent?> station)
|
||||
{
|
||||
if (!Resolve(station.Owner, ref station.Comp) || station.Comp.AlertLevels == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return station.Comp.AlertLevels.DefaultLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the alert level based on the station's entity ID.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Station.Systems;
|
||||
|
||||
namespace Content.Server.AlertLevel.Systems;
|
||||
|
||||
public sealed class AlertLevelChangeOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AlertLevelChangeOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<AlertLevelChangeOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
var stationUid = _station.GetOwningStation(ent.Owner);
|
||||
if (!stationUid.HasValue)
|
||||
return;
|
||||
|
||||
_alertLevelSystem.SetLevel(stationUid.Value, ent.Comp.Level, ent.Comp.PlaySound, ent.Comp.Announce, ent.Comp.Force);
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,6 @@ namespace Content.Server.Lathe
|
|||
/// Per-tick cache
|
||||
/// </summary>
|
||||
private readonly List<GasMixture> _environments = new();
|
||||
private readonly HashSet<ProtoId<LatheRecipePrototype>> _availableRecipes = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -163,12 +162,8 @@ namespace Content.Server.Lathe
|
|||
|
||||
public List<ProtoId<LatheRecipePrototype>> GetAvailableRecipes(EntityUid uid, LatheComponent component, bool getUnavailable = false)
|
||||
{
|
||||
_availableRecipes.Clear();
|
||||
AddRecipesFromPacks(_availableRecipes, component.StaticPacks);
|
||||
var ev = new LatheGetRecipesEvent(uid, getUnavailable)
|
||||
{
|
||||
Recipes = _availableRecipes
|
||||
};
|
||||
var ev = new LatheGetRecipesEvent((uid, component), getUnavailable);
|
||||
AddRecipesFromPacks(ev.Recipes, component.StaticPacks);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
return ev.Recipes.ToList();
|
||||
}
|
||||
|
|
@ -292,7 +287,7 @@ namespace Content.Server.Lathe
|
|||
var pack = _proto.Index(id);
|
||||
foreach (var recipe in pack.Recipes)
|
||||
{
|
||||
if (args.getUnavailable || database.UnlockedRecipes.Contains(recipe))
|
||||
if (args.GetUnavailable || database.UnlockedRecipes.Contains(recipe))
|
||||
args.Recipes.Add(recipe);
|
||||
}
|
||||
}
|
||||
|
|
@ -300,10 +295,8 @@ namespace Content.Server.Lathe
|
|||
|
||||
private void OnGetRecipes(EntityUid uid, TechnologyDatabaseComponent component, LatheGetRecipesEvent args)
|
||||
{
|
||||
if (uid != args.Lathe || !TryComp<LatheComponent>(uid, out var latheComponent))
|
||||
return;
|
||||
|
||||
AddRecipesFromDynamicPacks(ref args, component, latheComponent.DynamicPacks);
|
||||
if (uid == args.Lathe)
|
||||
AddRecipesFromDynamicPacks(ref args, component, args.Comp.DynamicPacks);
|
||||
}
|
||||
|
||||
private void GetEmagLatheRecipes(EntityUid uid, EmagLatheRecipesComponent component, LatheGetRecipesEvent args)
|
||||
|
|
@ -311,7 +304,7 @@ namespace Content.Server.Lathe
|
|||
if (uid != args.Lathe)
|
||||
return;
|
||||
|
||||
if (!args.getUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction))
|
||||
if (!args.GetUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction))
|
||||
return;
|
||||
|
||||
AddRecipesFromPacks(args.Recipes, component.EmagStaticPacks);
|
||||
|
|
|
|||
|
|
@ -4,46 +4,39 @@ using Content.Server.Popups;
|
|||
using Content.Server.Roles;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Mindshield;
|
||||
|
||||
/// <summary>
|
||||
/// System used for checking if the implanted is a Rev or Head Rev.
|
||||
/// System used for adding or removing components with a mindshield implant
|
||||
/// as well as checking if the implanted is a Rev or Head Rev.
|
||||
/// </summary>
|
||||
public sealed class MindShieldSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private readonly RoleSystem _roleSystem = default!;
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
[ValidatePrototypeId<TagPrototype>]
|
||||
public const string MindShieldTag = "MindShield";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SubdermalImplantComponent, ImplantImplantedEvent>(ImplantCheck);
|
||||
|
||||
SubscribeLocalEvent<MindShieldImplantComponent, ImplantImplantedEvent>(OnImplantImplanted);
|
||||
SubscribeLocalEvent<MindShieldImplantComponent, EntGotRemovedFromContainerMessage>(OnImplantDraw);
|
||||
SubscribeLocalEvent<SubdermalImplantComponent, ImplantEjectEvent>(ImplantCheck);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the implant was a mindshield or not
|
||||
/// </summary>
|
||||
public void ImplantCheck(EntityUid uid, SubdermalImplantComponent comp, ref ImplantImplantedEvent ev)
|
||||
private void OnImplantImplanted(Entity<MindShieldImplantComponent> ent, ref ImplantImplantedEvent ev)
|
||||
{
|
||||
if (_tag.HasTag(ev.Implant, MindShieldTag) && ev.Implanted != null)
|
||||
{
|
||||
EnsureComp<MindShieldComponent>(ev.Implanted.Value);
|
||||
MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant);
|
||||
}
|
||||
if (ev.Implanted == null)
|
||||
return;
|
||||
|
||||
EnsureComp<MindShieldComponent>(ev.Implanted.Value);
|
||||
MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant);
|
||||
}
|
||||
|
||||
// Sunrise-Start
|
||||
|
|
@ -59,7 +52,7 @@ public sealed class MindShieldSystem : EntitySystem
|
|||
/// <summary>
|
||||
/// Checks if the implanted person was a Rev or Head Rev and remove role or destroy mindshield respectively.
|
||||
/// </summary>
|
||||
public void MindShieldRemovalCheck(EntityUid implanted, EntityUid implant)
|
||||
private void MindShieldRemovalCheck(EntityUid implanted, EntityUid implant)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(implanted))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,16 +56,16 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
// hence we'll store it here.
|
||||
var appliedStates = new List<Dictionary<string, object>?>();
|
||||
|
||||
var tasksToProcess = new Queue<HTNTask>();
|
||||
var tasksToProcess = new Stack<HTNTask>();
|
||||
var finalPlan = new List<HTNPrimitiveTask>();
|
||||
tasksToProcess.Enqueue(_rootTask);
|
||||
tasksToProcess.Push(_rootTask);
|
||||
|
||||
// How many primitive tasks we've added since last record.
|
||||
var primitiveCount = 0;
|
||||
|
||||
int tasksProcessed = 0;
|
||||
|
||||
while (tasksToProcess.TryDequeue(out var currentTask))
|
||||
while (tasksToProcess.TryPop(out var currentTask))
|
||||
{
|
||||
if (tasksProcessed++ > _rootTask.MaximumTasks)
|
||||
throw new Exception("HTN Planner exceeded maximum tasks");
|
||||
|
|
@ -161,7 +161,7 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
/// <summary>
|
||||
/// Goes through each compound task branch and tries to find an appropriate one.
|
||||
/// </summary>
|
||||
private bool TryFindSatisfiedMethod(HTNCompoundTask compoundId, Queue<HTNTask> tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex)
|
||||
private bool TryFindSatisfiedMethod(HTNCompoundTask compoundId, Stack<HTNTask> tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex)
|
||||
{
|
||||
var compound = _protoManager.Index<HTNCompoundPrototype>(compoundId.Task);
|
||||
|
||||
|
|
@ -182,9 +182,9 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
if (!isValid)
|
||||
continue;
|
||||
|
||||
foreach (var task in branch.Tasks)
|
||||
foreach (var task in branch.Tasks.AsEnumerable().Reverse())
|
||||
{
|
||||
tasksToProcess.Enqueue(task);
|
||||
tasksToProcess.Push(task);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -198,7 +198,7 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
/// </summary>
|
||||
private void RestoreTolastDecomposedTask(
|
||||
Stack<DecompositionState> decompHistory,
|
||||
Queue<HTNTask> tasksToProcess,
|
||||
Stack<HTNTask> tasksToProcess,
|
||||
List<Dictionary<string, object>?> appliedStates,
|
||||
List<HTNPrimitiveTask> finalPlan,
|
||||
ref int primitiveCount,
|
||||
|
|
@ -223,7 +223,7 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
|||
|
||||
primitiveCount = lastDecomp.PrimitiveCount;
|
||||
blackboard = lastDecomp.Blackboard;
|
||||
tasksToProcess.Enqueue(lastDecomp.CompoundTask);
|
||||
tasksToProcess.Push(lastDecomp.CompoundTask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Content.Shared.Radiation.Systems;
|
|||
using Robust.Server.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Radiation.Systems;
|
||||
|
||||
|
|
@ -155,16 +156,17 @@ public sealed class GeigerSystem : SharedGeigerSystem
|
|||
if (!component.Sounds.TryGetValue(component.DangerLevel, out var sounds))
|
||||
return;
|
||||
|
||||
if (component.User == null)
|
||||
return;
|
||||
|
||||
if (!_player.TryGetSessionByEntity(component.User.Value, out var session))
|
||||
return;
|
||||
|
||||
var sound = _audio.ResolveSound(sounds);
|
||||
var param = sounds.Params.WithLoop(true).WithVolume(-4f);
|
||||
var param = sounds.Params.WithLoop(true).WithVolume(component.Volume);
|
||||
|
||||
component.Stream = _audio.PlayGlobal(sound, session, param)?.Entity;
|
||||
if (component.BroadcastAudio)
|
||||
{
|
||||
// For some reason PlayPvs sounds quieter even at distance 0, so we need to boost the volume a bit for consistency
|
||||
param = sounds.Params.WithLoop(true).WithVolume(component.Volume + 1.5f).WithMaxDistance(component.BroadcastRange);
|
||||
component.Stream = _audio.PlayPvs(sound, uid, param)?.Entity;
|
||||
}
|
||||
else if(component.User is not null && _player.TryGetSessionByEntity(component.User.Value, out var session))
|
||||
component.Stream = _audio.PlayGlobal(sound, session, param)?.Entity;
|
||||
}
|
||||
|
||||
public static GeigerDangerLevel RadsToLevel(float rads)
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems;
|
||||
|
||||
public sealed class StorageFillVisualizerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<StorageFillVisualizerComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<StorageFillVisualizerComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||
SubscribeLocalEvent<StorageFillVisualizerComponent, EntRemovedFromContainerMessage>(OnRemoved);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, StorageFillVisualizerComponent component, ComponentStartup args)
|
||||
{
|
||||
UpdateAppearance(uid, component: component);
|
||||
}
|
||||
|
||||
private void OnInserted(EntityUid uid, StorageFillVisualizerComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
UpdateAppearance(uid, component: component);
|
||||
}
|
||||
|
||||
private void OnRemoved(EntityUid uid, StorageFillVisualizerComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
UpdateAppearance(uid, component: component);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, StorageComponent? storage = null, AppearanceComponent? appearance = null,
|
||||
StorageFillVisualizerComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref storage, ref appearance, ref component, false))
|
||||
return;
|
||||
|
||||
if (component.MaxFillLevels < 1)
|
||||
return;
|
||||
|
||||
if (!_appearance.TryGetData<int>(uid, StorageVisuals.StorageUsed, out var used, appearance))
|
||||
return;
|
||||
|
||||
if (!_appearance.TryGetData<int>(uid, StorageVisuals.Capacity, out var capacity, appearance))
|
||||
return;
|
||||
|
||||
var level = ContentHelpers.RoundToLevels(used, capacity, component.MaxFillLevels);
|
||||
_appearance.SetData(uid, StorageFillVisuals.FillLevel, level, appearance);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,9 @@ namespace Content.Shared.Lathe
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<LatheRecipePackPrototype>> DynamicPacks = new();
|
||||
// Note that this shouldn't be modified dynamically.
|
||||
// I.e., this + the static recipies should represent all recipies that the lathe can ever make
|
||||
// Otherwise the material arbitrage test and/or LatheSystem.GetAllBaseRecipes needs to be updated
|
||||
|
||||
/// <summary>
|
||||
/// The lathe's construction queue
|
||||
|
|
@ -81,15 +84,16 @@ namespace Content.Shared.Lathe
|
|||
public sealed class LatheGetRecipesEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Lathe;
|
||||
public readonly LatheComponent Comp;
|
||||
|
||||
public bool getUnavailable;
|
||||
public bool GetUnavailable;
|
||||
|
||||
public HashSet<ProtoId<LatheRecipePrototype>> Recipes = new();
|
||||
|
||||
public LatheGetRecipesEvent(EntityUid lathe, bool forced)
|
||||
public LatheGetRecipesEvent(Entity<LatheComponent> lathe, bool forced)
|
||||
{
|
||||
Lathe = lathe;
|
||||
getUnavailable = forced;
|
||||
(Lathe, Comp) = lathe;
|
||||
GetUnavailable = forced;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,25 @@ public abstract class SharedLatheSystem : EntitySystem
|
|||
BuildInverseRecipeDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the set of all recipes that a lathe could possibly ever create (e.g., if all techs were unlocked).
|
||||
/// </summary>
|
||||
public HashSet<ProtoId<LatheRecipePrototype>> GetAllPossibleRecipes(LatheComponent component)
|
||||
{
|
||||
var recipes = new HashSet<ProtoId<LatheRecipePrototype>>();
|
||||
foreach (var pack in component.StaticPacks)
|
||||
{
|
||||
recipes.UnionWith(_proto.Index(pack).Recipes);
|
||||
}
|
||||
|
||||
foreach (var pack in component.DynamicPacks)
|
||||
{
|
||||
recipes.UnionWith(_proto.Index(pack).Recipes);
|
||||
}
|
||||
|
||||
return recipes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add every recipe in the list of recipe packs to a single hashset.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ public sealed class HungerSystem : EntitySystem
|
|||
entity.Comp.LastAuthoritativeHungerChangeTime = _timing.CurTime;
|
||||
entity.Comp.LastAuthoritativeHungerValue = ClampHungerWithinThresholds(entity.Comp, value);
|
||||
DirtyField(entity.Owner, entity.Comp, nameof(HungerComponent.LastAuthoritativeHungerChangeTime));
|
||||
DirtyField(entity.Owner, entity.Comp, nameof(HungerComponent.LastAuthoritativeHungerValue));
|
||||
}
|
||||
|
||||
private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = null)
|
||||
|
|
@ -140,6 +141,7 @@ public sealed class HungerSystem : EntitySystem
|
|||
return;
|
||||
|
||||
component.CurrentThreshold = calculatedHungerThreshold;
|
||||
DirtyField(uid, component, nameof(HungerComponent.CurrentThreshold));
|
||||
DoHungerThresholdEffects(uid, component);
|
||||
}
|
||||
|
||||
|
|
@ -176,10 +178,12 @@ public sealed class HungerSystem : EntitySystem
|
|||
if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier))
|
||||
{
|
||||
component.ActualDecayRate = component.BaseDecayRate * modifier;
|
||||
DirtyField(uid, component, nameof(HungerComponent.ActualDecayRate));
|
||||
SetAuthoritativeHungerValue((uid, component), GetHunger(component));
|
||||
}
|
||||
|
||||
component.LastThreshold = component.CurrentThreshold;
|
||||
DirtyField(uid, component, nameof(HungerComponent.LastThreshold));
|
||||
}
|
||||
|
||||
private void DoContinuousHungerEffects(EntityUid uid, HungerComponent? component = null)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ public sealed class ThirstSystem : EntitySystem
|
|||
component.CurrentThirst = _random.Next(
|
||||
(int) component.ThirstThresholds[ThirstThreshold.Thirsty] + 10,
|
||||
(int) component.ThirstThresholds[ThirstThreshold.Okay] - 1);
|
||||
|
||||
DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst));
|
||||
}
|
||||
component.NextUpdateTime = _timing.CurTime;
|
||||
component.CurrentThirstThreshold = GetThirstThreshold(component, component.CurrentThirst);
|
||||
|
|
@ -65,6 +67,8 @@ public sealed class ThirstSystem : EntitySystem
|
|||
// TODO: Check all thresholds make sense and throw if they don't.
|
||||
UpdateEffects(uid, component);
|
||||
|
||||
DirtyFields(uid, component, null, nameof(ThirstComponent.NextUpdateTime), nameof(ThirstComponent.CurrentThirstThreshold), nameof(ThirstComponent.LastThirstThreshold));
|
||||
|
||||
TryComp(uid, out MovementSpeedModifierComponent? moveMod);
|
||||
_movement.RefreshMovementSpeedModifiers(uid, moveMod);
|
||||
}
|
||||
|
|
@ -113,7 +117,7 @@ public sealed class ThirstSystem : EntitySystem
|
|||
component.ThirstThresholds[ThirstThreshold.OverHydrated]
|
||||
);
|
||||
|
||||
EntityManager.DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst));
|
||||
DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst));
|
||||
}
|
||||
|
||||
private bool IsMovementThreshold(ThirstThreshold threshold)
|
||||
|
|
@ -188,6 +192,9 @@ public sealed class ThirstSystem : EntitySystem
|
|||
_alerts.ClearAlertCategory(uid, component.ThirstyCategory);
|
||||
}
|
||||
|
||||
DirtyField(uid, component, nameof(ThirstComponent.LastThirstThreshold));
|
||||
DirtyField(uid, component, nameof(ThirstComponent.ActualDecayRate));
|
||||
|
||||
var ev = new MoodEffectEvent("Thirst" + component.CurrentThirstThreshold);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,24 @@ public sealed partial class GeigerComponent : Component
|
|||
/// Played only for current user.
|
||||
/// </summary>
|
||||
public EntityUid? Stream;
|
||||
|
||||
/// <summary>
|
||||
/// Mark true if the audio should be heard by everyone around the device
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool BroadcastAudio = false;
|
||||
|
||||
/// <summary>
|
||||
/// The distance within which the broadcast tone can be heard.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float BroadcastRange = 4f;
|
||||
|
||||
/// <summary>
|
||||
/// The volume of the warning tone.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Volume = -4f;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ using Robust.Shared.Random;
|
|||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Rounding;
|
||||
|
||||
namespace Content.Shared.Storage.EntitySystems;
|
||||
|
||||
|
|
@ -880,6 +881,12 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||
_appearance.SetData(uid, StorageVisuals.Open, isOpen, appearance);
|
||||
_appearance.SetData(uid, SharedBagOpenVisuals.BagState, isOpen ? SharedBagState.Open : SharedBagState.Closed, appearance);
|
||||
|
||||
if (TryComp<StorageFillVisualizerComponent>(uid, out var storageFillVisualizerComp))
|
||||
{
|
||||
var level = ContentHelpers.RoundToLevels(used, capacity, storageFillVisualizerComp.MaxFillLevels);
|
||||
_appearance.SetData(uid, StorageFillVisuals.FillLevel, level, appearance);
|
||||
}
|
||||
|
||||
// HideClosedStackVisuals true sets the StackVisuals.Hide to the open state of the storage.
|
||||
// This is for containers that only show their contents when open. (e.g. donut boxes)
|
||||
if (storage.HideStackVisualsWhenClosed)
|
||||
|
|
|
|||
|
|
@ -1,67 +1,4 @@
|
|||
Entries:
|
||||
- author: Beck Thompson
|
||||
changes:
|
||||
- message: Added some new scrap that contains uranium and plasma that can be found
|
||||
when salvaging!
|
||||
type: Add
|
||||
id: 7709
|
||||
time: '2024-12-16T12:33:34.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32198
|
||||
- author: Sirionaut, SpaceBaa
|
||||
changes:
|
||||
- message: Animals no longer consume nutrients when their udder/woolcoat is full.
|
||||
type: Fix
|
||||
- message: Entities with udders display a rough hunger level when examined.
|
||||
type: Add
|
||||
id: 7710
|
||||
time: '2024-12-16T12:53:22.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32905
|
||||
- author: thetolbean
|
||||
changes:
|
||||
- message: Teleporting or dashing now causes you to stop pulling objects.
|
||||
type: Tweak
|
||||
id: 7711
|
||||
time: '2024-12-16T14:09:20.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33252
|
||||
- author: KieueCaprie
|
||||
changes:
|
||||
- message: The lizard plushie will now be visible when held in your hands!
|
||||
type: Add
|
||||
id: 7712
|
||||
time: '2024-12-16T14:14:51.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32583
|
||||
- author: Bhijn and Myr
|
||||
changes:
|
||||
- message: Admins no longer count towards the playercount cap, meaning you no longer
|
||||
need to wait for multiple people to leave in order to join after an admin has
|
||||
joined.
|
||||
type: Tweak
|
||||
id: 7713
|
||||
time: '2024-12-16T14:19:15.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33424
|
||||
- author: dragonryan06
|
||||
changes:
|
||||
- message: 'New cocktail: the Zombie.'
|
||||
type: Add
|
||||
id: 7714
|
||||
time: '2024-12-16T15:25:06.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32802
|
||||
- author: SlamBamActionman
|
||||
changes:
|
||||
- message: Added Holy damage to the Chaplain's bible, holy water and the gohei,
|
||||
which can be exclusively dealt to spirits.
|
||||
type: Add
|
||||
id: 7715
|
||||
time: '2024-12-16T15:33:32.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32755
|
||||
- author: Vexerot
|
||||
changes:
|
||||
- message: Security Belts and Security Carriers now provide 10% reduced explosion
|
||||
damage to contents.
|
||||
type: Tweak
|
||||
id: 7716
|
||||
time: '2024-12-16T16:27:40.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33253
|
||||
- author: goet
|
||||
changes:
|
||||
- message: Spaceshrooms can now be cooked on the electric grill.
|
||||
|
|
@ -3909,3 +3846,63 @@
|
|||
id: 8208
|
||||
time: '2025-04-17T03:14:17.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36642
|
||||
- author: VerinSenpai
|
||||
changes:
|
||||
- message: You can now use actions within the action window.
|
||||
type: Add
|
||||
id: 8209
|
||||
time: '2025-04-17T10:08:09.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35642
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- message: Fixed some intense explosion types using incorrect overlay sprites.
|
||||
type: Fix
|
||||
id: 8210
|
||||
time: '2025-04-17T10:12:14.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36644
|
||||
- author: Beck Thompson, SlamBamActionman
|
||||
changes:
|
||||
- message: Fixed trash bag visuals. The appearance will now update correctly depending
|
||||
on how many items it contains!
|
||||
type: Fix
|
||||
id: 8211
|
||||
time: '2025-04-17T10:36:23.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32386
|
||||
- author: Callmore
|
||||
changes:
|
||||
- message: Cyborgs now require being pryed to open/close the panel instead of screwed.
|
||||
type: Tweak
|
||||
id: 8212
|
||||
time: '2025-04-17T10:38:00.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32796
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: Handheld geiger counters can now by heard by everyone nearby.
|
||||
type: Tweak
|
||||
id: 8213
|
||||
time: '2025-04-17T11:24:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30463
|
||||
- author: Centronias
|
||||
changes:
|
||||
- message: Tank harnesses can be made at lathes, similar to utility belts.
|
||||
type: Add
|
||||
- message: Tank harnesses take up a 1x2 space in inventories, half of their previous
|
||||
2x2.
|
||||
type: Tweak
|
||||
id: 8214
|
||||
time: '2025-04-17T12:33:31.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35590
|
||||
- author: Jacktastic09
|
||||
changes:
|
||||
- message: Added the Solid Headband, available via hacked ClothesMate vending machines
|
||||
type: Add
|
||||
id: 8215
|
||||
time: '2025-04-17T13:43:44.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36650
|
||||
- author: Hoodie42
|
||||
changes:
|
||||
- message: The banjo can now be worn on the back or suit storage slots.
|
||||
type: Tweak
|
||||
id: 8216
|
||||
time: '2025-04-17T14:05:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34057
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ meta:
|
|||
engineVersion: 253.0.0
|
||||
forkId: ""
|
||||
forkVersion: ""
|
||||
time: 04/14/2025 22:52:39
|
||||
entityCount: 17812
|
||||
time: 04/17/2025 05:34:31
|
||||
entityCount: 17813
|
||||
maps:
|
||||
- 1
|
||||
grids:
|
||||
|
|
@ -79,7 +79,7 @@ entities:
|
|||
version: 6
|
||||
-1,0:
|
||||
ind: -1,0
|
||||
tiles: HQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAABHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAAAHgAAAAACHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAACHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAADHgAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAABHgAAAAABHgAAAAACHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAAAHgAAAAAAHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAABHgAAAAAAHgAAAAABHgAAAAACHgAAAAACHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAACHgAAAAABHgAAAAADHgAAAAABHgAAAAAAHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAADHQAAAAAAHgAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAAAHgAAAAABHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHgAAAAACHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHQAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAADHgAAAAACHgAAAAABHgAAAAADHgAAAAADHgAAAAACHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAAAHgAAAAACHgAAAAABHQAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAAB
|
||||
tiles: HQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAABHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAAAHgAAAAACHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAACHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAADHgAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAABHgAAAAABHgAAAAACHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAAAHgAAAAAAHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAABHgAAAAAAHgAAAAABHgAAAAACHgAAAAACHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAACHgAAAAABHgAAAAADHgAAAAABHgAAAAAAHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAADHQAAAAAAHgAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAAAHgAAAAABHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHgAAAAACHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHQAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAADHgAAAAACHgAAAAABHgAAAAADHgAAAAADHgAAAAACHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAAAHgAAAAACHgAAAAABHQAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAAB
|
||||
version: 6
|
||||
0,-1:
|
||||
ind: 0,-1
|
||||
|
|
@ -7277,7 +7277,7 @@ entities:
|
|||
pos: -30.5,13.5
|
||||
parent: 2
|
||||
- type: Door
|
||||
secondsUntilStateChange: -52499.977
|
||||
secondsUntilStateChange: -52533.863
|
||||
state: Opening
|
||||
- type: DeviceLinkSource
|
||||
lastSignals:
|
||||
|
|
@ -43892,7 +43892,7 @@ entities:
|
|||
pos: -9.5,51.5
|
||||
parent: 2
|
||||
- type: Door
|
||||
secondsUntilStateChange: -300255.53
|
||||
secondsUntilStateChange: -300289.4
|
||||
state: Opening
|
||||
- uid: 6747
|
||||
components:
|
||||
|
|
@ -43900,7 +43900,7 @@ entities:
|
|||
pos: -8.5,51.5
|
||||
parent: 2
|
||||
- type: Door
|
||||
secondsUntilStateChange: -300256.25
|
||||
secondsUntilStateChange: -300290.12
|
||||
state: Opening
|
||||
- uid: 6749
|
||||
components:
|
||||
|
|
@ -43908,7 +43908,7 @@ entities:
|
|||
pos: -6.5,51.5
|
||||
parent: 2
|
||||
- type: Door
|
||||
secondsUntilStateChange: -300254.8
|
||||
secondsUntilStateChange: -300288.7
|
||||
state: Opening
|
||||
- uid: 6750
|
||||
components:
|
||||
|
|
@ -43916,7 +43916,7 @@ entities:
|
|||
pos: -5.5,51.5
|
||||
parent: 2
|
||||
- type: Door
|
||||
secondsUntilStateChange: -300254.2
|
||||
secondsUntilStateChange: -300288.06
|
||||
state: Opening
|
||||
- uid: 9721
|
||||
components:
|
||||
|
|
@ -54147,14 +54147,6 @@ entities:
|
|||
parent: 2
|
||||
- type: AtmosPipeColor
|
||||
color: '#0055CCFF'
|
||||
- uid: 11648
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -4.5,6.5
|
||||
parent: 2
|
||||
- type: AtmosPipeColor
|
||||
color: '#990000FF'
|
||||
- uid: 11654
|
||||
components:
|
||||
- type: Transform
|
||||
|
|
@ -54513,6 +54505,11 @@ entities:
|
|||
parent: 2
|
||||
- type: AtmosPipeColor
|
||||
color: '#990000FF'
|
||||
- uid: 14534
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -3.5,6.5
|
||||
parent: 2
|
||||
- uid: 15192
|
||||
components:
|
||||
- type: Transform
|
||||
|
|
@ -66771,6 +66768,12 @@ entities:
|
|||
parent: 2
|
||||
- type: AtmosPipeColor
|
||||
color: '#0055CCFF'
|
||||
- uid: 11648
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -4.5,6.5
|
||||
parent: 2
|
||||
- uid: 11655
|
||||
components:
|
||||
- type: Transform
|
||||
|
|
@ -104939,12 +104942,6 @@ entities:
|
|||
- type: Transform
|
||||
pos: 3.5,-6.5
|
||||
parent: 2
|
||||
- uid: 14534
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 1.5707963267948966 rad
|
||||
pos: -3.5,6.5
|
||||
parent: 2
|
||||
- uid: 14555
|
||||
components:
|
||||
- type: Transform
|
||||
|
|
@ -105657,6 +105654,11 @@ entities:
|
|||
rot: 3.141592653589793 rad
|
||||
pos: 12.5,-7.5
|
||||
parent: 2
|
||||
- uid: 17813
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -3.5,6.5
|
||||
parent: 2
|
||||
- proto: WallSolid
|
||||
entities:
|
||||
- uid: 19
|
||||
|
|
|
|||
|
|
@ -149,4 +149,5 @@
|
|||
ToyFigurinePassenger: 1
|
||||
ToyFigurineGreytider: 1
|
||||
ClothingBackpackSatchelSmugglerUnanchored: 1
|
||||
ClothingHeadHatSolidHeadband: 2
|
||||
# DO NOT ADD MORE, USE UNIFORM DYING
|
||||
|
|
|
|||
|
|
@ -1148,3 +1148,14 @@
|
|||
sprite: Clothing/Head/Hats/beret_medic.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/beret_medic.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
id: ClothingHeadHatSolidHeadband
|
||||
name: solid headband
|
||||
description: "You'll feel like you're Invisible while wearing this! (DISCLAIMER: DOES NOT ACTUALLY MAKE THE WEARER INVISIBLE)"
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Hats/solidheadband.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/solidheadband.rsi
|
||||
|
|
|
|||
|
|
@ -129,3 +129,7 @@
|
|||
sprite: Clothing/OuterClothing/Vests/tankharness.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/OuterClothing/Vests/tankharness.rsi
|
||||
- type: Item
|
||||
size: Normal # Make smaller than typical outer clothing
|
||||
shape:
|
||||
- 0, 0, 0, 1
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
soundHit:
|
||||
collection: MetalThud
|
||||
- type: CombatMode
|
||||
- type: NoSlip
|
||||
- type: StaticPrice
|
||||
price: 1250
|
||||
- type: Fixtures
|
||||
|
|
@ -35,7 +36,7 @@
|
|||
- type: Sprite
|
||||
sprite: Mobs/Silicon/chassis.rsi
|
||||
- type: RotationVisuals
|
||||
defaultRotation: 90
|
||||
defaultRotation: 90 # Sunrise-Edit
|
||||
horizontalRotation: 90
|
||||
- type: MobState
|
||||
allowedStates:
|
||||
|
|
@ -122,6 +123,7 @@
|
|||
- BorgChassis
|
||||
- RoboticsConsole
|
||||
- type: WiresPanel
|
||||
openingTool: Prying
|
||||
openDelay: 3
|
||||
- type: ActivatableUIRequiresPanel
|
||||
- type: NameIdentifier
|
||||
|
|
|
|||
|
|
@ -4,34 +4,34 @@
|
|||
name: Geiger counter
|
||||
description: A handheld device used for detecting and measuring radiation pulses.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/geiger.rsi
|
||||
layers:
|
||||
- state: geiger_base
|
||||
- state: geiger_on_idle
|
||||
map: ["enum.GeigerLayers.Screen"]
|
||||
shader: unshaded
|
||||
visible: false
|
||||
- type: Item
|
||||
sprite: Objects/Tools/geiger.rsi
|
||||
- type: Geiger
|
||||
showControl: true
|
||||
showExamine: true
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.GeigerVisuals.IsEnabled:
|
||||
GeigerLayers.Screen:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
enum.GeigerVisuals.DangerLevel:
|
||||
GeigerLayers.Screen:
|
||||
None: {state: geiger_on_idle}
|
||||
Low: {state: geiger_on_low}
|
||||
Med: {state: geiger_on_med}
|
||||
High: {state: geiger_on_high}
|
||||
Extreme: {state: geiger_on_ext}
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Plastic: 100
|
||||
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/geiger.rsi
|
||||
layers:
|
||||
- state: geiger_base
|
||||
- state: geiger_on_idle
|
||||
map: ["enum.GeigerLayers.Screen"]
|
||||
shader: unshaded
|
||||
visible: false
|
||||
- type: Item
|
||||
sprite: Objects/Tools/geiger.rsi
|
||||
- type: Geiger
|
||||
showControl: true
|
||||
showExamine: true
|
||||
broadcastAudio: true
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.GeigerVisuals.IsEnabled:
|
||||
GeigerLayers.Screen:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
enum.GeigerVisuals.DangerLevel:
|
||||
GeigerLayers.Screen:
|
||||
None: {state: geiger_on_idle}
|
||||
Low: {state: geiger_on_low}
|
||||
Med: {state: geiger_on_med}
|
||||
High: {state: geiger_on_high}
|
||||
Extreme: {state: geiger_on_ext}
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Plastic: 100
|
||||
|
|
|
|||
|
|
@ -184,6 +184,12 @@
|
|||
- type: Item
|
||||
size: Normal
|
||||
sprite: Objects/Fun/Instruments/banjo.rsi
|
||||
- type: Clothing
|
||||
quickEquip: false
|
||||
slots:
|
||||
- back
|
||||
- suitStorage
|
||||
sprite: Objects/Fun/Instruments/banjo.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- StringInstrument
|
||||
|
|
|
|||
|
|
@ -420,7 +420,6 @@
|
|||
- type: MindShieldImplant
|
||||
- type: Tag
|
||||
tags:
|
||||
- MindShield
|
||||
- SubdermalImplant
|
||||
|
||||
# Centcomm implants
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
id: SolarPanelPlasma
|
||||
parent: SolarPanelBasePhysSprite
|
||||
name: solar panel plasma
|
||||
description: A plasma solar panel that generates power.
|
||||
description: A few sheets of plasma glass that generate electricity when hit by photons.
|
||||
components:
|
||||
- type: PowerSupplier
|
||||
supplyRampTolerance: 500
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
id: SolarPanelUranium
|
||||
parent: SolarPanelBasePhysSprite
|
||||
name: solar panel uranium
|
||||
description: A uranium solar panel that generates power.
|
||||
description: A few sheets of uranium glass that generate electricity when hit by photons.
|
||||
components:
|
||||
- type: PowerSupplier
|
||||
supplyRampTolerance: 500
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
id: SolarPanel
|
||||
parent: SolarPanelBasePhysSprite
|
||||
name: solar panel
|
||||
description: A solar panel that generates power.
|
||||
description: A few sheets of glass that generate electricity when hit by photons.
|
||||
components:
|
||||
- type: PowerSupplier
|
||||
supplyRampTolerance: 500
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
- HandheldGPSBasic
|
||||
- TRayScanner
|
||||
- UtilityBelt
|
||||
- ClothingOuterVestTank
|
||||
- HandheldStationMap
|
||||
- ClothingHeadHatWelding
|
||||
- ClothingHeadHatCone
|
||||
|
|
|
|||
|
|
@ -237,3 +237,11 @@
|
|||
completetime: 3
|
||||
materials:
|
||||
Plastic: 200
|
||||
|
||||
- type: latheRecipe
|
||||
id: ClothingOuterVestTank
|
||||
result: ClothingOuterVestTank
|
||||
completetime: 2
|
||||
materials:
|
||||
Cloth: 100
|
||||
Steel: 50
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
- PetsNT
|
||||
- Zombie
|
||||
- Revolutionary
|
||||
- Dragon
|
||||
- Xeno
|
||||
- AllHostile
|
||||
- Wizard
|
||||
# Sunrise Edit
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
# Does not currently support prototype hot-reloading. See comments in c# file.
|
||||
|
||||
# Note that for every explosion type you define, explosions & nukes will start performing worse
|
||||
# You should only define a new explopsion type if you really need to
|
||||
#
|
||||
# If you just want to modify properties other than `damagePerIntensity`, it'd be better to
|
||||
# split off explosion damage & explosion visuals/effects into their own separate prototypes.
|
||||
|
||||
- type: explosion
|
||||
id: Default
|
||||
damagePerIntensity:
|
||||
|
|
@ -43,7 +49,7 @@
|
|||
intensityPerState: 20
|
||||
lightColor: Orange
|
||||
texturePath: /Textures/Effects/fire.rsi
|
||||
fireStates: 6
|
||||
fireStates: 3
|
||||
|
||||
- type: explosion
|
||||
id: Radioactive
|
||||
|
|
@ -100,7 +106,7 @@
|
|||
intensityPerState: 20
|
||||
lightColor: Orange
|
||||
texturePath: /Textures/Effects/fire.rsi
|
||||
fireStates: 6
|
||||
fireStates: 3
|
||||
|
||||
- type: explosion
|
||||
id: HardBomb
|
||||
|
|
@ -116,7 +122,7 @@
|
|||
intensityPerState: 20
|
||||
lightColor: Orange
|
||||
texturePath: /Textures/Effects/fire.rsi
|
||||
fireStates: 6
|
||||
fireStates: 3
|
||||
|
||||
- type: explosion
|
||||
id: FireBomb
|
||||
|
|
@ -129,7 +135,7 @@
|
|||
#Sunrise-end
|
||||
lightColor: Orange
|
||||
texturePath: /Textures/Effects/fire.rsi
|
||||
fireStates: 6
|
||||
fireStates: 3
|
||||
fireStacks: 2
|
||||
|
||||
# STOP
|
||||
|
|
|
|||
|
|
@ -870,9 +870,6 @@
|
|||
- type: Tag
|
||||
id: MimeHappyHonk
|
||||
|
||||
- type: Tag
|
||||
id: MindShield
|
||||
|
||||
- type: Tag
|
||||
id: MindTransferTarget
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 464 B |
BIN
Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/icon.png
Normal file
BIN
Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
Binary file not shown.
|
After Width: | Height: | Size: 468 B |
Binary file not shown.
|
After Width: | Height: | Size: 462 B |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Sprited by Jacktastic09 (Discord)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "equipped-HELMET",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 983 B |
Binary file not shown.
|
After Width: | Height: | Size: 983 B |
|
|
@ -5,7 +5,7 @@
|
|||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/vgstation-coders/vgstation13 at 8d9c91e19cb52713c7f7f1804c2b6f7203f8d331",
|
||||
"copyright": "https://github.com/vgstation-coders/vgstation13 at 8d9c91e19cb52713c7f7f1804c2b6f7203f8d331, equipped sprites made by Hoodie42 for Space Station 14",
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
|
|
@ -17,6 +17,14 @@
|
|||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "equipped-BACKPACK",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "equipped-SUITSTORAGE",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue