Skip to content

Commit

Permalink
Mineral Scanner (space-wizards#31390)
Browse files Browse the repository at this point in the history
* Mineral Scanner

* doink

* review

* sunday funday

* review and fix bugs i think?

* Update MiningOverlay.cs
  • Loading branch information
EmoGarbage404 authored Sep 6, 2024
1 parent 582a644 commit 8599251
Show file tree
Hide file tree
Showing 25 changed files with 568 additions and 23 deletions.
96 changes: 96 additions & 0 deletions Content.Client/Mining/MiningOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System.Numerics;
using Content.Shared.Mining.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace Content.Client.Mining;

public sealed class MiningOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
private readonly EntityLookupSystem _lookup;
private readonly SpriteSystem _sprite;
private readonly TransformSystem _xform;

private readonly EntityQuery<SpriteComponent> _spriteQuery;
private readonly EntityQuery<TransformComponent> _xformQuery;

public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => false;

private readonly HashSet<Entity<MiningScannerViewableComponent>> _viewableEnts = new();

public MiningOverlay()
{
IoCManager.InjectDependencies(this);

_lookup = _entityManager.System<EntityLookupSystem>();
_sprite = _entityManager.System<SpriteSystem>();
_xform = _entityManager.System<TransformSystem>();

_spriteQuery = _entityManager.GetEntityQuery<SpriteComponent>();
_xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
}

protected override void Draw(in OverlayDrawArgs args)
{
var handle = args.WorldHandle;

if (_player.LocalEntity is not { } localEntity ||
!_entityManager.TryGetComponent<MiningScannerViewerComponent>(localEntity, out var viewerComp))
return;

if (viewerComp.LastPingLocation == null)
return;

var scaleMatrix = Matrix3Helpers.CreateScale(Vector2.One);

_viewableEnts.Clear();
_lookup.GetEntitiesInRange(viewerComp.LastPingLocation.Value, viewerComp.ViewRange, _viewableEnts);
foreach (var ore in _viewableEnts)
{
if (!_xformQuery.TryComp(ore, out var xform) ||
!_spriteQuery.TryComp(ore, out var sprite))
continue;

if (xform.MapID != args.MapId || !sprite.Visible)
continue;

if (!sprite.LayerMapTryGet(MiningScannerVisualLayers.Overlay, out var idx))
continue;
var layer = sprite[idx];

if (layer.ActualRsi?.Path == null || layer.RsiState.Name == null)
continue;

var gridRot = xform.GridUid == null ? 0 : _xformQuery.CompOrNull(xform.GridUid.Value)?.LocalRotation ?? 0;
var rotationMatrix = Matrix3Helpers.CreateRotation(gridRot);

var worldMatrix = Matrix3Helpers.CreateTranslation(_xform.GetWorldPosition(xform));
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
handle.SetTransform(matty);

var spriteSpec = new SpriteSpecifier.Rsi(layer.ActualRsi.Path, layer.RsiState.Name);
var texture = _sprite.GetFrame(spriteSpec, TimeSpan.FromSeconds(layer.AnimationTime));

var animTime = (viewerComp.NextPingTime - _timing.CurTime).TotalSeconds;


var alpha = animTime < viewerComp.AnimationDuration
? 0
: (float) Math.Clamp((animTime - viewerComp.AnimationDuration) / viewerComp.AnimationDuration, 0f, 1f);
var color = Color.White.WithAlpha(alpha);

handle.DrawTexture(texture, -(Vector2) texture.Size / 2f / EyeManager.PixelsPerMeter, layer.Rotation, modulate: color);

}
handle.SetTransform(Matrix3x2.Identity);
}
}
54 changes: 54 additions & 0 deletions Content.Client/Mining/MiningOverlaySystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Content.Shared.Mining.Components;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;

namespace Content.Client.Mining;

/// <summary>
/// This handles the lifetime of the <see cref="MiningOverlay"/> for a given entity.
/// </summary>
public sealed class MiningOverlaySystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;

private MiningOverlay _overlay = default!;

/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<MiningScannerViewerComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<MiningScannerViewerComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<MiningScannerViewerComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<MiningScannerViewerComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);

_overlay = new();
}

private void OnPlayerAttached(Entity<MiningScannerViewerComponent> ent, ref LocalPlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}

private void OnPlayerDetached(Entity<MiningScannerViewerComponent> ent, ref LocalPlayerDetachedEvent args)
{
_overlayMan.RemoveOverlay(_overlay);
}

private void OnInit(Entity<MiningScannerViewerComponent> ent, ref ComponentInit args)
{
if (_player.LocalEntity == ent)
{
_overlayMan.AddOverlay(_overlay);
}
}

private void OnShutdown(Entity<MiningScannerViewerComponent> ent, ref ComponentShutdown args)
{
if (_player.LocalEntity == ent)
{
_overlayMan.RemoveOverlay(_overlay);
}
}
}
6 changes: 0 additions & 6 deletions Content.Client/Mining/OreVeinVisualsComponent.cs

This file was deleted.

2 changes: 1 addition & 1 deletion Content.Server/Mining/MiningSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Content.Server.Mining.Components;
using Content.Shared.Destructible;
using Content.Shared.Mining;
using Content.Shared.Mining.Components;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Robust.Shared.Prototypes;
Expand Down
2 changes: 1 addition & 1 deletion Content.Shared/Item/ItemToggle/ItemToggleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ private void UpdateActiveSound(Entity<ItemToggleActiveSoundComponent> ent, ref I

if (comp.ActiveSound != null && comp.PlayingStream == null)
{
var loop = AudioParams.Default.WithLoop(true);
var loop = comp.ActiveSound.Params.WithLoop(true);
var stream = args.Predicted
? _audio.PlayPredicted(comp.ActiveSound, uid, args.User, loop)
: _audio.PlayPvs(comp.ActiveSound, uid, loop);
Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/Mining/Components/MiningScannerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Mining.Components;

/// <summary>
/// This is a component that, when held in the inventory or pocket of a player, gives the the MiningOverlay.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(MiningScannerSystem))]
public sealed partial class MiningScannerComponent : Component
{
[DataField]
public float Range = 5;
}
13 changes: 13 additions & 0 deletions Content.Shared/Mining/Components/MiningScannerViewableComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;

namespace Content.Shared.Mining.Components;

[RegisterComponent, NetworkedComponent, Access(typeof(MiningScannerSystem))]
public sealed partial class MiningScannerViewableComponent : Component;

[Serializable, NetSerializable]
public enum MiningScannerVisualLayers : byte
{
Overlay
}
36 changes: 36 additions & 0 deletions Content.Shared/Mining/Components/MiningScannerViewerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Map;

namespace Content.Shared.Mining.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause, Access(typeof(MiningScannerSystem))]
public sealed partial class MiningScannerViewerComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public float ViewRange;

[DataField, AutoNetworkedField]
public float AnimationDuration = 1.5f;

[DataField, AutoNetworkedField]
public TimeSpan PingDelay = TimeSpan.FromSeconds(5);

[DataField, AutoNetworkedField, AutoPausedField]
public TimeSpan NextPingTime = TimeSpan.MaxValue;

[DataField]
public EntityCoordinates? LastPingLocation;

[DataField, AutoNetworkedField]
public SoundSpecifier? PingSound = new SoundPathSpecifier("/Audio/Machines/sonar-ping.ogg")
{
Params = new AudioParams
{
Volume = -3,
}
};

[DataField]
public bool QueueRemoval;
}
15 changes: 7 additions & 8 deletions Content.Shared/Mining/Components/OreVeinComponent.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using Content.Shared.Mining;
using Content.Shared.Random;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Prototypes;

namespace Content.Server.Mining.Components;
namespace Content.Shared.Mining.Components;

/// <summary>
/// Defines an entity that will drop a random ore after being destroyed.
Expand All @@ -14,19 +13,19 @@ public sealed partial class OreVeinComponent : Component
/// How often an entity will be seeded with ore. Note: the amount of ore
/// that is dropped is dependent on the ore prototype. <see crefalso="OrePrototype"/>
/// </summary>
[DataField("oreChance")]
[DataField]
public float OreChance = 0.1f;

/// <summary>
/// The weighted random prototype used for determining what ore will be dropped.
/// </summary>
[DataField("oreRarityPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<WeightedRandomOrePrototype>))]
public string? OreRarityPrototypeId;
[DataField]
public ProtoId<WeightedRandomOrePrototype>? OreRarityPrototypeId;

/// <summary>
/// The ore that this entity holds.
/// If set in the prototype, it will not be overriden.
/// </summary>
[DataField("currentOre", customTypeSerializer: typeof(PrototypeIdSerializer<OrePrototype>)), ViewVariables(VVAccess.ReadWrite)]
public string? CurrentOre;
[DataField]
public ProtoId<OrePrototype>? CurrentOre;
}
101 changes: 101 additions & 0 deletions Content.Shared/Mining/MiningScannerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Content.Shared.Inventory;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Mining.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Timing;

namespace Content.Shared.Mining;

public sealed class MiningScannerSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly InventorySystem _inventory = default!;

/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<MiningScannerComponent, EntGotInsertedIntoContainerMessage>(OnInserted);
SubscribeLocalEvent<MiningScannerComponent, EntGotRemovedFromContainerMessage>(OnRemoved);
SubscribeLocalEvent<MiningScannerComponent, ItemToggledEvent>(OnToggled);
}

private void OnInserted(Entity<MiningScannerComponent> ent, ref EntGotInsertedIntoContainerMessage args)
{
UpdateViewerComponent(args.Container.Owner);
}

private void OnRemoved(Entity<MiningScannerComponent> ent, ref EntGotRemovedFromContainerMessage args)
{
UpdateViewerComponent(args.Container.Owner);
}

private void OnToggled(Entity<MiningScannerComponent> ent, ref ItemToggledEvent args)
{
if (_container.TryGetContainingContainer((ent.Owner, null, null), out var container))
UpdateViewerComponent(container.Owner);
}

public void UpdateViewerComponent(EntityUid uid)
{
Entity<MiningScannerComponent>? scannerEnt = null;

var ents = _inventory.GetHandOrInventoryEntities(uid);
foreach (var ent in ents)
{
if (!TryComp<MiningScannerComponent>(ent, out var scannerComponent) ||
!TryComp<ItemToggleComponent>(ent, out var toggle))
continue;

if (!toggle.Activated)
continue;

if (scannerEnt == null || scannerComponent.Range > scannerEnt.Value.Comp.Range)
scannerEnt = (ent, scannerComponent);
}

if (_net.IsServer)
{
if (scannerEnt == null)
{
if (TryComp<MiningScannerViewerComponent>(uid, out var viewer))
viewer.QueueRemoval = true;
}
else
{
var viewer = EnsureComp<MiningScannerViewerComponent>(uid);
viewer.ViewRange = scannerEnt.Value.Comp.Range;
viewer.QueueRemoval = false;
viewer.NextPingTime = _timing.CurTime + viewer.PingDelay;
Dirty(uid, viewer);
}
}
}

public override void Update(float frameTime)
{
base.Update(frameTime);

var query = EntityQueryEnumerator<MiningScannerViewerComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var viewer, out var xform))
{
if (viewer.QueueRemoval)
{
RemCompDeferred(uid, viewer);
continue;
}

if (_timing.CurTime < viewer.NextPingTime)
continue;

viewer.NextPingTime = _timing.CurTime + viewer.PingDelay;
viewer.LastPingLocation = xform.Coordinates;
if (_net.IsClient && _timing.IsFirstTimePredicted)
_audio.PlayEntity(viewer.PingSound, uid, uid);
}
}
}
Loading

0 comments on commit 8599251

Please sign in to comment.