Merge branch 'master' into ion-storm-refactor

This commit is contained in:
metalgearsloth
2024-11-20 18:22:08 +11:00
committed by GitHub
150 changed files with 5700 additions and 2230 deletions

View File

@@ -28,7 +28,8 @@ namespace Content.Server.Announcements
}
else
{
var message = string.Join(' ', new ArraySegment<string>(args, 1, args.Length-1));
// Explicit IEnumerable<string> due to overload ambiguity on .NET 9
var message = string.Join(' ', (IEnumerable<string>)new ArraySegment<string>(args, 1, args.Length-1));
chat.DispatchGlobalAnnouncement(message, args[0], colorOverride: Color.Gold);
}
shell.WriteLine("Sent!");

View File

@@ -184,7 +184,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
return;
var players = _playerManager.Sessions
.Where(x => GameTicker.PlayerGameStatuses[x.UserId] == PlayerGameStatus.JoinedGame)
.Where(x => GameTicker.PlayerGameStatuses.TryGetValue(x.UserId, out var status) && status == PlayerGameStatus.JoinedGame)
.ToList();
ChooseAntags((uid, component), players, midround: true);

View File

@@ -1,6 +1,6 @@
using System.Globalization;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.Ghost;
using Content.Server.Hands.Systems;
using Content.Server.Inventory;
@@ -14,6 +14,7 @@ using Content.Shared.Bed.Cryostorage;
using Content.Shared.Chat;
using Content.Shared.Climbing.Systems;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Shared.Mind.Components;
using Content.Shared.StationRecords;
@@ -26,7 +27,6 @@ using Robust.Shared.Containers;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Player;
using System.Globalization;
namespace Content.Server.Bed.Cryostorage;

View File

@@ -38,6 +38,7 @@ public sealed class SeedExtractorSystem : EntitySystem
args.User, PopupType.Medium);
QueueDel(args.Used);
args.Handled = true;
var amount = _random.Next(seedExtractor.BaseMinSeeds, seedExtractor.BaseMaxSeeds + 1);
var coords = Transform(uid).Coordinates;

View File

@@ -424,7 +424,7 @@ public record struct PriceCalculationEvent()
[ByRefEvent]
public record struct EstimatedPriceCalculationEvent()
{
public EntityPrototype Prototype;
public required EntityPrototype Prototype;
/// <summary>
/// The total price of the entity.

View File

@@ -82,6 +82,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
if (component.DeleteEmpty && component.Charges <= 0)
UseUpCrayon(uid, args.User);
else
_uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
}
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)

View File

@@ -184,6 +184,6 @@ namespace Content.Server.GameTicking
=> UserHasJoinedGame(session.UserId);
public bool UserHasJoinedGame(NetUserId userId)
=> PlayerGameStatuses[userId] == PlayerGameStatus.JoinedGame;
=> PlayerGameStatuses.TryGetValue(userId, out var status) && status == PlayerGameStatus.JoinedGame;
}
}

View File

@@ -7,12 +7,12 @@ using Content.Server.Spawners.Components;
using Content.Server.Speech.Components;
using Content.Server.Station.Components;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Players;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
@@ -455,71 +455,4 @@ namespace Content.Server.GameTicking
#endregion
}
/// <summary>
/// Event raised broadcast before a player is spawned by the GameTicker.
/// You can use this event to spawn a player off-station on late-join but also at round start.
/// When this event is handled, the GameTicker will not perform its own player-spawning logic.
/// </summary>
[PublicAPI]
public sealed class PlayerBeforeSpawnEvent : HandledEntityEventArgs
{
public ICommonSession Player { get; }
public HumanoidCharacterProfile Profile { get; }
public string? JobId { get; }
public bool LateJoin { get; }
public EntityUid Station { get; }
public PlayerBeforeSpawnEvent(ICommonSession player,
HumanoidCharacterProfile profile,
string? jobId,
bool lateJoin,
EntityUid station)
{
Player = player;
Profile = profile;
JobId = jobId;
LateJoin = lateJoin;
Station = station;
}
}
/// <summary>
/// Event raised both directed and broadcast when a player has been spawned by the GameTicker.
/// You can use this to handle people late-joining, or to handle people being spawned at round start.
/// Can be used to give random players a role, modify their equipment, etc.
/// </summary>
[PublicAPI]
public sealed class PlayerSpawnCompleteEvent : EntityEventArgs
{
public EntityUid Mob { get; }
public ICommonSession Player { get; }
public string? JobId { get; }
public bool LateJoin { get; }
public bool Silent { get; }
public EntityUid Station { get; }
public HumanoidCharacterProfile Profile { get; }
// Ex. If this is the 27th person to join, this will be 27.
public int JoinOrder { get; }
public PlayerSpawnCompleteEvent(EntityUid mob,
ICommonSession player,
string? jobId,
bool lateJoin,
bool silent,
int joinOrder,
EntityUid station,
HumanoidCharacterProfile profile)
{
Mob = mob;
Player = player;
JobId = jobId;
LateJoin = lateJoin;
Silent = silent;
Station = station;
Profile = profile;
JoinOrder = joinOrder;
}
}
}

View File

@@ -6,6 +6,7 @@ using Content.Server.Mind;
using Content.Server.Points;
using Content.Server.RoundEnd;
using Content.Server.Station.Systems;
using Content.Shared.GameTicking;
using Content.Shared.GameTicking.Components;
using Content.Shared.Points;
using Content.Shared.Storage;

View File

@@ -34,10 +34,11 @@ public sealed class GatewayGeneratorSystem : EntitySystem
[Dependency] private readonly GatewaySystem _gateway = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedSalvageSystem _salvage = default!;
[Dependency] private readonly TileSystem _tile = default!;
[ValidatePrototypeId<DatasetPrototype>]
private const string PlanetNames = "names_borer";
[ValidatePrototypeId<LocalizedDatasetPrototype>]
private const string PlanetNames = "NamesBorer";
// TODO:
// Fix shader some more
@@ -102,7 +103,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem
var mapId = _mapManager.CreateMap();
var mapUid = _mapManager.GetMapEntityId(mapId);
var gatewayName = SharedSalvageSystem.GetFTLName(_protoManager.Index<DatasetPrototype>(PlanetNames), seed);
var gatewayName = _salvage.GetFTLName(_protoManager.Index<LocalizedDatasetPrototype>(PlanetNames), seed);
_metadata.SetEntityName(mapUid, gatewayName);
var origin = new Vector2i(random.Next(-MaxOffset, MaxOffset), random.Next(-MaxOffset, MaxOffset));

View File

@@ -4,7 +4,6 @@ using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.Emp;
using Content.Server.GameTicking;
using Content.Server.Medical.CrewMonitoring;
using Content.Server.Popups;
using Content.Server.Station.Systems;
@@ -14,8 +13,10 @@ using Content.Shared.Damage;
using Content.Shared.DeviceNetwork;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.GameTicking;
using Content.Shared.Interaction;
using Content.Shared.Medical.SuitSensor;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Verbs;
@@ -383,7 +384,7 @@ public sealed class SuitSensorSystem : EntitySystem
// Get mob total damage crit threshold
int? totalDamageThreshold = null;
if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, Shared.Mobs.MobState.Critical, out var critThreshold))
if (_mobThresholdSystem.TryGetThresholdForState(sensor.User.Value, MobState.Critical, out var critThreshold))
totalDamageThreshold = critThreshold.Value.Int();
// finally, form suit sensor status

View File

@@ -11,8 +11,8 @@ public sealed partial class PathfindingSystem
/// </summary>
public record struct BreadthPathArgs()
{
public Vector2i Start;
public List<Vector2i> Ends;
public required Vector2i Start;
public required List<Vector2i> Ends;
public bool Diagonals = false;

View File

@@ -19,7 +19,7 @@ public sealed partial class PathfindingSystem
public List<Vector2i> Points = new();
public List<Vector2i> Path = new();
public Dictionary<Vector2i, Vector2i> CameFrom;
public Dictionary<Vector2i, Vector2i>? CameFrom;
}
public record struct SplinePathArgs(SimplePathArgs Args)

View File

@@ -84,6 +84,6 @@ public sealed partial class PathfindingSystem
public float MaxWiden = 7f;
public List<Vector2i> Path;
public required List<Vector2i> Path;
}
}

View File

@@ -44,7 +44,7 @@ namespace Content.Server.Nutrition.EntitySystems
public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity<UtensilComponent> utensil)
{
if (!EntityManager.TryGetComponent(target, out FoodComponent? food))
return (false, true);
return (false, false);
//Prevents food usage with a wrong utensil
if ((food.Utensil & utensil.Comp.Types) == 0)

View File

@@ -53,7 +53,7 @@ public sealed class KillPersonConditionSystem : EntitySystem
return;
// no other humans to kill
var allHumans = _mind.GetAliveHumansExcept(args.MindId);
var allHumans = _mind.GetAliveHumans(args.MindId);
if (allHumans.Count == 0)
{
args.Cancelled = true;
@@ -77,14 +77,14 @@ public sealed class KillPersonConditionSystem : EntitySystem
return;
// no other humans to kill
var allHumans = _mind.GetAliveHumansExcept(args.MindId);
var allHumans = _mind.GetAliveHumans(args.MindId);
if (allHumans.Count == 0)
{
args.Cancelled = true;
return;
}
var allHeads = new List<EntityUid>();
var allHeads = new HashSet<Entity<MindComponent>>();
foreach (var person in allHumans)
{
if (TryComp<MindComponent>(person, out var mind) && mind.OwnedEntity is { } ent && HasComp<CommandStaffComponent>(ent))

View File

@@ -314,6 +314,9 @@ public sealed class MoverController : SharedMoverController
var linearInput = Vector2.Zero;
var brakeInput = 0f;
var angularInput = 0f;
var linearCount = 0;
var brakeCount = 0;
var angularCount = 0;
foreach (var (pilotUid, pilot, _, consoleXform) in pilots)
{
@@ -322,24 +325,27 @@ public sealed class MoverController : SharedMoverController
if (brakes > 0f)
{
brakeInput += brakes;
brakeCount++;
}
if (strafe.Length() > 0f)
{
var offsetRotation = consoleXform.LocalRotation;
linearInput += offsetRotation.RotateVec(strafe);
linearCount++;
}
if (rotation != 0f)
{
angularInput += rotation;
angularCount++;
}
}
var count = pilots.Count;
linearInput /= count;
angularInput /= count;
brakeInput /= count;
// Don't slow down the shuttle if there's someone just looking at the console
linearInput /= Math.Max(1, linearCount);
angularInput /= Math.Max(1, angularCount);
brakeInput /= Math.Max(1, brakeCount);
// Handle shuttle movement
if (brakeInput > 0f)

View File

@@ -28,7 +28,7 @@ public sealed partial class SalvageSystem
var mission = GetMission(_prototypeManager.Index<SalvageDifficultyPrototype>(missionparams.Difficulty), missionparams.Seed);
data.NextOffer = _timing.CurTime + mission.Duration + TimeSpan.FromSeconds(1);
_labelSystem.Label(cdUid, GetFTLName(_prototypeManager.Index<DatasetPrototype>("names_borer"), missionparams.Seed));
_labelSystem.Label(cdUid, GetFTLName(_prototypeManager.Index<LocalizedDatasetPrototype>("NamesBorer"), missionparams.Seed));
_audio.PlayPvs(component.PrintSound, uid);
UpdateConsoles((station.Value, data));

View File

@@ -104,7 +104,9 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
destComp.BeaconsOnly = true;
destComp.RequireCoordinateDisk = true;
destComp.Enabled = true;
_metaData.SetEntityName(mapUid, SharedSalvageSystem.GetFTLName(_prototypeManager.Index<DatasetPrototype>("names_borer"), _missionParams.Seed));
_metaData.SetEntityName(
mapUid,
_entManager.System<SharedSalvageSystem>().GetFTLName(_prototypeManager.Index<LocalizedDatasetPrototype>("NamesBorer"), _missionParams.Seed));
_entManager.AddComponent<FTLBeaconComponent>(mapUid);
// Saving the mission mapUid to a CD is made optional, in case one is somehow made in a process without a CD entity

View File

@@ -32,7 +32,7 @@ public interface IGridSpawnGroup
public float MaximumDistance { get; }
/// <inheritdoc />
public ProtoId<DatasetPrototype>? NameDataset { get; }
public ProtoId<LocalizedDatasetPrototype>? NameDataset { get; }
/// <inheritdoc />
int MinCount { get; set; }
@@ -75,7 +75,7 @@ public sealed class DungeonSpawnGroup : IGridSpawnGroup
public float MaximumDistance { get; }
/// <inheritdoc />
public ProtoId<DatasetPrototype>? NameDataset { get; }
public ProtoId<LocalizedDatasetPrototype>? NameDataset { get; }
/// <inheritdoc />
public int MinCount { get; set; } = 1;
@@ -106,7 +106,7 @@ public sealed class GridSpawnGroup : IGridSpawnGroup
/// <inheritdoc />
public float MaximumDistance { get; }
public ProtoId<DatasetPrototype>? NameDataset { get; }
public ProtoId<LocalizedDatasetPrototype>? NameDataset { get; }
public int MinCount { get; set; } = 1;
public int MaxCount { get; set; } = 1;
public ComponentRegistry AddComponents { get; set; } = new();

View File

@@ -19,10 +19,10 @@ using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Damage.Components;
using Content.Shared.DeviceNetwork;
using Content.Shared.GameTicking;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components;
using Content.Shared.Parallax.Biomes;
using Content.Shared.Preferences;
using Content.Shared.Salvage;
using Content.Shared.Shuttles.Components;
using Content.Shared.Tiles;

View File

@@ -208,7 +208,7 @@ public sealed partial class ShuttleSystem
if (_protoManager.TryIndex(group.NameDataset, out var dataset))
{
_metadata.SetEntityName(spawned, SharedSalvageSystem.GetFTLName(dataset, _random.Next()));
_metadata.SetEntityName(spawned, _salvage.GetFTLName(dataset, _random.Next()));
}
if (group.Hide)

View File

@@ -8,6 +8,7 @@ using Content.Server.Station.Systems;
using Content.Server.Stunnable;
using Content.Shared.GameTicking;
using Content.Shared.Mobs.Systems;
using Content.Shared.Salvage;
using Content.Shared.Shuttles.Systems;
using Content.Shared.Throwing;
using JetBrains.Annotations;
@@ -51,6 +52,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedSalvageSystem _salvage = default!;
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly StunSystem _stuns = default!;

View File

@@ -1,7 +1,6 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.Radio.Components;
using Content.Server.Roles;
using Content.Server.Station.Systems;
@@ -9,6 +8,7 @@ using Content.Shared.Administration;
using Content.Shared.Chat;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
@@ -17,6 +17,7 @@ using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Stunnable;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;

View File

@@ -1,33 +0,0 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Content.Server.Singularity.EntitySystems;
namespace Content.Server.Singularity.Components;
[RegisterComponent]
public sealed partial class SingularityGeneratorComponent : Component
{
/// <summary>
/// The amount of power this generator has accumulated.
/// If you want to set this use <see cref="SingularityGeneratorSystem.SetPower"/>
/// </summary>
[DataField("power")]
[Access(friends:typeof(SingularityGeneratorSystem))]
public float Power = 0;
/// <summary>
/// The power threshold at which this generator will spawn a singularity.
/// If you want to set this use <see cref="SingularityGeneratorSystem.SetThreshold"/>
/// </summary>
[DataField("threshold")]
[Access(friends:typeof(SingularityGeneratorSystem))]
public float Threshold = 16;
/// <summary>
/// The prototype ID used to spawn a singularity.
/// </summary>
[DataField("spawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
[ViewVariables(VVAccess.ReadWrite)]
public string? SpawnPrototype = "Singularity";
}

View File

@@ -1,14 +1,23 @@
using Content.Server.ParticleAccelerator.Components;
using Content.Server.Singularity.Components;
using Content.Shared.Popups;
using Content.Shared.Singularity.Components;
using Content.Shared.Singularity.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Timing;
namespace Content.Server.Singularity.EntitySystems;
public sealed class SingularityGeneratorSystem : EntitySystem
public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSystem
{
#region Dependencies
[Dependency] private readonly IViewVariablesManager _vvm = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
#endregion Dependencies
public override void Initialize()
@@ -100,11 +109,37 @@ public sealed class SingularityGeneratorSystem : EntitySystem
/// <param name="args">The state of the beginning of the collision.</param>
private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, ref StartCollideEvent args)
{
if (EntityManager.TryGetComponent<SingularityGeneratorComponent>(args.OtherEntity, out var singularityGeneratorComponent))
if (!EntityManager.TryGetComponent<SingularityGeneratorComponent>(args.OtherEntity, out var generatorComp))
return;
if (_timing.CurTime < _metadata.GetPauseTime(uid) + generatorComp.NextFailsafe && !generatorComp.FailsafeDisabled)
{
EntityManager.QueueDeleteEntity(uid);
return;
}
var contained = true;
if (!generatorComp.FailsafeDisabled)
{
var transform = Transform(args.OtherEntity);
var directions = Enum.GetValues<Direction>().Length;
for (var i = 0; i < directions - 1; i += 2) // Skip every other direction, checking only cardinals
{
if (!CheckContainmentField((Direction)i, new Entity<SingularityGeneratorComponent>(args.OtherEntity, generatorComp), transform))
contained = false;
}
}
if (!contained && !generatorComp.FailsafeDisabled)
{
generatorComp.NextFailsafe = _timing.CurTime + generatorComp.FailsafeCooldown;
PopupSystem.PopupEntity(Loc.GetString("comp-generator-failsafe", ("target", args.OtherEntity)), args.OtherEntity, PopupType.LargeCaution);
}
else
{
SetPower(
args.OtherEntity,
singularityGeneratorComponent.Power + component.State switch
generatorComp.Power + component.State switch
{
ParticleAcceleratorPowerState.Standby => 0,
ParticleAcceleratorPowerState.Level0 => 1,
@@ -113,10 +148,46 @@ public sealed class SingularityGeneratorSystem : EntitySystem
ParticleAcceleratorPowerState.Level3 => 8,
_ => 0
},
singularityGeneratorComponent
generatorComp
);
EntityManager.QueueDeleteEntity(uid);
}
EntityManager.QueueDeleteEntity(uid);
}
#endregion Event Handlers
/// <summary>
/// Checks whether there's a containment field in a given direction away from the generator
/// </summary>
/// <param name="transform">The transform component of the singularity generator.</param>
/// <remarks>Mostly copied from <see cref="ContainmentFieldGeneratorSystem"/> </remarks>
private bool CheckContainmentField(Direction dir, Entity<SingularityGeneratorComponent> generator, TransformComponent transform)
{
var component = generator.Comp;
var (worldPosition, worldRotation) = _transformSystem.GetWorldPositionRotation(transform);
var dirRad = dir.ToAngle() + worldRotation;
var ray = new CollisionRay(worldPosition, dirRad.ToVec(), component.CollisionMask);
var rayCastResults = _physics.IntersectRay(transform.MapID, ray, component.FailsafeDistance, generator, false);
var genQuery = GetEntityQuery<ContainmentFieldComponent>();
RayCastResults? closestResult = null;
foreach (var result in rayCastResults)
{
if (genQuery.HasComponent(result.HitEntity))
closestResult = result;
break;
}
if (closestResult == null)
return false;
var ent = closestResult.Value.HitEntity;
// Check that the field can't be moved. The fields' transform parenting is weird, so skip that
return TryComp<PhysicsComponent>(ent, out var collidableComponent) && collidableComponent.BodyType == BodyType.Static;
}
}

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Content.Server.Access.Systems;
using Content.Server.Forensics;
using Content.Server.GameTicking;
using Content.Shared.Access.Components;
using Content.Shared.GameTicking;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Content.Shared.Preferences;

View File

@@ -256,6 +256,11 @@ public sealed partial class StoreSystem
RaiseLocalEvent(buyer, listing.ProductEvent);
}
if (listing.DisableRefund)
{
component.RefundAllowed = false;
}
//log dat shit.
_admin.Add(LogType.StorePurchase,
LogImpact.Low,

View File

@@ -1,11 +1,10 @@
using Content.Server.GameTicking;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Roles;
using Content.Shared.Traits;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
namespace Content.Server.Traits;