Files
wwdpublic/Content.Server/Materials/MaterialReclaimerSystem.cs
SimpleStation14 a7511522a0 Mirror: Add logs to recycler gibbing and broadcasting. (#156)
## Mirror of PR #26008: [Add logs to recycler gibbing and
broadcasting.](https://github.com/space-wizards/space-station-14/pull/26008)
from <img src="https://avatars.githubusercontent.com/u/10567778?v=4"
alt="space-wizards" width="22"/>
[space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14)

###### `2b8415b378c181fd0222cdf7f4d005a70ebe8044`

PR opened by <img
src="https://avatars.githubusercontent.com/u/32041239?v=4"
width="16"/><a href="https://github.com/nikthechampiongr">
nikthechampiongr</a> at 2024-03-11 18:01:29 UTC
PR merged by <img
src="https://avatars.githubusercontent.com/u/19864447?v=4"
width="16"/><a href="https://github.com/web-flow"> web-flow</a> at
2024-03-12 10:57:05 UTC

---

PR changed 3 files with 8 additions and 0 deletions.

The PR had the following labels:
- Status: Needs Review


---

<details open="true"><summary><h1>Original Body</h1></summary>

> fixes #25556 
> fixes #25037 
> 
> <!-- Please read these guidelines before opening your PR:
https://docs.spacestation14.io/en/getting-started/pr-guideline -->
> <!-- The text between the arrows are comments - they will not be
visible on your PR. -->
> 
> ## About the PR
> <!-- What did you change in this PR? -->
> This just adds logs for broadcasting and getting gibbed by a recycler.
> 
> ## Why / Balance
> <!-- Why was it changed? Link any discussions or issues here. Please
discuss how this would affect game balance. -->
> Me admin. Me want this logs. Me beat logs with rock if I no know why
passenger gibbed or who put the nword on all the monitors in the
station.
> 
> ## Technical details
> <!-- If this is a code change, summarize at high level how your new
code works. This makes it easier to review. -->
> The broadcast log uses the new DeviceNetwork log because I can find no
other appropriate log type. The closest is Chat which announcements use
but this makes no sense.
> 
> ## Media
> <!-- 
> PRs which make ingame changes (adding clothing, items, new features,
etc) are required to have media attached that showcase the changes.
> Small fixes/refactors are exempt.
> Any media may be used in SS14 progress reports, with clear credit
given.
> 
> If you're unsure whether your PR will require media, ask a maintainer.
> 
> Check the box below to confirm that you have in fact seen this (put an
X in the brackets, like [X]):
> -->
> 
> - [x] I have added screenshots/videos to this PR showcasing its
changes ingame, **or** this PR does not require an ingame showcase
> 
> **Changelog**
> <!--
> Make players aware of new features and changes that could affect how
they play the game by adding a Changelog entry. Please read the
Changelog guidelines located at:
https://docs.spacestation14.io/en/getting-started/pr-guideline#changelog
> -->
> 
> <!--
> Make sure to take this Changelog template out of the comment block in
order for it to show up.
> 🆑
> - add: Added fun!
> - remove: Removed fun!
> - tweak: Changed fun!
> - fix: Fixed fun!
> -->
> 🆑
> ADMIN:
> - add: Recyclers now leave logs when they gib people.
> - add: People sending a broadcast in the communications console now
leave logs.


</details>

Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
2024-05-04 19:57:20 -04:00

252 lines
9.8 KiB
C#

using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Fluids.EntitySystems;
using Content.Server.GameTicking;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Stack;
using Content.Server.Wires;
using Content.Shared.Body.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Materials;
using Content.Shared.Mind;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.Utility;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Shared.Database;
namespace Content.Server.Materials;
/// <inheritdoc/>
public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
[Dependency] private readonly OpenableSystem _openable = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly SharedBodySystem _body = default!; //bobby
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MaterialReclaimerComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<MaterialReclaimerComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<MaterialReclaimerComponent, InteractUsingEvent>(OnInteractUsing,
before: new []{typeof(WiresSystem), typeof(SolutionTransferSystem)});
SubscribeLocalEvent<MaterialReclaimerComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<ActiveMaterialReclaimerComponent, PowerChangedEvent>(OnActivePowerChanged);
}
private void OnStartup(Entity<MaterialReclaimerComponent> entity, ref ComponentStartup args)
{
_solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionContainerId);
}
private void OnPowerChanged(Entity<MaterialReclaimerComponent> entity, ref PowerChangedEvent args)
{
AmbientSound.SetAmbience(entity.Owner, entity.Comp.Enabled && args.Powered);
entity.Comp.Powered = args.Powered;
Dirty(entity);
}
private void OnInteractUsing(Entity<MaterialReclaimerComponent> entity, ref InteractUsingEvent args)
{
if (args.Handled)
return;
// if we're trying to get a solution out of the reclaimer, don't destroy it
if (_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.SolutionContainerId, out _, out var outputSolution) && outputSolution.Contents.Any())
{
if (TryComp<SolutionContainerManagerComponent>(args.Used, out var managerComponent) &&
_solutionContainer.EnumerateSolutions((args.Used, managerComponent)).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
{
if (_openable.IsClosed(args.Used))
return;
if (TryComp<SolutionTransferComponent>(args.Used, out var transfer) &&
transfer.CanReceive)
return;
}
}
args.Handled = TryStartProcessItem(entity.Owner, args.Used, entity.Comp, args.User);
}
private void OnSuicide(Entity<MaterialReclaimerComponent> entity, ref SuicideEvent args)
{
if (args.Handled)
return;
args.SetHandled(SuicideKind.Bloodloss);
var victim = args.Victim;
if (TryComp(victim, out ActorComponent? actor) &&
_mind.TryGetMind(actor.PlayerSession, out var mindId, out var mind))
{
_ticker.OnGhostAttempt(mindId, false, mind: mind);
if (mind.OwnedEntity is { Valid: true } suicider)
{
_popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), suicider);
}
}
_popup.PopupEntity(Loc.GetString("recycler-component-suicide-message-others", ("victim", Identity.Entity(victim, EntityManager))),
victim,
Filter.PvsExcept(victim, entityManager: EntityManager), true);
_body.GibBody(victim, true);
_appearance.SetData(entity.Owner, RecyclerVisuals.Bloody, true);
}
private void OnActivePowerChanged(Entity<ActiveMaterialReclaimerComponent> entity, ref PowerChangedEvent args)
{
if (!args.Powered)
TryFinishProcessItem(entity, null, entity.Comp);
}
/// <inheritdoc/>
public override bool TryFinishProcessItem(EntityUid uid, MaterialReclaimerComponent? component = null, ActiveMaterialReclaimerComponent? active = null)
{
if (!Resolve(uid, ref component, ref active, false))
return false;
if (!base.TryFinishProcessItem(uid, component, active))
return false;
if (active.ReclaimingContainer.ContainedEntities.FirstOrNull() is not { } item)
return false;
Container.Remove(item, active.ReclaimingContainer);
Dirty(component);
// scales the output if the process was interrupted.
var completion = 1f - Math.Clamp((float) Math.Round((active.EndTime - Timing.CurTime) / active.Duration),
0f, 1f);
Reclaim(uid, item, completion, component);
return true;
}
/// <inheritdoc/>
public override void Reclaim(EntityUid uid,
EntityUid item,
float completion = 1f,
MaterialReclaimerComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
base.Reclaim(uid, item, completion, component);
var xform = Transform(uid);
SpawnMaterialsFromComposition(uid, item, completion * component.Efficiency, xform: xform);
if (CanGib(uid, item, component))
{
_adminLogger.Add(LogType.Gib, LogImpact.Extreme, $"{ToPrettyString(item):victim} was gibbed by {ToPrettyString(uid):entity} ");
SpawnChemicalsFromComposition(uid, item, completion, false, component, xform);
_body.GibBody(item, true);
_appearance.SetData(uid, RecyclerVisuals.Bloody, true);
}
else
{
SpawnChemicalsFromComposition(uid, item, completion, true, component, xform);
}
QueueDel(item);
}
private void SpawnMaterialsFromComposition(EntityUid reclaimer,
EntityUid item,
float efficiency,
MaterialStorageComponent? storage = null,
TransformComponent? xform = null,
PhysicalCompositionComponent? composition = null)
{
if (!Resolve(reclaimer, ref storage, ref xform, false))
return;
if (!Resolve(item, ref composition, false))
return;
foreach (var (material, amount) in composition.MaterialComposition)
{
var outputAmount = (int) (amount * efficiency);
_materialStorage.TryChangeMaterialAmount(reclaimer, material, outputAmount, storage);
}
foreach (var (storedMaterial, storedAmount) in storage.Storage)
{
var stacks = _materialStorage.SpawnMultipleFromMaterial(storedAmount, storedMaterial,
xform.Coordinates,
out var materialOverflow);
var amountConsumed = storedAmount - materialOverflow;
_materialStorage.TryChangeMaterialAmount(reclaimer, storedMaterial, -amountConsumed, storage);
foreach (var stack in stacks)
{
_stack.TryMergeToContacts(stack);
}
}
}
private void SpawnChemicalsFromComposition(EntityUid reclaimer,
EntityUid item,
float efficiency,
bool sound = true,
MaterialReclaimerComponent? reclaimerComponent = null,
TransformComponent? xform = null,
PhysicalCompositionComponent? composition = null)
{
if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
return;
if (!_solutionContainer.TryGetSolution(reclaimer, reclaimerComponent.SolutionContainerId, out var outputSolution))
return;
efficiency *= reclaimerComponent.Efficiency;
var totalChemicals = new Solution();
if (Resolve(item, ref composition, false))
{
foreach (var (key, value) in composition.ChemicalComposition)
{
// TODO use ReagentQuantity
totalChemicals.AddReagent(key, value * efficiency, false);
}
}
// if the item we inserted has reagents, add it in.
if (TryComp<SolutionContainerManagerComponent>(item, out var solutionContainer))
{
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solutionContainer)))
{
var solution = soln.Comp.Solution;
foreach (var quantity in solution.Contents)
{
totalChemicals.AddReagent(quantity.Reagent.Prototype, quantity.Quantity * efficiency, false);
}
}
}
_solutionContainer.TryTransferSolution(outputSolution.Value, totalChemicals, totalChemicals.Volume);
if (totalChemicals.Volume > 0)
{
_puddle.TrySpillAt(reclaimer, totalChemicals, out _, sound, transformComponent: xform);
}
}
}