mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-09-15 23:02:59 +02:00
* add tile gun module
* fix stacks being loaded all at once in guns
* fix tile gun module
* fixed indentation
* remove not necessary stuff
Co-authored-by: āda <ss.adasts@gmail.com>
* Use alias
Co-authored-by: āda <ss.adasts@gmail.com>
* fix OnBallisticInteractUsing
* forgot one
Co-authored-by: āda <ss.adasts@gmail.com>
* fix FillAmmo
* add another todo
* pass user instead of event args
Co-authored-by: āda <ss.adasts@gmail.com>
* fix last commit
* update todo
Co-authored-by: āda <ss.adasts@gmail.com>
* wrong place
* move gun to first slot
* custom sprite
* call a event
* important detail in the commentary
* remove recipe
* not used
* also not used anymore
* when did i remove this?
* fix sprite
* shoot faster
* move to shared
* use API method
* remove useless stuff
* Revert "remove useless stuff"
This reverts commit b198303aed.
* fix code
* shot sound
* change spread
* fix attribution
* load sound
* empty sound
* commentary
* more commentary fix
* rename event
* remove duplicate
* who knew
* turn to mono
* yet another beep
* increase volume
* make it a crowbar
* make it able to self load clicking tiles
* make it able to pry tiles
* who needs a crowbar anymore
* make it networked
* no more hands
* move BeforeAmmoLoadedEvent call to TryBallisticInsert
* before now
* change sprite
* slight update to sprite
* mag visuals
* move split back to server
* get one method
* remove subscription
* use API method in the gun system
* move API call up
Co-authored-by: āda <ss.adasts@gmail.com>
* remove unused stuff
* fix get one method
* comment change
* half the firerate
---------
Co-authored-by: āda <ss.adasts@gmail.com>
Co-authored-by: beck-thompson <beck314159@hotmail.com>
Co-authored-by: iaada <iaada@users.noreply.github.com>
307 lines
13 KiB
C#
307 lines
13 KiB
C#
using Content.Shared.Popups;
|
|
using Content.Shared.Stacks;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Prototypes;
|
|
|
|
namespace Content.Server.Stack
|
|
{
|
|
/// <summary>
|
|
/// Entity system that handles everything relating to stacks.
|
|
/// This is a good example for learning how to code in an ECS manner.
|
|
/// </summary>
|
|
[UsedImplicitly]
|
|
public sealed class StackSystem : SharedStackSystem
|
|
{
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
|
|
#region Spawning
|
|
|
|
/// <summary>
|
|
/// Spawns a new entity and moves an amount to it from the stack.
|
|
/// Moves nothing if amount is greater than ent's stack count.
|
|
/// </summary>
|
|
/// <param name="ent">Entity to split in a new stack.</param>
|
|
/// <param name="amount">How much to move to the new entity.</param>
|
|
/// <param name="spawnPosition">Where to spawn the new stack</param>
|
|
/// <returns>Null if StackComponent doesn't resolve, or amount to move is greater than ent has available.</returns>
|
|
[PublicAPI]
|
|
public EntityUid? Split(Entity<StackComponent?> ent, int amount, EntityCoordinates spawnPosition)
|
|
{
|
|
if (!Resolve(ent.Owner, ref ent.Comp))
|
|
return null;
|
|
|
|
// Try to remove the amount of things we want to split from the original stack...
|
|
if (!TryUse(ent, amount))
|
|
return null;
|
|
|
|
if (!_prototypeManager.Resolve(ent.Comp.StackTypeId, out var stackType))
|
|
return null;
|
|
|
|
// Set the output parameter in the event instance to the newly split stack.
|
|
var newEntity = SpawnAtPosition(stackType.Spawn, spawnPosition);
|
|
|
|
// There should always be a StackComponent
|
|
var stackComp = Comp<StackComponent>(newEntity);
|
|
|
|
SetCount((newEntity, stackComp), amount);
|
|
stackComp.Unlimited = false; // Don't let people dupe unlimited stacks
|
|
Dirty(newEntity, stackComp);
|
|
|
|
var ev = new StackSplitEvent(newEntity);
|
|
RaiseLocalEvent(ent, ref ev);
|
|
|
|
return newEntity;
|
|
}
|
|
|
|
#region SpawnAtPosition
|
|
|
|
/// <summary>
|
|
/// Spawns a stack of a certain stack type and sets its count. Won't set the stack over its max.
|
|
/// </summary>
|
|
/// <param name="count">The amount to set the spawned stack to.</param>
|
|
[PublicAPI]
|
|
public EntityUid SpawnAtPosition(int count, StackPrototype prototype, EntityCoordinates spawnPosition)
|
|
{
|
|
var entity = SpawnAtPosition(prototype.Spawn, spawnPosition); // The real SpawnAtPosition
|
|
|
|
SetCount((entity, null), count);
|
|
return entity;
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnAtPosition(int, StackPrototype, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public EntityUid SpawnAtPosition(int count, ProtoId<StackPrototype> id, EntityCoordinates spawnPosition)
|
|
{
|
|
var proto = _prototypeManager.Index(id);
|
|
return SpawnAtPosition(count, proto, spawnPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Say you want to spawn 97 units of something that has a max stack count of 30.
|
|
/// This would spawn 3 stacks of 30 and 1 stack of 7.
|
|
/// </summary>
|
|
/// <returns>The entities spawned.</returns>
|
|
/// <remarks> If the entity to spawn doesn't have stack component this will spawn a bunch of single items. </remarks>
|
|
private List<EntityUid> SpawnMultipleAtPosition(EntProtoId entityPrototype,
|
|
List<int> amounts,
|
|
EntityCoordinates spawnPosition)
|
|
{
|
|
if (amounts.Count <= 0)
|
|
{
|
|
Log.Error(
|
|
$"Attempted to spawn stacks of nothing: {entityPrototype}, {amounts}. Trace: {Environment.StackTrace}");
|
|
return new();
|
|
}
|
|
|
|
var spawnedEnts = new List<EntityUid>();
|
|
foreach (var count in amounts)
|
|
{
|
|
var entity = SpawnAtPosition(entityPrototype, spawnPosition); // The real SpawnAtPosition
|
|
spawnedEnts.Add(entity);
|
|
if (TryComp<StackComponent>(entity, out var stackComp)) // prevent errors from the Resolve
|
|
SetCount((entity, stackComp), count);
|
|
}
|
|
|
|
return spawnedEnts;
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleAtPosition(EntProtoId, List{int}, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleAtPosition(EntProtoId entityPrototypeId,
|
|
int amount,
|
|
EntityCoordinates spawnPosition)
|
|
{
|
|
return SpawnMultipleAtPosition(entityPrototypeId,
|
|
CalculateSpawns(entityPrototypeId, amount),
|
|
spawnPosition);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleAtPosition(EntProtoId, List{int}, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleAtPosition(EntityPrototype entityProto,
|
|
int amount,
|
|
EntityCoordinates spawnPosition)
|
|
{
|
|
return SpawnMultipleAtPosition(entityProto.ID,
|
|
CalculateSpawns(entityProto, amount),
|
|
spawnPosition);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleAtPosition(EntProtoId, List{int}, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleAtPosition(StackPrototype stack,
|
|
int amount,
|
|
EntityCoordinates spawnPosition)
|
|
{
|
|
return SpawnMultipleAtPosition(stack.Spawn,
|
|
CalculateSpawns(stack, amount),
|
|
spawnPosition);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleAtPosition(EntProtoId, List{int}, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleAtPosition(ProtoId<StackPrototype> stackId,
|
|
int amount,
|
|
EntityCoordinates spawnPosition)
|
|
{
|
|
var stackProto = _prototypeManager.Index(stackId);
|
|
return SpawnMultipleAtPosition(stackProto.Spawn,
|
|
CalculateSpawns(stackProto, amount),
|
|
spawnPosition);
|
|
}
|
|
|
|
#endregion
|
|
#region SpawnNextToOrDrop
|
|
|
|
/// <inheritdoc cref="SpawnAtPosition(int, StackPrototype, EntityCoordinates)"/>
|
|
[PublicAPI]
|
|
public EntityUid SpawnNextToOrDrop(int amount, StackPrototype prototype, EntityUid source)
|
|
{
|
|
var entity = SpawnNextToOrDrop(prototype.Spawn, source); // The real SpawnNextToOrDrop
|
|
SetCount((entity, null), amount);
|
|
return entity;
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnNextToOrDrop(int, StackPrototype, EntityUid)"/>
|
|
[PublicAPI]
|
|
public EntityUid SpawnNextToOrDrop(int amount, ProtoId<StackPrototype> id, EntityUid source)
|
|
{
|
|
var proto = _prototypeManager.Index(id);
|
|
return SpawnNextToOrDrop(amount, proto, source);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleAtPosition(EntProtoId, List{int}, EntityCoordinates)"/>
|
|
private List<EntityUid> SpawnMultipleNextToOrDrop(EntProtoId entityPrototype,
|
|
List<int> amounts,
|
|
EntityUid target)
|
|
{
|
|
if (amounts.Count <= 0)
|
|
{
|
|
Log.Error(
|
|
$"Attempted to spawn stacks of nothing: {entityPrototype}, {amounts}. Trace: {Environment.StackTrace}");
|
|
return new();
|
|
}
|
|
|
|
var spawnedEnts = new List<EntityUid>();
|
|
foreach (var count in amounts)
|
|
{
|
|
var entity = SpawnNextToOrDrop(entityPrototype, target); // The real SpawnNextToOrDrop
|
|
spawnedEnts.Add(entity);
|
|
if (TryComp<StackComponent>(entity, out var stackComp)) // prevent errors from the Resolve
|
|
SetCount((entity, stackComp), count);
|
|
}
|
|
|
|
return spawnedEnts;
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleNextToOrDrop(EntProtoId, List{int}, EntityUid)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleNextToOrDrop(EntProtoId stack,
|
|
int amount,
|
|
EntityUid target)
|
|
{
|
|
return SpawnMultipleNextToOrDrop(stack,
|
|
CalculateSpawns(stack, amount),
|
|
target);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleNextToOrDrop(EntProtoId, List{int}, EntityUid)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleNextToOrDrop(EntityPrototype stack,
|
|
int amount,
|
|
EntityUid target)
|
|
{
|
|
return SpawnMultipleNextToOrDrop(stack.ID,
|
|
CalculateSpawns(stack, amount),
|
|
target);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleNextToOrDrop(EntProtoId, List{int}, EntityUid)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleNextToOrDrop(StackPrototype stack,
|
|
int amount,
|
|
EntityUid target)
|
|
{
|
|
return SpawnMultipleNextToOrDrop(stack.Spawn,
|
|
CalculateSpawns(stack, amount),
|
|
target);
|
|
}
|
|
|
|
/// <inheritdoc cref="SpawnMultipleNextToOrDrop(EntProtoId, List{int}, EntityUid)"/>
|
|
[PublicAPI]
|
|
public List<EntityUid> SpawnMultipleNextToOrDrop(ProtoId<StackPrototype> stackId,
|
|
int amount,
|
|
EntityUid target)
|
|
{
|
|
var stackProto = _prototypeManager.Index(stackId);
|
|
return SpawnMultipleNextToOrDrop(stackProto.Spawn,
|
|
CalculateSpawns(stackProto, amount),
|
|
target);
|
|
}
|
|
|
|
#endregion
|
|
#region Calculate
|
|
|
|
/// <summary>
|
|
/// Calculates how many stacks to spawn that total up to <paramref name="amount"/>.
|
|
/// </summary>
|
|
/// <returns>The list of stack counts per entity.</returns>
|
|
private List<int> CalculateSpawns(int maxCountPerStack, int amount)
|
|
{
|
|
var amounts = new List<int>();
|
|
while (amount > 0)
|
|
{
|
|
var countAmount = Math.Min(maxCountPerStack, amount);
|
|
amount -= countAmount;
|
|
amounts.Add(countAmount);
|
|
}
|
|
|
|
return amounts;
|
|
}
|
|
|
|
/// <inheritdoc cref="CalculateSpawns(int, int)"/>
|
|
private List<int> CalculateSpawns(StackPrototype stackProto, int amount)
|
|
{
|
|
return CalculateSpawns(GetMaxCount(stackProto), amount);
|
|
}
|
|
|
|
/// <inheritdoc cref="CalculateSpawns(int, int)"/>
|
|
private List<int> CalculateSpawns(EntityPrototype entityPrototype, int amount)
|
|
{
|
|
return CalculateSpawns(GetMaxCount(entityPrototype), amount);
|
|
}
|
|
|
|
/// <inheritdoc cref="CalculateSpawns(int, int)"/>
|
|
private List<int> CalculateSpawns(EntProtoId entityId, int amount)
|
|
{
|
|
return CalculateSpawns(GetMaxCount(entityId), amount);
|
|
}
|
|
|
|
#endregion
|
|
#endregion
|
|
#region Event Handlers
|
|
|
|
/// <inheritdoc />
|
|
protected override void UserSplit(Entity<StackComponent> stack, Entity<TransformComponent?> user, int amount)
|
|
{
|
|
if (!Resolve(user.Owner, ref user.Comp, false))
|
|
return;
|
|
|
|
if (amount <= 0)
|
|
{
|
|
Popup.PopupCursor(Loc.GetString("comp-stack-split-too-small"), user.Owner, PopupType.Medium);
|
|
return;
|
|
}
|
|
|
|
if (Split(stack.AsNullable(), amount, user.Comp.Coordinates) is not { } split)
|
|
return;
|
|
|
|
Hands.PickupOrDrop(user.Owner, split);
|
|
|
|
Popup.PopupCursor(Loc.GetString("comp-stack-split"), user.Owner);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|