Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate crate machine from market system #2681

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using Content.Shared._NF.Market;
using Content.Shared._NF.CrateMachine;
using Content.Shared._NF.CrateMachine.Components;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Audio.Systems;
using static Content.Shared._NF.Market.Components.CrateMachineComponent;
using CrateMachineComponent = Content.Shared._NF.Market.Components.CrateMachineComponent;

namespace Content.Client._NF.Market.Systems;
namespace Content.Client._NF.CrateMachine;

public sealed class MarketSystem : SharedMarketSystem
public sealed class CrateMachineSystem: SharedCrateMachineSystem
{
Copy link
Contributor

@whatston3 whatston3 Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public sealed class CrateMachineSystem: SharedCrateMachineSystem
public sealed partial class CrateMachineSystem : SharedCrateMachineSystem

per https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods:
All the parts must use the partial keyword

[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
Expand Down Expand Up @@ -37,19 +36,19 @@ private void OnComponentInit(EntityUid uid, CrateMachineComponent crateMachine,
/// </summary>
private void UpdateState(EntityUid uid, CrateMachineComponent component, SpriteComponent sprite, AppearanceComponent appearance)
{
if (!_appearanceSystem.TryGetData<CrateMachineVisualState>(uid, CrateMachineVisuals.VisualState, out var state, appearance))
if (!_appearanceSystem.TryGetData<CrateMachineComponent.CrateMachineVisualState>(uid, CrateMachineComponent.CrateMachineVisuals.VisualState, out var state, appearance))
{
return;
}

sprite.LayerSetVisible(CrateMachineVisualLayers.Base, true);
sprite.LayerSetVisible(CrateMachineVisualLayers.Closed, state == CrateMachineVisualState.Closed);
sprite.LayerSetVisible(CrateMachineVisualLayers.Opening, state == CrateMachineVisualState.Opening);
sprite.LayerSetVisible(CrateMachineVisualLayers.Closing, state == CrateMachineVisualState.Closing);
sprite.LayerSetVisible(CrateMachineVisualLayers.Open, state == CrateMachineVisualState.Open);
sprite.LayerSetVisible(CrateMachineVisualLayers.Crate, state == CrateMachineVisualState.Opening);
sprite.LayerSetVisible(CrateMachineVisualLayers.Closed, state == CrateMachineComponent.CrateMachineVisualState.Closed);
sprite.LayerSetVisible(CrateMachineVisualLayers.Opening, state == CrateMachineComponent.CrateMachineVisualState.Opening);
sprite.LayerSetVisible(CrateMachineVisualLayers.Closing, state == CrateMachineComponent.CrateMachineVisualState.Closing);
sprite.LayerSetVisible(CrateMachineVisualLayers.Open, state == CrateMachineComponent.CrateMachineVisualState.Open);
sprite.LayerSetVisible(CrateMachineVisualLayers.Crate, state == CrateMachineComponent.CrateMachineVisualState.Opening);

GreaseMonk marked this conversation as resolved.
Show resolved Hide resolved
if (state == CrateMachineVisualState.Opening && !_animationSystem.HasRunningAnimation(uid, AnimationKey))
if (state == CrateMachineComponent.CrateMachineVisualState.Opening && !_animationSystem.HasRunningAnimation(uid, AnimationKey))
{
var openingState = sprite.LayerMapTryGet(CrateMachineVisualLayers.Opening, out var flushLayer)
? sprite.LayerGetState(flushLayer)
Expand Down Expand Up @@ -92,7 +91,7 @@ private void UpdateState(EntityUid uid, CrateMachineComponent component, SpriteC

_animationSystem.Play(uid, anim, AnimationKey);
}
else if (state == CrateMachineVisualState.Closing && !_animationSystem.HasRunningAnimation(uid, AnimationKey))
else if (state == CrateMachineComponent.CrateMachineVisualState.Closing && !_animationSystem.HasRunningAnimation(uid, AnimationKey))
{
var closingState = sprite.LayerMapTryGet(CrateMachineVisualLayers.Closing, out var flushLayer)
? sprite.LayerGetState(flushLayer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private void Return(ButtonEventArgs args)

private void PurchaseCrate(ButtonEventArgs args)
{
SendMessage(new CrateMachinePurchaseMessage());
SendMessage(new MarketPurchaseMessage());
Close();
}

Expand Down
117 changes: 117 additions & 0 deletions Content.Server/_NF/CrateMachine/CrateMachineSystem.Animation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using Content.Server.Power.Components;
using Content.Shared._NF.CrateMachine;
using Content.Shared._NF.CrateMachine.Interfaces;
using AppearanceSystem = Robust.Server.GameObjects.AppearanceSystem;
using CrateMachineComponent = Content.Shared._NF.CrateMachine.Components.CrateMachineComponent;

namespace Content.Server._NF.CrateMachine;

/// <summary>
/// Handles starting the opening animation.
/// Updates the time remaining on the component.
/// Notifies <see cref="ICrateMachineDelegate"/>.
/// </summary>
public sealed partial class CrateMachineSystem: SharedCrateMachineSystem
{
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;

/// <summary>
/// Keep track of time in this function, in order to process the animation.
/// </summary>
/// <param name="frameTime">The current frame time</param>
public override void Update(float frameTime)
{
base.Update(frameTime);

var query = EntityQueryEnumerator<CrateMachineComponent, ApcPowerReceiverComponent>();
while (query.MoveNext(out var uid, out var crateMachine, out var receiver))
{
if (!receiver.Powered)
continue;

ProcessOpeningAnimation(uid, frameTime, crateMachine);
ProcessClosingAnimation(uid, frameTime, crateMachine);
}
}

/// <summary>
/// Updates the time remaining for the opening animation, calls the delegate when the animation finishes, and updates the visual state.
/// </summary>
/// <param name="uid">The Uid of the crate machine</param>
/// <param name="frameTime">The current frame time</param>
/// <param name="comp">The crate machine component</param>
private void ProcessOpeningAnimation(EntityUid uid, float frameTime, CrateMachineComponent comp)
{
if (comp.OpeningTimeRemaining <= 0)
return;

comp.OpeningTimeRemaining -= frameTime;

// Automatically start closing after it finishes open animation.
if (comp.OpeningTimeRemaining <= 0)
{
comp.DidTakeCrate = false;
comp.Delegate?.OnCrateMachineOpened(uid, comp);
}

// Update at the end so the closing animation can start automatically.
UpdateVisualState(uid, comp);
}

/// <summary>
/// Updates the time remaining for the closing animation, calls the delegate when the animation finishes, and updates the visual state.
/// </summary>
/// <param name="uid">The Uid of the crate machine</param>
/// <param name="frameTime">The current frame time</param>
/// <param name="comp">The crate machine component</param>
private void ProcessClosingAnimation(EntityUid uid, float frameTime, CrateMachineComponent comp)
{
if (!comp.DidTakeCrate && !IsOccupied(uid, comp, true))
{
comp.DidTakeCrate = true;
comp.ClosingTimeRemaining = comp.ClosingTime;
comp.Delegate?.OnCrateTaken(uid, comp);
}

if (comp.ClosingTimeRemaining <= 0)
{
comp.Delegate?.OnCrateMachineClosed(uid, comp);
}

comp.ClosingTimeRemaining -= frameTime;
UpdateVisualState(uid, comp);
}

/// <summary>
/// Updates the visual state of the crate machine by setting the visual state using the appearance system.
/// </summary>
/// <param name="uid">The Uid of the crate machine</param>
/// <param name="component">The crate machine component</param>
private void UpdateVisualState(EntityUid uid, CrateMachineComponent? component = null)
{
if (!Resolve(uid, ref component))
return;

if (component.OpeningTimeRemaining > 0)
_appearanceSystem.SetData(uid, CrateMachineComponent.CrateMachineVisuals.VisualState, CrateMachineComponent.CrateMachineVisualState.Opening);
else if (component.ClosingTimeRemaining > 0)
_appearanceSystem.SetData(uid, CrateMachineComponent.CrateMachineVisuals.VisualState, CrateMachineComponent.CrateMachineVisualState.Closing);
else if (!component.DidTakeCrate)
_appearanceSystem.SetData(uid, CrateMachineComponent.CrateMachineVisuals.VisualState, CrateMachineComponent.CrateMachineVisualState.Open);
else
_appearanceSystem.SetData(uid, CrateMachineComponent.CrateMachineVisuals.VisualState, CrateMachineComponent.CrateMachineVisualState.Closed);
}

/// <summary>
/// Starts the opening animation of the crate machine and calls the delegate when the animation finishes.
/// </summary>
/// <param name="crateMachineUid">The Uid of the crate machine</param>
/// <param name="component">The crate machine component</param>
/// <param name="delegate">The delegate to call when the animation finishes</param>
public void OpenFor(EntityUid crateMachineUid, CrateMachineComponent component, ICrateMachineDelegate @delegate)
{
component.Delegate = @delegate;
component.OpeningTimeRemaining = component.OpeningTime;
UpdateVisualState(crateMachineUid, component);
}
GreaseMonk marked this conversation as resolved.
Show resolved Hide resolved
}
122 changes: 122 additions & 0 deletions Content.Server/_NF/CrateMachine/CrateMachineSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Storage.EntitySystems;
using Content.Shared._NF.CrateMachine.Components;
using Content.Shared._NF.CrateMachine.Interfaces;
using Content.Shared.Maps;
using Robust.Shared.Map;

namespace Content.Server._NF.CrateMachine;

/// <summary>
/// The crate machine system can be used to make a crate machine open and spawn crates.
/// When calling <see cref="OpenFor"/>, the machine will open the door and give a callback to the given
/// <see cref="ICrateMachineDelegate"/> when it is done opening.
/// </summary>
public sealed partial class CrateMachineSystem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public sealed partial class CrateMachineSystem
public sealed partial class CrateMachineSystem : SharedCrateMachineSystem

Extend the shared version, add the needed import above?

{
[Dependency] private readonly IEntityManager _entityManager = default!;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[Dependency] private readonly IEntityManager _entityManager = default!;

Shouldn't need this - why isn't this extending SharedCrateMachineSystem? EntitySystem should already has a reference in EntityManager.

[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly EntityStorageSystem _storage = default!;

/// <summary>
/// Checks if there is a crate on the crate machine.
/// </summary>
/// <param name="crateMachineUid">The Uid of the crate machine</param>
/// <param name="component">The crate machine component</param>
/// <param name="ignoreAnimation">Ignores animation checks</param>
/// <returns>False if not occupied, true if it is.</returns>
public bool IsOccupied(EntityUid crateMachineUid, CrateMachineComponent component, bool ignoreAnimation = false)
{
if (!_entityManager.TryGetComponent<TransformComponent>(crateMachineUid, out var crateMachineTransform))
return true;
var tileRef = crateMachineTransform.Coordinates.GetTileRef(EntityManager, _mapManager);
if (tileRef == null)
return true;

if (!ignoreAnimation && (component.OpeningTimeRemaining > 0 || component.ClosingTimeRemaining > 0f))
return true;

// Finally check if there is a crate intersecting the crate machine.
return _lookup.GetLocalEntitiesIntersecting(tileRef.Value, flags: LookupFlags.All | LookupFlags.Approximate)
.Any(entity => _entityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype?.ID ==
component.CratePrototype);
}

/// <summary>
/// Calculates distance between two EntityCoordinates on the same grid.
/// Used to check for cargo pallets around the console instead of on the grid.
/// </summary>
/// <param name="point1">the first point</param>
/// <param name="point2">the second point</param>
/// <returns></returns>
private static double CalculateDistance(EntityCoordinates point1, EntityCoordinates point2)
{
var xDifference = point2.X - point1.X;
var yDifference = point2.Y - point1.Y;

return Math.Sqrt(xDifference * xDifference + yDifference * yDifference);
}
Comment on lines +45 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <summary>
/// Calculates distance between two EntityCoordinates on the same grid.
/// Used to check for cargo pallets around the console instead of on the grid.
/// </summary>
/// <param name="point1">the first point</param>
/// <param name="point2">the second point</param>
/// <returns></returns>
private static double CalculateDistance(EntityCoordinates point1, EntityCoordinates point2)
{
var xDifference = point2.X - point1.X;
var yDifference = point2.Y - point1.Y;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference);
}

Just use Vector2.Distance.


/// <summary>
/// Find the nearest unoccupied crate machine, that is anchored.
/// </summary>
/// <param name="from">The Uid of the entity to find the nearest crate machine from</param>
/// <param name="maxDistance">The maximum distance to search for a crate machine</param>
/// <param name="machineUid">The Uid of the nearest unoccupied crate machine, or null if none found</param>
public bool FindNearestUnoccupied(EntityUid from, int maxDistance, [NotNullWhen(true)] out EntityUid? machineUid)
{
machineUid = null;
if (maxDistance < 0)
return false;

var crateMachineQuery = AllEntityQuery<CrateMachineComponent, TransformComponent>();
var consoleGridUid = Transform(from).GridUid!.Value;
while (crateMachineQuery.MoveNext(out var crateMachineUid, out var comp, out var compXform))
{
// Skip crate machines that aren't mounted on a grid.
if (Transform(crateMachineUid).GridUid == null)
continue;
// Skip crate machines that are not on the same grid.
if (Transform(crateMachineUid).GridUid!.Value != consoleGridUid)
continue;
var currentDistance = CalculateDistance(compXform.Coordinates, Transform(from).Coordinates);

var isTooFarAway = currentDistance > maxDistance;
Comment on lines +83 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var currentDistance = CalculateDistance(compXform.Coordinates, Transform(from).Coordinates);
var isTooFarAway = currentDistance > maxDistance;
var isTooFarAway = Vector2.Distance(compXform.Coordinates.Position, Transform(from).Coordinates.Position) > maxDistance;

Vector2 has a convenient function for this, and TransformSystem does too. I believe the snippet above should work - I also wrong a version using TransformSystem, which is even better since it'll handle weird parenting better - no false positives.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd to assign some conditions to variables when they're only used in an if statement but eh.

var isBusy = IsOccupied(crateMachineUid, comp);

if (!compXform.Anchored || isTooFarAway || isBusy)
{
continue;
}

machineUid = crateMachineUid;
return true;
}
return false;
}


/// <summary>
/// Convenience function that simply spawns a crate and returns the uid.
/// </summary>
/// <param name="uid">The Uid of the crate machine</param>
/// <param name="component">The crate machine component</param>
/// <returns>The Uid of the spawned crate</returns>
public EntityUid SpawnCrate(EntityUid uid, CrateMachineComponent component)
{
return Spawn(component.CratePrototype, Transform(uid).Coordinates);
}

/// <summary>
/// Convenience function that simply inserts a entity into the container entity and relieve the caller of
/// using the storage system reference.
/// </summary>
/// <param name="uid">The Uid of the crate machine</param>
/// <param name="container">The Uid of the container</param>
public void InsertIntoCrate(EntityUid uid, EntityUid container)
{
_storage.Insert(uid, container);
}
}
Loading
Loading