Merge remote-tracking branch 'upstream/master' into ups

This commit is contained in:
Jabak
2024-06-21 21:53:12 +03:00
103 changed files with 2092 additions and 340 deletions

View File

@@ -30,7 +30,7 @@ public sealed partial class IdCardConsoleComponent : Component
public readonly List<ProtoId<AccessLevelPrototype>> AccessList;
public readonly ProtoId<AccessLevelPrototype> JobPrototype;
public readonly string? SelectedIcon; //WD-EDIT
public WriteToTargetIdMessage(string fullName, string jobTitle, List<ProtoId<AccessLevelPrototype>> accessList, ProtoId<AccessLevelPrototype> jobPrototype,
string? selectedIcon)
{
@@ -79,63 +79,35 @@ public sealed partial class IdCardConsoleComponent : Component
"Theatre"
};
//WD-EDIT
[DataField("jobIcons")]
public List<string> JobIcons = new()
// WD edit
[DataField, AutoNetworkedField]
public List<List<ProtoId<AccessLevelPrototype>>> AccessLevelsConsole = new()
{
"AtmosphericTechnician",
"Bartender",
"Borg",
"Botanist",
"Boxer",
"Brigmedic",
"Captain",
"CargoTechnician",
"Chaplain",
"Chef",
"Chemist",
"ChiefEngineer",
"ChiefMedicalOfficer",
"Clown",
"CustomId",
"Detective",
"Geneticist",
"HeadOfPersonnel",
"HeadOfSecurity",
"Inspector",
"Janitor",
"Lawyer",
"Librarian",
"MedicalDoctor",
"MedicalIntern",
"Mime",
"Musician",
"Paramedic",
"Passenger",
"Psychologist",
"QuarterMaster",
"Reporter",
"ResearchAssistant",
"ResearchDirector",
"Roboticist",
"Scientist",
"SecurityCadet",
"SecurityOfficer",
"SeniorEngineer",
"SeniorOfficer",
"SeniorPhysician",
"SeniorResearcher",
"ServiceWorker",
"ShaftMiner",
"StationEngineer",
"TechnicalAssistant",
"Virologist",
"Visitor",
"Warden",
"Zookeeper"
new List<ProtoId<AccessLevelPrototype>> {"Captain", "HeadOfPersonnel", "HeadOfSecurity", "ChiefMedicalOfficer", "ChiefEngineer", "ResearchDirector", "Quartermaster", "Command"}, // Command
new List<ProtoId<AccessLevelPrototype>> {"Armory", "Brig", "Security","Detective", "Lawyer"}, // Security
new List<ProtoId<AccessLevelPrototype>> {"Chemistry", "Cryogenics", "Medical"}, // Medical
new List<ProtoId<AccessLevelPrototype>> {"Atmospherics", "Engineering", "External", "Maintenance"}, // Engineering
new List<ProtoId<AccessLevelPrototype>> {"Research"}, // Researching
new List<ProtoId<AccessLevelPrototype>> {"Cargo", "Salvage"}, // Cargo
new List<ProtoId<AccessLevelPrototype>> { "Service", "Theatre", "Bar", "Chapel", "Hydroponics", "Janitor", "Kitchen"} // Service
};
//WD-EDIT
// Command, Service, Security, Medical, Engineering, Researching, Cargo,
[DataField("jobIcons")]
public List<List<string>> JobIcons = new()
{
new List<string> {"Captain", "HeadOfPersonnel", "HeadOfSecurity", "ChiefMedicalOfficer", "ChiefEngineer", "ResearchDirector", "QuarterMaster", "Inspector"},
new List<string> {"HeadOfPersonnel", "Lawyer", "Clown", "Bartender", "Reporter", "Chef", "Botanist", "ServiceWorker", "Zookeeper", "Musician", "Librarian", "Janitor", "Chaplain", "Mime", "Boxer", "Passenger", "Visitor", "Borg", "CustomId"},
new List<string> {"HeadOfSecurity", "Warden", "SeniorOfficer", "SecurityOfficer", "Detective", "SecurityCadet", "Brigmedic", "Lawyer"},
new List<string> {"ChiefMedicalOfficer", "SeniorPhysician", "Paramedic", "Chemist", "MedicalDoctor", "Virologist", "Geneticist", "MedicalIntern", "Psychologist"},
new List<string> {"ChiefEngineer", "SeniorEngineer", "AtmosphericTechnician", "StationEngineer", "TechnicalAssistant"},
new List<string> {"ResearchDirector", "SeniorResearcher", "Scientist", "Roboticist", "ResearchAssistant"},
new List<string> {"QuarterMaster", "ShaftMiner", "CargoTechnician"},
};
// WD EDIT END
[Serializable, NetSerializable]
public sealed class IdCardConsoleBoundUserInterfaceState : BoundUserInterfaceState
{
@@ -150,7 +122,7 @@ public sealed partial class IdCardConsoleComponent : Component
public readonly List<ProtoId<AccessLevelPrototype>>? AllowedModifyAccessList;
public readonly ProtoId<AccessLevelPrototype> TargetIdJobPrototype;
public readonly string? TargetIdJobIcon; //WD-EDIT
public IdCardConsoleBoundUserInterfaceState(
bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized,

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.Components;
/// <summary>
/// Disables client-side physics prediction for this entity.
/// Without this, movement with <see cref="PilotedClothingSystem"/> is very rubberbandy.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class PilotedByClothingComponent : Component
{
}

View File

@@ -0,0 +1,38 @@
using Content.Shared.Whitelist;
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.Components;
/// <summary>
/// Allows an entity stored in this clothing item to pass inputs to the entity wearing it.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class PilotedClothingComponent : Component
{
/// <summary>
/// Whitelist for entities that are allowed to act as pilots when inside this entity.
/// </summary>
[DataField]
public EntityWhitelist? PilotWhitelist;
/// <summary>
/// Should movement input be relayed from the pilot to the target?
/// </summary>
[DataField]
public bool RelayMovement = true;
/// <summary>
/// Reference to the entity contained in the clothing and acting as pilot.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Pilot;
/// <summary>
/// Reference to the entity wearing this clothing who will be controlled by the pilot.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Wearer;
public bool IsActive => Pilot != null && Wearer != null;
}

View File

@@ -0,0 +1,168 @@
using Content.Shared.Clothing.Components;
using Content.Shared.Inventory.Events;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Storage;
using Content.Shared.Whitelist;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
namespace Content.Shared.Clothing.EntitySystems;
public sealed partial class PilotedClothingSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedMoverController _moverController = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PilotedClothingComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<PilotedClothingComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<PilotedClothingComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<PilotedClothingComponent, GotUnequippedEvent>(OnUnequipped);
}
private void OnEntInserted(Entity<PilotedClothingComponent> entity, ref EntInsertedIntoContainerMessage args)
{
// Make sure the entity was actually inserted into storage and not a different container.
if (!TryComp(entity, out StorageComponent? storage) || args.Container != storage.Container)
return;
// Check potential pilot against whitelist, if one exists.
if (entity.Comp.PilotWhitelist?.IsValid(args.Entity, EntityManager) is false)
return;
entity.Comp.Pilot = args.Entity;
Dirty(entity);
// Attempt to setup control link, if Pilot and Wearer are both present.
StartPiloting(entity);
}
private void OnEntRemoved(Entity<PilotedClothingComponent> entity, ref EntRemovedFromContainerMessage args)
{
// Make sure the removed entity is actually the pilot.
if (args.Entity != entity.Comp.Pilot)
return;
StopPiloting(entity);
entity.Comp.Pilot = null;
Dirty(entity);
}
private void OnEquipped(Entity<PilotedClothingComponent> entity, ref GotEquippedEvent args)
{
if (!TryComp(entity, out ClothingComponent? clothing))
return;
// Make sure the clothing item was equipped to the right slot, and not just held in a hand.
var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
if (!isCorrectSlot)
return;
entity.Comp.Wearer = args.Equipee;
Dirty(entity);
// Attempt to setup control link, if Pilot and Wearer are both present.
StartPiloting(entity);
}
private void OnUnequipped(Entity<PilotedClothingComponent> entity, ref GotUnequippedEvent args)
{
StopPiloting(entity);
entity.Comp.Wearer = null;
Dirty(entity);
}
/// <summary>
/// Attempts to establish movement/interaction relay connection(s) from Pilot to Wearer.
/// If either is missing, fails and returns false.
/// </summary>
private bool StartPiloting(Entity<PilotedClothingComponent> entity)
{
// Make sure we have both a Pilot and a Wearer
if (entity.Comp.Pilot == null || entity.Comp.Wearer == null)
return false;
if (!_timing.IsFirstTimePredicted)
return false;
var pilotEnt = entity.Comp.Pilot.Value;
var wearerEnt = entity.Comp.Wearer.Value;
// Add component to block prediction of wearer
EnsureComp<PilotedByClothingComponent>(wearerEnt);
if (entity.Comp.RelayMovement)
{
// Establish movement input relay.
_moverController.SetRelay(pilotEnt, wearerEnt);
}
var pilotEv = new StartedPilotingClothingEvent(entity, wearerEnt);
RaiseLocalEvent(pilotEnt, ref pilotEv);
var wearerEv = new StartingBeingPilotedByClothing(entity, pilotEnt);
RaiseLocalEvent(wearerEnt, ref wearerEv);
return true;
}
/// <summary>
/// Removes components from the Pilot and Wearer to stop the control relay.
/// Returns false if a connection does not already exist.
/// </summary>
private bool StopPiloting(Entity<PilotedClothingComponent> entity)
{
if (entity.Comp.Pilot == null || entity.Comp.Wearer == null)
return false;
// Clean up components on the Pilot
var pilotEnt = entity.Comp.Pilot.Value;
RemCompDeferred<RelayInputMoverComponent>(pilotEnt);
// Clean up components on the Wearer
var wearerEnt = entity.Comp.Wearer.Value;
RemCompDeferred<MovementRelayTargetComponent>(wearerEnt);
RemCompDeferred<PilotedByClothingComponent>(wearerEnt);
// Raise an event on the Pilot
var pilotEv = new StoppedPilotingClothingEvent(entity, wearerEnt);
RaiseLocalEvent(pilotEnt, ref pilotEv);
// Raise an event on the Wearer
var wearerEv = new StoppedBeingPilotedByClothing(entity, pilotEnt);
RaiseLocalEvent(wearerEnt, ref wearerEv);
return true;
}
}
/// <summary>
/// Raised on the Pilot when they gain control of the Wearer.
/// </summary>
[ByRefEvent]
public record struct StartedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer);
/// <summary>
/// Raised on the Pilot when they lose control of the Wearer,
/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer.
/// </summary>
[ByRefEvent]
public record struct StoppedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer);
/// <summary>
/// Raised on the Wearer when the Pilot gains control of them.
/// </summary>
[ByRefEvent]
public record struct StartingBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot);
/// <summary>
/// Raised on the Wearer when the Pilot loses control of them
/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer.
/// </summary>
[ByRefEvent]
public record struct StoppedBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot);

View File

@@ -1,3 +1,4 @@
using Content.Shared._White.Lighting;
using Content.Shared.Doors.Components;
using Content.Shared.Popups;
using Content.Shared.Prying.Components;
@@ -120,6 +121,7 @@ public abstract class SharedAirlockSystem : EntitySystem
public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component)
{
RaiseLocalEvent(uid, new DoorlightsChangedEvent(DoorVisuals.EmergencyLights, component.EmergencyAccess));
Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess);
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._White.Lighting;
using Content.Shared.Doors.Components;
using Content.Shared.Prying.Components;
@@ -55,7 +56,8 @@ public abstract partial class SharedDoorSystem
public void UpdateBoltLightStatus(Entity<DoorBoltComponent> ent)
{
AppearanceSystem.SetData(ent, DoorVisuals.BoltLights, GetBoltLightsVisible(ent));
var value = GetBoltLightsVisible(ent);
AppearanceSystem.SetData(ent, DoorVisuals.BoltLights, value);
}
public bool GetBoltLightsVisible(Entity<DoorBoltComponent> ent)
@@ -84,6 +86,8 @@ public abstract partial class SharedDoorSystem
Dirty(ent, ent.Comp);
UpdateBoltLightStatus(ent);
RaiseLocalEvent(ent, new DoorlightsChangedEvent(DoorVisuals.BoltLights, value), true);
var sound = value ? ent.Comp.BoltDownSound : ent.Comp.BoltUpSound;
if (predicted)
Audio.PlayPredicted(sound, ent, user: user);

View File

@@ -1,6 +1,7 @@
using System.Linq;
using Content.Shared._White.Cult.Structures;
using Content.Shared._White.Keyhole.Components;
using Content.Shared._White.Lighting;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
@@ -116,6 +117,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
|| door.State == DoorState.Opening && !door.Partial;
SetCollidable(ent, collidable, door);
RaiseLocalEvent(ent, new DoorlightsChangedEvent(door.State, true), true);
AppearanceSystem.SetData(ent, DoorVisuals.State, door.State);
}
@@ -164,6 +167,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
_activeDoors.Add(ent);
RaiseLocalEvent(ent, new DoorStateChangedEvent(door.State));
RaiseLocalEvent(ent, new DoorlightsChangedEvent(door.State, true), true);
AppearanceSystem.SetData(ent, DoorVisuals.State, door.State);
}
@@ -212,7 +217,10 @@ public abstract partial class SharedDoorSystem : EntitySystem
door.State = state;
Dirty(uid, door);
RaiseLocalEvent(uid, new DoorStateChangedEvent(state));
RaiseLocalEvent(uid, new DoorlightsChangedEvent(door.State, true), true);
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
return true;
}
@@ -516,6 +524,8 @@ public abstract partial class SharedDoorSystem : EntitySystem
{
door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo;
door.State = DoorState.Opening;
RaiseLocalEvent(uid, new DoorlightsChangedEvent(door.State, true), true);
AppearanceSystem.SetData(uid, DoorVisuals.State, DoorState.Opening);
return false;
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.DoAfter;
using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -20,6 +20,12 @@ public sealed partial class HandTeleporterComponent : Component
[ViewVariables, DataField("secondPortal")]
public EntityUid? SecondPortal = null;
/// <summary>
/// Portals can't be placed on different grids?
/// </summary>
[DataField]
public bool AllowPortalsOnDifferentGrids;
[DataField("firstPortalPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string FirstPortalPrototype = "PortalRed";

View File

@@ -1,4 +1,5 @@
using System.Linq;
using Content.Shared._White.Wizard.Timestop;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Gravity;
@@ -25,6 +26,7 @@ namespace Content.Shared.Throwing
[Dependency] private readonly SharedGravitySystem _gravity = default!;
private const string ThrowingFixture = "throw-fixture";
private readonly HashSet<(EntityUid, EntityUid)> _processed = new (); // WD edit
public override void Initialize()
{
@@ -65,7 +67,14 @@ namespace Content.Shared.Throwing
if (args.OtherEntity == component.Thrower)
return;
// WD edit start
var collisionPair = (uid, args.OtherEntity);
if (_processed.Contains(collisionPair))
return;
// WD edit end
ThrowCollideInteraction(component, args.OurEntity, args.OtherEntity);
_processed.Add(collisionPair);
}
private void PreventCollision(EntityUid uid, ThrownItemComponent component, ref PreventCollideEvent args)
@@ -78,6 +87,8 @@ namespace Content.Shared.Throwing
private void OnSleep(EntityUid uid, ThrownItemComponent thrownItem, ref PhysicsSleepEvent @event)
{
if (HasComp<FrozenComponent>(uid)) // WD
return;
StopThrow(uid, thrownItem);
}
@@ -110,6 +121,7 @@ namespace Content.Shared.Throwing
EntityManager.EventBus.RaiseLocalEvent(uid, new StopThrowEvent { User = thrownItemComponent.Thrower }, true);
EntityManager.RemoveComponent<ThrownItemComponent>(uid);
_processed.Clear(); // WD edit
}
public void LandComponent(EntityUid uid, ThrownItemComponent thrownItem, PhysicsComponent physics, bool playSound)

View File

@@ -0,0 +1,13 @@
namespace Content.Shared._White.Lighting;
public sealed class DoorlightsChangedEvent : EntityEventArgs
{
public Enum? State;
public bool Value;
public DoorlightsChangedEvent(Enum? key, bool value)
{
State = key;
Value = value;
}
}

View File

@@ -0,0 +1,19 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Lighting.PointLight.Airlock;
[RegisterComponent, NetworkedComponent]
public sealed partial class PointLightAirlockComponent : Component
{
[ViewVariables]
public string RedColor = "#D56C6C";
[ViewVariables]
public string BlueColor = "#7F93C0";
[ViewVariables]
public string YellowColor = "#BDC07F";
[ViewVariables]
public string GreenColor = "#7FC080";
}

View File

@@ -0,0 +1,83 @@
using Content.Shared.Doors.Components;
namespace Content.Shared._White.Lighting.PointLight.Airlock;
//TODO: Когда-нибудь починить эту хуйню: Когда дверь открыта на аварийный доступ и ее болтируют, то свет будет желтым, хотя должен быть красным из-за болтов.
public sealed class SharedPointLightAirlockSystem : EntitySystem
{
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PointLightAirlockComponent, DoorlightsChangedEvent>(OnDoorLightChanged);
}
public void ToggleLight(EntityUid uid, string hex, bool enable = true)
{
if (!_pointLightSystem.TryGetLight(uid, out var pointLightComponent))
return;
if (enable)
{
var color = Color.FromHex(hex);
_pointLightSystem.SetColor(uid, color, pointLightComponent);
}
_pointLightSystem.SetEnabled(uid, enable, pointLightComponent);
RaiseLocalEvent(uid, new PointLightToggleEvent(enable), true);
}
public void OnDoorLightChanged(EntityUid uid, PointLightAirlockComponent component, DoorlightsChangedEvent args)
{
if (!TryComp<DoorComponent>(uid, out var door))
return;
if (TryComp<AirlockComponent>(uid, out var airlockComponent) && airlockComponent.EmergencyAccess && args.Value && args.State is not DoorVisuals.EmergencyLights && args.State != null)
return; // While emergency access lights must be yellow no matter what
switch (args.State)
{
case DoorVisuals.BoltLights:
if (args.Value)
ToggleLight(uid, component.RedColor);
else
RaiseLocalEvent(uid, new DoorlightsChangedEvent(door.State, true));
break;
case DoorState.Denying:
ToggleLight(uid, component.RedColor);
break;
case DoorState.Closed:
ToggleLight(uid, component.BlueColor);
break;
case DoorVisuals.EmergencyLights:
if (args.Value)
ToggleLight(uid, component.YellowColor);
else
RaiseLocalEvent(uid, new DoorlightsChangedEvent(door.State, true));
break;
case DoorState.Open:
ToggleLight(uid, component.BlueColor);
break;
case DoorState.Opening:
ToggleLight(uid, component.GreenColor);
break;
case DoorState.Closing:
ToggleLight(uid, component.GreenColor);
break;
default:
ToggleLight(uid, "", false);
break;
}
}
}

View File

@@ -0,0 +1,19 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Lighting.PointLight.Locker;
[RegisterComponent, NetworkedComponent]
public sealed partial class PointLightLockerComponent : Component
{
[DataField, ViewVariables]
public string RedColor = "#D56C6C";
[DataField, ViewVariables]
public string GreenColor = "#7FC080";
[DataField, ViewVariables]
public float ReduceEnergyOnOpen = 0.1f;
[DataField, ViewVariables]
public float ReduceRadiusOnOpen = 0.1f;
}

View File

@@ -0,0 +1,70 @@
using Content.Shared.Lock;
using Content.Shared.Storage.Components;
namespace Content.Shared._White.Lighting.PointLight.Locker;
public sealed class PointLightLockerSystem : EntitySystem
{
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PointLightLockerComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<PointLightLockerComponent, LockToggledEvent>(OnLockToggled);
SubscribeLocalEvent<PointLightLockerComponent, StorageAfterOpenEvent>(OnStorageAfterOpen);
SubscribeLocalEvent<PointLightLockerComponent, StorageAfterCloseEvent>(OnStorageAfterClose);
}
public void ToggleLight(EntityUid uid, string hex, bool enable = true)
{
if (!_pointLightSystem.TryGetLight(uid, out var pointLightComponent))
return;
if (enable)
{
var color = Color.FromHex(hex);
_pointLightSystem.SetColor(uid, color, pointLightComponent);
}
_pointLightSystem.SetEnabled(uid, enable, pointLightComponent);
RaiseLocalEvent(uid, new PointLightToggleEvent(enable), true);
}
public void OnComponentInit(EntityUid uid, PointLightLockerComponent component, ComponentInit args)
{
if (!TryComp<LockComponent>(uid, out var locker))
return;
ToggleLight(uid, locker.Locked ? component.RedColor : component.GreenColor, true);
}
public void OnLockToggled(EntityUid uid, PointLightLockerComponent component, LockToggledEvent args)
{
ToggleLight(uid, args.Locked ? component.RedColor : component.GreenColor, true);
}
public void OnStorageAfterOpen(EntityUid uid, PointLightLockerComponent component, StorageAfterOpenEvent args)
{
ChangeLightOnDoorToggled(uid, component, true);
}
public void OnStorageAfterClose(EntityUid uid, PointLightLockerComponent component, StorageAfterCloseEvent args)
{
ChangeLightOnDoorToggled(uid, component, false);
}
public void ChangeLightOnDoorToggled(EntityUid uid, PointLightLockerComponent component, bool status)
{
if (!_pointLightSystem.TryGetLight(uid, out var pointLightComponent))
return;
var factor = status ? 1f : -1f;
_pointLightSystem.SetEnergy(uid, pointLightComponent.Energy - component.ReduceEnergyOnOpen * factor);
_pointLightSystem.SetRadius(uid, pointLightComponent.Radius- component.ReduceRadiusOnOpen * factor);
RaiseLocalEvent(uid, new PointLightToggleEvent(true), true);
}
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.Serialization;
namespace Content.Shared._White.NiceIdCards;
[Serializable, NetSerializable]
public enum IdVisuals : byte
{
State
}

View File

@@ -0,0 +1,8 @@
namespace Content.Shared._White.WeaponModules;
[RegisterComponent]
public sealed partial class AimModuleComponent : BaseModuleComponent
{
[ViewVariables(VVAccess.ReadWrite), DataField("divisor")]
public float Divisor = 0.3F;
}

View File

@@ -2,7 +2,6 @@
namespace Content.Shared._White.WeaponModules;
[RegisterComponent, NetworkedComponent]
public partial class BaseModuleComponent : Component
{

View File

@@ -1,8 +1,5 @@
namespace Content.Shared._White.WeaponModules;
/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class FlameHiderModuleComponent : BaseModuleComponent
{

View File

@@ -20,7 +20,8 @@ public partial class WeaponModulesComponent : Component
public enum ModuleVisualState : byte
{
BarrelModule,
HandGuardModule
HandGuardModule,
AimModule
}
[Serializable, NetSerializable]

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Wizard.Timestop;
[RegisterComponent, NetworkedComponent]
public sealed partial class FreezeContactsComponent : Component
{
}

View File

@@ -0,0 +1,183 @@
using System.Linq;
using System.Numerics;
using Content.Shared.ActionBlocker;
using Content.Shared.Emoting;
using Content.Shared.Hands;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Events;
using Content.Shared.Speech;
using Content.Shared.Throwing;
using Robust.Shared.Containers;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Spawners;
namespace Content.Shared._White.Wizard.Timestop;
public sealed class FreezeContactsSystem : EntitySystem
{
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FreezeContactsComponent, StartCollideEvent>(OnEntityEnter);
SubscribeLocalEvent<FreezeContactsComponent, EndCollideEvent>(OnEntityExit);
SubscribeLocalEvent<FrozenComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<FrozenComponent, ComponentRemove>(OnRemove);
SubscribeLocalEvent<FrozenComponent, PreventCollideEvent>(OnPreventCollide);
SubscribeLocalEvent<FrozenComponent, EntGotInsertedIntoContainerMessage>(OnGetInserted);
SubscribeLocalEvent<FrozenComponent, SpeakAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, EmoteAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, ChangeDirectionAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, UpdateCanMoveEvent>(OnMoveAttempt);
SubscribeLocalEvent<FrozenComponent, InteractionAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, UseAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, ThrowAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, DropAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, AttackAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, PickupAttemptEvent>(OnAttempt);
SubscribeLocalEvent<FrozenComponent, IsEquippingAttemptEvent>(OnEquipAttempt);
SubscribeLocalEvent<FrozenComponent, IsUnequippingAttemptEvent>(OnUnequipAttempt);
}
private void OnMoveAttempt(EntityUid uid, FrozenComponent component, UpdateCanMoveEvent args)
{
if (component.LifeStage > ComponentLifeStage.Running)
return;
args.Cancel();
}
private void OnAttempt(EntityUid uid, FrozenComponent component, CancellableEntityEventArgs args)
{
args.Cancel();
}
private void OnEquipAttempt(EntityUid uid, FrozenComponent component, IsEquippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
if (args.Equipee == uid)
args.Cancel();
}
private void OnUnequipAttempt(EntityUid uid, FrozenComponent component, IsUnequippingAttemptEvent args)
{
// is this a self-equip, or are they being stripped?
if (args.Unequipee == uid)
args.Cancel();
}
private void OnGetInserted(Entity<FrozenComponent> ent, ref EntGotInsertedIntoContainerMessage args)
{
RemCompDeferred<FrozenComponent>(ent);
}
private void OnPreventCollide(Entity<FrozenComponent> ent, ref PreventCollideEvent args)
{
if (args.OurBody.BodyType == BodyType.Dynamic && !HasComp<FreezeContactsComponent>(args.OtherEntity))
args.Cancelled = true;
}
private void OnRemove(Entity<FrozenComponent> ent, ref ComponentRemove args)
{
var (uid, comp) = ent;
_blocker.UpdateCanMove(uid);
if (_container.IsEntityOrParentInContainer(uid))
return;
if (!TryComp(uid, out PhysicsComponent? physics))
return;
_physics.SetLinearVelocity(uid, comp.OldLinearVelocity, false, body: physics);
_physics.SetAngularVelocity(uid, comp.OldAngularVelocity, body: physics);
}
private void OnInit(Entity<FrozenComponent> ent, ref ComponentInit args)
{
var (uid, comp) = ent;
_blocker.UpdateCanMove(uid);
if (!TryComp(uid, out PhysicsComponent? physics))
return;
comp.OldLinearVelocity = physics.LinearVelocity;
comp.OldAngularVelocity = physics.AngularVelocity;
_physics.SetLinearVelocity(uid, Vector2.Zero, false, body: physics);
_physics.SetAngularVelocity(uid, 0f, body: physics);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = AllEntityQuery<FrozenComponent, FixturesComponent, PhysicsComponent>();
while (query.MoveNext(out var uid, out var frozen, out var fixtures, out var physics))
{
frozen.Lifetime -= frameTime;
if (physics.LinearVelocity != Vector2.Zero)
_physics.SetLinearVelocity(uid, Vector2.Zero, manager: fixtures, body: physics);
if (physics.AngularVelocity != 0f)
_physics.SetAngularVelocity(uid, 0f, manager: fixtures, body: physics);
if (frozen.Lifetime > 0)
continue;
RemCompDeferred<FrozenComponent>(uid);
}
}
private void OnEntityExit(Entity<FreezeContactsComponent> ent, ref EndCollideEvent args)
{
if (IsTouchingFrozenContacts(args.OtherEntity, args.OtherBody))
return;
RemCompDeferred<FrozenComponent>(args.OtherEntity);
}
private void OnEntityEnter(Entity<FreezeContactsComponent> ent, ref StartCollideEvent args)
{
var hadFrozen = HasComp<FrozenComponent>(args.OtherEntity);
var frozen = EnsureComp<FrozenComponent>(args.OtherEntity);
if (!TryComp(ent, out TimedDespawnComponent? timedDespawn))
return;
frozen.Lifetime = timedDespawn.Lifetime;
if (TryComp(args.OtherEntity, out TimedDespawnComponent? otherTimedDespawn))
otherTimedDespawn.Lifetime += timedDespawn.Lifetime;
if (hadFrozen)
return;
if (!TryComp(args.OtherEntity, out ThrownItemComponent? thrownItem))
return;
if (thrownItem.LandTime != null)
thrownItem.LandTime = thrownItem.LandTime.Value + TimeSpan.FromSeconds(timedDespawn.Lifetime);
if (thrownItem.ThrownTime != null)
thrownItem.ThrownTime = thrownItem.ThrownTime.Value + TimeSpan.FromSeconds(timedDespawn.Lifetime);
}
private bool IsTouchingFrozenContacts(EntityUid uid, PhysicsComponent body)
{
return _physics.GetContactingEntities(uid, body).Any(HasComp<FreezeContactsComponent>);
}
}

View File

@@ -0,0 +1,17 @@
using System.Numerics;
using Robust.Shared.GameStates;
namespace Content.Shared._White.Wizard.Timestop;
[RegisterComponent, NetworkedComponent]
public sealed partial class FrozenComponent : Component
{
[ViewVariables]
public float Lifetime = 10f;
[ViewVariables]
public Vector2 OldLinearVelocity;
[ViewVariables]
public float OldAngularVelocity;
}

View File

@@ -184,4 +184,13 @@ public sealed partial class MindswapSpellEvent : EntityTargetActionEvent, ISpeak
public string? Speech { get; private set; }
}
public sealed partial class StopTimeSpellEvent : InstantActionEvent, ISpeakSpell
{
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Prototype = default!;
[DataField("speech")]
public string? Speech { get; private set; }
}
#endregion