diff --git a/code/__DEFINES/modular_computer.dm b/code/__DEFINES/modular_computer.dm
index 1c9f3b59ecd..c4f1b3884f3 100644
--- a/code/__DEFINES/modular_computer.dm
+++ b/code/__DEFINES/modular_computer.dm
@@ -8,7 +8,7 @@
#define PROGRAM_ALL ALL
#define PROGRAM_CONSOLE (1<<0)
#define PROGRAM_LAPTOP (1<<1)
-#define PROGRAM_TABLET (1<<2)
+#define PROGRAM_PDA (1<<2)
//Program categories
#define PROGRAM_CATEGORY_CREW "Crew"
#define PROGRAM_CATEGORY_ENGI "Engineering"
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 5ac9be55345..e7ebc19c682 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -43,7 +43,6 @@
#define ROLE_WIZARD_MIDROUND "Wizard (Midround)"
//SKYRAT EDIT START
#define ROLE_BORER "Borer"
-#define ROLE_DRIFTING_CONTRACTOR "Drifting Contractor"
#define ROLE_LONE_INFILTRATOR "Lone Infiltrator"
#define ROLE_MUTANT "Mutated Abomination"
#define ROLE_CLOCK_CULTIST "Clock Cultist"
@@ -172,7 +171,6 @@ GLOBAL_LIST_INIT(special_roles, list(
//SKYRAT EDIT START
ROLE_LONE_INFILTRATOR = 0,
ROLE_BORER = 0,
- ROLE_DRIFTING_CONTRACTOR = 14,
ROLE_MUTANT = 0,
//SKYRAT EDIT END
diff --git a/code/__DEFINES/uplink.dm b/code/__DEFINES/uplink.dm
index fcc2118348c..d6412e0e4d1 100644
--- a/code/__DEFINES/uplink.dm
+++ b/code/__DEFINES/uplink.dm
@@ -9,6 +9,9 @@
/// This item is purchasable to clown ops
#define UPLINK_CLOWN_OPS (1 << 2)
+/// This item is purchasable to infiltrators (midround traitors)
+#define UPLINK_INFILTRATORS (1 << 3)
+
/// Progression gets turned into a user-friendly form. This is just an abstract equation that makes progression not too large.
#define DISPLAY_PROGRESSION(time) round(time/60, 0.01)
diff --git a/code/__DEFINES/~skyrat_defines/examine_defines.dm b/code/__DEFINES/~skyrat_defines/examine_defines.dm
index 933717b3178..2c942cd3215 100644
--- a/code/__DEFINES/~skyrat_defines/examine_defines.dm
+++ b/code/__DEFINES/~skyrat_defines/examine_defines.dm
@@ -12,4 +12,3 @@ See the examinemore module for information.
#define EXAMINE_CHECK_ROLE "role" //For displaying description information based on a specific ROLE, e.g. traitor. Remember to set the special_desc_role var on the item.
#define EXAMINE_CHECK_JOB "job" //For displaying descriptors for specific jobs, e.g scientist. Remember to set the special_desc_job var on the item.
#define EXAMINE_CHECK_FACTION "faction" //For displaying descriptors for mob factions, e.g. a zombie, or... turrets. Or syndicate. Remember to set special_desc_factions.
-#define EXAMINE_CHECK_CONTRACTOR "contractor" // For contractors and syndicate agents.
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index cba35c8b95c..16eb099a486 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -276,63 +276,6 @@
new /obj/item/implanter/freedom(src) // 5 tc
new /obj/item/stack/telecrystal(src) //The failsafe/self destruct isn't an item we can physically include in the kit, but 1 TC is technically enough to buy the equivalent.
-/obj/item/storage/box/syndicate/contract_kit //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Contract Kit"
- desc = "Supplied to Syndicate contractors."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/syndicate/contractor_loadout //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Standard Loadout"
- desc = "Supplied to Syndicate contractors, providing their specialised space suit and chameleon uniform."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/paper/contractor_guide //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- name = "Contractor Guide"
-
-/obj/item/paper/contractor_guide/Initialize(mapload) //SKYRAT EDIT - CHANGED IN MODULAR FOLDER
- default_raw_text = {"
Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,
- this kit will provide you contracts to take on for TC payments.
-
-
Provided within, we give your specialist contractor space suit. It's even more compact, being able to fit into a pocket, and faster than the
- Syndicate space suit available to you on the uplink. We also provide your chameleon jumpsuit and mask, both of which can be changed
- to any form you need for the moment. The cigarettes are a special blend - it'll heal your injuries slowly overtime.
-
-
Your standard issue contractor baton hits harder than the ones you might be used to, and likely be your go to weapon for kidnapping your
- targets. The three additional items have been randomly selected from what we had available. We hope they're useful to you for your mission.
-
-
The contractor hub, available at the top right of the uplink, will provide you unique items and abilities. These are bought using Contractor Rep,
- with two Rep being provided each time you complete a contract.
-
-
Using the tablet
-
-
Open the Syndicate Contract Uplink program.
-
Here, you can accept a contract, and redeem your TC payments from completed contracts.
-
The payment number shown in brackets is the bonus you'll receive when bringing your target alive. You receive the
- other number regardless of if they were alive or dead.
-
Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them
- inside the pod.
-
-
-
Be careful when accepting a contract. While you'll be able to see the location of the dropoff point, cancelling will make it
- unavailable to take on again.
-
The tablet can also be recharged at any cell charger.
-
Extracting
-
-
Make sure both yourself and your target are at the dropoff.
-
Call the extraction, and stand back from the drop point.
-
If it fails, make sure your target is inside, and there's a free space for the pod to land.
-
Grab your target, and drag them into the pod.
-
-
Ransoms
-
We need your target for our own reasons, but we ransom them back to your mission area once their use is served. They will return back
- from where you sent them off from in several minutes time. Don't worry, we give you a cut of what we get paid. We pay this into whatever
- ID card you have equipped, on top of the TC payment we give.
-
-
Good luck agent. You can burn this document with the supplied lighter.
"}
- return ..()
-
/obj/item/storage/box/syndie_kit
name = "box"
desc = "A sleek, sturdy box."
@@ -821,6 +764,63 @@
/obj/item/food/grown/apple = 1,
), src)
+/obj/item/storage/box/syndicate/contract_kit
+ name = "Contract Kit"
+ desc = "Supplied to Syndicate contractors."
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/syndicate/contract_kit/PopulateContents()
+ new /obj/item/modular_computer/pda/syndicate_contract_uplink(src)
+ new /obj/item/storage/box/syndicate/contractor_loadout(src)
+ new /obj/item/melee/baton/telescopic/contractor_baton(src)
+
+ // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
+ var/static/list/item_list = list(
+ /obj/item/storage/backpack/duffelbag/syndie/x4,
+ /obj/item/storage/box/syndie_kit/throwing_weapons,
+ /obj/item/gun/syringe/syndicate,
+ /obj/item/pen/edagger,
+ /obj/item/pen/sleepy,
+ /obj/item/flashlight/emp,
+ /obj/item/reagent_containers/syringe/mulligan,
+ /obj/item/clothing/shoes/chameleon/noslip,
+ /obj/item/storage/medkit/tactical,
+ /obj/item/encryptionkey/syndicate,
+ /obj/item/clothing/glasses/thermal/syndi,
+ /obj/item/slimepotion/slime/sentience/nuclear,
+ /obj/item/storage/box/syndie_kit/imp_radio,
+ ///obj/item/storage/box/syndie_kit/imp_uplink, // SKYRAT EDIT REMOVAL
+ ///obj/item/clothing/gloves/krav_maga/combatglovesplus, // SKYRAT EDIT REMOVAL
+ /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
+ /obj/item/reagent_containers/hypospray/medipen/stimulants,
+ /obj/item/storage/box/syndie_kit/imp_freedom,
+ /obj/item/toy/eightball/haunted,
+ )
+ for(var/i in 1 to 3)
+ var/selected_item = pick_n_take(list(item_list + modular_item_list)) // SKYRAT EDIT CHANGE - ORIGINAL : var/selected_item = pick_n_take(item_list)
+ new selected_item(src)
+
+ // Paper guide is always last.
+ new /obj/item/paper/contractor_guide(src)
+
+/obj/item/storage/box/syndicate/contractor_loadout
+ name = "Standard Loadout"
+ desc = "Supplied to Syndicate contractors, providing their specialised space suit and chameleon uniform."
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/syndicate/contractor_loadout/PopulateContents()
+ /* SKYRAT EDIT REMOVAL BEGIN - Contractors get MODSuits instead, see contractor modular for overrides
+ new /obj/item/clothing/head/helmet/space/syndicate/contract(src)
+ new /obj/item/clothing/suit/space/syndicate/contract(src)
+ SKYRAT EDIT REMOVAL END */
+ new /obj/item/clothing/under/chameleon(src)
+ new /obj/item/clothing/mask/chameleon(src)
+ new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
+ new /obj/item/card/id/advanced/chameleon(src)
+ new /obj/item/lighter(src)
+
#undef KIT_RECON
#undef KIT_BLOODY_SPAI
#undef KIT_STEALTHY
diff --git a/code/modules/antagonists/traitor/contractor/contract_teammate.dm b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
new file mode 100644
index 00000000000..1aebd9fb951
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
@@ -0,0 +1,72 @@
+///Spawns a contractor partner to a spawning user, with a given key to assign to the new player.
+/proc/spawn_contractor_partner(mob/living/user, key)
+ var/mob/living/carbon/human/partner = new()
+ var/datum/outfit/contractor_partner/partner_outfit = new()
+
+ partner_outfit.equip(partner)
+
+ var/obj/structure/closet/supplypod/arrival_pod = new(null, STYLE_SYNDICATE)
+ arrival_pod.explosionSize = list(0,0,0,1)
+ arrival_pod.bluespace = TRUE
+
+ var/turf/free_location = find_obstruction_free_location(2, user)
+
+ // We really want to send them - if we can't find a nice location just land it on top of them.
+ if (!free_location)
+ free_location = get_turf(user)
+
+ partner.forceMove(arrival_pod)
+ partner.ckey = key
+
+ /// We give a reference to the mind that'll be the support unit
+ var/datum/antagonist/traitor/contractor_support/new_datum = partner.mind.add_antag_datum(/datum/antagonist/traitor/contractor_support)
+
+ to_chat(partner, "\n[span_alertwarning("[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.")]")
+ to_chat(partner, "[span_alertwarning("Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.")]")
+
+ new /obj/effect/pod_landingzone(free_location, arrival_pod)
+ return new_datum
+
+/// Support unit gets it's own very basic antag datum for admin logging.
+/datum/antagonist/traitor/contractor_support
+ name = "Contractor Support Unit"
+ show_in_roundend = FALSE
+ give_objectives = TRUE
+ give_uplink = FALSE
+
+/datum/antagonist/traitor/contractor_support/forge_traitor_objectives()
+ var/datum/objective/generic_objective = new
+ generic_objective.name = "Follow Contractor's Orders"
+ generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
+ generic_objective.completed = TRUE
+ objectives += generic_objective
+
+/datum/antagonist/traitor/contractor_support/forge_ending_objective()
+ return
+
+/datum/outfit/contractor_partner
+ name = "Contractor Support Unit"
+
+ uniform = /obj/item/clothing/under/chameleon
+ suit = /obj/item/clothing/suit/chameleon
+ back = /obj/item/storage/backpack
+ belt = /obj/item/modular_computer/pda/chameleon
+ mask = /obj/item/clothing/mask/cigarette/syndicate
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
+ ears = /obj/item/radio/headset/chameleon
+ id = /obj/item/card/id/advanced/chameleon
+ r_hand = /obj/item/storage/toolbox/syndicate
+ id_trim = /datum/id_trim/chameleon/operative
+
+ backpack_contents = list(
+ /obj/item/storage/box/survival,
+ /obj/item/implanter/uplink,
+ /obj/item/clothing/mask/chameleon,
+ /obj/item/storage/fancy/cigarettes/cigpack_syndicate,
+ /obj/item/lighter,
+ )
+
+/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/H, visualsOnly)
+ . = ..()
+ var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(ITEM_SLOT_MASK)
+ cig.light()
diff --git a/code/modules/antagonists/traitor/contractor/contractor_hub.dm b/code/modules/antagonists/traitor/contractor/contractor_hub.dm
new file mode 100644
index 00000000000..28a9b8e0017
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contractor_hub.dm
@@ -0,0 +1,69 @@
+/datum/contractor_hub
+ ///The current contract in progress, and can be null if no contract is in progress.
+ var/datum/syndicate_contract/current_contract
+ ///List of all available syndicate contracts that can be taken.
+ var/list/datum/syndicate_contract/assigned_contracts = list()
+
+ ///Reference to a contractor teammate, if one has been purchased.
+ var/datum/antagonist/traitor/contractor_support/contractor_teammate
+
+ ///List of all people currently used as targets, to not roll doubles.
+ var/list/assigned_targets = list()
+
+ ///Amount of contracts that have already been completed, for flavor in the UI & round-end logs.
+ var/contracts_completed = 0
+ ///How much TC has been paid out, for flavor in the UI & round-end logs.
+ var/contract_TC_payed_out = 0
+ ///How much TC we can cash out currently. Used when redeeming TC and for round-end logs.
+ var/contract_TC_to_redeem = 0
+
+/datum/contractor_hub/proc/create_contracts(datum/mind/owner)
+ // 6 initial contracts
+ var/list/to_generate = list(
+ CONTRACT_PAYOUT_LARGE,
+ CONTRACT_PAYOUT_MEDIUM,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_SMALL
+ )
+
+ //What the fuck
+ if(length(to_generate) > length(GLOB.manifest.locked))
+ to_generate.Cut(1, length(GLOB.manifest.locked))
+
+ // We don't want the sum of all the payouts to be under this amount
+ var/lowest_TC_threshold = 30
+
+ var/total = 0
+ var/lowest_paying_sum = 0
+ var/datum/syndicate_contract/lowest_paying_contract
+
+ // Randomise order, so we don't have contracts always in payout order.
+ to_generate = shuffle(to_generate)
+
+ // Support contract generation happening multiple times
+ var/start_index = 1
+ if (assigned_contracts.len != 0)
+ start_index = assigned_contracts.len + 1
+
+ // Generate contracts, and find the lowest paying.
+ for(var/i in 1 to to_generate.len)
+ var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[i])
+ var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
+
+ assigned_targets.Add(contract_to_add.contract.target)
+
+ if (!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
+ lowest_paying_sum = contract_payout_total
+ lowest_paying_contract = contract_to_add
+
+ total += contract_payout_total
+ contract_to_add.id = start_index
+ assigned_contracts.Add(contract_to_add)
+
+ start_index++
+
+ // If the threshold for TC payouts isn't reached, boost the lowest paying contract
+ if (total < lowest_TC_threshold)
+ lowest_paying_contract.contract.payout_bonus += (lowest_TC_threshold - total)
diff --git a/code/modules/antagonists/traitor/contractor/contractor_items.dm b/code/modules/antagonists/traitor/contractor/contractor_items.dm
new file mode 100644
index 00000000000..90c495358f2
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/contractor_items.dm
@@ -0,0 +1,53 @@
+/obj/item/pinpointer/crew/contractor
+ name = "contractor pinpointer"
+ desc = "A handheld tracking device that locks onto certain signals. Ignores suit sensors, but is much less accurate."
+ icon_state = "pinpointer_syndicate"
+ worn_icon_state = "pinpointer_black"
+ minimum_range = 25
+ has_owner = TRUE
+ ignore_suit_sensor_level = TRUE
+
+/obj/item/storage/box/contractor/fulton_extraction
+ name = "Fulton Extraction Kit"
+ icon_state = "syndiebox"
+ illustration = "writing_syndie"
+
+/obj/item/storage/box/contractor/fulton_extraction/PopulateContents()
+ new /obj/item/extraction_pack/syndicate(src)
+ new /obj/item/fulton_core(src)
+
+/obj/item/paper/contractor_guide
+ name = "Contractor Guide"
+ default_raw_text = {"Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,\
+ this kit will provide you contracts to take on for TC payments.\
+
Provided within, we give your specialist contractor space suit. It's even more compact, being able to fit into a pocket, and faster than the\
+ Syndicate space suit available to you on the uplink. We also provide your chameleon jumpsuit and mask, both of which can be changed\
+ to any form you need for the moment. The cigarettes are a special blend - it'll heal your injuries slowly overtime.
\
+
Your standard issue contractor baton hits harder than the ones you might be used to, and likely be your go to weapon for kidnapping your\
+ targets. The three additional items have been randomly selected from what we had available. We hope they're useful to you for your mission.
\
+
The contractor hub, available at the top right of the uplink, will provide you unique items and abilities. These are bought using Contractor Rep,\
+ with two Rep being provided each time you complete a contract.
\
+
Using the tablet
\
+ \
+
Open the Syndicate Contract Uplink program.
\
+
Here, you can accept a contract, and redeem your TC payments from completed contracts.
\
+
The payment number shown in brackets is the bonus you'll receive when bringing your target alive. You receive the\
+ other number regardless of if they were alive or dead.
\
+
Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them\
+ inside the pod.
\
+ \
+
Be careful when accepting a contract. While you'll be able to see the location of the dropoff point, cancelling will make it\
+ unavailable to take on again.
\
+
The tablet can also be recharged at any cell charger.
\
+
Extracting
\
+ \
+
Make sure both yourself and your target are at the dropoff.
\
+
Call the extraction, and stand back from the drop point.
\
+
If it fails, make sure your target is inside, and there's a free space for the pod to land.
\
+
Grab your target, and drag them into the pod.
\
+ \
+
Ransoms
\
+
We need your target for our own reasons, but we ransom them back to your mission area once their use is served. They will return back\
+ from where you sent them off from in several minutes time. Don't worry, we give you a cut of what we get paid. We pay this into whatever\
+ ID card you have equipped, on top of the TC payment we give.
\
+
Good luck agent. You can burn this document with the supplied lighter.
"}
diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
new file mode 100644
index 00000000000..3f7370031da
--- /dev/null
+++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
@@ -0,0 +1,271 @@
+/datum/syndicate_contract
+ ///The 'id' of this particular contract. Used to keep track of statuses from TGUI.
+ var/id
+ ///The current status of the contract. Starts off by default.
+ var/status = CONTRACT_STATUS_INACTIVE
+ ///The related Objective datum for the contract, holding the target and such.
+ var/datum/objective/contract/contract
+ ///The job position of the target.
+ var/target_rank
+ ///How much we will pay out upon completion. This is not the TC completion, it's typically credits.
+ var/ransom = 0
+ ///The level of payout, which affects the TC we get paid on completion.
+ var/payout_type
+ ///Flavortext wanted message for the person we're after.
+ var/wanted_message
+ ///List of everything found on the victim at the time of contracting, used to return their stuff afterwards.
+ var/list/victim_belongings = list()
+
+/datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL)
+ contract = new(src)
+ contract.owner = contract_owner
+ payout_type = type
+
+ generate(blacklist)
+
+/datum/syndicate_contract/proc/generate(blacklist)
+ contract.find_target(null, blacklist)
+
+ var/datum/record/crew/record
+ if (contract.target)
+ record = find_record(contract.target.name)
+
+ if (record)
+ target_rank = record.rank
+ else
+ target_rank = "Unknown"
+
+ if (payout_type == CONTRACT_PAYOUT_LARGE)
+ contract.payout_bonus = rand(9,13)
+ else if (payout_type == CONTRACT_PAYOUT_MEDIUM)
+ contract.payout_bonus = rand(6,8)
+ else
+ contract.payout_bonus = rand(2,4)
+
+ contract.payout = rand(0, 2)
+ contract.generate_dropoff()
+
+ ransom = 100 * rand(18, 45)
+
+ var/base = pick_list(WANTED_FILE, "basemessage")
+ var/verb_string = pick_list(WANTED_FILE, "verb")
+ var/noun = pick_list_weighted(WANTED_FILE, "noun")
+ var/location = pick_list_weighted(WANTED_FILE, "location")
+ wanted_message = "[base] [verb_string] [noun] [location]."
+
+/datum/syndicate_contract/proc/handle_extraction(mob/living/user)
+ if (contract.target && contract.dropoff_check(user, contract.target.current))
+
+ var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
+
+ if (free_location)
+ // We've got a valid location, launch.
+ launch_extraction_pod(free_location)
+ return TRUE
+
+ return FALSE
+
+// Launch the pod to collect our victim.
+/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
+ var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
+
+ RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, PROC_REF(enter_check))
+
+ empty_pod.stay_after_drop = TRUE
+ empty_pod.reversing = TRUE
+ empty_pod.explosionSize = list(0,0,0,1)
+ empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
+
+ new /obj/effect/pod_landingzone(empty_pod_turf, empty_pod)
+
+/datum/syndicate_contract/proc/enter_check(datum/source, sent_mob)
+ SIGNAL_HANDLER
+
+ if(!istype(source, /obj/structure/closet/supplypod/extractionpod))
+ return
+ if(!isliving(sent_mob))
+ return
+ var/mob/living/person_sent = sent_mob
+ var/datum/antagonist/traitor/traitor_data = contract.owner.has_antag_datum(/datum/antagonist/traitor)
+ if(person_sent == contract.target.current)
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem += contract.payout
+ traitor_data.uplink_handler.contractor_hub.contracts_completed++
+ if(person_sent.stat != DEAD)
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
+ status = CONTRACT_STATUS_COMPLETE
+ if(traitor_data.uplink_handler.contractor_hub.current_contract == src)
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+ else
+ status = CONTRACT_STATUS_ABORTED // Sending a target that wasn't even yours is as good as just aborting it
+ if(traitor_data.uplink_handler.contractor_hub.current_contract == src)
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+
+ if(iscarbon(person_sent))
+ for(var/obj/item/person_contents in person_sent.gather_belongings())
+ if(ishuman(person_sent))
+ var/mob/living/carbon/human/human_sent = person_sent
+ if(person_contents == human_sent.w_uniform)
+ continue //So all they're left with are shoes and uniform.
+ if(person_contents == human_sent.shoes)
+ continue
+ person_sent.transferItemToLoc(person_contents)
+ victim_belongings.Add(WEAKREF(person_contents))
+
+ var/obj/structure/closet/supplypod/extractionpod/pod = source
+ // Handle the pod returning
+ pod.startExitSequence(pod)
+
+ if(ishuman(person_sent))
+ var/mob/living/carbon/human/target = person_sent
+ // After we remove items, at least give them what they need to live.
+ target.dna.species.give_important_for_life(target)
+
+ //we'll start the effects in a few seconds since it takes a moment for the pod to leave.
+ addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), person_sent), 3 SECONDS)
+
+ // This is slightly delayed because of the sleep calls above to handle the narrative.
+ // We don't want to tell the station instantly.
+ var/points_to_check
+ var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if(cargo_account)
+ points_to_check = cargo_account.account_balance
+ if(points_to_check >= ransom)
+ cargo_account.adjust_money(-ransom)
+ else
+ cargo_account.adjust_money(-points_to_check)
+ priority_announce(
+ text = "One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
+ As is policy we've taken a portion of the station's funds to offset the overall cost.",
+ sender_override = "Nanotrasen Asset Protection")
+
+ addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS)
+
+/datum/syndicate_contract/proc/finish_enter()
+ // Pay contractor their portion of ransom
+ if(status != CONTRACT_STATUS_COMPLETE)
+ return
+ var/obj/item/card/id/contractor_id = contract.owner.current?.get_idcard(TRUE)
+ if(!contractor_id || !contractor_id.registered_account)
+ return
+ contractor_id.registered_account.adjust_money(ransom * 0.35)
+ contractor_id.registered_account.bank_card_talk("We've processed the ransom, agent. \
+ Here's your cut - your balance is now [contractor_id.registered_account.account_balance] cr.", TRUE)
+
+#define VICTIM_EXPERIENCE_START 0
+#define VICTIM_EXPERIENCE_FIRST_HIT 1
+#define VICTIM_EXPERIENCE_SECOND_HIT 2
+#define VICTIM_EXPERIENCE_THIRD_HIT 3
+#define VICTIM_EXPERIENCE_LAST_HIT 4
+
+/**
+ * handle_victim_experience
+ *
+ * Handles the effects given to victims upon being contracted.
+ * We heal them up and cause them immersive effects, just for fun.
+ * Args:
+ * victim - The person we're harassing
+ * level - The current stage of harassement they are facing. This increases by itself, looping until finished.
+ */
+/datum/syndicate_contract/proc/handle_victim_experience(mob/living/victim, level = VICTIM_EXPERIENCE_START)
+ // Ship 'em back - dead or alive, 4 minutes wait.
+ // Even if they weren't the target, we're still treating them the same.
+ if(!level)
+ addtimer(CALLBACK(src, PROC_REF(return_victim), victim), (60 * 10) * 4)
+ if(victim.stat == DEAD)
+ return
+
+ var/time_until_next
+ switch(level)
+ if(VICTIM_EXPERIENCE_START)
+ // Heal them up - gets them out of crit/soft crit. If omnizine is removed in the future, this needs to be replaced with a
+ // method of healing them, consequence free, to a reasonable amount of health.
+ victim.reagents.add_reagent(/datum/reagent/medicine/omnizine, amount = 20)
+ victim.flash_act()
+ victim.adjust_confusion(1 SECONDS)
+ victim.adjust_eye_blur(5 SECONDS)
+ to_chat(victim, span_warning("You feel strange..."))
+ time_until_next = 6 SECONDS
+ if(VICTIM_EXPERIENCE_FIRST_HIT)
+ to_chat(victim, span_warning("That pod did something to you..."))
+ victim.adjust_dizzy(3.5 SECONDS)
+ time_until_next = 6.5 SECONDS
+ if(VICTIM_EXPERIENCE_SECOND_HIT)
+ to_chat(victim, span_warning("Your head pounds... It feels like it's going to burst out your skull!"))
+ victim.flash_act()
+ victim.adjust_confusion(2 SECONDS)
+ victim.adjust_eye_blur(3 SECONDS)
+ time_until_next = 3 SECONDS
+ if(VICTIM_EXPERIENCE_THIRD_HIT)
+ to_chat(victim, span_warning("Your head pounds..."))
+ time_until_next = 10 SECONDS
+ if(VICTIM_EXPERIENCE_LAST_HIT)
+ victim.flash_act()
+ victim.Unconscious(200)
+ to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Your mind held many valuable secrets - \
+ we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
+ so it's only a matter of time before we ship you back...\""))
+ victim.adjust_eye_blur(10 SECONDS)
+ victim.adjust_dizzy(1.5 SECONDS)
+ victim.adjust_confusion(2 SECONDS)
+
+ level++ //move onto the next level.
+ if(time_until_next)
+ addtimer(CALLBACK(src, PROC_REF(handle_victim_experience), victim, level), time_until_next)
+
+#undef VICTIM_EXPERIENCE_START
+#undef VICTIM_EXPERIENCE_FIRST_HIT
+#undef VICTIM_EXPERIENCE_SECOND_HIT
+#undef VICTIM_EXPERIENCE_THIRD_HIT
+#undef VICTIM_EXPERIENCE_LAST_HIT
+
+// We're returning the victim
+/datum/syndicate_contract/proc/return_victim(mob/living/victim)
+ var/list/possible_drop_loc = list()
+
+ for(var/turf/possible_drop in contract.dropoff.contents)
+ if(!isspaceturf(possible_drop) && !isclosedturf(possible_drop))
+ if(!possible_drop.is_blocked_turf())
+ possible_drop_loc.Add(possible_drop)
+
+ if(!possible_drop_loc.len)
+ to_chat(victim, span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \
+ be able to handle our pod... You will die here instead.\""))
+ if(iscarbon(victim))
+ var/mob/living/carbon/carbon_victim = victim
+ if(carbon_victim.can_heartattack())
+ carbon_victim.set_heartattack(TRUE)
+ return
+
+ var/pod_rand_loc = rand(1, possible_drop_loc.len)
+ var/obj/structure/closet/supplypod/return_pod = new()
+ return_pod.bluespace = TRUE
+ return_pod.explosionSize = list(0,0,0,0)
+ return_pod.style = STYLE_SYNDICATE
+
+ do_sparks(8, FALSE, victim)
+ victim.visible_message(span_notice("[victim] vanishes..."))
+
+ for(var/datum/weakref/belonging_ref in victim_belongings)
+ var/obj/item/belonging = belonging_ref.resolve()
+ if(!belonging)
+ continue
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ //So all they're left with are shoes and uniform.
+ if(belonging == human_victim.w_uniform)
+ continue
+ if(belonging == human_victim.shoes)
+ continue
+ belonging.forceMove(return_pod)
+
+ for(var/obj/item/W in victim_belongings)
+ W.forceMove(return_pod)
+
+ victim.forceMove(return_pod)
+
+ victim.flash_act()
+ victim.adjust_eye_blur(3 SECONDS)
+ victim.adjust_dizzy(3.5 SECONDS)
+ victim.adjust_confusion(2 SECONDS)
+
+ new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 31eedd09cce..d338abca641 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -17,6 +17,10 @@
can_assign_self_objectives = TRUE
default_custom_objective = "Perform an overcomplicated heist on valuable Nanotrasen assets."
hardcore_random_bonus = TRUE
+
+ ///The flag of uplink that this traitor is supposed to have.
+ var/uplink_flag_given = UPLINK_TRAITORS
+
var/give_objectives = TRUE
/// Whether to give secondary objectives to the traitor, which aren't necessary but can be completed for a progression and TC boost.
var/give_secondary_objectives = TRUE
@@ -53,6 +57,7 @@
// There will still be a timelock on uplink items
name = "\improper Infiltrator"
give_secondary_objectives = FALSE
+ uplink_flag_given = UPLINK_TRAITORS | UPLINK_INFILTRATORS
/datum/antagonist/traitor/infiltrator/sleeper_agent
name = "\improper Syndicate Sleeper Agent"
@@ -75,6 +80,7 @@
uplink.uplink_handler = uplink_handler
else
uplink_handler = uplink.uplink_handler
+ uplink_handler.uplink_flag = uplink_flag_given
uplink_handler.primary_objectives = objectives
uplink_handler.has_progression = TRUE
SStraitor.register_uplink_handler(uplink_handler)
@@ -351,6 +357,8 @@
result += objectives_text
if(uplink_handler)
+ if (uplink_handler.contractor_hub)
+ result += contractor_round_end()
result += " The traitor had a total of [DISPLAY_PROGRESSION(uplink_handler.progression_points)] Reputation and [uplink_handler.telecrystals] Unused Telecrystals."
var/special_role_text = lowertext(name)
@@ -363,6 +371,23 @@
return result.Join(" ")
+///Tells how many contracts have been completed.
+/datum/antagonist/traitor/proc/contractor_round_end()
+ var/completed_contracts = uplink_handler.contractor_hub.contracts_completed
+ var/tc_total = uplink_handler.contractor_hub.contract_TC_payed_out + uplink_handler.contractor_hub.contract_TC_to_redeem
+
+ var/datum/antagonist/traitor/contractor_support/contractor_support_unit = uplink_handler.contractor_hub.contractor_teammate
+
+ if(completed_contracts <= 0)
+ return
+ var/plural_check = "contract"
+ if (completed_contracts > 1)
+ plural_check = "contracts"
+ var/sent_data = "Completed [span_greentext("[completed_contracts]")] [plural_check] for a total of [span_greentext("[tc_total] TC")]! "
+ if(contractor_support_unit)
+ sent_data += "[contractor_support_unit.owner.key] played [contractor_support_unit.owner.current.name], their contractor support unit. "
+ return sent_data
+
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index d74b586efae..732230f9248 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -227,7 +227,7 @@
if(sent_mob.mind)
ADD_TRAIT(sent_mob.mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC)
- for(var/obj/item/belonging in gather_belongings(sent_mob))
+ for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
@@ -298,7 +298,7 @@
do_sparks(8, FALSE, sent_mob)
sent_mob.visible_message(span_notice("[sent_mob] vanishes!"))
- for(var/obj/item/belonging in gather_belongings(sent_mob))
+ for(var/obj/item/belonging in sent_mob.gather_belongings())
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
sent_mob.dropItemToGround(belonging) // No souvenirs, except shoes and t-shirts
@@ -317,10 +317,3 @@
sent_mob.dna.species.give_important_for_life(sent_mob) // so plasmamen do not get left for dead
new /obj/effect/pod_landingzone(pick(possible_turfs), return_pod)
-
-/// Returns a list of things that the provided mob has which we would rather that they do not have
-/datum/traitor_objective/target_player/kidnapping/proc/gather_belongings(mob/living/carbon/human/kidnapee)
- var/list/belongings = kidnapee.get_all_gear()
- for (var/obj/item/implant/storage/internal_bag in kidnapee.implants)
- belongings += internal_bag.contents
- return belongings
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index 06ce9667035..5d164d8d3d9 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -52,6 +52,8 @@
var/datum/callback/can_replace_objectives
/// Callback which performs that operation
var/datum/callback/replace_objectives
+ ///Reference to a contractor hub that the infiltrator can run, if they purchase it.
+ var/datum/contractor_hub/contractor_hub
/datum/uplink_handler/New()
. = ..()
@@ -73,6 +75,8 @@
/// Checks for uplink flags as well as items restricted to roles and species
/datum/uplink_handler/proc/check_if_restricted(datum/uplink_item/to_purchase)
+ if(!to_purchase.can_be_bought(src))
+ return FALSE
if((to_purchase in extra_purchasable))
return TRUE
if(!(to_purchase.purchasable_from & uplink_flag))
@@ -99,7 +103,7 @@
return FALSE
var/current_stock = item_stock[to_purchase.stock_key]
- var/stock = current_stock != null? current_stock : INFINITY
+ var/stock = current_stock != null ? current_stock : INFINITY
if(telecrystals < to_purchase.cost || stock <= 0 || not_enough_reputation(to_purchase))
return FALSE
diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm
index 3ed69267d75..13e3bfa718f 100644
--- a/code/modules/client/preferences/middleware/antags.dm
+++ b/code/modules/client/preferences/middleware/antags.dm
@@ -118,7 +118,6 @@
ROLE_GLITCH = /datum/antagonist/bitrunning_glitch,
ROLE_FUGITIVE = /datum/antagonist/fugitive,
ROLE_LONE_OPERATIVE = /datum/antagonist/nukeop/lone,
- ROLE_DRIFTING_CONTRACTOR = /datum/antagonist/contractor, //SKYRAT EDIT
ROLE_SENTIENCE = /datum/antagonist/sentient_creature,
)
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index 0b42fd23212..006c920d4b9 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -243,3 +243,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
/obj/effect/extraction_holder/singularity_pull()
return
+
+/obj/item/extraction_pack/syndicate
+ name = "syndicate fulton extraction pack"
+ can_use_indoors = TRUE
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index bca29039064..e261c8ddb01 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -557,14 +557,21 @@
..() //Don't redraw hands until we have organs for them
//GetAllContents that is reasonable and not stupid
-/mob/living/carbon/proc/get_all_gear()
+/mob/living/proc/get_all_gear()
var/list/processing_list = get_equipped_items(include_pockets = TRUE, include_accessories = TRUE) + held_items
list_clear_nulls(processing_list) // handles empty hands
var/i = 0
- while(i < length(processing_list) )
+ while(i < length(processing_list))
var/atom/A = processing_list[++i]
if(A.atom_storage)
var/list/item_stuff = list()
A.atom_storage.return_inv(item_stuff)
processing_list += item_stuff
return processing_list
+
+/// Returns a list of things that the provided mob has, including any storage-capable implants.
+/mob/living/proc/gather_belongings()
+ var/list/belongings = get_all_gear()
+ for (var/obj/item/implant/storage/internal_bag in implants)
+ belongings += internal_bag.contents
+ return belongings
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 0492cb71489..9527e82cfea 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -48,7 +48,7 @@
///Flag of the type of device the modular computer is, deciding what types of apps it can run.
var/hardware_flag = NONE
-// Options: PROGRAM_ALL | PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET
+// Options: PROGRAM_ALL | PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_PDA
///The theme, used for the main menu and file browser apps.
var/device_theme = PDA_THEME_NTOS
diff --git a/code/modules/modular_computers/computers/item/disks/unique_disks.dm b/code/modules/modular_computers/computers/item/disks/unique_disks.dm
index 144fa52c65a..6d8614f56ac 100644
--- a/code/modules/modular_computers/computers/item/disks/unique_disks.dm
+++ b/code/modules/modular_computers/computers/item/disks/unique_disks.dm
@@ -6,3 +6,6 @@
/obj/item/computer_disk/syndicate/camera_app
starting_programs = list(/datum/computer_file/program/secureye/syndicate)
+
+/obj/item/computer_disk/syndicate/contractor
+ starting_programs = list(/datum/computer_file/program/contract_uplink)
diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm
index 0c027b5f30b..0c2580a32d4 100644
--- a/code/modules/modular_computers/computers/item/pda.dm
+++ b/code/modules/modular_computers/computers/item/pda.dm
@@ -18,7 +18,7 @@
icon_state_menu = "menu"
max_capacity = 64
allow_chunky = TRUE
- hardware_flag = PROGRAM_TABLET
+ hardware_flag = PROGRAM_PDA
max_idle_programs = 2
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT
@@ -276,6 +276,19 @@
if(msg)
msg.invisible = TRUE
+/obj/item/modular_computer/pda/syndicate_contract_uplink
+ name = "contractor tablet"
+ device_theme = PDA_THEME_SYNDICATE
+ icon_state_menu = "contractor-assign"
+ comp_light_luminosity = 6.3
+ has_pda_programs = FALSE
+ greyscale_config = /datum/greyscale_config/tablet/stripe_double
+ greyscale_colors = "#696969#000000#FFA500"
+
+ starting_programs = list(
+ /datum/computer_file/program/contract_uplink,
+ )
+
/**
* Silicon PDA
*
@@ -368,7 +381,7 @@
return robotact
qdel(robotact)
robotact = null
- CRASH("Cyborg [silicon_owner]'s tablet hard drive rejected recieving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
+ CRASH("Cyborg [silicon_owner]'s tablet hard drive rejected receiving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
//Makes the light settings reflect the borg's headlamp settings
/obj/item/modular_computer/pda/silicon/cyborg/ui_data(mob/user)
diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md
index 925f2f5668b..ba11790f2fd 100644
--- a/code/modules/modular_computers/documentation.md
+++ b/code/modules/modular_computers/documentation.md
@@ -44,7 +44,7 @@ This is how the base program is setup. the rest is mostly tgui stuff. I'll use t
/// ditto but on emagged syndie net. Use this for antag programs
available_on_syndinet = 0
- /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination)
+ /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_PDA combination)
/// or PROGRAM_ALL. Use this to limit what kind of machines can run the
/// program. For example, comms program should be limited to consoles and laptops.
usage_flags = PROGRAM_ALL
diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm
index d050bd804b3..931e8d283be 100644
--- a/code/modules/modular_computers/file_system/program.dm
+++ b/code/modules/modular_computers/file_system/program.dm
@@ -28,7 +28,7 @@
var/requires_ntnet = FALSE
/// NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc.
var/ntnet_status = 1
- /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
+ /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_PDA combination) or PROGRAM_ALL
var/usage_flags = PROGRAM_ALL
/// Whether the program can be downloaded from NTNet. Set to FALSE to disable.
var/available_on_ntnet = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm b/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm
new file mode 100644
index 00000000000..f1dbc302e61
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/antagonist/contractor_program.dm
@@ -0,0 +1,173 @@
+/datum/computer_file/program/contract_uplink
+ filename = "contractor uplink"
+ filedesc = "Syndicate Contractor Uplink"
+ extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
+ category = PROGRAM_CATEGORY_MISC
+ program_icon_state = "contractor-assign"
+ program_icon = "tasks"
+ size = 10
+
+ requires_ntnet = FALSE
+ available_on_ntnet = FALSE
+ available_on_syndinet = FALSE
+ usage_flags = PROGRAM_PDA //this is all we've got sprites for :sob:
+ unique_copy = TRUE
+ undeletable = TRUE
+ tgui_id = "SyndicateContractor"
+
+ ///The traitor datum stored on the program. Starts off as null and is set by the player.
+ var/datum/antagonist/traitor/traitor_data
+ ///The error screen sent to the UI so they can show the player.
+ var/error = ""
+ ///Boolean on whether the UI is on the Information screen.
+ var/info_screen = TRUE
+ ///Boolean on whether the program is being loaded for the first time, for a unique screen animation.
+ var/first_load = TRUE
+
+/datum/computer_file/program/contract_uplink/clone()
+ var/datum/computer_file/program/contract_uplink/temp = ..()
+ temp.traitor_data = traitor_data
+ return temp
+
+/datum/computer_file/program/contract_uplink/Destroy(force)
+ traitor_data = null
+ return ..()
+
+/datum/computer_file/program/contract_uplink/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ var/mob/living/user = usr
+
+ switch(action)
+ if("PRG_contract-accept")
+ var/contract_id = text2num(params["contract_id"])
+ traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE
+ traitor_data.uplink_handler.contractor_hub.current_contract = traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id]
+ program_icon_state = "contractor-contract"
+ return TRUE
+
+ if("PRG_login")
+ var/datum/antagonist/traitor/traitor_user = user.mind.has_antag_datum(/datum/antagonist/traitor)
+ if(!traitor_user)
+ error = "UNAUTHORIZED USER"
+ return TRUE
+
+ traitor_data = traitor_user
+ if(!traitor_data.uplink_handler.contractor_hub)
+ traitor_data.uplink_handler.contractor_hub = new
+ traitor_data.uplink_handler.contractor_hub.create_contracts(traitor_user.owner)
+ user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE)
+ program_icon_state = "contractor-contractlist"
+ return TRUE
+
+ if("PRG_call_extraction")
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.handle_extraction(user))
+ user.playsound_local(user, 'sound/effects/confirmdropoff.ogg', 100, TRUE)
+ traitor_data.uplink_handler.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING
+
+ program_icon_state = "contractor-extracted"
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ error = "Either both you or your target aren't at the dropoff location, or the pod hasn't got a valid place to land. Clear space, or make sure you're both inside."
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible."
+
+ return TRUE
+ if("PRG_contract_abort")
+ var/contract_id = traitor_data.uplink_handler.contractor_hub.current_contract.id
+
+ traitor_data.uplink_handler.contractor_hub.current_contract = null
+ traitor_data.uplink_handler.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
+
+ program_icon_state = "contractor-contractlist"
+
+ return TRUE
+ if("PRG_redeem_TC")
+ if (traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem)
+ var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user), traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(H.put_in_hands(crystals))
+ to_chat(H, span_notice("Your payment materializes into your hands!"))
+ else
+ to_chat(user, span_notice("Your payment materializes onto the floor."))
+
+ traitor_data.uplink_handler.contractor_hub.contract_TC_payed_out += traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem
+ traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem = 0
+ return TRUE
+ else
+ user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
+ return TRUE
+ if ("PRG_clear_error")
+ error = ""
+ return TRUE
+ if("PRG_set_first_load_finished")
+ first_load = FALSE
+ return TRUE
+ if("PRG_toggle_info")
+ info_screen = !info_screen
+ return TRUE
+
+/datum/computer_file/program/contract_uplink/ui_data(mob/user)
+ var/list/data = list()
+
+ data["first_load"] = first_load
+ data["logged_in"] = !!traitor_data
+ data["station_name"] = GLOB.station_name
+ data["info_screen"] = info_screen
+ data["error"] = error
+
+ if(!traitor_data)
+ data["ongoing_contract"] = FALSE
+ data["extraction_enroute"] = FALSE
+ update_computer_icon()
+ return data
+
+ data["ongoing_contract"] = !!traitor_data.uplink_handler.contractor_hub.current_contract
+ if(traitor_data.uplink_handler.contractor_hub.current_contract)
+ program_icon_state = "contractor-contract"
+ if (traitor_data.uplink_handler.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
+ data["extraction_enroute"] = TRUE
+ program_icon_state = "contractor-extracted"
+ else
+ data["extraction_enroute"] = FALSE
+ var/turf/curr = get_turf(user)
+ var/turf/dropoff_turf
+ data["current_location"] = "[get_area_name(curr, TRUE)]"
+ for (var/turf/content in traitor_data.uplink_handler.contractor_hub.current_contract.contract.dropoff.contents)
+ if (isturf(content))
+ dropoff_turf = content
+ break
+ var/direction
+ if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only
+ direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful.
+ if(get_area(user) == traitor_data.uplink_handler.contractor_hub.current_contract.contract.dropoff)
+ direction = "LOCATION CONFIRMED"
+ else
+ direction = "???"
+ data["dropoff_direction"] = direction
+ data["redeemable_tc"] = traitor_data.uplink_handler.contractor_hub.contract_TC_to_redeem
+ data["earned_tc"] = traitor_data.uplink_handler.contractor_hub.contract_TC_payed_out
+ data["contracts_completed"] = traitor_data.uplink_handler.contractor_hub.contracts_completed
+ for (var/datum/syndicate_contract/contract in traitor_data.uplink_handler.contractor_hub.assigned_contracts)
+ if(!contract.contract)
+ stack_trace("Syndiate contract with null contract objective found in [traitor_data.owner]'s contractor hub!")
+ contract.status = CONTRACT_STATUS_ABORTED
+ continue
+ data["contracts"] += list(list(
+ "target" = contract.contract.target,
+ "target_rank" = contract.target_rank,
+ "payout" = contract.contract.payout,
+ "payout_bonus" = contract.contract.payout_bonus,
+ "dropoff" = contract.contract.dropoff,
+ "id" = contract.id,
+ "status" = contract.status,
+ "message" = contract.wanted_message,
+ ))
+
+ update_computer_icon()
+ return data
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index 6264531428f..d0044433919 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -57,7 +57,7 @@
var/list/data = list()
var/turf/turf = get_turf(computer)
data["atmozphereMode"] = atmozphere_mode
- data["clickAtmozphereCompatible"] = (computer.hardware_flag & PROGRAM_TABLET)
+ data["clickAtmozphereCompatible"] = (computer.hardware_flag & PROGRAM_PDA)
switch (atmozphere_mode) //Null air wont cause errors, don't worry.
if(ATMOZPHERE_SCAN_ENV)
var/datum/gas_mixture/air = turf?.return_air()
@@ -74,7 +74,7 @@
atmozphere_mode = ATMOZPHERE_SCAN_ENV
UnregisterSignal(computer, COMSIG_ITEM_ATTACK_SELF_SECONDARY)
return TRUE
- if(!(computer.hardware_flag & PROGRAM_TABLET))
+ if(!(computer.hardware_flag & PROGRAM_PDA))
computer.say("Device incompatible for scanning objects!")
return FALSE
atmozphere_mode = ATMOZPHERE_SCAN_CLICK
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index d2133697194..68b251f3d19 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -5,7 +5,7 @@
program_icon_state = "request"
extended_desc = "Nanotrasen Internal Requisition Network interface for supply purchasing using a department budget account."
requires_ntnet = TRUE
- usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
+ usage_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 10
tgui_id = "NtosCargo"
///Are you actually placing orders with it?
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
index 50276a1bcd8..a6ef5d08459 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/camera.dm
@@ -5,7 +5,7 @@
category = PROGRAM_CATEGORY_MISC
extended_desc = "This program allows the taking of pictures."
size = 4
- usage_flags = PROGRAM_TABLET
+ usage_flags = PROGRAM_PDA
tgui_id = "NtosCamera"
program_icon = "camera"
diff --git a/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm b/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
index 4c332d0d7d8..e96ae083126 100644
--- a/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
+++ b/code/modules/modular_computers/file_system/programs/maintenance/phys_scanner.dm
@@ -4,7 +4,7 @@
category = PROGRAM_CATEGORY_MISC
extended_desc = "This program allows the tablet to scan physical objects and display a data output."
size = 2
- usage_flags = PROGRAM_TABLET
+ usage_flags = PROGRAM_PDA
tgui_id = "NtosPhysScanner"
program_icon = "barcode"
/// Information from the last scanned person, to display on the app.
diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
index 637b2b33f16..1001a12ba92 100644
--- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
+++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
@@ -15,7 +15,7 @@
undeletable = TRUE // It comes by default in tablets, can't be downloaded, takes no space and should obviously not be able to be deleted.
header_program = TRUE
available_on_ntnet = FALSE
- usage_flags = PROGRAM_TABLET
+ usage_flags = PROGRAM_PDA
ui_header = "ntnrc_idle.gif"
tgui_id = "NtosMessenger"
program_icon = "comment-alt"
diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm
index 7c074040cef..f79ecf26d38 100644
--- a/code/modules/modular_computers/file_system/programs/radar.dm
+++ b/code/modules/modular_computers/file_system/programs/radar.dm
@@ -6,7 +6,7 @@
program_icon_state = "radarntos"
requires_ntnet = TRUE
available_on_ntnet = FALSE
- usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
+ usage_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 5
tgui_id = "NtosRadar"
///List of trackable entities. Updated by the scan() proc.
diff --git a/code/modules/modular_computers/file_system/programs/records.dm b/code/modules/modular_computers/file_system/programs/records.dm
index 12c682f5626..e8d61cda2ea 100644
--- a/code/modules/modular_computers/file_system/programs/records.dm
+++ b/code/modules/modular_computers/file_system/programs/records.dm
@@ -7,7 +7,7 @@
program_icon_state = "crew"
tgui_id = "NtosRecords"
size = 4
- usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
+ usage_flags = PROGRAM_PDA | PROGRAM_LAPTOP
available_on_ntnet = FALSE
detomatix_resistance = DETOMATIX_RESIST_MINOR
diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm
index e64691ccba2..79c3ca93112 100644
--- a/code/modules/modular_computers/file_system/programs/robotact.dm
+++ b/code/modules/modular_computers/file_system/programs/robotact.dm
@@ -8,7 +8,7 @@
requires_ntnet = FALSE
available_on_ntnet = FALSE
undeletable = TRUE
- usage_flags = PROGRAM_TABLET
+ usage_flags = PROGRAM_PDA
size = 5
tgui_id = "NtosRobotact"
program_icon = "terminal"
diff --git a/code/modules/modular_computers/file_system/programs/signalcommander.dm b/code/modules/modular_computers/file_system/programs/signalcommander.dm
index e8140b62b17..81b95f1f5ba 100644
--- a/code/modules/modular_computers/file_system/programs/signalcommander.dm
+++ b/code/modules/modular_computers/file_system/programs/signalcommander.dm
@@ -7,7 +7,7 @@
size = 2
tgui_id = "NtosSignaler"
program_icon = "satellite-dish"
- usage_flags = PROGRAM_TABLET | PROGRAM_LAPTOP
+ usage_flags = PROGRAM_PDA | PROGRAM_LAPTOP
///What is the saved signal frequency?
var/signal_frequency = FREQ_SIGNALER
/// What is the saved signal code?
diff --git a/code/modules/modular_computers/file_system/programs/skill_tracker.dm b/code/modules/modular_computers/file_system/programs/skill_tracker.dm
index c68cffb3374..787aeb9f910 100644
--- a/code/modules/modular_computers/file_system/programs/skill_tracker.dm
+++ b/code/modules/modular_computers/file_system/programs/skill_tracker.dm
@@ -7,7 +7,7 @@
size = 2
tgui_id = "NtosSkillTracker"
program_icon = "medal"
- usage_flags = PROGRAM_TABLET // Must be a handheld device to read read your chakras or whatever
+ usage_flags = PROGRAM_PDA // Must be a handheld device to read read your chakras or whatever
/datum/computer_file/program/skill_tracker/ui_data(mob/user)
var/list/data = list()
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 2c060b6ff98..32783504fe8 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -137,6 +137,10 @@
to_chat(user, span_boldnotice("[A] materializes onto the floor!"))
return A
+///For special overrides if an item can be bought or not.
+/datum/uplink_item/proc/can_be_bought(datum/uplink_handler/source)
+ return TRUE
+
/datum/uplink_category/discounts
name = "Discounted Gear"
weight = -1
diff --git a/code/modules/uplink/uplink_items/contractor.dm b/code/modules/uplink/uplink_items/contractor.dm
new file mode 100644
index 00000000000..8a24be00dd2
--- /dev/null
+++ b/code/modules/uplink/uplink_items/contractor.dm
@@ -0,0 +1,107 @@
+/datum/uplink_category/contractor
+ name = "Contractor"
+ weight = 10
+
+/datum/uplink_item/bundles_tc/contract_kit
+ name = "Contract Kit"
+ desc = "The Syndicate have offered you the chance to become a contractor, take on kidnapping contracts for TC \
+ and cash payouts. Upon purchase, you'll be granted your own contract uplink embedded within the supplied \
+ tablet computer. Additionally, you'll be granted standard contractor gear to help with your mission - \
+ comes supplied with the tablet, specialised space suit, chameleon jumpsuit and mask, agent card, \
+ specialised contractor baton, and three randomly selected low cost items. \
+ Can include otherwise unobtainable items."
+ item = /obj/item/storage/box/syndicate/contract_kit
+ category = /datum/uplink_category/contractor
+ cost = 20
+ purchasable_from = UPLINK_INFILTRATORS
+
+/datum/uplink_item/bundles_tc/contract_kit/purchase(mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ . = ..()
+ for(var/uplink_items in subtypesof(/datum/uplink_item/contractor))
+ var/datum/uplink_item/uplink_item = new uplink_items
+ uplink_handler.extra_purchasable += uplink_item
+
+/datum/uplink_item/contractor
+ restricted = TRUE
+ category = /datum/uplink_category/contractor
+ purchasable_from = NONE //they will be added to extra_purchasable
+
+//prevents buying contractor stuff before you make an account.
+/datum/uplink_item/contractor/can_be_bought(datum/uplink_handler/uplink_handler)
+ if(!uplink_handler.contractor_hub)
+ return FALSE
+ return ..()
+
+/datum/uplink_item/contractor/reroll
+ name = "Contract Reroll"
+ desc = "Request a reroll of your current contract list. Will generate a new target, \
+ payment, and dropoff for the contracts you currently have available."
+ item = /obj/effect/gibspawner/generic
+ limited_stock = 2
+ cost = 0
+
+/datum/uplink_item/contractor/reroll/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ //We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
+ var/list/new_target_list = list()
+ for(var/datum/syndicate_contract/contract_check in uplink_handler.contractor_hub.assigned_contracts)
+ if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
+ if (contract_check.contract.target)
+ new_target_list.Add(contract_check.contract.target)
+ continue
+
+ //Reroll contracts without duplicates
+ for(var/datum/syndicate_contract/rerolling_contract in uplink_handler.contractor_hub.assigned_contracts)
+ if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
+ continue
+
+ rerolling_contract.generate(new_target_list)
+ new_target_list.Add(rerolling_contract.contract.target)
+
+ //Set our target list with the new set we've generated.
+ uplink_handler.contractor_hub.assigned_targets = new_target_list
+ return source //for log icon
+
+/datum/uplink_item/contractor/pinpointer
+ name = "Contractor Pinpointer"
+ desc = "A pinpointer that finds targets even without active suit sensors. \
+ Due to taking advantage of an exploit within the system, it can't pinpoint \
+ to the same accuracy as the traditional models. \
+ Becomes permanently locked to the user that first activates it."
+ item = /obj/item/pinpointer/crew/contractor
+ limited_stock = 2
+ cost = 1
+
+/datum/uplink_item/contractor/extraction_kit
+ name = "Fulton Extraction Kit"
+ desc = "For getting your target across the station to those difficult dropoffs. \
+ Place the beacon somewhere secure, and link the pack. \
+ Activating the pack on your target will send them over to the beacon - \
+ make sure they're not just going to run away though!"
+ item = /obj/item/storage/box/contractor/fulton_extraction
+ limited_stock = 1
+ cost = 1
+
+/datum/uplink_item/contractor/partner
+ name = "Reinforcements"
+ desc = "Upon purchase we'll contact available units in the area. Should there be an agent free, \
+ we'll send them down to assist you immediately. If no units are free, we give a full refund."
+ item = /obj/effect/gibspawner/generic
+ limited_stock = 1
+ cost = 2
+
+/datum/uplink_item/contractor/partner/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
+ to_chat(user, span_notice("The uplink vibrates quietly, connecting to nearby agents..."))
+ var/list/candidates = poll_ghost_candidates(
+ question = "Do you want to play as the Contractor Support Unit for [user.real_name]?",
+ jobban_type = ROLE_TRAITOR,
+ be_special_flag = ROLE_TRAITOR,
+ poll_time = 10 SECONDS,
+ ignore_category = POLL_IGNORE_CONTRACTOR_SUPPORT,
+ )
+ if(!LAZYLEN(candidates))
+ to_chat(user, span_notice("No available agents at this time, please try again later."))
+ limited_stock++
+ return //bobux no icon
+ var/mob/dead/observer/selected_player = pick(candidates)
+ uplink_handler.contractor_hub.contractor_teammate = spawn_contractor_partner(user, selected_player.key)
+ return source //for log icon
diff --git a/code/modules/uplink/uplink_items/stealthy.dm b/code/modules/uplink/uplink_items/stealthy.dm
index 36cb47cc28f..6ba88eda43e 100644
--- a/code/modules/uplink/uplink_items/stealthy.dm
+++ b/code/modules/uplink/uplink_items/stealthy.dm
@@ -101,4 +101,5 @@
item = /obj/item/melee/baton/telescopic/contractor_baton
cost = 12
surplus = 50
+ limited_stock = 1
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
diff --git a/icons/obj/antags/contractor_tablet.dmi b/icons/obj/antags/contractor_tablet.dmi
deleted file mode 100644
index ae3cb579e2b..00000000000
Binary files a/icons/obj/antags/contractor_tablet.dmi and /dev/null differ
diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi
index b43be78e717..6dfe29add9e 100644
Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ
diff --git a/modular_skyrat/master_files/code/game/gamemodes/objective.dm b/modular_skyrat/master_files/code/game/gamemodes/objective.dm
index 53b6dc7e937..a9867d9933f 100644
--- a/modular_skyrat/master_files/code/game/gamemodes/objective.dm
+++ b/modular_skyrat/master_files/code/game/gamemodes/objective.dm
@@ -1,3 +1,7 @@
+/datum/objective
+ /// Should space areas count when finding a target?
+ var/count_space_areas = TRUE
+
// For modularity, we hook into the update_explanation_text to be sure we have a target to register.
/datum/objective/assassinate/update_explanation_text()
RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(register_target_death))
diff --git a/modular_skyrat/modules/clock_cult/code/actions/_action.dm b/modular_skyrat/modules/clock_cult/code/actions/_action.dm
index da48b3712cf..bbc1347d3d3 100644
--- a/modular_skyrat/modules/clock_cult/code/actions/_action.dm
+++ b/modular_skyrat/modules/clock_cult/code/actions/_action.dm
@@ -20,7 +20,7 @@
return ..()
-/datum/action/innate/clockcult/quick_bind/Grant(mob/living/recieving_mob)
+/datum/action/innate/clockcult/quick_bind/Grant(mob/living/receiving_mob)
name = scripture.name
desc = scripture.tip
button_icon_state = scripture.button_icon_state
@@ -28,7 +28,7 @@
if(scripture.power_cost)
desc += " Draws [scripture.power_cost]W from the ark per use."
- return ..(recieving_mob)
+ return ..(receiving_mob)
/datum/action/innate/clockcult/quick_bind/Remove(mob/losing_mob)
var/obj/item/clockwork/clockwork_slab/activation_slab = slab_weakref.resolve()
diff --git a/modular_skyrat/modules/contractor/code/datums/contract.dm b/modular_skyrat/modules/contractor/code/datums/contract.dm
deleted file mode 100644
index 8a5f702b6c5..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contract.dm
+++ /dev/null
@@ -1,318 +0,0 @@
-#define RANSOM_LOWER 75
-#define RANSOM_UPPER 150
-#define CONTRACTOR_RANSOM_CUT 0.35
-
-/datum/syndicate_contract
- /// Unique ID tied to the contract
- var/id = 0
- /// If the contract is the contractor's current one
- var/status = CONTRACT_STATUS_INACTIVE
- /// Reference to the objective datum
- var/datum/objective/contract/contract = new()
- /// Target's job
- var/target_rank
- /// A number in multiples of 100, anywhere from 7500 credits to 15000, station cost when someone is kidnapped
- var/ransom = 0
- /// TC payout size, will be small, medium, or large.
- var/payout_type
- /// Mad-libs style wanted message, just flavor.
- var/wanted_message
- /// List of the items the victim had on them prior to kidnapping.
- var/list/victim_belongings = list()
-
-/datum/syndicate_contract/New(contract_owner, blacklist, type = CONTRACT_PAYOUT_SMALL)
- contract.owner = contract_owner
- payout_type = type
-
- generate(blacklist)
-
-/// Generation of the contract, called on New()
-/datum/syndicate_contract/proc/generate(blacklist)
- contract.find_target(null, blacklist)
-
- var/datum/record/crew/record
- if (contract.target)
- record = find_record(contract.target.name)
-
- if (record)
- target_rank = record.rank
- else
- target_rank = "Unknown"
-
- if (payout_type == CONTRACT_PAYOUT_LARGE)
- contract.payout_bonus = rand(9,13)
- else if (payout_type == CONTRACT_PAYOUT_MEDIUM)
- contract.payout_bonus = rand(6,8)
- else
- contract.payout_bonus = rand(2,4)
-
- contract.payout = rand(1, 2)
- contract.generate_dropoff()
-
- ransom = 100 * rand(RANSOM_LOWER, RANSOM_UPPER)
-
- var/base = pick_list(WANTED_FILE, "basemessage")
- var/verb_string = pick_list(WANTED_FILE, "verb")
- var/noun = pick_list_weighted(WANTED_FILE, "noun")
- var/location = pick_list_weighted(WANTED_FILE, "location")
- wanted_message = "[base] [verb_string] [noun] [location]."
-
-/// Handler to find a valid turn and launch victim collector
-/datum/syndicate_contract/proc/handle_extraction(mob/living/user)
- if(!(contract.target && contract.dropoff_check(user, contract.target.current)))
- return FALSE
-
- var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
-
- if(!free_location)
- return FALSE
-
- launch_extraction_pod(free_location)
- return TRUE
-
-
-/// Launch the pod to collect our victim.
-/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
- var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
- empty_pod.contract_hub = contract.owner?.opposing_force?.contractor_hub
- empty_pod.tied_contract = src
- empty_pod.recieving = TRUE
-
- RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, PROC_REF(enter_check))
-
- empty_pod.stay_after_drop = TRUE
- empty_pod.reversing = TRUE
- empty_pod.explosionSize = list(0,0,0,1)
- empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
-
- new /obj/effect/pod_landingzone(empty_pod_turf, empty_pod)
-
-/datum/syndicate_contract/proc/enter_check(datum/source, mob/living/sent_mob)
- SIGNAL_HANDLER
- if(!istype(source, /obj/structure/closet/supplypod/extractionpod))
- return
- if(!istype(sent_mob))
- return
- var/datum/opposing_force/opfor_data = contract.owner.opposing_force
-
- if (sent_mob == contract.target.current)
- opfor_data.contractor_hub.contract_TC_to_redeem += contract.payout
- opfor_data.contractor_hub.contracts_completed += 1
-
- if (sent_mob.stat != DEAD)
- opfor_data.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
-
- status = CONTRACT_STATUS_COMPLETE
-
- if (opfor_data.contractor_hub.current_contract == src)
- opfor_data.contractor_hub.current_contract = null
-
- opfor_data.contractor_hub.contract_rep += 2
- else
- status = CONTRACT_STATUS_ABORTED // Sending a sent_mob that wasn't even yours is as good as just aborting it
-
- if (opfor_data.contractor_hub.current_contract == src)
- opfor_data.contractor_hub.current_contract = null
-
- if(iscarbon(sent_mob))
- for(var/obj/item/sent_mob_item in sent_mob)
- if (ishuman(sent_mob))
- var/mob/living/carbon/human/sent_mob_human = sent_mob
- if(sent_mob_item == sent_mob_human.w_uniform)
- continue //So all they're left with are shoes and uniform.
- if(sent_mob_item == sent_mob_human.shoes)
- continue
-
-
- sent_mob.transferItemToLoc(sent_mob_item)
- victim_belongings.Add(sent_mob_item)
-
- var/obj/structure/closet/supplypod/extractionpod/pod = source
- pod.recieving = FALSE
-
- // Handle the pod returning
- pod.startExitSequence(pod)
-
- if (ishuman(sent_mob))
- var/mob/living/carbon/human/sent_mob_human = sent_mob
-
- // After we remove items, at least give them what they need to live.
- sent_mob_human.dna.species.give_important_for_life(sent_mob_human)
-
- // After pod is sent we start the victim narrative/heal.
- INVOKE_ASYNC(src, PROC_REF(handle_victim_experience), sent_mob)
-
- // This is slightly delayed because of the sleep calls above to handle the narrative.
- // We don't want to tell the station instantly.
- var/points_to_check
- var/datum/bank_account/bank = SSeconomy.get_dep_account(ACCOUNT_CAR)
- if(bank)
- points_to_check = bank.account_balance
- if(points_to_check >= ransom)
- bank.adjust_money(-ransom)
- else
- bank.adjust_money(-points_to_check)
-
- priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
- As is policy we've taken a portion of the station's funds to offset the overall cost.", null, null, null, "Nanotrasen Asset Protection")
-
- addtimer(CALLBACK(src, PROC_REF(finish_enter)), 3 SECONDS)
-
-/// Called when person is finished shoving in, awards ransome money
-/datum/syndicate_contract/proc/finish_enter()
- // Pay contractor their portion of ransom
- if(status != CONTRACT_STATUS_COMPLETE)
- return
-
- var/obj/item/card/id/owner_id = contract.owner.current?.get_idcard(TRUE)
-
- if(owner_id?.registered_account.account_id) // why do we check for account id? because apparently unset agent IDs have existing bank accounts that can't be accessed. this is suboptimal
- owner_id.registered_account.adjust_money(ransom * CONTRACTOR_RANSOM_CUT)
-
- owner_id.registered_account.bank_card_talk("We've processed the ransom, agent. Here's your cut - your balance is now \
- [owner_id.registered_account.account_balance] credits.", TRUE)
- else
- to_chat(contract.owner.current, span_notice("A briefcase appears at your feet!"))
- var/obj/item/storage/briefcase/secure/case = new(get_turf(contract.owner.current))
- for(var/i in 1 to (round((ransom * CONTRACTOR_RANSOM_CUT) / 1000))) // Gets slightly less/more but whatever
- new /obj/item/stack/spacecash/c1000(case)
-
-/// They're off to holding - handle the return timer and give some text about what's going on.
-/datum/syndicate_contract/proc/handle_victim_experience(mob/living/target)
- // Ship 'em back - dead or alive, 4 minutes wait.
- // Even if they weren't the target, we're still treating them the same.
- addtimer(CALLBACK(src, PROC_REF(return_victim), target), 4 MINUTES)
-
- if (target.stat == DEAD)
- return
- // Heal them up - gets them out of crit/soft crit. If omnizine is removed in the future, this needs to be replaced with a
- // method of healing them, consequence free, to a reasonable amount of health.
- victim_stage_one(target)
-
-/// Adds omnizine and begins the victim handling
-/datum/syndicate_contract/proc/victim_stage_one(mob/living/target)
- target.reagents.add_reagent(/datum/reagent/medicine/omnizine, 20)
-
- target.flash_act()
- target.adjust_confusion(10 SECONDS)
- target.set_eye_blur_if_lower(10 SECONDS)
- to_chat(target, span_warning("You feel strange..."))
- addtimer(CALLBACK(src, PROC_REF(victim_stage_two), target), 6 SECONDS)
-
-/// Continued victim handling
-/datum/syndicate_contract/proc/victim_stage_two(mob/living/target)
- var/list/parts_to_fuck_up = list(
- BODY_ZONE_L_ARM,
- BODY_ZONE_R_ARM,
- BODY_ZONE_L_LEG,
- BODY_ZONE_R_LEG,
- )
-
- to_chat(target, span_warning("That pod did something to you..."))
- target.set_dizzy(70 SECONDS)
-
- for(var/i in 1 to 2)
- var/obj/item/bodypart/limb = target.get_bodypart(pick_n_take(parts_to_fuck_up))
- var/min_wound = limb.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_SEVERE, return_value_if_no_wound = 40)
- var/max_wound = limb.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_CRITICAL, return_value_if_no_wound = 60)
- limb.receive_damage(brute = WOUND_MINIMUM_DAMAGE, wound_bonus = rand(min_wound, max_wound))
- target.update_damage_overlays()
-
- addtimer(CALLBACK(src, PROC_REF(victim_stage_three), target), 6 SECONDS)
-
-/// Continued victim handling, flashes them as well
-/datum/syndicate_contract/proc/victim_stage_three(mob/living/target)
- to_chat(target, span_warning("Your head pounds... It feels like it's going to burst out your skull!"))
- target.flash_act()
- target.adjust_confusion(20 SECONDS)
- target.set_eye_blur_if_lower(6 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(victim_stage_four), target), 3 SECONDS)
-
-/// Continued victim handling
-/datum/syndicate_contract/proc/victim_stage_four(mob/living/target)
- to_chat(target, span_warning("Your head pounds..."))
-
- if(iscarbon(target))
- var/mob/living/carbon/carbon_target = target
- switch(rand(1, 100))
- if(1 to 45)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
-
- if(46 to 73)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
-
- if(74 to 94)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
-
- if(75 to 100)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
- carbon_target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
-
- addtimer(CALLBACK(src, PROC_REF(victim_stage_five), target), 10 SECONDS)
-
-/// Continued victim handling, some unconsciousness
-/datum/syndicate_contract/proc/victim_stage_five(mob/living/target)
- target.flash_act()
- target.Unconscious(200)
- to_chat(target, span_hypnophrase(span_reallybig("A million voices echo in your head... \"Your mind held many valuable secrets - \
- we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
- so it's only a matter of time before we ship you back...\"")))
- target.set_eye_blur_if_lower(20 SECONDS)
- target.set_dizzy_if_lower(30 SECONDS)
- target.adjust_confusion(20 SECONDS)
-
-/// We're returning the victim
-/datum/syndicate_contract/proc/return_victim(mob/living/target)
- var/list/possible_drop_loc = list()
-
- for(var/turf/possible_drop in contract.dropoff.contents)
- if(is_safe_turf(possible_drop))
- possible_drop_loc += possible_drop
- if(!length(possible_drop_loc)) //Prioritize safe tiles first, then unsafe
- for(var/turf/open/possible_unsafe_drop in contract.dropoff.contents)
- possible_drop_loc += possible_unsafe_drop
-
- if (length(possible_drop_loc))
- var/pod_rand_loc = rand(1, length(possible_drop_loc))
-
- var/obj/structure/closet/supplypod/return_pod = new()
- return_pod.bluespace = TRUE
- return_pod.explosionSize = list(0,0,0,0)
- return_pod.style = STYLE_SYNDICATE
-
- do_sparks(8, FALSE, target)
- target.visible_message(span_notice("[target] vanishes..."))
-
- for(var/obj/item/target_item as anything in target)
- if(ishuman(target))
- var/mob/living/carbon/human/human_target = target
- if(target_item == human_target.w_uniform)
- continue //So all they're left with are shoes and uniform.
- if(target_item == human_target.shoes)
- continue
- target.dropItemToGround(target_item)
-
- for(var/obj/item/target_item as anything in victim_belongings)
- target_item.forceMove(return_pod)
-
- target.forceMove(return_pod)
-
- target.flash_act()
- target.set_eye_blur_if_lower(60 SECONDS)
- target.set_dizzy_if_lower(70 SECONDS)
- target.adjust_confusion(20 SECONDS)
-
- new /obj/effect/pod_landingzone(possible_drop_loc[pod_rand_loc], return_pod)
- else
- to_chat(target, span_reallybig(span_hypnophrase("A million voices echo in your head... \"Seems where you got sent here from won't \
- be able to handle our pod... You will die here instead.\"")))
- if(!isliving(target))
- return
- var/mob/living/unlucky_fellow = target
- unlucky_fellow.investigate_log("was returned without a valid drop location by the contractor [contract.owner?.current].", INVESTIGATE_DEATHS)
- unlucky_fellow.death()
-
-#undef RANSOM_LOWER
-#undef RANSOM_UPPER
-#undef CONTRACTOR_RANSOM_CUT
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm b/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm
deleted file mode 100644
index 5346d412702..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_datum.dm
+++ /dev/null
@@ -1,34 +0,0 @@
-/// End-round generation proc
-/datum/opposing_force/proc/contractor_round_end()
- var/result = ""
- var/total_spent_rep = 0
-
- var/completed_contracts = contractor_hub.contracts_completed
- var/tc_total = contractor_hub.contract_paid_out + contractor_hub.contract_TC_to_redeem
-
- var/contractor_item_icons = "" // Icons of purchases
- var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
-
- // Get all the icons/total cost for all our items bought
- for (var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items)
- contractor_item_icons += "\[ [contractor_purchase.name] - [contractor_purchase.cost] Rep
[contractor_purchase.desc] \]"
-
- total_spent_rep += contractor_purchase.cost
-
- // Special case for reinforcements, we want to show their ckey and name on round end.
- if (istype(contractor_purchase, /datum/contractor_item/contractor_partner))
- var/datum/contractor_item/contractor_partner/partner = contractor_purchase
- var/mob/living/carbon/human/partner_mob = partner.partner_mind.current
- contractor_support_unit += " [partner_mob.name] was [partner_mob.p_their()] contractor support unit."
-
- if (length(contractor_hub.purchased_items))
- result += " (used [total_spent_rep] Rep) "
- result += contractor_item_icons
- result += " "
- if (completed_contracts > 0)
- var/plural_check = "contract[completed_contracts > 1 ? "s" : ""]"
-
- result += "Completed [span_greentext("[completed_contracts]")] [plural_check] for a total of \
- [span_greentext("[tc_total] TC")]![contractor_support_unit] "
-
- return result
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm b/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm
deleted file mode 100644
index 130cd3d2a4b..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_hub.dm
+++ /dev/null
@@ -1,92 +0,0 @@
-#define LOWEST_TC 30
-
-/datum/contractor_hub
- /// How much reputation the contractor has
- var/contract_rep = 0
- /// What contractor items can be purchased
- var/list/hub_items = list()
- /// List of what the contractor's purchased
- var/list/purchased_items = list()
- /// Static of contractor_item subtypes
- var/static/list/contractor_items = typecacheof(/datum/contractor_item, ignore_root_path = TRUE)
- /// Reference to the current contract datum
- var/datum/syndicate_contract/current_contract
- /// List of all contract datums the contractor has available
- var/list/datum/syndicate_contract/assigned_contracts = list()
- /// used as a blacklist to make sure we're not assigning targets already assigned
- var/list/assigned_targets = list()
- /// NUmber of how many contracts you've done
- var/contracts_completed = 0
- /// How many TC you've paid out in contracts
- var/contract_paid_out = 0
- /// Amount of TC that has yet to be redeemed
- var/contract_TC_to_redeem = 0
- /// Current index number for contract IDs
- var/start_index = 1
-
-/// Generates a list of all valid hub items to set for purchase
-/datum/contractor_hub/proc/create_hub_items()
- for(var/path in contractor_items)
- var/datum/contractor_item/contractor_item = new path
-
- hub_items.Add(contractor_item)
-
-/// Create initial list of contracts
-/datum/contractor_hub/proc/create_contracts(datum/mind/owner)
-
- // 6 initial contracts
- var/list/to_generate = list(
- CONTRACT_PAYOUT_LARGE,
- CONTRACT_PAYOUT_MEDIUM,
- CONTRACT_PAYOUT_MEDIUM,
- CONTRACT_PAYOUT_SMALL,
- CONTRACT_PAYOUT_SMALL,
- CONTRACT_PAYOUT_SMALL
- )
-
- //What the fuck
- if(length(to_generate) > length(GLOB.manifest.locked))
- to_generate.Cut(1, length(GLOB.manifest.locked))
-
- var/total = 0
- var/lowest_paying_sum = 0
- var/datum/syndicate_contract/lowest_paying_contract
-
- // Randomise order, so we don't have contracts always in payout order.
- to_generate = shuffle(to_generate)
-
- // Support contract generation happening multiple times
- if (length(assigned_contracts))
- start_index = length(assigned_contracts) + 1
-
- // Generate contracts, and find the lowest paying.
- for(var/contract_gen in 1 to length(to_generate))
- var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[contract_gen])
- var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
-
- assigned_targets.Add(contract_to_add.contract.target)
-
- if (!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
- lowest_paying_sum = contract_payout_total
- lowest_paying_contract = contract_to_add
-
- total += contract_payout_total
- contract_to_add.id = start_index
- assigned_contracts.Add(contract_to_add)
-
- start_index++
-
- // If the threshold for TC payouts isn't reached, boost the lowest paying contract
- if ((total < LOWEST_TC) && lowest_paying_contract)
- lowest_paying_contract.contract.payout_bonus += (LOWEST_TC - total)
-
-#undef LOWEST_TC
-
-/datum/contractor_hub/proc/create_single_contract(datum/mind/owner, contract_payout_tier)
- var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, contract_payout_tier)
-
- assigned_targets.Add(contract_to_add.contract.target)
-
- contract_to_add.id = start_index
- assigned_contracts.Add(contract_to_add)
- start_index++
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_items.dm b/modular_skyrat/modules/contractor/code/datums/contractor_items.dm
deleted file mode 100644
index 80013aef23e..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_items.dm
+++ /dev/null
@@ -1,220 +0,0 @@
-/datum/contractor_item
- /// Name of the item datum
- var/name
- /// Description of the item datum
- var/desc
- /// Item path to spawn, no item path means you need to override `handle_purchase()`
- var/item
- /// fontawesome icon to use inside the hub - https://fontawesome.com/icons/
- var/item_icon = "broadcast-tower"
- /// Any number above 0 for how many times it can be bought in a round for a single traitor. -1 is unlimited.
- var/limited = -1
- /// Cost of the item in contract rep.
- var/cost
-
-/// Subtract cost, and spawn if it's an item.
-/datum/contractor_item/proc/handle_purchase(datum/contractor_hub/hub, mob/living/user)
-
- if(hub.contract_rep >= cost)
- hub.contract_rep -= cost
- else
- return FALSE
-
- if(limited >= 1)
- limited -= 1
- else
- return FALSE
-
- hub.purchased_items.Add(src)
-
- user.playsound_local(user, 'sound/machines/uplinkpurchase.ogg', 100)
-
- if(item)
- var/atom/item_to_create = new item(get_turf(user))
- if(user.put_in_hands(item_to_create))
- to_chat(user, span_notice("Your purchase materializes into your hands!"))
- else
- to_chat(user, span_notice("Your purchase materializes onto the floor."))
-
- return TRUE
-
-/datum/contractor_item/contract_reroll
- name = "Contract Reroll"
- desc = "Request a reroll of your current contract list. Will generate a new target, payment, and dropoff for the contracts you currently have available."
- item_icon = "dice"
- limited = 3
- cost = 0
-
-/datum/contractor_item/contract_reroll/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!(.))
- return
- // We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
- var/list/new_target_list = list()
- for(var/datum/syndicate_contract/contract_check as anything in hub.assigned_contracts)
- if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
- if (contract_check.contract.target)
- new_target_list.Add(contract_check.contract.target)
- continue
-
- // Reroll contracts without duplicates
- for(var/datum/syndicate_contract/rerolling_contract as anything in hub.assigned_contracts)
- if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
- continue
-
- rerolling_contract.generate(new_target_list)
- new_target_list.Add(rerolling_contract.contract.target)
-
- // Set our target list with the new set we've generated.
- hub.assigned_targets = new_target_list
-
-/datum/contractor_item/contractor_pinpointer
- name = "Contractor Pinpointer"
- desc = "A pinpointer that finds targets even without active suit sensors. Due to taking advantage of an exploit within the system, it can't pinpoint to the same accuracy as the traditional models. Becomes permanently locked to the user that first activates it."
- item = /obj/item/pinpointer/crew/contractor
- item_icon = "search-location"
- limited = 2
- cost = 1
-
-/datum/contractor_item/fulton_extraction_kit
- name = "Fulton Extraction Kit"
- desc = "For getting your target across the station to those difficult dropoffs. Place the beacon somewhere secure, and link the pack. Activating the pack on your target will send them over to the beacon - make sure they're not just going to run away though!"
- item = /obj/item/storage/box/contractor/fulton_extraction
- item_icon = "parachute-box"
- limited = 1
- cost = 1
-
-/datum/contractor_item/contractor_partner
- name = "Reinforcements"
- desc = "Upon purchase we'll contact available units in the area. Should there be an agent free, we'll send them down to assist you immediately. If no units are free, we give a full refund."
- item_icon = "user-friends"
- limited = 1
- cost = 2
- var/datum/mind/partner_mind = null
-
-/datum/contractor_item/contractor_partner/handle_purchase(datum/contractor_hub/hub, mob/living/user)
- . = ..()
-
- if(!.)
- return
- to_chat(user, span_notice("The uplink vibrates quietly, connecting to nearby agents..."))
-
- var/list/mob/dead/observer/candidates = poll_ghost_candidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
-
- if(LAZYLEN(candidates))
- var/mob/dead/observer/picked_obs = pick(candidates)
- spawn_contractor_partner(user, picked_obs.key)
- else
- to_chat(user, span_notice("No available agents at this time, please try again later."))
-
- // refund and add the limit back.
- limited += 1
- hub.contract_rep += cost
- hub.purchased_items -= src
-
-/datum/contractor_item/contractor_partner/proc/spawn_contractor_partner(mob/living/user, key)
- var/mob/living/carbon/human/partner = new()
- var/datum/outfit/contractor_partner/partner_outfit = new()
-
- partner_outfit.equip(partner)
-
- var/obj/structure/closet/supplypod/arrival_pod = new(null, STYLE_SYNDICATE)
- arrival_pod.explosionSize = list(0,0,0,1)
- arrival_pod.bluespace = TRUE
-
- var/turf/free_location = find_obstruction_free_location(2, user)
-
- // We really want to send them - if we can't find a nice location just land it on top of them.
- if (!free_location)
- free_location = get_turf(user)
-
- partner.forceMove(arrival_pod)
- partner.ckey = key
-
- /// We give a reference to the mind that'll be the support unit
- partner_mind = partner.mind
- partner_mind.make_contractor_support()
-
- to_chat(partner_mind.current, "\n[span_alertwarning("[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.")]")
- to_chat(partner_mind.current, "[span_alertwarning("Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.")]\n\n")
-
- new /obj/effect/pod_landingzone(free_location, arrival_pod)
-
-/datum/contractor_item/blackout
- name = "Blackout"
- desc = "Request Syndicate Command to distrupt the station's powernet. Disables power across the station for a short duration."
- item_icon = "bolt"
- limited = 2
- cost = 2
-
-/datum/contractor_item/blackout/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!.)
- return
- power_fail(35, 50)
- priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", ANNOUNCER_POWEROFF)
-
-/datum/contractor_item/comms_blackout
- name = "Comms Outage"
- desc = "Request Syndicate Command to disable station Telecommunications. Disables telecommunications across the station for a medium duration."
- item_icon = "phone-slash"
- limited = 2
- cost = 2
-
-/datum/contractor_item/comms_blackout/handle_purchase(datum/contractor_hub/hub)
- . = ..()
-
- if(!.)
- return
- var/datum/round_event_control/event = locate(/datum/round_event_control/communications_blackout) in SSevents.control
- event.run_event()
-
-/datum/contractor_item/mod_baton_holster
- name = "Baton Holster Module"
- desc = "Never worry about dropping your baton again with this holster module! Simply insert your baton into the module, put it in your MODsuit, and the baton will retract whenever dropped."
- item = /obj/item/mod/module/baton_holster
- item_icon = "wrench" //I cannot find anything better, replace if you find something more fitting
- limited = 1
- cost = 2
-
-/datum/contractor_item/baton_upgrade_cuff
- name = "Baton Cuff Upgrade"
- desc = "Using technology reverse-engineered from some alien batons we had lying around, you can now cuff people using your baton with the secondary attack. Due to technical limitations, only cable cuffs and zipties work, and they need to be loaded into the baton manually."
- item = /obj/item/baton_upgrade/cuff
- item_icon = "bacon" //ditto
- limited = 1
- cost = 1
-
-/datum/contractor_item/baton_upgrade_mute
- name = "Baton Mute Upgrade"
- desc = "A relatively new advancement in completely proprietary baton technology, this baton upgrade will mute anyone hit for ten seconds, maximizing at twenty seconds."
- item = /obj/item/baton_upgrade/mute
- item_icon = "comment-slash"
- limited = 1
- cost = 2
-
-/datum/contractor_item/baton_upgrade_focus
- name = "Baton Focus Upgrade"
- desc = "When applied to a baton, it will exhaust the target even more, should they be the target of your current contract."
- item = /obj/item/baton_upgrade/focus
- item_icon = "eye"
- limited = 1
- cost = 2
-
-/datum/contractor_item/mod_magnetic_suit
- name = "Magnetic Deployment Module"
- desc = "A module that utilizes magnets to largely reduce the time needed to deploy and retract your MODsuit."
- item = /obj/item/mod/module/springlock/contractor
- item_icon = "magnet"
- limited = 1
- cost = 2
-
-/datum/contractor_item/mod_scorpion_hook
- name = "SCORPION Hook Module"
- desc = "A module that allows you to launch a hardlight hook from your MODsuit, pulling a target into range of your baton."
- item = /obj/item/mod/module/scorpion_hook
- item_icon = "arrow-left" //replace if fontawesome gets an actual hook icon
- limited = 1
- cost = 3
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_support.dm b/modular_skyrat/modules/contractor/code/datums/contractor_support.dm
deleted file mode 100644
index c758608ee1a..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_support.dm
+++ /dev/null
@@ -1,53 +0,0 @@
-/datum/antagonist/traitor/contractor_support
- name = "Contractor Support Unit"
- antag_moodlet = /datum/mood_event/focused
-
- show_in_roundend = FALSE // We're already adding them in to the contractor's roundend.
- give_objectives = TRUE // We give them their own custom objective.
- give_uplink = FALSE // Don't give them an uplink, they get one in their backpack
- /// Team datum that contains the contractor and the support unit
- var/datum/team/contractor_team/contractor_team
-
-/// Team for storing both the contractor and their support unit - only really for the HUD and admin logging.
-/datum/team/contractor_team
- show_roundend_report = FALSE
-
-/datum/antagonist/traitor/contractor_support/on_gain()
- var/datum/objective/generic_objective = new
-
- generic_objective.name = "Follow the Contractor's Orders"
- generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
-
- generic_objective.completed = TRUE
-
- objectives += generic_objective
- return ..()
-
-/datum/outfit/contractor_partner
- name = "Contractor Support Unit"
-
- uniform = /obj/item/clothing/under/chameleon
- suit = /obj/item/clothing/suit/chameleon
- back = /obj/item/storage/backpack
- belt = /obj/item/modular_computer/pda/chameleon
- mask = /obj/item/clothing/mask/cigarette/syndicate
- shoes = /obj/item/clothing/shoes/chameleon/noslip
- ears = /obj/item/radio/headset/chameleon
- id = /obj/item/card/id/advanced/chameleon
- r_hand = /obj/item/storage/toolbox/syndicate
- id_trim = /datum/id_trim/chameleon/operative
-
- backpack_contents = list(
- /obj/item/storage/box/survival,
- /obj/item/implanter/uplink,
- /obj/item/clothing/mask/chameleon,
- /obj/item/storage/fancy/cigarettes/cigpack_syndicate,
- /obj/item/lighter
- )
-
-/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/partner, visualsOnly)
- . = ..()
- var/obj/item/clothing/mask/cigarette/syndicate/cig = partner.get_item_by_slot(ITEM_SLOT_MASK)
-
- // pre-light their cig
- cig.light()
diff --git a/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm b/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm
deleted file mode 100644
index 02f5790d5e8..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/contractor_tablet.dm
+++ /dev/null
@@ -1,216 +0,0 @@
-/datum/computer_file/program/contract_uplink
- filename = "contractor uplink"
- filedesc = "Syndicate Contractor Uplink"
- category = PROGRAM_CATEGORY_MISC
- program_icon_state = "assign"
- extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
- size = 10
- requires_ntnet = FALSE
- available_on_ntnet = FALSE
- undeletable = TRUE
- tgui_id = "SyndContractor"
- program_icon = "tasks"
- /// Error message if there is one
- var/error = ""
- /// If the info screen is displayed or not
- var/info_screen = TRUE
- /// If the contract uplink's been assigned to a person yet
- var/assigned = FALSE
- /// If this is the first opening of the tablet
- var/first_load = TRUE
-
-/datum/computer_file/program/contract_uplink/on_start(mob/living/user)
- . = ..(user)
-
-/datum/computer_file/program/contract_uplink/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- var/mob/living/user = usr
- var/obj/item/modular_computer/pda/contractor/uplink_computer = computer
-
- if(!istype(uplink_computer))
- return
-
- switch(action)
- if("PRG_contract-accept")
- var/contract_id = text2num(params["contract_id"])
-
- // Set as the active contract
- uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE
- uplink_computer.opfor_data.contractor_hub.current_contract = uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id]
-
- program_icon_state = "single_contract"
- return TRUE
- if("PRG_login")
- if(!user.mind.opposing_force)
- var/datum/opposing_force/opposing_force = new(user.mind)
- user.mind.opposing_force = opposing_force
- SSopposing_force.new_opfor(opposing_force)
- var/datum/opposing_force/opfor_data = user.mind.opposing_force
-
- if (!opfor_data) // Just in case
- return FALSE
- // Only play greet sound, and handle contractor hub when assigning for the first time.
- if (!opfor_data.contractor_hub)
- user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE)
- opfor_data.contractor_hub = new
- opfor_data.contractor_hub.create_hub_items()
-
- // Stops any topic exploits such as logging in multiple times on a single system.
- if (!assigned)
- opfor_data.contractor_hub.create_contracts(opfor_data.mind_reference)
-
- uplink_computer.opfor_data = opfor_data
-
- program_icon_state = "contracts"
- assigned = TRUE
- return TRUE
- if("PRG_call_extraction")
- if (uplink_computer.opfor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING)
- if (uplink_computer.opfor_data.contractor_hub.current_contract.handle_extraction(user))
- user.playsound_local(user, 'modular_skyrat/modules/contractor/sound/confirmdropoff.ogg', 100, TRUE)
- uplink_computer.opfor_data.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING
-
- program_icon_state = "extracted"
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- error = "Either both you or your target aren't at the dropoff location, or the pod hasn't got a valid place to land. Clear space, or make sure you're both inside."
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible."
-
- return TRUE
- if("PRG_contract_abort")
- var/contract_id = uplink_computer.opfor_data.contractor_hub.current_contract.id
-
- uplink_computer.opfor_data.contractor_hub.current_contract = null
- uplink_computer.opfor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
-
- program_icon_state = "contracts"
-
- return TRUE
- if("PRG_redeem_TC")
- if (uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem)
- var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user),
- uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.put_in_hands(crystals))
- to_chat(H, span_notice("Your payment materializes into your hands!"))
- else
- to_chat(user, span_notice("Your payment materializes onto the floor."))
-
- uplink_computer.opfor_data.contractor_hub.contract_paid_out += uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem
- uplink_computer.opfor_data.contractor_hub.contract_TC_to_redeem = 0
- return TRUE
- else
- user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50)
- return TRUE
- if ("PRG_clear_error")
- error = ""
- return TRUE
- if("PRG_set_first_load_finished")
- first_load = FALSE
- return TRUE
- if("PRG_toggle_info")
- info_screen = !info_screen
- return TRUE
- if ("buy_hub")
- if (uplink_computer.opfor_data.mind_reference.current == user)
- var/item = params["item"]
-
- for (var/datum/contractor_item/hub_item in uplink_computer.opfor_data.contractor_hub.hub_items)
- if (hub_item.name == item)
- hub_item.handle_purchase(uplink_computer.opfor_data.contractor_hub, user)
- else
- error = "Invalid user... You weren't recognised as the user of this system."
-
-/datum/computer_file/program/contract_uplink/ui_data(mob/user)
- var/list/data = list()
- var/screen_to_be = null
- var/obj/item/modular_computer/pda/contractor/uplink_computer = computer
-
- data["first_load"] = first_load
-
- if (uplink_computer?.opfor_data)
- var/datum/opposing_force/opfor_data = uplink_computer.opfor_data
- data += list()
-
- if (opfor_data.contractor_hub.current_contract)
- data["ongoing_contract"] = TRUE
- screen_to_be = "single_contract"
- if (opfor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING)
- data["extraction_enroute"] = TRUE
- screen_to_be = "extracted"
- else
- data["extraction_enroute"] = FALSE
- else
- data["ongoing_contract"] = FALSE
- data["extraction_enroute"] = FALSE
-
- data["logged_in"] = TRUE
- data["station_name"] = GLOB.station_name
- data["redeemable_tc"] = opfor_data.contractor_hub.contract_TC_to_redeem
- data["earned_tc"] = opfor_data.contractor_hub.contract_paid_out
- data["contracts_completed"] = opfor_data.contractor_hub.contracts_completed
- data["contract_rep"] = opfor_data.contractor_hub.contract_rep
-
- data["info_screen"] = info_screen
-
- data["error"] = error
-
- for (var/datum/contractor_item/hub_item in opfor_data.contractor_hub.hub_items)
- data["contractor_hub_items"] += list(list(
- "name" = hub_item.name,
- "desc" = hub_item.desc,
- "cost" = hub_item.cost,
- "limited" = hub_item.limited,
- "item_icon" = hub_item.item_icon
- ))
-
- for (var/datum/syndicate_contract/contract in opfor_data.contractor_hub.assigned_contracts)
- if(!contract.contract)
- stack_trace("Syndiate contract with null contract objective found in [opfor_data.mind_reference]'s contractor hub!")
- contract.status = CONTRACT_STATUS_ABORTED
- continue
-
- data["contracts"] += list(list(
- "target" = contract.contract.target,
- "target_rank" = contract.target_rank,
- "payout" = contract.contract.payout,
- "payout_bonus" = contract.contract.payout_bonus,
- "dropoff" = contract.contract.dropoff,
- "id" = contract.id,
- "status" = contract.status,
- "message" = contract.wanted_message
- ))
-
- var/direction
- if (opfor_data.contractor_hub.current_contract)
- var/turf/curr = get_turf(user)
- var/turf/dropoff_turf
- data["current_location"] = "[get_area_name(curr, TRUE)]"
-
- for (var/turf/content in opfor_data.contractor_hub.current_contract.contract.dropoff.contents)
- if (isturf(content))
- dropoff_turf = content
- break
-
- if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only
- direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful.
- if(get_area(user) == opfor_data.contractor_hub.current_contract.contract.dropoff)
- direction = "LOCATION CONFIRMED"
- else
- direction = "???"
-
- data["dropoff_direction"] = direction
-
- else
- data["logged_in"] = FALSE
-
- if(screen_to_be)
- program_icon_state = screen_to_be
- update_computer_icon()
- return data
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm b/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm
deleted file mode 100644
index 3c7fbeb2a21..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/antag_datum.dm
+++ /dev/null
@@ -1,59 +0,0 @@
-// USED FOR THE MIDROUND
-/datum/antagonist/contractor
- name = "Drifting Contractor"
- antagpanel_category = "DriftingContractor"
- preview_outfit = /datum/outfit/contractor_preview
- job_rank = ROLE_DRIFTING_CONTRACTOR
- hud_icon = 'modular_skyrat/modules/contractor/icons/hud_icon.dmi'
- antag_hud_name = "contractor"
- antagpanel_category = ANTAG_GROUP_SYNDICATE
- antag_moodlet = /datum/mood_event/focused
- show_to_ghosts = TRUE
- suicide_cry = "FOR THE CONTRACTS!!"
- /// The outfit the contractor is equipped with
- var/contractor_outfit = /datum/outfit/contractor
-
-/datum/antagonist/contractor/proc/equip_guy()
- if(!ishuman(owner.current))
- return
- var/mob/living/carbon/human/person = owner.current
- person.equipOutfit(contractor_outfit)
- return TRUE
-
-/datum/antagonist/contractor/on_gain()
- forge_objectives()
- . = ..()
- equip_guy()
-
-/datum/antagonist/contractor/forge_objectives()
- var/datum/objective/contractor_total/contract_objectives = new
- contract_objectives.owner = owner
- objectives += contract_objectives
-
-/datum/antagonist/contractor/roundend_report()
- var/list/report = list()
-
- if(!owner)
- CRASH("antagonist datum without owner")
-
- report += "[printplayer(owner)]"
-
- var/objectives_complete = TRUE
- if(length(objectives))
- report += printobjectives(objectives)
- for(var/datum/objective/objective as anything in objectives)
- if(!objective.check_completion())
- objectives_complete = FALSE
- break
-
- report += owner.opposing_force.contractor_round_end()
-
- if(!length(objectives) || objectives_complete)
- report += "The [name] was successful!"
- else
- report += "The [name] has failed!"
-
- return report.Join(" ")
-
-/datum/job/drifting_contractor
- title = ROLE_DRIFTING_CONTRACTOR
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/event.dm b/modular_skyrat/modules/contractor/code/datums/midround/event.dm
deleted file mode 100644
index d90a2b90dff..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/event.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-/datum/round_event_control/contractor
- name = "Drifting Contractor"
- typepath = /datum/round_event/ghost_role/contractor
- weight = 8
- max_occurrences = 1
- category = EVENT_CATEGORY_INVASION
- description = "A pre-equipped contractor floats towards the station to fulfill contracts."
-
-/datum/round_event/ghost_role/contractor
- minimum_required = 1
- role_name = "Drifting Contractor"
- fakeable = FALSE
-
-/datum/round_event/ghost_role/contractor/spawn_role()
- var/list/candidates = get_candidates(ROLE_DRIFTING_CONTRACTOR)
- if(!length(candidates))
- return NOT_ENOUGH_PLAYERS
-
- var/mob/dead/selected = pick_n_take(candidates)
-
- var/list/spawn_locs = list()
- for(var/obj/effect/landmark/carpspawn/carp in GLOB.landmarks_list)
- spawn_locs += carp.loc
- if(!length(spawn_locs))
- return MAP_ERROR
-
- var/mob/living/carbon/human/operative = new(pick(spawn_locs))
- operative.dna.update_dna_identity()
- var/datum/mind/mind = new /datum/mind(selected.key)
- selected.client?.prefs?.apply_prefs_to(operative)
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/drifting_contractor))
- mind.special_role = ROLE_DRIFTING_CONTRACTOR
- mind.active = TRUE
- mind.transfer_to(operative)
- mind.add_antag_datum(/datum/antagonist/contractor)
-
- message_admins("[ADMIN_LOOKUPFLW(operative)] has been made into [src] by an event.")
- log_game("[key_name(operative)] was spawned as a [src] by an event.")
- spawned_mobs += operative
- return SUCCESSFUL_SPAWN
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/objective.dm b/modular_skyrat/modules/contractor/code/datums/midround/objective.dm
deleted file mode 100644
index 74247f9a9db..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/objective.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/datum/objective/contractor_total
- name = "contractor"
- explanation_text = "Complete at least %CONTRACTNUM% contract%S%."
- martyr_compatible = TRUE
- /// How many contracts are needed, rand(1, 3)
- var/contracts_needed
-
-/datum/objective/contractor_total/New(text)
- . = ..()
- contracts_needed = rand(1, 3)
- explanation_text = replacetext(explanation_text, "%CONTRACTNUM%", contracts_needed)
- explanation_text = replacetext(explanation_text, "%S%", (contracts_needed > 1 ? "s" : ""))
-
-/datum/objective/contractor_total/check_completion()
- if(owner?.opposing_force?.contractor_hub.contracts_completed >= contracts_needed)
- return TRUE
- return FALSE
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm b/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm
deleted file mode 100644
index c2886de801d..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/outfit.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-/datum/outfit/contractor
- name = "Syndicate Contractor - Full Kit"
-
- glasses = /obj/item/clothing/glasses/night
- mask = /obj/item/clothing/mask/gas/syndicate
- back = /obj/item/mod/control/pre_equipped/contractor/upgraded
- r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
- internals_slot = ITEM_SLOT_RPOCKET
- belt = /obj/item/storage/belt/military
-
- uniform = /obj/item/clothing/under/syndicate/coldres
- shoes = /obj/item/clothing/shoes/combat
- gloves = /obj/item/clothing/gloves/combat
- ears = /obj/item/radio/headset/syndicate/alt
- l_pocket = /obj/item/modular_computer/pda/contractor
- id = /obj/item/card/id/advanced/chameleon
- backpack_contents = list(
- /obj/item/storage/box/survival/syndie,
- /obj/item/storage/box/syndicate/contract_kit/midround,
- /obj/item/knife/combat/survival,
- /obj/item/pinpointer/crew/contractor,
- )
-
- implants = list(
- /obj/item/implant/uplink,
- )
-
- id_trim = /datum/id_trim/chameleon/contractor
-
-/datum/outfit/contractor_preview
- name = "Syndicate Contractor (Preview only)"
-
- back = /obj/item/mod/control/pre_equipped/empty/contractor
- uniform = /obj/item/clothing/under/syndicate
- glasses = /obj/item/clothing/glasses/night
-
-/datum/outfit/contractor/upgraded
- name = "Syndicate Contractor (Upgraded)"
- back = /obj/item/mod/control/pre_equipped/contractor/upgraded/adminbus
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm b/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm
deleted file mode 100644
index d5ae8c7b59c..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/ruleset.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-/datum/dynamic_ruleset/midround/from_ghosts/contractor
- name = "Drifting Contractors"
- midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
- antag_datum = /datum/antagonist/contractor
- antag_flag = ROLE_DRIFTING_CONTRACTOR
- antag_flag_override = ROLE_DRIFTING_CONTRACTOR
- enemy_roles = list(
- JOB_CAPTAIN,
- JOB_DETECTIVE,
- JOB_HEAD_OF_SECURITY,
- JOB_SECURITY_OFFICER,
- )
- required_enemies = list(2,2,1,1,1,1,1,0,0,0)
- required_candidates = 2
- weight = 6
- cost = 10
- minimum_players = 25
- flags = HIGH_IMPACT_RULESET
- /// valid places to spawn
- var/list/spawn_locs = list()
-
-/datum/dynamic_ruleset/midround/from_ghosts/contractor/ready(forced = FALSE)
- if(prob(33))
- required_candidates++
- if (required_candidates > (length(dead_players) + length(list_observers)))
- return FALSE
- for(var/obj/effect/landmark/carpspawn/carp in GLOB.landmarks_list)
- spawn_locs += carp.loc
- if(!length(spawn_locs))
- log_admin("Cannot accept Drifting Contractors ruleset. Couldn't find any carp spawn points.")
- message_admins("Cannot accept Drifting Contractors ruleset. Couldn't find any carp spawn points.")
- return FALSE
- return TRUE
-
-/datum/dynamic_ruleset/midround/from_ghosts/contractor/finish_setup(mob/new_character, index)
- ..()
- new_character.forceMove(pick_n_take(spawn_locs))
- new_character.mind.set_assigned_role(SSjob.GetJobType(/datum/job/drifting_contractor))
- new_character.mind.active = TRUE
diff --git a/modular_skyrat/modules/contractor/code/datums/midround/trim.dm b/modular_skyrat/modules/contractor/code/datums/midround/trim.dm
deleted file mode 100644
index 8fbf98912e2..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/midround/trim.dm
+++ /dev/null
@@ -1,7 +0,0 @@
-/// Trim for Chameleon ID cards. Many outfits, nuke ops and some corpses hold Chameleon ID cards.
-/datum/id_trim/chameleon/contractor
- assignment = "Syndicate Contractor"
- trim_state = "trim_contractor"
- trim_icon = 'modular_skyrat/master_files/icons/obj/card.dmi'
- department_color = COLOR_ORANGE
- subdepartment_color = COLOR_ORANGE
diff --git a/modular_skyrat/modules/contractor/code/datums/mind_datum.dm b/modular_skyrat/modules/contractor/code/datums/mind_datum.dm
deleted file mode 100644
index 91cb9a5ac69..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/mind_datum.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-/datum/mind/proc/make_contractor_support()
- if(has_antag_datum(/datum/antagonist/traitor/contractor_support))
- return
- add_antag_datum(/datum/antagonist/traitor/contractor_support)
diff --git a/modular_skyrat/modules/contractor/code/datums/objective.dm b/modular_skyrat/modules/contractor/code/datums/objective.dm
deleted file mode 100644
index 557bdddfbbf..00000000000
--- a/modular_skyrat/modules/contractor/code/datums/objective.dm
+++ /dev/null
@@ -1,6 +0,0 @@
-/datum/objective
- /// Should space areas count when finding a target?
- var/count_space_areas = TRUE
-
-/datum/objective/contract
- count_space_areas = FALSE
diff --git a/modular_skyrat/modules/contractor/code/items/boxes.dm b/modular_skyrat/modules/contractor/code/items/boxes.dm
deleted file mode 100644
index 99bfa6600fb..00000000000
--- a/modular_skyrat/modules/contractor/code/items/boxes.dm
+++ /dev/null
@@ -1,114 +0,0 @@
-#define SMALL_ITEM_AMOUNT 3
-
-/obj/item/storage/box/syndicate/contract_kit
- name = "contract kit"
- special_desc = "Supplied to Syndicate contractors."
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- icon_state = "syndiebox"
- illustration = "writing_syndie"
- var/list/item_list = list(
- /obj/item/storage/backpack/duffelbag/syndie/x4,
- /obj/item/storage/box/syndie_kit/throwing_weapons,
- /obj/item/gun/syringe/syndicate,
- /obj/item/pen/edagger,
- /obj/item/pen/sleepy,
- /obj/item/flashlight/emp,
- /obj/item/reagent_containers/syringe/mulligan,
- /obj/item/clothing/shoes/chameleon/noslip,
- /obj/item/storage/medkit/tactical,
- /obj/item/encryptionkey/syndicate,
- /obj/item/clothing/glasses/thermal/syndi,
- /obj/item/slimepotion/slime/sentience/nuclear,
- /obj/item/storage/box/syndie_kit/imp_radio,
- /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
- /obj/item/reagent_containers/hypospray/medipen/stimulants,
- /obj/item/storage/box/syndie_kit/imp_freedom,
- /obj/item/crowbar/power/syndicate,
- /obj/item/clothing/gloves/tackler/combat/insulated,
- /obj/item/storage/box/syndie_kit/emp,
- /obj/item/radio/headset/chameleon/advanced,
- /obj/item/shield/energy,
- /obj/item/healthanalyzer/rad_laser
- )
-
-/obj/item/storage/box/syndicate/contract_kit/PopulateContents()
- new /obj/item/modular_computer/pda/contractor(src)
- new /obj/item/storage/box/syndicate/contractor_loadout(src)
- new /obj/item/melee/baton/telescopic/contractor_baton(src)
-
- // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
- for(var/iteration in 1 to SMALL_ITEM_AMOUNT)
- var/obj/item/small_item = pick_n_take(item_list)
- new small_item(src)
-
- // Paper guide
- new /obj/item/paper/contractor_guide(src)
-
-/obj/item/storage/box/syndicate/contractor_loadout
- name = "standard loadout"
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- special_desc = "Supplied to Syndicate contractors, providing their specialised MODSuit and chameleon uniform."
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/syndicate/contractor_loadout/PopulateContents()
- new /obj/item/mod/control/pre_equipped/contractor(src)
- new /obj/item/clothing/under/chameleon(src)
- new /obj/item/clothing/mask/chameleon(src)
- new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
- new /obj/item/card/id/advanced/chameleon(src)
- new /obj/item/lighter(src)
- new /obj/item/uplink/old_radio(src)
- new /obj/item/jammer(src)
-
-/obj/item/storage/box/contractor/fulton_extraction
- name = "fulton extraction kit"
- icon_state = "syndiebox"
- illustration = "writing_syndie"
-
-/obj/item/storage/box/contractor/fulton_extraction/PopulateContents()
- new /obj/item/extraction_pack/contractor(src)
- new /obj/item/fulton_core(src)
-
-/obj/item/storage/box/syndicate/contract_kit/midround
- name = "contract kit"
- special_desc = "Supplied to Syndicate contractors."
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
- icon_state = "syndiebox"
- illustration = "writing_syndie"
- item_list = list(
- /obj/item/storage/backpack/duffelbag/syndie/x4,
- /obj/item/storage/box/syndie_kit/throwing_weapons,
- /obj/item/gun/syringe/syndicate,
- /obj/item/pen/edagger,
- /obj/item/pen/sleepy,
- /obj/item/flashlight/emp,
- /obj/item/reagent_containers/syringe/mulligan,
- /obj/item/storage/medkit/tactical,
- /obj/item/clothing/glasses/thermal/syndi,
- /obj/item/slimepotion/slime/sentience/nuclear,
- /obj/item/storage/box/syndie_kit/imp_radio,
- /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot,
- /obj/item/reagent_containers/hypospray/medipen/stimulants,
- /obj/item/storage/box/syndie_kit/imp_freedom,
- /obj/item/crowbar/power/syndicate,
- /obj/item/clothing/gloves/tackler/combat/insulated,
- /obj/item/storage/box/syndie_kit/emp,
- /obj/item/shield/energy,
- /obj/item/healthanalyzer/rad_laser
- )
-
-/obj/item/storage/box/syndicate/contract_kit/midround/PopulateContents()
- // All about 4 TC or less - some nukeops only items, but fit nicely to the theme.
- for(var/iteration in 1 to SMALL_ITEM_AMOUNT)
- var/obj/item/small_item = pick_n_take(item_list)
- new small_item(src)
-
- // Paper guide
- new /obj/item/paper/contractor_guide/midround(src)
- new /obj/item/reagent_containers/hypospray/medipen/atropine(src)
- new /obj/item/jammer(src)
- new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
- new /obj/item/lighter(src)
-
-#undef SMALL_ITEM_AMOUNT
diff --git a/modular_skyrat/modules/contractor/code/items/misc.dm b/modular_skyrat/modules/contractor/code/items/misc.dm
index abadf28d291..f3badcd6902 100644
--- a/modular_skyrat/modules/contractor/code/items/misc.dm
+++ b/modular_skyrat/modules/contractor/code/items/misc.dm
@@ -1,6 +1,3 @@
-/obj/item/paper/contractor_guide
- name = "Contractor Guide"
-
/obj/item/paper/contractor_guide/Initialize(mapload)
default_raw_text = {"
Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,
this kit will provide you contracts to take on for TC payments.
@@ -57,7 +54,6 @@
name = "black fulton extraction pack"
icon = 'modular_skyrat/modules/contractor/icons/fulton.dmi'
can_use_indoors = TRUE
- special_desc_requirement = EXAMINE_CHECK_CONTRACTOR
special_desc = "A modified fulton pack that can be used indoors thanks to Bluespace technology. Favored by Syndicate Contractors."
diff --git a/modular_skyrat/modules/contractor/code/items/tablet.dm b/modular_skyrat/modules/contractor/code/items/tablet.dm
deleted file mode 100644
index 44b28b75c6a..00000000000
--- a/modular_skyrat/modules/contractor/code/items/tablet.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/obj/item/modular_computer/pda/contractor
- name = "contract PDA"
- icon = 'icons/obj/antags/contractor_tablet.dmi'
- icon_state = "tablet"
- icon_state_unpowered = "tablet"
- icon_state_powered = "tablet"
- icon_state_menu = "assign"
- greyscale_config = null
- greyscale_colors = null
- comp_light_luminosity = 6.3
- saved_identification = "John Doe"
- saved_job = "Citizen"
- device_theme = "syndicate"
- starting_programs = list(
- /datum/computer_file/program/contract_uplink,
- /datum/computer_file/program/crew_manifest,
- )
- device_theme = "syndicate"
-
- /// Contractor uplink system board has the user's data baked directly into it on creation
- var/datum/opposing_force/opfor_data
-
-/obj/item/modular_computer/pda/contractor/Initialize(mapload)
- . = ..()
- var/datum/computer_file/program/contract_uplink/uplink = locate() in stored_files
-
- active_program = uplink
-
- var/datum/computer_file/program/messenger/msg = locate() in stored_files
- if(msg)
- msg.invisible = TRUE
-
-/obj/item/modular_computer/pda/contractor/Destroy()
- opfor_data = null
- return ..()
-
-/obj/item/modular_computer/pda/contractor/UpdateDisplay()
- return
diff --git a/modular_skyrat/modules/contractor/code/objects/supplypod.dm b/modular_skyrat/modules/contractor/code/objects/supplypod.dm
index 2cca53e20b2..1f0e1632dd9 100644
--- a/modular_skyrat/modules/contractor/code/objects/supplypod.dm
+++ b/modular_skyrat/modules/contractor/code/objects/supplypod.dm
@@ -1,10 +1,10 @@
/obj/structure/closet/supplypod/extractionpod
var/datum/contractor_hub/contract_hub
var/datum/syndicate_contract/tied_contract
- var/recieving = FALSE
+ var/receiving = FALSE
/obj/structure/closet/supplypod/extractionpod/Destroy()
- if(!recieving)
+ if(!receiving)
return ..()
to_chat(tied_contract.contract.owner, " [span_userdanger("Extraction pod destroyed. Contract aborted.")]")
var/contract_id = tied_contract.id
@@ -18,7 +18,7 @@
return ..()
/obj/structure/closet/supplypod/extractionpod/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
- if(recieving && (atom_integrity <= 0))
+ if(receiving && (atom_integrity <= 0))
to_chat(tied_contract.contract.owner, " [span_userdanger("Extraction pod destroyed. Contract aborted.")]")
var/contract_id = tied_contract.id
if (contract_hub.current_contract == tied_contract)
diff --git a/modular_skyrat/modules/cryosleep/code/cryopod.dm b/modular_skyrat/modules/cryosleep/code/cryopod.dm
index 08a0bf8b8bb..f2d9c4b5bae 100644
--- a/modular_skyrat/modules/cryosleep/code/cryopod.dm
+++ b/modular_skyrat/modules/cryosleep/code/cryopod.dm
@@ -279,11 +279,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/cryopod, 32)
if(!(affected_contract.contract == objective))
continue
var/contract_id = affected_contract.id
- affected_contractor_hub.create_single_contract(objective.owner, affected_contract.payout_type)
affected_contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED
if (affected_contractor_hub.current_contract == objective)
affected_contractor_hub.current_contract = null
- to_chat(objective.owner.current, " [span_userdanger("Contract target out of reach. Contract rerolled.")]")
+ to_chat(objective.owner.current, " [span_userdanger("Contract target out of reach. Contract aborted.")]")
break
else if(istype(objective.target) && objective.target == mob_occupant.mind)
var/old_target = objective.target
diff --git a/modular_skyrat/modules/examinemore/code/examine_more.dm b/modular_skyrat/modules/examinemore/code/examine_more.dm
index e8500f18cf7..e1581f5a68e 100644
--- a/modular_skyrat/modules/examinemore/code/examine_more.dm
+++ b/modular_skyrat/modules/examinemore/code/examine_more.dm
@@ -103,21 +103,7 @@ would only be recognisable with someone that had the syndicate trait.
composed_message = "You note the following because of your loyalty to [faction_i]: "
composed_message += special_desc
. += composed_message
- //If they are a syndicate contractor or a syndicate
- if(EXAMINE_CHECK_CONTRACTOR)
- var/mob/living/carbon/human/human_user = user
- if(human_user.mind.special_role == ROLE_DRIFTING_CONTRACTOR)
- composed_message = "You note the following because of your [span_red("Contractor Status")]: "
- composed_message += special_desc
- . += composed_message
- else if(HAS_TRAIT(human_user, TRAIT_DETECTIVE)) //Useful detective!
- composed_message = "You note the following because of your brilliant Detective skills: "
- composed_message += special_desc
- . += composed_message
- else if((human_user.mind.special_role == ROLE_TRAITOR) || (ROLE_SYNDICATE in human_user.faction))
- composed_message = "You note the following because of your [span_red("[special_desc_affiliation ? special_desc_affiliation : "Syndicate Affiliation"]")]: "
- composed_message += special_desc
- . += composed_message
+
/*
* EXAMPLES
diff --git a/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm b/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
index 4cd6beea379..1bab63287bc 100644
--- a/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
+++ b/modular_skyrat/modules/modular_implants/code/nifsoft_catalog.dm
@@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(purchasable_nifsofts, list(
size = 3
tgui_id = "NtosNifsoftCatalog"
program_icon = "bag-shopping"
- usage_flags = PROGRAM_TABLET
+ usage_flags = PROGRAM_PDA
///What bank account is money being drawn out of?
var/datum/bank_account/paying_account
///What NIF are the NIFSofts being sent to?
diff --git a/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm b/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
index 322a5a8f47d..d86011d0636 100644
--- a/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
+++ b/modular_skyrat/modules/opposing_force/code/opposing_force_datum.dm
@@ -875,9 +875,6 @@
report += "[opfor_equipment.opposing_force_equipment.name] "
report += " "
- if(contractor_hub)
- report += contractor_round_end()
-
return report.Join("\n")
/// Adds the OPFOR in question to the ticket ping subsystem should it not be approved.
diff --git a/sound/effects/confirmdropoff.ogg b/sound/effects/confirmdropoff.ogg
new file mode 100644
index 00000000000..835d9319921
Binary files /dev/null and b/sound/effects/confirmdropoff.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index a1a4a7fad5f..c1741293134 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3216,6 +3216,10 @@
#include "code\modules\antagonists\traitor\components\traitor_objective_helpers.dm"
#include "code\modules\antagonists\traitor\components\traitor_objective_limit_per_time.dm"
#include "code\modules\antagonists\traitor\components\traitor_objective_mind_tracker.dm"
+#include "code\modules\antagonists\traitor\contractor\contract_teammate.dm"
+#include "code\modules\antagonists\traitor\contractor\contractor_hub.dm"
+#include "code\modules\antagonists\traitor\contractor\contractor_items.dm"
+#include "code\modules\antagonists\traitor\contractor\syndicate_contract.dm"
#include "code\modules\antagonists\traitor\objectives\assassination.dm"
#include "code\modules\antagonists\traitor\objectives\demoralise_assault.dm"
#include "code\modules\antagonists\traitor\objectives\destroy_heirloom.dm"
@@ -5127,6 +5131,7 @@
#include "code\modules\modular_computers\file_system\programs\techweb.dm"
#include "code\modules\modular_computers\file_system\programs\theme_selector.dm"
#include "code\modules\modular_computers\file_system\programs\wirecarp.dm"
+#include "code\modules\modular_computers\file_system\programs\antagonist\contractor_program.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm"
#include "code\modules\modular_computers\file_system\programs\maintenance\_maintenance_program.dm"
@@ -5871,6 +5876,7 @@
#include "code\modules\uplink\uplink_items\badass.dm"
#include "code\modules\uplink\uplink_items\bundle.dm"
#include "code\modules\uplink\uplink_items\clownops.dm"
+#include "code\modules\uplink\uplink_items\contractor.dm"
#include "code\modules\uplink\uplink_items\dangerous.dm"
#include "code\modules\uplink\uplink_items\device_tools.dm"
#include "code\modules\uplink\uplink_items\explosive.dm"
@@ -6861,25 +6867,9 @@
#include "modular_skyrat\modules\connecting_computer\code\connectable_component.dm"
#include "modular_skyrat\modules\connecting_computer\code\non_connecting_computers.dm"
#include "modular_skyrat\modules\container_emotes\code\container_emotes.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contract.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_hub.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_items.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_support.dm"
-#include "modular_skyrat\modules\contractor\code\datums\contractor_tablet.dm"
-#include "modular_skyrat\modules\contractor\code\datums\mind_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\objective.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\antag_datum.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\event.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\objective.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\outfit.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\ruleset.dm"
-#include "modular_skyrat\modules\contractor\code\datums\midround\trim.dm"
#include "modular_skyrat\modules\contractor\code\items\baton.dm"
-#include "modular_skyrat\modules\contractor\code\items\boxes.dm"
#include "modular_skyrat\modules\contractor\code\items\hook.dm"
#include "modular_skyrat\modules\contractor\code\items\misc.dm"
-#include "modular_skyrat\modules\contractor\code\items\tablet.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\modsuit.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\modules.dm"
#include "modular_skyrat\modules\contractor\code\items\modsuit\theme.dm"
diff --git a/tgui/packages/tgui/interfaces/SyndicateContractor.tsx b/tgui/packages/tgui/interfaces/SyndicateContractor.tsx
new file mode 100644
index 00000000000..cd6b1f8d8d3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/SyndicateContractor.tsx
@@ -0,0 +1,313 @@
+import { BooleanLike } from 'common/react';
+
+import { useBackend } from '../backend';
+import {
+ Box,
+ Button,
+ Flex,
+ Grid,
+ Icon,
+ LabeledList,
+ Modal,
+ NoticeBox,
+ Section,
+} from '../components';
+import { FakeTerminal } from '../components/FakeTerminal';
+import { NtosWindow } from '../layouts';
+
+const CONTRACT_STATUS_INACTIVE = 1;
+const CONTRACT_STATUS_ACTIVE = 2;
+const CONTRACT_STATUS_BOUNTY_CONSOLE_ACTIVE = 3;
+const CONTRACT_STATUS_EXTRACTING = 4;
+const CONTRACT_STATUS_COMPLETE = 5;
+const CONTRACT_STATUS_ABORTED = 6;
+
+export const SyndicateContractor = (props) => {
+ return (
+
+
+
+
+
+ );
+};
+
+type Data = {
+ error: string;
+ logged_in: BooleanLike;
+ first_load: BooleanLike;
+ info_screen: BooleanLike;
+ redeemable_tc: Number;
+ earned_tc: Number;
+ contracts_completed: Number;
+ contracts: ContractData[];
+ ongoing_contract: BooleanLike;
+ extraction_enroute: BooleanLike;
+ dropoff_direction: string;
+};
+
+type ContractData = {
+ id: Number;
+ status: Number;
+ target: string;
+ target_rank: string;
+ extraction_enroute: BooleanLike;
+ message: string;
+ contract: string;
+ dropoff: string;
+ payout: Number;
+ payout_bonus: Number;
+};
+
+export const SyndicateContractorContent = (props) => {
+ const { data, act } = useBackend();
+ const { error, logged_in, first_load, info_screen } = data;
+
+ const terminalMessages = [
+ 'Recording biometric data...',
+ 'Analyzing embedded syndicate info...',
+ 'STATUS CONFIRMED',
+ 'Contacting syndicate database...',
+ 'Awaiting response...',
+ 'Awaiting response...',
+ 'Awaiting response...',
+ 'Awaiting response...',
+ 'Awaiting response...',
+ 'Awaiting response...',
+ 'Response received, ack 4851234...',
+ 'CONFIRM ACC ' + Math.round(Math.random() * 20000),
+ 'Setting up private accounts...',
+ 'CONTRACTOR ACCOUNT CREATED',
+ 'Searching for available contracts...',
+ 'Searching for available contracts...',
+ 'Searching for available contracts...',
+ 'Searching for available contracts...',
+ 'CONTRACTS FOUND',
+ 'WELCOME, AGENT',
+ ];
+
+ const infoEntries = [
+ 'SyndTract v2.0',
+ '',
+ "We've identified potentional high-value targets that are",
+ 'currently assigned to your mission area. They are believed',
+ 'to hold valuable information which could be of immediate',
+ 'importance to our organisation.',
+ '',
+ 'Listed below are all of the contracts available to you. You',
+ 'are to bring the specified target to the designated',
+ 'drop-off, and contact us via this uplink. We will send',
+ 'a specialised extraction unit to put the body into.',
+ '',
+ 'We want targets alive - but we will sometimes pay slight',
+ "amounts if they're not, you just won't receive the shown",
+ 'bonus. You can redeem your payment through this uplink in',
+ 'the form of raw telecrystals, which can be put into your',
+ 'regular Syndicate uplink to purchase whatever you may need.',
+ 'We provide you with these crystals the moment you send the',
+ 'target up to us, which can be collected at anytime through',
+ 'this system.',
+ '',
+ 'Targets extracted will be ransomed back to the station once',
+ 'their use to us is fulfilled, with us providing you a small',
+ 'percentage cut. You may want to be mindful of them',
+ 'identifying you when they come back. We provide you with',
+ 'a standard contractor loadout, which will help cover your',
+ 'identity.',
+ ];
+
+ const errorPane = !!error && (
+
+
+
+
+
+
+
+ {error}
+
+
+
+
+ );
+
+ if (!logged_in) {
+ return (
+
+
+
+ {!!error && {error}}
+
+ );
+ }
+
+ if (logged_in && first_load) {
+ return (
+
+ act('PRG_set_first_load_finished')}
+ />
+
+ );
+ }
+
+ if (info_screen) {
+ return (
+ <>
+
+
+
+