diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index c285362bcd2..975e42dd58a 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -31,6 +31,8 @@
VAR_FINAL/initalized = FALSE
/// Whether the bounty has been completed.
VAR_FINAL/claimed = FALSE
+ /// What pool we picked the loot from.
+ VAR_FINAL/loot_difficulty
/// What uplink item the bounty will reward on completion.
VAR_FINAL/datum/uplink_item/reward_item
@@ -70,16 +72,47 @@
/// Selects what uplink item the bounty will reward on completion.
/datum/spy_bounty/proc/select_reward(datum/spy_bounty_handler/handler)
- var/list/loot_pool = handler.possible_uplink_items[difficulty]
+ loot_difficulty = difficulty
+
+ var/list/loot_pool
+ // work backwards from the highest difficulty loot pool to find one that has items
+ for(var/i in length(handler.possible_uplink_items) to 1 step -1)
+ var/pool_tier = handler.possible_uplink_items[i]
+ // we're not looking for this difficult, skip
+ if(pool_tier != loot_difficulty)
+ continue
+ // we found our difficulty's loot pool, if it has items we're done
+ loot_pool = handler.possible_uplink_items[pool_tier]
+ if(length(loot_pool) || i == 1)
+ break
+ // if our difficult does not have items, and we're not at the lowest difficulty, step down and try again
+ loot_difficulty = handler.possible_uplink_items[i - 1]
if(!length(loot_pool))
- reward_item = /datum/uplink_item/bundles_tc/telecrystal
+ reward_item = SStraitor.uplink_items_by_type[/datum/uplink_item/bundles_tc/telecrystal]
return // future todo : add some junk items for when we run out of items
reward_item = pick(loot_pool)
+ // we remove here, rather than on claim, to reduce the chance of duplicate rewards in a single batch
+ // otherwise it would be not only possible, but *likely* to get the same reward simultaneously across bounties
+ // (though the reason this is a probability is so there is a rare chance this can happen anyways, for the fun of it)
if(prob(80))
loot_pool -= reward_item
+/**
+ * Called when the bounty gets cleared after the end of a bounty period
+ *
+ * * handler - The bounty handler that is handling this bounty.
+ */
+/datum/spy_bounty/proc/clear_bounty(datum/spy_bounty_handler/handler)
+ ASSERT(initalized, "Trying to clear an uninitialized bounty!")
+
+ // another chance to return unclaimed reward items to the bounty pool
+ if(!claimed && reward_item && prob(40))
+ handler.possible_uplink_items[loot_difficulty || difficulty] |= reward_item
+
+ qdel(src)
+
/**
* Checks if the passed movable is a valid target for this bounty.
*
diff --git a/code/modules/antagonists/spy/spy_bounty_handler.dm b/code/modules/antagonists/spy/spy_bounty_handler.dm
index 4b4ba9a835c..2f4cfc876c7 100644
--- a/code/modules/antagonists/spy/spy_bounty_handler.dm
+++ b/code/modules/antagonists/spy/spy_bounty_handler.dm
@@ -113,22 +113,43 @@
continue
bounty_types[difficulty][bounty] = weight
+ var/list/limited_items = list()
+ var/list/show_anyways = list()
+ show_anyways += typesof(/datum/uplink_item/spy_unique/shotgun_ammo) // acquiring a shotgun is not difficult
+
for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
- if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM)
+ // limited items is populated as we go
+ if(item in limited_items)
continue
- if(!(item.purchasable_from & UPLINK_SPY))
+ // proc handles checking if the item is valid
+ if(!try_add_to_loot_pool(item))
continue
- // This will have some overlap, and that's intentional -
- // Adds some variety, rare moments where you can get a hard reward for an easier bounty (or visa versa)
- if(item.cost <= CONFIG_GET(number/spy_easy_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_EASY] += item
- if(item.cost >= CONFIG_GET(number/spy_easy_reward_tc_threshold) && item.cost <= CONFIG_GET(number/spy_hard_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_MEDIUM] += item
- if(item.cost >= CONFIG_GET(number/spy_hard_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_HARD] += item
+ // any child items, such as ammo, are removed from the pool until the parent item is rewarded
+ for(var/child_item_type in (item.relevant_child_items || list()) - show_anyways)
+ if(prob(10)) // 10% chance to have it anyways though. teehee
+ continue
+ var/child_item = SStraitor.uplink_items_by_type[child_item_type]
+ for(var/difficulty in possible_uplink_items)
+ possible_uplink_items[difficulty] -= child_item
+ limited_items |= child_item
refresh_bounty_list()
+/// Helper to attempt to add the passed uplink item datum to the possible bounty pool(s).
+/datum/spy_bounty_handler/proc/try_add_to_loot_pool(datum/uplink_item/item)
+ if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM || !(item.purchasable_from & UPLINK_SPY))
+ return FALSE
+
+ // This will have some overlap, and that's intentional -
+ // Adds some variety, rare moments where you can get a hard reward for an easier bounty (or visa versa)
+ if(item.cost <= CONFIG_GET(number/spy_easy_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_EASY] |= item
+ if(item.cost >= CONFIG_GET(number/spy_easy_reward_tc_threshold) && item.cost <= CONFIG_GET(number/spy_hard_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_MEDIUM] |= item
+ if(item.cost >= CONFIG_GET(number/spy_hard_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_HARD] |= item
+ return TRUE
+
/// Helper that returns a list of all active bounties in a single list, regardless of difficulty.
/datum/spy_bounty_handler/proc/get_all_bounties() as /list
var/list/all_bounties = list()
@@ -152,7 +173,9 @@
bounties_to_give[SPY_DIFFICULTY_MEDIUM] += converted_medium_bounties
for(var/difficulty in bounties)
- QDEL_LIST(bounties[difficulty])
+ for(var/datum/spy_bounty/bounty as anything in bounties[difficulty])
+ bounty.clear_bounty(src)
+ bounties[difficulty].Cut()
var/list/pool = bounty_types[difficulty]
var/amount_to_give = bounties_to_give[difficulty]
diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm
index 7131a0028ca..879061ed25a 100644
--- a/code/modules/antagonists/spy/spy_uplink.dm
+++ b/code/modules/antagonists/spy/spy_uplink.dm
@@ -148,12 +148,16 @@
return FALSE
var/bounty_key = bounty.get_dupe_protection_key(stealing)
+ // record that we've claimed this type of bounty
handler.all_claimed_bounty_types[bounty_key] += 1
handler.claimed_bounties_from_last_pool[bounty_key] = TRUE
-
+ // clear up the bounty itself
bounty.clean_up_stolen_item(stealing, spy, handler)
bounty.claimed = TRUE
-
+ // adds child items to the bounty pool, ie ammo for a newly acquired gun
+ for(var/child_item_type in bounty.reward_item.relevant_child_items)
+ handler.try_add_to_loot_pool(SStraitor.uplink_items_by_type[child_item_type])
+ // and finally, spawn the reward
var/atom/movable/reward = bounty.reward_item.spawn_item_for_generic_use(spy)
if(isitem(reward))
spy.put_in_hands(reward)
diff --git a/code/modules/cargo/packs/general.dm b/code/modules/cargo/packs/general.dm
index 92364c33b8a..896649cc961 100644
--- a/code/modules/cargo/packs/general.dm
+++ b/code/modules/cargo/packs/general.dm
@@ -224,8 +224,7 @@
///Generate assorted uplink items, taking into account the same surplus modifiers used for surplus crates
/datum/supply_pack/misc/syndicate/fill(obj/structure/closet/crate/C)
var/list/uplink_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
if(item.purchasable_from & contents_uplink_type && item.item)
uplink_items += item
diff --git a/code/modules/research/techweb/nodes/syndicate_nodes.dm b/code/modules/research/techweb/nodes/syndicate_nodes.dm
index f4317669597..d031cf7c848 100644
--- a/code/modules/research/techweb/nodes/syndicate_nodes.dm
+++ b/code/modules/research/techweb/nodes/syndicate_nodes.dm
@@ -30,9 +30,8 @@
SIGNAL_HANDLER
UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
required_items_to_unlock = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
- if(!item.item || !(item.uplink_item_flags & SYNDIE_ILLEGAL_TECH))
+ for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
+ if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM || !(item.uplink_item_flags & SYNDIE_ILLEGAL_TECH))
continue
required_items_to_unlock |= item.item //allows deconning to unlock.
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index b89fff956df..5644e058244 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -109,6 +109,9 @@
/// Uses the purchase log, so items purchased that are not visible in the purchase log will not count towards this.
/// However, they won't be purchasable afterwards.
var/lock_other_purchases = FALSE
+ /// A lazylist of typepaths to uplink items relevant to this this item
+ /// EX: a pistol would list its magazines or modifications here
+ var/list/relevant_child_items
/datum/uplink_item/New()
. = ..()
diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm
index b6cdc2fd3d6..6247e32e3b8 100644
--- a/code/modules/uplink/uplink_items/bundle.dm
+++ b/code/modules/uplink/uplink_items/bundle.dm
@@ -18,8 +18,7 @@
/datum/uplink_item/bundles_tc/random/purchase(mob/user, datum/uplink_handler/handler, atom/movable/source)
var/list/possible_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/uplink_item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/uplink_item as anything in SStraitor.uplink_items)
if(src == uplink_item || !uplink_item.item)
continue
if(!handler.can_purchase_item(user, uplink_item))
@@ -81,8 +80,7 @@
/// generates items that can go inside crates, edit this proc to change what items could go inside your specialized crate
/datum/uplink_item/bundles_tc/surplus/proc/generate_possible_items(mob/user, datum/uplink_handler/handler)
var/list/possible_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/uplink_item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/uplink_item as anything in SStraitor.uplink_items)
if(src == uplink_item || !uplink_item.item)
continue
if(!handler.check_if_restricted(uplink_item))
diff --git a/code/modules/uplink/uplink_items/dangerous.dm b/code/modules/uplink/uplink_items/dangerous.dm
index 59d5644778d..ad8509a3e7c 100644
--- a/code/modules/uplink/uplink_items/dangerous.dm
+++ b/code/modules/uplink/uplink_items/dangerous.dm
@@ -15,7 +15,10 @@
item = /obj/item/storage/toolbox/guncase/traitor/donksoft
cost = 6
surplus = 10
- purchasable_from = ~UPLINK_SERIOUS_OPS
+ purchasable_from = ~(UPLINK_SERIOUS_OPS | UPLINK_SPY)
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/toydarts,
+ )
/datum/uplink_item/dangerous/pistol
name = "Makarov Pistol Case"
@@ -24,7 +27,13 @@
While not included in the kit, the pistol is compatible with suppressors, which can be purchased separately."
item = /obj/item/storage/toolbox/guncase/traitor
cost = 7
- purchasable_from = ~UPLINK_ALL_SYNDIE_OPS
+ purchasable_from = ~(UPLINK_ALL_SYNDIE_OPS | UPLINK_SPY)
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/pistol,
+ /datum/uplink_item/ammo/pistolap,
+ /datum/uplink_item/ammo/pistolhp,
+ /datum/uplink_item/ammo/pistolfire,
+ )
/datum/uplink_item/dangerous/throwingweapons
name = "Box of Throwing Weapons"
@@ -96,3 +105,9 @@
cost = 13
surplus = 50
purchasable_from = ~UPLINK_ALL_SYNDIE_OPS //only traitors get the original revolver
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index 887a5165ad4..37ee8997aee 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -72,6 +72,17 @@
desc = "A fully-loaded 2-round burst fire drum-fed shotgun, complete with a secondary magazine you can hotswap. The gun has a handy label to explain how. \
Compatible with all 12g rounds. Designed for close quarter anti-personnel engagements. Comes with three spare magazines."
item = /obj/item/storage/toolbox/guncase/bulldog
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/buck,
+ /datum/uplink_item/ammo_nuclear/basic/donk,
+ /datum/uplink_item/ammo_nuclear/basic/flechette,
+ /datum/uplink_item/ammo_nuclear/basic/slug,
+ /datum/uplink_item/ammo_nuclear/incendiary/dragon,
+ /datum/uplink_item/ammo_nuclear/special/meteor,
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/ammo_nuclear/basic/buck
name = "12g Buckshot Drum (Bulldog)"
@@ -122,6 +133,12 @@
desc = "A small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines and is compatible \
with suppressors. Comes with three spare magazines."
item = /obj/item/storage/toolbox/guncase/clandestine
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/m10mm,
+ /datum/uplink_item/ammo_nuclear/ap/m10mm,
+ /datum/uplink_item/ammo_nuclear/hp/m10mm,
+ /datum/uplink_item/ammo_nuclear/incendiary/m10mm,
+ )
/datum/uplink_item/ammo_nuclear/basic/m10mm
name = "10mm Handgun Magazine (Ansem)"
@@ -164,6 +181,12 @@
desc = "A fully-loaded Scarborough Arms bullpup submachine gun. The C-20r fires .45 rounds with a \
24-round magazine and is compatible with suppressors. Comes with spare three magazines."
item = /obj/item/storage/toolbox/guncase/c20r
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/smg,
+ /datum/uplink_item/ammo_nuclear/basic/smg,
+ /datum/uplink_item/ammo_nuclear/hp/smg,
+ /datum/uplink_item/ammo_nuclear/incendiary/smg,
+ )
/datum/uplink_item/ammo_nuclear/basic/smg
name = ".45 SMG Magazine (C-20r)"
@@ -213,6 +236,12 @@
desc = "Waffle Corp's modernized Syndicate revolver. Fires 7 brutal rounds of .357 Magnum. \
A classic operative weapon, improved for the modern era. Comes with 3 additional speedloaders of .357."
item = /obj/item/storage/toolbox/guncase/revolver
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
/datum/uplink_item/ammo_nuclear/basic/revolver
name = ".357 Speed Loader (Revolver)"
@@ -246,6 +275,10 @@
desc = "A reusable rocket propelled grenade launcher preloaded with a low-yield 84mm HE round. \
Guaranteed to take your target out with a bang, or your money back! Comes with a bouquet of additional rockets!"
item = /obj/item/storage/toolbox/guncase/rocketlauncher
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/rocket,
+ /datum/uplink_item/ammo_nuclear/basic/rocket,
+ )
/datum/uplink_item/ammo_nuclear/basic/rocket
name = "84mm HE Rocket Bouquet (Rocket Launcher)"
@@ -272,6 +305,13 @@
desc = "A fully-loaded Aussec Armoury belt-fed machine gun. \
This deadly weapon has a massive 50-round magazine of devastating 7mm ammunition."
item = /obj/item/gun/ballistic/automatic/l6_saw
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/machinegun,
+ /datum/uplink_item/ammo_nuclear/basic/machinegun,
+ /datum/uplink_item/ammo_nuclear/hp/machinegun,
+ /datum/uplink_item/ammo_nuclear/incendiary/machinegun,
+ /datum/uplink_item/ammo_nuclear/special/machinegun,
+ )
/datum/uplink_item/ammo_nuclear/basic/machinegun
name = "7mm Box Magazine (L6 SAW)"
@@ -311,6 +351,11 @@
Comes with a 40mm underbarrel grenade launcher. Use secondary-fire to fire the grenade launcher. Also comes with two spare magazines \
and a box of 40mm rubber slugs."
item = /obj/item/storage/toolbox/guncase/m90gl
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/carbine,
+ /datum/uplink_item/ammo_nuclear/special/carbine,
+ /datum/uplink_item/ammo_nuclear/basic/carbine/a40mm,
+ )
/datum/uplink_item/ammo_nuclear/basic/carbine
name = ".223 Toploader Magazine (M-90gl)"
@@ -340,6 +385,13 @@
Can be fitted with a suppressor. If anyone asks how that even works, tell them it's Nanotrasen's fault. Comes with \
3 spare magazines; 2 regular magazines and 1 disruptor magazine. Also comes with a suit and tie."
item = /obj/item/storage/briefcase/sniper
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/sniper/penetrator,
+ /datum/uplink_item/ammo_nuclear/basic/sniper,
+ /datum/uplink_item/ammo_nuclear/basic/sniper/disruptor,
+ /datum/uplink_item/ammo_nuclear/incendiary/sniper,
+ /datum/uplink_item/ammo_nuclear/special/sniper/marksman,
+ )
/datum/uplink_item/ammo_nuclear/basic/sniper
name = ".50 BMG Magazine (AMSR)"
@@ -398,6 +450,9 @@
item = /obj/item/gun/ballistic/automatic/smartgun
cost = 2
purchasable_from = UPLINK_SERIOUS_OPS
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/surplus_smg,
+ )
/datum/uplink_item/ammo_nuclear/surplus_smg
name = "Surplus Smart-SMG Magazine (Smartgun)"
@@ -629,6 +684,12 @@
item = /obj/item/storage/box/syndie_kit/cowboy
cost = 18
purchasable_from = UPLINK_SERIOUS_OPS
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
// Mech related gear
@@ -650,6 +711,9 @@
for hit-and-run style attacks. Features a scattershot shotgun, armor boosters against melee and ranged attacks, and ion thrusters."
item = /obj/vehicle/sealed/mecha/gygax/dark/loaded
cost = 60
+ relevant_child_items = list(
+ /datum/uplink_item/mech/support_bag/mauler,
+ )
/datum/uplink_item/mech/mauler
name = "Mauler Exosuit"
@@ -657,6 +721,9 @@
and deployable smoke. Comes equipped with an LMG, scattershot carbine, missile rack, and an antiprojectile armor booster."
item = /obj/vehicle/sealed/mecha/marauder/mauler/loaded
cost = 100
+ relevant_child_items = list(
+ /datum/uplink_item/mech/support_bag/mauler,
+ )
// ~~ Mech Support ~~
diff --git a/code/modules/uplink/uplink_items/spy_unique.dm b/code/modules/uplink/uplink_items/spy_unique.dm
index e201b804df5..abc7449e567 100644
--- a/code/modules/uplink/uplink_items/spy_unique.dm
+++ b/code/modules/uplink/uplink_items/spy_unique.dm
@@ -76,6 +76,11 @@
item = /obj/item/gun/ballistic/shotgun/automatic/dual_tube/deadly
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/spy_unique/bulldog_shotgun
name = "Bulldog Shotgun"
@@ -83,6 +88,17 @@
item = /obj/item/gun/ballistic/shotgun/bulldog/unrestricted
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/buck,
+ /datum/uplink_item/ammo_nuclear/basic/donk,
+ /datum/uplink_item/ammo_nuclear/basic/flechette,
+ /datum/uplink_item/ammo_nuclear/basic/slug,
+ /datum/uplink_item/ammo_nuclear/incendiary/dragon,
+ /datum/uplink_item/ammo_nuclear/special/meteor,
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/spy_unique/ansem_pistol
name = "Ansem Pistol"
@@ -90,6 +106,36 @@
item = /obj/item/gun/ballistic/automatic/pistol/clandestine
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/m10mm,
+ /datum/uplink_item/ammo_nuclear/ap/m10mm,
+ /datum/uplink_item/ammo_nuclear/hp/m10mm,
+ /datum/uplink_item/ammo_nuclear/incendiary/m10mm,
+ )
+
+/datum/uplink_item/spy_unique/makarov_pistol
+ name = "Makarov Pistol"
+ desc = "A Makarov pistol - Reliable and sturdy."
+ item = /obj/item/gun/ballistic/automatic/pistol
+ cost = SPY_LOWER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/pistol,
+ /datum/uplink_item/ammo/pistolap,
+ /datum/uplink_item/ammo/pistolhp,
+ /datum/uplink_item/ammo/pistolfire,
+ )
+
+/datum/uplink_item/spy_unique/toy_pistol
+ name = "Donksoft Riot Pistol"
+ desc = "A Donksoft Riot Pistol - A toy pistol that fires foam darts at higher than normal velocity."
+ item = /obj/item/gun/ballistic/automatic/pistol/toy/riot/clandestine
+ cost = SPY_LOWER_COST_THRESHOLD
+ cost = SPY_LOWER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/toydarts,
+ )
/datum/uplink_item/spy_unique/rocket_launcher
name = "Rocket Launcher"
@@ -97,6 +143,10 @@
item = /obj/item/gun/ballistic/rocketlauncher
cost = SPY_UPPER_COST_THRESHOLD - 1 // It's a meme item
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/rocket,
+ /datum/uplink_item/ammo_nuclear/basic/rocket,
+ )
/datum/uplink_item/spy_unique/shotgun_ammo
name = "Box of Buckshot"
diff --git a/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx b/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
index ea1e142de19..3549e9fafd8 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
@@ -56,7 +56,9 @@ export const AntagInfoSpy = () => {
Work together or work against them: The choice is yours, but{' '}
- you cannot share the rewards.
+
+ the same bounty cannot be claimed twice.
+