From 754dc8f9aac4e29802773a8733a62f2a7dcfff3e Mon Sep 17 00:00:00 2001 From: significant harassment Date: Sun, 2 Aug 2026 18:10:13 -0400 Subject: [PATCH 1/2] Barricade Code Port (#4435) Co-authored-by: PunishedJoe Co-authored-by: Onezero0 --- .../BarricadeBlock/BarricadeBlockComponent.cs | 38 +++++ .../Projectiles/SharedProjectileSystem.cs | 135 +++++++++++------- .../Assorted/ExtendDescriptionComponent.cs | 36 +++++ .../Assorted/ExtendDescriptionSystem.cs | 30 ++++ Resources/Prototypes/Entities/Mobs/base.yml | 10 ++ .../Weapons/Guns/Projectiles/projectiles.yml | 2 +- .../Structures/Machines/vending_machines.yml | 3 + .../_Goobstation/Damage/modifier_sets.yml | 57 ++++++++ 8 files changed, 257 insertions(+), 54 deletions(-) create mode 100644 Content.Shared/BarricadeBlock/BarricadeBlockComponent.cs create mode 100644 Content.Shared/Traits/Assorted/ExtendDescriptionComponent.cs create mode 100644 Content.Shared/Traits/Assorted/ExtendDescriptionSystem.cs diff --git a/Content.Shared/BarricadeBlock/BarricadeBlockComponent.cs b/Content.Shared/BarricadeBlock/BarricadeBlockComponent.cs new file mode 100644 index 00000000000..8ef7ec4305a --- /dev/null +++ b/Content.Shared/BarricadeBlock/BarricadeBlockComponent.cs @@ -0,0 +1,38 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using System.Collections.Generic; +using Content.Shared.Tag; + +namespace Content.Shared.BarricadeBlock; + +[RegisterComponent] +public sealed partial class BarricadeBlockComponent : Component +{ + /// + /// % chance of blocking a projectile passing overhead + /// + [DataField("blocking")] + public int Blocking = 66; + + /// + /// Can it be used bidirectionally (e.g. sandbags) or only from behind (e.g. crenelated walls)? + /// + [DataField("bidirectional")] + public bool Bidirectional = true; + + /// + /// Can it be used omnidirectionally (e.g. vending machines) + /// + [DataField("omnidirectional")] + public bool Omnidirectional = true; + + /// + /// Distance between the shooter and barricade + /// + [DataField("passthroughdistance")] + public float PassThroughDistance = 1.5f; + +} + +/// ported from civ14 diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index dfa03bed17a..718a1122c78 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -36,13 +36,14 @@ using Robust.Shared.Configuration; using Content.Shared._Mono.CCVar; using Robust.Shared; +using Content.Shared.BarricadeBlock; // BF14 +using Robust.Shared.Random; // BF14 namespace Content.Shared.Projectiles; public abstract partial class SharedProjectileSystem : EntitySystem { public const string ProjectileFixture = "projectile"; - [Dependency] private ISharedAdminLogManager _adminLogger = default!; [Dependency] private SharedAudioSystem _audio = default!; [Dependency] private SharedColorFlashEffectSystem _color = default!; @@ -58,6 +59,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem [Dependency] private IGameTiming _gameTiming = default!; [Dependency] private INetManager _net = default!; [Dependency] private IConfigurationManager _cfg = default!; // Mono + [Dependency] private IRobustRandom _random = default!; // BF14 // Cache of projectiles waiting for collision checks private readonly ConcurrentQueue<(EntityUid Uid, ProjectileComponent Component, EntityUid Target)> _pendingCollisionChecks = new(); @@ -377,6 +379,85 @@ private void OnEmbedRemove(Entity embeddable, ref _hands.TryPickupAnyHand(args.User, embeddable); } + //ported from civ14 + private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args) + { + if (component.IgnoreShooter && (args.OtherEntity == component.Shooter || args.OtherEntity == component.Weapon)) + { + args.Cancelled = true; + } + //check for BarricadeBlock component (percentage of chance to hit/pass over) + if (TryComp(args.OtherEntity, out BarricadeBlockComponent? BarricadeBlock)) + { + var alwaysPassThrough = false; + //_sawmill.Info("Checking BarricadeBlock..."); + if (component.Shooter is { } shooterUid && Exists(shooterUid)) + { + // Condition 1: Directions are the same (using cardinal directions). + // Or, if bidirectional, directions can be opposite. + var shooterWorldRotation = _transform.GetWorldRotation(shooterUid); + var BarricadeBlockWorldRotation = _transform.GetWorldRotation(args.OtherEntity); + + var shooterDir = shooterWorldRotation.GetCardinalDir(); + var BarricadeBlockDir = BarricadeBlockWorldRotation.GetCardinalDir(); + + bool directionallyAllowed = false; + if (shooterDir == BarricadeBlockDir) + { + directionallyAllowed = true; + //_sawmill.Debug("Shooter and BarricadeBlock facing same cardinal direction."); + } + else if (BarricadeBlock.Bidirectional) + { + var oppositeBarricadeBlockDir = (Direction)(((int)BarricadeBlockDir + 4) % 8); + if (shooterDir == oppositeBarricadeBlockDir) + { + directionallyAllowed = true; + //_sawmill.Debug("Shooter and BarricadeBlock facing opposite cardinal directions (bidirectional pass)."); + } + } + else if (BarricadeBlock.Omnidirectional) + { + directionallyAllowed = true; + //_sawmill.Debug("Has the omnidirectional field"); + } + + if (directionallyAllowed) + { + // Condition 2: Firer is within 1 tile of the BarricadeBlock. + var shooterCoords = Transform(shooterUid).Coordinates; + var BarricadeBlockCoords = Transform(args.OtherEntity).Coordinates; + var BypassDistance = BarricadeBlock.PassThroughDistance; + + if (shooterCoords.TryDistance(EntityManager, BarricadeBlockCoords, out var distance) && + distance <= BypassDistance) + { + alwaysPassThrough = true; + } + } + } + + if (alwaysPassThrough) + { + args.Cancelled = true; + } + else + { + //_sawmill.Debug("BarricadeBlock direction/distance check failed or shooter not valid."); + // Standard BarricadeBlock blocking logic if the special conditions are not met. + var rando = _random.NextFloat(0.0f, 100.0f); + if (rando >= BarricadeBlock.Blocking) + { + args.Cancelled = true; + } + else + { + return; + } + } + } + } + private void OnEmbedThrowDoHit(Entity embeddable, ref ThrowDoHitEvent args) { if (!embeddable.Comp.EmbedOnThrow) @@ -487,58 +568,6 @@ public void DetachAllEmbedded(Entity container) } } - private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args) - { - // Goobstation - Crawling fix - if (TryComp(args.OtherEntity, out var requireTarget) && requireTarget.IgnoreThrow && requireTarget.Active) - return; - - if (component.IgnoreShooter && (args.OtherEntity == component.Shooter || args.OtherEntity == component.Weapon)) - { - args.Cancelled = true; - return; - } - - // Get transforms once for subsequent checks to avoid repeated calls - var projectileXform = Transform(uid); - var targetXform = Transform(args.OtherEntity); - - // Add collision check to queue for batch processing if we have enough - if (_pendingCollisionChecks.Count >= MinProjectilesForParallel / 2) - { - _pendingCollisionChecks.Enqueue((uid, component, args.OtherEntity)); - - // Assume collision for now - if shield check passes, we'll handle it in the batch process - return; - } - - // For low volume, process immediately - // Check if any shield system wants to prevent collision - var ev = new ProjectileCollisionAttemptEvent(uid, args.OtherEntity); - RaiseLocalEvent(ref ev); - - if (ev.Cancelled) - { - args.Cancelled = true; - return; - } - - // Check if target and projectile are on different maps/z-levels - if (projectileXform.MapID != targetXform.MapID) - { - args.Cancelled = true; - return; - } - - // Define the tag constant - const string GunCanAimShooterTag = "GunCanAimShooter"; - - if ((component.Shooter == args.OtherEntity || component.Weapon == args.OtherEntity) && - component.Weapon != null && _tag.HasTag(component.Weapon.Value, GunCanAimShooterTag) && - TryComp(uid, out TargetedProjectileComponent? targeted) && targeted.Target == args.OtherEntity) - return; - } - // Goobstation - Crawling fix private void EmbeddablePreventCollision(EntityUid uid, EmbeddableProjectileComponent component, ref PreventCollideEvent args) { diff --git a/Content.Shared/Traits/Assorted/ExtendDescriptionComponent.cs b/Content.Shared/Traits/Assorted/ExtendDescriptionComponent.cs new file mode 100644 index 00000000000..58d1fe621a2 --- /dev/null +++ b/Content.Shared/Traits/Assorted/ExtendDescriptionComponent.cs @@ -0,0 +1,36 @@ +//using Content.Shared.Customization.Systems; +using Robust.Shared.Serialization; + +namespace Content.Shared.Traits.Assorted.Components; + +[Serializable, NetSerializable, DataDefinition] +public sealed partial class DescriptionExtension +{ +// [DataField] +// public List? Requirements; + + [DataField] + public string Description = ""; + +// [DataField] +// public string RequirementsNotMetDescription = ""; + + [DataField] + public int FontSize = 12; + + [DataField] + public string Color = "#ffffff"; + + [DataField] + public bool RequireDetailRange = true; +} + +[RegisterComponent] +public sealed partial class ExtendDescriptionComponent : Component +{ + /// + /// The list of all descriptions to add to an entity when examined at close range. + /// + [DataField] + public List DescriptionList = new(); +} diff --git a/Content.Shared/Traits/Assorted/ExtendDescriptionSystem.cs b/Content.Shared/Traits/Assorted/ExtendDescriptionSystem.cs new file mode 100644 index 00000000000..ba46fcca099 --- /dev/null +++ b/Content.Shared/Traits/Assorted/ExtendDescriptionSystem.cs @@ -0,0 +1,30 @@ +//using Content.Shared.Customization.Systems; +using Content.Shared.Examine; +using Content.Shared.Traits.Assorted.Components; + +namespace Content.Shared.Traits.Assorted.Systems; + +public sealed class ExtendDescriptionSystem : EntitySystem +{ +// [Dependency] private readonly CharacterRequirementsSystem _characterRequirements = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnExamined); + } + + private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, ExaminedEvent args) + { + if (component.DescriptionList.Count <= 0) + return; + + foreach (var desc in component.DescriptionList) + { + if (!args.IsInDetailsRange && desc.RequireDetailRange) + continue; + + args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]"); + } + } +} diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 7bc23ace817..eded3d85ccc 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -52,6 +52,16 @@ - type: PassiveThermalSignature # Mono signature: 225 # if nothing else is generating heat, 30m radius, 60m with 4 mobs, 120 with 16 - type: ThermalSignature # Mono + - type: BarricadeBlock # Mono + bidirectional: false + omnidirectional: true + passthroughdistance: 1 + blocking: 100 + - type: ExtendDescription # BF14 + descriptionList: + - description: Firing a weapon within half a tile will fire over their shoulder. + color: "#4f94ad" + requireDetailRange: true - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 3fa7c465bf6..40d23ae2535 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -85,7 +85,7 @@ projectile: shape: !type:PhysShapeAabb - bounds: "-0.1,-0.1,0.1,0.1" + bounds: "-0.1,-0.1,0.1,0.3" hard: false mask: - Impassable diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index b2808ee0e87..f04e9e38e24 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -141,6 +141,9 @@ - type: ShipRepairable repairTime: 5 repairCost: 10 + - type: BarricadeBlock + bidirectional: true + blocking: 67 - type: entity parent: VendingMachine diff --git a/Resources/Prototypes/_Goobstation/Damage/modifier_sets.yml b/Resources/Prototypes/_Goobstation/Damage/modifier_sets.yml index 01bf6809eca..83990c326de 100644 --- a/Resources/Prototypes/_Goobstation/Damage/modifier_sets.yml +++ b/Resources/Prototypes/_Goobstation/Damage/modifier_sets.yml @@ -92,3 +92,60 @@ Piercing: 1.15 Heat: 1.20 Cold: 0.80 + +# Barricades + +- type: damageModifierSet + id: BarricadeSteel + coefficients: + Blunt: 0.8 + Slash: 0.7 + Piercing: 0.5 + flatReductions: + Blunt: 5 + Slash: 5 + Piercing: 5 + Heat: 5 + Structural: 5 + +- type: damageModifierSet + id: BarricadeSteelFolding + coefficients: + Blunt: 0.9 + Slash: 0.9 + Piercing: 0.65 + flatReductions: + Blunt: 5 + Slash: 5 + Piercing: 5 + Heat: 3 + +- type: damageModifierSet + id: BarricadePlasteel + coefficients: + Blunt: 0.6 + Slash: 0.5 + Piercing: 0.3 + Structural: 0.75 + Heat: 0.5 + flatReductions: + Blunt: 5 + Slash: 5 + Piercing: 5 + Heat: 3 + Structural: 5 + +- type: damageModifierSet + id: BarricadePlasteelFolding + coefficients: + Blunt: 0.8 + Slash: 0.7 + Piercing: 0.8 + Structural: 0.85 + Heat: 0.5 + flatReductions: + Blunt: 5 + Slash: 5 + Piercing: 5 + Heat: 3 + Structural: 3 From 33ca60e406deeb9b0f3c83cd24bea4d230256389 Mon Sep 17 00:00:00 2001 From: fox76055 Date: Tue, 4 Aug 2026 13:50:01 +0500 Subject: [PATCH 2/2] Update CCVars.Movement.cs --- Content.Shared/CCVar/CCVars.Movement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/CCVar/CCVars.Movement.cs b/Content.Shared/CCVar/CCVars.Movement.cs index 8bfbdcad608..ac39ec645ca 100644 --- a/Content.Shared/CCVar/CCVars.Movement.cs +++ b/Content.Shared/CCVar/CCVars.Movement.cs @@ -11,7 +11,7 @@ public sealed partial class CCVars /// [CVarControl(AdminFlags.VarEdit)] public static readonly CVarDef MovementMobPushing = - CVarDef.Create("movement.mob_pushing", false, CVar.SERVER | CVar.REPLICATED); + CVarDef.Create("movement.mob_pushing", true, CVar.SERVER | CVar.REPLICATED); // LuaM false -> true /// /// Can we push mobs not moving.