using System.Numerics;
using Content.Server.Physics.Components;
using Content.Shared.Follower.Components;
using Content.Shared.Throwing;
using Robust.Server.GameObjects;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Physics.Controllers;
///
/// The entity system responsible for managing s.
/// Handles updating the direction they move in when their cooldown elapses.
///
internal sealed class RandomWalkController : VirtualController
{
#region Dependencies
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
#endregion Dependencies
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnRandomWalkStartup);
}
///
/// Updates the cooldowns of all random walkers.
/// If each of them is off cooldown it updates their velocity and resets its cooldown.
///
/// ??? Not documented anywhere I can see ??? // TODO: Document this.
/// The amount of time that has elapsed since the last time random walk cooldowns were updated.
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
var query = EntityQueryEnumerator();
while (query.MoveNext(out var uid, out var randomWalk, out var physics))
{
if (EntityManager.HasComponent(uid)
|| EntityManager.HasComponent(uid)
|| EntityManager.HasComponent(uid))
continue;
var curTime = _timing.CurTime;
if (randomWalk.NextStepTime <= curTime)
Update(uid, randomWalk, physics);
}
}
///
/// Updates the direction and speed a random walker is moving at.
/// Also resets the random walker's cooldown.
///
/// The random walker state.
/// The physics body associated with the random walker.
public void Update(EntityUid uid, RandomWalkComponent? randomWalk = null, PhysicsComponent? physics = null)
{
if(!Resolve(uid, ref randomWalk))
return;
var curTime = _timing.CurTime;
randomWalk.NextStepTime = curTime + TimeSpan.FromSeconds(_random.NextDouble(randomWalk.MinStepCooldown.TotalSeconds, randomWalk.MaxStepCooldown.TotalSeconds));
if(!Resolve(uid, ref physics))
return;
var pushVec = _random.NextAngle().ToVec();
pushVec += randomWalk.BiasVector;
pushVec.Normalize();
if (randomWalk.ResetBiasOnWalk)
randomWalk.BiasVector *= 0f;
var pushStrength = _random.NextFloat(randomWalk.MinSpeed, randomWalk.MaxSpeed);
_physics.SetLinearVelocity(uid, physics.LinearVelocity * randomWalk.AccumulatorRatio, body: physics);
_physics.ApplyLinearImpulse(uid, pushVec * (pushStrength * physics.Mass), body: physics);
}
///
/// Syncs up a random walker step timing when the component starts up.
///
/// The uid of the random walker to start up.
/// The state of the random walker to start up.
/// The startup prompt arguments.
private void OnRandomWalkStartup(EntityUid uid, RandomWalkComponent comp, ComponentStartup args)
{
if (comp.StepOnStartup)
Update(uid, comp);
else
comp.NextStepTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextDouble(comp.MinStepCooldown.TotalSeconds, comp.MaxStepCooldown.TotalSeconds));
}
}