mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-19 22:49:01 +03:00
* Уэээээээ * Почти настрадались * Скоро конец.... * СКОРО * Мышки плакали, кололись, но продолжали упорно жрать кактус * Все ближе! * Это такой конец? * Книжка говна * фиксики * ОНО ЖИВОЕ * Телепорт * разное * Added byond * ивенты теперь работают * Разфикс телепорта * Свет мой зеркальце скажи, да всю правду доложи - Я ль робастней всех на свете? * Разное * Еще многа всего * Многа разнава * Скоро конец.... * ЭТО КОНЕЦ * Фикс линтера (ну, или я на это надеюсь) * Еще один фикс линтера * Победа! * фиксики * пу пу пу * Фикс подмастерья * Мисклик * Высокочастотный меч * Неймспейсы * Пул способностей мага
167 lines
6.9 KiB
C#
167 lines
6.9 KiB
C#
// SPDX-FileCopyrightText: 2024 Ilya246 <57039557+Ilya246@users.noreply.github.com>
|
|
// SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
|
|
// SPDX-FileCopyrightText: 2024 whateverusername0 <whateveremail>
|
|
// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
|
|
// SPDX-FileCopyrightText: 2025 Misandry <mary@thughunt.ing>
|
|
// SPDX-FileCopyrightText: 2025 gus <august.eymann@gmail.com>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
using System.Numerics;
|
|
using Content.Server._Shitcode.Teleportation.Components;
|
|
using Content.Server.Administration.Logs;
|
|
using Content.Server.Stack;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.Destructible.Thresholds;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Movement.Pulling.Components;
|
|
using Content.Shared.Movement.Pulling.Systems;
|
|
using Content.Shared.Physics;
|
|
using Content.Shared.Stacks;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Server._Shitcode.Teleportation.Systems;
|
|
|
|
public sealed class TeleportSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
|
[Dependency] private readonly PullingSystem _pullingSystem = default!;
|
|
[Dependency] private readonly IAdminLogManager _alog = default!;
|
|
[Dependency] private readonly StackSystem _stack = default!;
|
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
|
[Dependency] private readonly IPrototypeManager _prot = default!;
|
|
|
|
private EntityQuery<PhysicsComponent> _physicsQuery;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<RandomTeleportOnUseComponent, UseInHandEvent>(OnUseInHand);
|
|
|
|
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
|
}
|
|
|
|
private void OnUseInHand(EntityUid uid, RandomTeleportOnUseComponent component, UseInHandEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
RandomTeleport(args.User, component);
|
|
|
|
if (component.ConsumeOnUse)
|
|
{
|
|
if (TryComp<StackComponent>(uid, out var stack))
|
|
{
|
|
_stack.SetCount(uid, stack.Count - 1, stack);
|
|
return;
|
|
}
|
|
|
|
// It's consumed on use and it's not a stack so delete it
|
|
QueueDel(uid);
|
|
}
|
|
|
|
_alog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):actor} randomly teleported with {ToPrettyString(uid)}");
|
|
}
|
|
|
|
public void RandomTeleport(EntityUid uid, RandomTeleportComponent component, bool playSound = true)
|
|
{
|
|
// play sound before and after teleport if playSound is true
|
|
if (playSound)
|
|
_audio.PlayPvs(component.DepartureSound, Transform(uid).Coordinates, AudioParams.Default);
|
|
|
|
RandomTeleport(uid, component.Radius, component.TeleportAttempts, component.ForceSafeTeleport);
|
|
|
|
if (playSound)
|
|
_audio.PlayPvs(component.ArrivalSound, Transform(uid).Coordinates, AudioParams.Default);
|
|
|
|
}
|
|
|
|
public Vector2 GetTeleportVector(float minRadius, float extraRadius)
|
|
{
|
|
// Generate a random number from 0 to 1 and multiply by radius to get distance we should teleport to
|
|
// A square root is taken from the random number so we get an uniform distribution of teleports, else you would get more teleports close to you
|
|
var distance = minRadius + extraRadius * MathF.Sqrt(_random.NextFloat());
|
|
// Generate a random vector with the length we've chosen
|
|
return _random.NextAngle().ToVec() * distance;
|
|
}
|
|
|
|
public void RandomTeleport(EntityUid uid, MinMax radius, int triesBase = 10, bool forceSafe = true)
|
|
{
|
|
var xform = Transform(uid);
|
|
// break any active pulls e.g. secoff pulling you with cuffs
|
|
if (TryComp<PullableComponent>(uid, out var pullable) && _pullingSystem.IsPulled(uid, pullable))
|
|
_pullingSystem.TryStopPull(uid, pullable, ignoreGrab: true);
|
|
|
|
// if we teleport the pulled entity goes with us
|
|
EntityUid? pullableEntity = null;
|
|
if (TryComp<PullerComponent>(uid, out var puller))
|
|
pullableEntity = puller.Pulling;
|
|
|
|
var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform);
|
|
|
|
var targetCoords = new MapCoordinates();
|
|
// Randomly picks tiles in range until it finds a valid tile
|
|
// If attempts is 1 or less, degenerates to a completely random teleport
|
|
var tries = triesBase;
|
|
// If forcing a safe teleport, try double the attempts but gradually lower radius in the second half of them
|
|
if (forceSafe)
|
|
tries *= 2;
|
|
// How far outwards from the minimum radius we can teleport
|
|
var extraRadiusBase = radius.Max - radius.Min;
|
|
var foundValid = false;
|
|
for (var i = 0; i < tries; i++)
|
|
{
|
|
var extraRadius = extraRadiusBase;
|
|
// If we're trying to force a safe teleport and haven't found a valid destination in a while, gradually lower the search radius so we're searching in a smaller area
|
|
if (forceSafe && i >= triesBase)
|
|
extraRadius *= (tries - i) / triesBase;
|
|
|
|
targetCoords = entityCoords.Offset(GetTeleportVector(radius.Min, extraRadius));
|
|
|
|
// Try to not teleport into open space
|
|
if (!_mapManager.TryFindGridAt(targetCoords, out var gridUid, out var grid))
|
|
continue;
|
|
// Check if we picked a position inside a solid object
|
|
var valid = true;
|
|
foreach (var entity in grid.GetAnchoredEntities(targetCoords))
|
|
{
|
|
if (!_physicsQuery.TryGetComponent(entity, out var body))
|
|
continue;
|
|
|
|
if (body.BodyType != BodyType.Static ||
|
|
!body.Hard ||
|
|
(body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
|
|
continue;
|
|
|
|
valid = false;
|
|
break;
|
|
}
|
|
// Current target coordinates are not inside a solid body, can go ahead and teleport
|
|
if (valid)
|
|
{
|
|
foundValid = true;
|
|
break;
|
|
}
|
|
}
|
|
// We haven't found a valid teleport, so just teleport to any spot in range
|
|
if (!foundValid)
|
|
targetCoords = entityCoords.Offset(GetTeleportVector(radius.Min, extraRadiusBase));
|
|
|
|
_xform.SetWorldPosition(uid, targetCoords.Position);
|
|
// pulled entity goes with us
|
|
if (pullableEntity != null)
|
|
_xform.SetWorldPosition((EntityUid) pullableEntity, _xform.GetWorldPosition(uid));
|
|
}
|
|
}
|