traitor reputation real (#2913)
* add reputation system * add contracts button to PDA * give traitors contracts * add GetRandomObjective to SharedObjectivesSystem * add TryRemoveObjective overload * add everything needed for reputation store and objectives * "ui" * giant uplink reputation tagging * prevent buying reputation-locked gear with uplink implant * :trollface: * more ui stuff * :trollface: * :trollface: * remove default objectives * :trollface: * UI fixes * stuff * objective component changes * add offerings and stuff to yml * make some objectives work * make rcd objective real * more ui fix * :trollface: * ui usable * bunch of low risk objectives * syndie jail wip * more wip * massive amount of work * add bad guidebooks * prevent fultoning anchored things * fixes * disable claim buttons when no slots are open * :trollface: * :trollface: * :trollface: * update ui when a slot unlocks * move rejecting to offerings, remove rescan (automatic now) * add verb to attach fulton, fix * dont get objective to ransom/kill someone you already ransomed * make offering slots random + maximum count in rep level * :trollface: * rela * update syndie jail * more syndie jail update * make marshal real * fix * fix power room power * remove access from timer * :trollface: * engine * allow syndicate items on syndie jail * add ransom ui * update map loading * add ransom purchasing to cargo request console * warn not error for loading * ui fixes * final fixy * mg book fix * final fixy II * remove redundant Announcement word * more [] * end ransom when leaving the jail * :trollface: * 10 tc for roundstart traitors * make nuke core objective work for traitor * guidebook gaming * early merge of guidebook comments * update bunch of objectives * add min reputation to kill fellow traitor * guidebook gaming * evil * giant stuff * dont offer dupe objectives * evil * pronoun * fix * grr * blacklist assist and DAGD from assist objective * grr * fix linter * remove objectives from traitor rule test * nuke disk anti-troll * evil test * add objectives test to find bad objective groups * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixy * fix * fix stuff * reword ransom announcement * pro * goida --------- Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
3f0b91df49
commit
ac1b82d406
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Cargo;
|
||||
using Content.Client.Cargo.UI;
|
||||
using Content.Shared._DV.Traitor; // DeltaV
|
||||
using Content.Shared.Cargo.BUI;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Content.Shared.Cargo.Events;
|
||||
|
|
@ -92,6 +93,7 @@ namespace Content.Client.Cargo.BUI
|
|||
};
|
||||
_menu.OnOrderApproved += ApproveOrder;
|
||||
_menu.OnOrderCanceled += RemoveOrder;
|
||||
_menu.OnRansomPurchase += ent => SendMessage(new RansomPurchaseMessage(ent)); // DeltaV
|
||||
_orderMenu.SubmitButton.OnPressed += (_) =>
|
||||
{
|
||||
if (AddOrder())
|
||||
|
|
@ -140,6 +142,7 @@ namespace Content.Client.Cargo.BUI
|
|||
|
||||
_menu?.UpdateStation(station);
|
||||
Populate(cState.Orders);
|
||||
_menu?.UpdateRansoms(cState.Ransoms, BankBalance); // DeltaV
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:traitor="clr-namespace:Content.Client._DV.Traitor.UI"
|
||||
SetSize="600 600"
|
||||
MinSize="600 600">
|
||||
<BoxContainer Orientation="Vertical" Margin="15 5 15 10">
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
<RichTextLabel Name="PointsLabel"
|
||||
Text="$0" />
|
||||
</BoxContainer>
|
||||
<traitor:RansomContainer Name="RansomContainer"/> <!-- DeltaV - show ransomed mobs -->
|
||||
<Control MinHeight="10"/>
|
||||
<TabContainer Name="TabContainer" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Cargo.Systems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._DV.Traitor; // DeltaV
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
|
|
@ -32,6 +33,7 @@ namespace Content.Client.Cargo.UI
|
|||
public event Action<ButtonEventArgs>? OnItemSelected;
|
||||
public event Action<ButtonEventArgs>? OnOrderApproved;
|
||||
public event Action<ButtonEventArgs>? OnOrderCanceled;
|
||||
public event Action<NetEntity>? OnRansomPurchase; // DeltaV
|
||||
|
||||
public event Action<ProtoId<CargoAccountPrototype>?, int>? OnAccountAction;
|
||||
|
||||
|
|
@ -57,6 +59,7 @@ namespace Content.Client.Cargo.UI
|
|||
|
||||
SearchBar.OnTextChanged += OnSearchBarTextChanged;
|
||||
Categories.OnItemSelected += OnCategoryItemSelected;
|
||||
RansomContainer.OnPurchase += ent => OnRansomPurchase?.Invoke(ent); // DeltaV
|
||||
|
||||
if (entMan.TryGetComponent<CargoOrderConsoleComponent>(owner, out var orderConsole))
|
||||
{
|
||||
|
|
@ -238,6 +241,14 @@ namespace Content.Client.Cargo.UI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV: Forwards new ransom data to the ransom container.
|
||||
/// </summary>
|
||||
public void UpdateRansoms(List<RansomData> ransoms, int balance)
|
||||
{
|
||||
RansomContainer.UpdateRansoms(ransoms, balance);
|
||||
}
|
||||
|
||||
public void PopulateAccountActions()
|
||||
{
|
||||
if (!_entityManager.TryGetComponent<StationBankAccountComponent>(_station, out var bank) ||
|
||||
|
|
@ -264,6 +275,7 @@ namespace Content.Client.Cargo.UI
|
|||
public void UpdateStation(EntityUid station)
|
||||
{
|
||||
_station = station;
|
||||
RansomContainer.UpdateStation(_station); // DeltaV
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Client.CartridgeLoader;
|
||||
using Content.Shared._DV.Reputation; // DeltaV
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.PDA;
|
||||
|
|
@ -72,6 +73,8 @@ namespace Content.Client.PDA
|
|||
SendMessage(new PdaLockUplinkMessage());
|
||||
};
|
||||
|
||||
_menu.ContractsButton.OnPressed += _ => SendMessage(new PdaShowContractsMessage()); // DeltaV
|
||||
|
||||
_menu.OnProgramItemPressed += ActivateCartridge;
|
||||
_menu.OnInstallButtonPressed += InstallCartridge;
|
||||
_menu.OnUninstallButtonPressed += UninstallCartridge;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@
|
|||
Visible="False"
|
||||
Text="{Loc 'pda-bound-user-interface-lock-uplink-title'}"
|
||||
Description="{Loc 'pda-bound-user-interface-lock-uplink-description'}"/>
|
||||
<!-- Begin DeltaV Additions -->
|
||||
<pda:PdaSettingsButton Name="ContractsButton"
|
||||
Access="Public"
|
||||
Visible="False"
|
||||
Text="{Loc 'pda-bound-user-interface-contracts-title'}"
|
||||
Description="{Loc 'pda-bound-user-interface-contracts-description'}"/>
|
||||
<!-- End DeltaV Additions -->
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Orientation="Vertical"
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ namespace Content.Client.PDA
|
|||
ActivateMusicButton.Visible = state.CanPlayMusic;
|
||||
ShowUplinkButton.Visible = state.HasUplink;
|
||||
LockUplinkButton.Visible = state.HasUplink;
|
||||
ContractsButton.Visible = state.HasUplink; // DeltaV
|
||||
}
|
||||
|
||||
public void UpdateAvailablePrograms(List<(EntityUid, CartridgeComponent)> programs)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public sealed class FultonSystem : SharedFultonSystem
|
|||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
protected override void UpdateAppearance(EntityUid uid, FultonedComponent component)
|
||||
public override void UpdateAppearance(EntityUid uid, FultonedComponent component) // DeltaV - made public
|
||||
{
|
||||
if (!component.Effect.IsValid())
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared._DV.Objectives.Systems;
|
||||
|
||||
namespace Content.Client._DV.Objectives.Systems;
|
||||
|
||||
public sealed class ContractObjectiveSystem : SharedContractObjectiveSystem;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Horizontal"
|
||||
HorizontalExpand="true"
|
||||
MinHeight="64"
|
||||
Margin="5">
|
||||
<Label Name="Title" HorizontalExpand="True"/> <!-- Set in constructor -->
|
||||
<Button Name="CompleteButton" Text="Complete" Margin="5"/>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Contract control placed in a slot that has a contract active.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class Contract : BoxContainer
|
||||
{
|
||||
public event Action? OnComplete;
|
||||
|
||||
public Contract(string title)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Title.Text = title;
|
||||
|
||||
CompleteButton.OnPressed += _ => OnComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:rep="clr-namespace:Content.Client._DV.Reputation.UI"
|
||||
Orientation="Horizontal"
|
||||
HorizontalExpand="true"
|
||||
Align="Center"
|
||||
MinHeight="48"
|
||||
Margin="5">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="5">
|
||||
<Label Name="Title"/> <!-- Set in constructor -->
|
||||
<rep:UnlockLabel Name="UnlockLabel"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="ButtonsContainer" Orientation="Vertical">
|
||||
<Button Name="AcceptButton" Text="{Loc 'contracts-accept'}"/>
|
||||
<Button Name="RejectButton" Text="{Loc 'contracts-reject'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using Content.Shared._DV.Reputation;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Contract control placed in a slot that with no contract.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ContractOffering : BoxContainer
|
||||
{
|
||||
public event Action? OnAccept;
|
||||
public event Action? OnReject;
|
||||
public event Action? OnUnlock;
|
||||
|
||||
public bool AcceptDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return AcceptButton.Disabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
AcceptButton.Disabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ContractOffering(OfferingSlot slot)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
if (slot.Title is {} title)
|
||||
Title.Text = title;
|
||||
else
|
||||
Title.Visible = false;
|
||||
|
||||
UnlockLabel.NextUnlock = slot.NextUnlock;
|
||||
UnlockLabel.OnUnlock += () => OnUnlock?.Invoke();
|
||||
|
||||
OnUnlock += () => ButtonsContainer.Visible = true;
|
||||
ButtonsContainer.Visible = !UnlockLabel.IsLocked;
|
||||
|
||||
AcceptButton.OnPressed += _ => OnAccept?.Invoke();
|
||||
RejectButton.OnPressed += _ => OnReject?.Invoke();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Shared._DV.Reputation;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
public sealed class ContractsBUI : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private ContractsWindow? _window;
|
||||
|
||||
public ContractsBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
_window = this.CreateWindow<ContractsWindow>();
|
||||
_window.Owner = Owner;
|
||||
_window.OnAccept += i => SendMessage(new ContractsAcceptMessage(i));
|
||||
_window.OnComplete += i => SendMessage(new ContractsCompleteMessage(i));
|
||||
_window.OnReject += i => SendMessage(new ContractsRejectMessage(i));
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is ContractsState)
|
||||
_window?.UpdateState();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MinSize="500 400"
|
||||
Title="{Loc 'contracts-menu-title'}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<controls:StripeBack>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="Level" Align="Center" StyleClasses="LabelBig"/> <!-- Set at runtime -->
|
||||
<Label Name="Reputation" Align="Center" StyleClasses="LabelBig"/> <!-- Set at runtime -->
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
<Label Text="{Loc 'contracts-contracts'}" Margin="5"/>
|
||||
<controls:HLine Color="#404040" Thickness="2" Margin="0 5 0 5"/>
|
||||
<BoxContainer Name="Contracts" Orientation="Vertical" HorizontalExpand="True" Margin="5"/> <!-- Populated at runtime -->
|
||||
<controls:HLine Color="#404040" Thickness="2" Margin="0 5 0 5"/>
|
||||
<Label Text="{Loc 'contracts-offerings'}" Align="Right" Margin="5"/>
|
||||
<controls:HLine Color="#404040" Thickness="2" Margin="0 5 0 5"/>
|
||||
<BoxContainer Name="Offerings" Orientation="Vertical" HorizontalExpand="True" Margin="5"/> <!-- Populated at runtime -->
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._DV.Reputation;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ContractsWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private EntityManager _entMan = default!;
|
||||
|
||||
public event Action<int>? OnAccept;
|
||||
public event Action<int>? OnComplete;
|
||||
public event Action<int>? OnReject;
|
||||
|
||||
public EntityUid Owner;
|
||||
|
||||
public ContractsWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void UpdateState()
|
||||
{
|
||||
if (!_entMan.TryGetComponent<ContractsComponent>(Owner, out var comp))
|
||||
return;
|
||||
|
||||
if (comp.CurrentLevel is {} level)
|
||||
Level.Text = Loc.GetString(level.Name);
|
||||
Reputation.Text = $"{comp.Reputation} Reputation";
|
||||
|
||||
Contracts.RemoveAllChildren();
|
||||
var slotsFull = true;
|
||||
for (int i = 0; i < comp.Slots.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
if (comp.Slots[i].ObjectiveTitle is {} title)
|
||||
{
|
||||
var contract = new Contract(title);
|
||||
contract.OnComplete += () => OnComplete?.Invoke(index);
|
||||
Contracts.AddChild(contract);
|
||||
// TODO: green when objective is complete
|
||||
}
|
||||
else
|
||||
{
|
||||
var empty = new EmptyContract(comp.Slots[i].NextUnlock);
|
||||
empty.OnUnlock += EnableAccepts;
|
||||
if (!empty.IsLocked)
|
||||
slotsFull = false;
|
||||
Contracts.AddChild(empty);
|
||||
}
|
||||
}
|
||||
|
||||
Offerings.RemoveAllChildren();
|
||||
for (int i = 0; i < comp.OfferingSlots.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
var offering = new ContractOffering(comp.OfferingSlots[i]);
|
||||
offering.AcceptDisabled = slotsFull;
|
||||
offering.OnAccept += () => OnAccept?.Invoke(index);
|
||||
offering.OnReject += () => OnReject?.Invoke(index);
|
||||
Offerings.AddChild(offering);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableAccepts()
|
||||
{
|
||||
foreach (var child in Offerings.Children)
|
||||
{
|
||||
((ContractOffering) child).AcceptDisabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:rep="clr-namespace:Content.Client._DV.Reputation.UI"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="true"
|
||||
Align="Center"
|
||||
MinHeight="64"
|
||||
Margin="5">
|
||||
<Label Text="{Loc 'contract-slot-empty'}" HorizontalExpand="True"/>
|
||||
<rep:UnlockLabel Name="UnlockLabel"/>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Contract control placed in a slot that with no contract.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EmptyContract : BoxContainer
|
||||
{
|
||||
public event Action? OnUnlock;
|
||||
|
||||
public bool IsLocked => UnlockLabel.IsLocked;
|
||||
|
||||
public EmptyContract(TimeSpan? nextUnlock)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
UnlockLabel.NextUnlock = nextUnlock;
|
||||
UnlockLabel.OnUnlock += () => OnUnlock?.Invoke();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._DV.Reputation.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Label that updates for an unlock timer automatically.
|
||||
/// </summary>
|
||||
public sealed class UnlockLabel : Label
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public event Action? OnUnlock;
|
||||
|
||||
private TimeSpan? _nextUnlock;
|
||||
private TimeSpan _nextUpdate;
|
||||
|
||||
public TimeSpan? NextUnlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return _nextUnlock;
|
||||
}
|
||||
set
|
||||
{
|
||||
_nextUnlock = value;
|
||||
_nextUpdate = _timing.CurTime;
|
||||
UpdateUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLocked => Visible;
|
||||
|
||||
public UnlockLabel()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
private void UpdateUnlock()
|
||||
{
|
||||
if (_nextUnlock is not {} next)
|
||||
return;
|
||||
|
||||
var now = _timing.CurTime;
|
||||
if (now >= next)
|
||||
{
|
||||
// unlocked now
|
||||
_nextUnlock = null;
|
||||
Visible = false;
|
||||
OnUnlock?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
Visible = true;
|
||||
var remaining = next - now;
|
||||
var time = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
|
||||
Text = Loc.GetString("contract-next-unlock", ("time", time));
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var now = _timing.CurTime;
|
||||
if (now < _nextUpdate)
|
||||
return;
|
||||
|
||||
_nextUpdate = now + TimeSpan.FromSeconds(1);
|
||||
UpdateUnlock();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared._DV.Traitor;
|
||||
|
||||
namespace Content.Client._DV.Traitor;
|
||||
|
||||
public sealed class ExtractionFultonSystem : SharedExtractionFultonSystem;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Visible="False"
|
||||
HorizontalExpand="True">
|
||||
<controls:StripeBack HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" Align="Center">
|
||||
<RichTextLabel Text="{Loc 'ransom-ui-warning'}"/>
|
||||
</BoxContainer>
|
||||
<PanelContainer HorizontalExpand="True" Margin="8">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#222" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="ListingsContainer" Orientation="Vertical"/> <!-- Populated at runtime with RansomListing -->
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Traitor.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RansomContainer : BoxContainer
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
public event Action<NetEntity>? OnPurchase;
|
||||
|
||||
public RansomContainer()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void UpdateRansoms(List<RansomData> ransoms, int balance)
|
||||
{
|
||||
Visible = ransoms.Count > 0;
|
||||
ListingsContainer.RemoveAllChildren();
|
||||
foreach (var ransom in ransoms)
|
||||
{
|
||||
var listing = new RansomListing(ransom);
|
||||
listing.UpdateBalance(balance);
|
||||
listing.OnPurchase += ent => OnPurchase?.Invoke(ent);
|
||||
ListingsContainer.AddChild(listing);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateStation(EntityUid? station)
|
||||
{
|
||||
if (!_entMan.TryGetComponent<StationBankAccountComponent>(station, out var bank))
|
||||
return;
|
||||
|
||||
var balance = bank.Accounts[bank.PrimaryAccount];
|
||||
foreach (var control in ListingsContainer.Children)
|
||||
{
|
||||
if (control is RansomListing listing)
|
||||
listing.UpdateBalance(balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
MinHeight="64" HorizontalExpand="True">
|
||||
<Button Name="PurchaseButton" HorizontalExpand="True" Disabled="True"/>
|
||||
<Label Name="PriceLabel" Margin="8" MinWidth="64" Align="Center"/>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Shared._DV.Traitor;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Traitor.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RansomListing : BoxContainer
|
||||
{
|
||||
public event Action<NetEntity>? OnPurchase;
|
||||
|
||||
private NetEntity _ent;
|
||||
private int _price;
|
||||
|
||||
public RansomListing(RansomData data)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_ent = data.Entity;
|
||||
_price = data.Price;
|
||||
|
||||
PurchaseButton.Text = Loc.GetString("ransom-ui-purchase", ("name", data.Name));
|
||||
PriceLabel.Text = "$" + _price;
|
||||
|
||||
PurchaseButton.OnPressed += _ => OnPurchase?.Invoke(_ent);
|
||||
}
|
||||
|
||||
public void UpdateBalance(int balance)
|
||||
{
|
||||
PurchaseButton.Disabled = balance < _price;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ public sealed class TraitorRuleTest
|
|||
private const string TraitorGameRuleProtoId = "Traitor";
|
||||
private const string TraitorAntagRoleName = "Traitor";
|
||||
|
||||
[Test]
|
||||
//[Test] // DeltaV - test is useless with reputation
|
||||
public async Task TestTraitorObjectives()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
using Content.Shared._DV.Reputation;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.IntegrationTests.Tests._DV;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that all ids in objective groups are valid
|
||||
/// Objectives system just does nothing if you have a nonexistent prototype in an objective group.
|
||||
/// </summary>
|
||||
public sealed class ObjectivesTest
|
||||
{
|
||||
[Test]
|
||||
public async Task AllReputationObjectivesValid()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
var protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var factory = server.ResolveDependency<IComponentFactory>();
|
||||
|
||||
var pools = new HashSet<WeightedRandomPrototype>();
|
||||
foreach (var level in protoMan.EnumeratePrototypes<ReputationLevelPrototype>())
|
||||
{
|
||||
pools.Add(protoMan.Index(level.OfferingGroups));
|
||||
}
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// 3 nested loops we gaming
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var pool in pools)
|
||||
{
|
||||
foreach (var id in pool.Weights.Keys)
|
||||
{
|
||||
Assert.That(protoMan.TryIndex<WeightedRandomPrototype>(id, out var proto),
|
||||
$"Unknown objective group {id} found in offering group {pool.ID}");
|
||||
foreach (var obj in proto.Weights.Keys)
|
||||
{
|
||||
Assert.That(protoMan.TryIndex<EntityPrototype>(obj, out var objProto),
|
||||
$"Unknown objective {obj} found in objective group {id}");
|
||||
Assert.That(objProto.TryGetComponent<ObjectiveComponent>(out _, factory),
|
||||
$"Entity {obj} in objective group {id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Shared._DV.Traitor; // DeltaV
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.BUI;
|
||||
using Content.Shared.Cargo.Components;
|
||||
|
|
@ -24,6 +25,7 @@ namespace Content.Server.Cargo.Systems
|
|||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly EmagSystem _emag = default!;
|
||||
[Dependency] private readonly RansomSystem _ransom = default!; // DeltaV
|
||||
|
||||
private void InitializeConsole()
|
||||
{
|
||||
|
|
@ -419,7 +421,8 @@ namespace Content.Server.Cargo.Systems
|
|||
GetOutstandingOrderCount(orderDatabase, console.Account),
|
||||
orderDatabase.Capacity,
|
||||
GetNetEntity(station.Value),
|
||||
orderDatabase.Orders[console.Account]
|
||||
orderDatabase.Orders[console.Account],
|
||||
_ransom.GetRansoms() // DeltaV
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public sealed partial class CargoSystem : SharedCargoSystem
|
|||
InitializeBounty();
|
||||
InitializeFunds();
|
||||
InitializeATS(); // DeltaV
|
||||
InitializeRansom(); // DeltaV
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Content.Server.Objectives;
|
|||
using Content.Server.PDA.Ringer;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Traitor.Uplink;
|
||||
using Content.Shared._DV.Reputation; // DeltaV
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
|
|
@ -34,6 +35,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
|||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!; // DeltaV
|
||||
[Dependency] private readonly SharedRoleCodewordSystem _roleCodewordSystem = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
|
||||
[Dependency] private readonly UplinkSystem _uplink = default!;
|
||||
|
|
@ -186,6 +188,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
|||
// Codes are only generated if the uplink is a PDA
|
||||
var ev = new GenerateUplinkCodeEvent();
|
||||
RaiseLocalEvent(pda.Value, ref ev);
|
||||
_reputation.AddContracts(traitor, pda.Value); // DeltaV
|
||||
|
||||
if (ev.Code is { } generatedCode)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -237,7 +237,8 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
|
|||
}
|
||||
}
|
||||
|
||||
public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId<WeightedRandomPrototype> objectiveGroupProto, float maxDifficulty)
|
||||
// DeltaV - override shared method
|
||||
public override EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId<WeightedRandomPrototype> objectiveGroupProto, float maxDifficulty)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groupsProto))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public sealed class PickObjectiveTargetSystem : EntitySystem
|
|||
/// DeltaV - Common code deduplicated from above functions.
|
||||
/// Filters all alive humans and picks a target from them.
|
||||
/// </summary>
|
||||
private void AssignRandomTarget(EntityUid uid, ref ObjectiveAssignedEvent args, Predicate<EntityUid> filter, bool fallbackToAny = true)
|
||||
public void AssignRandomTarget(EntityUid uid, ref ObjectiveAssignedEvent args, Predicate<EntityUid> filter, bool fallbackToAny = true)
|
||||
{
|
||||
// invalid prototype
|
||||
if (!TryComp<TargetObjectiveComponent>(uid, out var target))
|
||||
|
|
@ -124,6 +124,20 @@ public sealed class PickObjectiveTargetSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
// Begin DeltaV Additions - no being asked to kill someone at centcom or whatever
|
||||
if (args.Mind.OwnedEntity is {} mob)
|
||||
{
|
||||
var map = Transform(mob).MapID;
|
||||
allHumans.RemoveAll(mindId =>
|
||||
{
|
||||
if (Comp<MindComponent>(mindId).OwnedEntity is {} otherMob)
|
||||
return Transform(otherMob).MapID != map;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// End DeltaV Additions
|
||||
|
||||
// Filter out targets based on the filter
|
||||
var filteredHumans = allHumans.Where(mind => filter(mind)).ToList();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Server.PDA.Ringer;
|
|||
using Content.Server.Station.Systems;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Server.Traitor.Uplink;
|
||||
using Content.Shared._DV.Reputation; // DeltaV
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.Chat;
|
||||
|
|
@ -35,6 +36,7 @@ namespace Content.Server.PDA
|
|||
[Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
|
||||
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly IdCardSystem _idCard = default!;
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!; // DeltaV
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -50,6 +52,7 @@ namespace Content.Server.PDA
|
|||
SubscribeLocalEvent<PdaComponent, PdaShowMusicMessage>(OnUiMessage);
|
||||
SubscribeLocalEvent<PdaComponent, PdaShowUplinkMessage>(OnUiMessage);
|
||||
SubscribeLocalEvent<PdaComponent, PdaLockUplinkMessage>(OnUiMessage);
|
||||
SubscribeLocalEvent<PdaComponent, PdaShowContractsMessage>(OnShowContracts); // DeltaV
|
||||
|
||||
SubscribeLocalEvent<PdaComponent, CartridgeLoaderNotificationSentEvent>(OnNotification);
|
||||
|
||||
|
|
@ -278,6 +281,18 @@ namespace Content.Server.PDA
|
|||
}
|
||||
}
|
||||
|
||||
// End DeltaV Additions
|
||||
private void OnShowContracts(EntityUid uid, PdaComponent pda, PdaShowContractsMessage msg)
|
||||
{
|
||||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||||
return;
|
||||
|
||||
// check if its locked again to prevent malicious clients opening locked uplinks
|
||||
if (HasComp<ContractsComponent>(uid) && IsUnlocked(uid))
|
||||
_reputation.ToggleUI(msg.Actor, uid);
|
||||
}
|
||||
// End DeltaV Additions
|
||||
|
||||
private bool IsUnlocked(EntityUid uid)
|
||||
{
|
||||
return !TryComp<RingerUplinkComponent>(uid, out var uplink) || uplink.Unlocked;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared._DV.Traitor; // DeltaV
|
||||
using Content.Shared.Salvage.Fulton;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
|
|
@ -74,6 +75,10 @@ public sealed class FultonSystem : SharedFultonSystem
|
|||
Coordinates = GetNetCoordinates(oldCoords),
|
||||
});
|
||||
}
|
||||
// Begin DeltaV Additions: Event for syndicate fultons to use
|
||||
var ev = new FultonedEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
// End DeltaV Additions
|
||||
|
||||
Audio.PlayPvs(component.Sound, uid);
|
||||
RemCompDeferred<FultonedComponent>(uid);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Content.Server._DV.Cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a cabinet count as a steal target if it has a steal target inside it.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class StealTargetCabinetComponent : Component;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server._DV.Cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Handles container events for <see cref="StealTargetCabinetComponent"/>.
|
||||
/// </summary>
|
||||
public sealed class StealTargetCabinetSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StealTargetCabinetComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||
SubscribeLocalEvent<StealTargetCabinetComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
}
|
||||
|
||||
private void OnEntInserted(Entity<StealTargetCabinetComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (TryComp<StealTargetComponent>(args.Entity, out var target))
|
||||
EnsureComp<StealTargetComponent>(ent).StealGroup = target.StealGroup;
|
||||
}
|
||||
|
||||
private void OnEntRemoved(Entity<StealTargetCabinetComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
RemComp<StealTargetComponent>(ent);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Cargo.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles purchasing ransomed entities from a cargo request console.
|
||||
/// </summary>
|
||||
public sealed partial class CargoSystem
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly SharedEntityStorageSystem _entityStorage = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The crate to put ransomed entities in when purchasing them.
|
||||
/// </summary>
|
||||
public static readonly EntProtoId RansomCrate = "CrateSyndicate";
|
||||
|
||||
/// <summary>
|
||||
/// Status effect for <see cref="ForcedSleepingComponent"/>.
|
||||
/// </summary>
|
||||
public static readonly ProtoId<StatusEffectPrototype> StatusEffectKey = "ForcedSleep";
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play for the ransom victim when being "trafficked" to the ATS.
|
||||
/// </summary>
|
||||
public static readonly SoundSpecifier HypoSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// How long to be slept for.
|
||||
/// </summary>
|
||||
public static readonly TimeSpan SleepyTime = TimeSpan.FromSeconds(10);
|
||||
|
||||
private void InitializeRansom()
|
||||
{
|
||||
Subs.BuiEvents<CargoOrderConsoleComponent>(CargoConsoleUiKey.Orders, subs =>
|
||||
{
|
||||
subs.Event<RansomPurchaseMessage>(OnPurchaseMessage);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPurchaseMessage(Entity<CargoOrderConsoleComponent> ent, ref RansomPurchaseMessage args)
|
||||
{
|
||||
var user = args.Actor;
|
||||
if (!_accessReaderSystem.IsAllowed(user, ent))
|
||||
{
|
||||
ConsolePopup(user, Loc.GetString("cargo-console-order-not-allowed"));
|
||||
PlayDenySound(ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// malf client or they somehow got gibbed in jail
|
||||
if (GetEntity(args.Entity) is not { Valid: true } uid ||
|
||||
// got released already
|
||||
!TryComp<RansomComponent>(uid, out var ransom) ||
|
||||
// not on a station
|
||||
_station.GetOwningStation(uid) is not {} station ||
|
||||
!TryComp<StationBankAccountComponent>(station, out var bank) ||
|
||||
!TryComp<StationDataComponent>(station, out var stationData))
|
||||
{
|
||||
ConsolePopup(user, Loc.GetString("cargo-console-station-not-found"));
|
||||
PlayDenySound(ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
var cost = ransom.Ransom;
|
||||
var balance = bank.Accounts[bank.PrimaryAccount];
|
||||
if (cost > balance)
|
||||
{
|
||||
ConsolePopup(user, Loc.GetString("cargo-console-insufficient-funds", ("cost", cost)));
|
||||
PlayDenySound(ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// paid the ransom, time to bring em home
|
||||
if (TryReturnEntity(uid, stationData) is not {} trade)
|
||||
{
|
||||
ConsolePopup(user, Loc.GetString("cargo-console-unfulfilled"));
|
||||
PlayDenySound(ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
_audio.PlayPvs(ApproveSound, ent);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
||||
$"{ToPrettyString(user):user} paid the ransom of ${cost} for {ToPrettyString(uid)} with balance at {balance}");
|
||||
|
||||
UpdateBankAccount((station, bank), -cost, CreateAccountDistribution((station, bank)));
|
||||
|
||||
// announce it so everyone knows
|
||||
var msg = Loc.GetString("syndicate-ransom-return-announcement", ("station", trade));
|
||||
var sender = Loc.GetString("syndicate-ransom-return-announcement-sender");
|
||||
var sound = new SoundPathSpecifier("/Audio/Misc/notice1.ogg");
|
||||
var color = Color.Red;
|
||||
_chat.DispatchGlobalAnnouncement(msg, sender, playSound: true, sound, color);
|
||||
}
|
||||
|
||||
// like TryFulfillOrder but for ransoms
|
||||
private EntityUid? TryReturnEntity(EntityUid uid, StationDataComponent station)
|
||||
{
|
||||
_listEnts.Clear();
|
||||
GetTradeStations(station, ref _listEnts);
|
||||
// Try to fulfill from any station where possible, if the pad is not occupied.
|
||||
foreach (var trade in _listEnts)
|
||||
{
|
||||
var tradePads = GetCargoPallets(trade, BuySellType.Buy);
|
||||
_random.Shuffle(tradePads);
|
||||
|
||||
var freePads = GetFreeCargoPallets(trade, tradePads);
|
||||
if (freePads.Count == 0)
|
||||
continue;
|
||||
|
||||
// sleepy time
|
||||
_audio.PlayPvs(HypoSound, uid);
|
||||
_statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(uid, StatusEffectKey, SleepyTime, refresh: false);
|
||||
|
||||
var pad = _random.Pick(freePads);
|
||||
var coordinates = new EntityCoordinates(trade, pad.Transform.LocalPosition);
|
||||
var crate = Spawn(RansomCrate, coordinates);
|
||||
if (!_entityStorage.Insert(uid, crate))
|
||||
_transformSystem.DropNextTo(uid, crate); // just teleport directly if it somehow fails
|
||||
return trade;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using Content.Server.Antag;
|
||||
using Content.Server.Antag.Components;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Nuke;
|
||||
using Content.Shared.Popups;
|
||||
|
||||
namespace Content.Server._DV.Nuke;
|
||||
|
||||
/// <summary>
|
||||
/// When a syndie extracts the nuke disk, gives it to nukies as soon as possible.
|
||||
/// If nukies are taking years and a sleeper steals it, an arbitrary nukie gets it.
|
||||
/// If there are no nukies it waits until a loneop spawns.
|
||||
/// </summary>
|
||||
public sealed class NukeDiskSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NukeDiskComponent, FultonedEvent>(OnFultoned);
|
||||
SubscribeLocalEvent<TeleportDiskRuleComponent, AfterAntagEntitySelectedEvent>(OnAntagEntSelected);
|
||||
}
|
||||
|
||||
private void OnFultoned(Entity<NukeDiskComponent> ent, ref FultonedEvent args)
|
||||
{
|
||||
// no free win for using salv fultons
|
||||
if (!HasComp<ExtractingComponent>(ent))
|
||||
return;
|
||||
|
||||
// just incase another system somehow doesn't do it
|
||||
RemCompDeferred<ExtractingComponent>(ent);
|
||||
|
||||
ent.Comp.Extracted = true;
|
||||
// give it to an arbitrary nukie if a sleeper/whatever steals it
|
||||
// everyone wins
|
||||
if (FindLivingNukie() is {} target)
|
||||
TeleportDisk(ent, target);
|
||||
}
|
||||
|
||||
private void OnAntagEntSelected(Entity<TeleportDiskRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (FindExtractedDisk() is not {} disk)
|
||||
return;
|
||||
|
||||
// this nukie is arbitrary but its probably definitely a loneop anyway
|
||||
TeleportDisk(disk, args.EntityUid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to give the disk to a living nukie.
|
||||
/// </summary>
|
||||
public void TeleportDisk(Entity<NukeDiskComponent> ent, EntityUid target)
|
||||
{
|
||||
if (!ent.Comp.Extracted)
|
||||
return;
|
||||
|
||||
ent.Comp.Extracted = false; // no repeated teleports
|
||||
|
||||
_adminLogger.Add(LogType.Teleport, LogImpact.High, $"Teleported {ToPrettyString(ent):disk} to {ToPrettyString(target)} because it was extracted by a syndie");
|
||||
_hands.PickupOrDrop(target, ent);
|
||||
_popup.PopupEntity(Loc.GetString("nuke-disk-teleported", ("disk", ent)), target, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a nuke disk that has been stolen by a syndie via extraction fulton.
|
||||
/// </summary>
|
||||
public Entity<NukeDiskComponent>? FindExtractedDisk()
|
||||
{
|
||||
var query = EntityQueryEnumerator<NukeDiskComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.Extracted)
|
||||
return (uid, comp);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a living nukie mob to give the disk to.
|
||||
/// </summary>
|
||||
public EntityUid? FindLivingNukie()
|
||||
{
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, AntagSelectionComponent>();
|
||||
while (query.MoveNext(out _, out _, out var comp))
|
||||
{
|
||||
foreach (var (mindId, _) in comp.AssignedMinds)
|
||||
{
|
||||
if (TryComp<MindComponent>(mindId, out var mind) &&
|
||||
GetEntity(mind.OriginalOwnedEntity) is {} mob &&
|
||||
_mob.IsAlive(mob))
|
||||
{
|
||||
return mob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Content.Server._DV.Nuke;
|
||||
|
||||
/// <summary>
|
||||
/// Component added to a nukie gamerule to teleport a stolen nuke disk to the first nukie it spawns.
|
||||
/// <see cref="NukeDiskSystem"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TeleportDiskRuleComponent : Component;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Picks a random contract of the target mind and requires that it be completed.
|
||||
/// Requires that the objective has picked a target with at least 1 objective.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AssistRandomContractSystem))]
|
||||
public sealed partial class AssistRandomContractComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Description that gets "contract" passed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId Description = "objective-condition-assist-traitor-description";
|
||||
|
||||
/// <summary>
|
||||
/// Blacklist for objective entities that cannot be assisted with.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
|
||||
/// <summary>
|
||||
/// The picked contract.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Contract;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks which assist objectives are active for a given contract.
|
||||
/// If this contract is failed all of the assists are failed too.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AssistRandomContractSystem))]
|
||||
public sealed partial class AssistedContractComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public HashSet<EntityUid> Assisting = new();
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Store;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this objective part of a syndicate contract, granting TC and reputation upon completion.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ContractObjectiveSystem))]
|
||||
public sealed partial class ContractObjectiveComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How much reputation to add when completed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// How much currency to give when completed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public FixedPoint2 Payment;
|
||||
|
||||
/// <summary>
|
||||
/// Pay when the contract is taken but disable rejecting it.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Prepaid;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this contract can be rejected.
|
||||
/// Funded contracts cannot be rejected to prevent infinite TC exploiting.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Rejectable => !Prepaid;
|
||||
|
||||
/// <summary>
|
||||
/// What currency to add.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CurrencyPrototype> Currency = "Telecrystal";
|
||||
|
||||
/// <summary>
|
||||
/// The PDA used to take this contract.
|
||||
/// Might not always exist.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Pda;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
using Content.Shared.Objectives;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that you extract an item using syndicate fultons.
|
||||
/// This effectively removes it from the round.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ExtractConditionSystem))]
|
||||
public sealed partial class ExtractConditionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A group of items to be stolen
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<StealTargetGroupPrototype> StealGroup;
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, disables generation of this target if there is no entity on the map (disable for objects that can be created mid-round).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool VerifyMapExistence = true;
|
||||
|
||||
/// <summary>
|
||||
/// Help newer players by saying e.g. "steal the chief engineer's advanced magboots"
|
||||
/// instead of "steal advanced magboots". Should be a loc string.
|
||||
/// </summary>
|
||||
[DataField("owner")]
|
||||
public LocId? OwnerText;
|
||||
|
||||
[DataField(required: true)]
|
||||
public LocId ObjectiveText;
|
||||
[DataField(required: true)]
|
||||
public LocId ObjectiveNoOwnerText;
|
||||
[DataField(required: true)]
|
||||
public LocId DescriptionText;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the player is not in a certain department to have this objective.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(NotDepartmentRequirementSystem))]
|
||||
public sealed partial class NotDepartmentRequirementComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the department to ban from having this objective.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DepartmentPrototype> Department;
|
||||
}
|
||||
|
|
@ -1,9 +1,23 @@
|
|||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server._DV.Objectives.Systems;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target for <see cref="TargetObjectiveComponent"/> to a random traitor.
|
||||
/// Sets the target for <see cref="TargetObjectiveComponent"/> to a random traitor who has enough reputation.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PickRandomTraitorComponent : Component;
|
||||
[RegisterComponent, Access(typeof(PickRandomTraitorSystem))]
|
||||
public sealed partial class PickRandomTraitorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum reputation to require, or 0 for no requirement.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinReputation;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum number of active contracts a traitor needs to have.
|
||||
/// By necessity requires a traitor to have a PDA that isn't deleted.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinContracts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that you kidnap a person using syndicate fultons.
|
||||
/// They can be bought back at a cargo ordering console.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(RansomConditionSystem))]
|
||||
public sealed partial class RansomConditionComponent : Component;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires a certain number of reputation to roll an objective.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ReputationConditionSystem))]
|
||||
public sealed partial class ReputationConditionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The required reputation.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public int Reputation;
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared._DV.Reputation;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
public sealed class AssistRandomContractSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||
[Dependency] private readonly ContractObjectiveSystem _contractObjective = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly MetaDataSystem _meta = default!;
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!;
|
||||
[Dependency] private readonly TargetObjectiveSystem _target = default!;
|
||||
|
||||
private List<EntityUid> _available = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AssistRandomContractComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||
SubscribeLocalEvent<AssistRandomContractComponent, ComponentShutdown>(OnShutdown);
|
||||
|
||||
SubscribeLocalEvent<AssistedContractComponent, ContractCompletedEvent>(OnCompleted);
|
||||
SubscribeLocalEvent<AssistedContractComponent, ContractFailedEvent>(OnFailed);
|
||||
}
|
||||
|
||||
private void OnAfterAssign(Entity<AssistRandomContractComponent> ent, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
if (!_target.GetTarget(ent, out var target))
|
||||
return;
|
||||
|
||||
if (_reputation.GetMindContracts(target.Value) is not {} contracts)
|
||||
return;
|
||||
|
||||
_available.Clear();
|
||||
foreach (var obj in contracts.Comp.Objectives)
|
||||
{
|
||||
if (obj is {} uid && _whitelist.IsBlacklistFailOrNull(ent.Comp.Blacklist, uid))
|
||||
_available.Add(uid);
|
||||
}
|
||||
|
||||
var contract = _random.Pick(_available);
|
||||
ent.Comp.Contract = contract;
|
||||
StartAssisting(contract, ent);
|
||||
|
||||
// set description so you know what to do
|
||||
var desc = Loc.GetString(ent.Comp.Description, ("contract", Name(contract)));
|
||||
_meta.SetEntityDescription(ent, desc, args.Meta);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<AssistRandomContractComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (ent.Comp.Contract is {} contract)
|
||||
StopAssisting(contract, ent);
|
||||
}
|
||||
|
||||
private void OnCompleted(Entity<AssistedContractComponent> ent, ref ContractCompletedEvent args)
|
||||
{
|
||||
foreach (var uid in ent.Comp.Assisting)
|
||||
{
|
||||
_codeCondition.SetCompleted(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFailed(Entity<AssistedContractComponent> ent, ref ContractFailedEvent args)
|
||||
{
|
||||
foreach (var uid in ent.Comp.Assisting)
|
||||
{
|
||||
_contractObjective.TryFailContract(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartAssisting(EntityUid contract, EntityUid assisting)
|
||||
{
|
||||
EnsureComp<AssistedContractComponent>(contract).Assisting.Add(assisting);
|
||||
}
|
||||
|
||||
public void StopAssisting(EntityUid contract, EntityUid assisting)
|
||||
{
|
||||
if (!TryComp<AssistedContractComponent>(contract, out var comp))
|
||||
return;
|
||||
|
||||
comp.Assisting.Remove(assisting);
|
||||
if (comp.Assisting.Count > 0)
|
||||
return;
|
||||
|
||||
// nobody is assisting anymore :(
|
||||
RemComp<AssistedContractComponent>(contract);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared._DV.Objectives.Systems;
|
||||
using Content.Shared._DV.Reputation;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles reputation + TC gains for <see cref="ContractObjectiveComponent"/>.
|
||||
/// </summary>
|
||||
public sealed class ContractObjectiveSystem : SharedContractObjectiveSystem
|
||||
{
|
||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!;
|
||||
[Dependency] private readonly StoreSystem _store = default!;
|
||||
|
||||
private Dictionary<string, FixedPoint2> _currency = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ContractObjectiveComponent, ContractTakenEvent>(OnTaken);
|
||||
SubscribeLocalEvent<ContractObjectiveComponent, ContractCompletedEvent>(OnCompleted);
|
||||
}
|
||||
|
||||
private void OnTaken(Entity<ContractObjectiveComponent> ent, ref ContractTakenEvent args)
|
||||
{
|
||||
ent.Comp.Pda = args.Pda;
|
||||
|
||||
if (ent.Comp.Prepaid)
|
||||
Pay(ent, args.Pda);
|
||||
}
|
||||
|
||||
private void OnCompleted(Entity<ContractObjectiveComponent> ent, ref ContractCompletedEvent args)
|
||||
{
|
||||
_reputation.GiveReputation(args.Pda, ent.Comp.Reputation);
|
||||
if (!ent.Comp.Prepaid)
|
||||
Pay(ent, args.Pda);
|
||||
}
|
||||
|
||||
private void Pay(Entity<ContractObjectiveComponent> ent, EntityUid pda)
|
||||
{
|
||||
_currency.Clear();
|
||||
_currency[ent.Comp.Currency] = ent.Comp.Payment;
|
||||
_store.TryAddCurrency(_currency, pda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fail all active incomplete contracts with a given component, based on a predicate.
|
||||
/// </summary>
|
||||
public void FailContracts<T>(Predicate<Entity<T>> pred) where T: Component
|
||||
{
|
||||
var query = EntityQueryEnumerator<T, ContractObjectiveComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var contract))
|
||||
{
|
||||
if (_codeCondition.IsCompleted(uid) || !pred((uid, comp)))
|
||||
continue;
|
||||
|
||||
if (contract.Pda is {} pda && TryComp<ContractsComponent>(pda, out var contracts))
|
||||
_reputation.TryFailContract((pda, contracts), uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Look up an objective's stored pda and try to fail it.
|
||||
/// </summary>
|
||||
public bool TryFailContract(Entity<ContractObjectiveComponent?> objective)
|
||||
{
|
||||
return Resolve(objective, ref objective.Comp) &&
|
||||
objective.Comp.Pda is {} pda &&
|
||||
TryComp<ContractsComponent>(pda, out var comp) &&
|
||||
_reputation.TryFailContract((pda, comp), objective);
|
||||
}
|
||||
|
||||
public override string ContractName(EntityUid objective)
|
||||
{
|
||||
var title = base.ContractName(objective);
|
||||
if (!TryComp<ContractObjectiveComponent>(objective, out var contract))
|
||||
return title;
|
||||
|
||||
return $"{title} - {contract.Reputation} REP + {contract.Payment} TC";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
public sealed class ExtractConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly MetaDataSystem _meta = default!;
|
||||
[Dependency] private readonly ContractObjectiveSystem _contract = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExtractConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||
SubscribeLocalEvent<ExtractConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||
|
||||
SubscribeLocalEvent<StealTargetComponent, FultonedEvent>(OnFultoned);
|
||||
}
|
||||
|
||||
/// start checks of target acceptability, and generation of start values.
|
||||
private void OnAssigned(Entity<ExtractConditionComponent> ent, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
if (args.Cancelled || !ent.Comp.VerifyMapExistence || args.Mind.OwnedEntity is not {} mob)
|
||||
return;
|
||||
|
||||
// very important: only check the current map, so syndie vault doesn't count as existing
|
||||
var map = Transform(mob).MapID;
|
||||
|
||||
var found = false;
|
||||
var query = EntityQueryEnumerator<StealTargetComponent, TransformComponent>();
|
||||
var group = ent.Comp.StealGroup;
|
||||
while (query.MoveNext(out var target, out var xform))
|
||||
{
|
||||
if (xform.MapID != map || target.StealGroup != group)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
args.Cancelled = !found;
|
||||
}
|
||||
|
||||
//Set the visual, name, icon for the objective.
|
||||
private void OnAfterAssign(Entity<ExtractConditionComponent> ent, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
var group = _proto.Index(ent.Comp.StealGroup);
|
||||
string localizedName = Loc.GetString(group.Name);
|
||||
|
||||
var title = ent.Comp.OwnerText == null
|
||||
? Loc.GetString(ent.Comp.ObjectiveNoOwnerText, ("itemName", localizedName))
|
||||
: Loc.GetString(ent.Comp.ObjectiveText, ("owner", Loc.GetString(ent.Comp.OwnerText)), ("itemName", localizedName));
|
||||
|
||||
var description = Loc.GetString(ent.Comp.DescriptionText, ("itemName", localizedName));
|
||||
|
||||
_meta.SetEntityName(ent, title, args.Meta);
|
||||
_meta.SetEntityDescription(ent, description, args.Meta);
|
||||
_objectives.SetIcon(ent, group.Sprite, args.Objective);
|
||||
}
|
||||
|
||||
private void OnFultoned(Entity<StealTargetComponent> ent, ref FultonedEvent args)
|
||||
{
|
||||
// don't touch objectives for salv fultons, return early
|
||||
if (!TryComp<ExtractingComponent>(ent, out var extracting))
|
||||
return;
|
||||
|
||||
RemCompDeferred<ExtractingComponent>(ent);
|
||||
|
||||
// complete the objective of the person that extracted it
|
||||
if (extracting.Mind is {} mindId && FindObjective(mindId, (ent, ent.Comp)) is {} objective)
|
||||
_codeCondition.SetCompleted(objective);
|
||||
|
||||
// fail every other contract for the same thing
|
||||
var group = ent.Comp.StealGroup;
|
||||
_contract.FailContracts<ExtractConditionComponent>(obj => obj.Comp.StealGroup == group);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find an objective that wants an item, or null if it isn't wanted.
|
||||
/// </summary>
|
||||
public EntityUid? FindObjective(Entity<MindComponent?> mind, Entity<StealTargetComponent?> item)
|
||||
{
|
||||
if (!Resolve(mind, ref mind.Comp) || !Resolve(item, ref item.Comp, false))
|
||||
return null;
|
||||
|
||||
var group = item.Comp.StealGroup;
|
||||
foreach (var objective in mind.Comp.Objectives)
|
||||
{
|
||||
if (!TryComp<ExtractConditionComponent>(objective, out var comp))
|
||||
continue;
|
||||
|
||||
// skip already completed objectives
|
||||
if (_codeCondition.IsCompleted(objective))
|
||||
continue;
|
||||
|
||||
if (comp.StealGroup == group)
|
||||
return objective;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the kill fellow traitor objective.
|
||||
/// </summary>
|
||||
public sealed class KillFellowTraitorObjectiveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly TargetObjectiveSystem _target = default!;
|
||||
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PickRandomTraitorComponent, ObjectiveAssignedEvent>(OnTraitorKillAssigned);
|
||||
}
|
||||
|
||||
private void OnTraitorKillAssigned(EntityUid uid, PickRandomTraitorComponent comp, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
if (!TryComp<TargetObjectiveComponent>(uid, out var target))
|
||||
{
|
||||
Log.Error($"Missing components for {uid}.");
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Target already assigned
|
||||
if (target.Target != null)
|
||||
{
|
||||
Log.Error($"Target already assigned for {uid}.");
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind);
|
||||
|
||||
List<EntityUid> validTraitorMinds = [];
|
||||
|
||||
// Going through each OTHER traitor
|
||||
foreach (var traitor in traitors)
|
||||
{
|
||||
var valid = true;
|
||||
// Going through each of OUR objectives.
|
||||
foreach (var objective in args.Mind.Objectives)
|
||||
{
|
||||
// If one of OUR objectives already targets a traitor, don't add it to the list.
|
||||
if (TryComp<TargetObjectiveComponent>(objective, out var targetComp) && targetComp.Target == traitor.Id)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
validTraitorMinds.Add(traitor.Id);
|
||||
}
|
||||
|
||||
// No other traitors
|
||||
if (validTraitorMinds.Count == 0)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_target.SetTarget(uid, _random.Pick(validTraitorMinds), target);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles checking the department blacklist for this objective.
|
||||
/// </summary>
|
||||
public sealed class NotDepartmentRequirementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedJobSystem _job = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NotDepartmentRequirementComponent, RequirementCheckEvent>(OnCheck);
|
||||
}
|
||||
|
||||
private void OnCheck(Entity<NotDepartmentRequirementComponent> ent, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled ||
|
||||
!_job.MindTryGetJob(args.MindId, out var job) ||
|
||||
!_job.TryGetPrimaryDepartment(job.ID, out var primary))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (primary.ID == ent.Comp.Department)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles picking a random traitor for the kill fellow traitor objective.
|
||||
/// </summary>
|
||||
public sealed class PickRandomTraitorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PickObjectiveTargetSystem _pickTarget = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PickRandomTraitorComponent, ObjectiveAssignedEvent>(OnRandomTraitorAssigned);
|
||||
}
|
||||
|
||||
private void OnRandomTraitorAssigned(Entity<PickRandomTraitorComponent> ent, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
_pickTarget.AssignRandomTarget(ent, ref args, mindId =>
|
||||
_role.MindHasRole<TraitorRoleComponent>(mindId));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Makes ransom announcements for ransom objectives and mob extraction objectives.
|
||||
/// </summary>
|
||||
public sealed class RansomConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||
[Dependency] private readonly ContractObjectiveSystem _contract = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly RansomSystem _ransom = default!;
|
||||
[Dependency] private readonly TargetObjectiveSystem _targetObjective = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MobStateComponent, FultonedEvent>(OnFultoned);
|
||||
}
|
||||
|
||||
private void OnFultoned(Entity<MobStateComponent> ent, ref FultonedEvent args)
|
||||
{
|
||||
if (!TryComp<ExtractingComponent>(ent, out var extracting))
|
||||
return;
|
||||
|
||||
RemCompDeferred<ExtractingComponent>(ent);
|
||||
|
||||
var ransom = _ransom.RansomEntity(ent);
|
||||
var msg = Loc.GetString("syndicate-ransom-announcement", ("hostage", ent), ("ransom", ransom));
|
||||
var sender = Loc.GetString("syndicate-ransom-announcement-sender");
|
||||
var sound = new SoundPathSpecifier("/Audio/Misc/notice1.ogg");
|
||||
var color = Color.Red;
|
||||
_chat.DispatchGlobalAnnouncement(msg, sender, playSound: true, sound, color);
|
||||
|
||||
// TODO: put their inventory into the vault
|
||||
|
||||
// complete the objective of the person that kidnapped them
|
||||
if (_mob.IsAlive(ent) && extracting.Mind is {} mindId && FindObjective(mindId, ent) is {} objective)
|
||||
_codeCondition.SetCompleted(objective);
|
||||
|
||||
_contract.FailContracts<RansomConditionComponent>(obj => TargetEquals(obj, ent));
|
||||
}
|
||||
|
||||
public EntityUid? FindObjective(Entity<MindComponent?> mind, EntityUid mob)
|
||||
{
|
||||
if (!Resolve(mind, ref mind.Comp))
|
||||
return null;
|
||||
|
||||
foreach (var objective in mind.Comp.Objectives)
|
||||
{
|
||||
if (!HasComp<RansomConditionComponent>(objective) || _codeCondition.IsCompleted(objective))
|
||||
continue;
|
||||
|
||||
if (TargetEquals(objective, mob))
|
||||
return objective;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool TargetEquals(EntityUid objective, EntityUid mob)
|
||||
{
|
||||
if (!_targetObjective.GetTarget(objective, out var target))
|
||||
return false;
|
||||
|
||||
// get the actual mob targeted for the objective
|
||||
if (TryComp<MindComponent>(target, out var targetMind) && GetEntity(targetMind.OriginalOwnedEntity) is {} targetMob)
|
||||
target = targetMob;
|
||||
|
||||
return mob == target;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using Content.Server._DV.Objectives.Components;
|
||||
using Content.Shared._DV.Reputation;
|
||||
using Content.Shared.Objectives.Components;
|
||||
|
||||
namespace Content.Server._DV.Objectives.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents <see cref="ReputationConditionComponent"/> being added if you lack the required reputation.
|
||||
/// </summary>
|
||||
public sealed class ReputationConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReputationConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||
}
|
||||
|
||||
private void OnAssigned(Entity<ReputationConditionComponent> ent, ref ObjectiveAssignedEvent args)
|
||||
{
|
||||
var reputation = _reputation.GetMindReputation(args.MindId) ?? 0;
|
||||
if (reputation < ent.Comp.Reputation)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Content.Server._DV.Shuttles.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._DV.Shuttles.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Added to station entity to load the syndie jail in its centcomm map.
|
||||
/// Without this syndie fultons won't work.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SyndieJailSystem))]
|
||||
public sealed partial class SyndieJailComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The grid to load.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ResPath Path = new ResPath("/Maps/_DV/Nonstations/syndie_jail.yml");
|
||||
|
||||
/// <summary>
|
||||
/// Minimum distance to load the grid at.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MinRange = 800f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance to load the grid at.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MaxRange = 1000f;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Content.Server._DV.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Station.Events;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._DV.Shuttles.Systems;
|
||||
|
||||
public sealed class SyndieJailSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SyndieJailComponent, StationPostInitEvent>(OnStationStartup);
|
||||
}
|
||||
|
||||
private void OnStationStartup(Entity<SyndieJailComponent> ent, ref StationPostInitEvent args)
|
||||
{
|
||||
var cc = Comp<StationCentcommComponent>(ent);
|
||||
if (cc.Entity is not {} gridUid || cc.MapEntity is not {} map)
|
||||
{
|
||||
Log.Warning($"No centcomm grid to load syndie jail from {ToPrettyString(ent)}!");
|
||||
return;
|
||||
}
|
||||
|
||||
var mapId = Comp<MapComponent>(map).MapId;
|
||||
var offset = _random.NextVector2(ent.Comp.MinRange, ent.Comp.MaxRange);
|
||||
_mapLoader.TryLoadGrid(mapId, ent.Comp.Path, out _,
|
||||
offset: _transform.GetWorldPosition(gridUid) + offset);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using Content.Shared._DV.Reputation;
|
||||
using Content.Shared.Store;
|
||||
|
||||
namespace Content.Server._DV.Store.Conditions;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that an uplink using <see cref="ContractsComponent"/> has enough reputation.
|
||||
/// This is ignored for nukie uplinks and surplus crates.
|
||||
/// </summary>
|
||||
public sealed partial class ReputationCondition : ListingCondition
|
||||
{
|
||||
/// <summary>
|
||||
/// The required reputation for traitors.
|
||||
/// This is unused for nukie uplinks.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public int Reputation;
|
||||
|
||||
public override bool Condition(ListingConditionArgs args)
|
||||
{
|
||||
var reputation = args.EntityManager.System<ReputationSystem>();
|
||||
if (args.StoreEntity is not {} pda || reputation.GetReputation(pda) is not {} rep)
|
||||
return true; // nukie uplink or a surplus
|
||||
|
||||
return rep >= Reputation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
using Content.Server._DV.Objectives.Systems;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared._DV.Traitor;
|
||||
using Content.Shared.Charges.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Salvage.Fulton;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._DV.Traitor;
|
||||
|
||||
public sealed class ExtractionFultonSystem : SharedExtractionFultonSystem
|
||||
{
|
||||
[Dependency] private readonly ExtractConditionSystem _extractCondition = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly RansomConditionSystem _ransomCondition = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedFultonSystem _fulton = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExtractionFultonComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<ExtractionFultonComponent, ExtractionFultonDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<ExtractionFultonComponent> ent, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || args.Target is not {} target)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
AttachFulton(ent, target, args.User);
|
||||
}
|
||||
|
||||
protected override void AttachFulton(Entity<ExtractionFultonComponent> ent, EntityUid target, EntityUid user)
|
||||
{
|
||||
if (_mind.GetMind(user) is not {} mindId || !TryComp<MindComponent>(mindId, out var mind))
|
||||
return;
|
||||
|
||||
if (HasComp<FultonedComponent>(target))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("fulton-fultoned"), target, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_charges.IsEmpty(ent.Owner))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("emag-no-charges"), ent, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanExtractPopup((mindId, mind), user, target))
|
||||
return;
|
||||
|
||||
if (FindBeacon(ent, target) is not {} beacon)
|
||||
{
|
||||
Log.Error($"No beacon found accepting {ToPrettyString(target)} from {ToPrettyString(ent)}");
|
||||
Popup.PopupEntity(Loc.GetString("extraction-fulton-no-destination"), ent, user);
|
||||
return;
|
||||
}
|
||||
|
||||
var ev = new ExtractionFultonDoAfterEvent(GetNetEntity(beacon));
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, ent.Comp.ApplyDelay, ev, eventTarget: ent, target: target, used: ent)
|
||||
{
|
||||
BreakOnMove = true,
|
||||
NeedHand = true
|
||||
});
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<ExtractionFultonComponent> ent, ref ExtractionFultonDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Target is not {} target || GetEntity(args.Beacon) is not {} beacon)
|
||||
return;
|
||||
|
||||
if (!_charges.TryUseCharge(ent.Owner))
|
||||
return;
|
||||
|
||||
var duration = HasComp<MobStateComponent>(target)
|
||||
? ent.Comp.MobDelay
|
||||
: ent.Comp.ItemDelay;
|
||||
|
||||
// this is checked when extracted to only complete this persons objective
|
||||
EnsureComp<ExtractingComponent>(target).Mind = _mind.GetMind(args.User);
|
||||
|
||||
var comp = AddComp<FultonedComponent>(target);
|
||||
comp.Beacon = beacon;
|
||||
comp.NextFulton = _timing.CurTime + duration;
|
||||
comp.FultonDuration = duration;
|
||||
comp.Removeable = true;
|
||||
_fulton.UpdateAppearance(target, comp);
|
||||
Dirty(target, comp);
|
||||
_audio.PlayPvs(ent.Comp.FultonSound, target);
|
||||
|
||||
// TODO: make mobs beep while fultoned
|
||||
}
|
||||
|
||||
private bool CanExtractPopup(Entity<MindComponent?> mind, EntityUid user, EntityUid target)
|
||||
{
|
||||
if (Transform(target).Anchored)
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("extraction-fulton-anchored"), target, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_extractCondition.FindObjective(mind, target) != null)
|
||||
return true;
|
||||
|
||||
if (_ransomCondition.FindObjective(mind, target) != null)
|
||||
{
|
||||
if (!_mob.IsAlive(target))
|
||||
{
|
||||
Popup.PopupEntity(Loc.GetString("extraction-fulton-dead"), target, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Popup.PopupEntity(Loc.GetString("extraction-fulton-not-target"), target, user);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared._DV.Traitor; // DeltaV
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Cargo.BUI;
|
||||
|
|
@ -10,13 +11,16 @@ public sealed class CargoConsoleInterfaceState : BoundUserInterfaceState
|
|||
public int Capacity;
|
||||
public NetEntity Station;
|
||||
public List<CargoOrderData> Orders;
|
||||
public List<RansomData> Ransoms; // DeltaV
|
||||
|
||||
public CargoConsoleInterfaceState(string name, int count, int capacity, NetEntity station, List<CargoOrderData> orders)
|
||||
// DeltaV - added ransoms
|
||||
public CargoConsoleInterfaceState(string name, int count, int capacity, NetEntity station, List<CargoOrderData> orders, List<RansomData> ransoms)
|
||||
{
|
||||
Name = name;
|
||||
Count = count;
|
||||
Capacity = capacity;
|
||||
Station = station;
|
||||
Orders = orders;
|
||||
Ransoms = ransoms;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,10 +374,20 @@ public abstract partial class SharedMindSystem : EntitySystem
|
|||
return false;
|
||||
|
||||
var objective = mind.Objectives[index];
|
||||
return TryRemoveObjective((mindId, mind), objective); // DeltaV
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV: Remove an objective from this mind, if you already know its uid.
|
||||
/// </summary>
|
||||
public bool TryRemoveObjective(Entity<MindComponent> mind, EntityUid objective)
|
||||
{
|
||||
if (!mind.Comp.Objectives.Remove(objective))
|
||||
return false;
|
||||
|
||||
var title = Name(objective);
|
||||
_adminLogger.Add(LogType.Mind, LogImpact.Low, $"Objective {objective} ({title}) removed from the mind of {MindOwnerLoggingString(mind)}");
|
||||
mind.Objectives.Remove(objective);
|
||||
mind.Comp.Objectives.Remove(objective);
|
||||
|
||||
// garbage collection - only delete the objective entity if no mind uses it anymore
|
||||
// This comes up for stuff like paradox clones where the objectives share the same entity
|
||||
|
|
|
|||
|
|
@ -8,5 +8,9 @@ namespace Content.Shared.Nuke;
|
|||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class NukeDiskComponent : Component
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV: When extracted by a syndie, this makes the disk teleport to any nukies.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Extracted;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared._DV.Reputation; // DeltaV
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Random; // DeltaV
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -13,6 +15,7 @@ public abstract class SharedObjectivesSystem : EntitySystem
|
|||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] private readonly ReputationSystem _reputation = default!; // DeltaV
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
|
|
@ -45,6 +48,16 @@ public abstract class SharedObjectivesSystem : EntitySystem
|
|||
if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto)
|
||||
return false;
|
||||
}
|
||||
// Begin DeltaV Additions - check available contracts too
|
||||
if (_reputation.GetMindContracts(mindId) is {} contracts)
|
||||
{
|
||||
foreach (var objective in contracts.Comp.Offerings)
|
||||
{
|
||||
if (objective is {} obj && _metaQuery.Comp(obj).EntityPrototype?.ID == proto)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// End DeltaV Additions
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -164,4 +177,12 @@ public abstract class SharedObjectivesSystem : EntitySystem
|
|||
|
||||
comp.Icon = icon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Lets code in shared call this
|
||||
/// </summary>
|
||||
public virtual EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId<WeightedRandomPrototype> objectiveGroupProto, float maxDifficulty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,12 @@ namespace Content.Shared.Roles
|
|||
[DataField("alwaysUseSpawner")]
|
||||
public bool AlwaysUseSpawner { get; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV: Multiplies syndicate ransom price by this number.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float RansomModifier = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The "weight" or importance of this job. If this number is large, the job system will assign this job
|
||||
/// before assigning other jobs.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public abstract partial class SharedFultonSystem : EntitySystem
|
|||
|
||||
private void OnFultonContainerInserted(EntityUid uid, FultonedComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (!component.Removeable) return; // DeltaV
|
||||
RemCompDeferred<FultonedComponent>(uid);
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +168,7 @@ public abstract partial class SharedFultonSystem : EntitySystem
|
|||
Dirty(args.NewId, newFulton);
|
||||
}
|
||||
|
||||
protected virtual void UpdateAppearance(EntityUid uid, FultonedComponent fultoned)
|
||||
public virtual void UpdateAppearance(EntityUid uid, FultonedComponent fultoned) // DeltaV - made public
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ public sealed class DCCVars
|
|||
public static readonly CVarDef<bool> EnableBacktoBack =
|
||||
CVarDef.Create("game.disable_preset_test", false, CVar.SERVERONLY);
|
||||
|
||||
/* Chat highlighting */
|
||||
|
||||
/// <summary>
|
||||
/// A string containing a list of newline-separated strings to be highlighted in the chat.
|
||||
/// </summary>
|
||||
|
|
@ -182,6 +184,33 @@ public sealed class DCCVars
|
|||
CVar.CLIENTONLY | CVar.ARCHIVE,
|
||||
"The color in which the highlights will be displayed.");
|
||||
|
||||
/* Traitors */
|
||||
|
||||
/// <summary>
|
||||
/// Base ransom for a non-humanoid mob, like shiva.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> MobRansom =
|
||||
CVarDef.Create("game.ransom.mob_base", 5000f, CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Base ransom for a humanoid.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> HumanoidRansom =
|
||||
CVarDef.Create("game.ransom.humanoid_base", 10000f, CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Ransom modifier for critical mobs.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> RansomCritModifier =
|
||||
CVarDef.Create("game.ransom.critical_modifier", 0.5f, CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Ransom modifier for dead mobs.
|
||||
/// The ransomer will also fail their objective.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> RansomDeadModifier =
|
||||
CVarDef.Create("game.ransom.dead_modifier", 0.2f, CVar.REPLICATED);
|
||||
|
||||
/* Laying down combat */
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Content.Shared._DV.Objectives.Systems;
|
||||
|
||||
public abstract class SharedContractObjectiveSystem : EntitySystem
|
||||
{
|
||||
public virtual string ContractName(EntityUid objective)
|
||||
{
|
||||
return Name(objective);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
using Content.Shared.Random;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// Component added to traitor PDAs to store contract data.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(ReputationSystem))]
|
||||
[AutoGenerateComponentState(true)]
|
||||
public sealed partial class ContractsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How much reputation there is.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public int Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// The mind of the traitor.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Mind;
|
||||
|
||||
/// <summary>
|
||||
/// The current reputation level, updated when it changes.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReputationLevelPrototype? CurrentLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Offering objectives that can be taken in the UI.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntityUid?> Offerings = new();
|
||||
|
||||
/// <summary>
|
||||
/// All slots for offerings.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<OfferingSlot> OfferingSlots = new();
|
||||
|
||||
/// <summary>
|
||||
/// The objectives for each slot.
|
||||
/// Not sent to the client as objective entities are not networked.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntityUid?> Objectives = new();
|
||||
|
||||
/// <summary>
|
||||
/// All slots for contracts.
|
||||
/// Dynamically increased when levelling up.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<ContractSlot> Slots = new();
|
||||
|
||||
/// <summary>
|
||||
/// How long you have to wait before you can get a new contract after the objective is completed or failed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan CompleteDelay = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// How long you have to wait before you can get a new offering after you reject one.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan RejectDelay = TimeSpan.FromMinutes(15);
|
||||
|
||||
/// <summary>
|
||||
/// How long you have to wait before you can get a new offering after you accept one.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan AcceptDelay = TimeSpan.FromMinutes(3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A contract slot which can either have a contract objective, be available for new contracts or be on cooldown.
|
||||
/// </summary>
|
||||
[DataDefinition, Serializable, NetSerializable]
|
||||
public partial record struct ContractSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// The title of the current objective, or null if there is none.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? ObjectiveTitle;
|
||||
|
||||
/// <summary>
|
||||
/// When the slot gets unlocked and a new contract can be taken.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan? NextUnlock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An offering slot which can have an available objective.
|
||||
/// </summary>
|
||||
[DataDefinition, Serializable, NetSerializable]
|
||||
public partial record struct OfferingSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// The title of the available objective, or null if locked.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? Title;
|
||||
|
||||
/// <summary>
|
||||
/// When the slot gets unlocked and a new offering is rolled.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan? NextUnlock;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ContractsUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ContractsState : BoundUserInterfaceState;
|
||||
// TODO
|
||||
|
||||
/// <summary>
|
||||
/// Accept a contract with offerings index.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ContractsAcceptMessage(int index) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly int Index = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete a contract whose objective has been completed, with slot index.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ContractsCompleteMessage(int index) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly int Index = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rejects a contract offering with offerings index.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ContractsRejectMessage(int index) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly int Index = index;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PdaShowContractsMessage : BoundUserInterfaceMessage;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Content.Shared.Mind;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised on an objective after it has been added and taken.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ContractTakenEvent(Entity<ContractsComponent> Pda, Entity<MindComponent> Mind);
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised on an objective after it becomes impossible to completed.
|
||||
/// It gets deleted afterwards.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ContractFailedEvent(Entity<ContractsComponent> Pda);
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets raised on an objective after it has been completed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ContractCompletedEvent(Entity<ContractsComponent> Pda);
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// Stores reputation-related data for mind entities.
|
||||
/// Has a backup reputation value incase their PDA is deleted.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(ReputationSystem))]
|
||||
public sealed partial class MindReputationComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The traitor's PDA with <see cref="ContractsComponent"/>, might not always exist.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Pda;
|
||||
|
||||
[DataField]
|
||||
public int Reputation;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
using Content.Shared.Random;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// Data associated with a reputation level.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class ReputationLevelPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the reputation level to display in UIs.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public LocId Name;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum reputation someone needs to get this.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public int Reputation;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of contracts that can be active at once.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public int MaxContracts;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of offering slots that there can be.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public int MaxOfferings;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum difficulty for objectives that can be rolled.
|
||||
/// <c>ReputationCondition</c> should be used for fine-grained control.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MaxDifficulty = 6f;
|
||||
|
||||
/// <summary>
|
||||
/// Offering groups that can be used.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<WeightedRandomPrototype> OfferingGroups = "ReputationOfferings";
|
||||
}
|
||||
|
|
@ -0,0 +1,516 @@
|
|||
using Content.Shared._DV.Objectives.Systems;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._DV.Reputation;
|
||||
|
||||
public sealed class ReputationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly SharedContractObjectiveSystem _contract = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
|
||||
private List<ReputationLevelPrototype> _levels = new();
|
||||
public IReadOnlyList<ReputationLevelPrototype> AllLevels => _levels;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ContractsComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<ContractsComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ContractsComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ContractsComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<ContractsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
Subs.BuiEvents<ContractsComponent>(ContractsUiKey.Key, subs =>
|
||||
{
|
||||
subs.Event<ContractsAcceptMessage>(OnAcceptMessage);
|
||||
subs.Event<ContractsCompleteMessage>(OnCompleteMessage);
|
||||
subs.Event<ContractsRejectMessage>(OnRejectMessage);
|
||||
});
|
||||
|
||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
||||
|
||||
CacheLevels();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<ContractsComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
PickOfferings((uid, comp));
|
||||
}
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OnInit(Entity<ContractsComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
_ui.SetUi(ent.Owner, ContractsUiKey.Key, new InterfaceData("ContractsBUI"));
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<ContractsComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
// creates the slots for fresh pdas
|
||||
UpdateLevel(ent);
|
||||
PickOfferings(ent);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ContractsComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
// if the PDA is cremated or thrown in a singulo or something,
|
||||
// delete all the offerings and fail the active contracts
|
||||
foreach (var uid in ent.Comp.Offerings)
|
||||
{
|
||||
Del(uid);
|
||||
}
|
||||
|
||||
foreach (var obj in ent.Comp.Objectives)
|
||||
{
|
||||
ContractFailed(ent, obj);
|
||||
}
|
||||
|
||||
// unlink it from the mind
|
||||
if (TryComp<MindReputationComponent>(ent.Comp.Mind, out var mind))
|
||||
mind.Pda = null;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<ContractsComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
for (var i = 0; i < ent.Comp.Slots.Count; i++)
|
||||
{
|
||||
var slot = ent.Comp.Slots[i];
|
||||
slot.NextUnlock += args.PausedTime;
|
||||
ent.Comp.Slots[i] = slot;
|
||||
}
|
||||
|
||||
for (var i = 0; i < ent.Comp.OfferingSlots.Count; i++)
|
||||
{
|
||||
var slot = ent.Comp.OfferingSlots[i];
|
||||
slot.NextUnlock += args.PausedTime;
|
||||
ent.Comp.OfferingSlots[i] = slot;
|
||||
}
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ContractsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
// update CurrentLevel for client after server changes it, so UI can use it
|
||||
UpdateLevel(ent);
|
||||
UpdateUI(ent);
|
||||
}
|
||||
|
||||
private void OnAcceptMessage(Entity<ContractsComponent> ent, ref ContractsAcceptMessage args)
|
||||
{
|
||||
var i = args.Index;
|
||||
if (i < 0 || i >= ent.Comp.Offerings.Count)
|
||||
return;
|
||||
|
||||
if (ent.Comp.Offerings[i] is not {} objective || !TryTakeContract(ent, objective))
|
||||
return;
|
||||
|
||||
ent.Comp.Offerings[i] = null;
|
||||
ent.Comp.OfferingSlots[i] = new OfferingSlot
|
||||
{
|
||||
NextUnlock = _timing.CurTime + ent.Comp.AcceptDelay
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCompleteMessage(Entity<ContractsComponent> ent, ref ContractsCompleteMessage args)
|
||||
{
|
||||
TryCompleteContract(ent, args.Index);
|
||||
}
|
||||
|
||||
private void OnRejectMessage(Entity<ContractsComponent> ent, ref ContractsRejectMessage args)
|
||||
{
|
||||
TryRejectOffering(ent, args.Index);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Add contracts to a traitor's PDA.
|
||||
/// Throws if you call this multiple times on the same mind or pda.
|
||||
/// </summary>
|
||||
public void AddContracts(EntityUid mob, EntityUid pda)
|
||||
{
|
||||
if (_mind.GetMind(mob) is not {} mindId)
|
||||
return;
|
||||
|
||||
// AddComp so it will throw if you are trying to bulldoze a used mind or pda
|
||||
var contracts = AddComp<ContractsComponent>(pda);
|
||||
var mind = AddComp<MindReputationComponent>(mindId);
|
||||
contracts.Mind = mindId;
|
||||
mind.Pda = pda;
|
||||
PickOfferings((pda, contracts));
|
||||
}
|
||||
|
||||
public void ToggleUI(EntityUid user, EntityUid uid)
|
||||
{
|
||||
UpdateUI(uid);
|
||||
_ui.TryToggleUi(uid, ContractsUiKey.Key, user);
|
||||
}
|
||||
|
||||
private void UpdateUI(EntityUid uid)
|
||||
{
|
||||
_ui.SetUiState(uid, ContractsUiKey.Key, new ContractsState());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick new offerings for open offering slots.
|
||||
/// </summary>
|
||||
public void PickOfferings(Entity<ContractsComponent> ent)
|
||||
{
|
||||
if (GetMind(ent) is not {} mind || ent.Comp.CurrentLevel is not {} level)
|
||||
return;
|
||||
|
||||
var difficulty = level.MaxDifficulty;
|
||||
var groups = level.OfferingGroups;
|
||||
for (var i = 0; i < ent.Comp.OfferingSlots.Count; i++)
|
||||
{
|
||||
// can't add a new offering yet
|
||||
if (ent.Comp.Offerings[i] != null || IsLocked(ent.Comp.OfferingSlots[i].NextUnlock))
|
||||
continue;
|
||||
|
||||
if (_objectives.GetRandomObjective(mind, mind, groups, difficulty) is not {} objective)
|
||||
{
|
||||
// prevent spinlock
|
||||
ent.Comp.OfferingSlots[i] = new OfferingSlot
|
||||
{
|
||||
NextUnlock = _timing.CurTime + ent.Comp.AcceptDelay
|
||||
};
|
||||
Dirty(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
ent.Comp.Offerings[i] = objective;
|
||||
ent.Comp.OfferingSlots[i] = new OfferingSlot
|
||||
{
|
||||
Title = _contract.ContractName(objective)
|
||||
};
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to take a new contract by adding an existing objective entity.
|
||||
/// </summary>
|
||||
public bool TryTakeContract(Entity<ContractsComponent> ent, EntityUid objective)
|
||||
{
|
||||
if (GetMind(ent) is not {} mind ||
|
||||
FindOpenSlot(ent) is not {} index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mind.AddObjective(mind, mind, objective);
|
||||
|
||||
ent.Comp.Objectives[index] = objective;
|
||||
var slot = ent.Comp.Slots[index];
|
||||
slot.ObjectiveTitle = _contract.ContractName(objective);
|
||||
ent.Comp.Slots[index] = slot;
|
||||
Dirty(ent);
|
||||
|
||||
var ev = new ContractTakenEvent(ent, mind);
|
||||
RaiseLocalEvent(objective, ref ev);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a contract's objective is complete, pays out etc and removes it.
|
||||
/// </summary>
|
||||
public bool TryCompleteContract(Entity<ContractsComponent> ent, int index)
|
||||
{
|
||||
if (index < 0 ||
|
||||
index >= ent.Comp.Slots.Count ||
|
||||
ent.Comp.Objectives[index] is not {} objective ||
|
||||
GetMind(ent) is not {} mind ||
|
||||
!_objectives.IsCompleted(objective, mind))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ev = new ContractCompletedEvent(ent);
|
||||
RaiseLocalEvent(objective, ref ev);
|
||||
|
||||
ClearSlot(ent, index, ent.Comp.CompleteDelay);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryRejectOffering(Entity<ContractsComponent> ent, int index)
|
||||
{
|
||||
if (index < 0 ||
|
||||
index >= ent.Comp.OfferingSlots.Count ||
|
||||
ent.Comp.Offerings[index] is not {} objective)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ent.Comp.Offerings[index] = null;
|
||||
ent.Comp.OfferingSlots[index] = new OfferingSlot
|
||||
{
|
||||
Title = null,
|
||||
NextUnlock = _timing.CurTime + ent.Comp.RejectDelay
|
||||
};
|
||||
Dirty(ent);
|
||||
Del(objective);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to fail a contract if it becomes impossible to complete.
|
||||
/// E.g. trying to steal an item that gets deleted
|
||||
/// </summary>
|
||||
public bool TryFailContract(Entity<ContractsComponent> ent, EntityUid objective)
|
||||
{
|
||||
if (FindContract(ent, objective) is not {} index)
|
||||
return false;
|
||||
|
||||
ContractFailed(ent, objective);
|
||||
ClearSlot(ent, index, ent.Comp.CompleteDelay);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the mind that belongs to a contracts PDA.
|
||||
/// </summary>
|
||||
public Entity<MindComponent>? GetMind(Entity<ContractsComponent> ent)
|
||||
{
|
||||
if (ent.Comp.Mind is not {} mindId)
|
||||
return null;
|
||||
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
return null;
|
||||
|
||||
return (mindId, mind);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the contracts pda for a mind, if it exists.
|
||||
/// </summary>
|
||||
public Entity<ContractsComponent>? GetMindContracts(EntityUid mindId)
|
||||
{
|
||||
if (CompOrNull<MindReputationComponent>(mindId)?.Pda is not {} pda)
|
||||
return null;
|
||||
|
||||
if (!TryComp<ContractsComponent>(pda, out var comp))
|
||||
return null;
|
||||
|
||||
return (pda, comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reputation for a mind, null if it had no <see cref="ContractsComponent"/>.
|
||||
/// </summary>
|
||||
public int? GetMindReputation(EntityUid mindId)
|
||||
{
|
||||
if (CompOrNull<MindReputationComponent>(mindId)?.Pda is not {} pda)
|
||||
return null;
|
||||
|
||||
return GetReputation(pda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reputation for a PDA, null if it had no <see cref="ContractsComponent"/>.
|
||||
/// </summary>
|
||||
public int? GetReputation(Entity<ContractsComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return null;
|
||||
|
||||
return ent.Comp.Reputation;
|
||||
}
|
||||
|
||||
public bool GiveMindReputation(EntityUid mindId, int amount)
|
||||
{
|
||||
return amount != 0 &&
|
||||
GetMindContracts(mindId) is {} contracts &&
|
||||
GiveReputation(contracts, amount);
|
||||
}
|
||||
|
||||
public bool GiveReputation(Entity<ContractsComponent> ent, int amount)
|
||||
{
|
||||
if (amount == 0)
|
||||
return false;
|
||||
|
||||
ent.Comp.Reputation = Math.Clamp(ent.Comp.Reputation + amount, 0, 100);
|
||||
Dirty(ent);
|
||||
if (TryComp<MindReputationComponent>(ent.Comp.Mind, out var mind))
|
||||
mind.Reputation = ent.Comp.Reputation;
|
||||
UpdateLevel(ent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level prototype for a given reputation.
|
||||
/// </summary>
|
||||
public ReputationLevelPrototype? GetLevel(int rep)
|
||||
{
|
||||
foreach (var proto in _levels)
|
||||
{
|
||||
if (rep >= proto.Reputation)
|
||||
return proto;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private bool IsLocked(TimeSpan? nextUnlock)
|
||||
{
|
||||
return nextUnlock is {} unlock && _timing.CurTime < unlock;
|
||||
}
|
||||
|
||||
private int? FindOpenSlot(Entity<ContractsComponent> ent)
|
||||
{
|
||||
for (var i = 0; i < ent.Comp.Slots.Count; i++)
|
||||
{
|
||||
if (ent.Comp.Objectives[i] != null)
|
||||
continue;
|
||||
|
||||
if (IsLocked(ent.Comp.Slots[i].NextUnlock))
|
||||
continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int? FindContract(Entity<ContractsComponent> ent, EntityUid objective)
|
||||
{
|
||||
for (var i = 0; i < ent.Comp.Slots.Count; i++)
|
||||
{
|
||||
if (ent.Comp.Objectives[i] == objective)
|
||||
return i;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ClearSlot(Entity<ContractsComponent> ent, int index, TimeSpan delay)
|
||||
{
|
||||
// old objective is intentionally not deleted, objective stays in the character menu for your greentextful glory / redtextful shame
|
||||
ent.Comp.Objectives[index] = null;
|
||||
ent.Comp.Slots[index] = new ContractSlot()
|
||||
{
|
||||
NextUnlock = _timing.CurTime + delay
|
||||
};
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void UpdateLevel(Entity<ContractsComponent> ent)
|
||||
{
|
||||
var old = ent.Comp.CurrentLevel;
|
||||
ent.Comp.CurrentLevel = GetLevel(ent.Comp.Reputation);
|
||||
UpdateContractSlots(ent);
|
||||
UpdateOfferingSlots(ent);
|
||||
}
|
||||
|
||||
private void UpdateContractSlots(Entity<ContractsComponent> ent)
|
||||
{
|
||||
var oldSlots = ent.Comp.Slots.Count;
|
||||
var newSlots = ent.Comp.CurrentLevel?.MaxContracts ?? 0;
|
||||
if (oldSlots == newSlots)
|
||||
return;
|
||||
|
||||
if (newSlots > oldSlots)
|
||||
{
|
||||
// levelling up, add new slot(s)
|
||||
for (var i = oldSlots; i < newSlots; i++)
|
||||
{
|
||||
ent.Comp.Objectives.Add(null);
|
||||
ent.Comp.Slots.Add(new ContractSlot());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this should never happen but removing objectives just incase
|
||||
for (var i = newSlots; i > oldSlots; i--)
|
||||
{
|
||||
var j = i - 1;
|
||||
var objective = ent.Comp.Objectives[j];
|
||||
ContractFailed(ent, objective);
|
||||
ent.Comp.Objectives.RemoveAt(j);
|
||||
ent.Comp.Slots.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void UpdateOfferingSlots(Entity<ContractsComponent> ent)
|
||||
{
|
||||
var oldSlots = ent.Comp.OfferingSlots.Count;
|
||||
var newSlots = ent.Comp.CurrentLevel?.MaxOfferings ?? 0;
|
||||
if (oldSlots == newSlots)
|
||||
return;
|
||||
|
||||
if (newSlots > oldSlots)
|
||||
{
|
||||
// levelling up, add new slot(s)
|
||||
for (var i = oldSlots; i < newSlots; i++)
|
||||
{
|
||||
ent.Comp.Offerings.Add(null);
|
||||
ent.Comp.OfferingSlots.Add(new OfferingSlot());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this should never happen but removing objectives just incase
|
||||
for (var i = newSlots; i > oldSlots; i--)
|
||||
{
|
||||
var j = i - 1;
|
||||
var objective = ent.Comp.Offerings[j];
|
||||
Del(objective);
|
||||
ent.Comp.Offerings.RemoveAt(j);
|
||||
ent.Comp.OfferingSlots.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void ContractFailed(Entity<ContractsComponent> ent, EntityUid? uid)
|
||||
{
|
||||
if (GetMind(ent) is not {} mind)
|
||||
return;
|
||||
|
||||
if (uid is not {} objective)
|
||||
return;
|
||||
|
||||
var ev = new ContractFailedEvent(ent);
|
||||
RaiseLocalEvent(objective, ref ev);
|
||||
_mind.TryRemoveObjective(mind, objective);
|
||||
}
|
||||
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
|
||||
{
|
||||
if (!args.WasModified<ReputationLevelPrototype>())
|
||||
return;
|
||||
|
||||
CacheLevels();
|
||||
}
|
||||
|
||||
private void CacheLevels()
|
||||
{
|
||||
_levels.Clear();
|
||||
foreach (var proto in _proto.EnumeratePrototypes<ReputationLevelPrototype>())
|
||||
{
|
||||
_levels.Add(proto);
|
||||
}
|
||||
// sort levels by their reputation requirement, descending
|
||||
// this allows GetLevel to work
|
||||
_levels.Sort((a, b) => (b.Reputation.CompareTo(a.Reputation)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
/// <summary>
|
||||
/// Added to an entity being extracted with a syndie fulton.
|
||||
/// Used to control whos objectives get completed.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SharedExtractionFultonSystem))]
|
||||
public sealed partial class ExtractingComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Mind of the player that extracted it.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Mind;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
/// <summary>
|
||||
/// A marker that extraction beacons can teleport entities to.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedExtractionFultonSystem))]
|
||||
public sealed partial class ExtractionBeaconComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// If defined, entities must match this whitelist to get teleported here.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
/// <summary>
|
||||
/// If defined, entities cannot match this blacklist to get teleported here.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
/// <summary>
|
||||
/// Fulton that can be used for traitor extraction and ransom objectives.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedExtractionFultonSystem))]
|
||||
public sealed partial class ExtractionFultonComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan ApplyDelay = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes for a stolen item to get sent to the vault.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan ItemDelay = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes for a mob to get sent to jail.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan MobDelay = TimeSpan.FromSeconds(60);
|
||||
|
||||
/// <summary>
|
||||
/// Sound that gets played when the fulton is applied.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? FultonSound = new SoundPathSpecifier("/Audio/Items/Mining/fultext_deploy.ogg");
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
/// <summary>
|
||||
/// This entity is being held ransom and can be purchased to teleport to the ATS.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(RansomSystem))]
|
||||
public sealed partial class RansomComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public int Ransom;
|
||||
|
||||
/// <summary>
|
||||
/// The map the entity is being held on.
|
||||
/// Ransom is ended if the entity leaves this map for any reason.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public MapId Map = MapId.Nullspace;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ransom data for an entity visible on a cargo request console.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public readonly record struct RansomData(NetEntity Entity, string Name, int Price);
|
||||
|
||||
/// <summary>
|
||||
/// BUI message for a cargo request console to purchase a ransomed entity.
|
||||
/// It gets teleported to the ATS if successful.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RansomPurchaseMessage(NetEntity entity) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly NetEntity Entity = entity;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
using Content.Shared._DV.CCVars;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
/// <summary>
|
||||
/// Provides API for ransoming entities.
|
||||
/// </summary>
|
||||
public sealed class RansomSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||
[Dependency] private readonly SharedJobSystem _job = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
|
||||
private float _mobBase;
|
||||
private float _humanoidBase;
|
||||
private float _deadMod;
|
||||
private float _critMod;
|
||||
private List<RansomData> _ransoms = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RansomComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<RansomComponent, MoveEvent>(OnMove);
|
||||
|
||||
Subs.CVar(_cfg, DCCVars.MobRansom, n => _mobBase = n, true);
|
||||
Subs.CVar(_cfg, DCCVars.HumanoidRansom, n => _humanoidBase = n, true);
|
||||
Subs.CVar(_cfg, DCCVars.RansomDeadModifier, n => _deadMod = n, true);
|
||||
Subs.CVar(_cfg, DCCVars.RansomCritModifier, n => _critMod = n, true);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<RansomComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.Map = Transform(ent).MapID;
|
||||
}
|
||||
|
||||
private void OnMove(Entity<RansomComponent> ent, ref MoveEvent args)
|
||||
{
|
||||
// remove ransom when its paid, or if they sneak a fulton/whatever into the jail, or get admin help
|
||||
if (Transform(ent).MapID != ent.Comp.Map)
|
||||
RemCompDeferred<RansomComponent>(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ransoms an entity and returns the price.
|
||||
/// </summary>
|
||||
public int RansomEntity(EntityUid uid)
|
||||
{
|
||||
var ransom = GetRansom(uid);
|
||||
EnsureComp<RansomComponent>(uid).Ransom = ransom;
|
||||
return ransom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the price for an entity's ransom.
|
||||
/// </summary>
|
||||
public int GetRansom(EntityUid uid)
|
||||
{
|
||||
var ransom = HasComp<HumanoidAppearanceComponent>(uid)
|
||||
? _humanoidBase
|
||||
: _mobBase;
|
||||
|
||||
// hostages are more valuable alive than dead, shocker
|
||||
if (_mob.IsDead(uid))
|
||||
ransom *= _deadMod;
|
||||
else if (_mob.IsCritical(uid))
|
||||
ransom *= _critMod;
|
||||
|
||||
// multiply by the job's ransom value
|
||||
if (_mind.GetMind(uid) is {} mind && _job.MindTryGetJob(mind, out var job))
|
||||
ransom *= job.RansomModifier;
|
||||
|
||||
return (int) ransom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all ransomed mobs for sending to cargo request console UI.
|
||||
/// It is reused, do not modify it.
|
||||
/// </summary>
|
||||
public List<RansomData> GetRansoms()
|
||||
{
|
||||
_ransoms.Clear();
|
||||
var query = EntityQueryEnumerator<RansomComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
var ent = GetNetEntity(uid);
|
||||
var name = Name(uid);
|
||||
var price = comp.Ransom;
|
||||
_ransoms.Add(new RansomData(ent, name, price));
|
||||
}
|
||||
return _ransoms;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Salvage.Fulton;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Traitor;
|
||||
|
||||
public abstract class SharedExtractionFultonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExtractionFultonComponent, GetVerbsEvent<UtilityVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<FultonedComponent, ContainerGettingInsertedAttemptEvent>(OnInsertAttempt);
|
||||
}
|
||||
|
||||
private void OnGetVerbs(Entity<ExtractionFultonComponent> ent, ref GetVerbsEvent<UtilityVerb> args)
|
||||
{
|
||||
var target = args.Target;
|
||||
var user = args.User;
|
||||
args.Verbs.Add(new UtilityVerb()
|
||||
{
|
||||
Act = () => AttachFulton(ent, target, user),
|
||||
Text = Loc.GetString("extraction-fulton-verb-text"),
|
||||
Disabled = FindBeacon(ent, target) != null
|
||||
});
|
||||
}
|
||||
|
||||
private void OnInsertAttempt(Entity<FultonedComponent> ent, ref ContainerGettingInsertedAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
if (_net.IsServer)
|
||||
Popup.PopupEntity(Loc.GetString("extraction-fulton-remove-first"), ent);
|
||||
}
|
||||
|
||||
protected virtual void AttachFulton(Entity<ExtractionFultonComponent> ent, EntityUid target, EntityUid user)
|
||||
{
|
||||
}
|
||||
|
||||
public EntityUid? FindBeacon(Entity<ExtractionFultonComponent> ent, EntityUid target)
|
||||
{
|
||||
// TODO: whitelist for the fulton to support non-traitor uses
|
||||
var query = EntityQueryEnumerator<ExtractionBeaconComponent>();
|
||||
while (query.MoveNext(out var uid, out var beacon))
|
||||
{
|
||||
if (ValidTarget(beacon, target))
|
||||
return uid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether an extraction beacon can accept a given target entity.
|
||||
/// </summary>
|
||||
public bool ValidTarget(ExtractionBeaconComponent comp, EntityUid uid)
|
||||
{
|
||||
return _whitelist.IsWhitelistPassOrNull(comp.Whitelist, uid)
|
||||
&& !_whitelist.IsBlacklistPass(comp.Blacklist, uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity after it has been fultoned to somewhere.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct FultonedEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ExtractionFultonDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
[DataField]
|
||||
public NetEntity? Beacon;
|
||||
|
||||
public ExtractionFultonDoAfterEvent(NetEntity? beacon = null)
|
||||
{
|
||||
Beacon = beacon;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
ghost-role-information-syndicate-marshal-name = Syndicate Marshal
|
||||
ghost-role-information-syndicate-marshal-description = Run the syndicate prison and keep the hostages in line.
|
||||
ghost-role-information-syndicate-marshal-rules =
|
||||
You are a [color=green][bold]Non-antagonist[/bold][/color] working for the Syndicate.
|
||||
Don't abuse prisoners, they are hostages being kept for ransom!
|
||||
You can /ghost if you get bored, the role will stay available for others to take.
|
||||
All normal rules apply unless an administrator tells you otherwise.
|
||||
|
|
@ -32,3 +32,10 @@ guide-entry-glimmer-creatures = Glimmer Creatures
|
|||
guide-entry-trade-station = Trade Station
|
||||
|
||||
guide-entry-frequently-used-chemicals = Frequently Used Chemicals
|
||||
|
||||
guide-entry-contracts = Syndicate Contracts
|
||||
guide-entry-assisting = Assisting Traitors
|
||||
guide-entry-extraction = Extraction
|
||||
guide-entry-ransom = Ransom
|
||||
guide-entry-murder = Murder
|
||||
guide-entry-special-objectives = Special Objectives
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
nuke-disk-teleported = {CAPITALIZE(THE($disk))} materializes into your hands!
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
objective-condition-assist-traitor-title = Assist fellow traitor {$targetName}, {CAPITALIZE($job)}
|
||||
objective-condition-assist-traitor-description = Help your fellow traitor to complete this contract: {$contract}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# Traitor
|
||||
steal-target-groups-plutonium-core = plutonium core
|
||||
steal-target-groups-lucky-bill = logistics officer's lucky bill
|
||||
steal-target-groups-ian-dossier = head of personnel's photobook
|
||||
|
|
@ -7,7 +8,18 @@ steal-target-groups-notary-stamp = notary stamp
|
|||
steal-target-groups-silvia = silvia
|
||||
steal-target-groups-box-folder-rd-clipboard = research digi-board
|
||||
steal-target-groups-bible-mystagogue = book of mysteries
|
||||
steal-target-groups-rcd = RCD
|
||||
steal-target-groups-research-computer-circuitboard = R&D computer board
|
||||
steal-target-groups-cargo-request-computer-circuitboard = cargo request computer board
|
||||
steal-target-groups-cargo-bounty-computer-circuitboard = cargo bounty computer board
|
||||
steal-target-groups-criminal-records-computer-circuitboard = criminal records computer board
|
||||
steal-target-groups-id-computer-circuitboard = ID computer board
|
||||
steal-target-groups-comms-computer-circuitboard = comms computer board
|
||||
|
||||
# Recruiter
|
||||
steal-target-groups-recruiter-pen = recruiter's pen
|
||||
|
||||
# Ninja
|
||||
steal-target-groups-captains-cloak = captain's cloak
|
||||
steal-target-groups-engineering-techfab-circuitboard = engineering techfab's circuitboard
|
||||
steal-target-groups-logistics-techfab-circuitboard = logistics techfab's circuitboard
|
||||
|
|
|
|||
|
|
@ -1 +1,7 @@
|
|||
objective-condition-extract-title-no-owner = Extract the {$itemName}
|
||||
objective-condition-extract-title-alive-no-owner = Extract {$itemName}
|
||||
objective-condition-extract-title = Extract the {$owner}'s {$itemName}
|
||||
objective-condition-extract-description = We need you to extract {$itemName} using syndicate fultons from your uplink.
|
||||
|
||||
objective-condition-steal-nuclear-bomb = nuclear bomb
|
||||
objective-condition-steal-engineering = engineering department
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
objective-condition-teach-person-title = Teach {$targetName}, {CAPITALIZE($job)} a lesson
|
||||
objective-condition-make-example-title = Make an example of {$targetName}, {CAPITALIZE($job)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
extraction-fulton-not-target = You don't need to extract this.
|
||||
extraction-fulton-dead = They need to be alive to be extracted!
|
||||
extraction-fulton-no-destination = No destination found!
|
||||
extraction-fulton-anchored = It needs to be unanchored first.
|
||||
extraction-fulton-verb-text = Attach Fulton
|
||||
extraction-fulton-remove-first = Remove the fulton first!
|
||||
|
||||
syndicate-ransom-announcement-sender = Automated Systems
|
||||
syndicate-ransom-announcement =
|
||||
Transmissions received from the Syndicate. They have taken {$hostage} as a ransomed hostage.
|
||||
They are requesting {$ransom} spesos. Entry automatically added to the Cargo Request Computer.
|
||||
Threat of taking too much time: HIGH.
|
||||
|
||||
syndicate-ransom-return-announcement-sender = Automated Trade Station
|
||||
syndicate-ransom-return-announcement = Unauthorized delivery received at [{$station}] from [UNKNOWN SENDER]. Crew are to inspect it and may claim it if desired.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
objective-condition-ransom-title = Ransom {$targetName}, {$job}
|
||||
|
||||
ransom-ui-warning = [color=red][bold]WARNING: Ransomed crewmembers detected![/bold][/color]
|
||||
ransom-ui-purchase = Buy back {$name}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
reputation-level-unknown-agent = Unknown Agent
|
||||
reputation-level-preferred-pawn = Preferred Pawn
|
||||
reputation-level-reputable-insider = Reputable Insider
|
||||
reputation-level-reliable-contact = Reliable Contact
|
||||
reputation-level-syndicate-operative = Syndicate Operative
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
pda-bound-user-interface-contracts-title = Contract Hub
|
||||
pda-bound-user-interface-contracts-description = Complete syndicate contracts to earn the big bucks.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# UI
|
||||
contracts-menu-title = Contract Hub
|
||||
contracts-contracts = Active Contracts
|
||||
contracts-offerings = Available
|
||||
contracts-rescan = Rescan
|
||||
contracts-complete = Complete
|
||||
contracts-accept = Accept
|
||||
contracts-reject = Reject
|
||||
|
||||
contract-slot-empty = <no contract active>
|
||||
contract-unavailable = <no contract available>
|
||||
contract-next-unlock = Locked for {$time}
|
||||
|
|
@ -15,3 +15,6 @@ uplink-syndicate-hostage-implanter-bundle-desc = These implants pacify when inje
|
|||
|
||||
uplink-objective-syndicate-board-name = Syndicate law board
|
||||
uplink-objective-syndicate-board-desc = Its expensive, don't lose it!
|
||||
|
||||
uplink-syndie-fulton-name = Syndicate Fultons
|
||||
uplink-syndie-fulton-desc = Proprietary Waffle Corp fultons that send high value items to the Syndicate Vault for processing, or send sophonts to the Syndicate Jail for ransom.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,6 +13,9 @@
|
|||
Telecrystal: 4 # DeltaV - was 3
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Sidearm reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkRevolverPython
|
||||
|
|
@ -26,6 +29,9 @@
|
|||
Telecrystal: 6 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Sidearm reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
# Inbuilt suppressor so it's sneaky + more expensive.
|
||||
- type: listing
|
||||
|
|
@ -40,6 +46,9 @@
|
|||
Telecrystal: 6 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Sidearm reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 45
|
||||
|
||||
# Poor accuracy, slow to fire, cheap option
|
||||
- type: listing
|
||||
|
|
@ -51,6 +60,9 @@
|
|||
Telecrystal: 1
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Long arm reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkEsword
|
||||
|
|
@ -65,6 +77,9 @@
|
|||
Telecrystal: 8
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Melee weapon reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 30
|
||||
|
||||
- type: listing
|
||||
id: UplinkEnergyDagger
|
||||
|
|
@ -79,6 +94,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Melee weapon reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkThrowingKnivesKit
|
||||
|
|
@ -93,6 +111,9 @@
|
|||
Telecrystal: 4 # DeltaV - was 6
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Melee weapons reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkGlovesNorthStar
|
||||
|
|
@ -106,6 +127,9 @@
|
|||
Telecrystal: 8
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Melee weapons reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 30
|
||||
|
||||
- type: listing
|
||||
id: UplinkDisposableTurret
|
||||
|
|
@ -124,6 +148,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Long arms(???) reputation
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkEshield
|
||||
|
|
@ -143,6 +169,8 @@
|
|||
whitelist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Sidearms(?????) reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkSniperBundle
|
||||
|
|
@ -152,11 +180,14 @@
|
|||
productEntity: BriefcaseSyndieSniperBundleFilled
|
||||
discountCategory: usualDiscounts
|
||||
discountDownTo:
|
||||
Telecrystal: 5 # DeltaV - was 6
|
||||
Telecrystal: 6
|
||||
cost:
|
||||
Telecrystal: 10 # DeltaV - was 12 changed because the Hristov is REALLY bad
|
||||
Telecrystal: 12
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Long Arms reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkC20RBundle
|
||||
|
|
@ -171,6 +202,9 @@
|
|||
Telecrystal: 20 # DeltaV - was 17
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Long Arms reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkBulldogBundle
|
||||
|
|
@ -185,6 +219,9 @@
|
|||
Telecrystal: 22 # DeltaV - was 20
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - Long Arms reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkGrenadeLauncherBundle
|
||||
|
|
@ -199,6 +236,9 @@
|
|||
Telecrystal: 40 # DeltaV - Was 25
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - High Power Weapons reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 95
|
||||
|
||||
- type: listing
|
||||
id: UplinkL6SawBundle
|
||||
|
|
@ -213,6 +253,9 @@
|
|||
Telecrystal: 28 # DeltaV - Was 12
|
||||
categories:
|
||||
- UplinkWeaponry
|
||||
conditions: # DeltaV - High Power Weapons reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 80
|
||||
|
||||
# Explosives
|
||||
|
||||
|
|
@ -228,6 +271,9 @@
|
|||
Telecrystal: 2 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkExplosiveGrenadeFlash
|
||||
|
|
@ -261,6 +307,9 @@
|
|||
Telecrystal: 6
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 50
|
||||
|
||||
- type: listing
|
||||
id: UplinkSingularityGrenade
|
||||
|
|
@ -274,6 +323,9 @@
|
|||
Telecrystal: 6 # DeltaV - was 2, this is explosive now
|
||||
categories:
|
||||
- UplinkExplosives # DeltaV - this is explosive here
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 55
|
||||
|
||||
- type: listing
|
||||
id: UplinkWhiteholeGrenade
|
||||
|
|
@ -287,6 +339,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkGrenadePenguin
|
||||
|
|
@ -305,6 +360,8 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition # DeltaV - Explosives reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkC4
|
||||
|
|
@ -318,6 +375,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkGrenadierRig
|
||||
|
|
@ -349,6 +409,9 @@
|
|||
Telecrystal: 12 #you're buying bulk so its a 25% discount, so no additional random discount over it
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - More Powerful Explosions reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 65
|
||||
|
||||
- type: listing
|
||||
id: UplinkEmpGrenade
|
||||
|
|
@ -362,6 +425,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkExplodingPen
|
||||
|
|
@ -376,6 +442,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkSyndicateBomb
|
||||
|
|
@ -392,6 +461,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - More Powerful Explosions reputation
|
||||
reputation: 75
|
||||
|
||||
- type: listing
|
||||
id: UplinkSyndicateBombNukie
|
||||
|
|
@ -420,6 +491,9 @@
|
|||
Telecrystal: 6 # DeltaV - was 8
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - More Powerful Explosions reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkGrenadeShrapnel
|
||||
|
|
@ -433,6 +507,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 45
|
||||
|
||||
- type: listing
|
||||
id: UplinkGrenadeIncendiary
|
||||
|
|
@ -446,6 +523,9 @@
|
|||
Telecrystal: 4
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 45
|
||||
|
||||
- type: listing
|
||||
id: UplinkEmpKit
|
||||
|
|
@ -459,6 +539,9 @@
|
|||
Telecrystal: 5 # DeltaV - was 6
|
||||
categories:
|
||||
- UplinkExplosives
|
||||
conditions: # DeltaV - Explosives reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 50
|
||||
|
||||
# Ammo
|
||||
|
||||
|
|
@ -565,6 +648,9 @@
|
|||
Telecrystal: 8 # DeltaV - was 6
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 25
|
||||
|
||||
- type: listing
|
||||
id: UplinkHypoDart
|
||||
|
|
@ -579,6 +665,9 @@
|
|||
Telecrystal: 1 # DeltaV - was 2
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkChemistryKitBundle
|
||||
|
|
@ -593,6 +682,9 @@
|
|||
Telecrystal: 4
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkZombieBundle
|
||||
|
|
@ -626,6 +718,9 @@
|
|||
Telecrystal: 6
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 25
|
||||
|
||||
- type: listing
|
||||
id: UplinkCombatMedkit
|
||||
|
|
@ -639,6 +734,9 @@
|
|||
Telecrystal: 5
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 30
|
||||
|
||||
- type: listing
|
||||
id: UplinkCombatMedipen
|
||||
|
|
@ -652,6 +750,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 25
|
||||
|
||||
- type: listing
|
||||
id: UplinkStimpack
|
||||
|
|
@ -665,6 +766,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkStimkit
|
||||
|
|
@ -678,6 +782,9 @@
|
|||
Telecrystal: 10 # DeltaV - was 12
|
||||
categories:
|
||||
- UplinkChemicals
|
||||
conditions: # DeltaV - Chemicals reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 35
|
||||
|
||||
- type: listing
|
||||
id: UplinkCigarettes
|
||||
|
|
@ -752,7 +859,7 @@
|
|||
categories:
|
||||
- UplinkDeception
|
||||
|
||||
# Delta-V: replaced by syndicate radio implant
|
||||
# DeltaV - replaced by syndicate radio implant
|
||||
#- type: listing
|
||||
# id: UplinkHeadsetEncryptionKey
|
||||
# name: uplink-encryption-key-name
|
||||
|
|
@ -784,6 +891,9 @@
|
|||
Telecrystal: 1
|
||||
categories:
|
||||
- UplinkDeception
|
||||
conditions: # DeltaV - Melee Weapons reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkDecoyDisk
|
||||
|
|
@ -820,6 +930,9 @@
|
|||
Telecrystal: 4
|
||||
categories:
|
||||
- UplinkDeception
|
||||
conditions: # DeltaV - Money is expensive? no bribing fresh out of cryo
|
||||
- !type:ReputationCondition
|
||||
reputation: 15
|
||||
|
||||
# - type: listing
|
||||
# id: UplinkGigacancerScanner
|
||||
|
|
@ -857,6 +970,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkDeception
|
||||
conditions: # DeltaV - Explosives, to not have fake syndie bombs 5 minutes into the round
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
# Disruption
|
||||
|
||||
|
|
@ -886,6 +1002,9 @@
|
|||
Telecrystal: 4 # DeltaV - was 5
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - it's iconic...
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkRadioJammer
|
||||
|
|
@ -912,6 +1031,9 @@
|
|||
Telecrystal: 5
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - Side Arms reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkSyndicateMartyrModule
|
||||
|
|
@ -926,6 +1048,9 @@
|
|||
Telecrystal: 4
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - More Powerful Explosions reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 60
|
||||
|
||||
- type: listing
|
||||
id: UplinkSoapSyndie
|
||||
|
|
@ -949,6 +1074,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - do 1 task before you get super soap
|
||||
- !type:ReputationCondition
|
||||
reputation: 5
|
||||
|
||||
- type: listing
|
||||
id: UplinkToolbox
|
||||
|
|
@ -1007,6 +1135,8 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition # DeltaV - Explosives reputation
|
||||
reputation: 50
|
||||
|
||||
- type: listing
|
||||
id: UplinkAntimovCircuitBoard
|
||||
|
|
@ -1025,6 +1155,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Station Destructive Items reputation
|
||||
reputation: 80
|
||||
|
||||
- type: listing
|
||||
id: UplinkNukieAntimovCircuitBoard
|
||||
|
|
@ -1089,20 +1221,14 @@
|
|||
productEntity: SingularityBeacon
|
||||
discountCategory: usualDiscounts
|
||||
discountDownTo:
|
||||
Telecrystal: 4
|
||||
Telecrystal: 10 # DeltaV - was 4
|
||||
cost:
|
||||
Telecrystal: 12
|
||||
Telecrystal: 16 # DeltaV - was 12
|
||||
categories:
|
||||
- UplinkDisruption
|
||||
conditions: # DeltaV - Blacklists from traitor uplink and removes from surplus bundle
|
||||
- !type:StoreWhitelistCondition
|
||||
whitelist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:BuyerWhitelistCondition
|
||||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
conditions: # DeltaV - Station Destructive Items reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 85
|
||||
|
||||
# DeltaV: disabled in favour of observation kit
|
||||
#- type: listing
|
||||
|
|
@ -1135,6 +1261,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Allies reputation
|
||||
reputation: 50
|
||||
|
||||
- type: listing
|
||||
id: UplinkReinforcementRadioSyndicate
|
||||
|
|
@ -1154,6 +1282,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Allies reputation
|
||||
reputation: 45
|
||||
|
||||
- type: listing
|
||||
id: UplinkReinforcementRadioSyndicateNukeops # Version for Nukeops that spawns another nuclear operative without the uplink.
|
||||
|
|
@ -1205,6 +1335,8 @@
|
|||
blacklist:
|
||||
tags:
|
||||
- NukeOpsUplink
|
||||
- !type:ReputationCondition # DeltaV - Allies reputation but its a monkey so less
|
||||
reputation: 30
|
||||
|
||||
- type: listing
|
||||
id: UplinkReinforcementRadioSyndicateAncestorNukeops # Version for Nukeops that spawns a syndicate monkey with the NukeOperative component.
|
||||
|
|
@ -1256,6 +1388,9 @@
|
|||
# Telecrystal: 6
|
||||
# categories:
|
||||
# - UplinkAllies
|
||||
# conditions: # DeltaV - Allies reputation
|
||||
# - !type:ReputationCondition
|
||||
# reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkSyndicatePersonalAI
|
||||
|
|
@ -1336,6 +1471,9 @@
|
|||
Telecrystal: 5
|
||||
categories:
|
||||
- UplinkImplants
|
||||
conditions: # DeltaV - You don't need this for stealing shoes...
|
||||
- !type:ReputationCondition
|
||||
reputation: 25
|
||||
|
||||
- type: listing
|
||||
id: UplinkEmpImplanter
|
||||
|
|
@ -1350,6 +1488,9 @@
|
|||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkImplants
|
||||
conditions: # DeltaV - Melee Weapons reputation, its melee range
|
||||
- !type:ReputationCondition
|
||||
reputation: 25
|
||||
|
||||
- type: listing
|
||||
id: UplinkMicroBombImplanter
|
||||
|
|
@ -1401,8 +1542,10 @@
|
|||
Telecrystal: 3 # DeltaV- Was 4
|
||||
categories:
|
||||
- UplinkImplants
|
||||
# conditions: # DeltaV- Allows Death Acidifer for our Syndibros
|
||||
# - !type:StoreWhitelistCondition
|
||||
conditions:
|
||||
- !type:ReputationCondition # DeltaV - Chemicals reputation
|
||||
reputation: 20
|
||||
# - !type:StoreWhitelistCondition # DeltaV - Allows Death Acidifer for our Syndibros
|
||||
# whitelist:
|
||||
# tags:
|
||||
# - NukeOpsUplink
|
||||
|
|
@ -1548,6 +1691,9 @@
|
|||
Telecrystal: 7 # DeltaV - Was 4, 4TC was too cheap for the items power
|
||||
categories:
|
||||
- UplinkWearables
|
||||
conditions: # DeltaV - They're very strong, shouldn't have it roundstart
|
||||
- !type:ReputationCondition
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkClothingOuterVestWeb
|
||||
|
|
@ -1561,6 +1707,9 @@
|
|||
Telecrystal: 3
|
||||
categories:
|
||||
- UplinkWearables
|
||||
conditions: # DeltaV - Armour
|
||||
- !type:ReputationCondition
|
||||
reputation: 15
|
||||
|
||||
- type: listing
|
||||
id: UplinkClothingOuterVestWebElite
|
||||
|
|
@ -1602,6 +1751,9 @@
|
|||
Telecrystal: 1 # DeltaV - was 2
|
||||
categories:
|
||||
- UplinkWearables
|
||||
conditions: # DeltaV - Evil EVA suit
|
||||
- !type:ReputationCondition
|
||||
reputation: 10
|
||||
|
||||
- type: listing
|
||||
id: UplinkHardsuitCarp
|
||||
|
|
@ -1616,6 +1768,9 @@
|
|||
Telecrystal: 3 # DeltaV - was 4
|
||||
categories:
|
||||
- UplinkWearables
|
||||
conditions: # DeltaV - Funny EVA suit
|
||||
- !type:ReputationCondition
|
||||
reputation: 15
|
||||
|
||||
- type: listing
|
||||
id: UplinkHardsuitSyndie
|
||||
|
|
@ -1630,6 +1785,9 @@
|
|||
Telecrystal: 8
|
||||
categories:
|
||||
- UplinkWearables
|
||||
conditions: # DeltaV - Hardsuits reputation
|
||||
- !type:ReputationCondition
|
||||
reputation: 65
|
||||
|
||||
- type: listing
|
||||
id: UplinkClothingOuterArmorRaid
|
||||
|
|
@ -1767,15 +1925,18 @@
|
|||
categories:
|
||||
- UplinkPointless
|
||||
|
||||
#- type: listing # DeltaV - Remove cat ears
|
||||
# id: UplinkCatEars
|
||||
# name: uplink-cat-ears-name
|
||||
# description: uplink-cat-ears-desc
|
||||
# productEntity: ClothingHeadHatCatEars
|
||||
# cost:
|
||||
# Telecrystal: 26
|
||||
# categories:
|
||||
# - UplinkPointless
|
||||
- type: listing
|
||||
id: UplinkCatEars
|
||||
name: uplink-cat-ears-name
|
||||
description: uplink-cat-ears-desc
|
||||
productEntity: ClothingHeadHatCatEars
|
||||
cost:
|
||||
Telecrystal: 26
|
||||
categories:
|
||||
- UplinkPointless
|
||||
conditions: # DeltaV - Station Destructive Items...
|
||||
- !type:ReputationCondition
|
||||
reputation: 100
|
||||
|
||||
- type: listing
|
||||
id: UplinkOutlawHat
|
||||
|
|
@ -1852,6 +2013,9 @@
|
|||
Telecrystal: 1 # DeltaV - was 20
|
||||
categories:
|
||||
- UplinkPointless
|
||||
conditions: # DeltaV
|
||||
- !type:ReputationCondition
|
||||
reputation: 50
|
||||
|
||||
- type: listing
|
||||
id: UplinkScarfSyndieRed
|
||||
|
|
@ -1903,6 +2067,8 @@
|
|||
whitelist:
|
||||
- Botanist
|
||||
- ServiceWorker # DeltaV
|
||||
- !type:ReputationCondition # DeltaV - Less than Sidearms as you have to actually grow it
|
||||
reputation: 30
|
||||
|
||||
- type: listing
|
||||
id: uplinkRiggedBoxingGlovesPassenger
|
||||
|
|
@ -1920,6 +2086,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Passenger
|
||||
- !type:ReputationCondition # DeltaV - Melee Weapons reputation
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: uplinkRiggedBoxingGlovesBoxer
|
||||
|
|
@ -1937,6 +2105,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Boxer
|
||||
- !type:ReputationCondition # DeltaV - Melee Weapons reputation
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: uplinkNecronomicon
|
||||
|
|
@ -1958,6 +2128,8 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition # DeltaV - Allies reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: uplinkHolyHandGrenade
|
||||
|
|
@ -1975,6 +2147,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Chaplain
|
||||
- !type:ReputationCondition # DeltaV - More Powerful Explosions
|
||||
reputation: 70
|
||||
|
||||
- type: listing
|
||||
id: uplinkRevolverCapGunFake
|
||||
|
|
@ -1993,6 +2167,8 @@
|
|||
whitelist:
|
||||
- Mime
|
||||
- Clown
|
||||
- !type:ReputationCondition # DeltaV - Sidearms reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: uplinkBananaPeelExplosive
|
||||
|
|
@ -2011,6 +2187,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Clown
|
||||
- !type:ReputationCondition # DeltaV - Explosives reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkClusterBananaPeel
|
||||
|
|
@ -2028,6 +2206,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Clown
|
||||
- !type:ReputationCondition # DeltaV - More Powerful Explosions reputation
|
||||
reputation: 65
|
||||
|
||||
- type: listing
|
||||
id: UplinkHoloclownKit
|
||||
|
|
@ -2046,6 +2226,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Clown
|
||||
- !type:ReputationCondition # DeltaV - Allies reputation
|
||||
reputation: 45
|
||||
|
||||
- type: listing
|
||||
id: uplinkHotPotato
|
||||
|
|
@ -2067,6 +2249,8 @@
|
|||
- Clown
|
||||
- Mime
|
||||
- ServiceWorker # DeltaV
|
||||
- !type:ReputationCondition # DeltaV - Explosives reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: UplinkChimpUpgradeKit
|
||||
|
|
@ -2084,6 +2268,8 @@
|
|||
- !type:BuyerDepartmentCondition
|
||||
whitelist:
|
||||
- Epistemics # DeltaV - Epistemics Department replacing Science
|
||||
- !type:ReputationCondition # DeltaV - Sidearms reputation
|
||||
reputation: 40
|
||||
|
||||
- type: listing
|
||||
id: uplinkProximityMine
|
||||
|
|
@ -2105,6 +2291,8 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition # DeltaV - Explosives reputation
|
||||
reputation: 45
|
||||
|
||||
- type: listing
|
||||
id: UplinkSyndicateSpongeBox
|
||||
|
|
@ -2148,6 +2336,8 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition # DeltaV - Melee Weapons reputation
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkCombatBakery
|
||||
|
|
@ -2167,6 +2357,8 @@
|
|||
whitelist:
|
||||
- Chef
|
||||
- Mime
|
||||
- !type:ReputationCondition # DeltaV - Melee Weapons reputation
|
||||
reputation: 20
|
||||
|
||||
- type: listing
|
||||
id: UplinkSmugglerSatchel
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@
|
|||
state: cpu_security
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerCriminalRecords
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: CriminalRecordsComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
|
@ -96,6 +98,8 @@
|
|||
prototype: ComputerCargoOrders
|
||||
- type: StaticPrice
|
||||
price: 750
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: CargoRequestComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
|
@ -197,6 +201,8 @@
|
|||
- type: ComputerBoard
|
||||
prototype: ComputerCargoBounty
|
||||
- type: StaticPrice
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: CargoBountyComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
|
@ -271,6 +277,8 @@
|
|||
state: cpu_science
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerResearchAndDevelopment
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: ResearchComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
|
@ -324,6 +332,8 @@
|
|||
- type: Tag
|
||||
tags:
|
||||
- HighRiskItem
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: IDComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
|
@ -346,6 +356,8 @@
|
|||
state: cpu_command
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerComms
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: CommsComputerCircuitboard
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseComputerCircuitboard, BaseSyndicateContraband ]
|
||||
|
|
|
|||
|
|
@ -164,10 +164,13 @@
|
|||
- type: Store
|
||||
balance:
|
||||
Telecrystal: 0
|
||||
- type: Contracts # DeltaV - Forced 0 reputation to prevent buying bloodreds etc on it
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.StoreUiKey.Key:
|
||||
type: StoreBoundUserInterface
|
||||
enum.ContractsUiKey.Key: # DeltaV
|
||||
type: ContractsBUI
|
||||
|
||||
- type: entity
|
||||
parent: BaseSubdermalImplant
|
||||
|
|
|
|||
|
|
@ -369,6 +369,8 @@
|
|||
- type: ActivatableUI
|
||||
inHandsOnly: true
|
||||
key: enum.RcdUiKey.Key
|
||||
- type: StealTarget # DeltaV
|
||||
stealGroup: RCD
|
||||
|
||||
- type: entity
|
||||
id: RCDEmpty
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
abstract: true
|
||||
components:
|
||||
- type: StationCentcomm
|
||||
- type: SyndieJail # DeltaV
|
||||
|
||||
- type: entity
|
||||
id: BaseStationEvacuation
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
- NamesOperationSuffix
|
||||
nameFormat: name-format-nuclear-operation
|
||||
- type: NukeopsRule
|
||||
- type: TeleportDiskRule # DeltaV - syndie fulton disk interaction
|
||||
- type: RuleGrids
|
||||
- type: AntagSelection
|
||||
- type: AntagLoadProfileRule
|
||||
|
|
@ -187,10 +188,10 @@
|
|||
- type: TraitorRule
|
||||
# TODO: codewords in yml
|
||||
# TODO: uplink in yml
|
||||
- type: AntagRandomObjectives
|
||||
sets:
|
||||
- groups: TraitorObjectiveGroups
|
||||
maxDifficulty: 5
|
||||
#- type: AntagRandomObjectives # DeltaV - replaced by contracts system
|
||||
# sets:
|
||||
# - groups: TraitorObjectiveGroups
|
||||
# maxDifficulty: 5
|
||||
- type: AntagSelection
|
||||
agentName: traitor-round-end-agent-name
|
||||
|
||||
|
|
@ -203,6 +204,8 @@
|
|||
delay:
|
||||
min: 240
|
||||
max: 420
|
||||
- type: TraitorRule # DeltaV - roundstart traitors have less TC as they have time to complete more contracts
|
||||
startingBalance: 10
|
||||
- type: AntagSelection
|
||||
selectionTime: IntraPlayerSpawn
|
||||
definitions:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
id: Traitors
|
||||
name: guide-entry-traitors
|
||||
text: "/ServerInfo/Guidebook/Antagonist/Traitors.xml"
|
||||
children: # DeltaV
|
||||
- Contracts
|
||||
|
||||
- type: guideEntry
|
||||
id: NuclearOperatives
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
- !type:BuyerSpeciesCondition
|
||||
whitelist:
|
||||
- Oni
|
||||
- !type:ReputationCondition
|
||||
reputation: 30 # Melee Weapons
|
||||
|
||||
- type: listing
|
||||
id: UplinkRickenbacker
|
||||
|
|
@ -31,6 +33,8 @@
|
|||
- !type:BuyerJobCondition
|
||||
whitelist:
|
||||
- Musician
|
||||
- !type:ReputationCondition
|
||||
reputation: 20 # Melee Weapons
|
||||
|
||||
- type: listing
|
||||
id: UplinkSamurai
|
||||
|
|
@ -52,3 +56,5 @@
|
|||
blacklist:
|
||||
components:
|
||||
- SurplusBundle
|
||||
- !type:ReputationCondition
|
||||
reputation: 15 # Armour
|
||||
|
|
|
|||
|
|
@ -4,49 +4,6 @@
|
|||
components:
|
||||
- type: NotJobRequirement
|
||||
job: ForensicMantis
|
||||
- type: StealCondition
|
||||
- type: ExtractCondition
|
||||
stealGroup: AntiPsychicKnife
|
||||
owner: job-name-mantis
|
||||
|
||||
# /-- This objective does not encourage antagonism, thus 1984
|
||||
#- type: entity
|
||||
# id: BecomePsionicObjective
|
||||
# parent: BaseTraitorObjective
|
||||
# name: Become psionic
|
||||
# description: We need you to acquire psionics and keep them until your mission is complete.
|
||||
# components:
|
||||
# - type: NotJobsRequirement
|
||||
# jobs:
|
||||
# - Mime
|
||||
# - ForensicMantis
|
||||
# - type: Objective
|
||||
# difficulty: 2.5
|
||||
# #unique: false
|
||||
# icon:
|
||||
# sprite: Nyanotrasen/Icons/psi.rsi
|
||||
# state: psi
|
||||
# - type: ObjectiveBlacklistRequirement
|
||||
# blacklist:
|
||||
# components:
|
||||
# - BecomeGolemCondition
|
||||
# - type: BecomePsionicCondition
|
||||
|
||||
#- type: entity
|
||||
# id: BecomeGolemObjective
|
||||
# parent: BaseTraitorObjective
|
||||
# name: objective-condition-become-golem-title
|
||||
# description: objective-condition-become-golem-description.
|
||||
# components:
|
||||
# - type: NotJobRequirement
|
||||
# job: Chaplain
|
||||
# - type: Objective
|
||||
# difficulty: 3.5
|
||||
# #unique: false
|
||||
# icon:
|
||||
# sprite: Nyanotrasen/Mobs/Species/Golem/cult.rsi
|
||||
# state: full
|
||||
# - type: ObjectiveBlacklistRequirement
|
||||
# blacklist:
|
||||
# components:
|
||||
# - BecomePsionicCondition
|
||||
# - type: BecomeGolemCondition
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue