mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-18 05:59:03 +03:00
# Description This PR adds several new features for Space Wind to make it far less annoying to players. Notably, it now includes the first new Space Wind Visuals, whereby visual effects will periodically appear on top of space wind sources. They also visually move in the same direction that the wind is currently throwing at that source. <details><summary><h1>Media</h1></summary> <p> https://github.com/user-attachments/assets/858a3433-607a-4513-9acf-b60b4eb6e745 </p> </details> # Changelog 🆑 - add: Added visual effects to Space Wind, allowing players to see motes of dust being blown about by wind, which provides an early warning that space winds are in effect. - tweak: tweaked the default "aggression" values of space wind to make it half as angry and sensitive. - add: Added a new equation modeling character height and mass as a means of resisting being knocked down by space wind. The taller and heavier you are, the more resistant you are to being knocked down by air currents. This also means that air currents no longer automatically knock you down. - add: Harpies and Moths can now use their wings to fly around in air currents. - tweak: Tweaked the mass of various items and objects so that they are more or less resistant to space wind. Particularly tools, armor, lockers, and air canisters. - tweak: As a significant performance optimization, Space Wind no longer applies on Lavaland. --------- Signed-off-by: VMSolidus <evilexecutive@gmail.com> Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> (cherry picked from commit 97ef05f574b7f2e7d58df999e9b1c15d0353e725)
188 lines
8.6 KiB
C#
188 lines
8.6 KiB
C#
using Content.Server.Atmos.Components;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.Atmos.Components;
|
|
using Content.Shared.Humanoid;
|
|
using Content.Shared.Maps;
|
|
using Content.Shared.Projectiles;
|
|
using Content.Shared.Throwing;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Prototypes;
|
|
using System.Numerics;
|
|
|
|
namespace Content.Server.Atmos.EntitySystems;
|
|
|
|
public sealed partial class AtmosphereSystem
|
|
{
|
|
private EntProtoId _spaceWindProto = "SpaceWindVisual";
|
|
private readonly HashSet<Entity<MovedByPressureComponent>> _activePressures = new();
|
|
private void UpdateHighPressure(float frameTime)
|
|
{
|
|
foreach (var ent in _activePressures)
|
|
{
|
|
if (!ent.Comp.Throwing || _gameTiming.CurTime < ent.Comp.ThrowingCutoffTarget
|
|
|| !TryComp(ent.Owner, out PhysicsComponent? physics))
|
|
continue;
|
|
|
|
if (TryComp(ent.Owner, out ThrownItemComponent? thrown))
|
|
{
|
|
_thrown.LandComponent(ent.Owner, thrown, physics, true);
|
|
_thrown.StopThrow(ent.Owner, thrown);
|
|
}
|
|
|
|
_physics.SetBodyStatus(ent.Owner, physics, BodyStatus.OnGround);
|
|
_physics.SetSleepingAllowed(ent.Owner, physics, true);
|
|
|
|
ent.Comp.Throwing = false;
|
|
_activePressures.Remove(ent);
|
|
}
|
|
}
|
|
|
|
private void HighPressureMovements(Entity<GridAtmosphereComponent> gridAtmosphere,
|
|
TileAtmosphere tile,
|
|
EntityQuery<PhysicsComponent> bodies,
|
|
EntityQuery<TransformComponent> xforms,
|
|
EntityQuery<MovedByPressureComponent> pressureQuery,
|
|
EntityQuery<MetaDataComponent> metas,
|
|
EntityQuery<ProjectileComponent> projectileQuery,
|
|
double gravity)
|
|
{
|
|
var atmosComp = gridAtmosphere.Comp;
|
|
var oneAtmos = Atmospherics.OneAtmosphere;
|
|
|
|
// No atmos yeets, return early.
|
|
if (!SpaceWind
|
|
|| !gridAtmosphere.Comp.SpaceWindSimulation // Is the grid marked as exempt from space wind?
|
|
|| tile.Air is null || tile.Space // No Air Checks. Pressure differentials can't exist in a hard vacuum.
|
|
|| tile.Air.Pressure <= atmosComp.PressureCutoff // Below 5kpa(can't throw a base item)
|
|
|| oneAtmos - atmosComp.PressureCutoff <= tile.Air.Pressure
|
|
&& tile.Air.Pressure <= oneAtmos + atmosComp.PressureCutoff // Check within 5kpa of default pressure.
|
|
|| !TryComp(gridAtmosphere.Owner, out MapGridComponent? mapGrid)
|
|
|| !_mapSystem.TryGetTileRef(gridAtmosphere.Owner, mapGrid, tile.GridIndices, out var tileRef))
|
|
return;
|
|
|
|
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
|
|
if (!tileDef.SimulatedTurf)
|
|
return;
|
|
|
|
var partialFrictionComposition = gravity * tileDef.MobFrictionNoInput ?? 0.2f;
|
|
|
|
var pressureVector = GetPressureVectorFromTile(gridAtmosphere, tile);
|
|
if (!pressureVector.IsValid())
|
|
return;
|
|
tile.LastPressureDirection = pressureVector;
|
|
|
|
// Calculate this HERE so that we aren't running the square root of a whole Newton vector per item.
|
|
var pVecLength = pressureVector.Length();
|
|
if (pVecLength <= 1) // Then guard against extremely small vectors.
|
|
return;
|
|
|
|
pressureVector *= SpaceWindStrengthMultiplier;
|
|
|
|
if (SpaceWindVisuals && atmosComp.SpaceWindSoundCooldown == 0)
|
|
{
|
|
var location = _mapSystem.GridTileToLocal(gridAtmosphere.Owner, mapGrid, tile.GridIndices);
|
|
var visualEnt = SpawnAtPosition(_spaceWindProto, location);
|
|
_transformSystem.SetLocalRotation(visualEnt, pressureVector.ToAngle() - MathF.PI / 2);
|
|
}
|
|
|
|
if (pVecLength > 15 && !tile.Hotspot.Valid && atmosComp.SpaceWindSoundCooldown == 0)
|
|
{
|
|
var coordinates = _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.GridIndices);
|
|
var volume = Math.Clamp(pVecLength / atmosComp.SpaceWindSoundDenominator, atmosComp.SpaceWindSoundMinVolume, atmosComp.SpaceWindSoundMaxVolume);
|
|
_audio.PlayPvs(atmosComp.SpaceWindSound, coordinates, AudioParams.Default.WithVariation(0.125f).WithVolume(volume));
|
|
}
|
|
|
|
if (atmosComp.SpaceWindSoundCooldown++ > atmosComp.SpaceWindSoundCooldownCycles)
|
|
atmosComp.SpaceWindSoundCooldown = 0;
|
|
|
|
// TODO: Deprecated for now, it sucks ass and I'm disassembling monstermos because it sucks. This'll be handled by Space Wind after I'm done whiteboarding better equations for it.
|
|
// - TCJ
|
|
// HandleDecompressionFloorRip(mapGrid, otherTile, otherTile.PressureDifference);
|
|
|
|
_entSet.Clear();
|
|
_lookup.GetLocalEntitiesIntersecting(tile.GridIndex, tile.GridIndices, _entSet, 0f);
|
|
|
|
foreach (var entity in _entSet)
|
|
{
|
|
// Ideally containers would have their own EntityQuery internally or something given recursively it may need to slam GetComp<T> anyway.
|
|
// Also, don't care about static bodies (but also due to collisionwakestate can't query dynamic directly atm).
|
|
if (!bodies.TryGetComponent(entity, out var body)
|
|
|| !pressureQuery.TryGetComponent(entity, out var pressure)
|
|
|| !pressure.Enabled
|
|
|| _containers.IsEntityInContainer(entity, metas.GetComponent(entity))
|
|
|| pressure.LastHighPressureMovementAirCycle >= gridAtmosphere.Comp.UpdateCounter)
|
|
continue;
|
|
|
|
// tl;dr YEET
|
|
ExperiencePressureDifference(
|
|
(entity, pressure),
|
|
gridAtmosphere.Comp.UpdateCounter,
|
|
pressureVector,
|
|
pVecLength,
|
|
partialFrictionComposition,
|
|
projectileQuery,
|
|
xforms.GetComponent(entity),
|
|
body);
|
|
}
|
|
}
|
|
|
|
// Called from AtmosphereSystem.LINDA.cs with SpaceWind CVar check handled there.
|
|
private void ConsiderPressureDifference(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) => gridAtmosphere.HighPressureDelta.Add(tile);
|
|
|
|
public void ExperiencePressureDifference(Entity<MovedByPressureComponent> ent,
|
|
int cycle,
|
|
Vector2 pressureVector,
|
|
float pVecLength,
|
|
double partialFrictionComposition,
|
|
EntityQuery<ProjectileComponent> projectileQuery,
|
|
TransformComponent? xform = null,
|
|
PhysicsComponent? physics = null)
|
|
{
|
|
var (uid, component) = ent;
|
|
if (!Resolve(uid, ref physics, false)
|
|
|| !Resolve(uid, ref xform)
|
|
|| physics.BodyType == BodyType.Static
|
|
|| physics.LinearVelocity.Length() >= SpaceWindMaxForce)
|
|
return;
|
|
|
|
var alwaysThrow = partialFrictionComposition == 0 || physics.BodyStatus == BodyStatus.InAir;
|
|
|
|
// Coefficient of static friction in Newtons (kg * m/s^2), which might not apply under certain conditions.
|
|
var coefficientOfFriction = partialFrictionComposition * physics.Mass;
|
|
coefficientOfFriction *= _standingSystem.IsDown(uid) ? 3 : 1;
|
|
|
|
if (TryComp(ent.Owner, out HumanoidAppearanceComponent? humanoidAppearance))
|
|
{
|
|
pressureVector *= HumanoidThrowMultiplier;
|
|
|
|
if (SpaceWindAllowKnockdown)
|
|
{
|
|
// Torque threshold for a humanoid shaped object is 1/3rd mass * height squared. Ignore the 3, it's not a magic number in this context.
|
|
// Same with 1.75f, we're quick and dirty shorthanding for the standard height of a human (in meters).
|
|
var heightSquared = MathF.Pow(humanoidAppearance.Height * 1.75f, 2);
|
|
var knockdownThreshold = heightSquared / 3;
|
|
if (knockdownThreshold <= pVecLength)
|
|
_sharedStunSystem.TryKnockdown(uid, TimeSpan.FromSeconds(SpaceWindKnockdownTime), true);
|
|
}
|
|
}
|
|
|
|
if (!alwaysThrow && pVecLength < coefficientOfFriction)
|
|
return;
|
|
|
|
// Yes this technically increases the magnitude by a small amount... I detest having to swap between "World" and "Local" vectors.
|
|
// ThrowingSystem increments linear velocity by a given vector, but we have to do this anyways because reasons.
|
|
var velocity = _transformSystem.GetWorldRotation(uid).ToWorldVec() + pressureVector;
|
|
|
|
_throwing.TryThrow(uid, velocity, physics, xform, projectileQuery,
|
|
1, doSpin: physics.AngularVelocity < SpaceWindMaxAngularVelocity);
|
|
|
|
component.LastHighPressureMovementAirCycle = cycle;
|
|
component.Throwing = true;
|
|
component.ThrowingCutoffTarget = _gameTiming.CurTime + component.CutoffTime;
|
|
_activePressures.Add(ent);
|
|
}
|
|
}
|