diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index a2faef0dd4..23e8ccab82 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -102,7 +102,7 @@ public sealed class MaterialArbitrageTest continue; var stackProto = protoManager.Index(materialStep.MaterialPrototypeId); - var spawnProto = protoManager.Index(stackProto.Spawn); + var spawnProto = protoManager.Index(stackProto.Spawn); if (!spawnProto.Components.ContainsKey(materialName) || !spawnProto.Components.TryGetValue(compositionName, out var compositionReg)) diff --git a/Content.Server/Construction/MachineFrameSystem.cs b/Content.Server/Construction/MachineFrameSystem.cs index 09d8d413ec..e20c36d849 100644 --- a/Content.Server/Construction/MachineFrameSystem.cs +++ b/Content.Server/Construction/MachineFrameSystem.cs @@ -59,23 +59,27 @@ public sealed class MachineFrameSystem : EntitySystem return; } - // Machine parts cannot currently satisfy stack/component/tag restrictions. Similarly stacks cannot satisfy - // component/tag restrictions. However, there is no reason this cannot be supported in the future. If this - // changes, then RegenerateProgress() also needs to be updated. - // + // If this changes in the future, then RegenerateProgress() also needs to be updated. // Note that one entity is ALLOWED to satisfy more than one kind of component or tag requirements. This is // necessary in order to avoid weird entity-ordering shenanigans in RegenerateProgress(). + var stack = CompOrNull(args.Used); + var machinePart = CompOrNull(args.Used); + if (stack != null && machinePart != null) + { + if (TryInsertPartStack(uid, args.Used, component, machinePart, stack)) + args.Handled = true; + return; + } // Handle parts - if (TryComp(args.Used, out var machinePart)) + if (machinePart != null) { if (TryInsertPart(uid, args.Used, component, machinePart)) args.Handled = true; return; } - // Handle stacks - if (TryComp(args.Used, out var stack)) + if (stack != null) { if (TryInsertStack(uid, args.Used, component, stack)) args.Handled = true; @@ -191,6 +195,44 @@ public sealed class MachineFrameSystem : EntitySystem return true; } + /// Whether or not the function had any effect. Does not indicate success. + private bool TryInsertPartStack(EntityUid uid, EntityUid used, MachineFrameComponent component, MachinePartComponent machinePart, StackComponent stack) + { + if (!component.Requirements.ContainsKey(machinePart.PartType)) + return false; + + var progress = component.Progress[machinePart.PartType]; + var requirement = component.Requirements[machinePart.PartType]; + + var needed = requirement - progress; + if (needed <= 0) + return false; + + var count = stack.Count; + if (count < needed) + { + if (!_container.Insert(used, component.PartContainer)) + return true; + + component.Progress[machinePart.PartType] += count; + return true; + } + + var splitStack = _stack.Split(used, needed, Transform(uid).Coordinates, stack); + + if (splitStack == null) + return false; + + if (!_container.Insert(splitStack.Value, component.PartContainer)) + return true; + + component.Progress[machinePart.PartType] += needed; + if (IsComplete(component)) + _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid); + + return true; + } + /// Whether or not the function had any effect. Does not indicate success. private bool TryInsertStack(EntityUid uid, EntityUid used, MachineFrameComponent component, StackComponent stack) { @@ -328,8 +370,6 @@ public sealed class MachineFrameSystem : EntitySystem { if (TryComp(part, out var machinePart)) { - DebugTools.Assert(!HasComp(part)); - // Check this is part of the requirements... if (!component.Requirements.ContainsKey(machinePart.PartType)) continue; @@ -338,7 +378,6 @@ public sealed class MachineFrameSystem : EntitySystem component.Progress[machinePart.PartType] = 1; else component.Progress[machinePart.PartType]++; - continue; } diff --git a/Content.Server/Destructible/DestructibleSystem.cs b/Content.Server/Destructible/DestructibleSystem.cs index 16c54fd3b0..e0183a037d 100644 --- a/Content.Server/Destructible/DestructibleSystem.cs +++ b/Content.Server/Destructible/DestructibleSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Administration.Logs; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Systems; @@ -90,6 +91,16 @@ namespace Content.Server.Destructible } } + public bool TryGetDestroyedAt(Entity ent, [NotNullWhen(true)] out FixedPoint2? destroyedAt) + { + destroyedAt = null; + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + destroyedAt = DestroyedAt(ent, ent.Comp); + return true; + } + // FFS this shouldn't be this hard. Maybe this should just be a field of the destructible component. Its not // like there is currently any entity that is NOT just destroyed upon reaching a total-damage value. /// diff --git a/Content.Server/Destructible/Thresholds/MinMax.cs b/Content.Server/Destructible/Thresholds/MinMax.cs index b438e7c0e8..c3433aff3e 100644 --- a/Content.Server/Destructible/Thresholds/MinMax.cs +++ b/Content.Server/Destructible/Thresholds/MinMax.cs @@ -1,13 +1,25 @@ -namespace Content.Server.Destructible.Thresholds +using Robust.Shared.Random; + +namespace Content.Server.Destructible.Thresholds { - [Serializable] - [DataDefinition] + [DataDefinition, Serializable] public partial struct MinMax { - [DataField("min")] + [DataField] public int Min; - [DataField("max")] + [DataField] public int Max; + + public MinMax(int min, int max) + { + Min = min; + Max = max; + } + + public int Next(IRobustRandom random) + { + return random.Next(Min, Max + 1); + } } } diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs index 3d4c8a4ec5..93ee18f683 100644 --- a/Content.Server/Doors/Systems/FirelockSystem.cs +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -1,71 +1,60 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Monitor.Systems; -using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; -using Content.Shared.Access.Systems; using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; -using Content.Shared.Doors; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; -using Content.Shared.Popups; -using Content.Shared.Prying.Components; +using Robust.Server.GameObjects; using Robust.Shared.Map.Components; namespace Content.Server.Doors.Systems { - public sealed class FirelockSystem : EntitySystem + public sealed class FirelockSystem : SharedFirelockSystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedDoorSystem _doorSystem = default!; [Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly SharedMapSystem _mapping = default!; + [Dependency] private readonly PointLightSystem _pointLight = default!; - private static float _visualUpdateInterval = 0.5f; - private float _accumulatedFrameTime; + private const int UpdateInterval = 30; + private int _accumulatedTicks; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnBeforeDoorOpened); - SubscribeLocalEvent(OnDoorGetPryTimeModifier); - SubscribeLocalEvent(OnUpdateState); - - SubscribeLocalEvent(OnBeforeDoorAutoclose); SubscribeLocalEvent(OnAtmosAlarm); - // Visuals - SubscribeLocalEvent(UpdateVisuals); - SubscribeLocalEvent(UpdateVisuals); SubscribeLocalEvent(PowerChanged); + } private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args) { // TODO this should REALLLLY not be door specific appearance thing. _appearance.SetData(uid, DoorVisuals.Powered, args.Powered); + component.Powered = args.Powered; + Dirty(uid, component); } - #region Visuals - private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component); - public override void Update(float frameTime) { - _accumulatedFrameTime += frameTime; - if (_accumulatedFrameTime < _visualUpdateInterval) + _accumulatedTicks += 1; + if (_accumulatedTicks < UpdateInterval) return; - _accumulatedFrameTime -= _visualUpdateInterval; + _accumulatedTicks = 0; var airtightQuery = GetEntityQuery(); var appearanceQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); + var pointLightQuery = GetEntityQuery(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var firelock, out var door)) @@ -82,109 +71,20 @@ namespace Content.Server.Doors.Systems && xformQuery.TryGetComponent(uid, out var xform) && appearanceQuery.TryGetComponent(uid, out var appearance)) { - var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery); + var (pressure, fire) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery); _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance); + firelock.Temperature = fire; + firelock.Pressure = pressure; + Dirty(uid, firelock); + + if (pointLightQuery.TryComp(uid, out var pointLight)) + { + _pointLight.SetEnabled(uid, fire | pressure, pointLight); + } } } } - private void UpdateVisuals(EntityUid uid, - FirelockComponent? firelock = null, - DoorComponent? door = null, - AirtightComponent? airtight = null, - AppearanceComponent? appearance = null, - TransformComponent? xform = null) - { - if (!Resolve(uid, ref door, ref appearance, false)) - return; - - // only bother to check pressure on doors that are some variation of closed. - if (door.State != DoorState.Closed - && door.State != DoorState.Welded - && door.State != DoorState.Denying) - { - _appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance); - return; - } - - var query = GetEntityQuery(); - if (!Resolve(uid, ref firelock, ref airtight, ref appearance, ref xform, false) || !query.Resolve(uid, ref airtight, false)) - return; - - var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, query); - _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance); - } - #endregion - - public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null) - { - if (!Resolve(uid, ref firelock, ref door)) - return false; - - if (door.State == DoorState.Open) - { - if (_doorSystem.TryClose(uid, door)) - { - return _doorSystem.OnPartialClose(uid, door); - } - } - return false; - } - - private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) - { - // Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas - var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid); - - if (!this.IsPowered(uid, EntityManager) || (!overrideAccess && IsHoldingPressureOrFire(uid, component))) - args.Cancel(); - } - - private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args) - { - var state = CheckPressureAndFire(uid, component); - - if (state.Fire) - { - _popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-fire-message"), - uid, args.User, PopupType.MediumCaution); - } - else if (state.Pressure) - { - _popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-pressure-message"), - uid, args.User, PopupType.MediumCaution); - } - - if (state.Fire || state.Pressure) - args.PryTimeModifier *= component.LockedPryTimeModifier; - } - - private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args) - { - var ev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, ev); - UpdateVisuals(uid, component, args); - if (ev.Cancelled) - { - return; - } - - _doorSystem.SetNextStateChange(uid, component.AutocloseDelay); - } - - private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args) - { - if (!this.IsPowered(uid, EntityManager)) - args.Cancel(); - - // Make firelocks autoclose, but only if the last alarm type it - // remembers was a danger. This is to prevent people from - // flooding hallways with endless bad air/fire. - if (component.AlarmAutoClose && - (_atmosAlarmable.TryGetHighestAlert(uid, out var alarm) && alarm != AtmosAlarmType.Danger || alarm == null)) - args.Cancel(); - } - private void OnAtmosAlarm(EntityUid uid, FirelockComponent component, AtmosAlarmEvent args) { if (!this.IsPowered(uid, EntityManager)) @@ -193,7 +93,7 @@ namespace Content.Server.Doors.Systems if (!TryComp(uid, out var doorComponent)) return; - if (args.AlarmType == AtmosAlarmType.Normal || args.AlarmType == AtmosAlarmType.Warning) + if (args.AlarmType == AtmosAlarmType.Normal) { if (doorComponent.State == DoorState.Closed) _doorSystem.TryOpen(uid); @@ -204,12 +104,6 @@ namespace Content.Server.Doors.Systems } } - public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock) - { - var result = CheckPressureAndFire(uid, firelock); - return result.Pressure || result.Fire; - } - public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock) { var query = GetEntityQuery(); @@ -234,17 +128,17 @@ namespace Content.Server.Doors.Systems return (false, false); } - if (!TryComp(xform.ParentUid, out GridAtmosphereComponent? gridAtmosphere)) + if (!HasComp(xform.ParentUid)) return (false, false); var grid = Comp(xform.ParentUid); - var pos = grid.CoordinatesToTile(xform.Coordinates); + var pos = _mapping.CoordinatesToTile(xform.ParentUid, grid, xform.Coordinates); var minPressure = float.MaxValue; var maxPressure = float.MinValue; var minTemperature = float.MaxValue; var maxTemperature = float.MinValue; - bool holdingFire = false; - bool holdingPressure = false; + var holdingFire = false; + var holdingPressure = false; // We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option // works, we want to ignore the firelock's blocking, while including blockers on other tiles. @@ -284,7 +178,7 @@ namespace Content.Server.Doors.Systems { // Is there some airtight entity blocking this direction? If yes, don't include this direction in the // pressure differential - if (HasAirtightBlocker(grid.GetAnchoredEntities(adjacentPos), dir.GetOpposite(), airtightQuery)) + if (HasAirtightBlocker(_mapping.GetAnchoredEntities(xform.ParentUid, grid, adjacentPos), dir.GetOpposite(), airtightQuery)) continue; var p = gas.Pressure; diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 839fcae153..437a567b86 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -141,4 +141,9 @@ public abstract partial class GameRuleSystem where T: IComponent return found; } + protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) + { + GameTicker.EndGameRule(uid, component); + } + } diff --git a/Content.Server/Mining/MeteorComponent.cs b/Content.Server/Mining/MeteorComponent.cs new file mode 100644 index 0000000000..059c450a8a --- /dev/null +++ b/Content.Server/Mining/MeteorComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared.Damage; + +namespace Content.Server.Mining; + +/// +/// This is used for meteors which hit objects, dealing damage to destroy/kill the object and dealing equal damage back to itself. +/// +[RegisterComponent, Access(typeof(MeteorSystem))] +public sealed partial class MeteorComponent : Component +{ + /// + /// Damage specifier that is multiplied against the calculated damage amount to determine what damage is applied to the colliding entity. + /// + /// + /// The values of this should add up to 1 or else the damage will be scaled. + /// + [DataField] + public DamageSpecifier DamageTypes = new(); + + /// + /// A list of entities that this meteor has collided with. used to ensure no double collisions occur. + /// + [DataField] + public HashSet HitList = new(); +} diff --git a/Content.Server/Mining/MeteorSystem.cs b/Content.Server/Mining/MeteorSystem.cs new file mode 100644 index 0000000000..fc00147f70 --- /dev/null +++ b/Content.Server/Mining/MeteorSystem.cs @@ -0,0 +1,65 @@ +using Content.Server.Administration.Logs; +using Content.Server.Destructible; +using Content.Shared.Damage; +using Content.Shared.Database; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs.Systems; +using Robust.Shared.Physics.Events; +using Robust.Shared.Player; + +namespace Content.Server.Mining; + +public sealed class MeteorSystem : EntitySystem +{ + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly DestructibleSystem _destructible = default!; + [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnCollide); + } + + private void OnCollide(EntityUid uid, MeteorComponent component, ref StartCollideEvent args) + { + if (TerminatingOrDeleted(args.OtherEntity) || TerminatingOrDeleted(uid)) + return; + + if (component.HitList.Contains(args.OtherEntity)) + return; + + FixedPoint2 threshold; + if (_mobThreshold.TryGetDeadThreshold(args.OtherEntity, out var mobThreshold)) + { + threshold = mobThreshold.Value; + if (HasComp(args.OtherEntity)) + _adminLog.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(args.OtherEntity):player} was struck by meteor {ToPrettyString(uid):ent} and killed instantly."); + } + else if (_destructible.TryGetDestroyedAt(args.OtherEntity, out var destroyThreshold)) + { + threshold = destroyThreshold.Value; + } + else + { + threshold = FixedPoint2.MaxValue; + } + var otherEntDamage = CompOrNull(args.OtherEntity)?.TotalDamage ?? FixedPoint2.Zero; + // account for the damage that the other entity has already taken: don't overkill + threshold -= otherEntDamage; + + // The max amount of damage our meteor can take before breaking. + var maxMeteorDamage = _destructible.DestroyedAt(uid) - CompOrNull(uid)?.TotalDamage ?? FixedPoint2.Zero; + + // Cap damage so we don't overkill the meteor + var trueDamage = FixedPoint2.Min(maxMeteorDamage, threshold); + + var damage = component.DamageTypes * trueDamage; + _damageable.TryChangeDamage(args.OtherEntity, damage, true, origin: uid); + _damageable.TryChangeDamage(uid, damage); + + if (!TerminatingOrDeleted(args.OtherEntity)) + component.HitList.Add(args.OtherEntity); + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/InContainerPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/InContainerPrecondition.cs new file mode 100644 index 0000000000..aa0ad98ede --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/InContainerPrecondition.cs @@ -0,0 +1,27 @@ +using Robust.Server.Containers; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the owner in container or not +/// +public sealed partial class InContainerPrecondition : HTNPrecondition +{ + private ContainerSystem _container = default!; + + [ViewVariables(VVAccess.ReadWrite)] [DataField("isInContainer")] public bool IsInContainer = true; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _container = sysManager.GetEntitySystem(); + } + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + return IsInContainer && _container.IsEntityInContainer(owner) || + !IsInContainer && !_container.IsEntityInContainer(owner); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/ContainerOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/ContainerOperator.cs new file mode 100644 index 0000000000..667d0b8ec4 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/ContainerOperator.cs @@ -0,0 +1,40 @@ +using Robust.Server.Containers; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +public sealed partial class ContainerOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private ContainerSystem _container = default!; + private EntityQuery _transformQuery; + + [DataField("shutdownState")] + public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.TaskFinished; + + [DataField("targetKey", required: true)] + public string TargetKey = default!; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _container = sysManager.GetEntitySystem(); + _transformQuery = _entManager.GetEntityQuery(); + } + + public override void Startup(NPCBlackboard blackboard) + { + base.Startup(blackboard); + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!_container.TryGetOuterContainer(owner, _transformQuery.GetComponent(owner), out var outerContainer) && outerContainer == null) + return; + + var target = outerContainer.Owner; + blackboard.SetValue(TargetKey, target); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + return HTNOperatorStatus.Finished; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/EscapeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/EscapeOperator.cs new file mode 100644 index 0000000000..a794e1e314 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/EscapeOperator.cs @@ -0,0 +1,140 @@ +using System.Threading; +using System.Threading.Tasks; +using Content.Server.NPC.Components; +using Content.Server.Storage.EntitySystems; +using Content.Shared.CombatMode; +using Robust.Server.Containers; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat.Melee; + +public sealed partial class EscapeOperator : HTNOperator, IHtnConditionalShutdown +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private ContainerSystem _container = default!; + private EntityStorageSystem _entityStorage = default!; + + [DataField("shutdownState")] + public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.TaskFinished; + + [DataField("targetKey", required: true)] + public string TargetKey = default!; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _container = sysManager.GetEntitySystem(); + _entityStorage = sysManager.GetEntitySystem(); + } + + public override void Startup(NPCBlackboard blackboard) + { + base.Startup(blackboard); + var owner = blackboard.GetValue(NPCBlackboard.Owner); + var target = blackboard.GetValue(TargetKey); + + if (_entityStorage.TryOpenStorage(owner, target)) + { + TaskShutdown(blackboard, HTNOperatorStatus.Finished); + return; + } + + var melee = _entManager.EnsureComponent(owner); + melee.MissChance = blackboard.GetValueOrDefault(NPCBlackboard.MeleeMissChance, _entManager); + melee.Target = target; + } + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) + { + return (false, null); + } + + if (!_container.IsEntityInContainer(owner)) + { + return (false, null); + } + + if (_entityStorage.TryOpenStorage(owner, target)) + { + return (false, null); + } + + return (true, null); + } + + public void ConditionalShutdown(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + _entManager.System().SetInCombatMode(owner, false); + _entManager.RemoveComponent(owner); + blackboard.Remove(TargetKey); + } + + public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status) + { + base.TaskShutdown(blackboard, status); + + ConditionalShutdown(blackboard); + } + + public override void PlanShutdown(NPCBlackboard blackboard) + { + base.PlanShutdown(blackboard); + + ConditionalShutdown(blackboard); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + base.Update(blackboard, frameTime); + var owner = blackboard.GetValue(NPCBlackboard.Owner); + HTNOperatorStatus status; + + if (_entManager.TryGetComponent(owner, out var combat) && + blackboard.TryGetValue(TargetKey, out var target, _entManager)) + { + combat.Target = target; + + // Success + if (!_container.IsEntityInContainer(owner)) + { + status = HTNOperatorStatus.Finished; + } + else + { + if (_entityStorage.TryOpenStorage(owner, target)) + { + status = HTNOperatorStatus.Finished; + } + else + { + switch (combat.Status) + { + case CombatStatus.TargetOutOfRange: + case CombatStatus.Normal: + status = HTNOperatorStatus.Continuing; + break; + default: + status = HTNOperatorStatus.Failed; + break; + } + } + } + } + else + { + status = HTNOperatorStatus.Failed; + } + + // Mark it as finished to continue the plan. + if (status == HTNOperatorStatus.Continuing && ShutdownState == HTNPlanState.PlanFinished) + { + status = HTNOperatorStatus.Finished; + } + + return status; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnPullOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnPullOperator.cs new file mode 100644 index 0000000000..54f422fe67 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnPullOperator.cs @@ -0,0 +1,35 @@ +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +public sealed partial class UnPullOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private PullingSystem _pulling = default!; + + private EntityQuery _pullableQuery; + + [DataField("shutdownState")] + public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.TaskFinished; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _pulling = sysManager.GetEntitySystem(); + _pullableQuery = _entManager.GetEntityQuery(); + } + + public override void Startup(NPCBlackboard blackboard) + { + base.Startup(blackboard); + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + _pulling.TryStopPull(owner, _pullableQuery.GetComponent(owner), owner); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + return HTNOperatorStatus.Finished; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs new file mode 100644 index 0000000000..207665d786 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs @@ -0,0 +1,34 @@ +using Content.Server.Buckle.Systems; +using Content.Shared.Buckle.Components; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +public sealed partial class UnbuckleOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private BuckleSystem _buckle = default!; + + [DataField("shutdownState")] + public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.TaskFinished; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _buckle = sysManager.GetEntitySystem(); + } + + public override void Startup(NPCBlackboard blackboard) + { + base.Startup(blackboard); + var owner = blackboard.GetValue(NPCBlackboard.Owner); + if (!_entManager.TryGetComponent(owner, out var buckle) || !buckle.Buckled) + return; + + _buckle.TryUnbuckle(owner, owner, true, buckle); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + return HTNOperatorStatus.Finished; + } +} diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index 001093a8dd..e34592b45f 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -51,7 +51,7 @@ namespace Content.Server.Stack // Get a prototype ID to spawn the new entity. Null is also valid, although it should rarely be picked... var prototype = _prototypeManager.TryIndex(stack.StackTypeId, out var stackType) - ? stackType.Spawn + ? stackType.Spawn.ToString() : Prototype(uid)?.ID; // Set the output parameter in the event instance to the newly split stack. diff --git a/Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs b/Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs new file mode 100644 index 0000000000..e71a284a89 --- /dev/null +++ b/Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs @@ -0,0 +1,35 @@ +using Content.Shared.Random; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents.Components; + +/// +/// This is used for running meteor swarm events at regular intervals. +/// +[RegisterComponent, Access(typeof(MeteorSchedulerSystem)), AutoGenerateComponentPause] +public sealed partial class MeteorSchedulerComponent : Component +{ + /// + /// The weights for which swarms will be selected. + /// + [DataField] + public ProtoId Config = "DefaultConfig"; + + /// + /// The time at which the next swarm occurs. + /// + [DataField, AutoPausedField] + public TimeSpan NextSwarmTime = TimeSpan.Zero; + + /// + /// The minimum time between swarms + /// + [DataField] + public TimeSpan MinSwarmDelay = TimeSpan.FromMinutes(7.5f); + + /// + /// The maximum time between swarms + /// + [DataField] + public TimeSpan MaxSwarmDelay = TimeSpan.FromMinutes(12.5f); +} diff --git a/Content.Server/StationEvents/Components/MeteorSwarmComponent.cs b/Content.Server/StationEvents/Components/MeteorSwarmComponent.cs new file mode 100644 index 0000000000..cca4465154 --- /dev/null +++ b/Content.Server/StationEvents/Components/MeteorSwarmComponent.cs @@ -0,0 +1,58 @@ +using Content.Server.Destructible.Thresholds; +using Content.Server.StationEvents.Events; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents.Components; + +[RegisterComponent, Access(typeof(MeteorSwarmSystem)), AutoGenerateComponentPause] +public sealed partial class MeteorSwarmComponent : Component +{ + [DataField, AutoPausedField] + public TimeSpan NextWaveTime; + + /// + /// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer. + /// + [DataField] + public int WaveCounter; + + [DataField] + public float MeteorVelocity = 10f; + + /// + /// If true, meteors will be thrown from all angles instead of from a singular source + /// + [DataField] + public bool NonDirectional; + + /// + /// The announcement played when a meteor swarm begins. + /// + [DataField] + public LocId? Announcement = "station-event-meteor-swarm-start-announcement"; + + [DataField] + public SoundSpecifier? AnnouncementSound = new SoundPathSpecifier("/Audio/Announcements/meteors.ogg") + { + Params = new() + { + Volume = -4 + } + }; + + /// + /// Each meteor entity prototype and their corresponding weight in being picked. + /// + [DataField] + public Dictionary Meteors = new(); + + [DataField] + public MinMax Waves = new(3, 3); + + [DataField] + public MinMax MeteorsPerWave = new(3, 4); + + [DataField] + public MinMax WaveCooldown = new (10, 60); +} diff --git a/Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs b/Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs deleted file mode 100644 index 3927f94319..0000000000 --- a/Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(MeteorSwarmRule))] -public sealed partial class MeteorSwarmRuleComponent : Component -{ - [DataField("cooldown")] - public float Cooldown; - - /// - /// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer. - /// - [DataField("waveCounter")] - public int WaveCounter; - - [DataField("minimumWaves")] - public int MinimumWaves = 3; - - [DataField("maximumWaves")] - public int MaximumWaves = 8; - - [DataField("minimumCooldown")] - public float MinimumCooldown = 10f; - - [DataField("maximumCooldown")] - public float MaximumCooldown = 60f; - - [DataField("meteorsPerWave")] - public int MeteorsPerWave = 5; - - [DataField("meteorVelocity")] - public float MeteorVelocity = 10f; - - [DataField("maxAngularVelocity")] - public float MaxAngularVelocity = 0.25f; - - [DataField("minAngularVelocity")] - public float MinAngularVelocity = -0.25f; -} diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs index ad56479b37..e69de29bb2 100644 --- a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs @@ -1,84 +0,0 @@ -using System.Numerics; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.StationEvents.Components; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Spawners; - -namespace Content.Server.StationEvents.Events -{ - public sealed class MeteorSwarmRule : StationEventSystem - { - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - - protected override void Started(EntityUid uid, MeteorSwarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - - component.WaveCounter = RobustRandom.Next(component.MinimumWaves, component.MaximumWaves); - } - - protected override void ActiveTick(EntityUid uid, MeteorSwarmRuleComponent component, GameRuleComponent gameRule, float frameTime) - { - if (component.WaveCounter <= 0) - { - ForceEndSelf(uid, gameRule); - return; - } - - component.Cooldown -= frameTime; - - if (component.Cooldown > 0f) - return; - - component.WaveCounter--; - - component.Cooldown += (component.MaximumCooldown - component.MinimumCooldown) * RobustRandom.NextFloat() + component.MinimumCooldown; - - Box2? playableArea = null; - var mapId = GameTicker.DefaultMap; - - var query = AllEntityQuery(); - while (query.MoveNext(out var gridId, out _, out var xform)) - { - if (xform.MapID != mapId) - continue; - - var aabb = _physics.GetWorldAABB(gridId); - playableArea = playableArea?.Union(aabb) ?? aabb; - } - - if (playableArea == null) - { - ForceEndSelf(uid, gameRule); - return; - } - - var minimumDistance = (playableArea.Value.TopRight - playableArea.Value.Center).Length() + 50f; - var maximumDistance = minimumDistance + 100f; - - var center = playableArea.Value.Center; - - for (var i = 0; i < component.MeteorsPerWave; i++) - { - var angle = new Angle(RobustRandom.NextFloat() * MathF.Tau); - var offset = angle.RotateVec(new Vector2((maximumDistance - minimumDistance) * RobustRandom.NextFloat() + minimumDistance, 0)); - var spawnPosition = new MapCoordinates(center + offset, mapId); - var meteor = Spawn("MeteorLarge", spawnPosition); - var physics = EntityManager.GetComponent(meteor); - _physics.SetBodyStatus(meteor, physics, BodyStatus.InAir); - _physics.SetLinearDamping(meteor, physics, 0f); - _physics.SetAngularDamping(meteor, physics, 0f); - _physics.ApplyLinearImpulse(meteor, -offset.Normalized() * component.MeteorVelocity * physics.Mass, body: physics); - _physics.ApplyAngularImpulse( - meteor, - physics.Mass * ((component.MaxAngularVelocity - component.MinAngularVelocity) * RobustRandom.NextFloat() + component.MinAngularVelocity), - body: physics); - - EnsureComp(meteor).Lifetime = 120f; - } - } - } -} diff --git a/Content.Server/StationEvents/Events/MeteorSwarmSystem.cs b/Content.Server/StationEvents/Events/MeteorSwarmSystem.cs new file mode 100644 index 0000000000..663bf9bdf8 --- /dev/null +++ b/Content.Server/StationEvents/Events/MeteorSwarmSystem.cs @@ -0,0 +1,88 @@ +using System.Numerics; +using Content.Server.Chat.Systems; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Station.Components; +using Content.Server.Station.Systems; +using Content.Server.StationEvents.Components; +using Robust.Server.Audio; +using Robust.Shared.Map; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.StationEvents.Events; + +public sealed class MeteorSwarmSystem : GameRuleSystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly StationSystem _station = default!; + + protected override void Added(EntityUid uid, MeteorSwarmComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + component.WaveCounter = component.Waves.Next(RobustRandom); + + if (component.Announcement is { } locId) + _chat.DispatchGlobalAnnouncement(Loc.GetString(locId), playSound: false, colorOverride: Color.Yellow); + _audio.PlayGlobal(component.AnnouncementSound, Filter.Broadcast(), true); + } + + protected override void ActiveTick(EntityUid uid, MeteorSwarmComponent component, GameRuleComponent gameRule, float frameTime) + { + if (Timing.CurTime < component.NextWaveTime) + return; + + component.NextWaveTime += TimeSpan.FromSeconds(component.WaveCooldown.Next(RobustRandom)); + + + if (_station.GetStations().Count == 0) + return; + + var station = RobustRandom.Pick(_station.GetStations()); + if (_station.GetLargestGrid(Comp(station)) is not { } grid) + return; + + var mapId = Transform(grid).MapID; + var playableArea = _physics.GetWorldAABB(grid); + + var minimumDistance = (playableArea.TopRight - playableArea.Center).Length() + 50f; + var maximumDistance = minimumDistance + 100f; + + var center = playableArea.Center; + + var meteorsToSpawn = component.MeteorsPerWave.Next(RobustRandom); + for (var i = 0; i < meteorsToSpawn; i++) + { + var spawnProto = RobustRandom.Pick(component.Meteors).Key; + + var angle = component.NonDirectional + ? RobustRandom.NextAngle() + : new Random(uid.Id).NextAngle(); + + var offset = angle.RotateVec(new Vector2((maximumDistance - minimumDistance) * RobustRandom.NextFloat() + minimumDistance, 0)); + + // the line at which spawns occur is perpendicular to the offset. + // This means the meteors are less likely to bunch up and hit the same thing. + var subOffsetAngle = RobustRandom.Prob(0.5f) + ? angle + Math.PI / 2 + : angle - Math.PI / 2; + var subOffset = subOffsetAngle.RotateVec(new Vector2( (playableArea.TopRight - playableArea.Center).Length() / 3 * RobustRandom.NextFloat(), 0)); + + var spawnPosition = new MapCoordinates(center + offset + subOffset, mapId); + var meteor = Spawn(spawnProto, spawnPosition); + var physics = Comp(meteor); + _physics.ApplyLinearImpulse(meteor, -offset.Normalized() * component.MeteorVelocity * physics.Mass, body: physics); + } + + component.WaveCounter--; + if (component.WaveCounter <= 0) + { + ForceEndSelf(uid, gameRule); + } + } +} diff --git a/Content.Server/StationEvents/MeteorSchedulerSystem.cs b/Content.Server/StationEvents/MeteorSchedulerSystem.cs new file mode 100644 index 0000000000..afeb417e5b --- /dev/null +++ b/Content.Server/StationEvents/MeteorSchedulerSystem.cs @@ -0,0 +1,39 @@ +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.StationEvents.Components; +using Content.Shared.Random.Helpers; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents; + +/// +/// This handles scheduling and launching meteors at a station at regular intervals. +/// TODO: there is 100% a world in which this is genericized and can be used for lots of basic event scheduling +/// +public sealed class MeteorSchedulerSystem : GameRuleSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + protected override void Started(EntityUid uid, MeteorSchedulerComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + component.NextSwarmTime = Timing.CurTime + RobustRandom.Next(component.MinSwarmDelay, component.MaxSwarmDelay); + } + + protected override void ActiveTick(EntityUid uid, MeteorSchedulerComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + if (Timing.CurTime < component.NextSwarmTime) + return; + RunSwarm((uid, component)); + component.NextSwarmTime += RobustRandom.Next(component.MinSwarmDelay, component.MaxSwarmDelay); + } + + private void RunSwarm(Entity ent) + { + var swarmWeights = _prototypeManager.Index(ent.Comp.Config); + GameTicker.StartGameRule(swarmWeights.Pick(RobustRandom)); + } +} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 6041e31826..c8660fa445 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -121,6 +121,18 @@ namespace Content.Shared.CCVar public static readonly CVarDef EventsRampingAverageChaos = CVarDef.Create("events.ramping_average_chaos", 6f, CVar.ARCHIVE | CVar.SERVERONLY); + /// + /// Minimum time between meteor swarms in minutes. + /// + public static readonly CVarDef + MeteorSwarmMinTime = CVarDef.Create("events.meteor_swarm_min_time", 12.5f, CVar.ARCHIVE | CVar.SERVERONLY); + + /// + /// Maximum time between meteor swarms in minutes. + /// + public static readonly CVarDef + MeteorSwarmMaxTime = CVarDef.Create("events.meteor_swarm_max_time", 17.5f, CVar.ARCHIVE | CVar.SERVERONLY); + /* * Game */ diff --git a/Content.Shared/Construction/MachinePartSystem.cs b/Content.Shared/Construction/MachinePartSystem.cs index 1a19040b41..359b58c881 100644 --- a/Content.Shared/Construction/MachinePartSystem.cs +++ b/Content.Shared/Construction/MachinePartSystem.cs @@ -87,9 +87,9 @@ namespace Content.Shared.Construction foreach (var (stackId, amount) in comp.MaterialIdRequirements) { var stackProto = _prototype.Index(stackId); + var defaultProto = _prototype.Index(stackProto.Spawn); - if (_prototype.TryIndex(stackProto.Spawn, out var defaultProto) && - defaultProto.TryGetComponent(out var physComp)) + if (defaultProto.TryGetComponent(out var physComp)) { foreach (var (mat, matAmount) in physComp.MaterialComposition) { diff --git a/Content.Shared/Doors/Components/FirelockComponent.cs b/Content.Shared/Doors/Components/FirelockComponent.cs index 97e57185ca..3f7d6c3f70 100644 --- a/Content.Shared/Doors/Components/FirelockComponent.cs +++ b/Content.Shared/Doors/Components/FirelockComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Doors.Components; +using Robust.Shared.GameStates; namespace Content.Shared.Doors.Components { @@ -7,9 +7,11 @@ namespace Content.Shared.Doors.Components /// auto-closing on depressurization, air/fire alarm interactions, and preventing normal door functions when /// retaining pressure.. /// - [RegisterComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class FirelockComponent : Component { + #region Settings + /// /// Pry time modifier to be used when the firelock is currently closed due to fire or pressure. /// @@ -17,8 +19,6 @@ namespace Content.Shared.Doors.Components [DataField("lockedPryTimeModifier"), ViewVariables(VVAccess.ReadWrite)] public float LockedPryTimeModifier = 1.5f; - [DataField("autocloseDelay")] public TimeSpan AutocloseDelay = TimeSpan.FromSeconds(3f); - /// /// Maximum pressure difference before the firelock will refuse to open, in kPa. /// @@ -39,5 +39,47 @@ namespace Content.Shared.Doors.Components /// [DataField("alarmAutoClose"), ViewVariables(VVAccess.ReadWrite)] public bool AlarmAutoClose = true; + + /// + /// The cooldown duration before a firelock can automatically close due to a hazardous environment after it has + /// been pried open. Measured in seconds. + /// + [DataField] + public TimeSpan EmergencyCloseCooldownDuration = TimeSpan.FromSeconds(2); + + #endregion + + #region Set by system + + /// + /// When the firelock will be allowed to automatically close again due to a hazardous environment. + /// + [DataField] + public TimeSpan? EmergencyCloseCooldown; + + /// + /// Whether the firelock can open, or is locked due to its environment. + /// + public bool IsLocked => Pressure || Temperature; + + /// + /// Whether the firelock is holding back a hazardous pressure. + /// + [DataField, AutoNetworkedField] + public bool Pressure; + + /// + /// Whether the firelock is holding back extreme temperatures. + /// + [DataField, AutoNetworkedField] + public bool Temperature; + + /// + /// Whether the airlock is powered. + /// + [DataField, AutoNetworkedField] + public bool Powered; + + #endregion } } diff --git a/Content.Shared/Doors/Systems/SharedFirelockSystem.cs b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs new file mode 100644 index 0000000000..4afe26039b --- /dev/null +++ b/Content.Shared/Doors/Systems/SharedFirelockSystem.cs @@ -0,0 +1,124 @@ +using Content.Shared.Access.Systems; +using Content.Shared.Doors.Components; +using Content.Shared.Examine; +using Content.Shared.Popups; +using Content.Shared.Prying.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Doors.Systems; + +public abstract class SharedFirelockSystem : EntitySystem +{ + [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedDoorSystem _doorSystem = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + + public override void Initialize() + { + base.Initialize(); + + // Access/Prying + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnDoorGetPryTimeModifier); + SubscribeLocalEvent(OnAfterPried); + + // Visuals + SubscribeLocalEvent(UpdateVisuals); + SubscribeLocalEvent(UpdateVisuals); + + SubscribeLocalEvent(OnExamined); + } + + public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null) + { + if (!Resolve(uid, ref firelock, ref door)) + return false; + + if (door.State != DoorState.Open + || firelock.EmergencyCloseCooldown != null + && _gameTiming.CurTime < firelock.EmergencyCloseCooldown) + return false; + + if (!_doorSystem.TryClose(uid, door)) + return false; + + return _doorSystem.OnPartialClose(uid, door); + } + + #region Access/Prying + + private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) + { + // Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas + var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid); + + if (!component.Powered || (!overrideAccess && component.IsLocked)) + args.Cancel(); + } + + private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args) + { + if (component.Temperature) + { + _popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-fire-message"), + uid, args.User, PopupType.MediumCaution); + } + else if (component.Pressure) + { + _popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-pressure-message"), + uid, args.User, PopupType.MediumCaution); + } + + if (component.IsLocked) + args.PryTimeModifier *= component.LockedPryTimeModifier; + } + + private void OnAfterPried(EntityUid uid, FirelockComponent component, ref PriedEvent args) + { + component.EmergencyCloseCooldown = _gameTiming.CurTime + component.EmergencyCloseCooldownDuration; + } + + #endregion + + #region Visuals + + private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component); + + private void UpdateVisuals(EntityUid uid, + FirelockComponent? firelock = null, + DoorComponent? door = null, + AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref door, ref appearance, false)) + return; + + // only bother to check pressure on doors that are some variation of closed. + if (door.State != DoorState.Closed + && door.State != DoorState.Welded + && door.State != DoorState.Denying) + { + _appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance); + return; + } + + if (!Resolve(uid, ref firelock, ref appearance, false)) + return; + + _appearance.SetData(uid, DoorVisuals.ClosedLights, firelock.IsLocked, appearance); + } + + #endregion + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + using (args.PushGroup(nameof(FirelockComponent))) + { + if (ent.Comp.Pressure) + args.PushMarkup(Loc.GetString("firelock-component-examine-pressure-warning")); + if (ent.Comp.Temperature) + args.PushMarkup(Loc.GetString("firelock-component-examine-temperature-warning")); + } + } +} diff --git a/Content.Shared/Materials/MaterialPrototype.cs b/Content.Shared/Materials/MaterialPrototype.cs index 905a2359d3..5adf13213e 100644 --- a/Content.Shared/Materials/MaterialPrototype.cs +++ b/Content.Shared/Materials/MaterialPrototype.cs @@ -29,7 +29,7 @@ namespace Content.Shared.Materials /// include which stack we should spawn by default. /// [DataField] - public ProtoId? StackEntity; + public EntProtoId? StackEntity; [DataField] public string Name = string.Empty; diff --git a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs index 59d9fb4c23..4df8ee716c 100644 --- a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs +++ b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs @@ -212,7 +212,7 @@ public sealed class MobThresholdSystem : EntitySystem MobThresholdsComponent? thresholdComponent = null) { threshold = null; - if (!Resolve(target, ref thresholdComponent)) + if (!Resolve(target, ref thresholdComponent, false)) return false; return TryGetThresholdForState(target, MobState.Dead, out threshold, thresholdComponent); diff --git a/Content.Shared/Stacks/StackPrototype.cs b/Content.Shared/Stacks/StackPrototype.cs index 28b7da8f2a..f108419a9e 100644 --- a/Content.Shared/Stacks/StackPrototype.cs +++ b/Content.Shared/Stacks/StackPrototype.cs @@ -4,7 +4,7 @@ using Robust.Shared.Utility; namespace Content.Shared.Stacks; -[Prototype("stack")] +[Prototype] public sealed partial class StackPrototype : IPrototype { [ViewVariables] @@ -15,33 +15,26 @@ public sealed partial class StackPrototype : IPrototype /// Human-readable name for this stack type e.g. "Steel" /// /// This is a localization string ID. - [DataField("name")] + [DataField] public string Name { get; private set; } = string.Empty; /// /// An icon that will be used to represent this stack type. /// - [DataField("icon")] + [DataField] public SpriteSpecifier? Icon { get; private set; } /// /// The entity id that will be spawned by default from this stack. /// - [DataField("spawn", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Spawn { get; private set; } = string.Empty; + [DataField(required: true)] + public EntProtoId Spawn { get; private set; } = string.Empty; /// /// The maximum amount of things that can be in a stack. /// Can be overriden on /// if null, simply has unlimited max count. /// - [DataField("maxCount")] + [DataField] public int? MaxCount { get; private set; } - - /// - /// The size of an individual unit of this stack. - /// - [DataField("itemSize")] - public int? ItemSize; } - diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml index c6fea50bd2..eedc8371d5 100644 --- a/Resources/Audio/Items/attributions.yml +++ b/Resources/Audio/Items/attributions.yml @@ -117,3 +117,10 @@ license: "CC0-1.0" copyright: "Original sound by stomachache on freesound.org, processed by vanilla" source: "https://freesound.org/s/262213/" + +- files: + - "sheath.ogg" + - "unsheath.ogg" + license: "CC-BY-SA-3.0" + copyright: "Taken from tgstation." + source: "https://github.com/tgstation/tgstation/blob/a7f525bce9a359ab5282fc754078cd4b5678a006/sound/items" diff --git a/Resources/Audio/Items/sheath.ogg b/Resources/Audio/Items/sheath.ogg new file mode 100644 index 0000000000..9e1d5cdc00 Binary files /dev/null and b/Resources/Audio/Items/sheath.ogg differ diff --git a/Resources/Audio/Items/unsheath.ogg b/Resources/Audio/Items/unsheath.ogg new file mode 100644 index 0000000000..09867f5966 Binary files /dev/null and b/Resources/Audio/Items/unsheath.ogg differ diff --git a/Resources/Locale/en-US/atmos/firelock-component.ftl b/Resources/Locale/en-US/atmos/firelock-component.ftl index fc375183e9..81f7e58462 100644 --- a/Resources/Locale/en-US/atmos/firelock-component.ftl +++ b/Resources/Locale/en-US/atmos/firelock-component.ftl @@ -1,2 +1,4 @@ firelock-component-is-holding-pressure-message = A gush of air blows in your face... Maybe you should reconsider. -firelock-component-is-holding-fire-message = A gush of warm air blows in your face... Maybe you should reconsider. \ No newline at end of file +firelock-component-is-holding-fire-message = A gush of warm air blows in your face... Maybe you should reconsider. +firelock-component-examine-pressure-warning = The [color=red]extreme pressure[/color] differential warning is active. +firelock-component-examine-temperature-warning = The [color=red]extreme temperature[/color] warning is active. diff --git a/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl b/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl index 6a96c56048..0090c170ca 100644 --- a/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl +++ b/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl @@ -1,2 +1,5 @@ -station-event-meteor-swarm-start-announcement = Meteors are on a collision course with the station. Brace for impact. +station-event-meteor-swarm-start-announcement = Meteors have been detected on collision course with the station. station-event-meteor-swarm-end-announcement = The meteor swarm has passed. Please return to your stations. + +station-event-space-dust-start-announcement = The station is passing through a debris cloud, expect minor damage to external fittings and fixtures. +station-event-meteor-urist-start-announcement = The station is colliding with an unidentified swarm of debris. Please stay calm and do not listen to them. diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 6d71e06415..28a5e3710b 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -293,6 +293,9 @@ uplink-disposable-turret-desc = Looks and functions like a normal electrical too uplink-cluster-banana-peel-name = Cluster Banana uplink-cluster-banana-peel-desc = Splits into 6 explosive banana peels after being thrown, the peels detonate automatically after 20 seconds if nobody slips on them. +uplink-cane-blade-name = Cane Blade +uplink-cane-blade-desc = A cane that has a hidden blade that can be unsheathed. + # Armor uplink-chameleon-name = Chameleon Kit uplink-chameleon-desc = A backpack full of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more! diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml index aac1cbb3f4..fe332ea52d 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/curadrobe.yml @@ -4,6 +4,7 @@ BooksBag: 2 BriefcaseBrown: 2 HandLabeler: 2 + Cane: 3 ClothingEyesGlasses: 2 ClothingEyesGlassesJamjar: 2 ClothingNeckScarfStripedGreen: 2 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 0d6998f3f2..b3e34297b9 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1715,3 +1715,22 @@ - ResearchDirector - Chef - ResearchAssistant + +- type: listing + id: UplinkCaneBlade + name: uplink-cane-blade-name + description: uplink-cane-blade-desc + icon: { sprite: Objects/Weapons/Melee/cane.rsi, state: cane} + productEntity: CaneSheathFilled + cost: + Telecrystal: 5 + categories: + - UplinkJob + conditions: + - !type:BuyerJobCondition + whitelist: + - Librarian + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle diff --git a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml index d0e4e4d7e9..dcbd4d3498 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml @@ -7,7 +7,7 @@ components: - type: Tag tags: # ignore "WhitelistChameleon" tag - - HidesNose + - HidesNose - type: Sprite sprite: Clothing/Mask/gas.rsi - type: Clothing @@ -19,8 +19,8 @@ - type: IdentityBlocker # need that for default ClothingMaskGas - type: UserInterface interfaces: - - key: enum.ChameleonUiKey.Key - type: ChameleonBoundUserInterface + - key: enum.ChameleonUiKey.Key + type: ChameleonBoundUserInterface - type: entity parent: ClothingMaskGasChameleon @@ -28,3 +28,29 @@ suffix: Voice Mask, Chameleon components: - type: VoiceMasker + +- type: entity + parent: ClothingMaskBase + id: ClothingMaskWeldingGas + name: welding gas mask + description: A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd. + components: + - type: Sprite + sprite: Clothing/Mask/welding-gas.rsi + state: icon + - type: Clothing + sprite: Clothing/Mask/welding-gas.rsi + - type: BreathMask + - type: IngestionBlocker + - type: IdentityBlocker + - type: FlashImmunity + - type: EyeProtection + - type: PhysicalComposition + materialComposition: + Steel: 200 + Glass: 100 + - type: StaticPrice + price: 100 + - type: Tag + tags: + - WhitelistChameleon diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml index 127fc7e44b..9cec0324fe 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml @@ -742,6 +742,9 @@ - type: Sprite layers: - state: moldy-slice + - type: Tag + tags: + - Trash - type: SolutionContainerManager solutions: food: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index bdce1d4408..7b503e247f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -521,6 +521,9 @@ - type: Sprite layers: - state: moldy-slice + - type: Tag + tags: + - Trash - type: SolutionContainerManager solutions: food: diff --git a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml index f906ba62b5..27b6867d9d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml @@ -9,6 +9,8 @@ sprite: Objects/Misc/stock_parts.rsi - type: Item size: Tiny + - type: Stack + count: 1 - type: entity id: CapacitorStockPart @@ -25,6 +27,8 @@ - type: Tag tags: - CapacitorStockPart + - type: Stack + stackType: Capacitor - type: entity id: MicroManipulatorStockPart @@ -38,6 +42,8 @@ - type: MachinePart part: Manipulator rating: 1 + - type: Stack + stackType: MicroManipulator - type: entity id: MatterBinStockPart @@ -51,3 +57,5 @@ - type: MachinePart part: MatterBin rating: 1 + - type: Stack + stackType: MatterBin diff --git a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml index 5255e5f303..cfd0b8f770 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml @@ -7,7 +7,6 @@ state: extraction_pack spawn: Fulton1 maxCount: 10 - itemSize: 2 # Entities - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 7856b5d63e..ee2bcf93c6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -420,7 +420,7 @@ whitelist: tags: - CartridgeRocket - proto: MeteorLarge + proto: MeteorMedium - type: GiftIgnore - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml index 6bdac1e85f..3468cade76 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml @@ -1,40 +1,206 @@ - type: entity - id: MeteorLarge + id: BaseMeteor name: meteor - noSpawn: true + description: You prefer them when they're burning up in the atmosphere. + abstract: true components: - type: Sprite noRot: false - sprite: Objects/Weapons/Guns/Projectiles/meteor.rsi - scale: 4,4 - layers: - - state: large - shader: unshaded - - type: ExplodeOnTrigger - - type: DeleteOnTrigger - - type: TriggerOnCollide - fixtureID: projectile + sprite: Objects/Misc/meteor.rsi - type: Projectile damage: {} deleteOnCollide: false - - type: Explosive - explosionType: Default - totalIntensity: 600.0 - intensitySlope: 30 - maxIntensity: 45 + - type: Meteor + damageTypes: + types: + Blunt: 1 + - type: TimedDespawn + lifetime: 120 + - type: Clickable - type: Physics bodyType: Dynamic + bodyStatus: InAir + angularDamping: 0 + linearDamping: 0 - type: Fixtures fixtures: projectile: shape: !type:PhysShapeCircle - radius: 0.8 + radius: 0.4 density: 100 - hard: true - # Didn't use MapGridComponent for now as the bounds are stuffed. + hard: false layer: - LargeMobLayer mask: - Impassable - BulletImpassable + - type: Damageable + damageContainer: Inorganic + - type: Explosive + explosionType: Default + intensitySlope: 4 + maxIntensity: 100 + +- type: entity + parent: BaseMeteor + id: MeteorSpaceDust + name: space dust + description: Makes a station sneeze. + components: + - type: Sprite + state: space_dust + - type: Fixtures + fixtures: + projectile: + shape: + !type:PhysShapeCircle + radius: 0.45 + density: 100 + hard: false + layer: + - LargeMobLayer + mask: + - Impassable + - BulletImpassable + - type: Explosive + totalIntensity: 25 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ExplodeBehavior + +- type: entity + parent: BaseMeteor + id: MeteorSmall + suffix: Small + components: + - type: Sprite + state: small + - type: Fixtures + fixtures: + projectile: + shape: + !type:PhysShapeCircle + radius: 0.25 + density: 100 + hard: false + layer: + - LargeMobLayer + mask: + - Impassable + - BulletImpassable + - type: Explosive + totalIntensity: 100 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1250 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ExplodeBehavior + +- type: entity + parent: BaseMeteor + id: MeteorMedium + suffix: Medium + components: + - type: Sprite + state: medium + - type: Fixtures + fixtures: + projectile: + shape: + !type:PhysShapeCircle + radius: 0.3 + density: 100 + hard: false + layer: + - LargeMobLayer + mask: + - Impassable + - BulletImpassable + - type: Explosive + totalIntensity: 200 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1750 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ExplodeBehavior + +- type: entity + parent: BaseMeteor + id: MeteorLarge + suffix: Large + components: + - type: Sprite + state: big + - type: Explosive + totalIntensity: 300 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 2500 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ExplodeBehavior + +- type: entity + parent: BaseMeteor + id: MeteorUrist + name: Urist McMeteor + description: As a successful member of society with a stable unflinching psyche and limitless drive, natural affinity for finance and domination, you have been selected, no, you have been effortlessly guided by divine (biological) trauma towards this moment. The gates of destiny fling open, and once again you're left standing on pulsating nothingness. A strobing headache of the soul. + suffix: Meteor + components: + - type: Sprite + state: human_pixel + - type: SolutionContainerManager + solutions: + blood: + maxVol: 1000 + reagents: + - ReagentId: Blood + Quantity: 1000 + - type: Explosive + totalIntensity: 25 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 3000 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MaleScreams + params: + volume: 10 + - !type:SpillBehavior + solution: blood + - !type:ExplodeBehavior diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml new file mode 100644 index 0000000000..9bb266bbde --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -0,0 +1,97 @@ +- type: entity + parent: BaseItem + id: Cane + name: cane + description: A wooden cane. + components: + - type: Sprite + sprite: Objects/Weapons/Melee/cane.rsi + state: cane + - type: Item + size: Normal + sprite: Objects/Weapons/Melee/cane.rsi + - type: Appearance + - type: MeleeWeapon + wideAnimationRotation: 45 + damage: + types: + Blunt: 5 + - type: StaminaDamageOnHit + damage: 5 + - type: Wieldable + - type: IncreaseDamageOnWield + damage: + types: + Blunt: 3 + - type: UseDelay + delay: 1 + +- type: entity + name: cane blade + parent: BaseItem + id: CaneBlade + description: A sharp blade with a cane shaped hilt. + components: + - type: Sharp + - type: Sprite + sprite: Objects/Weapons/Melee/cane_blade.rsi + state: icon + - type: MeleeWeapon + wideAnimationRotation: 65 + attackRate: 1.5 + damage: + types: + Slash: 14 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + - type: Item + size: Normal + sprite: Objects/Weapons/Melee/cane_blade.rsi + - type: Tag + tags: + - CaneBlade + - type: DisarmMalus + +- type: entity + parent: Cane + id: CaneSheath + suffix: Empty + components: + - type: Sprite + sprite: Objects/Weapons/Melee/cane.rsi + state: cane-empty + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + - type: UserInterface + interfaces: + - key: enum.StorageUiKey.Key + type: StorageBoundUserInterface + - type: ItemSlots + slots: + item: + name: CaneBlade + insertVerbText: sheath-insert-verb + ejectVerbText: sheath-eject-verb + whitelist: + tags: + - CaneBlade + insertSound: /Audio/Items/sheath.ogg + ejectSound: /Audio/Items/unsheath.ogg + - type: ItemMapper + mapLayers: + cane: + whitelist: + tags: + - CaneBlade + +- type: entity + id: CaneSheathFilled + parent: CaneSheath + suffix: Filled + components: + - type: ContainerFill + containers: + item: + - CaneBlade \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 7d41cb450e..2e335f34fc 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -317,6 +317,7 @@ - PowerCellMicroreactor - PowerCellHigh - WeaponPistolCHIMP + - ClothingMaskWeldingGas - WeaponGauntletGorilla - SynthesizerInstrument - ClothingShoesBootsMagSci diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml index 9c1572fa8f..64d10a65ff 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml @@ -32,7 +32,9 @@ collection: MetalBreak - type: UseDelay delay: 0.2 # prevent light-toggling auto-clickers. -# WD edit start + - type: Tag + tags: + - Structure - type: entity id: SignalSwitch diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index ad9e77d669..f0df2b0203 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -158,25 +158,6 @@ duration: 240 - type: KudzuGrowthRule -- type: entity - id: MeteorSwarm - parent: BaseGameRule - noSpawn: true - components: - - type: StationEvent - earliestStart: 30 - weight: 7.5 - minimumPlayers: 10 #Enough to hopefully have at least one engineering guy - startAnnouncement: station-event-meteor-swarm-start-announcement - endAnnouncement: station-event-meteor-swarm-end-announcement - startAudio: - path: /Audio/Announcements/meteors.ogg - params: - volume: -4 - duration: null #ending is handled by MeteorSwarmRule - startDelay: 30 - - type: MeteorSwarmRule - - type: entity id: MouseMigration parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml new file mode 100644 index 0000000000..17ed82f0cb --- /dev/null +++ b/Resources/Prototypes/GameRules/meteorswarms.yml @@ -0,0 +1,111 @@ +- type: entity + parent: BaseGameRule + id: GameRuleMeteorScheduler + noSpawn: true + components: + - type: GameRule + minPlayers: 25 + - type: MeteorScheduler + +- type: weightedRandomEntity + id: DefaultConfig + weights: + GameRuleSpaceDustMinor: 44 + GameRuleSpaceDustMajor: 22 + GameRuleMeteorSwarmSmall: 18 + GameRuleMeteorSwarmMedium: 10 + GameRuleMeteorSwarmLarge: 5 + GameRuleUristSwarm: 0.05 + +- type: entity + parent: BaseGameRule + id: GameRuleMeteorSwarm + noSpawn: true + components: + - type: GameRule + - type: MeteorSwarm + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleSpaceDustMinor + noSpawn: true + components: + - type: MeteorSwarm + announcement: null + announcementSound: null + nonDirectional: true + meteors: + MeteorSpaceDust: 1 + waves: + min: 2 + max: 3 + meteorsPerWave: + min: 3 + max: 5 + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleSpaceDustMajor + noSpawn: true + components: + - type: MeteorSwarm + announcement: station-event-space-dust-start-announcement + announcementSound: /Audio/Announcements/attention.ogg + nonDirectional: true + meteors: + MeteorSpaceDust: 1 + waves: + min: 2 + max: 3 + meteorsPerWave: + min: 8 + max: 12 + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleMeteorSwarmSmall + noSpawn: true + components: + - type: MeteorSwarm + meteors: + MeteorSmall: 7 + MeteorMedium: 3 + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleMeteorSwarmMedium + noSpawn: true + components: + - type: MeteorSwarm + meteors: + MeteorSmall: 3 + MeteorMedium: 6 + MeteorLarge: 1 + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleMeteorSwarmLarge + noSpawn: true + components: + - type: MeteorSwarm + meteors: + MeteorSmall: 2 + MeteorMedium: 4 + MeteorLarge: 4 + +- type: entity + parent: GameRuleMeteorSwarm + id: GameRuleUristSwarm + noSpawn: true + components: + - type: MeteorSwarm + announcement: station-event-meteor-urist-start-announcement + announcementSound: /Audio/Announcements/attention.ogg + meteors: + MeteorUrist: 1 + waves: + min: 3 + max: 3 + meteorsPerWave: + min: 10 + max: 10 diff --git a/Resources/Prototypes/NPCs/Combat/melee.yml b/Resources/Prototypes/NPCs/Combat/melee.yml index 122875ed97..e4b207d16e 100644 --- a/Resources/Prototypes/NPCs/Combat/melee.yml +++ b/Resources/Prototypes/NPCs/Combat/melee.yml @@ -17,6 +17,29 @@ - !type:HTNCompoundTask task: PickupMeleeCompound + - preconditions: + - !type:BuckledPrecondition + isBuckled: true + tasks: + - !type:HTNPrimitiveTask + shutdownState: TaskFinished + operator: !type:UnbuckleOperator + + - preconditions: + - !type:InContainerPrecondition + isInContainer: true + tasks: + - !type:HTNCompoundTask + task: EscapeCompound + + - preconditions: + - !type:PulledPrecondition + isPulled: true + tasks: + - !type:HTNPrimitiveTask + shutdownState: TaskFinished + operator: !type:UnPullOperator + # Melee combat (unarmed or otherwise) - tasks: - !type:HTNPrimitiveTask @@ -101,6 +124,21 @@ proto: NearbyMeleeTargets key: Target +- type: htnCompound + id: EscapeCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + shutdownState: TaskFinished + operator: !type:ContainerOperator + targetKey: Target + - !type:HTNPrimitiveTask + operator: !type:EscapeOperator + targetKey: Target + preconditions: + - !type:InContainerPrecondition + isInContainer: true + - type: htnCompound id: MeleeAttackOrderedTargetCompound branches: diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 8b748f3fe5..6a786f2b24 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -76,7 +76,7 @@ Steel: 100 Plastic: 200 Glass: 100 - + - type: latheRecipe id: SignallerAdvanced result: RemoteSignallerAdvanced @@ -175,6 +175,14 @@ Plasma: 1500 Uranium: 150 +- type: latheRecipe + id: ClothingMaskWeldingGas + result: ClothingMaskWeldingGas + completetime: 3 + materials: + Steel: 600 + Glass: 200 + - type: latheRecipe id: WeaponForceGun result: WeaponForceGun diff --git a/Resources/Prototypes/Research/industrial.yml b/Resources/Prototypes/Research/industrial.yml index 30345a5dfc..ccc6e9c0f4 100644 --- a/Resources/Prototypes/Research/industrial.yml +++ b/Resources/Prototypes/Research/industrial.yml @@ -14,6 +14,7 @@ - BorgModuleMining - OreProcessorIndustrialMachineCircuitboard - OreBagOfHolding + - ClothingMaskWeldingGas - type: technology id: AdvancedPowercells diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml b/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml index 6613a311ca..d8cfbe3332 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/glass.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: glass } spawn: SheetGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedGlass @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: rglass } spawn: SheetRGlass1 maxCount: 30 - itemSize: 1 - type: stack id: PlasmaGlass @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: pglass } spawn: SheetPGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedPlasmaGlass @@ -28,7 +25,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: rpglass } spawn: SheetRPGlass1 maxCount: 30 - itemSize: 1 - type: stack id: UraniumGlass @@ -36,7 +32,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: uglass } spawn: SheetUGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ReinforcedUraniumGlass @@ -44,7 +39,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: ruglass } spawn: SheetRUGlass1 maxCount: 30 - itemSize: 1 - type: stack id: ClockworkGlass @@ -52,4 +46,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/glass.rsi, state: cglass } spawn: SheetClockworkGlass1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml b/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml index fdaa79dab0..a32fa19176 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/metal.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: steel } spawn: SheetSteel1 maxCount: 30 - itemSize: 1 - type: stack id: Plasteel @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: plasteel } spawn: SheetPlasteel1 maxCount: 30 - itemSize: 1 - type: stack id: Brass @@ -20,4 +18,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/metal.rsi, state: brass } spawn: SheetBrass1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/Sheets/other.yml b/Resources/Prototypes/Stacks/Materials/Sheets/other.yml index 17ac1a2998..972dbe0a86 100644 --- a/Resources/Prototypes/Stacks/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Stacks/Materials/Sheets/other.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: paper } spawn: SheetPaper1 maxCount: 30 - itemSize: 1 - type: stack id: Plasma @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: plasma } spawn: SheetPlasma1 maxCount: 30 - itemSize: 1 - type: stack id: Plastic @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: plastic } spawn: SheetPlastic1 maxCount: 30 - itemSize: 1 - type: stack id: Uranium @@ -28,4 +25,3 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/other.rsi, state: uranium } spawn: SheetUranium1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/crystals.yml b/Resources/Prototypes/Stacks/Materials/crystals.yml index 274f9c10ea..28f4ed70ca 100644 --- a/Resources/Prototypes/Stacks/Materials/crystals.yml +++ b/Resources/Prototypes/Stacks/Materials/crystals.yml @@ -3,4 +3,3 @@ name: telecrystal icon: Objects/Specific/Syndicate/telecrystal.rsi spawn: Telecrystal1 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/ingots.yml b/Resources/Prototypes/Stacks/Materials/ingots.yml index 134145c28d..db1087a63a 100644 --- a/Resources/Prototypes/Stacks/Materials/ingots.yml +++ b/Resources/Prototypes/Stacks/Materials/ingots.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Materials/ingots.rsi", state: gold } spawn: IngotGold1 maxCount: 30 - itemSize: 1 - type: stack id: Silver @@ -12,4 +11,3 @@ icon: { sprite: "/Textures/Objects/Materials/ingots.rsi", state: silver } spawn: IngotSilver1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/materials.yml b/Resources/Prototypes/Stacks/Materials/materials.yml index 188e4a8b7c..c51d9cfcf8 100644 --- a/Resources/Prototypes/Stacks/Materials/materials.yml +++ b/Resources/Prototypes/Stacks/Materials/materials.yml @@ -1,66 +1,65 @@ - type: stack id: Biomass - name: биомасса + name: biomass icon: { sprite: /Textures/Objects/Misc/monkeycube.rsi, state: cube } spawn: MaterialBiomass1 maxCount: 100 - itemSize: 1 - type: stack id: WoodPlank - name: древесина + name: wood plank icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: wood } spawn: MaterialWoodPlank1 maxCount: 30 - itemSize: 1 - type: stack id: Cardboard - name: картонная коробка + name: cardboard icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cardboard } spawn: MaterialCardboard1 maxCount: 30 - itemSize: 1 - type: stack id: Cloth - name: ткань + name: cloth icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cloth } spawn: MaterialCloth1 maxCount: 30 - itemSize: 1 - type: stack id: Durathread - name: дюраткань + name: durathread icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: durathread } spawn: MaterialDurathread1 maxCount: 30 - itemSize: 1 - type: stack id: Diamond - name: алмаз + name: diamond icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: diamond } spawn: MaterialDiamond1 maxCount: 30 - itemSize: 2 - type: stack id: Cotton - name: хлопок + name: cotton icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: cotton } spawn: MaterialCotton1 maxCount: 30 - itemSize: 1 + +- type: stack + id: Pyrotton + name: pyrotton + icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: pyrotton } + spawn: MaterialPyrotton1 + maxCount: 30 - type: stack id: Bananium - name: бананиум + name: bananium icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: bananium } spawn: MaterialBananium1 maxCount: 10 - itemSize: 2 - type: stack id: MeatSheets @@ -68,7 +67,6 @@ icon: { sprite: /Textures/Objects/Materials/Sheets/meaterial.rsi, state: meat } spawn: MaterialSheetMeat1 maxCount: 30 - itemSize: 1 - type: stack id: WebSilk @@ -76,7 +74,6 @@ icon: { sprite: /Textures/Objects/Materials/silk.rsi, state: icon } spawn: MaterialWebSilk1 maxCount: 50 - itemSize: 1 - type: stack id: Bones @@ -84,7 +81,6 @@ icon: { sprite: /Textures/Objects/Materials/materials.rsi, state: bones} spawn: MaterialBones1 maxCount: 30 - itemSize: 1 - type: stack id: RunicMetalSheets @@ -92,12 +88,10 @@ icon: { sprite: /Textures/White/Cult/Entities/runic_metal.rsi, state: runic} spawn: CultRunicMetal1 maxCount: 30 - itemSize: 1 - type: stack id: Gunpowder - name: порох + name: gunpowder icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: MaterialGunpowder maxCount: 60 - itemSize: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Stacks/Materials/ore.yml b/Resources/Prototypes/Stacks/Materials/ore.yml index 2a95393c27..51254b5a7a 100644 --- a/Resources/Prototypes/Stacks/Materials/ore.yml +++ b/Resources/Prototypes/Stacks/Materials/ore.yml @@ -4,7 +4,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: gold } spawn: GoldOre1 maxCount: 30 - itemSize: 2 - type: stack id: SteelOre @@ -12,7 +11,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: iron } spawn: SteelOre1 maxCount: 30 - itemSize: 2 - type: stack id: PlasmaOre @@ -20,7 +18,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: plasma } spawn: PlasmaOre1 maxCount: 30 - itemSize: 2 - type: stack id: SilverOre @@ -28,7 +25,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: silver } spawn: SilverOre1 maxCount: 30 - itemSize: 2 - type: stack id: SpaceQuartz @@ -36,7 +32,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: spacequartz } spawn: SpaceQuartz1 maxCount: 30 - itemSize: 2 - type: stack id: UraniumOre @@ -44,7 +39,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: uranium } spawn: UraniumOre1 maxCount: 30 - itemSize: 2 - type: stack @@ -53,7 +47,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: bananium } spawn: BananiumOre1 maxCount: 30 - itemSize: 2 - type: stack id: Coal @@ -61,7 +54,6 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: coal } spawn: Coal1 maxCount: 30 - itemSize: 2 - type: stack id: SaltOre @@ -69,4 +61,3 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: salt } spawn: Salt1 maxCount: 30 - itemSize: 2 diff --git a/Resources/Prototypes/Stacks/Materials/parts.yml b/Resources/Prototypes/Stacks/Materials/parts.yml index fe8241688f..b1d814040c 100644 --- a/Resources/Prototypes/Stacks/Materials/parts.yml +++ b/Resources/Prototypes/Stacks/Materials/parts.yml @@ -4,4 +4,3 @@ icon: { sprite: /Textures/Objects/Materials/parts.rsi, state: rods } spawn: PartRodMetal1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/consumable_stacks.yml b/Resources/Prototypes/Stacks/consumable_stacks.yml index 0b64204117..e7feab7b52 100644 --- a/Resources/Prototypes/Stacks/consumable_stacks.yml +++ b/Resources/Prototypes/Stacks/consumable_stacks.yml @@ -5,7 +5,6 @@ name: pancake spawn: FoodBakedPancake maxCount: 3 - itemSize: 1 # Food Containers @@ -15,54 +14,60 @@ icon: { sprite: Objects/Consumable/Food/Baked/pizza.rsi, state: box } spawn: FoodBoxPizza maxCount: 30 - itemSize: 10 # Smokeables - type: stack id: PaperRolling - name: самокрутка + name: rolling paper icon: { sprite: /Textures/Objects/Consumable/Smokeables/Cigarettes/paper.rsi, state: cigpaper } spawn: PaperRolling maxCount: 5 - itemSize: 1 - type: stack id: CigaretteFilter - name: фильтр + name: cigarette filter icon: { sprite: /Textures/Objects/Consumable/Smokeables/Cigarettes/paper.rsi, state: cigfilter } spawn: CigaretteFilter maxCount: 5 - itemSize: 2 - type: stack id: GroundTobacco - name: измельчённый табак + name: ground tobacco icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: GroundTobacco maxCount: 5 - itemSize: 1 - type: stack id: GroundCannabis - name: измельчённый каннабис + name: ground cannabis icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile } spawn: GroundCannabis maxCount: - itemSize: 1 + +- type: stack + id: GroundCannabisRainbow + name: ground rainbow cannabis + icon: { sprite: /Textures/Objects/Specific/Hydroponics/rainbow_cannabis.rsi, state: powderpile_rainbow } + spawn: GroundCannabisRainbow + maxCount: - type: stack id: LeavesTobaccoDried - name: высушеные листья табака + name: dried tobacco leaves icon: { sprite: /Textures/Objects/Specific/Hydroponics/tobacco.rsi, state: dried } spawn: LeavesTobaccoDried maxCount: 5 - itemSize: 5 - type: stack id: LeavesCannabisDried - name: высушеные листья каннабиса + name: dried cannabis leaves icon: { sprite: /Textures/Objects/Specific/Hydroponics/tobacco.rsi, state: dried } spawn: LeavesCannabisDried maxCount: 5 - itemSize: 5 + +- type: stack + id: LeavesCannabisRainbowDried + name: dried rainbow cannabis leaves + icon: { sprite: /Textures/Objects/Specific/Hydroponics/rainbow_cannabis.rsi, state: dried } + spawn: LeavesCannabisRainbowDried diff --git a/Resources/Prototypes/Stacks/engineering_stacks.yml b/Resources/Prototypes/Stacks/engineering_stacks.yml index 77cc620402..b4550015dc 100644 --- a/Resources/Prototypes/Stacks/engineering_stacks.yml +++ b/Resources/Prototypes/Stacks/engineering_stacks.yml @@ -4,11 +4,9 @@ name: inflatable wall spawn: InflatableWallStack1 maxCount: 10 - itemSize: 1 - type: stack id: InflatableDoor name: inflatable door spawn: InflatableDoorStack1 maxCount: 4 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/floor_tile_stacks.yml b/Resources/Prototypes/Stacks/floor_tile_stacks.yml index c5e37013b8..c88786f0dc 100644 --- a/Resources/Prototypes/Stacks/floor_tile_stacks.yml +++ b/Resources/Prototypes/Stacks/floor_tile_stacks.yml @@ -3,469 +3,402 @@ name: steel tile spawn: FloorTileItemSteel maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMetalDiamond name: steel tile spawn: FloorTileItemMetalDiamond maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWood name: wood floor spawn: FloorTileItemWood maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWhite name: white tile spawn: FloorTileItemWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileDark name: dark tile spawn: FloorTileItemDark maxCount: 30 - itemSize: 5 - type: stack id: FloorTileTechmaint name: techmaint floor spawn: FloorTileItemTechmaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileFreezer name: freezer tile spawn: FloorTileItemFreezer maxCount: 30 - itemSize: 5 - type: stack id: FloorTileShowroom name: showroom tile spawn: FloorTileItemShowroom maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGCircuit name: green-circuit floor spawn: FloorTileItemGCircuit maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGold name: gold floor spawn: FloorTileItemGold maxCount: 30 - itemSize: 5 - type: stack id: FloorTileReinforced name: reinforced tile spawn: FloorTileItemReinforced maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMono name: mono tile spawn: FloorTileItemMono maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBrassFilled name: filled brass plate spawn: FloorTileItemBrassFilled maxCount: 30 - itemSize: 5 - + - type: stack id: FloorTileBrassReebe name: smooth brass plate spawn: FloorTileItemBrassReebe maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLino name: linoleum floor spawn: FloorTileItemLino maxCount: 30 - itemSize: 5 - type: stack id: FloorTileHydro name: hydro tile spawn: FloorTileItemHydro maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLime name: lime tile spawn: FloorTileItemLime maxCount: 30 - itemSize: 5 - type: stack id: FloorTileDirty name: dirty tile spawn: FloorTileItemDirty maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleWhite name: white shuttle tile spawn: FloorTileItemShuttleWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleBlue name: blue shuttle tile spawn: FloorTileItemShuttleBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleOrange name: orange shuttle tile spawn: FloorTileItemShuttleOrange maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttlePurple name: purple shuttle tile spawn: FloorTileItemShuttlePurple maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleRed name: red shuttle tile spawn: FloorTileItemShuttleRed maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleGrey name: grey shuttle tile spawn: FloorTileItemShuttleGrey maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackShuttleBlack name: black shuttle tile spawn: FloorTileItemShuttleBlack maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackEighties name: eighties floor tile spawn: FloorTileItemEighties maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeBlue name: blue arcade tile spawn: FloorTileItemArcadeBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeBlue2 name: blue arcade tile spawn: FloorTileItemArcadeBlue2 maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackArcadeRed name: red arcade tile spawn: FloorTileItemArcadeRed maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetRed name: red carpet tile spawn: FloorCarpetItemRed maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetBlack name: block carpet tile spawn: FloorCarpetItemBlack maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetBlue name: blue carpet tile spawn: FloorCarpetItemBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetGreen name: green carpet tile spawn: FloorCarpetItemGreen maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetOrange name: orange carpet tile spawn: FloorCarpetItemOrange maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetSkyBlue name: skyblue carpet tile spawn: FloorCarpetItemSkyBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetPurple name: purple carpet tile spawn: FloorCarpetItemPurple maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetPink name: pink carpet tile spawn: FloorCarpetItemPink maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetCyan name: cyan carpet tile spawn: FloorCarpetItemCyan maxCount: 30 - itemSize: 5 - type: stack id: FloorCarpetWhite name: white carpet tile spawn: FloorCarpetItemWhite maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackCarpetClown name: clown carpet tile spawn: FloorTileItemCarpetClown maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackCarpetOffice name: office carpet tile spawn: FloorTileItemCarpetOffice maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackBoxing name: boxing ring tile spawn: FloorTileItemBoxing maxCount: 30 - itemSize: 5 - type: stack id: FloorTileStackGym name: gym floor tile spawn: FloorTileItemGym maxCount: 30 - itemSize: 5 - type: stack id: FloorTileElevatorShaft name: elevator shaft tile spawn: FloorTileItemElevatorShaft maxCount: 30 - itemSize: 5 - type: stack id: FloorTileRockVault name: rock vault tile spawn: FloorTileItemRockVault maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBlue name: blue floor tile spawn: FloorTileItemBlue maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMining name: mining floor tile spawn: FloorTileItemMining maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMiningDark name: dark mining floor tile spawn: FloorTileItemMiningDark maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMiningLight name: light mining floor tile spawn: FloorTileItemMiningLight maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBar name: item bar floor tile spawn: FloorTileItemBar maxCount: 30 - itemSize: 5 - type: stack id: FloorTileClown name: clown floor tile spawn: FloorTileItemClown maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMime name: mime floor tile spawn: FloorTileItemMime maxCount: 30 - itemSize: 5 - type: stack id: FloorTileKitchen name: kitchen floor tile spawn: FloorTileItemKitchen maxCount: 30 - itemSize: 5 - type: stack id: FloorTileLaundry name: laundry floor tile spawn: FloorTileItemLaundry maxCount: 30 - itemSize: 5 - type: stack id: FloorTileConcrete name: concrete tile spawn: FloorTileItemGrayConcrete maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrayConcrete name: gray concrete tile spawn: FloorTileItemLaundry maxCount: 30 - itemSize: 5 - type: stack id: FloorTileOldConcrete name: old concrete tile spawn: FloorTileItemOldConcrete maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSilver name: silver floor tile spawn: FloorTileItemSilver maxCount: 30 - itemSize: 5 - type: stack id: FloorTileBCircuit name: bcircuit floor tile spawn: FloorTileItemBCircuit maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrass name: grass floor tile spawn: FloorTileItemGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGrassJungle name: grass jungle floor tile spawn: FloorTileItemGrassJungle maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSnow name: snow floor tile spawn: FloorTileItemSnow maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWoodPattern name: wood pattern floor spawn: FloorTileItemWoodPattern maxCount: 30 - itemSize: 5 - type: stack id: FloorTileFlesh name: flesh floor spawn: FloorTileItemFlesh maxCount: 30 - itemSize: 5 - type: stack id: FloorTileSteelMaint name: steel maint floor spawn: FloorTileItemSteelMaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileGratingMaint name: grating maint floor spawn: FloorTileItemGratingMaint maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWeb name: web tile spawn: FloorTileItemWeb maxCount: 30 - itemSize: 5 # Faux science tiles - type: stack @@ -473,38 +406,33 @@ name: astro-grass floor spawn: FloorTileItemAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileMowedAstroGrass name: mowed astro-grass floor spawn: FloorTileItemMowedAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileJungleAstroGrass name: jungle astro-grass floor spawn: FloorTileItemJungleAstroGrass maxCount: 30 - itemSize: 5 - type: stack id: FloorTileAstroIce name: astro-ice floor spawn: FloorTileItemAstroIce maxCount: 30 - itemSize: 5 - type: stack id: FloorTileAstroSnow name: astro-snow floor spawn: FloorTileItemAstroSnow maxCount: 30 - itemSize: 5 - type: stack id: FloorTileWoodLarge name: large wood floor spawn: FloorTileItemWoodLarge - maxCount: 30 \ No newline at end of file + maxCount: 30 diff --git a/Resources/Prototypes/Stacks/medical_stacks.yml b/Resources/Prototypes/Stacks/medical_stacks.yml index 9d2b77ec93..7ad3c21634 100644 --- a/Resources/Prototypes/Stacks/medical_stacks.yml +++ b/Resources/Prototypes/Stacks/medical_stacks.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: ointment } spawn: Ointment maxCount: 10 - itemSize: 1 - type: stack id: AloeCream @@ -12,7 +11,6 @@ icon: { sprite: "/Textures/Objects/Specific/Hydroponics/aloe.rsi", state: cream } spawn: AloeCream maxCount: 10 - itemSize: 1 - type: stack id: Gauze @@ -20,7 +18,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: gauze } spawn: Gauze maxCount: 10 - itemSize: 1 - type: stack id: Brutepack @@ -28,7 +25,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: gauze } spawn: Brutepack maxCount: 10 - itemSize: 1 - type: stack id: Bloodpack @@ -36,7 +32,6 @@ icon: { sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: bloodpack } spawn: Bloodpack maxCount: 10 - itemSize: 1 - type: stack id: MedicatedSuture @@ -44,7 +39,6 @@ icon: {sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: medicated-suture } spawn: MedicatedSuture maxCount: 10 - itemSize: 1 - type: stack id: RegenerativeMesh @@ -52,6 +46,4 @@ icon: {sprite: "/Textures/Objects/Specific/Medical/medical.rsi", state: regenerative-mesh} spawn: RegenerativeMesh maxCount: 10 - itemSize: 1 - diff --git a/Resources/Prototypes/Stacks/power_stacks.yml b/Resources/Prototypes/Stacks/power_stacks.yml index d2a45d511f..2e1c336d40 100644 --- a/Resources/Prototypes/Stacks/power_stacks.yml +++ b/Resources/Prototypes/Stacks/power_stacks.yml @@ -4,7 +4,6 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coil-30 } spawn: CableApcStack1 maxCount: 30 - itemSize: 1 - type: stack id: CableMV @@ -12,7 +11,6 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilmv-30 } spawn: CableMVStack1 maxCount: 30 - itemSize: 1 - type: stack id: CableHV @@ -20,4 +18,3 @@ icon: { sprite: "/Textures/Objects/Tools/cable-coils.rsi", state: coilhv-30 } spawn: CableHVStack1 maxCount: 30 - itemSize: 1 diff --git a/Resources/Prototypes/Stacks/science_stacks.yml b/Resources/Prototypes/Stacks/science_stacks.yml index 010a5dc659..bf58fad915 100644 --- a/Resources/Prototypes/Stacks/science_stacks.yml +++ b/Resources/Prototypes/Stacks/science_stacks.yml @@ -3,4 +3,21 @@ name: artifact fragment spawn: ArtifactFragment1 maxCount: 30 - itemSize: 5 \ No newline at end of file + +- type: stack + id: Capacitor + name: capacitor + spawn: CapacitorStockPart + maxCount: 10 + +- type: stack + id: MicroManipulator + name: micro manipulator + spawn: MicroManipulatorStockPart + maxCount: 10 + +- type: stack + id: MatterBin + name: matter bin + spawn: MatterBinStockPart + maxCount: 10 diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index 94136ccc5e..3a41de0e53 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -35,6 +35,7 @@ description: extended-description rules: - BasicStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - type: gamePreset @@ -68,6 +69,7 @@ description: secret-description rules: - BasicStationEventScheduler + - GameRuleMeteorScheduler - type: gamePreset id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby. @@ -99,6 +101,7 @@ - Traitor - SubGamemodesRule - BasicStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - type: gamePreset @@ -125,6 +128,7 @@ - Nukeops - SubGamemodesRule - BasicStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - type: gamePreset @@ -140,6 +144,7 @@ - Revolutionary - SubGamemodesRule - BasicStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - type: gamePreset @@ -156,6 +161,7 @@ rules: - Zombie - BasicStationEventScheduler + - GameRuleMeteorScheduler - BasicRoundstartVariation - type: gamePreset diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 008a90eb04..caf19f85f2 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -258,6 +258,9 @@ - type: Tag id: CableCoil +- type: Tag + id: CaneBlade + - type: Tag id: CannonBall @@ -1319,3 +1322,5 @@ - type: Tag id: WriteIgnoreStamps + +# ALPHABETICAL diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK-vox.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK-vox.png new file mode 100644 index 0000000000..e6028f3a4c Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK-vox.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK.png new file mode 100644 index 0000000000..5d0a7ea70d Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/equipped-MASK.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon-up.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon-up.png new file mode 100644 index 0000000000..352e536ca9 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon-up.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon.png new file mode 100644 index 0000000000..cdb8d4b7c7 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-left.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-left.png new file mode 100644 index 0000000000..c5111a66a9 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-right.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-right.png new file mode 100644 index 0000000000..5841f0b846 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/meta.json b/Resources/Textures/Clothing/Mask/welding-gas.rsi/meta.json new file mode 100644 index 0000000000..8e9b857325 --- /dev/null +++ b/Resources/Textures/Clothing/Mask/welding-gas.rsi/meta.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/6f5ca45e3ac06b30fb1957042214a888d8c01722. Inhands, worn sprites, and vox sprites created by EmoGarbage404 (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "icon-up" + }, + { + "name": "equipped-MASK", + "directions": 4 + }, + { + "name": "up-equipped-MASK", + "directions": 4 + }, + { + "name": "equipped-MASK-vox", + "directions": 4 + }, + { + "name": "up-equipped-MASK-vox", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "up-inhand-left", + "directions": 4 + }, + { + "name": "up-inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK-vox.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK-vox.png new file mode 100644 index 0000000000..90871d7d00 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK-vox.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK.png new file mode 100644 index 0000000000..449c8624c9 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-equipped-MASK.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-left.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-left.png new file mode 100644 index 0000000000..2868ab8317 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-left.png differ diff --git a/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-right.png b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-right.png new file mode 100644 index 0000000000..1362d99011 Binary files /dev/null and b/Resources/Textures/Clothing/Mask/welding-gas.rsi/up-inhand-right.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big.png b/Resources/Textures/Objects/Misc/meteor.rsi/big.png new file mode 100644 index 0000000000..34062b9a43 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png new file mode 100644 index 0000000000..292c93f6e6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png new file mode 100644 index 0000000000..292c93f6e6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png new file mode 100644 index 0000000000..34062b9a43 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/firework.png b/Resources/Textures/Objects/Misc/meteor.rsi/firework.png new file mode 100644 index 0000000000..1c661fc1d5 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/firework.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png new file mode 100644 index 0000000000..24b0847b76 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/human.png b/Resources/Textures/Objects/Misc/meteor.rsi/human.png new file mode 100644 index 0000000000..d73b573fbe Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/human.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png new file mode 100644 index 0000000000..8207cd57d2 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium.png new file mode 100644 index 0000000000..27b6b98ae6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png new file mode 100644 index 0000000000..b758c2c1dd Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png new file mode 100644 index 0000000000..b758c2c1dd Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png new file mode 100644 index 0000000000..27b6b98ae6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png new file mode 100644 index 0000000000..a4e0cf266c Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png new file mode 100644 index 0000000000..a4e0cf266c Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/meta.json b/Resources/Textures/Objects/Misc/meteor.rsi/meta.json new file mode 100644 index 0000000000..1e857be2cb --- /dev/null +++ b/Resources/Textures/Objects/Misc/meteor.rsi/meta.json @@ -0,0 +1,271 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31dd6749bfe32810c46e7913efc99a187479cd51/icons/obj/meteor.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "small", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "small_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "small_flash", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "small_flash_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "space_dust" + }, + { + "name": "medium", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "medium_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "medium_piercing", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "medium_piercing_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "medium_radioactive", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "medium_radioactive_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "big", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "big_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "big_cluster", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "big_cluster_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "human", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "human_pixel", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "firework_pixel" + }, + { + "name": "firework" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small.png b/Resources/Textures/Objects/Misc/meteor.rsi/small.png new file mode 100644 index 0000000000..2c729ac87e Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png new file mode 100644 index 0000000000..7ff19cde08 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png new file mode 100644 index 0000000000..7ff19cde08 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png new file mode 100644 index 0000000000..2c729ac87e Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png differ diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png b/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png new file mode 100644 index 0000000000..6e13a867d8 Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane-empty.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane-empty.png new file mode 100644 index 0000000000..64cb9ef3aa Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane-empty.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane.png new file mode 100644 index 0000000000..721847ff28 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/cane.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-left.png new file mode 100644 index 0000000000..caa2f23270 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-right.png new file mode 100644 index 0000000000..8f12a08afd Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/meta.json new file mode 100644 index 0000000000..913fcb524b --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by ps3moira#9488 on discord", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cane-empty" + }, + { + "name": "cane" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "wielded-inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "wielded-inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-left.png new file mode 100644 index 0000000000..f6f87a4a90 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-left.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-right.png new file mode 100644 index 0000000000..e1f0449b4c Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane.rsi/wielded-inhand-right.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/icon.png new file mode 100644 index 0000000000..6581dc96e8 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-left.png new file mode 100644 index 0000000000..f8e57880cb Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-right.png new file mode 100644 index 0000000000..5fa04f7f87 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/meta.json new file mode 100644 index 0000000000..a48335cc0d --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Melee/cane_blade.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by ps3moira#9488 on discord", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "icon" + } + ] +}