diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 145055206c..ea380cf49b 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -1,3 +1,6 @@ +#define TRAITOR_HUMAN /datum/traitor_class/human/freeform +#define TRAITOR_AI /datum/traitor_class/ai + #define NUKE_RESULT_FLUKE 0 #define NUKE_RESULT_NUKE_WIN 1 #define NUKE_RESULT_CREW_WIN 2 diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index a8e03946b6..e73465d785 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(uplinks) var/compact_mode = FALSE var/debug = FALSE -/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate) +/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE @@ -47,7 +47,11 @@ GLOBAL_LIST_EMPTY(uplinks) RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation) GLOB.uplinks += src - uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted) + var/list/filters = list() + if(istype(traitor_class)) + filters = traitor_class.uplink_filters + starting_tc = traitor_class.TC + uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted, filters) if(_owner) owner = _owner diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 29bb694a6f..edd5c25e2e 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -256,9 +256,11 @@ remove_rev() SSticker.mode.update_cult_icons_removed(src) -/datum/mind/proc/equip_traitor(employer = "The Syndicate", silent = FALSE, datum/antagonist/uplink_owner) +/datum/mind/proc/equip_traitor(datum/traitor_class/traitor_class, silent = FALSE, datum/antagonist/uplink_owner) if(!current) return + if(!traitor_class) + traitor_class = GLOB.traitor_classes[TRAITOR_HUMAN] var/mob/living/carbon/human/traitor_mob = current if (!istype(traitor_mob)) return @@ -306,21 +308,21 @@ if (!uplink_loc) if(!silent) - to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.") + to_chat(traitor_mob, "Unfortunately, [traitor_class.employer] wasn't able to get you an Uplink.") . = 0 else . = uplink_loc - var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key) + var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key,traitor_class) if(!U) CRASH("Uplink creation failed.") U.setup_unlock_code() if(!silent) if(uplink_loc == R) - to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.") + to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.") else if(uplink_loc == PDA) - to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.") + to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.") else if(uplink_loc == P) - to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.") + to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.") if(uplink_owner) uplink_owner.antag_memory += U.unlock_note + "
" diff --git a/code/modules/antagonists/traitor/IAA/internal_affairs.dm b/code/modules/antagonists/traitor/IAA/internal_affairs.dm index caf2f644df..19144d67c9 100644 --- a/code/modules/antagonists/traitor/IAA/internal_affairs.dm +++ b/code/modules/antagonists/traitor/IAA/internal_affairs.dm @@ -120,7 +120,7 @@ /datum/antagonist/traitor/internal_affairs/reinstate_escape_objective() ..() - var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive + var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive var/datum/objective/escape_objective = new objtype escape_objective.owner = owner add_objective(escape_objective) @@ -215,20 +215,12 @@ kill_objective.target = target_mind kill_objective.update_explanation_text() add_objective(kill_objective) - - //Optional traitor objective - if(prob(PROB_ACTUAL_TRAITOR)) - employer = "The Syndicate" - owner.special_role = TRAITOR_AGENT_ROLE - special_role = TRAITOR_AGENT_ROLE - syndicate = TRUE - forge_single_objective() return /datum/antagonist/traitor/internal_affairs/forge_traitor_objectives() forge_iaa_objectives() - var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive + var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive var/datum/objective/escape_objective = new objtype escape_objective.owner = owner add_objective(escape_objective) diff --git a/code/modules/antagonists/traitor/classes/ai.dm b/code/modules/antagonists/traitor/classes/ai.dm new file mode 100644 index 0000000000..fac1efeb49 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/ai.dm @@ -0,0 +1,68 @@ +/datum/traitor_class/ai // this one is special, so has no weight + name = "Malfunctioning AI" + +/datum/traitor_class/ai/forge_objectives(datum/antagonist/traitor/T) + var/objective_count = 0 + + if(prob(30)) + objective_count += forge_single_objective() + + for(var/i = objective_count, i < CONFIG_GET(number/traitor_objectives_amount), i++) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) + + var/datum/objective/survive/exist/exist_objective = new + exist_objective.owner = T.owner + T.add_objective(exist_objective) + +/datum/traitor_class/ai/forge_single_objective(datum/antagonist/traitor/T) + .=1 + var/special_pick = rand(1,4) + switch(special_pick) + if(1) + var/datum/objective/block/block_objective = new + block_objective.owner = T.owner + T.add_objective(block_objective) + if(2) + var/datum/objective/purge/purge_objective = new + purge_objective.owner = T.owner + T.add_objective(purge_objective) + if(3) + var/datum/objective/robot_army/robot_objective = new + robot_objective.owner = T.owner + T.add_objective(robot_objective) + if(4) //Protect and strand a target + var/datum/objective/protect/yandere_one = new + yandere_one.owner = T.owner + T.add_objective(yandere_one) + yandere_one.find_target() + var/datum/objective/maroon/yandere_two = new + yandere_two.owner = T.owner + yandere_two.target = yandere_one.target + yandere_two.update_explanation_text() // normally called in find_target() + T.add_objective(yandere_two) + .=2 + +/datum/traitor_class/ai/on_removal(datum/antagonist/traitor/T) + var/mob/living/silicon/ai/A = T.owner.current + A.set_zeroth_law("") + A.verbs -= /mob/living/silicon/ai/proc/choose_modules + A.malf_picker.remove_malf_verbs(A) + qdel(A.malf_picker) + + +/datum/traitor_class/ai/apply_innate_effects(mob/living/M) + var/mob/living/silicon/ai/A = M + A.hack_software = TRUE + +/datum/traitor_class/ai/remove_innate_effects(mob/living/M) + var/mob/living/silicon/ai/A = M + A.hack_software = FALSE + +/datum/traitor_class/ai/finalize_traitor(datum/antagonist/traitor/T) + T.add_law_zero() + T.owner.current.playsound_local(get_turf(T.owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE) + T.owner.current.grant_language(/datum/language/codespeak) + return FALSE diff --git a/code/modules/antagonists/traitor/classes/assassin.dm b/code/modules/antagonists/traitor/classes/assassin.dm new file mode 100644 index 0000000000..0c1d950f57 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/assassin.dm @@ -0,0 +1,37 @@ +/datum/traitor_class/human/assassin + name = "Donk Co Operative" + employer = "Donk Corporation" + weight = 0 + chaos = 1 + cost = 2 + +/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T) + .=1 + var/permakill_prob = 20 + var/is_dynamic = FALSE + var/datum/game_mode/dynamic/mode + if(istype(SSticker.mode,/datum/game_mode/dynamic)) + mode = SSticker.mode + is_dynamic = TRUE + permakill_prob = max(0,mode.threat_level-50) + var/list/active_ais = active_ais() + if(active_ais.len && prob(100/GLOB.joined_player_list.len)) + var/datum/objective/destroy/destroy_objective = new + destroy_objective.owner = T.owner + destroy_objective.find_target() + T.add_objective(destroy_objective) + else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN))) + var/datum/objective/maroon/maroon_objective = new + maroon_objective.owner = T.owner + maroon_objective.find_target() + T.add_objective(maroon_objective) + else if(prob(permakill_prob)) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) + else + var/datum/objective/assassinate/once/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) diff --git a/code/modules/antagonists/traitor/classes/freeform.dm b/code/modules/antagonists/traitor/classes/freeform.dm new file mode 100644 index 0000000000..f84eadb9f6 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/freeform.dm @@ -0,0 +1,12 @@ +/datum/traitor_class/human/freeform + name = "Waffle Co Agent" + employer = "Waffle Company" + weight = 16 + chaos = 0 + +/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T) + var/datum/objective/escape/O = new + O.explanation_text = "You have no goals! Whatever you can do do antagonize Nanotrasen, do it! The gimmickier, the better! Make sure to escape alive, though!" + O.owner = T.owner + T.add_objective(O) + return diff --git a/code/modules/antagonists/traitor/classes/hijack.dm b/code/modules/antagonists/traitor/classes/hijack.dm new file mode 100644 index 0000000000..e89eda1dcf --- /dev/null +++ b/code/modules/antagonists/traitor/classes/hijack.dm @@ -0,0 +1,18 @@ +/datum/traitor_class/human/hijack + name = "Gorlex Marauder" + employer = "The Gorlex Marauders" + weight = 3 + chaos = 5 + cost = 5 + uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit) + +/datum/traitor_class/human/hijack/forge_objectives(datum/antagonist/traitor/T) + var/datum/objective/hijack/O = new + O.explanation_text = "The Gorlex Marauders are letting you do what you want, with one condition: the shuttle must be hijacked by hacking its navigational protocols through the control console (alt click emergency shuttle console)." + O.owner = T.owner + T.add_objective(O) + return + +/datum/traitor_class/human/hijack/finalize_traitor(datum/antagonist/traitor/T) + T.hijack_speed=1 + return TRUE diff --git a/code/modules/antagonists/traitor/classes/human.dm b/code/modules/antagonists/traitor/classes/human.dm new file mode 100644 index 0000000000..4e096390c0 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/human.dm @@ -0,0 +1,82 @@ +/datum/traitor_class/human + name = "Syndicate Agent" + chaos = 0 + +/datum/traitor_class/human/forge_objectives(datum/antagonist/traitor/T) + var/objective_count = 0 //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) + SSticker.mode.exchange_red = T.owner + else + SSticker.mode.exchange_blue = T.owner + T.assign_exchange_role(SSticker.mode.exchange_red) + T.assign_exchange_role(SSticker.mode.exchange_blue) + objective_count += 1 //Exchange counts towards number of objectives + var/toa = CONFIG_GET(number/traitor_objectives_amount) + for(var/i = objective_count, i < toa, i++) + forge_single_objective(T) + if(!(locate(/datum/objective/escape) in T.objectives)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = T.owner + T.add_objective(escape_objective) + return + +/datum/traitor_class/human/forge_single_objective(datum/antagonist/traitor/T) + .=1 + var/assassin_prob = 50 + var/is_dynamic = FALSE + var/datum/game_mode/dynamic/mode + if(istype(SSticker.mode,/datum/game_mode/dynamic)) + mode = SSticker.mode + is_dynamic = TRUE + assassin_prob = max(0,mode.threat_level-20) + if(prob(assassin_prob)) + if(is_dynamic) + var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost) + mode.spend_threat(threat_spent) + mode.log_threat("[T.owner.name] spent [threat_spent] on an assassination target.") + var/list/active_ais = active_ais() + if(active_ais.len && prob(100/GLOB.joined_player_list.len)) + var/datum/objective/destroy/destroy_objective = new + destroy_objective.owner = T.owner + destroy_objective.find_target() + T.add_objective(destroy_objective) + else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN))) + var/datum/objective/maroon/maroon_objective = new + maroon_objective.owner = T.owner + maroon_objective.find_target() + T.add_objective(maroon_objective) + else if(prob(max(0,assassin_prob-20))) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) + else + var/datum/objective/assassinate/once/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) + else + if(prob(15) && !(locate(/datum/objective/download) in T.objectives) && !(T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist"))) + var/datum/objective/download/download_objective = new + download_objective.owner = T.owner + download_objective.gen_amount_goal() + T.add_objective(download_objective) + else if(prob(40)) // cum. not counting download: 40%. + var/datum/objective/steal/steal_objective = new + steal_objective.owner = T.owner + steal_objective.find_target() + T.add_objective(steal_objective) + else if(prob(100/3)) // cum. not counting download: 20%. + var/datum/objective/sabotage/sabotage_objective = new + sabotage_objective.owner = T.owner + sabotage_objective.find_target() + T.add_objective(sabotage_objective) + else // cum. not counting download: 40% + var/datum/objective/flavor/traitor/flavor_objective = new + flavor_objective.owner = T.owner + flavor_objective.forge_objective() + T.add_objective(flavor_objective) + +/datum/traitor_class/human/greet(datum/antagonist/traitor/T) + to_chat(T.owner.current, "You are under contract with [employer]. They have given you your objectives.") diff --git a/code/modules/antagonists/traitor/classes/martyr.dm b/code/modules/antagonists/traitor/classes/martyr.dm new file mode 100644 index 0000000000..78f8bf9b0c --- /dev/null +++ b/code/modules/antagonists/traitor/classes/martyr.dm @@ -0,0 +1,14 @@ +/datum/traitor_class/human/martyr + name = "Tiger Cooperator" + employer = "The Tiger Cooperative" + weight = 2 + chaos = 5 + cost = 5 + uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_TC/contract_kit) + +/datum/traitor_class/human/martyr/forge_objectives(datum/antagonist/traitor/T) + var/datum/objective/martyr/O = new + O.explanation_text = "The tiger cooperative have given you free reign. You may do as you wish, as long as you die a glorious death!" + O.owner = T.owner + T.add_objective(O) + return diff --git a/code/modules/antagonists/traitor/classes/subterfuge.dm b/code/modules/antagonists/traitor/classes/subterfuge.dm new file mode 100644 index 0000000000..ae43f11fe7 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/subterfuge.dm @@ -0,0 +1,40 @@ +/datum/traitor_class/human/subterfuge + name = "MI13 Operative" + employer = "MI13" + weight = 20 + chaos = -5 + +/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T) + .=1 + var/assassin_prob = 30 + var/datum/game_mode/dynamic/mode + if(istype(SSticker.mode,/datum/game_mode/dynamic)) + mode = SSticker.mode + assassin_prob = max(0,mode.threat_level-40) + if(prob(assassin_prob)) + if(prob(assassin_prob)) + var/datum/objective/assassinate/once/kill_objective = new + kill_objective.owner = T.owner + kill_objective.find_target() + T.add_objective(kill_objective) + else + var/datum/objective/maroon/maroon_objective = new + maroon_objective.owner = T.owner + maroon_objective.find_target() + T.add_objective(maroon_objective) + else + if(prob(15) && !(locate(/datum/objective/download) in T.objectives) && !(T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist"))) + var/datum/objective/download/download_objective = new + download_objective.owner = T.owner + download_objective.gen_amount_goal() + T.add_objective(download_objective) + else if(prob(70)) // cum. not counting download: 40%. + var/datum/objective/steal/steal_objective = new + steal_objective.owner = T.owner + steal_objective.find_target() + T.add_objective(steal_objective) + else + var/datum/objective/sabotage/sabotage_objective = new + sabotage_objective.owner = T.owner + sabotage_objective.find_target() + T.add_objective(sabotage_objective) diff --git a/code/modules/antagonists/traitor/classes/traitor_class.dm b/code/modules/antagonists/traitor/classes/traitor_class.dm new file mode 100644 index 0000000000..3df9dec929 --- /dev/null +++ b/code/modules/antagonists/traitor/classes/traitor_class.dm @@ -0,0 +1,40 @@ +GLOBAL_LIST_EMPTY(traitor_classes) + +/datum/traitor_class + var/name = "Bad Coders Ltd." + var/employer = "The Syndicate" + var/weight = 0 + var/chaos = 0 + var/cost = 0 + var/TC = 20 + var/list/uplink_filters + +/datum/traitor_class/New() + ..() + if(src.type in GLOB.traitor_classes) + qdel(src) + else + GLOB.traitor_classes += src.type + GLOB.traitor_classes[src.type] = src + +/datum/traitor_class/proc/forge_objectives(datum/antagonist/traitor/T) + // Like the old forge_human_objectives. Makes all the objectives for this traitor class. + +/datum/traitor_class/proc/forge_single_objective(datum/antagonist/traitor/T) + // As forge_single_objective. + +/datum/traitor_class/proc/on_removal(datum/antagonist/traitor/T) + // What this does to the antag datum on removal. Called before proper removal, obviously. + +/datum/traitor_class/proc/apply_innate_effects(mob/living/M) + // What innate effects it should have. See: AI. + +/datum/traitor_class/proc/remove_innate_effects(mob/living/M) + // Cleaning up the innate effects. + +/datum/traitor_class/proc/greet(datum/antagonist/traitor/T) + // Message upon creation. Not necessary, but can be useful. + +/datum/traitor_class/proc/finalize_traitor(datum/antagonist/traitor/T) + // Finalization. Return TRUE if should play standard traitor sound/equip, return FALSE if both are special case + return TRUE diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 0352272f70..a9dee81a6c 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -1,6 +1,3 @@ -#define TRAITOR_HUMAN "human" -#define TRAITOR_AI "AI" - /datum/antagonist/traitor name = "Traitor" roundend_category = "traitors" @@ -12,43 +9,52 @@ var/give_objectives = TRUE var/should_give_codewords = TRUE var/should_equip = TRUE - var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment + var/datum/traitor_class/traitor_kind var/datum/contractor_hub/contractor_hub hijack_speed = 0.5 //10 seconds per hijack stage by default +/datum/antagonist/traitor/New() + ..() + if(!GLOB.traitor_classes.len)//Only need to fill the list when it's needed. + for(var/I in subtypesof(/datum/traitor_class)) + new I + +/datum/antagonist/traitor/proc/set_traitor_kind(var/kind) + traitor_kind = GLOB.traitor_classes[kind] + if(istype(SSticker.mode, /datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + if(traitor_kind.cost) + mode.spend_threat(traitor_kind.cost) + mode.log_threat("[traitor_kind.cost] was spent due to [owner.name] being a [traitor_kind.name].") + /datum/antagonist/traitor/on_gain() if(owner.current && isAI(owner.current)) - traitor_kind = TRAITOR_AI - + set_traitor_kind(TRAITOR_AI) + else + var/chaos_weight = 0 + if(istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + chaos_weight = (mode.threat - 50)/50 + var/list/weights = list() + for(var/C in GLOB.traitor_classes) + var/datum/traitor_class/class = GLOB.traitor_classes[C] + var/weight = (1.5*class.weight)/(0.5+NUM_E**(-chaos_weight*class.chaos)) // just a logistic function + weights[C] = weight + var/choice = pickweightAllowZero(weights) + if(!choice) + choice = GLOB.traitor_classes[TRAITOR_HUMAN] + set_traitor_kind(pickweightAllowZero(weights)) + traitor_kind.weight *= 0.8 // less likely this round SSticker.mode.traitors += owner owner.special_role = special_role if(give_objectives) - forge_traitor_objectives() + traitor_kind.forge_objectives(src) finalize_traitor() ..() -/datum/antagonist/traitor/apply_innate_effects() - if(owner.assigned_role == "Clown") - var/mob/living/carbon/human/traitor_mob = owner.current - if(traitor_mob && istype(traitor_mob)) - if(!silent) - to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - traitor_mob.dna.remove_mutation(CLOWNMUT) - -/datum/antagonist/traitor/remove_innate_effects() - if(owner.assigned_role == "Clown") - var/mob/living/carbon/human/traitor_mob = owner.current - if(traitor_mob && istype(traitor_mob)) - traitor_mob.dna.add_mutation(CLOWNMUT) - /datum/antagonist/traitor/on_removal() //Remove malf powers. - if(traitor_kind == TRAITOR_AI && owner.current && isAI(owner.current)) - var/mob/living/silicon/ai/A = owner.current - A.set_zeroth_law("") - A.verbs -= /mob/living/silicon/ai/proc/choose_modules - A.malf_picker.remove_malf_verbs(A) - qdel(A.malf_picker) + traitor_kind.on_removal(src) SSticker.mode.traitors -= owner if(!silent && owner.current) to_chat(owner.current," You are no longer the [special_role]! ") @@ -69,192 +75,11 @@ objectives -= O /datum/antagonist/traitor/proc/forge_traitor_objectives() - switch(traitor_kind) - if(TRAITOR_AI) - forge_ai_objectives() - else - forge_human_objectives() - -/datum/antagonist/traitor/proc/forge_human_objectives() - 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.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(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(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(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) - SSticker.mode.exchange_red = owner - else - SSticker.mode.exchange_blue = owner - assign_exchange_role(SSticker.mode.exchange_red) - assign_exchange_role(SSticker.mode.exchange_blue) - objective_count += 1 //Exchange counts towards number of objectives - var/toa = CONFIG_GET(number/traitor_objectives_amount) - for(var/i = objective_count, i < toa, i++) - forge_single_objective() - - if(is_hijacker && objective_count <= toa) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount - if (!(locate(/datum/objective/hijack) in objectives)) - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = owner - add_objective(hijack_objective) - if(is_dynamic) - var/threat_spent = CONFIG_GET(number/dynamic_hijack_cost) - mode.spend_threat(threat_spent) - mode.log_threat("[owner.name] spent [threat_spent] on hijack.") - return - - - var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead. - for(var/datum/objective/O in objectives) - if(!O.martyr_compatible) - martyr_compatibility = 0 - break - - if(martyr_compatibility && martyr_chance) - var/datum/objective/martyr/martyr_objective = new - martyr_objective.owner = owner - add_objective(martyr_objective) - if(is_dynamic) - var/threat_spent = CONFIG_GET(number/dynamic_hijack_cost) - mode.spend_threat(threat_spent) - mode.log_threat("[owner.name] spent [threat_spent] on glorious death.") - return - - else - if(!(locate(/datum/objective/escape) in objectives)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = owner - add_objective(escape_objective) - return - -/datum/antagonist/traitor/proc/forge_ai_objectives() - var/objective_count = 0 - - if(prob(30)) - objective_count += forge_single_objective() - - for(var/i = objective_count, i < CONFIG_GET(number/traitor_objectives_amount), i++) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = owner - kill_objective.find_target() - add_objective(kill_objective) - - var/datum/objective/survive/exist/exist_objective = new - exist_objective.owner = owner - add_objective(exist_objective) - - -/datum/antagonist/traitor/proc/forge_single_objective() - switch(traitor_kind) - if(TRAITOR_AI) - return forge_single_AI_objective() - else - return forge_single_human_objective() - -/datum/antagonist/traitor/proc/forge_single_human_objective() //Returns how many objectives are added - .=1 - var/assassin_prob = 50 - var/is_dynamic = FALSE - var/datum/game_mode/dynamic/mode - if(istype(SSticker.mode,/datum/game_mode/dynamic)) - mode = SSticker.mode - is_dynamic = TRUE - assassin_prob = max(0,mode.threat_level-20) - if(prob(assassin_prob)) - if(is_dynamic) - var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost) - mode.spend_threat(threat_spent) - mode.log_threat("[owner.name] spent [threat_spent] on an assassination target.") - var/list/active_ais = active_ais() - if(active_ais.len && prob(100/GLOB.joined_player_list.len)) - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = owner - destroy_objective.find_target() - add_objective(destroy_objective) - else if(prob(30) || (is_dynamic && (mode.storyteller.flags & NO_ASSASSIN))) - var/datum/objective/maroon/maroon_objective = new - maroon_objective.owner = owner - maroon_objective.find_target() - add_objective(maroon_objective) - else if(prob(max(0,assassin_prob-20))) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = owner - kill_objective.find_target() - add_objective(kill_objective) - else - var/datum/objective/assassinate/once/kill_objective = new - kill_objective.owner = owner - kill_objective.find_target() - add_objective(kill_objective) - else - if(prob(15) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist"))) - var/datum/objective/download/download_objective = new - download_objective.owner = owner - download_objective.gen_amount_goal() - add_objective(download_objective) - else if(prob(40)) // cum. not counting download: 40%. - var/datum/objective/steal/steal_objective = new - steal_objective.owner = owner - steal_objective.find_target() - add_objective(steal_objective) - else if(prob(100/3)) // cum. not counting download: 20%. - var/datum/objective/sabotage/sabotage_objective = new - sabotage_objective.owner = owner - sabotage_objective.find_target() - add_objective(sabotage_objective) - else // cum. not counting download: 40% - 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 - var/special_pick = rand(1,4) - switch(special_pick) - if(1) - var/datum/objective/block/block_objective = new - block_objective.owner = owner - add_objective(block_objective) - if(2) - var/datum/objective/purge/purge_objective = new - purge_objective.owner = owner - add_objective(purge_objective) - if(3) - var/datum/objective/robot_army/robot_objective = new - robot_objective.owner = owner - add_objective(robot_objective) - if(4) //Protect and strand a target - var/datum/objective/protect/yandere_one = new - yandere_one.owner = owner - add_objective(yandere_one) - yandere_one.find_target() - var/datum/objective/maroon/yandere_two = new - yandere_two.owner = owner - yandere_two.target = yandere_one.target - yandere_two.update_explanation_text() // normally called in find_target() - add_objective(yandere_two) - .=2 + traitor_kind.forge_objectives(src) /datum/antagonist/traitor/greet() to_chat(owner.current, "You are the [owner.special_role].") + traitor_kind.greet(src) owner.announce_objectives() if(should_give_codewords) give_codewords() @@ -270,32 +95,33 @@ set_antag_hud(owner.current, null) /datum/antagonist/traitor/proc/finalize_traitor() - switch(traitor_kind) - if(TRAITOR_AI) - add_law_zero() - owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE) - owner.current.grant_language(/datum/language/codespeak) - if(TRAITOR_HUMAN) - if(should_equip) - equip(silent) - owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE) + if(traitor_kind.finalize_traitor(src)) + if(should_equip) + equip(silent) + owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE) /datum/antagonist/traitor/apply_innate_effects(mob/living/mob_override) . = ..() update_traitor_icons_added() var/mob/M = mob_override || owner.current - if(isAI(M) && traitor_kind == TRAITOR_AI) - var/mob/living/silicon/ai/A = M - A.hack_software = TRUE + traitor_kind.apply_innate_effects(M) + if(owner.assigned_role == "Clown") + var/mob/living/carbon/human/H = M + if(istype(H)) + if(!silent) + to_chat(H, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + H.dna.remove_mutation(CLOWNMUT) RegisterSignal(M, COMSIG_MOVABLE_HEAR, .proc/handle_hearing) /datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override) . = ..() update_traitor_icons_removed() var/mob/M = mob_override || owner.current - if(isAI(M) && traitor_kind == TRAITOR_AI) - var/mob/living/silicon/ai/A = M - A.hack_software = FALSE + traitor_kind.remove_innate_effects(M) + if(owner.assigned_role == "Clown") + var/mob/living/carbon/human/H = M + if(istype(H)) + H.dna.add_mutation(CLOWNMUT) UnregisterSignal(M, COMSIG_MOVABLE_HEAR) /datum/antagonist/traitor/proc/give_codewords() @@ -326,8 +152,7 @@ killer.add_malf_picker() /datum/antagonist/traitor/proc/equip(var/silent = FALSE) - if(traitor_kind == TRAITOR_HUMAN) - owner.equip_traitor(employer, silent, src) + owner.equip_traitor(traitor_kind, silent, src) /datum/antagonist/traitor/proc/assign_exchange_role() //set faction diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 52f6efd3df..bbfa186a48 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1,4 +1,4 @@ -/proc/get_uplink_items(datum/game_mode/gamemode, allow_sales = TRUE, allow_restricted = TRUE) +/proc/get_uplink_items(datum/game_mode/gamemode, allow_sales = TRUE, allow_restricted = TRUE, other_filter = list()) var/list/filtered_uplink_items = GLOB.uplink_categories.Copy() // list of uplink categories without associated values. var/list/sale_items = list() @@ -18,7 +18,8 @@ continue if (I.restricted && !allow_restricted) continue - + if (I.type in other_filter) + continue LAZYSET(filtered_uplink_items[I.category], I.name, I) if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 1) diff --git a/tgstation.dme b/tgstation.dme old mode 100755 new mode 100644 index 21f9f98d65..06c841e021 --- a/tgstation.dme +++ b/tgstation.dme @@ -1498,6 +1498,14 @@ #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\classes\ai.dm" +#include "code\modules\antagonists\traitor\classes\assassin.dm" +#include "code\modules\antagonists\traitor\classes\freeform.dm" +#include "code\modules\antagonists\traitor\classes\hijack.dm" +#include "code\modules\antagonists\traitor\classes\human.dm" +#include "code\modules\antagonists\traitor\classes\martyr.dm" +#include "code\modules\antagonists\traitor\classes\subterfuge.dm" +#include "code\modules\antagonists\traitor\classes\traitor_class.dm" #include "code\modules\antagonists\traitor\syndicate_contract.dm" #include "code\modules\antagonists\traitor\equipment\contractor.dm" #include "code\modules\antagonists\traitor\equipment\Malf_Modules.dm"