From 06c66617e6c6cebfd58ad1c9f5638b2a44cf25c4 Mon Sep 17 00:00:00 2001 From: Putnam Date: Sun, 19 Jan 2020 18:58:10 -0800 Subject: [PATCH] m o r e traitor objectives --- code/__HELPERS/roundend.dm | 6 +- code/controllers/subsystem/ticker.dm | 2 + code/datums/traits/negative.dm | 3 + code/game/gamemodes/objective.dm | 88 ++++++++++++++- code/game/gamemodes/objective_sabotage.dm | 106 ++++++++++++++++++ code/modules/admin/antag_panel.dm | 2 +- .../antagonists/traitor/datum_traitor.dm | 28 +++-- 7 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 code/game/gamemodes/objective_sabotage.dm diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 49825bcb50..5102eb4802 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -3,6 +3,9 @@ #define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only. /datum/controller/subsystem/ticker/proc/gather_roundend_feedback() + var/datum/station_state/end_state = new /datum/station_state() + end_state.count() + station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100) gather_antag_data() record_nuke_disk_location() var/json_file = file("[GLOB.log_directory]/round_end_data.json") @@ -71,9 +74,6 @@ mob_data += list("name" = m.name, "typepath" = m.type) var/pos = length(file_data["[escaped]"]["[category]"]) + 1 file_data["[escaped]"]["[category]"]["[pos]"] = mob_data - var/datum/station_state/end_state = new /datum/station_state() - end_state.count() - var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100) file_data["additional data"]["station integrity"] = station_integrity WRITE_FILE(json_file, json_encode(file_data)) SSblackbox.record_feedback("nested tally", "round_end_stats", num_survivors, list("survivors", "total")) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index c9562bcfd1..ceb003210e 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -68,6 +68,8 @@ SUBSYSTEM_DEF(ticker) var/modevoted = FALSE //Have we sent a vote for the gamemode? + var/station_integrity = 100 // stored at roundend for use in some antag goals + /datum/controller/subsystem/ticker/Initialize(timeofday) load_mode() diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index d556d02ebb..f1d394ee2e 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -39,6 +39,8 @@ var/obj/item/heirloom var/where +GLOBAL_LIST_EMPTY(family_heirlooms) + /datum/quirk/family_heirloom/on_spawn() var/mob/living/carbon/human/H = quirk_holder var/obj/item/heirloom_type @@ -77,6 +79,7 @@ /obj/item/lighter, /obj/item/dice/d20) heirloom = new heirloom_type(get_turf(quirk_holder)) + GLOB.family_heirlooms += heirloom var/list/slots = list( "in your left pocket" = SLOT_L_STORE, "in your right pocket" = SLOT_R_STORE, diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index bb18b89678..4b7eebb5b9 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -366,6 +366,28 @@ GLOBAL_LIST_EMPTY(objectives) return FALSE return TRUE +/datum/objective/breakout + name = "breakout" + martyr_compatible = 1 + var/target_role_type = 0 + var/human_check = TRUE + +/datum/objective/breakout/check_completion() + return !target || considered_escaped(target, enforce_human = human_check) + +/datum/objective/breakout/find_target_by_role(role, role_type=0, invert=0) + if(!invert) + target_role_type = role_type + ..() + return target + +/datum/objective/breakout/update_explanation_text() + ..() + if(target && target.current) + explanation_text = "Make sure [target.name], the [!target_role_type ? target.assigned_role : target.special_role] escapes on the shuttle or an escape pod alive and without being in custody." + else + explanation_text = "Free Objective" + /datum/objective/escape/escape_with_identity name = "escape with identity" var/target_real_name // Has to be stored because the target's real_name can change over the course of the round @@ -583,7 +605,6 @@ GLOBAL_LIST_EMPTY(possible_items_special) explanation_text = "Do not give up or lose [targetinfo.name]." steal_target = targetinfo.targetitem - /datum/objective/download name = "download" @@ -998,3 +1019,68 @@ GLOBAL_LIST_EMPTY(possible_items_special) if(I == horded_item) return TRUE return FALSE + +/datum/objective/horde/heirloom + name = "steal heirloom" + +/datum/objective/horde/heirloom/proc/find_target() + set_target(pick(GLOB.family_heirlooms)) + +GLOBAL_LIST_EMPTY(possible_sabotages) +// For saboteurs. Go in and cause some trouble somewhere. Not necessarily breaking things, just sufficiently troublemaking. +/datum/objective/sabotage + name = "sabotage" + var/datum/sabotage_objective/targetinfo = null //composition > inheritance. + +/datum/objective/sabotage/get_target() + return targetinfo.sabotage_type + +/datum/objective/sabotage/New() + ..() + 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() + check_sabotages: + for(var/datum/sabotage_objective/possible_sabotage in GLOB.possible_sabotages) + if(!is_unique_objective(possible_sabotage.sabotage_type) || possible_sabotage.check_conditions()) + continue + for(var/datum/mind/M in owners) + if(M.current.mind.assigned_role in possible_sabotage.excludefromjob) + continue check_sabotages + approved_targets += possible_sabotage + return set_target(safepick(approved_targets)) + +/datum/objective/sabotage/proc/set_target(datum/sabotage_objective/sabo) + if(sabo) + targetinfo = sabo + explanation_text = "[targetinfo.name]" + give_special_equipment(targetinfo.special_equipment) + return sabo + else + explanation_text = "Free objective" + return + +/datum/objective/sabotage/check_completion() + return targetinfo.check_conditions() + +/datum/objective/flavor + name = "Flavor" + completable = FALSE + +/datum/objective/flavor/proc/forge_objective() + +/datum/objective/flavor/traitor/forge_objective() + explanation_text = pickweight( + "Teach the heads of staff a lesson they will never forget." = 1, + "Show Nanotrasen the utility of a pure oxygen atmosphere." = 2, + "The Donk Corporation has hired you with the task to maim the crew in whatever way you can. Strain the resources of medical staff, and create a hostile working enviroment for human resources." = 3, + "The Waffle Corporation has given you the task to create the biggest prank the station's security force has seen! Harass security, and don't stop while you can still honk!" = 4, + "Kill one of the station's beloved pets. Make a show of it." = 3, + "Get illegal technology spread through the station." = 3, + "Slow down the process of research as much as possible." = (owner.assigned_role in list("Research Director", "Scientist", "Roboticist") ? 6 : 0), + "Channel your inner rat. Cut wires throughout the station" = (owner.assigned_role in list("Station Engineer", "Atmos Technician", "Assistant") ? 6 : 0), + ) diff --git a/code/game/gamemodes/objective_sabotage.dm b/code/game/gamemodes/objective_sabotage.dm new file mode 100644 index 0000000000..0ede317626 --- /dev/null +++ b/code/game/gamemodes/objective_sabotage.dm @@ -0,0 +1,106 @@ +/datum/sabotage_objective + var/name = "Free Objective" + var/sabotage_type = "nothing" + var/special_equipment = list() + var/list/excludefromjob = list() + +/datum/sabotage_objective/New() + ..() + if(sabotage_type!="nothing") + GLOB.possible_sabotages += src + +/datum/sabotage_objective/proc/check_conditions() + return TRUE + +/datum/sabotage_objective/processing + var/won = FALSE + +/datum/sabotage_objective/processing/New() + ..() + START_PROCESSING(SSprocessing, src) + +/datum/sabotage_objective/processing/proc/check_condition_processing() + return TRUE + +/datum/sabotage_objective/processing/process() + won = check_condition_processing() + if(won) + STOP_PROCESSING(SSprocessing,src) + +/datum/sabotage_objective/processing/power_sink + name = "Drain at least 1 gigajoule of power using a power sink." + sabotage_type = "powersink" + special_equipment = list(/obj/item/powersink) + var/sink_found = FALSE + var/count = 0 + +/datum/sabotage_objective/processing/power_sink/check_condition_processing() + count += 1 + if(count==10 || sink_found) // doesn't need to fire that often unless a sink exists + var/sink_found_this_time = FALSE + for(var/datum/powernet/PN in GLOB.powernets) + for(var/obj/item/powersink/sink in PN.nodes) + sink_found_this_time = TRUE + if(sink.power_drained>1e9) + return TRUE + sink_found = sink_found_this_time + count = 0 + return FALSE + +/obj/item/paper/guides/antag/supermatter_sabotage + info = "Ways to sabotage a supermatter:
\ + " + +/datum/sabotage_objective/processing/supermatter + name = "Sabotage the supermatter so that it goes under 50% integrity." + sabotage_type = "supermatter" + special_equipment = list(/obj/item/paper/guides/antag/supermatter_sabotage) + var/list/supermatters = list() + excludefromjob = list("Chief Engineer", "Station Engineer", "Atmospheric Technician") + +/datum/sabotage_objective/processing/supermatter/check_condition_processing() + if(!supermatters.len) + supermatters = list() + for(var/obj/machinery/power/supermatter_crystal/S in GLOB.machines) + // Delaminating, not within coverage, not on a tile. + if (!isturf(S.loc) || !(is_station_level(S.z) || is_mining_level(S.z))) + continue + supermatters.Add(S) + for(var/obj/machinery/power/supermatter_crystal/S in supermatters) + if(S.get_integrity() < 50) + return TRUE + return FALSE + +/datum/sabotage_objective/station_integrity + name = "Make sure the station is at less than 80% integrity by the end. Smash walls, windows etc. to reach this goal." + sabotage_type = "integrity" + +/datum/sabotage_objective/station_integrity/check_conditions() + return SSticker.station_integrity < 80 + +/datum/sabotage_objective/cloner + name = "Destroy all Nanotrasen cloning machines." + sabotage_type = "cloner" + +/datum/sabotage_objective/cloner/check_conditions() + return !(locate(/obj/machinery/clonepod) in GLOB.machines) + +/datum/sabotage_objective/ai_law + name = "Upload a hacked law to the AI." + sabotage_type = "ailaw" + special_equipment = list(/obj/item/aiModule/syndicate) + excludefromjob = list("Chief Engineer","Research Director","Head of Personnel","Captain","Chief Medical Officer","Head Of Security") + +/datum/sabotage_objective/ai_law/check_conditions() + for (var/i in GLOB.ai_list) + var/mob/living/silicon/ai/aiPlayer = i + if(aiPlayer.mind && length(aiPlayer.laws.hacked)) + return TRUE + return FALSE diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm index ffb086a18d..19e53b09b4 100644 --- a/code/modules/admin/antag_panel.dm +++ b/code/modules/admin/antag_panel.dm @@ -47,7 +47,7 @@ GLOBAL_VAR(antag_prototypes) else var/obj_count = 1 for(var/datum/objective/objective in objectives) - result += "[obj_count]: [objective.explanation_text] Edit Delete [objective.completed ? "Mark as incomplete" : "Mark as complete"]
" + result += "[obj_count]: [objective.explanation_text] Edit Delete [objective.completed ? "Mark as incomplete" : "Mark as complete"]
" obj_count++ result += "Add objective
" result += "Announce objectives
" diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 25fe24b605..62fdf56b71 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -77,19 +77,23 @@ var/is_hijacker = FALSE var/datum/game_mode/dynamic/mode var/is_dynamic = FALSE + var/hijack_prob = 0 if(istype(SSticker.mode,/datum/game_mode/dynamic)) mode = SSticker.mode is_dynamic = TRUE - if(mode.storyteller.flags & NO_ASSASSIN) - is_hijacker = FALSE + if(mode.threat >= CONFIG_GET(number/dynamic_hijack_cost)) + hijack_prob = CLAMP(mode.threat_level-50,0,20) if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit) - is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement)) + is_hijacker = (prob(hijack_prob) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement)) else var/indice_pop = min(10,round(GLOB.joined_player_list.len/mode.pop_per_requirement)+1) - is_hijacker = (prob(10) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop])) + is_hijacker = (prob(hijack_prob) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop])) + if(mode.storyteller.flags & NO_ASSASSIN) + is_hijacker = FALSE else if (GLOB.joined_player_list.len >= 30) // Less murderboning on lowpop thanks + hijack_prob = 10 is_hijacker = prob(10) - var/martyr_chance = prob(20) + var/martyr_chance = prob(hijack_prob*2) var/objective_count = is_hijacker //Hijacking counts towards number of objectives if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors if(!SSticker.mode.exchange_red) @@ -170,7 +174,7 @@ if(istype(SSticker.mode,/datum/game_mode/dynamic)) mode = SSticker.mode is_dynamic = TRUE - assassin_prob = mode.threat_level*(2/3) + assassin_prob = max(0,mode.threat_level-20) if(prob(assassin_prob)) if(is_dynamic) var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost) @@ -198,11 +202,21 @@ download_objective.owner = owner download_objective.gen_amount_goal() add_objective(download_objective) - else + else if(prob(40)) var/datum/objective/steal/steal_objective = new steal_objective.owner = owner steal_objective.find_target() add_objective(steal_objective) + else if(prob(40)) + var/datum/objective/sabotage/sabotage_objective = new + sabotage_objective.owner = owner + sabotage_objective.find_target() + add_objective(sabotage_objective) + else + var/datum/objective/flavor/traitor/flavor_objective = new + flavor_objective.owner = owner + flavor_objective.forge_objective() + add_objective(flavor_objective) /datum/antagonist/traitor/proc/forge_single_AI_objective() .=1