From f863bab809b2e835eca3180bdebebd5e467973fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D0=B8=D0=BB?= =?UTF-8?q?=D1=8E=D0=B2=D1=87=D0=B8=D1=86?= Date: Tue, 8 Oct 2024 19:06:36 +0300 Subject: [PATCH 1/7] Fract war update v0.7 --- .../EventCapturePointSystem.cs | 20 ++++++++++ .../SS220/FractWar/FractWarRuleComponent.cs | 6 +++ .../SS220/FractWar/FractWarRuleSystem.cs | 37 +++++++++++++++++++ .../EventCapturePointComponent.cs | 9 +++++ .../EventCapturePointFlagComponent.cs | 3 ++ .../game-presets/preset-FractWar.ftl | 2 + 6 files changed, 77 insertions(+) create mode 100644 Content.Server/SS220/FractWar/FractWarRuleComponent.cs create mode 100644 Content.Server/SS220/FractWar/FractWarRuleSystem.cs create mode 100644 Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl diff --git a/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs b/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs index 34bf9524e214b9..7305ba31da0a50 100644 --- a/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs +++ b/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.Item; using Content.Shared.SS220.EventCapturePoint; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Map; @@ -11,6 +12,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.SS220.EventCapturePoint; @@ -22,6 +24,7 @@ public sealed class EventCapturePointSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { @@ -36,6 +39,23 @@ public override void Initialize() SubscribeLocalEvent(OnFlagRemoved); } + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (component.FlagEntity is not { } flagUid || + !TryComp(flagUid, out var flagComp) || + flagComp.Fraction is not { } flagFraction) + continue; + + if (!component.PointRetentionTime.TryAdd(flagFraction, TimeSpan.Zero)) + component.PointRetentionTime[flagFraction] += _timing.TickPeriod; + } + } + #region Listeners private void OnActivated(Entity entity, ref ActivateInWorldEvent args) { diff --git a/Content.Server/SS220/FractWar/FractWarRuleComponent.cs b/Content.Server/SS220/FractWar/FractWarRuleComponent.cs new file mode 100644 index 00000000000000..a89b3d92590902 --- /dev/null +++ b/Content.Server/SS220/FractWar/FractWarRuleComponent.cs @@ -0,0 +1,6 @@ + +namespace Content.Server.SS220.FractWar; + +public sealed partial class FractWarRuleComponent : Component +{ +} diff --git a/Content.Server/SS220/FractWar/FractWarRuleSystem.cs b/Content.Server/SS220/FractWar/FractWarRuleSystem.cs new file mode 100644 index 00000000000000..96691ec14f44db --- /dev/null +++ b/Content.Server/SS220/FractWar/FractWarRuleSystem.cs @@ -0,0 +1,37 @@ +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Shared.GameTicking.Components; +using Content.Shared.SS220.EventCapturePoint; +using System.Linq; + +namespace Content.Server.SS220.FractWar; + +public sealed partial class FractWarRuleSystem : GameRuleSystem +{ + protected override void AppendRoundEndText(EntityUid uid, FractWarRuleComponent component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args) + { + base.AppendRoundEndText(uid, component, gameRule, ref args); + + Dictionary fractionsWinPoints = new(); + var capturePoints = EntityQueryEnumerator(); + while (capturePoints.MoveNext(out _, out var capturePointComponent)) + { + foreach (var (fraction, retentionTime) in capturePointComponent.PointRetentionTime) + { + var winPoints = (float)(retentionTime / capturePointComponent.RetentionTimeForWP) * capturePointComponent.WinPoints; + if (!fractionsWinPoints.TryAdd(fraction, winPoints)) + fractionsWinPoints[fraction] += winPoints; + } + } + + //Sort by value + fractionsWinPoints = fractionsWinPoints.OrderBy(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value); + + foreach (var (fraction, winPoints) in fractionsWinPoints) + { + args.AddLine(Loc.GetString("fract-war-round-end-fraction-points", ("fraction", Loc.GetString(fraction)), ("points", (int)winPoints))); + } + + args.AddLine(Loc.GetString("fract-war-round-end-winner", ("fraction", Loc.GetString(fractionsWinPoints.First().Key)))); + } +} diff --git a/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs b/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs index ce3f019ad1df69..aefe01517b4315 100644 --- a/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs +++ b/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs @@ -18,6 +18,15 @@ public sealed partial class EventCapturePointComponent : Component [ViewVariables, DataField] public float FlagRemovalImpulse = 35; + + [DataField] + public float WinPoints = 0.1f; + + [DataField] + public TimeSpan RetentionTimeForWP = TimeSpan.FromMinutes(1); + + [ViewVariables] + public Dictionary PointRetentionTime = new(); } [Serializable, NetSerializable] diff --git a/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs b/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs index 67afcf1cf58167..d8f2a51f0f5a95 100644 --- a/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs +++ b/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs @@ -12,6 +12,9 @@ public sealed partial class EventCapturePointFlagComponent : Component { [ViewVariables, DataField, AutoNetworkedField] public bool Planted; + + [DataField] + public string? Fraction; } [Serializable, NetSerializable] diff --git a/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl b/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl new file mode 100644 index 00000000000000..509fff23523587 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl @@ -0,0 +1,2 @@ +fract-war-round-end-fraction-points = { $fraction }: { $points } +fract-war-round-end-winner = { $fraction } становится победителем! From 6e06affbb5614927d8295dbcb5d39071758308c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D0=B8=D0=BB?= =?UTF-8?q?=D1=8E=D0=B2=D1=87=D0=B8=D1=86?= Date: Sun, 13 Oct 2024 01:15:55 +0300 Subject: [PATCH 2/7] add game preset and some changes --- .../EventCapturePointSystem.cs | 1 - .../SS220/FractWar/FractWarRuleComponent.cs | 3 ++- .../SS220/FractWar/FractWarRuleSystem.cs | 13 +++++++------ .../EventCapturePointComponent.cs | 10 ++++++++-- .../EventCapturePointFlagComponent.cs | 3 +++ .../game-presets/preset-FractWar.ftl | 8 ++++++-- .../entities/structures/specific/flags.ftl | 15 +++++++++++++++ .../Specific/EventPointCapture/flags.yml | 17 ++++++++++------- .../Prototypes/SS220/GameRules/roundstart.yml | 7 +++++++ Resources/Prototypes/SS220/game_presets.yml | 13 ++++++++++++- 10 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 Resources/Locale/ru-RU/ss220/prototypes/entities/structures/specific/flags.ftl create mode 100644 Resources/Prototypes/SS220/GameRules/roundstart.yml diff --git a/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs b/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs index 7305ba31da0a50..78d07e07ab1ea6 100644 --- a/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs +++ b/Content.Server/SS220/EventCapturePoint/EventCapturePointSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Interaction; using Content.Shared.Item; using Content.Shared.SS220.EventCapturePoint; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Map; diff --git a/Content.Server/SS220/FractWar/FractWarRuleComponent.cs b/Content.Server/SS220/FractWar/FractWarRuleComponent.cs index a89b3d92590902..399fb8ef3a2525 100644 --- a/Content.Server/SS220/FractWar/FractWarRuleComponent.cs +++ b/Content.Server/SS220/FractWar/FractWarRuleComponent.cs @@ -1,6 +1,7 @@ - +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt namespace Content.Server.SS220.FractWar; +[RegisterComponent] public sealed partial class FractWarRuleComponent : Component { } diff --git a/Content.Server/SS220/FractWar/FractWarRuleSystem.cs b/Content.Server/SS220/FractWar/FractWarRuleSystem.cs index 96691ec14f44db..3c5b176c44cd30 100644 --- a/Content.Server/SS220/FractWar/FractWarRuleSystem.cs +++ b/Content.Server/SS220/FractWar/FractWarRuleSystem.cs @@ -12,26 +12,27 @@ protected override void AppendRoundEndText(EntityUid uid, FractWarRuleComponent { base.AppendRoundEndText(uid, component, gameRule, ref args); + args.AddLine(Loc.GetString("fractwar-round-end-score-points")); + Dictionary fractionsWinPoints = new(); var capturePoints = EntityQueryEnumerator(); while (capturePoints.MoveNext(out _, out var capturePointComponent)) { foreach (var (fraction, retentionTime) in capturePointComponent.PointRetentionTime) { - var winPoints = (float)(retentionTime / capturePointComponent.RetentionTimeForWP) * capturePointComponent.WinPoints; + var winPoints = (float)(retentionTime.TotalSeconds / capturePointComponent.RetentionTimeForWP.TotalSeconds) * capturePointComponent.WinPoints; if (!fractionsWinPoints.TryAdd(fraction, winPoints)) fractionsWinPoints[fraction] += winPoints; } } - //Sort by value - fractionsWinPoints = fractionsWinPoints.OrderBy(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value); - foreach (var (fraction, winPoints) in fractionsWinPoints) { - args.AddLine(Loc.GetString("fract-war-round-end-fraction-points", ("fraction", Loc.GetString(fraction)), ("points", (int)winPoints))); + args.AddLine(Loc.GetString("fractwar-round-end-fraction-points", ("fraction", Loc.GetString(fraction)), ("points", (int)winPoints))); } - args.AddLine(Loc.GetString("fract-war-round-end-winner", ("fraction", Loc.GetString(fractionsWinPoints.First().Key)))); + //Sort by value + fractionsWinPoints = fractionsWinPoints.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value); + args.AddLine(Loc.GetString("fractwar-round-end-winner", ("fraction", Loc.GetString(fractionsWinPoints.First().Key)))); } } diff --git a/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs b/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs index aefe01517b4315..db37e4866f974b 100644 --- a/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs +++ b/Content.Shared/SS220/EventCapturePoint/EventCapturePointComponent.cs @@ -19,11 +19,17 @@ public sealed partial class EventCapturePointComponent : Component [ViewVariables, DataField] public float FlagRemovalImpulse = 35; + /// + /// How many points does this pedestal give + /// [DataField] - public float WinPoints = 0.1f; + public float WinPoints = 1f; + /// + /// Time to hold a capture point to get win points + /// [DataField] - public TimeSpan RetentionTimeForWP = TimeSpan.FromMinutes(1); + public TimeSpan RetentionTimeForWP = TimeSpan.FromSeconds(300); [ViewVariables] public Dictionary PointRetentionTime = new(); diff --git a/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs b/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs index d8f2a51f0f5a95..175daab589b3c3 100644 --- a/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs +++ b/Content.Shared/SS220/EventCapturePoint/EventCapturePointFlagComponent.cs @@ -13,6 +13,9 @@ public sealed partial class EventCapturePointFlagComponent : Component [ViewVariables, DataField, AutoNetworkedField] public bool Planted; + /// + /// The name of the faction to which this flag belongs + /// [DataField] public string? Fraction; } diff --git a/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl b/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl index 509fff23523587..d5f4ad8e500f09 100644 --- a/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl +++ b/Resources/Locale/ru-RU/ss220/game-ticking/game-presets/preset-FractWar.ftl @@ -1,2 +1,6 @@ -fract-war-round-end-fraction-points = { $fraction }: { $points } -fract-war-round-end-winner = { $fraction } становится победителем! +fractwar-preset-name = Война фракций +fractwar-preset-description = Ввиду неизвестных никому обстоятельств на бедной космической станции столкнутся три силы: Синдикат, НТ и СССП. Приведите свою фракцию к победе! + +fractwar-round-end-score-points = [color=yellow][font size=16]Итоговые очки![/font][/color] +fractwar-round-end-fraction-points = { $fraction } - { $points } +fractwar-round-end-winner = { $fraction } становится победителем! diff --git a/Resources/Locale/ru-RU/ss220/prototypes/entities/structures/specific/flags.ftl b/Resources/Locale/ru-RU/ss220/prototypes/entities/structures/specific/flags.ftl new file mode 100644 index 00000000000000..9757b55fba2975 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/prototypes/entities/structures/specific/flags.ftl @@ -0,0 +1,15 @@ +ent-EventCaptureFlagBase = флаг фракции + .desc = Флаг вашей фракции для захвата постаментов. + +ent-EventCaptureFlagNT = флаг НаноТрейзен + .desc = { ent-EventCaptureFlagBase.desc } + +ent-EventCaptureFlagSynd = флаг Синдиката + .desc = { ent-EventCaptureFlagBase.desc } + +ent-EventCaptureFlagUssp = флаг СССП + .desc = { ent-EventCaptureFlagBase.desc } + +flag-fraction-NT = [color=#046094]Нанотрейзен[/color] +flag-fraction-Synd = [color=crimson]Синдикат[/color] +flag-fraction-Ussp = [color=#e30000]СССП[/color] diff --git a/Resources/Prototypes/SS220/Entities/Structures/Specific/EventPointCapture/flags.yml b/Resources/Prototypes/SS220/Entities/Structures/Specific/EventPointCapture/flags.yml index f04750652cd85f..7161ac1fc1b301 100644 --- a/Resources/Prototypes/SS220/Entities/Structures/Specific/EventPointCapture/flags.yml +++ b/Resources/Prototypes/SS220/Entities/Structures/Specific/EventPointCapture/flags.yml @@ -2,7 +2,7 @@ id: EventCaptureFlagBase suffix: Shitspawn, Ebent, FractWar name: флаг фракции - description: Флаг вашей фракции для захвата постаментов. + description: The flag of your faction to capture the pedestals. abstract: true components: - type: Sprite @@ -50,10 +50,11 @@ - type: entity id: EventCaptureFlagNT suffix: Shitspawn, Ebent, FractWar - name: флаг НаноТрейзен - description: Флаг вашей фракции для захвата постаментов. + name: NanoTrazen flag parent: EventCaptureFlagBase components: + - type: EventCapturePointFlag + fraction: flag-fraction-NT - type: Sprite drawdepth: Overdoors noRot: true @@ -70,10 +71,11 @@ - type: entity id: EventCaptureFlagSynd suffix: Shitspawn, Ebent, FractWar - name: флаг Синдиката - description: Флаг вашей фракции для захвата постаментов. + name: syndicate flag parent: EventCaptureFlagBase components: + - type: EventCapturePointFlag + fraction: flag-fraction-Synd - type: Sprite drawdepth: Overdoors noRot: true @@ -90,10 +92,11 @@ - type: entity id: EventCaptureFlagUssp suffix: Shitspawn, Ebent, FractWar - name: флаг СССП - description: Флаг вашей фракции для захвата постаментов. + name: USSP flag parent: EventCaptureFlagBase components: + - type: EventCapturePointFlag + fraction: flag-fraction-Ussp - type: Sprite drawdepth: Overdoors noRot: true diff --git a/Resources/Prototypes/SS220/GameRules/roundstart.yml b/Resources/Prototypes/SS220/GameRules/roundstart.yml new file mode 100644 index 00000000000000..dab4f27fa2b4fc --- /dev/null +++ b/Resources/Prototypes/SS220/GameRules/roundstart.yml @@ -0,0 +1,7 @@ +- type: entity + parent: BaseGameRule + id: FractWar + components: + - type: LoadMapRule + mapPath: /Maps/SS220/EventShuttles/FractWarEbent/NT.yml + - type: FractWarRule diff --git a/Resources/Prototypes/SS220/game_presets.yml b/Resources/Prototypes/SS220/game_presets.yml index f98c69879b6998..1dcf775556bbf2 100644 --- a/Resources/Prototypes/SS220/game_presets.yml +++ b/Resources/Prototypes/SS220/game_presets.yml @@ -8,4 +8,15 @@ minPlayers: 30 rules: - DarkReaperSpawnMajor - - BasicStationEventScheduler \ No newline at end of file + - BasicStationEventScheduler + +- type: gamePreset + id: FractWar + alias: + - fractwar + name: fractwar-preset-name + description: fractwar-preset-description + showInVote: false + rules: + - FractWar + - BasicRoundstartVariation From 6d26efc123b046fb33e5062bf3d16c20222ff112 Mon Sep 17 00:00:00 2001 From: Kirus59 <145689588+Kirus59@users.noreply.github.com> Date: Tue, 24 Dec 2024 23:48:15 +0300 Subject: [PATCH 3/7] Add barricades --- .../SS220/Barricade/BarricadeSystem.cs | 52 ++++++++++++++++++ .../Projectiles/ProjectileComponent.cs | 12 +++++ .../Projectiles/SharedProjectileSystem.cs | 18 +++++++ .../SS220/Barricade/BarricadeComponent.cs | 25 +++++++++ .../SS220/Barricade/PassBarricadeComponent.cs | 12 +++++ .../SS220/Barricade/SharedBarricadeSystem.cs | 46 ++++++++++++++++ .../Weapons/Reflect/ReflectSystem.cs | 2 + .../SS220/Entities/Structures/barricade.yml | 53 ++++++++++++++----- 8 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 Content.Server/SS220/Barricade/BarricadeSystem.cs create mode 100644 Content.Shared/SS220/Barricade/BarricadeComponent.cs create mode 100644 Content.Shared/SS220/Barricade/PassBarricadeComponent.cs create mode 100644 Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs diff --git a/Content.Server/SS220/Barricade/BarricadeSystem.cs b/Content.Server/SS220/Barricade/BarricadeSystem.cs new file mode 100644 index 00000000000000..8c4bcc99509391 --- /dev/null +++ b/Content.Server/SS220/Barricade/BarricadeSystem.cs @@ -0,0 +1,52 @@ +using Content.Shared.Projectiles; +using Content.Shared.SS220.Barricade; +using Microsoft.Extensions.DependencyModel; +using Robust.Server.GameObjects; +using Robust.Shared.Random; + +namespace Content.Server.SS220.Barricade; + +public sealed partial class BarricadeSystem : SharedBarricadeSystem +{ + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + protected override void CalculateChance(Entity entity, Entity projEnt) + { + base.CalculateChance(entity, projEnt); + + var (uid, comp) = entity; + var (projUid, projComp) = projEnt; + + var passBarricade = EnsureComp(projUid); + if (passBarricade.CollideBarricades.ContainsKey(uid)) + return; + + float distance; + if (projComp.ShootGtidUid != null && projComp.ShootGridPos != null && projComp.ShootWorldPos != null) + { + var xform = Transform(entity); + var posdiff = xform.ParentUid == projComp.ShootGtidUid + ? xform.LocalPosition - projComp.ShootGridPos + : _transform.GetWorldPosition(uid) - projComp.ShootWorldPos; + + distance = posdiff.Value.Length(); + } + else + distance = comp.MaxDistance; + + var distanceDiff = comp.MaxDistance - comp.MinDistance; + var changeDiff = comp.MaxHitChance - comp.MinHitChance; + + /// How much the will increase. + var increaseChance = Math.Clamp(distance - comp.MinDistance, 0, distanceDiff) / distanceDiff * changeDiff; + + var hitChance = Math.Clamp(comp.MinHitChance + increaseChance, comp.MinHitChance, comp.MaxHitChance); + var isHit = _random.Prob(hitChance); + + passBarricade.CollideBarricades.Add(uid, isHit); + Dirty(projUid, passBarricade); + + return; + } +} diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index 8349252df2b718..cf71bc241af3a3 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -2,12 +2,24 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; +using System.Numerics; namespace Content.Shared.Projectiles; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ProjectileComponent : Component { + // SS220 add barricade begin + [ViewVariables, AutoNetworkedField] + public EntityUid? ShootGtidUid; + + [ViewVariables, AutoNetworkedField] + public Vector2? ShootGridPos; + + [ViewVariables, AutoNetworkedField] + public Vector2? ShootWorldPos; + // SS220 add barricade end + /// /// The angle of the fired projectile. /// diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 85e75d6d291324..5bc49a59c44ec6 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -80,6 +80,12 @@ private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent componen projectile.Shooter = null; projectile.Weapon = null; projectile.DamagedEntity = false; + + // SS220 add barricade begin + projectile.ShootGtidUid = null; + projectile.ShootGridPos = null; + projectile.ShootWorldPos = null; + // SS220 add barricade end } // Land it just coz uhhh yeah @@ -149,9 +155,21 @@ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid sh return; component.Shooter = shooterId; + SetShootPositions(id, component, shooterId); // SS220 add barricade Dirty(id, component); } + // SS220 add barricade begin + public void SetShootPositions(EntityUid uid, ProjectileComponent component, EntityUid shooterId) + { + var xform = Transform(uid); + component.ShootGtidUid = xform.ParentUid; + component.ShootGridPos = xform.LocalPosition; + component.ShootWorldPos = _transform.GetWorldPosition(shooterId); + Dirty(uid, component); + } + // SS220 add barricade end + [Serializable, NetSerializable] private sealed partial class RemoveEmbeddedProjectileEvent : DoAfterEvent { diff --git a/Content.Shared/SS220/Barricade/BarricadeComponent.cs b/Content.Shared/SS220/Barricade/BarricadeComponent.cs new file mode 100644 index 00000000000000..b6ba5252ac703d --- /dev/null +++ b/Content.Shared/SS220/Barricade/BarricadeComponent.cs @@ -0,0 +1,25 @@ + +using Robust.Shared.GameStates; + +namespace Content.Shared.SS220.Barricade; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedBarricadeSystem))] +public sealed partial class BarricadeComponent : Component +{ + /// + /// Chance to catch the projectile, if the distance between barricade and the shooter <= + /// + [DataField] + public float MinHitChance = 0; + /// + /// Chance to catch the projectile, if the distance between barricade and the shooter >= + /// + [DataField] + public float MaxHitChance = 0.7f; + + [DataField] + public float MinDistance = 1.5f; + [DataField] + public float MaxDistance = 10f; +} diff --git a/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs b/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs new file mode 100644 index 00000000000000..22b9a3926a3c1d --- /dev/null +++ b/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs @@ -0,0 +1,12 @@ + +using Robust.Shared.GameStates; + +namespace Content.Shared.SS220.Barricade; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedBarricadeSystem))] +public sealed partial class PassBarricadeComponent : Component +{ + [ViewVariables, AutoNetworkedField] + public Dictionary CollideBarricades = new(); +} diff --git a/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs new file mode 100644 index 00000000000000..6a04ad261bedeb --- /dev/null +++ b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs @@ -0,0 +1,46 @@ + +using Content.Shared.Projectiles; +using Robust.Shared.Physics.Events; + +namespace Content.Shared.SS220.Barricade; + +public abstract partial class SharedBarricadeSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPreventCollide); + + SubscribeLocalEvent(OnProjectileHit); + SubscribeLocalEvent(OnEndCollide); + } + + private void OnPreventCollide(Entity entity, ref PreventCollideEvent args) + { + if (!TryComp(args.OtherEntity, out var projectile)) + return; + + CalculateChance(entity, (args.OtherEntity, projectile)); + if (TryComp(args.OtherEntity, out var passBarricade) && + passBarricade.CollideBarricades.TryGetValue(entity.Owner, out var isHit) && + !isHit) + args.Cancelled = true; + } + + private void OnProjectileHit(Entity entity, ref ProjectileHitEvent args) + { + entity.Comp.CollideBarricades.Clear(); + } + + private void OnEndCollide(Entity entity, ref EndCollideEvent args) + { + if (HasComp(args.OtherEntity)) + entity.Comp.CollideBarricades.Remove(args.OtherEntity); + } + + protected virtual void CalculateChance(Entity entity, Entity projEnt) + { + + } +} diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index d0d366a0d95c24..02e2ca6658f8b0 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -39,6 +39,7 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly SharedProjectileSystem _projectileSystem = default!; // SS220 add barricade public override void Initialize() { @@ -131,6 +132,7 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectileComp.Shooter = user; projectileComp.Weapon = user; + _projectileSystem.SetShootPositions(projectile, projectileComp, user); // SS220 add barricade Dirty(projectile, projectileComp); } else diff --git a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml index 49e50790647d93..68db36f313a6ba 100644 --- a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml +++ b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml @@ -24,7 +24,7 @@ - FullTileMask layer: - WallLayer - + - type: entity id: BaseBarricadeDirectional220 description: A barricade made out of wood planks. It looks like it can take a few solid hits. @@ -141,7 +141,7 @@ max: 2 - !type:DoActsBehavior acts: [ "Destruction" ] - + - type: entity id: BarricadePlasteel description: Баррикада из листов пластали. Защищает вас от чужих глаз. Способна выдержать взрыв C4. @@ -174,11 +174,11 @@ max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] - + #Направленные баррикады - type: entity id: BarricadeWoodenDirectional - description: Баррикада из уплотнённых деревянных досок. Защищает вас от чужих глаз. + description: Баррикада из уплотнённых деревянных досок. Защищает вас от чужих глаз. parent: BaseBarricadeDirectional220 name: уплотнённая деревянная баррикада placement: @@ -196,7 +196,7 @@ fireSpread: true damage: types: - Heat: 2 + Heat: 2 - type: FireVisuals sprite: Effects/fire.rsi normalState: 1 @@ -216,7 +216,7 @@ max: 2 - !type:DoActsBehavior acts: [ "Destruction" ] - + - type: entity id: BarricadeSteelDirectional description: Баррикада из стальных листов. Защищает вас от чужих глаз. Способна выдержать пулю. @@ -241,7 +241,7 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] - + - type: entity id: BarricadePlasteelDirectional description: Баррикада из листов пластали. Защищает вас от чужих глаз. Способна выдержать взрыв C4. @@ -274,7 +274,7 @@ max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] - + #Напольные баррикады - type: entity id: BarricadeWoodenFloor @@ -296,10 +296,10 @@ fireSpread: true damage: types: - Heat: 2 + Heat: 2 - type: FireVisuals sprite: Effects/fire.rsi - normalState: 1 + normalState: 1 - type: Damageable damageModifierSet: Wood damageContainer: StructuralInorganic @@ -311,7 +311,7 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] - + - type: entity id: BarricadeSteelFloor description: Баррикада небольшой высоты из листов стали. Есть шанс защитить от шальной пули. @@ -336,7 +336,7 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] - + - type: entity id: BarricadePlasteelFloor description: Баррикада небольшой высоты из листов пластали. Есть шанс защитить от шальной пули. @@ -360,4 +360,31 @@ damage: 160 behaviors: - !type:DoActsBehavior - acts: [ "Destruction" ] \ No newline at end of file + acts: [ "Destruction" ] + +- type: entity + parent: BarricadePlasteelFloor + id: FoldingBarricadePlasteelFloor + name: напольная укреплённая баррикада + description: Баррикада небольшой высоты из листов пластали. Есть шанс защитить от шальной пули. + placement: + mode: SnapgridCenter + components: + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb {} + mask: + - FullTileMask + layer: + - GlassLayer + - type: Barricade + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1600 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] From b08af6d0b0e3166dcbf9d80d75777e18ea33088b Mon Sep 17 00:00:00 2001 From: Kirus59 <145689588+Kirus59@users.noreply.github.com> Date: Wed, 25 Dec 2024 17:46:16 +0300 Subject: [PATCH 4/7] Add hitscan to catch by barricade --- .../SS220/Barricade/BarricadeSystem.cs | 60 +++++++++++++------ .../Weapons/Ranged/Systems/GunSystem.cs | 20 +++++-- .../Projectiles/ProjectileComponent.cs | 2 +- .../Projectiles/SharedProjectileSystem.cs | 4 +- .../SS220/Barricade/BarricadeComponent.cs | 4 +- .../SS220/Barricade/PassBarricadeComponent.cs | 2 +- .../SS220/Barricade/SharedBarricadeSystem.cs | 35 ++++++++--- .../Weapons/Ranged/Events/HitscanAttempt.cs | 5 ++ .../SS220/Entities/Structures/barricade.yml | 7 ++- 9 files changed, 100 insertions(+), 39 deletions(-) create mode 100644 Content.Shared/SS220/Weapons/Ranged/Events/HitscanAttempt.cs diff --git a/Content.Server/SS220/Barricade/BarricadeSystem.cs b/Content.Server/SS220/Barricade/BarricadeSystem.cs index 8c4bcc99509391..71fd5ee2ab2557 100644 --- a/Content.Server/SS220/Barricade/BarricadeSystem.cs +++ b/Content.Server/SS220/Barricade/BarricadeSystem.cs @@ -1,8 +1,10 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt using Content.Shared.Projectiles; using Content.Shared.SS220.Barricade; using Microsoft.Extensions.DependencyModel; using Robust.Server.GameObjects; using Robust.Shared.Random; +using System.Numerics; namespace Content.Server.SS220.Barricade; @@ -11,42 +13,64 @@ public sealed partial class BarricadeSystem : SharedBarricadeSystem [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _random = default!; - protected override void CalculateChance(Entity entity, Entity projEnt) + protected override bool HitscanTryPassBarricade(Entity entity, EntityUid source, TransformComponent? sourceXform = null) { - base.CalculateChance(entity, projEnt); + if (!Resolve(source, ref sourceXform)) + return false; + var hitChance = CalculateHitChance(entity, sourceXform.GridUid, sourceXform.LocalPosition, _transform.GetWorldPosition(source)); + var isHit = _random.Prob(hitChance); + + return !isHit; + } + + protected override bool ProjectileTryPassBarricade(Entity entity, Entity projEnt) + { var (uid, comp) = entity; var (projUid, projComp) = projEnt; var passBarricade = EnsureComp(projUid); - if (passBarricade.CollideBarricades.ContainsKey(uid)) - return; + if (passBarricade.CollideBarricades.TryGetValue(uid, out var isPass)) + return isPass; + + var hitChance = CalculateHitChance(entity, projComp.ShootGridUid, projComp.ShootGridPos, projComp.ShootWorldPos); + var isHit = _random.Prob(hitChance); + + passBarricade.CollideBarricades.Add(uid, !isHit); + Dirty(projUid, passBarricade); + + return !isHit; + } + + private float CalculateHitChance(Entity entity, EntityUid? gridUid = null, Vector2? gridPos = null, Vector2? worldPos = null) + { + var (uid, comp) = entity; + var xform = Transform(entity); float distance; - if (projComp.ShootGtidUid != null && projComp.ShootGridPos != null && projComp.ShootWorldPos != null) + if (gridUid != null && gridPos != null && xform.ParentUid == gridUid) { - var xform = Transform(entity); - var posdiff = xform.ParentUid == projComp.ShootGtidUid - ? xform.LocalPosition - projComp.ShootGridPos - : _transform.GetWorldPosition(uid) - projComp.ShootWorldPos; - - distance = posdiff.Value.Length(); + var posDiff = xform.LocalPosition - gridPos; + distance = posDiff.Value.Length(); + } + else if (worldPos != null) + { + var posDiff = _transform.GetWorldPosition(uid) - worldPos; + distance = posDiff.Value.Length(); } else + { distance = comp.MaxDistance; + } var distanceDiff = comp.MaxDistance - comp.MinDistance; - var changeDiff = comp.MaxHitChance - comp.MinHitChance; + var chanceDiff = comp.MaxHitChance - comp.MinHitChance; /// How much the will increase. - var increaseChance = Math.Clamp(distance - comp.MinDistance, 0, distanceDiff) / distanceDiff * changeDiff; + var increaseChance = Math.Clamp(distance - comp.MinDistance, 0, distanceDiff) / distanceDiff * chanceDiff; var hitChance = Math.Clamp(comp.MinHitChance + increaseChance, comp.MinHitChance, comp.MaxHitChance); - var isHit = _random.Prob(hitChance); - - passBarricade.CollideBarricades.Add(uid, isHit); - Dirty(projUid, passBarricade); - return; + return hitChance; } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 22d62cbb306922..1ea78afd4f7864 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -22,6 +22,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; using Robust.Shared.Containers; +using Content.Shared.SS220.Weapons.Ranged.Events; namespace Content.Server.Weapons.Ranged.Systems; @@ -184,11 +185,22 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? { var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask); var rayCastResults = - Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList(); + Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList(). + Where(x => // SS220 add barricade begin + { + var attemptEv = new HitscanAttempt(lastUser); + RaiseLocalEvent(x.HitEntity, ref attemptEv); + return !attemptEv.Cancelled; + }); // SS220 add barricade end if (!rayCastResults.Any()) break; - var result = rayCastResults[0]; + // SS220 add barricade begin + //var result = rayCastResults[0]; + var result = rayCastResults.FirstOrNull(); + if (result is null) + break; + // SS220 add barricade end // Check if laser is shot from in a container if (!_container.IsEntityOrParentInContainer(lastUser)) @@ -207,10 +219,10 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? } } - var hit = result.HitEntity; + var hit = result.Value.HitEntity; // SS220 add barricade lastHit = hit; - FireEffects(fromEffect, result.Distance, dir.Normalized().ToAngle(), hitscan, hit); + FireEffects(fromEffect, result.Value.Distance, dir.Normalized().ToAngle(), hitscan, hit); // SS220 add barricade var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false); RaiseLocalEvent(hit, ref ev); diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index cf71bc241af3a3..acd2db347e6024 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -11,7 +11,7 @@ public sealed partial class ProjectileComponent : Component { // SS220 add barricade begin [ViewVariables, AutoNetworkedField] - public EntityUid? ShootGtidUid; + public EntityUid? ShootGridUid; [ViewVariables, AutoNetworkedField] public Vector2? ShootGridPos; diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 5bc49a59c44ec6..9a1b0364b366ef 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -82,7 +82,7 @@ private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent componen projectile.DamagedEntity = false; // SS220 add barricade begin - projectile.ShootGtidUid = null; + projectile.ShootGridUid = null; projectile.ShootGridPos = null; projectile.ShootWorldPos = null; // SS220 add barricade end @@ -163,7 +163,7 @@ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid sh public void SetShootPositions(EntityUid uid, ProjectileComponent component, EntityUid shooterId) { var xform = Transform(uid); - component.ShootGtidUid = xform.ParentUid; + component.ShootGridUid = xform.ParentUid; component.ShootGridPos = xform.LocalPosition; component.ShootWorldPos = _transform.GetWorldPosition(shooterId); Dirty(uid, component); diff --git a/Content.Shared/SS220/Barricade/BarricadeComponent.cs b/Content.Shared/SS220/Barricade/BarricadeComponent.cs index b6ba5252ac703d..fa32c7ac9e2faf 100644 --- a/Content.Shared/SS220/Barricade/BarricadeComponent.cs +++ b/Content.Shared/SS220/Barricade/BarricadeComponent.cs @@ -1,4 +1,4 @@ - +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt using Robust.Shared.GameStates; namespace Content.Shared.SS220.Barricade; @@ -21,5 +21,5 @@ public sealed partial class BarricadeComponent : Component [DataField] public float MinDistance = 1.5f; [DataField] - public float MaxDistance = 10f; + public float MaxDistance = 9f; } diff --git a/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs b/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs index 22b9a3926a3c1d..296ffeb7c6ff41 100644 --- a/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs +++ b/Content.Shared/SS220/Barricade/PassBarricadeComponent.cs @@ -1,4 +1,4 @@ - +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt using Robust.Shared.GameStates; namespace Content.Shared.SS220.Barricade; diff --git a/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs index 6a04ad261bedeb..88e69c8fa5e902 100644 --- a/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs +++ b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs @@ -1,5 +1,7 @@ - +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt using Content.Shared.Projectiles; +using Content.Shared.SS220.Weapons.Ranged.Events; +using Content.Shared.Throwing; using Robust.Shared.Physics.Events; namespace Content.Shared.SS220.Barricade; @@ -11,23 +13,31 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnPreventCollide); + SubscribeLocalEvent(OnHitscanAttempt); + SubscribeLocalEvent(OnLand); SubscribeLocalEvent(OnProjectileHit); SubscribeLocalEvent(OnEndCollide); } private void OnPreventCollide(Entity entity, ref PreventCollideEvent args) { - if (!TryComp(args.OtherEntity, out var projectile)) - return; + if (TryComp(args.OtherEntity, out var projectile) && + ProjectileTryPassBarricade(entity, (args.OtherEntity, projectile))) + args.Cancelled = true; + } - CalculateChance(entity, (args.OtherEntity, projectile)); - if (TryComp(args.OtherEntity, out var passBarricade) && - passBarricade.CollideBarricades.TryGetValue(entity.Owner, out var isHit) && - !isHit) + private void OnHitscanAttempt(Entity entity, ref HitscanAttempt args) + { + if (HitscanTryPassBarricade(entity, args.User)) args.Cancelled = true; } + private void OnLand(Entity entity, ref LandEvent args) + { + entity.Comp.CollideBarricades.Clear(); + } + private void OnProjectileHit(Entity entity, ref ProjectileHitEvent args) { entity.Comp.CollideBarricades.Clear(); @@ -39,8 +49,17 @@ private void OnEndCollide(Entity entity, ref EndCollideE entity.Comp.CollideBarricades.Remove(args.OtherEntity); } - protected virtual void CalculateChance(Entity entity, Entity projEnt) + protected virtual bool HitscanTryPassBarricade(Entity entity, EntityUid source, TransformComponent? sourceXform = null) + { + return false; + } + + protected virtual bool ProjectileTryPassBarricade(Entity entity, Entity projEnt) { + if (TryComp(projEnt.Owner, out var passBarricade) && + passBarricade.CollideBarricades.TryGetValue(entity.Owner, out var isPass)) + return isPass; + return false; } } diff --git a/Content.Shared/SS220/Weapons/Ranged/Events/HitscanAttempt.cs b/Content.Shared/SS220/Weapons/Ranged/Events/HitscanAttempt.cs new file mode 100644 index 00000000000000..eb63efe0a733db --- /dev/null +++ b/Content.Shared/SS220/Weapons/Ranged/Events/HitscanAttempt.cs @@ -0,0 +1,5 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +namespace Content.Shared.SS220.Weapons.Ranged.Events; + +[ByRefEvent] +public record struct HitscanAttempt(EntityUid User, bool Cancelled = false); diff --git a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml index 68db36f313a6ba..dfe3f3f1a0dc14 100644 --- a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml +++ b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml @@ -374,11 +374,12 @@ fixtures: fix1: shape: - !type:PhysShapeAabb {} + !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,-0.36" mask: - - FullTileMask + - TableMask layer: - - GlassLayer + - WallLayer - type: Barricade - type: Destructible thresholds: From 40a89761ca58c9c556419febe6241a7b0effc183 Mon Sep 17 00:00:00 2001 From: Kirus59 <145689588+Kirus59@users.noreply.github.com> Date: Thu, 2 Jan 2025 13:11:44 +0300 Subject: [PATCH 5/7] PlacementOnInteract --- .../PlacementOnInteractComponent.cs | 18 +++++ .../PlacementOnInteractSystem.cs | 76 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs create mode 100644 Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs diff --git a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs new file mode 100644 index 00000000000000..fdcbf0ce9a965c --- /dev/null +++ b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.SS220.PlacementOnInteract; + +[RegisterComponent, NetworkedComponent] +public sealed partial class PlacementOnInteractComponent : Component +{ + [ViewVariables] + public bool IsActive = false; + + [DataField(required: true)] + public EntProtoId ProtoId; + + [DataField] + public float DoAfter = 0; +} diff --git a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs new file mode 100644 index 00000000000000..63e8dd6280cd23 --- /dev/null +++ b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs @@ -0,0 +1,76 @@ +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.PlacementOnInteract; + +public sealed partial class PlacementOnInteractSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteractUsing); + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnAfterInteractUsing(Entity entity, ref AfterInteractUsingEvent args) + { + entity.Comp.IsActive = !entity.Comp.IsActive; + } + + private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || !entity.Comp.IsActive) + return; + + var (uid, comp) = entity; + var user = args.User; + var location = args.ClickLocation; + + if (!location.IsValid(EntityManager)) + return; + + Direction direction = 0; + + var doAfterTime = TimeSpan.FromSeconds(comp.DoAfter); + var ev = new PlacementOnInteractDoAfterEvent(GetNetCoordinates(location), direction, comp.ProtoId); + + var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, ev, uid, args.Target, uid) + { + BreakOnDamage = true, + BreakOnHandChange = true, + BreakOnMove = true, + AttemptFrequency = AttemptFrequency.EveryTick, + CancelDuplicate = false, + BlockDuplicate = false + }; + + _doAfter.TryStartDoAfter(doAfterArgs); + args.Handled = true; + } +} + +[Serializable, NetSerializable] +public sealed partial class PlacementOnInteractDoAfterEvent : DoAfterEvent +{ + public NetCoordinates Coordinates; + + public Direction Direction; + + public EntProtoId ProtoId; + + public PlacementOnInteractDoAfterEvent(NetCoordinates coordinates, Direction direction, EntProtoId protoId) + { + Coordinates = coordinates; + Direction = direction; + ProtoId = protoId; + } + + public override DoAfterEvent Clone() => this; +} From 7b6d4c31efe5ba0fcf3b7ea8f40c71a037817f2d Mon Sep 17 00:00:00 2001 From: Kirus59 <145689588+Kirus59@users.noreply.github.com> Date: Fri, 3 Jan 2025 03:30:51 +0300 Subject: [PATCH 6/7] PlacerItem tweaks --- .../PlacerItem/AlignPlacerItemConstrucrion.cs | 112 +++++++++++ .../PlacerItemConstructionGhostSystem.cs | 69 +++++++ .../PlacementOnInteractComponent.cs | 18 -- .../PlacementOnInteractSystem.cs | 76 ------- .../SS220/PlacerItem/PlacerItemComponent.cs | 40 ++++ .../SS220/PlacerItem/PlacerItemEvents.cs | 12 ++ .../SS220/PlacerItem/PlacerItemSystem.cs | 188 ++++++++++++++++++ Content.Shared/Tag/TagComponent.cs | 3 +- .../SS220/Entities/Structures/barricade.yml | 23 +++ 9 files changed, 446 insertions(+), 95 deletions(-) create mode 100644 Content.Client/SS220/PlacerItem/AlignPlacerItemConstrucrion.cs create mode 100644 Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs delete mode 100644 Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs delete mode 100644 Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs create mode 100644 Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs create mode 100644 Content.Shared/SS220/PlacerItem/PlacerItemEvents.cs create mode 100644 Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs diff --git a/Content.Client/SS220/PlacerItem/AlignPlacerItemConstrucrion.cs b/Content.Client/SS220/PlacerItem/AlignPlacerItemConstrucrion.cs new file mode 100644 index 00000000000000..c0cb52419c39bc --- /dev/null +++ b/Content.Client/SS220/PlacerItem/AlignPlacerItemConstrucrion.cs @@ -0,0 +1,112 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Client.Gameplay; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.RCD.Systems; +using Content.Shared.SS220.PlacerItem.Components; +using Content.Shared.SS220.PlacerItem.Systems; +using Robust.Client.Placement; +using Robust.Client.Player; +using Robust.Client.State; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using System.Numerics; + +namespace Content.Client.SS220.PlacerItem; + +public sealed class AlignPlacerItemConstrucrion : PlacementMode +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + + private readonly RCDSystem _rcdSystem; + private readonly PlacerItemSystem _placerItemSystem; + private readonly SharedTransformSystem _transformSystem; + private readonly SharedMapSystem _mapSystem; + + private const float SearchBoxSize = 2f; + private const float PlaceColorBaseAlpha = 0.5f; + + private EntityCoordinates _unalignedMouseCoords = default; + + public AlignPlacerItemConstrucrion(PlacementManager pMan) : base(pMan) + { + IoCManager.InjectDependencies(this); + _rcdSystem = _entityManager.System(); + _placerItemSystem = _entityManager.System(); + _transformSystem = _entityManager.System(); + _mapSystem = _entityManager.System(); + + ValidPlaceColor = ValidPlaceColor.WithAlpha(PlaceColorBaseAlpha); + } + + public override void AlignPlacementMode(ScreenCoordinates mouseScreen) + { + _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen); + MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager); + + var gridId = _transformSystem.GetGrid(MouseCoords); + if (!_entityManager.TryGetComponent(gridId, out var mapGrid)) + return; + + CurrentTile = _mapSystem.GetTileRef(gridId.Value, mapGrid, MouseCoords); + + float tileSize = mapGrid.TileSize; + GridDistancing = tileSize; + + if (pManager.CurrentPermission!.IsTile) + { + MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2, + CurrentTile.Y + tileSize / 2)); + } + else + { + MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2 + pManager.PlacementOffset.X, + CurrentTile.Y + tileSize / 2 + pManager.PlacementOffset.Y)); + } + } + + public override bool IsValidPosition(EntityCoordinates position) + { + var player = _playerManager.LocalSession?.AttachedEntity; + + // If the destination is out of interaction range, set the placer alpha to zero + if (!_entityManager.TryGetComponent(player, out var xform)) + return false; + + if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange)) + { + InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0); + return false; + } + // Otherwise restore the alpha value + else + { + InvalidPlaceColor = InvalidPlaceColor.WithAlpha(PlaceColorBaseAlpha); + } + + if (!_entityManager.TryGetComponent(player, out var hands)) + return false; + + var heldEntity = hands.ActiveHand?.HeldEntity; + + if (!_entityManager.TryGetComponent(heldEntity, out var placerItemComp)) + return false; + + if (!_rcdSystem.TryGetMapGridData(position, out var mapGridData)) + return false; + + // Determine if the user is hovering over a target + var currentState = _stateManager.CurrentState; + + if (currentState is not GameplayStateBase screen) + return false; + + var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords)); + + return _placerItemSystem.IsPlacementOperationStillValid((heldEntity.Value, placerItemComp), mapGridData.Value, target, player.Value); + } +} diff --git a/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs b/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs new file mode 100644 index 00000000000000..290d139b177d69 --- /dev/null +++ b/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs @@ -0,0 +1,69 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.SS220.PlacerItem; +using Content.Shared.SS220.PlacerItem.Components; +using Robust.Client.Placement; +using Robust.Client.Player; +using Robust.Shared.Enums; + +namespace Content.Client.SS220.PlacerItem; + +public sealed class PlacerItemConstructionGhostSystem : EntitySystem +{ + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IPlacementManager _placementManager = default!; + + private string _placementMode = typeof(AlignPlacerItemConstrucrion).Name; + private Direction _placementDirection = default; + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var placerEntity = _placementManager.CurrentPermission?.MobUid; + var placerProto = _placementManager.CurrentPermission?.EntityType; + var placerIsPlacerItem = HasComp(placerEntity); + + if (_placementManager.Eraser || + (placerEntity != null && !placerIsPlacerItem)) + return; + + var player = _playerManager.LocalSession?.AttachedEntity; + if (!TryComp(player, out var hands)) + return; + + var heldEntity = hands.ActiveHand?.HeldEntity; + if (!TryComp(heldEntity, out var comp) || !comp.Active) + { + if (placerIsPlacerItem) + _placementManager.Clear(); + + return; + } + + // Update the direction + if (_placementDirection != _placementManager.Direction) + { + _placementDirection = _placementManager.Direction; + RaiseNetworkEvent(new PlacerItemUpdateDirectionEvent(GetNetEntity(heldEntity.Value), _placementDirection)); + } + + if (heldEntity == placerEntity && placerProto == comp.ProtoId.Id) + return; + + var newObjInfo = new PlacementInformation + { + MobUid = heldEntity.Value, + EntityType = comp.ProtoId.Id, + PlacementOption = _placementMode, + Range = (int)Math.Ceiling(SharedInteractionSystem.InteractionRange), + IsTile = false, + UseEditorContext = false, + }; + + _placementManager.Clear(); + _placementManager.BeginPlacing(newObjInfo); + } +} diff --git a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs deleted file mode 100644 index fdcbf0ce9a965c..00000000000000 --- a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.SS220.PlacementOnInteract; - -[RegisterComponent, NetworkedComponent] -public sealed partial class PlacementOnInteractComponent : Component -{ - [ViewVariables] - public bool IsActive = false; - - [DataField(required: true)] - public EntProtoId ProtoId; - - [DataField] - public float DoAfter = 0; -} diff --git a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs b/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs deleted file mode 100644 index 63e8dd6280cd23..00000000000000 --- a/Content.Shared/SS220/PlacementOnInteract/PlacementOnInteractSystem.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Content.Shared.DoAfter; -using Content.Shared.Interaction; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; - -namespace Content.Shared.SS220.PlacementOnInteract; - -public sealed partial class PlacementOnInteractSystem : EntitySystem -{ - [Dependency] private readonly SharedTransformSystem _xform = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAfterInteractUsing); - SubscribeLocalEvent(OnAfterInteract); - } - - private void OnAfterInteractUsing(Entity entity, ref AfterInteractUsingEvent args) - { - entity.Comp.IsActive = !entity.Comp.IsActive; - } - - private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (args.Handled || !args.CanReach || !entity.Comp.IsActive) - return; - - var (uid, comp) = entity; - var user = args.User; - var location = args.ClickLocation; - - if (!location.IsValid(EntityManager)) - return; - - Direction direction = 0; - - var doAfterTime = TimeSpan.FromSeconds(comp.DoAfter); - var ev = new PlacementOnInteractDoAfterEvent(GetNetCoordinates(location), direction, comp.ProtoId); - - var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, ev, uid, args.Target, uid) - { - BreakOnDamage = true, - BreakOnHandChange = true, - BreakOnMove = true, - AttemptFrequency = AttemptFrequency.EveryTick, - CancelDuplicate = false, - BlockDuplicate = false - }; - - _doAfter.TryStartDoAfter(doAfterArgs); - args.Handled = true; - } -} - -[Serializable, NetSerializable] -public sealed partial class PlacementOnInteractDoAfterEvent : DoAfterEvent -{ - public NetCoordinates Coordinates; - - public Direction Direction; - - public EntProtoId ProtoId; - - public PlacementOnInteractDoAfterEvent(NetCoordinates coordinates, Direction direction, EntProtoId protoId) - { - Coordinates = coordinates; - Direction = direction; - ProtoId = protoId; - } - - public override DoAfterEvent Clone() => this; -} diff --git a/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs b/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs new file mode 100644 index 00000000000000..99a063f6f76427 --- /dev/null +++ b/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs @@ -0,0 +1,40 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.GameStates; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.PlacerItem.Components; + +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] +public sealed partial class PlacerItemComponent : Component +{ + [ViewVariables, AutoNetworkedField] + public bool Active = false; + + [ViewVariables, AutoNetworkedField] + public Direction ConstructionDirection + { + get => _constructionDirection; + set + { + _constructionDirection = value; + ConstructionTransform = new Transform(new(), _constructionDirection.ToAngle()); + } + } + + private Direction _constructionDirection = Direction.South; + + [ViewVariables(VVAccess.ReadOnly)] + public Transform ConstructionTransform { get; private set; } = default!; + + [DataField(required: true)] + public EntProtoId ProtoId; + + [DataField] + public float DoAfter = 0; + + [DataField] + public bool ToggleActiveOnUseInHand = false; +} diff --git a/Content.Shared/SS220/PlacerItem/PlacerItemEvents.cs b/Content.Shared/SS220/PlacerItem/PlacerItemEvents.cs new file mode 100644 index 00000000000000..9b92a65916e082 --- /dev/null +++ b/Content.Shared/SS220/PlacerItem/PlacerItemEvents.cs @@ -0,0 +1,12 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.PlacerItem; + +[Serializable, NetSerializable] +public sealed class PlacerItemUpdateDirectionEvent(NetEntity netEntity, Direction direction) : EntityEventArgs +{ + public readonly NetEntity NetEntity = netEntity; + public readonly Direction Direction = direction; +} diff --git a/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs b/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs new file mode 100644 index 00000000000000..483953cca66f35 --- /dev/null +++ b/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs @@ -0,0 +1,188 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Construction; +using Content.Shared.DoAfter; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.RCD.Systems; +using Content.Shared.SS220.PlacerItem.Components; +using Content.Shared.Tag; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.PlacerItem.Systems; + +public sealed partial class PlacerItemSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IComponentFactory _factory = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly RCDSystem _rcdSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnAfterInteract); + + SubscribeNetworkEvent(OnUpdateDirection); + } + + private void OnUseInHand(Entity entity, ref UseInHandEvent args) + { + if (args.Handled || !entity.Comp.ToggleActiveOnUseInHand) + return; + + SetActive(entity, !entity.Comp.Active); + args.Handled = true; + } + + private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || !entity.Comp.Active) + return; + + var (uid, comp) = entity; + var user = args.User; + var location = args.ClickLocation; + + if (!location.IsValid(EntityManager)) + return; + + if (!_rcdSystem.TryGetMapGridData(location, out var mapGridData)) + return; + + if (!IsPlacementOperationStillValid(entity, mapGridData.Value, args.Target, user)) + return; + + var doAfterTime = TimeSpan.FromSeconds(comp.DoAfter); + var ev = new PlacementOnInteractDoAfterEvent(GetNetCoordinates(mapGridData.Value.Location), comp.ConstructionDirection, comp.ProtoId); + + var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, ev, uid, args.Target, uid) + { + BreakOnDamage = true, + BreakOnHandChange = true, + BreakOnMove = true, + AttemptFrequency = AttemptFrequency.EveryTick, + CancelDuplicate = false, + BlockDuplicate = false + }; + + _doAfter.TryStartDoAfter(doAfterArgs); + args.Handled = true; + } + + private void OnUpdateDirection(PlacerItemUpdateDirectionEvent ev, EntitySessionEventArgs session) + { + var uid = GetEntity(ev.NetEntity); + var user = session.SenderSession.AttachedEntity; + + if (user == null) + return; + + if (!TryComp(user, out var hands) || + uid != hands.ActiveHand?.HeldEntity) + return; + + if (!TryComp(uid, out var comp)) + return; + + comp.ConstructionDirection = ev.Direction; + Dirty(uid, comp); + } + + private void SetActive(Entity entity, bool value) + { + entity.Comp.Active = value; + Dirty(entity); + } + + public bool IsPlacementOperationStillValid(Entity entity, MapGridData mapGridData, EntityUid? target, EntityUid user, bool popMsgs = true) + { + var (uid, comp) = entity; + var unobstracted = target == null + ? _interaction.InRangeUnobstructed(user, _mapSystem.GridTileToWorld(mapGridData.GridUid, mapGridData.Component, mapGridData.Position), popup: popMsgs) + : _interaction.InRangeUnobstructed(user, target.Value, popup: popMsgs); + + if (!unobstracted) + return false; + + if (!_prototypeManager.TryIndex(comp.ProtoId.Id, out var prototype)) + return false; + + prototype.TryGetComponent(_factory.GetComponentName(), out var tagComponent); + var isWindow = tagComponent?.Tags != null && tagComponent.Tags.Contains("Window"); + var isCatwalk = tagComponent?.Tags != null && tagComponent.Tags.Contains("Catwalk"); + + var intersectingEntities = _lookup.GetLocalEntitiesIntersecting(mapGridData.GridUid, mapGridData.Position, -0.05f, LookupFlags.Uncontained); + + foreach (var ent in intersectingEntities) + { + if (isWindow && HasComp(ent)) + continue; + + if (isCatwalk && _tag.HasTag(ent, "Catwalk")) + { + return false; + } + + if (prototype.TryGetComponent(_factory.GetComponentName(), out var protoFixtures) && + TryComp(ent, out var entFixtures) && + IsOurEntWillCollideWithOtherEnt(comp.ConstructionTransform, protoFixtures, ent, entFixtures)) + return false; + } + + return true; + } + + private bool IsOurEntWillCollideWithOtherEnt(Transform ourXform, FixturesComponent ourFixtures, EntityUid otherUid, FixturesComponent otherFixtures) + { + foreach (var ourFixture in ourFixtures.Fixtures.Values) + { + if (!ourFixture.Hard || ourFixture.CollisionLayer <= 0) + continue; + + foreach (var otherFixture in otherFixtures.Fixtures.Values) + { + if (!otherFixture.Hard || otherFixture.CollisionLayer <= 0 || + ourFixture.CollisionLayer != otherFixture.CollisionLayer) + continue; + + var otherXformComp = Transform(otherUid); + var otherXform = new Transform(new(), otherXformComp.LocalRotation); + + if (ourFixture.Shape.ComputeAABB(ourXform, 0).Intersects(otherFixture.Shape.ComputeAABB(otherXform, 0))) + return true; + } + } + + return false; + } +} + +[Serializable, NetSerializable] +public sealed partial class PlacementOnInteractDoAfterEvent : DoAfterEvent +{ + public NetCoordinates Coordinates; + + public Direction Direction; + + public EntProtoId ProtoId; + + public PlacementOnInteractDoAfterEvent(NetCoordinates coordinates, Direction direction, EntProtoId protoId) + { + Coordinates = coordinates; + Direction = direction; + ProtoId = protoId; + } + + public override DoAfterEvent Clone() => this; +} diff --git a/Content.Shared/Tag/TagComponent.cs b/Content.Shared/Tag/TagComponent.cs index ad4240ba06c724..73d246fb8058dc 100644 --- a/Content.Shared/Tag/TagComponent.cs +++ b/Content.Shared/Tag/TagComponent.cs @@ -1,4 +1,4 @@ -using Robust.Shared.GameStates; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; namespace Content.Shared.Tag; @@ -7,5 +7,6 @@ namespace Content.Shared.Tag; public sealed partial class TagComponent : Component { [DataField, ViewVariables, AutoNetworkedField] + [Access(Other = AccessPermissions.ReadExecute)] // SS220 Change permissions public HashSet> Tags = new(); } diff --git a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml index dfe3f3f1a0dc14..5cb5af8a2e0f39 100644 --- a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml +++ b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml @@ -362,6 +362,29 @@ - !type:DoActsBehavior acts: [ "Destruction" ] +- type: entity + parent: BaseItem + id: PlacementOnInteractTest + name: base flatpack + description: A flatpack used for constructing something. + components: + - type: Item + size: Large + - type: Sprite + sprite: Objects/Devices/flatpack.rsi + layers: + - state: base + - state: overlay + color: "#cec8ac" + map: ["enum.FlatpackVisualLayers.Overlay"] + - state: icon-default + - type: Appearance + - type: PlacerItem + protoId: FoldingBarricadePlasteelFloor + toggleActiveOnUseInHand: true + - type: StaticPrice + price: 250 + - type: entity parent: BarricadePlasteelFloor id: FoldingBarricadePlasteelFloor From 00c7eda7fcfc5e2fee28bc5036796d916669e1d2 Mon Sep 17 00:00:00 2001 From: Kirus59 <145689588+Kirus59@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:06:59 +0300 Subject: [PATCH 7/7] PlacerItem fixes + add barricade sprites --- .../PlacerItemConstructionGhostSystem.cs | 7 +- .../Components/SpawnOnDespawnComponent.cs | 5 + .../EntitySystems/SpawnOnDespawnSystem.cs | 16 ++- .../SS220/Barricade/BarricadeComponent.cs | 10 ++ .../SS220/Barricade/SharedBarricadeSystem.cs | 12 ++ .../SS220/PlacerItem/PlacerItemComponent.cs | 26 +++- .../SS220/PlacerItem/PlacerItemSystem.cs | 45 ++++-- .../ru-RU/ss220/strucrure/barricades.ftl | 13 ++ .../Objects/Weapons/Bombs/plastic.yml | 5 + .../Objects/Weapons/Throwable/grenades.yml | 5 + .../SS220/Entities/Structures/barricade.yml | 51 ------- .../Shitspawn/FractWarEbent/barricade.yml | 135 ++++++++++++++++++ Resources/Prototypes/SS220/tags.yml | 4 + .../fract_war_barricade.rsi/base.png | Bin 0 -> 4597 bytes .../fract_war_barricade.rsi/damaged.png | Bin 0 -> 5326 bytes .../fract_war_barricade.rsi/icon.png | Bin 0 -> 3623 bytes .../fract_war_barricade.rsi/inhand-left.png | Bin 0 -> 3967 bytes .../fract_war_barricade.rsi/inhand-right.png | Bin 0 -> 3998 bytes .../fract_war_barricade.rsi/meta.json | 104 ++++++++++++++ .../fract_war_barricade.rsi/stand_up.png | Bin 0 -> 9058 bytes .../fract_war_barricade_ussp.rsi/base.png | Bin 0 -> 4736 bytes .../fract_war_barricade_ussp.rsi/damaged.png | Bin 0 -> 5430 bytes .../fract_war_barricade_ussp.rsi/icon.png | Bin 0 -> 3623 bytes .../inhand-left.png | Bin 0 -> 3967 bytes .../inhand-right.png | Bin 0 -> 3998 bytes .../fract_war_barricade_ussp.rsi/meta.json | 104 ++++++++++++++ .../fract_war_barricade_ussp.rsi/stand_up.png | Bin 0 -> 9857 bytes 27 files changed, 474 insertions(+), 68 deletions(-) create mode 100644 Resources/Locale/ru-RU/ss220/strucrure/barricades.ftl create mode 100644 Resources/Prototypes/SS220/Shitspawn/FractWarEbent/barricade.yml create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/base.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/damaged.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/icon.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/inhand-left.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/inhand-right.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/meta.json create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/stand_up.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/base.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/damaged.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/icon.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/inhand-left.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/inhand-right.png create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/meta.json create mode 100644 Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/stand_up.png diff --git a/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs b/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs index 290d139b177d69..374aa2f0eba8e1 100644 --- a/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs +++ b/Content.Client/SS220/PlacerItem/PlacerItemConstructionGhostSystem.cs @@ -38,7 +38,10 @@ public override void Update(float frameTime) if (!TryComp(heldEntity, out var comp) || !comp.Active) { if (placerIsPlacerItem) + { _placementManager.Clear(); + _placementDirection = default; + } return; } @@ -50,13 +53,13 @@ public override void Update(float frameTime) RaiseNetworkEvent(new PlacerItemUpdateDirectionEvent(GetNetEntity(heldEntity.Value), _placementDirection)); } - if (heldEntity == placerEntity && placerProto == comp.ProtoId.Id) + if (heldEntity == placerEntity && placerProto == comp.SpawnProto.Id) return; var newObjInfo = new PlacementInformation { MobUid = heldEntity.Value, - EntityType = comp.ProtoId.Id, + EntityType = comp.ConstructionGhostProto ?? comp.SpawnProto, PlacementOption = _placementMode, Range = (int)Math.Ceiling(SharedInteractionSystem.InteractionRange), IsTile = false, diff --git a/Content.Server/Spawners/Components/SpawnOnDespawnComponent.cs b/Content.Server/Spawners/Components/SpawnOnDespawnComponent.cs index 24b57a4b1c0acb..03669f4f76f86c 100644 --- a/Content.Server/Spawners/Components/SpawnOnDespawnComponent.cs +++ b/Content.Server/Spawners/Components/SpawnOnDespawnComponent.cs @@ -15,4 +15,9 @@ public sealed partial class SpawnOnDespawnComponent : Component /// [DataField(required: true)] public EntProtoId Prototype = string.Empty; + + // SS220 Add inherit rotation begin + [DataField] + public bool InheritRotation = false; + // SS220 Add inherit rotation end } diff --git a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs index 2f850faab1366a..13979736034d11 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnOnDespawnSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Spawners.Components; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Spawners; @@ -6,6 +7,9 @@ namespace Content.Server.Spawners.EntitySystems; public sealed class SpawnOnDespawnSystem : EntitySystem { + [Dependency] private readonly MapSystem _mapSystem = default!; + [Dependency] private readonly TransformSystem _xform = default!; + public override void Initialize() { base.Initialize(); @@ -18,7 +22,17 @@ private void OnDespawn(EntityUid uid, SpawnOnDespawnComponent comp, ref TimedDes if (!TryComp(uid, out TransformComponent? xform)) return; - Spawn(comp.Prototype, xform.Coordinates); + // SS220 Add inherit rotation begin + //Spawn(comp.Prototype, xform.Coordinates); + + if (comp.InheritRotation) + { + var mapCords = _xform.ToMapCoordinates(GetNetCoordinates(xform.Coordinates)); + Spawn(comp.Prototype, mapCords, rotation: xform.LocalRotation); + } + else + Spawn(comp.Prototype, xform.Coordinates); + // SS220 Add inherit rotation end } public void SetPrototype(Entity entity, EntProtoId prototype) diff --git a/Content.Shared/SS220/Barricade/BarricadeComponent.cs b/Content.Shared/SS220/Barricade/BarricadeComponent.cs index fa32c7ac9e2faf..c55f7baafe1421 100644 --- a/Content.Shared/SS220/Barricade/BarricadeComponent.cs +++ b/Content.Shared/SS220/Barricade/BarricadeComponent.cs @@ -1,8 +1,12 @@ // © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt +using Content.Shared.Whitelist; using Robust.Shared.GameStates; namespace Content.Shared.SS220.Barricade; +/// +/// A component that allows entity to block projectiles or hitscans with a specific chance that change by fire distance +/// [RegisterComponent, NetworkedComponent] [Access(typeof(SharedBarricadeSystem))] public sealed partial class BarricadeComponent : Component @@ -22,4 +26,10 @@ public sealed partial class BarricadeComponent : Component public float MinDistance = 1.5f; [DataField] public float MaxDistance = 9f; + + /// + /// A whitelist of entities that will always pass through the barricade + /// + [DataField] + public EntityWhitelist? Whitelist; } diff --git a/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs index 88e69c8fa5e902..2f8e99e24ab20b 100644 --- a/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs +++ b/Content.Shared/SS220/Barricade/SharedBarricadeSystem.cs @@ -2,12 +2,15 @@ using Content.Shared.Projectiles; using Content.Shared.SS220.Weapons.Ranged.Events; using Content.Shared.Throwing; +using Content.Shared.Whitelist; using Robust.Shared.Physics.Events; namespace Content.Shared.SS220.Barricade; public abstract partial class SharedBarricadeSystem : EntitySystem { + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + public override void Initialize() { base.Initialize(); @@ -22,9 +25,18 @@ public override void Initialize() private void OnPreventCollide(Entity entity, ref PreventCollideEvent args) { + if (_entityWhitelist.IsWhitelistPass(entity.Comp.Whitelist, args.OtherEntity)) + { + args.Cancelled = true; + return; + } + if (TryComp(args.OtherEntity, out var projectile) && ProjectileTryPassBarricade(entity, (args.OtherEntity, projectile))) + { args.Cancelled = true; + return; + } } private void OnHitscanAttempt(Entity entity, ref HitscanAttempt args) diff --git a/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs b/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs index 99a063f6f76427..8679b313052176 100644 --- a/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs +++ b/Content.Shared/SS220/PlacerItem/PlacerItemComponent.cs @@ -6,13 +6,22 @@ namespace Content.Shared.SS220.PlacerItem.Components; +/// +/// A component that allows the user to place a specific entity using a construction ghost +/// [RegisterComponent, NetworkedComponent] [AutoGenerateComponentState] public sealed partial class PlacerItemComponent : Component { + /// + /// Is placement by this component active or not + /// [ViewVariables, AutoNetworkedField] public bool Active = false; + /// + /// In which direction should the construction be placed + /// [ViewVariables, AutoNetworkedField] public Direction ConstructionDirection { @@ -29,12 +38,27 @@ public Direction ConstructionDirection [ViewVariables(VVAccess.ReadOnly)] public Transform ConstructionTransform { get; private set; } = default!; + /// + /// Id of the prototype used in the construction ghost + /// + [DataField] + public EntProtoId? ConstructionGhostProto; + + /// + /// Id of the prototype that will be spawned + /// [DataField(required: true)] - public EntProtoId ProtoId; + public EntProtoId SpawnProto; + /// + /// The time required for the user to place the construction + /// [DataField] public float DoAfter = 0; + /// + /// Should the placement toggle when item used in hand + /// [DataField] public bool ToggleActiveOnUseInHand = false; } diff --git a/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs b/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs index 483953cca66f35..e3180860ce67ae 100644 --- a/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs +++ b/Content.Shared/SS220/PlacerItem/PlacerItemSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.SS220.PlacerItem.Components; using Content.Shared.Tag; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -19,12 +20,14 @@ public sealed partial class PlacerItemSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IComponentFactory _factory = default!; + [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly RCDSystem _rcdSystem = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; public override void Initialize() { @@ -32,6 +35,7 @@ public override void Initialize() SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnDoAfter); SubscribeNetworkEvent(OnUpdateDirection); } @@ -64,7 +68,7 @@ private void OnAfterInteract(Entity entity, ref AfterIntera return; var doAfterTime = TimeSpan.FromSeconds(comp.DoAfter); - var ev = new PlacementOnInteractDoAfterEvent(GetNetCoordinates(mapGridData.Value.Location), comp.ConstructionDirection, comp.ProtoId); + var ev = new PlacerItemDoAfterEvent(GetNetCoordinates(mapGridData.Value.Location), comp.ConstructionDirection, comp.SpawnProto); var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, ev, uid, args.Target, uid) { @@ -76,8 +80,25 @@ private void OnAfterInteract(Entity entity, ref AfterIntera BlockDuplicate = false }; - _doAfter.TryStartDoAfter(doAfterArgs); - args.Handled = true; + if (_doAfter.TryStartDoAfter(doAfterArgs)) + { + comp.Active = false; + args.Handled = true; + }; + } + + private void OnDoAfter(Entity entity, ref PlacerItemDoAfterEvent args) + { + if (args.Cancelled || !_net.IsServer) + return; + + if (!_rcdSystem.TryGetMapGridData(GetCoordinates(args.Location), out var mapGridData)) + return; + + var mapCords = _xform.ToMapCoordinates(_mapSystem.GridTileToLocal(mapGridData.Value.GridUid, mapGridData.Value.Component, mapGridData.Value.Position)); + Spawn(args.ProtoId.Id, mapCords, rotation: args.Direction.ToAngle()); + + QueueDel(entity); } private void OnUpdateDirection(PlacerItemUpdateDirectionEvent ev, EntitySessionEventArgs session) @@ -105,17 +126,17 @@ private void SetActive(Entity entity, bool value) Dirty(entity); } - public bool IsPlacementOperationStillValid(Entity entity, MapGridData mapGridData, EntityUid? target, EntityUid user, bool popMsgs = true) + public bool IsPlacementOperationStillValid(Entity entity, MapGridData mapGridData, EntityUid? target, EntityUid user) { var (uid, comp) = entity; var unobstracted = target == null - ? _interaction.InRangeUnobstructed(user, _mapSystem.GridTileToWorld(mapGridData.GridUid, mapGridData.Component, mapGridData.Position), popup: popMsgs) - : _interaction.InRangeUnobstructed(user, target.Value, popup: popMsgs); + ? _interaction.InRangeUnobstructed(user, _mapSystem.GridTileToWorld(mapGridData.GridUid, mapGridData.Component, mapGridData.Position)) + : _interaction.InRangeUnobstructed(user, target.Value); if (!unobstracted) return false; - if (!_prototypeManager.TryIndex(comp.ProtoId.Id, out var prototype)) + if (!_prototypeManager.TryIndex(comp.SpawnProto.Id, out var prototype)) return false; prototype.TryGetComponent(_factory.GetComponentName(), out var tagComponent); @@ -169,17 +190,15 @@ private bool IsOurEntWillCollideWithOtherEnt(Transform ourXform, FixturesCompone } [Serializable, NetSerializable] -public sealed partial class PlacementOnInteractDoAfterEvent : DoAfterEvent +public sealed partial class PlacerItemDoAfterEvent : DoAfterEvent { - public NetCoordinates Coordinates; - + public NetCoordinates Location; public Direction Direction; - public EntProtoId ProtoId; - public PlacementOnInteractDoAfterEvent(NetCoordinates coordinates, Direction direction, EntProtoId protoId) + public PlacerItemDoAfterEvent(NetCoordinates location, Direction direction, EntProtoId protoId) { - Coordinates = coordinates; + Location = location; Direction = direction; ProtoId = protoId; } diff --git a/Resources/Locale/ru-RU/ss220/strucrure/barricades.ftl b/Resources/Locale/ru-RU/ss220/strucrure/barricades.ftl new file mode 100644 index 00000000000000..6a903f0a7e3b79 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/strucrure/barricades.ftl @@ -0,0 +1,13 @@ +ent-FractWarFoldedBarricadeBase = сложенная баррикада + .desc = Сложенная баррикада из листов стали. Может быть быстро установлена в нужном месте. +ent-FractWarBarricadeSpawnBase = баррикада + .desc = Баррикада небольшой высоты из листов стали. Есть шанс защитить от шальной пули. +ent-FractWarBarricadeBase = баррикада + .desc = Баррикада небольшой высоты из листов стали. Есть шанс защитить от шальной пули. + +ent-FractWarFoldedBarricadeUssp = сложеная баррикада СССП + .desc = {ent-FractWarFoldedBarricadeBase.desc} +ent-FractWarBarricadeSpawnUssp = баррикада СССП + .desc = {ent-FractWarBarricadeSpawnBase.desc} +ent-FractWarBarricadeUssp = баррикада СССП + .desc = {ent-FractWarBarricadeBase.desc} diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/plastic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/plastic.yml index 1dd4c6234f6889..95fe09a1847658 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/plastic.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/plastic.yml @@ -34,6 +34,11 @@ base: Primed: { state: primed } Unprimed: { state: complete } + # SS220 add barricades begin + - type: Tag + tags: + - PassBarricade + # SS220 add barricades end - type: entity name: composition C-4 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 42fc68da127c64..4175448f25166f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -40,6 +40,11 @@ enum.ConstructionVisuals.Layer: Primed: { state: primed } Unprimed: { state: icon } + # SS220 add barricades begin + - type: Tag + tags: + - PassBarricade + # SS220 add barricades end - type: entity name: explosive grenade diff --git a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml index 5cb5af8a2e0f39..7673bc57e415cc 100644 --- a/Resources/Prototypes/SS220/Entities/Structures/barricade.yml +++ b/Resources/Prototypes/SS220/Entities/Structures/barricade.yml @@ -361,54 +361,3 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] - -- type: entity - parent: BaseItem - id: PlacementOnInteractTest - name: base flatpack - description: A flatpack used for constructing something. - components: - - type: Item - size: Large - - type: Sprite - sprite: Objects/Devices/flatpack.rsi - layers: - - state: base - - state: overlay - color: "#cec8ac" - map: ["enum.FlatpackVisualLayers.Overlay"] - - state: icon-default - - type: Appearance - - type: PlacerItem - protoId: FoldingBarricadePlasteelFloor - toggleActiveOnUseInHand: true - - type: StaticPrice - price: 250 - -- type: entity - parent: BarricadePlasteelFloor - id: FoldingBarricadePlasteelFloor - name: напольная укреплённая баррикада - description: Баррикада небольшой высоты из листов пластали. Есть шанс защитить от шальной пули. - placement: - mode: SnapgridCenter - components: - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.5,-0.5,0.5,-0.36" - mask: - - TableMask - layer: - - WallLayer - - type: Barricade - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 1600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] diff --git a/Resources/Prototypes/SS220/Shitspawn/FractWarEbent/barricade.yml b/Resources/Prototypes/SS220/Shitspawn/FractWarEbent/barricade.yml new file mode 100644 index 00000000000000..e94ac9ecf09d79 --- /dev/null +++ b/Resources/Prototypes/SS220/Shitspawn/FractWarEbent/barricade.yml @@ -0,0 +1,135 @@ +# Base +- type: entity + parent: BaseItem + id: FractWarFoldedBarricadeBase + suffix: Ebent, Shitspawn, FractWar + components: + - type: Item + size: Large + - type: Sprite + sprite: SS220/Structures/Barricades/fract_war_barricade.rsi + state: icon + - type: Appearance + - type: PlacerItem + constructionGhostProto: FractWarBarricadeBase + spawnProto: FractWarBarricadeSpawnBase + toggleActiveOnUseInHand: true + - type: StaticPrice + price: 250 + +- type: entity + parent: BaseStructure + id: FractWarBarricadeSpawnBase + suffix: Ebent, Shitspawn, FractWar + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Appearance + - type: AtmosExposed + - type: Sprite + drawdepth: Mobs + sprite: SS220/Structures/Barricades/fract_war_barricade.rsi + state: stand_up + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,-0.25" + mask: + - FullTileMask + layer: + - TableLayer + - type: Barricade + whitelist: + tags: + - PassBarricade + - type: TimedDespawn + lifetime: 2.25 + - type: SpawnOnDespawn + prototype: FractWarBarricadeBase + inheritRotation: true + - type: Damageable + damageModifierSet: StructuralMetallic + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + +- type: entity + parent: BaseStructure + id: FractWarBarricadeBase + suffix: Ebent, Shitspawn, FractWar + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Sprite + sprite: SS220/Structures/Barricades/fract_war_barricade.rsi + drawdepth: Mobs + noRot: false + state: base + - type: Appearance + - type: AtmosExposed + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,-0.25" + mask: + - FullTileMask + layer: + - WallLayer + - type: Barricade + whitelist: + tags: + - PassBarricade + - type: Damageable + damageModifierSet: StructuralMetallic + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 500 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + +# USSP +- type: entity + parent: FractWarFoldedBarricadeBase + id: FractWarFoldedBarricadeUssp + suffix: Ebent, Shitspawn, FractWar, Ussp + components: + - type: Sprite + sprite: SS220/Structures/Barricades/fract_war_barricade_ussp.rsi + - type: PlacerItem + constructionGhostProto: FractWarBarricadeUssp + spawnProto: FractWarBarricadeSpawnUssp + +- type: entity + parent: FractWarBarricadeSpawnBase + id: FractWarBarricadeSpawnUssp + suffix: Ebent, Shitspawn, FractWar, Ussp + components: + - type: Sprite + sprite: SS220/Structures/Barricades/fract_war_barricade_ussp.rsi + - type: SpawnOnDespawn + prototype: FractWarBarricadeUssp + inheritRotation: true + +- type: entity + parent: FractWarBarricadeBase + id: FractWarBarricadeUssp + suffix: Ebent, Shitspawn, FractWar, Ussp + components: + - type: Sprite + sprite: SS220/Structures/Barricades/fract_war_barricade_ussp.rsi diff --git a/Resources/Prototypes/SS220/tags.yml b/Resources/Prototypes/SS220/tags.yml index 23df46119a2b5a..a057ffc1888b2f 100644 --- a/Resources/Prototypes/SS220/tags.yml +++ b/Resources/Prototypes/SS220/tags.yml @@ -185,3 +185,7 @@ # SS220 Cult - type: Tag id: Omnivorous + +# Barricades +- type: Tag + id: PassBarricade diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/base.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4de80672a7fd8e3b25ebd3ce319eacffbf3f4f GIT binary patch literal 4597 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/icon.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9936a9a9ee927ae2c0d163eaf4b459e17c59d906 GIT binary patch literal 3623 zcmcIndvH|M9ljwI5Rk@Lg%%^%#Wq+u*>mpuY*`>|A_=k(Acde+IL~BZcki;hd61!k zsNe%+po5?T2GB~8$JAQ24$}fwQjw1JQAIliY}IL}oswyhK6HSdyLm`ENcW%K*~i(t zzx$ov@B6;uTnxZ#{Sq9jrccot>X(T8W#8#Tu%)PlY~b z+Ejf!!q~f6)5dSuwEp1qpErDE&A~CXM|R8``uh(um9ra??;KuRf5$_5)1KJIef63k#iwPDq$|Gj+wTsloOZbSuU9TFJ+k}W*nK0ec=*&O+fL!@UoUx*9^bZl_UbcD zd)nUKu>TLo-XA5m3|cvJ%lF&%v}$FQOJ5m%s%PlRjgcH$DR=j-a7q_>YLUNdF+wb&6mcmd2sqQ@4PvC)T=$Ei_qAl zTk+Do=lA~f?27ZztB;=%>8@!*r%Z^BT2S4wqiV&sM}O;kf2P04e05|G>1^Z&+{}w5 z!(Xg@;r9FgcHP90E3`x1v(BBFJNZ?`@NIE-gvq9sw+nH6(8XEw6V zB@FY>7>{2*G?f~N≧{)GU4ZUJ^LhxGW>Wb41Q>%`#EjCerzF z{gY){{JfuQ@xLIqfxrJFL(T>!%(P~c1$sCOi=;>??3{{4h5Ry+SXPj{pKbL0WWP`i z{WI#ZSRf46$Inlsx!<0?A{sKFVFQTNtIIy zAun}(jn7&GGJ@-Soy&cuFa`*X2w@S6dM@xd@_-2~8&GO8Adu}4=}8KS-H$dan^22~ zBriq{vf>KSgO(IjT0mL~!YgD&EC_^W8PK+{NevRxKc6?|C;e>P=Zal3q;nRLel|sv zg%T;!EW(*|b6{wNuBxa@=!rOeM_&oJ_j3Q6Xjqa)=2yG|iy3Iskjv0H%b(6j(Ui zfxTHI6aN#7OV@TS6F`PAphAr>QCOaIF?OlvIUXt&3t7;V1~-9SSdP%L0AYp$T+BVh zA>}SNipKIxgP1k}2$?o;Jc|J@NYlee5KJK=qUah;bq%DR0T^WrI8gEBSuO@9bv))` z%B3X5=4EnKoPz|wu5f@Q)w-sRrTDT$;fFxpoaYZ|Ixhj+h=1M6X z2Nunji>PZGuIi$Os!Y&8MUsu}rjnFU zyVx2rGr8d@SD8ZcU~LE1G8nK;RU|}?gKSnT7IQ2cDRO|J?rcDza^Mk(fO4B!+}N-k zRJ4v9+BU(;riMc?aMZ005M)y&D^fNimaFb+#pcDiYf4*EU^})Ec!B`sEM+(D+E7X; zp>c_|-G6(A!Jt4A8#)YHo?3zzWm4$Iy-hWI`R-eYylBtZ-^$fS zG#C!-fYpzIJ+w%xwx3ly)!i1R!^GObfeZXxt;(3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/inhand-left.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..92a3ec78b973b480cc0bf76285e6fbb38f3dd856 GIT binary patch literal 3967 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/meta.json b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/meta.json new file mode 100644 index 00000000000000..71d5aa14462f1b --- /dev/null +++ b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/meta.json @@ -0,0 +1,104 @@ +{ + "version": 1, + "license": "EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt", + "copyright": "Sprited by mixnikita (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "base", + "directions": 4 + }, + { + "name": "damaged", + "directions": 4 + }, + { + "name": "stand_up", + "directions": 4, + "delays": [ + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + } + ] + } diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/stand_up.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade.rsi/stand_up.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5816473b026400a39553677e335e7ab220800e GIT binary patch literal 9058 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 VcWZBLlJOrLfqtRlz2j%j|0k*i?&kmi literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/damaged.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/damaged.png new file mode 100644 index 0000000000000000000000000000000000000000..c6df47eb49a0bc858536d9748c81e6c28c4c69d1 GIT binary patch literal 5430 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≧{)GU4ZUJ^LhxGW>Wb41Q>%`#EjCerzF z{gY){{JfuQ@xLIqfxrJFL(T>!%(P~c1$sCOi=;>??3{{4h5Ry+SXPj{pKbL0WWP`i z{WI#ZSRf46$Inlsx!<0?A{sKFVFQTNtIIy zAun}(jn7&GGJ@-Soy&cuFa`*X2w@S6dM@xd@_-2~8&GO8Adu}4=}8KS-H$dan^22~ zBriq{vf>KSgO(IjT0mL~!YgD&EC_^W8PK+{NevRxKc6?|C;e>P=Zal3q;nRLel|sv zg%T;!EW(*|b6{wNuBxa@=!rOeM_&oJ_j3Q6Xjqa)=2yG|iy3Iskjv0H%b(6j(Ui zfxTHI6aN#7OV@TS6F`PAphAr>QCOaIF?OlvIUXt&3t7;V1~-9SSdP%L0AYp$T+BVh zA>}SNipKIxgP1k}2$?o;Jc|J@NYlee5KJK=qUah;bq%DR0T^WrI8gEBSuO@9bv))` z%B3X5=4EnKoPz|wu5f@Q)w-sRrTDT$;fFxpoaYZ|Ixhj+h=1M6X z2Nunji>PZGuIi$Os!Y&8MUsu}rjnFU zyVx2rGr8d@SD8ZcU~LE1G8nK;RU|}?gKSnT7IQ2cDRO|J?rcDza^Mk(fO4B!+}N-k zRJ4v9+BU(;riMc?aMZ005M)y&D^fNimaFb+#pcDiYf4*EU^})Ec!B`sEM+(D+E7X; zp>c_|-G6(A!Jt4A8#)YHo?3zzWm4$Iy-hWI`R-eYylBtZ-^$fS zG#C!-fYpzIJ+w%xwx3ly)!i1R!^GObfeZXxt;(3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/inhand-left.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..92a3ec78b973b480cc0bf76285e6fbb38f3dd856 GIT binary patch literal 3967 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/meta.json b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/meta.json new file mode 100644 index 00000000000000..71d5aa14462f1b --- /dev/null +++ b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/meta.json @@ -0,0 +1,104 @@ +{ + "version": 1, + "license": "EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt", + "copyright": "Sprited by mixnikita (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "base", + "directions": 4 + }, + { + "name": "damaged", + "directions": 4 + }, + { + "name": "stand_up", + "directions": 4, + "delays": [ + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ], + [ + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + } + ] + } diff --git a/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/stand_up.png b/Resources/Textures/SS220/Structures/Barricades/fract_war_barricade_ussp.rsi/stand_up.png new file mode 100644 index 0000000000000000000000000000000000000000..80736fa93ce46fb0bbd681d385ff7f2f1dd7d543 GIT binary patch literal 9857 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 Cu