using System.Numerics; using Content.Client.Parallax.Data; using Content.Client.Parallax.Managers; using Content.Shared.Parallax; using Robust.Client.Graphics; using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Client.Parallax; public sealed class ParallaxSystem : SharedParallaxSystem { [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IOverlayManager _overlay = default!; [Dependency] private readonly IParallaxManager _parallax = default!; [ValidatePrototypeId] private const string Fallback = "Default"; public const int ParallaxZIndex = 0; public override void Initialize() { base.Initialize(); _overlay.AddOverlay(new ParallaxOverlay()); SubscribeLocalEvent(OnReload); SubscribeLocalEvent(OnAfterAutoHandleState); } private void OnReload(PrototypesReloadedEventArgs obj) { if (!obj.WasModified()) return; _parallax.UnloadParallax(Fallback); _parallax.LoadDefaultParallax(); foreach (var comp in EntityQuery(true)) { _parallax.UnloadParallax(comp.Parallax); _parallax.LoadParallaxByName(comp.Parallax); } } public override void Shutdown() { base.Shutdown(); _overlay.RemoveOverlay(); } private void OnAfterAutoHandleState(EntityUid uid, ParallaxComponent component, ref AfterAutoHandleStateEvent args) { if (!_parallax.IsLoaded(component.Parallax)) { _parallax.LoadParallaxByName(component.Parallax); } } public ParallaxLayerPrepared[] GetParallaxLayers(MapId mapId) { return _parallax.GetParallaxLayers(GetParallax(_map.GetMapEntityId(mapId))); } public string GetParallax(MapId mapId) { return GetParallax(_map.GetMapEntityId(mapId)); } public string GetParallax(EntityUid mapUid) { return TryComp(mapUid, out var parallax) ? parallax.Parallax : Fallback; } /// /// Draws a texture as parallax in the specified world handle. /// /// /// WorldAABB to use /// Sprite to draw /// Current time, unused if scrolling not set /// Current position of the parallax /// How much to scroll the parallax texture per second /// Scale of the texture /// How slow the parallax moves compared to position /// Color modulation applied to drawing the texture public void DrawParallax( DrawingHandleWorld worldHandle, Box2 worldAABB, Texture sprite, TimeSpan curTime, Vector2 position, Vector2 scrolling, float scale = 1f, float slowness = 0f, Color? modulate = null) { // Size of the texture in world units. var size = sprite.Size / (float) EyeManager.PixelsPerMeter * scale; var scrolled = scrolling * (float) curTime.TotalSeconds; // Origin - start with the parallax shift itself. var originBL = position * slowness + scrolled; // Centre the image. originBL -= size / 2; // Remove offset so we can floor. var flooredBL = worldAABB.BottomLeft - originBL; // Floor to background size. flooredBL = (flooredBL / size).Floored() * size; // Re-offset. flooredBL += originBL; for (var x = flooredBL.X; x < worldAABB.Right; x += size.X) { for (var y = flooredBL.Y; y < worldAABB.Top; y += size.Y) { var box = Box2.FromDimensions(new Vector2(x, y), size); worldHandle.DrawTextureRect(sprite, box, modulate); } } } }