-
Notifications
You must be signed in to change notification settings - Fork 249
Expand file tree
/
Copy pathStackSystem.cs
More file actions
350 lines (299 loc) · 14.4 KB
/
StackSystem.cs
File metadata and controls
350 lines (299 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
using Content.Shared.Popups;
using Content.Shared.Stacks;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
// ADT-Tweak start
using Content.Server.Administration;
using Robust.Shared.Player;
// ADT-Tweak end
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!;
[Dependency] private readonly IPlayerManager _playerManager = default!; // ADT-Tweak
[Dependency] private readonly QuickDialogSystem _quickDialog = default!; // ADT-Tweak for system own split
#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="amount"> How much to move to the new entity. </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);
}
// ADT-Tweak start
protected override void RequestCustomSplit(Entity<StackComponent> stack, Entity<TransformComponent?> user)
{
if (!Resolve(user.Owner, ref user.Comp, false))
return;
if (!_playerManager.TryGetSessionByEntity(user.Owner, out var session))
return;
var maxCount = stack.Comp.Count;
_quickDialog.OpenDialog<int>(
session,
Loc.GetString("comp-stack-split-custom-title"),
Loc.GetString("comp-stack-split-custom-description", ("count", maxCount)),
(int amount) =>
{
if (amount <= 0)
{
Popup.PopupCursor(Loc.GetString("comp-stack-split-too-small"), user.Owner, PopupType.Medium);
return;
}
if (amount > maxCount)
{
Popup.PopupCursor(Loc.GetString("comp-stack-split-too-large"), 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);
});
}
// ADT-Tweak end
#endregion
}
}