Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Content.Shared/BarricadeBlock/BarricadeBlockComponent.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// % chance of blocking a projectile passing overhead
/// </summary>
[DataField("blocking")]
public int Blocking = 66;

/// <summary>
/// Can it be used bidirectionally (e.g. sandbags) or only from behind (e.g. crenelated walls)?
/// </summary>
[DataField("bidirectional")]
public bool Bidirectional = true;

/// <summary>
/// Can it be used omnidirectionally (e.g. vending machines)
/// </summary>
[DataField("omnidirectional")]
public bool Omnidirectional = true;

/// <summary>
/// Distance between the shooter and barricade
/// </summary>
[DataField("passthroughdistance")]
public float PassThroughDistance = 1.5f;

}

/// ported from civ14
2 changes: 1 addition & 1 deletion Content.Shared/CCVar/CCVars.Movement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed partial class CCVars
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<bool> MovementMobPushing =
CVarDef.Create("movement.mob_pushing", false, CVar.SERVER | CVar.REPLICATED);
CVarDef.Create("movement.mob_pushing", true, CVar.SERVER | CVar.REPLICATED); // LuaM false -> true

/// <summary>
/// Can we push mobs not moving.
Expand Down
135 changes: 82 additions & 53 deletions Content.Shared/Projectiles/SharedProjectileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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!;
Expand All @@ -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();
Expand Down Expand Up @@ -377,6 +379,85 @@ private void OnEmbedRemove(Entity<EmbeddableProjectileComponent> 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<EmbeddableProjectileComponent> embeddable, ref ThrowDoHitEvent args)
{
if (!embeddable.Comp.EmbedOnThrow)
Expand Down Expand Up @@ -487,58 +568,6 @@ public void DetachAllEmbedded(Entity<EmbeddedContainerComponent> container)
}
}

private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args)
{
// Goobstation - Crawling fix
if (TryComp<RequireProjectileTargetComponent>(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)
{
Expand Down
36 changes: 36 additions & 0 deletions Content.Shared/Traits/Assorted/ExtendDescriptionComponent.cs
Original file line number Diff line number Diff line change
@@ -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<CharacterRequirement>? 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
{
/// <summary>
/// The list of all descriptions to add to an entity when examined at close range.
/// </summary>
[DataField]
public List<DescriptionExtension> DescriptionList = new();
}
30 changes: 30 additions & 0 deletions Content.Shared/Traits/Assorted/ExtendDescriptionSystem.cs
Original file line number Diff line number Diff line change
@@ -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<ExtendDescriptionComponent, ExaminedEvent>(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]");
}
}
}
10 changes: 10 additions & 0 deletions Resources/Prototypes/Entities/Mobs/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@
- type: ShipRepairable
repairTime: 5
repairCost: 10
- type: BarricadeBlock
bidirectional: true
blocking: 67

- type: entity
parent: VendingMachine
Expand Down
57 changes: 57 additions & 0 deletions Resources/Prototypes/_Goobstation/Damage/modifier_sets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading