Merge branch 'master-upstream' into expl_int_analyzer
# Conflicts: # Content.Server/GameObjects/Components/Body/Part/BodyPartComponent.cs # Content.Server/GameObjects/Components/Botany/PlantHolderComponent.cs # Content.Server/GameObjects/Components/Chemistry/PillComponent.cs # Content.Server/GameObjects/Components/Interactable/TilePryingComponent.cs # Content.Server/GameObjects/Components/Items/FloorTileItemComponent.cs # Content.Server/GameObjects/Components/Items/RCD/RCDAmmoComponent.cs # Content.Server/GameObjects/Components/Items/RCD/RCDComponent.cs # Content.Server/GameObjects/Components/Medical/HealingComponent.cs # Content.Server/GameObjects/Components/Power/WirePlacerComponent.cs # Content.Shared/Chemistry/Solution.cs
This commit is contained in:
@@ -1,10 +1,52 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class FireExtinguisherComponent : Component
|
||||
public class FireExtinguisherComponent : Component, IAfterInteract
|
||||
{
|
||||
public override string Name => "FireExtinguisher";
|
||||
|
||||
// Higher priority than sprays.
|
||||
int IAfterInteract.Priority => 1;
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null || !eventArgs.CanReach)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank)
|
||||
&& eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution)
|
||||
&& targetSolution.CanDrain
|
||||
&& Owner.TryGetComponent(out SolutionContainerComponent? container))
|
||||
{
|
||||
var trans = ReagentUnit.Min(container.EmptyVolume, targetSolution.DrainAvailable);
|
||||
if (trans > 0)
|
||||
{
|
||||
var drained = targetSolution.Drain(trans);
|
||||
container.TryAddSolution(drained);
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/refill.ogg", Owner);
|
||||
eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("{0:TheName} is now refilled", Owner));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,14 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", location, AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
|
||||
async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return;
|
||||
if (!Owner.TryGetComponent(out StackComponent stack)) return;
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
||||
return true;
|
||||
|
||||
if (!Owner.TryGetComponent(out StackComponent stack))
|
||||
return true;
|
||||
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
var location = eventArgs.ClickLocation.AlignWithClosestGridTile();
|
||||
@@ -88,10 +92,9 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
PlaceAt(mapGrid, location, _tileDefinitionManager[_outputTiles[0]].TileId, mapGrid.TileSize / 2f);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,17 +32,17 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
message.AddMarkup(Loc.GetString("It holds {0} charges.", refillAmmo));
|
||||
}
|
||||
|
||||
async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null || !eventArgs.Target.TryGetComponent(out RCDComponent rcdComponent) || !eventArgs.User.TryGetComponent(out IHandsComponent hands))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rcdComponent.maxAmmo - rcdComponent._ammo < refillAmmo)
|
||||
{
|
||||
rcdComponent.Owner.PopupMessage(eventArgs.User, Loc.GetString("The RCD is full!"));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
rcdComponent._ammo = Math.Min(rcdComponent.maxAmmo, rcdComponent._ammo + refillAmmo);
|
||||
@@ -51,6 +51,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
//Deleting a held item causes a lot of errors
|
||||
hands.Drop(Owner, false);
|
||||
Owner.Delete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
message.AddMarkup(Loc.GetString("It's currently on {0} mode, and holds {1} charges.",_mode.ToString(), _ammo));
|
||||
}
|
||||
|
||||
async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
//No changing mode mid-RCD
|
||||
var startingMode = _mode;
|
||||
@@ -115,7 +115,7 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
var result = await doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
if (result == DoAfterStatus.Cancelled)
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_mode)
|
||||
@@ -145,12 +145,12 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
airlock.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing.
|
||||
break;
|
||||
default:
|
||||
return; //I don't know why this would happen, but sure I guess. Get out of here invalid state!
|
||||
return true; //I don't know why this would happen, but sure I guess. Get out of here invalid state!
|
||||
}
|
||||
|
||||
_entitySystemManager.GetEntitySystem<AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", Owner);
|
||||
_ammo--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsRCDStillValid(AfterInteractEventArgs eventArgs, IMapGrid mapGrid, TileRef tile, Vector2i snapPos, RcdMode startingMode)
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
@@ -24,6 +26,7 @@ using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -36,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IStorageComponent))]
|
||||
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct
|
||||
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct, IAfterInteract
|
||||
{
|
||||
private const string LoggerName = "Storage";
|
||||
|
||||
@@ -44,6 +47,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
private readonly Dictionary<IEntity, int> _sizeCache = new();
|
||||
|
||||
private bool _occludesLight;
|
||||
private bool _quickInsert; //Can insert storables by "attacking" them with the storage entity
|
||||
private bool _areaInsert; //"Attacking" with the storage entity causes it to insert all nearby storables after a delay
|
||||
private bool _storageInitialCalculated;
|
||||
private int _storageUsed;
|
||||
private int _storageCapacityMax;
|
||||
@@ -184,7 +189,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
/// </summary>
|
||||
/// <param name="player">The player to insert an entity from</param>
|
||||
/// <returns>true if inserted, false otherwise</returns>
|
||||
public bool PlayerInsertEntity(IEntity player)
|
||||
public bool PlayerInsertHeldEntity(IEntity player)
|
||||
{
|
||||
EnsureInitialCalculated();
|
||||
|
||||
@@ -212,6 +217,24 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an Entity (<paramref name="toInsert"/>) in the world into storage, informing <paramref name="player"/> if it fails.
|
||||
/// <paramref name="toInsert"/> is *NOT* held, see <see cref="PlayerInsertHeldEntity(IEntity)"/>.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to insert an entity with</param>
|
||||
/// <returns>true if inserted, false otherwise</returns>
|
||||
public bool PlayerInsertEntityInWorld(IEntity player, IEntity toInsert)
|
||||
{
|
||||
EnsureInitialCalculated();
|
||||
|
||||
if (!Insert(toInsert))
|
||||
{
|
||||
Owner.PopupMessage(player, "Can't insert.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the storage UI for an entity
|
||||
/// </summary>
|
||||
@@ -343,6 +366,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
serializer.DataField(ref _storageCapacityMax, "capacity", 10000);
|
||||
serializer.DataField(ref _occludesLight, "occludesLight", true);
|
||||
serializer.DataField(ref _quickInsert, "quickInsert", false);
|
||||
serializer.DataField(ref _areaInsert, "areaInsert", false);
|
||||
serializer.DataField(this, x => x.StorageSoundCollection, "storageSoundCollection", string.Empty);
|
||||
//serializer.DataField(ref StorageUsed, "used", 0);
|
||||
}
|
||||
@@ -418,7 +443,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
break;
|
||||
}
|
||||
|
||||
PlayerInsertEntity(player);
|
||||
PlayerInsertHeldEntity(player);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -449,7 +474,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
return false;
|
||||
}
|
||||
|
||||
return PlayerInsertEntity(eventArgs.User);
|
||||
return PlayerInsertHeldEntity(eventArgs.User);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,6 +494,97 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
((IUse) this).UseEntity(new UseEntityEventArgs { User = eventArgs.User });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a user to pick up entities by clicking them, or pick up all entities in a certain radius
|
||||
/// arround a click.
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <returns></returns>
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return false;
|
||||
|
||||
// Pick up all entities in a radius around the clicked location.
|
||||
// The last half of the if is because carpets exist and this is terrible
|
||||
if(_areaInsert && (eventArgs.Target == null || !eventArgs.Target.HasComponent<StorableComponent>()))
|
||||
{
|
||||
var validStorables = new List<IEntity>();
|
||||
foreach (var entity in Owner.EntityManager.GetEntitiesInRange(eventArgs.ClickLocation, 1))
|
||||
{
|
||||
if (!entity.Transform.IsMapTransform
|
||||
|| entity == eventArgs.User
|
||||
|| !entity.HasComponent<StorableComponent>())
|
||||
continue;
|
||||
validStorables.Add(entity);
|
||||
}
|
||||
|
||||
//If there's only one then let's be generous
|
||||
if (validStorables.Count > 1)
|
||||
{
|
||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, 0.2f * validStorables.Count, CancellationToken.None, Owner)
|
||||
{
|
||||
BreakOnStun = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnUserMove = true,
|
||||
NeedHand = true,
|
||||
};
|
||||
var result = await doAfterSystem.DoAfter(doAfterArgs);
|
||||
if (result != DoAfterStatus.Finished) return true;
|
||||
}
|
||||
|
||||
var successfullyInserted = new List<EntityUid>();
|
||||
var successfullyInsertedPositions = new List<EntityCoordinates>();
|
||||
foreach (var entity in validStorables)
|
||||
{
|
||||
// Check again, situation may have changed for some entities, but we'll still pick up any that are valid
|
||||
if (!entity.Transform.IsMapTransform
|
||||
|| entity == eventArgs.User
|
||||
|| !entity.HasComponent<StorableComponent>())
|
||||
continue;
|
||||
var coords = entity.Transform.Coordinates;
|
||||
if (PlayerInsertEntityInWorld(eventArgs.User, entity))
|
||||
{
|
||||
successfullyInserted.Add(entity.Uid);
|
||||
successfullyInsertedPositions.Add(coords);
|
||||
}
|
||||
}
|
||||
|
||||
// If we picked up atleast one thing, play a sound and do a cool animation!
|
||||
if (successfullyInserted.Count>0)
|
||||
{
|
||||
PlaySoundCollection(StorageSoundCollection);
|
||||
SendNetworkMessage(
|
||||
new AnimateInsertingEntitiesMessage(
|
||||
successfullyInserted,
|
||||
successfullyInsertedPositions
|
||||
)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Pick up the clicked entity
|
||||
else if(_quickInsert)
|
||||
{
|
||||
if (eventArgs.Target == null
|
||||
|| !eventArgs.Target.Transform.IsMapTransform
|
||||
|| eventArgs.Target == eventArgs.User
|
||||
|| !eventArgs.Target.HasComponent<StorableComponent>())
|
||||
return false;
|
||||
var position = eventArgs.Target.Transform.Coordinates;
|
||||
if(PlayerInsertEntityInWorld(eventArgs.User, eventArgs.Target))
|
||||
{
|
||||
SendNetworkMessage(new AnimateInsertingEntitiesMessage(
|
||||
new List<EntityUid>() { eventArgs.Target.Uid },
|
||||
new List<EntityCoordinates>() { position }
|
||||
));
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
|
||||
{
|
||||
var storedEntities = StoredEntities?.ToList();
|
||||
|
||||
Reference in New Issue
Block a user