| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- using Content.Server.GameTicking.Rules.Components;
- using Content.Shared.NPC.Components;
- using Content.Shared.Physics;
- using Robust.Shared.Physics.Events;
- using Robust.Shared.Physics.Systems;
- using Robust.Shared.Timing;
- using Content.Server.Chat.Systems;
- using Robust.Shared.Physics;
- using Content.Shared.GameTicking.Components;
- using Robust.Shared.Physics.Components;
- namespace Content.Server.GameTicking.Rules;
- public sealed class GracewallRuleSystem : GameRuleSystem<GracewallRuleComponent>
- {
- [Dependency] private readonly SharedPhysicsSystem _physics = default!;
- // Define a specific collision group for the grace wall.
- // Make sure this group is defined in CollisionGroups.yml and NPCs are set to not collide with it.
- [Dependency] private readonly ChatSystem _chat = default!;
- private const int GraceWallCollisionGroup = (int)CollisionGroup.MidImpassable; // Example: Use MidImpassable or define a custom one
- private FixturesComponent? _fixtures;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<GracewallAreaComponent, StartCollideEvent>(OnStartCollide);
- // Potentially subscribe to PreventCollideEvent if more fine-grained control is needed
- }
- protected override void Started(EntityUid uid, GracewallRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
- {
- base.Started(uid, component, gameRule, args);
- component.Timer = (float)component.GracewallDuration.TotalSeconds;
- component.GracewallActive = true;
- // Schedule the announcement for 15 seconds later
- var announcementMessage = $"The grace wall is up for {component.GracewallDuration.TotalMinutes} minutes!";
- Timer.Spawn(TimeSpan.FromSeconds(15), () =>
- {
- _chat.DispatchGlobalAnnouncement(announcementMessage, "Round", false, null, Color.Yellow);
- });
- Log.Info($"Grace wall active for {component.GracewallDuration.TotalMinutes} minutes.");
- // Activate all grace wall areas
- var query = EntityQueryEnumerator<GracewallAreaComponent, TransformComponent, FixturesComponent>();
- while (query.MoveNext(out var wallUid, out var area, out var xform, out var fixtures))
- {
- area.GracewallActive = true;
- UpdateGracewallPhysics(wallUid, area, fixtures, true);
- }
- }
- protected override void Ended(EntityUid uid, GracewallRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
- {
- base.Ended(uid, component, gameRule, args);
- // Ensure walls are deactivated if the rule ends unexpectedly
- DeactivateAllGraceWalls(component);
- _chat.DispatchGlobalAnnouncement("The grace wall is now down!", "Round", false, null, Color.Yellow);
- }
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- var query = EntityQueryEnumerator<GracewallRuleComponent, GameRuleComponent>();
- while (query.MoveNext(out var uid, out var gracewall, out var gameRule))
- {
- if (!GameTicker.IsGameRuleActive(uid, gameRule) || !gracewall.GracewallActive)
- continue;
- gracewall.Timer -= frameTime;
- if (gracewall.Timer <= 0)
- {
- Log.Info("Grace wall duration ended.");
- DeactivateAllGraceWalls(gracewall);
- _chat.DispatchGlobalAnnouncement("The grace wall is now down!", "Round", false, null, Color.Yellow);
- }
- }
- }
- private void DeactivateAllGraceWalls(GracewallRuleComponent component)
- {
- component.GracewallActive = false;
- // Deactivate all grace wall areas
- var query = EntityQueryEnumerator<GracewallAreaComponent, FixturesComponent>();
- while (query.MoveNext(out var wallUid, out var area, out var fixtures))
- {
- area.GracewallActive = false;
- UpdateGracewallPhysics(wallUid, area, fixtures, false);
- }
- }
- private void UpdateGracewallPhysics(EntityUid uid, GracewallAreaComponent component, FixturesComponent fixtures, bool active)
- {
- // Check if the specific fixture we defined in the prototype exists
- if (!fixtures.Fixtures.TryGetValue("gracewall", out var fixture))
- {
- Log.Warning($"Gracewall entity {ToPrettyString(uid)} is missing the 'gracewall' fixture!");
- // Attempt to create it dynamically if missing? Or just rely on the prototype.
- // For now, we'll just log a warning. If it's consistently missing, the prototype needs fixing.
- return;
- }
- // Modify the fixture's collision properties
- // This assumes NPCs have a mask that *doesn't* include GraceWallCollisionGroup
- _physics.SetCollisionLayer(uid, "gracewall", fixture, active ? GraceWallCollisionGroup : (int)CollisionGroup.None);
- // Setting the layer might require refreshing contacts or waking the body if it's asleep
- // to ensure the change takes effect immediately.
- if (TryComp<PhysicsComponent>(uid, out var physics))
- _physics.WakeBody(uid, body: physics);
- // Ensure the radius is correct (it might change dynamically later)
- // This requires getting the specific fixture and modifying its shape.
- // For simplicity, we assume the prototype radius is sufficient for now.
- // If dynamic radius is needed, we'd need more complex logic here.
- }
- private void OnStartCollide(EntityUid uid, GracewallAreaComponent component, ref StartCollideEvent args)
- {
- // This event triggers when something *starts* colliding with the grace wall fixture.
- // We only care when the wall is active.
- if (!component.GracewallActive)
- return;
- // Check if the other entity is an NPC
- var otherUid = args.OtherEntity;
- if (HasComp<NpcFactionMemberComponent>(otherUid))
- {
- // The collision system *should* prevent entry based on layers/masks.
- // However, if an NPC somehow starts colliding (e.g., spawned inside, teleported),
- // we could potentially push them out here.
- // For now, we rely on the collision group preventing entry.
- // Log.Debug($"NPC {ToPrettyString(otherUid)} collided with active grace wall {ToPrettyString(uid)}.");
- }
- }
- }
|