diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index cabcc0a17d..b849e272e7 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -34,6 +34,21 @@ #define HIJACK_HIJACKER 1 //Needs to be present for shuttle to be hijacked #define HIJACK_PREVENT 2 //Prevents hijacking same way as non-antags +//Syndicate Contracts +#define CONTRACT_STATUS_INACTIVE 1 +#define CONTRACT_STATUS_ACTIVE 2 +#define CONTRACT_STATUS_BOUNTY_CONSOLE_ACTIVE 3 +#define CONTRACT_STATUS_EXTRACTING 4 +#define CONTRACT_STATUS_COMPLETE 5 +#define CONTRACT_STATUS_ABORTED 6 + +#define CONTRACT_PAYOUT_LARGE 1 +#define CONTRACT_PAYOUT_MEDIUM 2 +#define CONTRACT_PAYOUT_SMALL 3 + +#define CONTRACT_UPLINK_PAGE_CONTRACTS "CONTRACTS" +#define CONTRACT_UPLINK_PAGE_HUB "HUB" + //Overthrow time to update heads obj #define OBJECTIVE_UPDATING_TIME 300 diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index dd2d464cf0..b63da0e1b1 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -49,7 +49,7 @@ alert("Invalid name.") return "" return sanitize(t) - + /proc/sanitize_filename(t) return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) @@ -798,3 +798,19 @@ GLOBAL_LIST_INIT(binary, list("0","1")) out += prob(replaceprob)? pick(replacementchars) : char return out.Join("") +/proc/readable_corrupted_text(text) + var/list/corruption_options = list("..", "£%", "~~\"", "!!", "*", "^", "$!", "-", "}", "?") + var/corrupted_text = "" + // Have every letter have a chance of creating corruption on either side + // Small chance of letters being removed in place of corruption - still overall readable + for(var/letter_index = 1; letter_index <= length(text); letter_index++) + var/letter = text[letter_index] + if(prob(15)) + corrupted_text += pick(corruption_options) + if(prob(95)) + corrupted_text += letter + else + corrupted_text += pick(corruption_options) + if(prob(15)) + corrupted_text += pick(corruption_options) + return corrupted_text \ No newline at end of file diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index e84b942151..5fa6dc5a4c 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1075,7 +1075,7 @@ GLOBAL_LIST_EMPTY(cult_contraband) var/mob/living/carbon/human/H = owner H.equip_in_one_of_slots(I, list("backpack" = SLOT_IN_BACKPACK)) hoarded_item = I - + GLOBAL_LIST_EMPTY(possible_sabotages) @@ -1092,7 +1092,7 @@ GLOBAL_LIST_EMPTY(possible_sabotages) if(!GLOB.possible_sabotages.len)//Only need to fill the list when it's needed. for(var/I in subtypesof(/datum/sabotage_objective)) new I - + /datum/objective/sabotage/find_target() var/list/datum/mind/owners = get_owners() var/approved_targets = list() @@ -1155,3 +1155,23 @@ GLOBAL_LIST_EMPTY(possible_sabotages) /datum/objective/flavor/wizard flavor_file = "strings/flavor_objectives/wizard.txt" + +/datum/objective/contract + var/payout = 0 + var/payout_bonus = 0 + var/area/dropoff = null + +// Generate a random valid area on the station that the dropoff will happen. +/datum/objective/contract/proc/generate_dropoff() + var/found = FALSE + while(!found) + var/area/dropoff_area = pick(GLOB.sortedAreas) + if(dropoff_area && is_station_level(dropoff_area.z) && dropoff_area.valid_territory) + dropoff = dropoff_area + found = TRUE + +// Check if both the contractor and contract target are at the dropoff point. +/datum/objective/contract/proc/dropoff_check(mob/user, mob/target) + var/area/user_area = get_area(user) + var/area/target_area = get_area(target) + return (istype(user_area, dropoff) && istype(target_area, dropoff)) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 611870912c..f87e0ea075 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -397,3 +397,47 @@ /obj/item/storage/box/syndie_kit/revolver/PopulateContents() new /obj/item/gun/ballistic/revolver(src) new /obj/item/ammo_box/a357(src) + +/obj/item/storage/box/syndie_kit/contract_kit + name = "contractor kit" + desc = "Supplied to Syndicate contractors in active mission areas." + +/obj/item/storage/box/syndie_kit/contract_kit/PopulateContents() + new /obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink(src) + new /obj/item/storage/box/syndie_kit/space/contract(src) + new /obj/item/clothing/under/chameleon(src) + new /obj/item/clothing/mask/chameleon(src) + new /obj/item/card/id/syndicate(src) + + // All 4 TC or less - some nukeops only items, but fit nicely to the theme. + var/list/item_list = list( + /obj/item/storage/backpack/duffelbag/syndie/x4, + /obj/item/storage/box/syndie_kit/emp, + /obj/item/storage/box/syndie_kit/throwing_weapons, + /obj/item/gun/syringe/syndicate, + /obj/item/pen/edagger, + /obj/item/pen/sleepy, + /obj/item/gun/ballistic/automatic/toy/pistol/riot, + /obj/item/flashlight/emp, + /obj/item/reagent_containers/syringe/mulligan, + /obj/item/clothing/shoes/chameleon/noslip, + /obj/item/storage/toolbox/syndicate, + /obj/item/storage/firstaid/tactical, + /obj/item/storage/backpack/duffelbag/syndie/surgery, + /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 + ) + + var/obj/item1 = pick_n_take(item_list) + var/obj/item2 = pick_n_take(item_list) + + // Create two, non repeat items from the list. + new item1(src) + new item2(src) + +/obj/item/storage/box/syndie_kit/space/contract/PopulateContents() + new /obj/item/clothing/suit/space/syndicate/black/red(src) + new /obj/item/clothing/head/helmet/space/syndicate/black/red(src) \ No newline at end of file diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 68963078c5..12c739b725 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -13,6 +13,10 @@ var/should_give_codewords = TRUE var/should_equip = TRUE var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment + var/datum/syndicate_contract/current_contract + var/list/assigned_contracts = list() + var/contract_TC_payed_out = 0 + var/contract_TC_to_redeem = 0 hijack_speed = 0.5 //10 seconds per hijack stage by default /datum/antagonist/traitor/on_gain() @@ -26,6 +30,14 @@ finalize_traitor() ..() +/datum/antagonist/traitor/proc/create_contracts() + var/contract_generation_quantity = 6 + + for(var/i = 1; i <= contract_generation_quantity; i++) + var/datum/syndicate_contract/contract_to_add = new(owner) + contract_to_add.id = i + assigned_contracts.Add(contract_to_add) + /datum/antagonist/traitor/apply_innate_effects() if(owner.assigned_role == "Clown") var/mob/living/carbon/human/traitor_mob = owner.current @@ -413,6 +425,20 @@ var/special_role_text = lowertext(name) + var/completed_contracts = 0 + var/tc_total = contract_TC_payed_out + contract_TC_to_redeem + for (var/datum/syndicate_contract/contract in assigned_contracts) + if (contract.status == CONTRACT_STATUS_COMPLETE) + completed_contracts++ + + + if (completed_contracts > 0) + var/pluralCheck = "contract" + if (completed_contracts > 1) + pluralCheck = "contracts" + result += "
Completed [completed_contracts] [pluralCheck] for a total of \ + [tc_total] TC!
" + if(traitorwin) result += "The [special_role_text] was successful!" else diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index adfbe3308a..d31b18e1d7 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -42,6 +42,8 @@ var/soundVolume = 80 //Volume to play sounds at. Ignores the cap var/bay //Used specifically for the centcom_podlauncher datum. Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map. var/list/explosionSize = list(0,0,2,3) + var/stay_after_drop = FALSE + var/specialised = TRUE // It's not a general use pod for cargo/admin use /obj/structure/closet/supplypod/bluespacepod style = STYLE_BLUESPACE @@ -49,6 +51,15 @@ explosionSize = list(0,0,1,2) landingDelay = 15 //Slightly quicker than the supplypod +/obj/structure/closet/supplypod/extractionpod + name = "Syndicate Extraction Pod" + desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas." + specialised = TRUE + style = STYLE_SYNDICATE + bluespace = TRUE + explosionSize = list(0,0,1,2) + landingDelay = 25 //Slightly longer than others + /obj/structure/closet/supplypod/centcompod style = STYLE_CENTCOM bluespace = TRUE @@ -56,6 +67,13 @@ landingDelay = 20 //Very speedy! resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF +/obj/structure/closet/supplypod/proc/specialisedPod() + return 1 + +/obj/structure/closet/supplypod/extractionpod/specialisedPod(atom/movable/holder) + holder.forceMove(pick(GLOB.holdingfacility)) // land in ninja jail + open(holder, forced = TRUE) + /obj/structure/closet/supplypod/Initialize() . = ..() setStyle(style, TRUE) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly @@ -76,7 +94,7 @@ return style = chosenStyle icon_state = POD_STYLES[chosenStyle][POD_ICON_STATE] //POD_STYLES is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array. - if (!adminNamed) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum + if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum name = POD_STYLES[chosenStyle][POD_NAME] desc = POD_STYLES[chosenStyle][POD_DESC] update_icon() @@ -96,6 +114,30 @@ /obj/structure/closet/supplypod/toggle(mob/living/user) //Supplypods shouldn't be able to be manually opened under any circumstances, as the open() proc generates supply order datums return +/obj/structure/closet/supplypod/proc/handleReturningClose(atom/movable/holder, returntobay) + opened = FALSE + INVOKE_ASYNC(holder, .proc/setClosed) //Use the INVOKE_ASYNC proc to call setClosed() on whatever the holder may be, without giving the atom/movable base class a setClosed() proc definition + for(var/atom/movable/O in get_turf(holder)) + if ((ismob(O) && !isliving(O)) || (is_type_in_typecache(O, GLOB.blacklisted_cargo_types) && !isliving(O))) //We dont want to take ghosts with us, and we don't want blacklisted items going, but we allow mobs. + continue + O.forceMove(holder) //Put objects inside before we close + var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up + risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot + animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod + if(returntobay) + holder.forceMove(bay) //Move the pod back to centcom, where it belongs + QDEL_IN(risingPod, 10) + reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) + bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever + open(holder, forced = TRUE) + else + reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) + bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever + QDEL_IN(risingPod, 10) + audible_message("The pod hisses, closing quickly and launching itself away from the station.", "The ground vibrates, the nearby pod launching away from the station.") + stay_after_drop = FALSE + specialisedPod(holder) // Do special actions for specialised pods - this is likely if we were already doing manual launches + /obj/structure/closet/supplypod/proc/preOpen() //Called before the open() proc. Handles anything that occurs right as the pod lands. var/turf/T = get_turf(src) var/list/B = explosionSize //Mostly because B is more readable than explosionSize :p @@ -172,7 +214,8 @@ if (style == STYLE_SEETHROUGH) depart(src) else - addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time + if(!stay_after_drop) // Departing should be handled manually + addtimer(CALLBACK(src, .proc/depart, holder), departureDelay) //Finish up the pod's duties after a certain amount of time /obj/structure/closet/supplypod/proc/depart(atom/movable/holder) if (leavingSound) @@ -187,20 +230,14 @@ qdel(holder) /obj/structure/closet/supplypod/centcompod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true - opened = FALSE - INVOKE_ASYNC(holder, .proc/setClosed) //Use the INVOKE_ASYNC proc to call setClosed() on whatever the holder may be, without giving the atom/movable base class a setClosed() proc definition - for (var/atom/movable/O in get_turf(holder)) - if (ismob(O) && !isliving(O)) //We dont want to take ghosts with us - continue - O.forceMove(holder) //Put objects inside before we close - var/obj/effect/temp_visual/risingPod = new /obj/effect/abstract/DPfall(get_turf(holder), src) //Make a nice animation of flying back up - risingPod.pixel_z = 0 //The initial value of risingPod's pixel_z is 200 because it normally comes down from a high spot - holder.forceMove(bay) //Move the pod back to centcom, where it belongs - animate(risingPod, pixel_z = 200, time = 10, easing = LINEAR_EASING) //Animate our rising pod - QDEL_IN(risingPod, 10) - reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open() ) - bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever - open(holder, forced = TRUE) + handleReturningClose(holder, TRUE) + +/obj/structure/closet/supplypod/extractionpod/close(atom/movable/holder) //handles closing, and returns pod - deletes itself when returned + if(!holder) + holder = src + if(leavingSound) + playsound(get_turf(holder), leavingSound, soundVolume, 0, 0) + handleReturningClose(holder, FALSE) /obj/structure/closet/supplypod/proc/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open() proc for more details update_icon() diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 54e43a8731..154e3b5e41 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -20,4 +20,16 @@ finish_color = pick("red","blue","brown","green","black") icon_state = "tablet-[finish_color]" icon_state_unpowered = "tablet-[finish_color]" - icon_state_powered = "tablet-[finish_color]" \ No newline at end of file + icon_state_powered = "tablet-[finish_color]" + +/obj/item/modular_computer/tablet/syndicate_contract_uplink + name = "tablet computer" + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "tablet-red" + icon_state_unpowered = "tablet" + icon_state_powered = "tablet" + icon_state_menu = "hostile" + w_class = WEIGHT_CLASS_SMALL + slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT + comp_light_luminosity = 4.3 + finish_color = "red" \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index c793ae22f6..c72b3061d0 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -27,3 +27,15 @@ install_component(new /obj/item/computer_hardware/hard_drive/small) install_component(new /obj/item/computer_hardware/network_card) install_component(new /obj/item/computer_hardware/printer/mini) + +// Given by the syndicate as part of the contract uplink bundle +/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() + . = ..() + var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = new + hard_drive.store_file(new /datum/computer_file/program/contract_uplink) + install_component(new /obj/item/computer_hardware/processor_unit/small) + install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) + install_component(hard_drive) + install_component(new /obj/item/computer_hardware/network_card) + install_component(new /obj/item/computer_hardware/card_slot) + install_component(new /obj/item/computer_hardware/printer/mini) \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 4109b2c3f0..303c74a8f8 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -158,6 +158,13 @@ icon_state = "ssd_mini" w_class = WEIGHT_CLASS_TINY +// Syndicate variant - very slight better +/obj/item/computer_hardware/hard_drive/small/syndicate + desc = "An efficient SSD for portable devices developed by a rival organisation." + power_usage = 8 + max_capacity = 70 + var/datum/antagonist/traitor/traitor_data // Syndicate hard drive has the user's data baked directly into it on creation + /obj/item/computer_hardware/hard_drive/micro name = "micro solid state drive" desc = "A highly efficient SSD chip for portable devices." diff --git a/code/modules/uplink/uplink_items/uplink_bundles.dm b/code/modules/uplink/uplink_items/uplink_bundles.dm index 23fb23f198..8b29c246b9 100644 --- a/code/modules/uplink/uplink_items/uplink_bundles.dm +++ b/code/modules/uplink/uplink_items/uplink_bundles.dm @@ -30,6 +30,17 @@ cost = 14 // normally 16 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/bundles_TC/contract_kit + name = "Contract Kit" + desc = "The Syndicate have a number of urgent contracts for you to take on, become a contractor and complete them for TC. Upon purchase, \ + you'll be granted your own contract uplink embedded within the supplied tablet computer. Additonally, you'll be granted contractor \ + gear to help with your mission - comes supplied with the tablet, space suit, chameleon jumpsuit and mask, agent card, and two \ + randomly selected low cost items." + item = /obj/item/storage/box/syndie_kit/contract_kit + cost = 20 + player_minimum = 20 + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + /datum/uplink_item/bundles_TC/cybernetics_bundle name = "Cybernetic Implants Bundle" desc = "A random selection of cybernetic implants. Guaranteed 5 high quality implants. Comes with an autosurgeon." diff --git a/tgstation.dme b/tgstation.dme index 6fe8ead35b..44218f00fc 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -79,8 +79,8 @@ #include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" -#include "code\__DEFINES\power.dm" #include "code\__DEFINES\pool.dm" +#include "code\__DEFINES\power.dm" #include "code\__DEFINES\preferences.dm" #include "code\__DEFINES\procpath.dm" #include "code\__DEFINES\profile.dm" @@ -1468,6 +1468,7 @@ #include "code\modules\antagonists\swarmer\swarmer.dm" #include "code\modules\antagonists\swarmer\swarmer_event.dm" #include "code\modules\antagonists\traitor\datum_traitor.dm" +#include "code\modules\antagonists\traitor\syndicate_contract.dm" #include "code\modules\antagonists\traitor\equipment\Malf_Modules.dm" #include "code\modules\antagonists\traitor\IAA\internal_affairs.dm" #include "code\modules\antagonists\valentines\heartbreaker.dm" @@ -2474,6 +2475,7 @@ #include "code\modules\modular_computers\file_system\programs\nttransfer.dm" #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\sm_monitor.dm" +#include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.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\hardware\_hardware.dm"