mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 05:27:38 +03:00
# Description Parkour Training, Sluggish and Snail-Paced have been reworked to be more impactful in everyday space life. A new trait has been added, Bad Knees. **Parkour Training** (-5 points) - No longer modifies laying down - Climbing speed bonus reduced from 65% to 50% - 30% shorter slip stuns - 50% resistance against slows in difficult terrain - Example: Spider webs inflict a 50% slow, reduced by 50% results in a 25% slow. Notably, the reworked Parkour Training is much closer to the CDDA trait [Parkour Expert](https://cddawiki.danmakudan.com/wiki/index.php/Parkour_Expert). Difficult terrain examples: Spider webs, slime/blood puddles, kudzu, space glue **(NEW)** **Bad Knees** (3 points) - Designed to be the opposite of Parkour Training - 50% climbing speed penalty - 40% longer slip stuns - 35% increased slows in difficult terrain - Example: Spider webs inflict a 50% slow, increased by 35% results in a 67.5% slow. Inspired by the CDDA trait of the same name [Bad Knees](https://cddawiki.danmakudan.com/wiki/index.php/Bad_Knees). The stun time for banana peels and puddles is 3 seconds. With Bad Knees, the stun is increased to 4.2 seconds. The time it takes to handcuff someone is 3.5 seconds. Go figure :trollface: **Sluggish** (3 points) - No longer increases the time for laying down and climbing tables (given to Bad Knees instead) - Speed penalty increased from 15% to 16% **Snail-Paced** (5 points) - No longer increases the time for laying down and climbing tables - Speed penalty increased from 30% to 32% - No longer prevents you from slipping when running due to how slow your sprint speed is. Snail-Paced trait users must suffer. ## Media **Parkour Training**  **Bad Knees**  **Sluggish**  **Snail-Paced**  ## Changelog 🆑 Skubman - tweak: The Parkour Training trait has been reworked. It no longer makes you lie down or get up faster, but grants you 30% shorter slip stuns and 50% resistance against slows in difficult terrain. - tweak: The Sluggish and Snail-Paced traits now only reduce your movement speed with no other effects. The speed reductions have been slightly increased from 15% to 16% for Sluggish, and 30% to 32% for Snail-Paced. - add: Added a new 3-point negative trait called Bad Knees. It is the opposite of Parkour Training and it makes you climb tables slower, stunned for longer against slip stuns, and move more slowly in difficult terrain. - tweak: Snail-Paced no longer prevents you from slipping when running. (cherry picked from commit 89354922a0f6fb55111e0ce2b4b19e5f6d6ea0e2)
242 lines
8.9 KiB
C#
242 lines
8.9 KiB
C#
using Content.Shared.Gravity;
|
|
using Content.Shared.StepTrigger.Components;
|
|
using Content.Shared.Traits.Assorted.Components;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Physics.Events;
|
|
|
|
namespace Content.Shared.StepTrigger.Systems;
|
|
|
|
public sealed class StepTriggerSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
|
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
UpdatesOutsidePrediction = true;
|
|
SubscribeLocalEvent<StepTriggerComponent, AfterAutoHandleStateEvent>(TriggerHandleState);
|
|
|
|
SubscribeLocalEvent<StepTriggerComponent, StartCollideEvent>(OnStartCollide);
|
|
SubscribeLocalEvent<StepTriggerComponent, EndCollideEvent>(OnEndCollide);
|
|
#if DEBUG
|
|
SubscribeLocalEvent<StepTriggerComponent, ComponentStartup>(OnStartup);
|
|
}
|
|
private void OnStartup(EntityUid uid, StepTriggerComponent component, ComponentStartup args)
|
|
{
|
|
if (!component.Active)
|
|
return;
|
|
|
|
if (!TryComp(uid, out FixturesComponent? fixtures) || fixtures.FixtureCount == 0)
|
|
Log.Warning($"{ToPrettyString(uid)} has an active step trigger without any fixtures.");
|
|
#endif
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
var query = GetEntityQuery<PhysicsComponent>();
|
|
var enumerator = EntityQueryEnumerator<StepTriggerActiveComponent, StepTriggerComponent, TransformComponent>();
|
|
|
|
while (enumerator.MoveNext(out var uid, out var active, out var trigger, out var transform))
|
|
{
|
|
if (!Update(uid, trigger, transform, query))
|
|
continue;
|
|
|
|
RemCompDeferred(uid, active);
|
|
}
|
|
}
|
|
|
|
private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform, EntityQuery<PhysicsComponent> query)
|
|
{
|
|
if (!component.Active || component.Colliding.Count == 0)
|
|
return true;
|
|
|
|
if (component.Blacklist != null && TryComp<MapGridComponent>(transform.GridUid, out var grid))
|
|
{
|
|
var positon = _map.LocalToTile(transform.GridUid.Value, grid, transform.Coordinates);
|
|
var anch = _map.GetAnchoredEntitiesEnumerator(uid, grid, positon);
|
|
|
|
while (anch.MoveNext(out var ent))
|
|
{
|
|
if (ent == uid)
|
|
continue;
|
|
|
|
if (_whitelistSystem.IsBlacklistPass(component.Blacklist, ent.Value))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
foreach (var otherUid in component.Colliding)
|
|
UpdateColliding(uid, component, transform, otherUid, query);
|
|
|
|
return false;
|
|
}
|
|
|
|
private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerXform, EntityUid otherUid, EntityQuery<PhysicsComponent> query)
|
|
{
|
|
if (!query.TryGetComponent(otherUid, out var otherPhysics))
|
|
return;
|
|
|
|
var otherXform = Transform(otherUid);
|
|
// TODO: This shouldn't be calculating based on world AABBs.
|
|
var ourAabb = _entityLookup.GetAABBNoContainer(uid, ownerXform.LocalPosition, ownerXform.LocalRotation);
|
|
var otherAabb = _entityLookup.GetAABBNoContainer(otherUid, otherXform.LocalPosition, otherXform.LocalRotation);
|
|
|
|
if (!ourAabb.Intersects(otherAabb))
|
|
{
|
|
if (component.CurrentlySteppedOn.Remove(otherUid))
|
|
Dirty(uid, component);
|
|
|
|
return;
|
|
}
|
|
|
|
// max 'area of enclosure' between the two aabbs
|
|
// this is hard to explain
|
|
var intersect = Box2.Area(otherAabb.Intersect(ourAabb));
|
|
var ratio = Math.Max(intersect / Box2.Area(otherAabb), intersect / Box2.Area(ourAabb));
|
|
var requiredTriggeredSpeed = component.RequiredTriggeredSpeed;
|
|
if (TryComp<TraitSpeedModifierComponent>(otherUid, out var speedModifier))
|
|
requiredTriggeredSpeed *= speedModifier.RequiredTriggeredSpeedModifier;
|
|
|
|
if (otherPhysics.LinearVelocity.Length() < requiredTriggeredSpeed
|
|
|| component.CurrentlySteppedOn.Contains(otherUid)
|
|
|| ratio < component.IntersectRatio
|
|
|| !CanTrigger(uid, otherUid, component))
|
|
return;
|
|
|
|
if (component.StepOn)
|
|
{
|
|
var evStep = new StepTriggeredOnEvent(uid, otherUid);
|
|
RaiseLocalEvent(uid, ref evStep);
|
|
}
|
|
else
|
|
{
|
|
var evStep = new StepTriggeredOffEvent(uid, otherUid);
|
|
RaiseLocalEvent(uid, ref evStep);
|
|
}
|
|
|
|
component.CurrentlySteppedOn.Add(otherUid);
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private bool CanTrigger(EntityUid uid, EntityUid otherUid, StepTriggerComponent component)
|
|
{
|
|
if (!component.Active
|
|
|| component.CurrentlySteppedOn.Contains(otherUid))
|
|
return false;
|
|
|
|
// Immunity checks
|
|
if (TryComp<StepTriggerImmuneComponent>(otherUid, out var stepTriggerImmuneComponent)
|
|
&& component.TriggerGroups != null
|
|
&& component.TriggerGroups.IsValid(stepTriggerImmuneComponent))
|
|
return false;
|
|
|
|
// Can't trigger if we don't ignore weightless entities
|
|
// and the entity is flying or currently weightless
|
|
// Makes sense simulation wise to have this be part of steptrigger directly IMO
|
|
if (!component.IgnoreWeightless && TryComp<PhysicsComponent>(otherUid, out var physics) &&
|
|
(physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid, physics)))
|
|
return false;
|
|
|
|
var msg = new StepTriggerAttemptEvent { Source = uid, Tripper = otherUid };
|
|
RaiseLocalEvent(uid, ref msg);
|
|
|
|
return msg.Continue && !msg.Cancelled;
|
|
}
|
|
|
|
private void OnStartCollide(EntityUid uid, StepTriggerComponent component, ref StartCollideEvent args)
|
|
{
|
|
var otherUid = args.OtherEntity;
|
|
|
|
if (!args.OtherFixture.Hard
|
|
|| !CanTrigger(uid, otherUid, component))
|
|
return;
|
|
|
|
EnsureComp<StepTriggerActiveComponent>(uid);
|
|
|
|
if (component.Colliding.Add(otherUid))
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
private void OnEndCollide(EntityUid uid, StepTriggerComponent component, ref EndCollideEvent args)
|
|
{
|
|
var otherUid = args.OtherEntity;
|
|
|
|
if (!component.Colliding.Remove(otherUid))
|
|
return;
|
|
|
|
component.CurrentlySteppedOn.Remove(otherUid);
|
|
Dirty(uid, component);
|
|
|
|
if (component.StepOn)
|
|
{
|
|
var evStepOff = new StepTriggeredOffEvent(uid, otherUid);
|
|
RaiseLocalEvent(uid, ref evStepOff);
|
|
}
|
|
|
|
if (component.Colliding.Count == 0)
|
|
RemCompDeferred<StepTriggerActiveComponent>(uid);
|
|
}
|
|
|
|
private void TriggerHandleState(EntityUid uid, StepTriggerComponent component, ref AfterAutoHandleStateEvent args)
|
|
{
|
|
if (component.Colliding.Count > 0)
|
|
EnsureComp<StepTriggerActiveComponent>(uid);
|
|
else
|
|
RemCompDeferred<StepTriggerActiveComponent>(uid);
|
|
}
|
|
|
|
public void SetIntersectRatio(EntityUid uid, float ratio, StepTriggerComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component)
|
|
|| MathHelper.CloseToPercent(component.IntersectRatio, ratio))
|
|
return;
|
|
|
|
component.IntersectRatio = ratio;
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
public void SetRequiredTriggerSpeed(EntityUid uid, float speed, StepTriggerComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component)
|
|
|| MathHelper.CloseToPercent(component.RequiredTriggeredSpeed, speed))
|
|
return;
|
|
|
|
component.RequiredTriggeredSpeed = speed;
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
public void SetActive(EntityUid uid, bool active, StepTriggerComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component)
|
|
|| active == component.Active)
|
|
return;
|
|
|
|
component.Active = active;
|
|
Dirty(uid, component);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised at the beginning of a step trigger, and before entering the checks.
|
|
/// Allows for entities to end the steptrigger early via args.Cancelled.
|
|
/// </summary>
|
|
[ByRefEvent]
|
|
public record struct StepTriggerAttemptEvent(EntityUid Source, EntityUid Tripper, bool Continue, bool Cancelled);
|
|
|
|
/// <summary>
|
|
/// Raised when an entity stands on a steptrigger initially (assuming it has both on and off states).
|
|
/// </summary>
|
|
[ByRefEvent]
|
|
public readonly record struct StepTriggeredOnEvent(EntityUid Source, EntityUid Tripper);
|
|
|
|
/// <summary>
|
|
/// Raised when an entity leaves a steptrigger if it has on and off states OR when an entity intersects a steptrigger.
|
|
/// </summary>
|
|
[ByRefEvent]
|
|
public readonly record struct StepTriggeredOffEvent(EntityUid Source, EntityUid Tripper);
|