Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plutonium core steal objective #26786

Merged
merged 12 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Content.Server/Objectives/Components/StoreUnlockerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Content.Shared.Store;
using Robust.Shared.Prototypes;

namespace Content.Server.Objectives.Components;

/// <summary>
/// Unlocks store listings that use <see cref="ObjectiveUnlockCondition"/>.
/// </summary>
[RegisterComponent]
public sealed partial class StoreUnlockerComponent : Component
{
[DataField(required: true)]
public List<ProtoId<ListingPrototype>> Listings = new();
}
34 changes: 34 additions & 0 deletions Content.Server/Objectives/Systems/StoreUnlockerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Content.Server.Objectives.Components;
using Content.Shared.Mind;

namespace Content.Server.Objectives.Systems;

/// <summary>
/// Provides api for listings with <c>ObjectiveUnlockRequirement</c> to use.
/// </summary>
public sealed class StoreUnlockerSystem : EntitySystem
{
private EntityQuery<StoreUnlockerComponent> _query;

public override void Initialize()
{
_query = GetEntityQuery<StoreUnlockerComponent>();
}

/// <summary>
/// Returns true if a listing id is unlocked by any objectives on a mind.
/// </summary>
public bool IsUnlocked(MindComponent mind, string id)
{
foreach (var obj in mind.Objectives)
{
if (!_query.TryComp(obj, out var comp))
continue;

if (comp.Listings.Contains(id))
return true;
}

return false;
}
}
21 changes: 21 additions & 0 deletions Content.Server/Store/Conditions/ObjectiveUnlockCondition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Content.Shared.Mind;
using Content.Shared.Store;
using Content.Server.Objectives.Systems;

namespace Content.Server.Store.Conditions;

/// <summary>
/// Requires that the buyer have an objective that unlocks this listing.
/// </summary>
public sealed partial class ObjectiveUnlockCondition : ListingCondition
{
public override bool Condition(ListingConditionArgs args)
{
var minds = args.EntityManager.System<SharedMindSystem>();
if (!minds.TryGetMind(args.Buyer, out _, out var mind))
return false;

var unlocker = args.EntityManager.System<StoreUnlockerSystem>();
return unlocker.IsUnlocked(mind, args.Listing.ID);
}
}
25 changes: 17 additions & 8 deletions Content.Shared/Cabinet/ItemCabinetSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ public sealed class ItemCabinetSystem : EntitySystem
[Dependency] private readonly OpenableSystem _openable = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;

private EntityQuery<ItemCabinetComponent> _cabinetQuery = default!;
private EntityQuery<ItemSlotsComponent> _slotsQuery = default!;

/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();

_cabinetQuery = GetEntityQuery<ItemCabinetComponent>();
_slotsQuery = GetEntityQuery<ItemSlotsComponent>();

SubscribeLocalEvent<ItemCabinetComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ItemCabinetComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ItemCabinetComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
Expand All @@ -37,12 +43,12 @@ private void OnStartup(Entity<ItemCabinetComponent> ent, ref ComponentStartup ar
private void OnMapInit(Entity<ItemCabinetComponent> ent, ref MapInitEvent args)
{
// update at mapinit to avoid copy pasting locked: true and locked: false for each closed/open prototype
SetSlotLock(ent, !_openable.IsOpen(ent));
SetSlotLock((ent, ent.Comp), !_openable.IsOpen(ent));
}

private void UpdateAppearance(Entity<ItemCabinetComponent> ent)
{
_appearance.SetData(ent, ItemCabinetVisuals.ContainsItem, HasItem(ent));
_appearance.SetData(ent, ItemCabinetVisuals.ContainsItem, HasItem((ent, ent.Comp)));
}

private void OnContainerModified(EntityUid uid, ItemCabinetComponent component, ContainerModifiedMessage args)
Expand All @@ -53,21 +59,24 @@ private void OnContainerModified(EntityUid uid, ItemCabinetComponent component,

private void OnOpened(Entity<ItemCabinetComponent> ent, ref OpenableOpenedEvent args)
{
SetSlotLock(ent, false);
SetSlotLock((ent, ent.Comp), false);
}

private void OnClosed(Entity<ItemCabinetComponent> ent, ref OpenableClosedEvent args)
{
SetSlotLock(ent, true);
SetSlotLock((ent, ent.Comp), true);
}

/// <summary>
/// Tries to get the cabinet's item slot.
/// </summary>
public bool TryGetSlot(Entity<ItemCabinetComponent> ent, [NotNullWhen(true)] out ItemSlot? slot)
public bool TryGetSlot(Entity<ItemCabinetComponent?> ent, [NotNullWhen(true)] out ItemSlot? slot)
{
slot = null;
if (!TryComp<ItemSlotsComponent>(ent, out var slots))
if (!_cabinetQuery.Resolve(ent, ref ent.Comp))
return false;

if (!_slotsQuery.TryComp(ent, out var slots))
return false;

return _slots.TryGetSlot(ent, ent.Comp.Slot, out slot, slots);
Expand All @@ -76,7 +85,7 @@ public bool TryGetSlot(Entity<ItemCabinetComponent> ent, [NotNullWhen(true)] out
/// <summary>
/// Returns true if the cabinet contains an item.
/// </summary>
public bool HasItem(Entity<ItemCabinetComponent> ent)
public bool HasItem(Entity<ItemCabinetComponent?> ent)
{
return TryGetSlot(ent, out var slot) && slot.HasItem;
}
Expand All @@ -86,7 +95,7 @@ public bool HasItem(Entity<ItemCabinetComponent> ent)
/// </summary>
public void SetSlotLock(Entity<ItemCabinetComponent> ent, bool closed)
{
if (!TryComp<ItemSlotsComponent>(ent, out var slots))
if (!_slotsQuery.TryComp(ent, out var slots))
return;

if (_slots.TryGetSlot(ent, ent.Comp.Slot, out var slot, slots))
Expand Down
23 changes: 23 additions & 0 deletions Content.Shared/Cabinet/SealingCabinetComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Cabinet;

/// <summary>
/// Item cabinet that cannot be opened if it has an item inside.
/// The only way to open it after that is to emag it.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SealingCabinetComponent : Component
{
/// <summary>
/// Popup shown when trying to open the cabinet once sealed.
/// </summary>
[DataField(required: true)]
public LocId SealedPopup = string.Empty;

/// <summary>
/// Set to false to disable emag unsealing.
/// </summary>
[DataField]
public bool Emaggable = true;
}
45 changes: 45 additions & 0 deletions Content.Shared/Cabinet/SealingCabinetSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Content.Shared.Emag.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Popups;

namespace Content.Shared.Cabinet;

public sealed class SealingCabinetSystem : EntitySystem
{
[Dependency] private readonly ItemCabinetSystem _cabinet = default!;
[Dependency] private readonly OpenableSystem _openable = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<SealingCabinetComponent, OpenableOpenAttemptEvent>(OnOpenAttempt);
SubscribeLocalEvent<SealingCabinetComponent, GotEmaggedEvent>(OnEmagged);
}

private void OnOpenAttempt(Entity<SealingCabinetComponent> ent, ref OpenableOpenAttemptEvent args)
{
if (!_cabinet.HasItem(ent.Owner))
return;

args.Cancelled = true;
if (args.User is {} user)
_popup.PopupClient(Loc.GetString(ent.Comp.SealedPopup, ("container", ent.Owner)), ent, user);
}

private void OnEmagged(Entity<SealingCabinetComponent> ent, ref GotEmaggedEvent args)
{
if (!ent.Comp.Emaggable)
return;

if (!_cabinet.HasItem(ent.Owner) || _openable.IsOpen(ent))
return;

_openable.SetOpen(ent, true);

args.Handled = true;
args.Repeatable = true;
}
}
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/nuke/nuke-core-container.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nuke-core-container-whitelist-fail-popup = That doesn't fit into the container.
nuke-core-container-sealed-popup = The {$container} is sealed shut!
1 change: 1 addition & 0 deletions Resources/Locale/en-US/objectives/conditions/steal.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ objective-condition-steal-title = Steal the {$owner}'s {$itemName}.
objective-condition-steal-description = We need you to steal {$itemName}. Don't get caught.
objective-condition-steal-station = station
objective-condition-steal-nuclear-bomb = nuclear bomb
objective-condition-steal-Ian = head of personnel's corgi
objective-condition-thief-description = The {$itemName} would be a great addition to my collection!
Expand Down
1 change: 1 addition & 0 deletions Resources/Locale/en-US/store/categories.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ store-category-allies = Allies
store-category-job = Job
store-category-wearables = Wearables
store-category-pointless = Pointless
store-category-objectives = Objectives
# Revenant
store-category-abilities = Abilities
Expand Down
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/store/uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,7 @@ uplink-barber-scissors-desc = A good tool to give your fellow agent a nice hairc
uplink-backpack-syndicate-name = Syndicate backpack
uplink-backpack-syndicate-desc = Lightweight explosion-proof а backpack for holding various traitor goods
# Objectives
uplink-core-extraction-toolbox-name = Core Extraction Toolbox
uplink-core-extraction-toolbox-desc = A toolbox containing everything you need to remove a nuclear bomb's plutonium core. Instructions not included.
5 changes: 4 additions & 1 deletion Resources/Locale/en-US/tools/tool-qualities.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ tool-quality-rolling-name = Rolling
tool-quality-rolling-tool-name = Rolling Pin
tool-quality-digging-name = Digging
tool-quality-digging-tool-name = Shovel
tool-quality-digging-tool-name = Shovel
tool-quality-fine-screwing-name = Fine Screwing
tool-quality-fine-screwing-tool-name = Thin-Tipped Screwdriver
13 changes: 13 additions & 0 deletions Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@
- id: ClothingHandsGlovesCombat
- id: ClothingMaskGasSyndicate

- type: entity
parent: ToolboxSyndicate
id: ToolboxSyndicateFilledCoreExtraction
suffix: Filled, Core Extraction
components:
- type: StorageFill
contents:
- id: Crowbar
- id: Welder
- id: Wrench
- id: ThinTippedScrewdriver
- id: NukeCoreContainer

- type: entity
id: ToolboxGoldFilled
name: golden toolbox
Expand Down
20 changes: 20 additions & 0 deletions Resources/Prototypes/Catalog/uplink_catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1727,3 +1727,23 @@
blacklist:
components:
- SurplusBundle

# Objective-specific
- type: listing
id: UplinkCoreExtractionToolbox
name: uplink-core-extraction-toolbox-name
description: uplink-core-extraction-toolbox-desc
icon:
sprite: Objects/Misc/nuke_core_container.rsi
state: closed
productEntity: ToolboxSyndicateFilledCoreExtraction
categories:
- UplinkObjectives
conditions:
- !type:ObjectiveUnlockCondition
- !type:ListingLimitedStockCondition
stock: 1
- !type:BuyerWhitelistCondition
blacklist:
components:
- SurplusBundle
63 changes: 63 additions & 0 deletions Resources/Prototypes/Entities/Objects/Misc/plutonium_core.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
- type: entity
parent: BaseItem
id: PlutoniumCore
name: plutonium core
description: Extremely radioactive, even looking at this with the naked eye is dangerous.
components:
- type: Sprite
sprite: Objects/Misc/plutonium_core.rsi
state: icon
- type: StealTarget
stealGroup: PlutoniumCore
- type: RadiationSource
intensity: 4
slope: 1
- type: StaticPrice
price: 49000
- type: Tag
tags:
- PlutoniumCore

- type: entity
parent: [BaseItem, BaseItemCabinet]
id: NukeCoreContainer
name: nuke core container
description: Solid container for radioactive objects.
components:
- type: Sprite
sprite: Objects/Misc/nuke_core_container.rsi
layers:
- state: closed
map: [ "enum.OpenableVisuals.Layer" ]
- state: core_closed
map: [ "enum.ItemCabinetVisuals.Layer" ]
visible: false
shader: unshaded
- type: Item
size: Normal
shape:
- 0,0,1,1
- type: RadiationBlockingContainer
resistance: 4
- type: SealingCabinet
sealedPopup: nuke-core-container-sealed-popup
- type: ItemSlots
slots:
ItemCabinet:
whitelist:
tags:
- PlutoniumCore
whitelistFailPopup: nuke-core-container-whitelist-fail-popup
- type: GenericVisualizer
visuals:
enum.OpenableVisuals.Opened:
enum.OpenableVisuals.Layer:
True: { state: open }
False: { state: closed }
enum.ItemCabinetVisuals.Layer:
True: { state: core_open }
False: { state: core_closed }
enum.ItemCabinetVisuals.ContainsItem:
enum.ItemCabinetVisuals.Layer:
True: { visible: true }
False: { visible: false }
10 changes: 10 additions & 0 deletions Resources/Prototypes/Entities/Objects/Tools/syndicate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- type: entity
parent: Screwdriver
id: ThinTippedScrewdriver
description: A screwdriver with an ultra thin tip that's carefully designed to boost screwing speed.
suffix: DO NOT MAP
components:
- type: Tool
qualities:
- Screwing
- FineScrewing
Loading
Loading