mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 11:36:24 +01:00
[BOUNTY] Re-adds Contractors (#79737)
## About The Pull Request Adds Contractors back to the game as a kit that Infiltrators (Only midround/latejoin Traitors, not roundstart) can buy. Buying this will give you access to the new Contractor items in the uplink (which replaces the Contractor shop). I've also refactored things about contractor and how it's handled, such as removed its sleep calls, swapping its UI to tsx, making it actually functional within more modern TG code and the reworked Traitors as a whole, among other things. I also fixed the contractor guide paper (the text was broken) and made contractor kit boxes no longer empty. Contractor items only appear once the contractor kit is purchased, but they can't be bought until you make an account. Video demonstration https://github.com/tgstation/tgstation/assets/53777086/c6aed4e8-bbd4-4ba0-b6a2-9f58fd308d09 Regular Traitors can't access it  The Contractor UI (Modified to not have a shop anymore) - Ignore the Invalid Targets, those appear when there's no possible targets  Roundend report  ## Why It's Good For The Game Contractor was a fun way of playing Traitor that isn't relying on RNG objectives to pop up, this is instead constant stream of content for the contractor that encourages non-lethal play, and is now a viable alternative now that Infiltrators don't have Reputation anymore. Link to bounty: https://tgstation13.org/phpBB/viewtopic.php?f=5&t=35160 Relevant image from bounty  ## Changelog 🆑 add: Infiltrators (Latejoin/Midround traitors) can now buy and use Contract kits again. del: Contractor baton can now only be purchased once. /🆑 --------- Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -277,63 +277,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
|
||||
name = "Contract Kit"
|
||||
desc = "Supplied to Syndicate contractors."
|
||||
icon_state = "syndiebox"
|
||||
illustration = "writing_syndie"
|
||||
|
||||
/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/paper/contractor_guide
|
||||
name = "Contractor Guide"
|
||||
|
||||
/obj/item/paper/contractor_guide/Initialize(mapload)
|
||||
default_raw_text = {"<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h3>Using the tablet</h3>
|
||||
<ol>
|
||||
<li>Open the Syndicate Contract Uplink program.</li>
|
||||
<li>Here, you can accept a contract, and redeem your TC payments from completed contracts.</li>
|
||||
<li>The payment number shown in brackets is the bonus you'll receive when bringing your target <b>alive</b>. You receive the
|
||||
other number regardless of if they were alive or dead.</li>
|
||||
<li>Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them
|
||||
inside the pod.</li>
|
||||
</ol>
|
||||
|
||||
<p>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.</p>
|
||||
<p>The tablet can also be recharged at any cell charger.</p>
|
||||
<h3>Extracting</h3>
|
||||
<ol>
|
||||
<li>Make sure both yourself and your target are at the dropoff.</li>
|
||||
<li>Call the extraction, and stand back from the drop point.</li>
|
||||
<li>If it fails, make sure your target is inside, and there's a free space for the pod to land.</li>
|
||||
<li>Grab your target, and drag them into the pod.</li>
|
||||
</ol>
|
||||
<h3>Ransoms</h3>
|
||||
<p>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.</p>
|
||||
|
||||
<p>Good luck agent. You can burn this document with the supplied lighter.</p>"}
|
||||
return ..()
|
||||
|
||||
/obj/item/storage/box/syndie_kit
|
||||
name = "box"
|
||||
desc = "A sleek, sturdy box."
|
||||
@@ -821,6 +764,61 @@
|
||||
/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,
|
||||
/obj/item/clothing/gloves/krav_maga/combatglovesplus,
|
||||
/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(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()
|
||||
new /obj/item/clothing/head/helmet/space/syndicate/contract(src)
|
||||
new /obj/item/clothing/suit/space/syndicate/contract(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)
|
||||
|
||||
#undef KIT_RECON
|
||||
#undef KIT_BLOODY_SPAI
|
||||
#undef KIT_STEALTHY
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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.\
|
||||
<p>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.</p>\
|
||||
<p>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.</p>\
|
||||
<p>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.</p>\
|
||||
<h3>Using the tablet</h3>\
|
||||
<ol>\
|
||||
<li>Open the Syndicate Contract Uplink program.</li>\
|
||||
<li>Here, you can accept a contract, and redeem your TC payments from completed contracts.</li>\
|
||||
<li>The payment number shown in brackets is the bonus you'll receive when bringing your target <b>alive</b>. You receive the\
|
||||
other number regardless of if they were alive or dead.</li>\
|
||||
<li>Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them\
|
||||
inside the pod.</li>\
|
||||
</ol>\
|
||||
<p>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.</p>\
|
||||
<p>The tablet can also be recharged at any cell charger.</p>\
|
||||
<h3>Extracting</h3>\
|
||||
<ol>\
|
||||
<li>Make sure both yourself and your target are at the dropoff.</li>\
|
||||
<li>Call the extraction, and stand back from the drop point.</li>\
|
||||
<li>If it fails, make sure your target is inside, and there's a free space for the pod to land.</li>\
|
||||
<li>Grab your target, and drag them into the pod.</li>\
|
||||
</ol>\
|
||||
<h3>Ransoms</h3>\
|
||||
<p>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.</p>\
|
||||
<p>Good luck agent. You can burn this document with the supplied lighter.</p>"}
|
||||
@@ -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... <i>\"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...\"</i>"))
|
||||
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)
|
||||
@@ -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 += "<br>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("<br>")
|
||||
|
||||
///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")]!<br>"
|
||||
if(contractor_support_unit)
|
||||
sent_data += "<b>[contractor_support_unit.owner.key]</b> played <b>[contractor_support_unit.owner.current.name]</b>, their contractor support unit.<br>"
|
||||
return sent_data
|
||||
|
||||
/datum/antagonist/traitor/roundend_report_footer()
|
||||
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
|
||||
var/responses = jointext(GLOB.syndicate_code_response, ", ")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -544,14 +544,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -272,6 +272,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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
@@ -3110,6 +3110,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"
|
||||
@@ -4994,6 +4998,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"
|
||||
@@ -5730,6 +5735,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"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Box } from './Box';
|
||||
import { Component, Fragment } from 'inferno';
|
||||
|
||||
export class FakeTerminal extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.timer = null;
|
||||
this.state = {
|
||||
currentIndex: 0,
|
||||
currentDisplay: [],
|
||||
};
|
||||
}
|
||||
|
||||
tick() {
|
||||
const { props, state } = this;
|
||||
if (state.currentIndex <= props.allMessages.length) {
|
||||
this.setState((prevState) => {
|
||||
return {
|
||||
currentIndex: prevState.currentIndex + 1,
|
||||
};
|
||||
});
|
||||
const { currentDisplay } = state;
|
||||
currentDisplay.push(props.allMessages[state.currentIndex]);
|
||||
} else {
|
||||
clearTimeout(this.timer);
|
||||
setTimeout(props.onFinished, props.finishedTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { linesPerSecond = 2.5 } = this.props;
|
||||
this.timer = setInterval(() => this.tick(), 1000 / linesPerSecond);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Box m={1}>
|
||||
{this.state.currentDisplay.map((value) => (
|
||||
<Fragment key={value}>
|
||||
{value}
|
||||
<br />
|
||||
</Fragment>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
import { BooleanLike } from 'common/react';
|
||||
import { FakeTerminal } from '../components/FakeTerminal';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Flex, Grid, Icon, LabeledList, Modal, NoticeBox, Section } from '../components';
|
||||
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, context) => {
|
||||
return (
|
||||
<NtosWindow width={500} height={600}>
|
||||
<NtosWindow.Content scrollable>
|
||||
<SyndicateContractorContent />
|
||||
</NtosWindow.Content>
|
||||
</NtosWindow>
|
||||
);
|
||||
};
|
||||
|
||||
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, context) => {
|
||||
const { data, act } = useBackend<Data>(context);
|
||||
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 recieve 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 && (
|
||||
<Modal backgroundColor="red">
|
||||
<Flex align="center">
|
||||
<Flex.Item mr={2}>
|
||||
<Icon size={4} name="exclamation-triangle" />
|
||||
</Flex.Item>
|
||||
<Flex.Item mr={2} grow={1} textAlign="center">
|
||||
<Box width="260px" textAlign="left" minHeight="80px">
|
||||
{error}
|
||||
</Box>
|
||||
<Button content="Dismiss" onClick={() => act('PRG_clear_error')} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
if (!logged_in) {
|
||||
return (
|
||||
<Section minHeight="525px">
|
||||
<Box width="100%" textAlign="center">
|
||||
<Button
|
||||
content="REGISTER USER"
|
||||
color="transparent"
|
||||
onClick={() => act('PRG_login')}
|
||||
/>
|
||||
</Box>
|
||||
{!!error && <NoticeBox>{error}</NoticeBox>}
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
if (logged_in && first_load) {
|
||||
return (
|
||||
<Box backgroundColor="rgba(0, 0, 0, 0.8)" minHeight="525px">
|
||||
<FakeTerminal
|
||||
allMessages={terminalMessages}
|
||||
finishedTimeout={3000}
|
||||
onFinished={() => act('PRG_set_first_load_finished')}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (info_screen) {
|
||||
return (
|
||||
<>
|
||||
<Box backgroundColor="rgba(0, 0, 0, 0.8)" minHeight="500px">
|
||||
<FakeTerminal allMessages={infoEntries} linesPerSecond={10} />
|
||||
</Box>
|
||||
<Button
|
||||
fluid
|
||||
content="CONTINUE"
|
||||
color="transparent"
|
||||
textAlign="center"
|
||||
onClick={() => act('PRG_toggle_info')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{errorPane}
|
||||
<StatusPane state={props.state} />
|
||||
<ContractsTab />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const StatusPane = (props, context) => {
|
||||
const { act, data } = useBackend<Data>(context);
|
||||
const { redeemable_tc, earned_tc, contracts_completed } = data;
|
||||
|
||||
return (
|
||||
<Section
|
||||
title={
|
||||
<>
|
||||
Contractor Status
|
||||
<Button
|
||||
content="View Information Again"
|
||||
color="transparent"
|
||||
mb={0}
|
||||
ml={1}
|
||||
onClick={() => act('PRG_toggle_info')}
|
||||
/>
|
||||
</>
|
||||
}>
|
||||
<Grid>
|
||||
<Grid.Column size={0.85}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="TC Available"
|
||||
buttons={
|
||||
<Button
|
||||
content="Claim"
|
||||
disabled={redeemable_tc <= 0}
|
||||
onClick={() => act('PRG_redeem_TC')}
|
||||
/>
|
||||
}>
|
||||
{redeemable_tc}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="TC Earned">{earned_tc}</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Grid.Column>
|
||||
<Grid.Column>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Contracts Completed">
|
||||
{contracts_completed}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Current Status">ACTIVE</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ContractsTab = (props, context) => {
|
||||
const { act, data } = useBackend<Data>(context);
|
||||
const { contracts, ongoing_contract, extraction_enroute, dropoff_direction } =
|
||||
data;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Section
|
||||
title="Available Contracts"
|
||||
buttons={
|
||||
<Button
|
||||
content="Call Extraction"
|
||||
disabled={!ongoing_contract || extraction_enroute}
|
||||
onClick={() => act('PRG_call_extraction')}
|
||||
/>
|
||||
}>
|
||||
{contracts.map((contract) => {
|
||||
if (ongoing_contract && contract.status !== CONTRACT_STATUS_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
const active = contract.status > CONTRACT_STATUS_INACTIVE;
|
||||
if (contract.status >= CONTRACT_STATUS_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Section
|
||||
key={contract.target}
|
||||
title={
|
||||
contract.target
|
||||
? `${contract.target} (${contract.target_rank})`
|
||||
: 'Invalid Target'
|
||||
}
|
||||
buttons={
|
||||
<>
|
||||
<Box inline bold mr={1}>
|
||||
{contract.payout} (+{contract.payout_bonus}) TC
|
||||
</Box>
|
||||
<Button
|
||||
content={active ? 'Abort' : 'Accept'}
|
||||
disabled={contract.extraction_enroute}
|
||||
color={active && 'bad'}
|
||||
onClick={() =>
|
||||
act('PRG_contract' + (active ? '_abort' : '-accept'), {
|
||||
contract_id: contract.id,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}>
|
||||
<Grid>
|
||||
<Grid.Column>{contract.message}</Grid.Column>
|
||||
<Grid.Column size={0.5}>
|
||||
<Box bold mb={1}>
|
||||
Dropoff Location:
|
||||
</Box>
|
||||
<Box>{contract.dropoff}</Box>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
<Section
|
||||
title="Dropoff Locator"
|
||||
textAlign="center"
|
||||
opacity={ongoing_contract ? 100 : 0}>
|
||||
<Box bold>{dropoff_direction}</Box>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user