@@ -1,6 +1,7 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Parallax;
|
||||
using Content.Client.Weather;
|
||||
using Content.Shared._CP14.WorldEdge;
|
||||
using Content.Shared.Salvage;
|
||||
using Content.Shared.Weather;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -72,6 +73,13 @@ public sealed partial class StencilOverlay : Overlay
|
||||
DrawRestrictedRange(args, restrictedRangeComponent, invMatrix);
|
||||
}
|
||||
|
||||
//CP14 World Edge overlay
|
||||
if (_entManager.TryGetComponent<CP14WorldEdgeComponent>(mapUid, out var worldEdge))
|
||||
{
|
||||
DrawWorldEdge(args, worldEdge, invMatrix);
|
||||
}
|
||||
//CP14 World Edge overlay end
|
||||
|
||||
args.WorldHandle.UseShader(null);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
57
Content.Client/_CP14/Overlays/StencilOverlay.WorldEdge.cs
Normal file
57
Content.Client/_CP14/Overlays/StencilOverlay.WorldEdge.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared._CP14.WorldEdge;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed partial class StencilOverlay
|
||||
{
|
||||
private void DrawWorldEdge(in OverlayDrawArgs args, CP14WorldEdgeComponent rangeComp, Matrix3x2 invMatrix)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var renderScale = args.Viewport.RenderScale.X;
|
||||
// TODO: This won't handle non-standard zooms so uhh yeah, not sure how to structure it on the shader side.
|
||||
var zoom = args.Viewport.Eye?.Zoom ?? Vector2.One;
|
||||
var length = zoom.X;
|
||||
var bufferRange = MathF.Min(10f, rangeComp.Range);
|
||||
|
||||
var pixelCenter = Vector2.Transform(rangeComp.Origin, invMatrix);
|
||||
// Something something offset?
|
||||
var vertical = args.Viewport.Size.Y;
|
||||
|
||||
var pixelMaxRange = rangeComp.Range * renderScale / length * EyeManager.PixelsPerMeter;
|
||||
var pixelBufferRange = bufferRange * renderScale / length * EyeManager.PixelsPerMeter;
|
||||
var pixelMinRange = pixelMaxRange - pixelBufferRange;
|
||||
|
||||
_shader.SetParameter("position", new Vector2(pixelCenter.X, vertical - pixelCenter.Y));
|
||||
_shader.SetParameter("maxRange", pixelMaxRange);
|
||||
_shader.SetParameter("minRange", pixelMinRange);
|
||||
_shader.SetParameter("bufferRange", pixelBufferRange);
|
||||
_shader.SetParameter("gradient", 0.80f);
|
||||
|
||||
var worldAABB = args.WorldAABB;
|
||||
var worldBounds = args.WorldBounds;
|
||||
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
|
||||
var localAABB = invMatrix.TransformBox(worldAABB);
|
||||
|
||||
// Cut out the irrelevant bits via stencil
|
||||
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
||||
// particularly for planet maps or stations.
|
||||
worldHandle.RenderInRenderTarget(_blep!, () =>
|
||||
{
|
||||
worldHandle.UseShader(_shader);
|
||||
worldHandle.DrawRect(localAABB, Color.White);
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
|
||||
|
||||
// Draw the rain
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.2f, 0.1f));
|
||||
}
|
||||
}
|
||||
143
Content.Server/_CP14/WorldEdge/CP14WorldEdgeSystem.cs
Normal file
143
Content.Server/_CP14/WorldEdge/CP14WorldEdgeSystem.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared._CP14.WorldEdge;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._CP14.WorldEdge;
|
||||
|
||||
public sealed class CP14WorldEdgeSystem : CP14SharedWorldEdgeSystem
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] protected readonly ISharedAdminLogManager AdminLog = default!;
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CP14WorldEdgeComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
SubscribeLocalEvent<CP14WorldBoundingComponent, StartCollideEvent>(OnWorldEdgeCollide);
|
||||
SubscribeLocalEvent<CP14WorldRemovePendingComponent, EntityUnpausedEvent>(OnPendingUnpaused);
|
||||
}
|
||||
|
||||
private void OnPendingUnpaused(Entity<CP14WorldRemovePendingComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.RemoveTime += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<CP14WorldRemovePendingComponent>();
|
||||
while (query.MoveNext(out var uid, out var pending))
|
||||
{
|
||||
if (pending.RemoveTime >= _timing.CurTime)
|
||||
continue;
|
||||
|
||||
if (Paused(uid))
|
||||
continue;
|
||||
|
||||
if (pending.Bounding == null)
|
||||
{
|
||||
CancelRemoving((uid, pending));
|
||||
continue;
|
||||
}
|
||||
|
||||
var entPos = _transform.GetWorldPosition(uid);
|
||||
var originPos = _transform.GetWorldPosition(pending.Bounding.Value);
|
||||
var distance = Vector2.Distance(entPos, originPos);
|
||||
|
||||
if (distance > pending.Bounding.Value.Comp.Range)
|
||||
{
|
||||
RoundRemoveMind((uid, pending));
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelRemoving((uid, pending));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RoundRemoveMind(Entity<CP14WorldRemovePendingComponent> ent)
|
||||
{
|
||||
AdminLog.Add(
|
||||
LogType.Action,
|
||||
LogImpact.High,
|
||||
$"{ToPrettyString(ent):player} has left the playing area, and is out of the round.");
|
||||
|
||||
QueueDel(ent);
|
||||
}
|
||||
|
||||
private void CancelRemoving(Entity<CP14WorldRemovePendingComponent> ent)
|
||||
{
|
||||
RemComp<CP14WorldRemovePendingComponent>(ent);
|
||||
|
||||
if (TryComp<ActorComponent>(ent, out var actor))
|
||||
{
|
||||
var msg = Loc.GetString("cp14-world-edge-cancel-removing-message");
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, msg, ent, false, actor.PlayerSession.Channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWorldEdgeCollide(Entity<CP14WorldBoundingComponent> bounding, ref StartCollideEvent args)
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(args.OtherEntity, out var mindContainer))
|
||||
return;
|
||||
|
||||
if (TryComp<ActorComponent>(args.OtherEntity, out var actor) &&
|
||||
!HasComp<CP14WorldRemovePendingComponent>(args.OtherEntity))
|
||||
{
|
||||
var msg = Loc.GetString("cp14-world-edge-pre-remove-message",
|
||||
("second", bounding.Comp.ReturnTime.TotalSeconds));
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, msg, args.OtherEntity, false, actor.PlayerSession.Channel);
|
||||
}
|
||||
|
||||
var removePending = EnsureComp<CP14WorldRemovePendingComponent>(args.OtherEntity);
|
||||
removePending.RemoveTime = _timing.CurTime + bounding.Comp.ReturnTime;
|
||||
removePending.Bounding = bounding;
|
||||
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<CP14WorldEdgeComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.BoundaryEntity = CreateBoundary(new EntityCoordinates(ent, ent.Comp.Origin), ent.Comp.Range);
|
||||
}
|
||||
|
||||
public EntityUid CreateBoundary(EntityCoordinates coordinates, float range)
|
||||
{
|
||||
var boundaryUid = Spawn(null, coordinates);
|
||||
var boundaryPhysics = AddComp<PhysicsComponent>(boundaryUid);
|
||||
var cShape = new ChainShape();
|
||||
// Don't need it to be a perfect circle, just need it to be loosely accurate.
|
||||
cShape.CreateLoop(Vector2.Zero, range + 0.25f, false, 4);
|
||||
_fixtures.TryCreateFixture(
|
||||
boundaryUid,
|
||||
cShape,
|
||||
"boundary",
|
||||
collisionLayer: (int) (CollisionGroup.HighImpassable | CollisionGroup.Impassable | CollisionGroup.LowImpassable),
|
||||
body: boundaryPhysics,
|
||||
hard: false);
|
||||
|
||||
_physics.WakeBody(boundaryUid, body: boundaryPhysics);
|
||||
var bounding = AddComp<CP14WorldBoundingComponent>(boundaryUid);
|
||||
bounding.Range = range + 0.25f;
|
||||
return boundaryUid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared._CP14.WorldEdge;
|
||||
|
||||
public abstract class CP14SharedWorldEdgeSystem : EntitySystem
|
||||
{
|
||||
}
|
||||
14
Content.Shared/_CP14/WorldEdge/CP14WorldBoundingComponent.cs
Normal file
14
Content.Shared/_CP14/WorldEdge/CP14WorldBoundingComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Content.Shared._CP14.WorldEdge;
|
||||
|
||||
/// <summary>
|
||||
/// when colliding with a player, starts a timer to remove him from the round.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedWorldEdgeSystem))]
|
||||
public sealed partial class CP14WorldBoundingComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan ReturnTime = TimeSpan.FromSeconds(15f);
|
||||
|
||||
[DataField]
|
||||
public float Range = 0f;
|
||||
}
|
||||
20
Content.Shared/_CP14/WorldEdge/CP14WorldEdgeComponent.cs
Normal file
20
Content.Shared/_CP14/WorldEdge/CP14WorldEdgeComponent.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._CP14.WorldEdge;
|
||||
|
||||
/// <summary>
|
||||
/// creates a world boundary that removes players who pass through it
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedWorldEdgeSystem))]
|
||||
public sealed partial class CP14WorldEdgeComponent : Component
|
||||
{
|
||||
[DataField(required: true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Range = 20f;
|
||||
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 Origin;
|
||||
|
||||
[DataField]
|
||||
public EntityUid BoundaryEntity;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Content.Shared._CP14.WorldEdge;
|
||||
|
||||
/// <summary>
|
||||
/// when colliding with a player, starts a timer to remove him from the round.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedWorldEdgeSystem))]
|
||||
public sealed partial class CP14WorldRemovePendingComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan RemoveTime;
|
||||
|
||||
[DataField]
|
||||
public Entity<CP14WorldBoundingComponent>? Bounding;
|
||||
}
|
||||
2
Resources/Locale/en-US/_CP14/worldEdge/world-edge.ftl
Normal file
2
Resources/Locale/en-US/_CP14/worldEdge/world-edge.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
cp14-world-edge-pre-remove-message = [color=red]CAUTION![/color] You are leaving the game zone! If you do not return within [color=red]{$second}[/color] seconds, you will be permanently removed from the round!
|
||||
cp14-world-edge-cancel-removing-message = The exit round has been canceled.
|
||||
2
Resources/Locale/ru-RU/_CP14/worldEdge/world-edge.ftl
Normal file
2
Resources/Locale/ru-RU/_CP14/worldEdge/world-edge.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
cp14-world-edge-pre-remove-message = [color=red]ВНИМАНИЕ![/color] Вы покидаете игровую зону! Если вы не вернетесь назад в течении [color=red]{$second}[/color] секунд, вы будете окончательно удалены из раунда!
|
||||
cp14-world-edge-cancel-removing-message = Выход из раунда отменен.
|
||||
@@ -36,6 +36,8 @@ entities:
|
||||
- type: OccluderTree
|
||||
- type: LoadedMap
|
||||
- type: MapLight
|
||||
- type: CP14WorldEdge
|
||||
range: 30
|
||||
- type: CP14DayCycle
|
||||
timeEntries:
|
||||
- duration: 80
|
||||
|
||||
Reference in New Issue
Block a user