Chill bounties + fixes (#23411)

* Chill bounties + fixes

* localize

* fix arbitage
This commit is contained in:
Nemanja
2024-01-03 19:34:47 -05:00
committed by GitHub
parent 9d8ac7846a
commit 4662d463b8
22 changed files with 284 additions and 182 deletions

View File

@@ -1,7 +1,6 @@
using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Cargo.BUI;

View File

@@ -8,14 +8,13 @@
HorizontalExpand="True">
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<RichTextLabel Name="TimeLabel"/>
<RichTextLabel Name="RewardLabel"/>
<RichTextLabel Name="ManifestLabel"/>
</BoxContainer>
<Control MinWidth="10"/>
<BoxContainer Orientation="Vertical" MinWidth="120">
<Button Name="PrintButton" Text="{Loc 'bounty-console-label-button-text'}" HorizontalExpand="False" HorizontalAlignment="Right"/>
<Label Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
</BoxContainer>
</BoxContainer>
<customControls:HSeparator Margin="5 10 5 10"/>

View File

@@ -27,8 +27,6 @@ public sealed partial class BountyEntry : BoxContainer
if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
return;
EndTime = bounty.EndTime;
var items = new List<string>();
foreach (var entry in bountyPrototype.Entries)
{
@@ -39,16 +37,8 @@ public sealed partial class BountyEntry : BoxContainer
ManifestLabel.SetMarkup(Loc.GetString("bounty-console-manifest-label", ("item", string.Join(", ", items))));
RewardLabel.SetMarkup(Loc.GetString("bounty-console-reward-label", ("reward", bountyPrototype.Reward)));
DescriptionLabel.SetMarkup(Loc.GetString("bounty-console-description-label", ("description", Loc.GetString(bountyPrototype.Description))));
IdLabel.Text = Loc.GetString("bounty-console-id-label", ("id", bounty.Id));
IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id)));
PrintButton.OnPressed += _ => OnButtonPressed?.Invoke();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
var remaining = TimeSpan.FromSeconds(Math.Max((EndTime - _timing.CurTime).TotalSeconds, 0));
TimeLabel.SetMarkup(Loc.GetString("bounty-console-time-label", ("time", remaining.ToString("mm':'ss"))));
}
}

View File

@@ -9,7 +9,7 @@ namespace Content.Client.Cargo.UI;
[GenerateTypedNameReferences]
public sealed partial class CargoBountyMenu : FancyWindow
{
public Action<int>? OnLabelButtonPressed;
public Action<string>? OnLabelButtonPressed;
public CargoBountyMenu()
{

View File

@@ -80,7 +80,7 @@ public sealed class CargoTest
foreach (var bounty in bounties)
{
if (cargo.IsBountyComplete(ent, bounty))
Assert.That(proto.PointCost, Is.GreaterThan(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.PointCost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!");
Assert.That(proto.PointCost, Is.GreaterThanOrEqualTo(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.PointCost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!");
}
entManager.DeleteEntity(ent);

View File

@@ -10,8 +10,8 @@ public sealed partial class CargoBountyLabelComponent : Component
/// <summary>
/// The ID for the bounty this label corresponds to.
/// </summary>
[DataField("id"), ViewVariables(VVAccess.ReadWrite)]
public int Id;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public string Id = string.Empty;
/// <summary>
/// Used to prevent recursion in calculating the price.

View File

@@ -11,37 +11,25 @@ public sealed partial class StationCargoBountyDatabaseComponent : Component
/// <summary>
/// Maximum amount of bounties a station can have.
/// </summary>
[DataField("maxBounties"), ViewVariables(VVAccess.ReadWrite)]
public int MaxBounties = 3;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int MaxBounties = 5;
/// <summary>
/// A list of all the bounties currently active for a station.
/// </summary>
[DataField("bounties"), ViewVariables(VVAccess.ReadWrite)]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public List<CargoBountyData> Bounties = new();
/// <summary>
/// Used to determine unique order IDs
/// </summary>
[DataField("totalBounties")]
[DataField]
public int TotalBounties;
/// <summary>
/// The minimum amount of time the bounty lasts before being removed.
/// </summary>
[DataField("minBountyTime"), ViewVariables(VVAccess.ReadWrite)]
public float MinBountyTime = 600f;
/// <summary>
/// The maximum amount of time the bounty lasts before being removed.
/// </summary>
[DataField("maxBountyTime"), ViewVariables(VVAccess.ReadWrite)]
public float MaxBountyTime = 905f;
/// <summary>
/// A list of bounty IDs that have been checked this tick.
/// Used to prevent multiplying bounty prices.
/// </summary>
[DataField]
public HashSet<int> CheckedBounties = new();
public HashSet<string> CheckedBounties = new();
}

View File

@@ -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(container, entries, out _);
}
return IsBountyComplete(contained, 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)
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);
}
}
}
}

View File

@@ -288,10 +288,15 @@ public sealed partial class CargoSystem
return false;
}
var complete = IsBountyComplete(uid, (EntityUid?) null, out var bountyEntities);
// Recursively check for mobs at any point.
var children = xform.ChildEnumerator;
while (children.MoveNext(out var child))
{
if (complete && bountyEntities.Contains(child))
continue;
if (!CanSell(child, _xformQuery.GetComponent(child)))
return false;
}

View File

@@ -13,7 +13,6 @@ using Content.Shared.Containers.ItemSlots;
using Content.Shared.Mobs.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Map;

View File

@@ -46,6 +46,15 @@ public sealed class NameIdentifierSystem : EntitySystem
}
}
/// <summary>
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
/// but does not set the entity's name.
/// </summary>
public string GenerateUniqueName(EntityUid uid, ProtoId<NameIdentifierGroupPrototype> proto, out int randomVal)
{
return GenerateUniqueName(uid, _prototypeManager.Index(proto), out randomVal);
}
/// <summary>
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
/// but does not set the entity's name.

View File

@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Linq;
using Content.Server.Administration;
using Content.Server.Cargo.Systems;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
@@ -15,6 +16,7 @@ namespace Content.Server.Station.Commands;
public sealed class StationsCommand : ToolshedCommand
{
private StationSystem? _station;
private CargoSystem? _cargo;
[CommandImplementation("list")]
public IEnumerable<EntityUid> List()
@@ -111,6 +113,15 @@ public sealed class StationsCommand : ToolshedCommand
_station.RenameStation(input, name.Evaluate(ctx)!);
}
[CommandImplementation("rerollBounties")]
public void RerollBounties([CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input)
{
_cargo ??= GetSys<CargoSystem>();
_cargo.RerollBountyDatabase(input);
}
}
public record struct OnlyOneStationsError : IConError

View File

@@ -1,7 +1,6 @@
using Robust.Shared.Serialization;
using Content.Shared.Cargo.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Prototypes;
namespace Content.Shared.Cargo;
@@ -9,28 +8,24 @@ namespace Content.Shared.Cargo;
/// A data structure for storing currently available bounties.
/// </summary>
[DataDefinition, NetSerializable, Serializable]
public readonly partial record struct CargoBountyData(int Id, string Bounty, TimeSpan EndTime)
public readonly partial record struct CargoBountyData
{
/// <summary>
/// A numeric id used to identify the bounty
/// A unique id used to identify the bounty
/// </summary>
[DataField("id"), ViewVariables(VVAccess.ReadWrite)]
public int Id { get; init; } = Id;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public string Id { get; init; } = string.Empty;
/// <summary>
/// The prototype containing information about the bounty.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("bounty", customTypeSerializer: typeof(PrototypeIdSerializer<CargoBountyPrototype>), required:true)]
public string Bounty { get; init; } = Bounty;
[DataField(required: true)]
public ProtoId<CargoBountyPrototype> Bounty { get; init; } = string.Empty;
/// <summary>
/// The time at which the bounty is closed and no longer is available.
/// </summary>
[DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan EndTime { get; init; } = EndTime;
public CargoBountyData() : this(default, string.Empty, default)
public CargoBountyData(CargoBountyPrototype bounty, int uniqueIdentifier)
{
Bounty = bounty.ID;
Id = $"{bounty.IdPrefix}{uniqueIdentifier:D3}";
}
}

View File

@@ -48,9 +48,9 @@ public sealed class CargoBountyConsoleState : BoundUserInterfaceState
[Serializable, NetSerializable]
public sealed class BountyPrintLabelMessage : BoundUserInterfaceMessage
{
public int BountyId;
public string BountyId;
public BountyPrintLabelMessage(int bountyId)
public BountyPrintLabelMessage(string bountyId)
{
BountyId = bountyId;
}

View File

@@ -9,7 +9,7 @@ namespace Content.Shared.Cargo.Prototypes;
/// that must be sold together in a labeled container in order
/// to receive a monetary reward.
/// </summary>
[Prototype("cargoBounty"), Serializable, NetSerializable]
[Prototype, Serializable, NetSerializable]
public sealed partial class CargoBountyPrototype : IPrototype
{
/// <inheritdoc/>
@@ -19,20 +19,26 @@ public sealed partial class CargoBountyPrototype : IPrototype
/// <summary>
/// The monetary reward for completing the bounty
/// </summary>
[DataField("reward", required: true)]
[DataField(required: true)]
public int Reward;
/// <summary>
/// A description for flava purposes.
/// </summary>
[DataField("description")]
public string Description = string.Empty;
[DataField]
public LocId Description = string.Empty;
/// <summary>
/// The entries that must be satisfied for the cargo bounty to be complete.
/// </summary>
[DataField("entries", required: true)]
[DataField( required: true)]
public List<CargoBountyItemEntry> Entries = new();
/// <summary>
/// A prefix appended to the beginning of a bounty's ID.
/// </summary>
[DataField]
public string IdPrefix = "NT";
}
[DataDefinition, Serializable, NetSerializable]
@@ -41,7 +47,7 @@ public readonly partial record struct CargoBountyItemEntry()
/// <summary>
/// A whitelist for determining what items satisfy the entry.
/// </summary>
[DataField("whitelist", required: true)]
[DataField(required: true)]
public EntityWhitelist Whitelist { get; init; } = default!;
// todo: implement some kind of simple generic condition system
@@ -49,12 +55,12 @@ public readonly partial record struct CargoBountyItemEntry()
/// <summary>
/// How much of the item must be present to satisfy the entry
/// </summary>
[DataField("amount")]
[DataField]
public int Amount { get; init; } = 1;
/// <summary>
/// A player-facing name for the item.
/// </summary>
[DataField("name")]
public string Name { get; init; } = string.Empty;
[DataField]
public LocId Name { get; init; } = string.Empty;
}

View File

@@ -31,7 +31,7 @@ bounty-item-pen = Pen
bounty-item-percussion = Percussion instrument
bounty-item-pie = Pie
bounty-item-prison-uniform = Prison uniform
bounty-item-radio = Radio
bounty-item-radio = Radio or Headset
bounty-item-research-disk = Research disk
bounty-item-shiv = Shiv
bounty-item-soap = Soap

View File

@@ -2,7 +2,7 @@
bounty-console-label-button-text = Print label
bounty-console-time-label = Time: [color=orange]{$time}[/color]
bounty-console-reward-label = Reward: [color=limegreen]${$reward}[/color]
bounty-console-manifest-label = Manifest: [color=gray]{$item}[/color]
bounty-console-manifest-label = Manifest: [color=orange]{$item}[/color]
bounty-console-manifest-entry =
{ $amount ->
[1] {$item}
@@ -14,5 +14,5 @@ bounty-console-id-label = ID#{$id}
bounty-console-flavor-left = Bounties sourced from local unscrupulous dealers.
bounty-console-flavor-right = v1.4
bounty-manifest-header = Official cargo bounty manifest (ID#{$id})
bounty-manifest-header = [font size=14][bold]Official cargo bounty manifest[/bold] (ID#{$id})[/font]
bounty-manifest-list-start = Item manifest:

View File

@@ -40,6 +40,8 @@ command-description-stations-rename =
Renames the given station.
command-description-stations-largestgrid =
Returns the largest grid the given station has, if any.
command-description-stations-rerollBounties =
Clears all the current bounties for the station and gets a new selection.
command-description-stationevent-lsprob =
Lists the probability of different station events occuring out of the entire pool.
command-description-stationevent-lsprobtime =

View File

@@ -11,8 +11,9 @@
- type: cargoBounty
id: BountyBaseballBat
reward: 8000
reward: 4000
description: bounty-description-baseball-bat
idPrefix: CC
entries:
- name: bounty-item-baseball-bat
amount: 5
@@ -22,7 +23,7 @@
- type: cargoBounty
id: BountyBoxHug
reward: 6000
reward: 3000
description: bounty-description-box-hugs
entries:
- name: bounty-item-box-hugs
@@ -33,7 +34,7 @@
- type: cargoBounty
id: BountyBrain
reward: 25000
reward: 12500
description: bounty-description-brain
entries:
- name: bounty-item-brain
@@ -44,7 +45,7 @@
- type: cargoBounty
id: BountyBread
reward: 4000
reward: 2000
description: bounty-description-bread
entries:
- name: bounty-item-bread
@@ -55,8 +56,9 @@
- type: cargoBounty
id: BountyBriefcase
reward: 15000
reward: 7500
description: bounty-description-briefcase
idPrefix: CC
entries:
- name: bounty-item-briefcase
amount: 5
@@ -66,8 +68,9 @@
- type: cargoBounty
id: BountyCarrot
reward: 15000
reward: 7500
description: bounty-description-carrot
idPrefix: SS15-
entries:
- name: bounty-item-carrot
amount: 10
@@ -77,7 +80,7 @@
- type: cargoBounty
id: BountyCarrotFries
reward: 14000
reward: 7000
description: bounty-description-carrot-fries
entries:
- name: bounty-item-carrot-fries
@@ -88,8 +91,9 @@
- type: cargoBounty
id: BountyCarp
reward: 25000
reward: 12500
description: bounty-description-carp
idPrefix: CC
entries:
- name: bounty-item-carp
amount: 1
@@ -115,7 +119,7 @@
- type: cargoBounty
id: BountyCorn
reward: 20000
reward: 10000
description: bounty-description-corn
entries:
- name: bounty-item-corn
@@ -126,7 +130,7 @@
- type: cargoBounty
id: BountyCrayon
reward: 8000
reward: 4000
description: bounty-description-crayon
entries:
- name: bounty-item-crayon
@@ -137,8 +141,9 @@
- type: cargoBounty
id: BountyCubanCarp
reward: 32000
reward: 16000
description: bounty-description-cuban-carp
idPrefix: CC
entries:
- name: bounty-item-cuban-carp
amount: 1
@@ -148,8 +153,9 @@
- type: cargoBounty
id: BountyDonkPocket
reward: 12000
reward: 6000
description: bounty-description-donk-pocket
idPrefix: CC
entries:
- name: bounty-item-donk-pocket
amount: 12
@@ -159,8 +165,9 @@
- type: cargoBounty
id: BountyDonut
reward: 12000
reward: 6000
description: bounty-description-donut
idPrefix: CC
entries:
- name: bounty-item-donut
amount: 10
@@ -170,7 +177,7 @@
- type: cargoBounty
id: BountyFigurine
reward: 16000
reward: 8000
description: bounty-description-figurine
entries:
- name: bounty-item-figurine
@@ -179,21 +186,11 @@
tags:
- Figurine
- type: cargoBounty
id: BountyFleshMonster
reward: 30000
description: bounty-description-flesh-monster
entries:
- name: bounty-item-flesh-monster
amount: 3
whitelist:
tags:
- Flesh
- type: cargoBounty
id: BountyFlower
reward: 4000
reward: 2000
description: bounty-description-flower
idPrefix: CC
entries:
- name: bounty-item-flower
amount: 3
@@ -203,7 +200,7 @@
- type: cargoBounty
id: BountyGalaxyThistle
reward: 24000
reward: 12000
description: bounty-description-galaxythistle
entries:
- name: bounty-item-galaxythistle
@@ -214,8 +211,9 @@
- type: cargoBounty
id: BountyHandcuffs
reward: 4000
reward: 1000
description: bounty-description-handcuffs
idPrefix: CC
entries:
- name: bounty-item-handcuffs
amount: 5
@@ -236,7 +234,7 @@
- type: cargoBounty
id: BountyKnife
reward: 12000
reward: 6000
description: bounty-description-knife
entries:
- name: bounty-item-knife
@@ -247,7 +245,7 @@
- type: cargoBounty
id: BountyLemon
reward: 20000
reward: 10000
description: bounty-description-lemon
entries:
- name: bounty-item-lemon
@@ -258,8 +256,9 @@
- type: cargoBounty
id: BountyLime
reward: 20000
reward: 10000
description: bounty-description-lime
idPrefix: CC
entries:
- name: bounty-item-lime
amount: 7
@@ -269,7 +268,7 @@
- type: cargoBounty
id: BountyLung
reward: 35000
reward: 2000
description: bounty-description-lung
entries:
- name: bounty-item-lung
@@ -280,8 +279,9 @@
- type: cargoBounty
id: BountyMonkeyCube
reward: 8000
reward: 2000
description: bounty-description-monkey-cube
idPrefix: CC
entries:
- name: bounty-item-monkey-cube
amount: 3
@@ -293,6 +293,7 @@
id: BountyMouse
reward: 2400
description: bounty-description-mouse
idPrefix: SS13-
entries:
- name: bounty-item-mouse
amount: 5
@@ -302,7 +303,7 @@
- type: cargoBounty
id: BountyPancake
reward: 20000
reward: 10000
description: bounty-description-pancake
entries:
- name: bounty-item-pancake
@@ -317,14 +318,14 @@
description: bounty-description-pen
entries:
- name: bounty-item-pen
amount: 10
amount: 5
whitelist:
tags:
- Write
- Pen
- type: cargoBounty
id: BountyPercussion
reward: 25000
reward: 15000
description: bounty-description-percussion
entries:
- name: bounty-item-percussion
@@ -335,7 +336,7 @@
- type: cargoBounty
id: BountyPie
reward: 31415
reward: 3141
description: bounty-description-pie
entries:
- name: bounty-item-pie
@@ -348,6 +349,7 @@
id: BountyPrisonUniform
reward: 8000
description: bounty-description-prison-uniform
idPrefix: TG
entries:
- name: bounty-item-prison-uniform
amount: 4
@@ -357,7 +359,7 @@
- type: cargoBounty
id: BountyRadio
reward: 10000
reward: 7500
description: bounty-description-radio
entries:
- name: bounty-item-radio
@@ -369,21 +371,11 @@
tags:
- Radio
- type: cargoBounty
id: BountyResearchDisk
reward: 12000
description: bounty-description-research-disk
entries:
- name: bounty-item-research-disk
amount: 1
whitelist:
components:
- ResearchDisk
- type: cargoBounty
id: BountyShiv
reward: 8000
reward: 4000
description: bounty-description-shiv
idPrefix: SYN
entries:
- name: bounty-item-shiv
amount: 5
@@ -393,7 +385,7 @@
- type: cargoBounty
id: BountySoap
reward: 8000
reward: 4000
description: bounty-description-soap
entries:
- name: bounty-item-soap
@@ -404,7 +396,7 @@
- type: cargoBounty
id: BountySoup
reward: 12000
reward: 6000
description: bounty-description-soup
entries:
- name: bounty-item-soup
@@ -426,7 +418,7 @@
- type: cargoBounty
id: BountySyringe
reward: 10000
reward: 5000
description: bounty-description-syringe
entries:
- name: bounty-item-syringe
@@ -439,6 +431,7 @@
id: BountyTechDisk
reward: 25000
description: bounty-description-tech-disk
idPrefix: SS13-
entries:
- name: bounty-item-tech-disk
amount: 5
@@ -450,6 +443,7 @@
id: BountyToolbox
reward: 8000
description: bounty-description-toolbox
idPrefix: CC
entries:
- name: bounty-item-toolbox
amount: 6
@@ -459,7 +453,7 @@
- type: cargoBounty
id: BountyTrash
reward: 700
reward: 500
description: bounty-description-trash
entries:
- name: bounty-item-trash
@@ -503,8 +497,9 @@
- type: cargoBounty
id: BountyOrgans
reward: 3500
reward: 2000
description: bounty-description-organs
idPrefix: UNTH
entries:
- name: bounty-item-organs
amount: 8
@@ -525,8 +520,9 @@
- type: cargoBounty
id: BountyWarmCloth
reward: 6000
reward: 2000
description: bounty-description-warm-cloth
idPrefix: UNTH
entries:
- name: bounty-item-warm-cloth
amount: 8
@@ -536,8 +532,9 @@
- type: cargoBounty
id: BountyBattery
reward: 24500
reward: 15000
description: bounty-description-battery
idPrefix: UNTH
entries:
- name: bounty-item-battery
amount: 15
@@ -549,6 +546,7 @@
id: BountyLaserGun
reward: 28500
description: bounty-description-lasergun
idPrefix: IV
entries:
- name: bounty-lasergun
amount: 6
@@ -558,8 +556,9 @@
- type: cargoBounty
id: BountyFood
reward: 9500
reward: 4000
description: bounty-description-food
idPrefix: UNTH
entries:
- name: bounty-food
amount: 30

View File

@@ -239,6 +239,7 @@
- type: Tag
tags:
- Write
- Pen
- type: Sprite
sprite: Objects/Misc/bureaucracy.rsi
state: pen

View File

@@ -35,3 +35,8 @@
# Used to suffix a number to any mob to identify player controlled mob griefing.
- type: nameIdentifierGroup
id: GenericNumber
- type: nameIdentifierGroup
id: Bounty
minValue: 0
maxValue: 999

View File

@@ -830,6 +830,9 @@
- type: Tag
id: Pancake
- type: Tag
id: Pen
- type: Tag
id: PepperShaker