Chill bounties + fixes (#23411)
* Chill bounties + fixes * localize * fix arbitage
This commit is contained in:
@@ -2,14 +2,16 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Labels;
|
||||
using Content.Server.NameIdentifier;
|
||||
using Content.Server.Paper;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.NameIdentifier;
|
||||
using Content.Shared.Stacks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -19,6 +21,14 @@ namespace Content.Server.Cargo.Systems;
|
||||
public sealed partial class CargoSystem
|
||||
{
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly NameIdentifierSystem _nameIdentifier = default!;
|
||||
|
||||
[ValidatePrototypeId<NameIdentifierGroupPrototype>]
|
||||
private const string BountyNameIdentifierGroup = "Bounty";
|
||||
|
||||
private EntityQuery<StackComponent> _stackQuery;
|
||||
private EntityQuery<ContainerManagerComponent> _containerQuery;
|
||||
private EntityQuery<CargoBountyLabelComponent> _bountyLabelQuery;
|
||||
|
||||
private void InitializeBounty()
|
||||
{
|
||||
@@ -27,6 +37,10 @@ public sealed partial class CargoSystem
|
||||
SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice);
|
||||
SubscribeLocalEvent<EntitySoldEvent>(OnSold);
|
||||
SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
_stackQuery = GetEntityQuery<StackComponent>();
|
||||
_containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
_bountyLabelQuery = GetEntityQuery<CargoBountyLabelComponent>();
|
||||
}
|
||||
|
||||
private void OnBountyConsoleOpened(EntityUid uid, CargoBountyConsoleComponent component, BoundUIOpenedEvent args)
|
||||
@@ -98,7 +112,7 @@ public sealed partial class CargoSystem
|
||||
if (!TryGetBountyFromId(station, component.Id, out var bounty, database))
|
||||
return;
|
||||
|
||||
if (!_protoMan.TryIndex<CargoBountyPrototype>(bounty.Value.Bounty, out var bountyPrototype) ||
|
||||
if (!_protoMan.TryIndex(bounty.Value.Bounty, out var bountyPrototype) ||
|
||||
!IsBountyComplete(container.Owner, bountyPrototype))
|
||||
return;
|
||||
|
||||
@@ -112,25 +126,15 @@ public sealed partial class CargoSystem
|
||||
|
||||
private void OnSold(ref EntitySoldEvent args)
|
||||
{
|
||||
var containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
var labelQuery = GetEntityQuery<CargoBountyLabelComponent>();
|
||||
foreach (var sold in args.Sold)
|
||||
{
|
||||
if (!containerQuery.TryGetComponent(sold, out var containerMan))
|
||||
continue;
|
||||
|
||||
// make sure this label was actually applied to a crate.
|
||||
if (!_container.TryGetContainer(sold, LabelSystem.ContainerName, out var container, containerMan))
|
||||
continue;
|
||||
|
||||
if (container.ContainedEntities.FirstOrNull() is not { } label ||
|
||||
!labelQuery.TryGetComponent(label, out var component))
|
||||
if (!TryGetBountyLabel(sold, out _, out var component))
|
||||
continue;
|
||||
|
||||
if (!TryGetBountyFromId(args.Station, component.Id, out var bounty))
|
||||
continue;
|
||||
|
||||
if (!IsBountyComplete(container.Owner, bounty.Value))
|
||||
if (!IsBountyComplete(sold, bounty.Value))
|
||||
continue;
|
||||
|
||||
TryRemoveBounty(args.Station, bounty.Value);
|
||||
@@ -139,6 +143,28 @@ public sealed partial class CargoSystem
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetBountyLabel(EntityUid uid,
|
||||
[NotNullWhen(true)] out EntityUid? labelEnt,
|
||||
[NotNullWhen(true)] out CargoBountyLabelComponent? labelComp)
|
||||
{
|
||||
labelEnt = null;
|
||||
labelComp = null;
|
||||
if (!_containerQuery.TryGetComponent(uid, out var containerMan))
|
||||
return false;
|
||||
|
||||
// make sure this label was actually applied to a crate.
|
||||
if (!_container.TryGetContainer(uid, LabelSystem.ContainerName, out var container, containerMan))
|
||||
return false;
|
||||
|
||||
if (container.ContainedEntities.FirstOrNull() is not { } label ||
|
||||
!_bountyLabelQuery.TryGetComponent(label, out var component))
|
||||
return false;
|
||||
|
||||
labelEnt = label;
|
||||
labelComp = component;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, StationCargoBountyDatabaseComponent component, MapInitEvent args)
|
||||
{
|
||||
FillBountyDatabase(uid, component);
|
||||
@@ -161,12 +187,53 @@ public sealed partial class CargoSystem
|
||||
UpdateBountyConsoles();
|
||||
}
|
||||
|
||||
public void RerollBountyDatabase(Entity<StationCargoBountyDatabaseComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return;
|
||||
|
||||
entity.Comp.Bounties.Clear();
|
||||
FillBountyDatabase(entity);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, EntityUid? station, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
if (!TryGetBountyLabel(container, out _, out var component))
|
||||
{
|
||||
bountyEntities = new();
|
||||
return false;
|
||||
}
|
||||
|
||||
station ??= _station.GetOwningStation(container);
|
||||
if (station == null)
|
||||
{
|
||||
bountyEntities = new();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetBountyFromId(station.Value, component.Id, out var bounty))
|
||||
{
|
||||
bountyEntities = new();
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsBountyComplete(container, bounty.Value, out bountyEntities);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, CargoBountyData data)
|
||||
{
|
||||
if (!_protoMan.TryIndex<CargoBountyPrototype>(data.Bounty, out var proto))
|
||||
return false;
|
||||
return IsBountyComplete(container, data, out _);
|
||||
}
|
||||
|
||||
return IsBountyComplete(container, proto.Entries);
|
||||
public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
if (!_protoMan.TryIndex(data.Bounty, out var proto))
|
||||
{
|
||||
bountyEntities = new();
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsBountyComplete(container, proto.Entries, out bountyEntities);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, string id)
|
||||
@@ -184,26 +251,18 @@ public sealed partial class CargoSystem
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, IEnumerable<CargoBountyItemEntry> entries)
|
||||
{
|
||||
var contained = new HashSet<EntityUid>();
|
||||
if (TryComp<ContainerManagerComponent>(container, out var containers))
|
||||
{
|
||||
foreach (var con in containers.Containers.Values)
|
||||
{
|
||||
if (con.ID == LabelSystem.ContainerName)
|
||||
continue;
|
||||
|
||||
foreach (var ent in con.ContainedEntities)
|
||||
{
|
||||
contained.Add(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IsBountyComplete(contained, entries);
|
||||
return IsBountyComplete(container, entries, out _);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(HashSet<EntityUid> entities, IEnumerable<CargoBountyItemEntry> entries)
|
||||
public bool IsBountyComplete(EntityUid container, IEnumerable<CargoBountyItemEntry> entries, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
return IsBountyComplete(GetBountyEntities(container), entries, out bountyEntities);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(HashSet<EntityUid> entities, IEnumerable<CargoBountyItemEntry> entries, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
bountyEntities = new();
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var count = 0;
|
||||
@@ -215,7 +274,8 @@ public sealed partial class CargoSystem
|
||||
{
|
||||
if (!entry.Whitelist.IsValid(entity, EntityManager))
|
||||
continue;
|
||||
count++;
|
||||
|
||||
count += _stackQuery.CompOrNull(entity)?.Count ?? 1;
|
||||
temp.Add(entity);
|
||||
|
||||
if (count >= entry.Amount)
|
||||
@@ -228,17 +288,58 @@ public sealed partial class CargoSystem
|
||||
foreach (var ent in temp)
|
||||
{
|
||||
entities.Remove(ent);
|
||||
bountyEntities.Add(ent);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private HashSet<EntityUid> GetBountyEntities(EntityUid uid)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>
|
||||
{
|
||||
uid
|
||||
};
|
||||
if (!TryComp<ContainerManagerComponent>(uid, out var containers))
|
||||
return entities;
|
||||
|
||||
foreach (var container in containers.Containers.Values)
|
||||
{
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
if (_bountyLabelQuery.HasComponent(ent))
|
||||
continue;
|
||||
|
||||
var children = GetBountyEntities(ent);
|
||||
foreach (var child in children)
|
||||
{
|
||||
entities.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public bool TryAddBounty(EntityUid uid, StationCargoBountyDatabaseComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
// todo: consider making the cargo bounties weighted.
|
||||
var bounty = _random.Pick(_protoMan.EnumeratePrototypes<CargoBountyPrototype>().ToList());
|
||||
var allBounties = _protoMan.EnumeratePrototypes<CargoBountyPrototype>().ToList();
|
||||
var filteredBounties = new List<CargoBountyPrototype>();
|
||||
foreach (var proto in allBounties)
|
||||
{
|
||||
if (component.Bounties.Any(b => b.Bounty == proto.ID))
|
||||
continue;
|
||||
filteredBounties.Add(proto);
|
||||
}
|
||||
|
||||
var pool = filteredBounties.Count == 0 ? allBounties : filteredBounties;
|
||||
var bounty = _random.Pick(pool);
|
||||
return TryAddBounty(uid, bounty, component);
|
||||
}
|
||||
|
||||
@@ -261,17 +362,15 @@ public sealed partial class CargoSystem
|
||||
if (component.Bounties.Count >= component.MaxBounties)
|
||||
return false;
|
||||
|
||||
var duration = MathF.Round(_random.NextFloat(component.MinBountyTime, component.MaxBountyTime) / 15) * 15;
|
||||
var endTime = _timing.CurTime + TimeSpan.FromSeconds(duration);
|
||||
|
||||
component.Bounties.Add(new CargoBountyData(component.TotalBounties, bounty.ID, endTime));
|
||||
_nameIdentifier.GenerateUniqueName(uid, BountyNameIdentifierGroup, out var randomVal);
|
||||
component.Bounties.Add(new CargoBountyData(bounty, randomVal));
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"Added bounty \"{bounty.ID}\" (id:{component.TotalBounties}) to station {ToPrettyString(uid)}");
|
||||
component.TotalBounties++;
|
||||
return true;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public bool TryRemoveBounty(EntityUid uid, int dataId, StationCargoBountyDatabaseComponent? component = null)
|
||||
public bool TryRemoveBounty(EntityUid uid, string dataId, StationCargoBountyDatabaseComponent? component = null)
|
||||
{
|
||||
if (!TryGetBountyFromId(uid, dataId, out var data, component))
|
||||
return false;
|
||||
@@ -298,7 +397,7 @@ public sealed partial class CargoSystem
|
||||
|
||||
public bool TryGetBountyFromId(
|
||||
EntityUid uid,
|
||||
int id,
|
||||
string id,
|
||||
[NotNullWhen(true)] out CargoBountyData? bounty,
|
||||
StationCargoBountyDatabaseComponent? component = null)
|
||||
{
|
||||
@@ -333,17 +432,9 @@ public sealed partial class CargoSystem
|
||||
private void UpdateBounty()
|
||||
{
|
||||
var query = EntityQueryEnumerator<StationCargoBountyDatabaseComponent>();
|
||||
while (query.MoveNext(out var uid, out var bountyDatabase))
|
||||
while (query.MoveNext(out var bountyDatabase))
|
||||
{
|
||||
bountyDatabase.CheckedBounties.Clear();
|
||||
var bounties = new ValueList<CargoBountyData>(bountyDatabase.Bounties);
|
||||
foreach (var bounty in bounties)
|
||||
{
|
||||
if (_timing.CurTime < bounty.EndTime)
|
||||
continue;
|
||||
TryRemoveBounty(uid, bounty, bountyDatabase);
|
||||
FillBountyDatabase(uid, bountyDatabase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user