lets add traitor classes

This commit is contained in:
Putnam
2020-02-25 16:10:23 -08:00
parent 19ca51a639
commit ebac066b2f
8 changed files with 306 additions and 61 deletions
+8 -6
View File
@@ -257,9 +257,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(traitor_class, silent = FALSE, datum/antagonist/uplink_owner)
if(!current)
return
if(!traitor_class)
traitor_class = GLOB.traitor_classes[BASIC_TRAITOR]
var/mob/living/carbon/human/traitor_mob = current
if (!istype(traitor_mob))
return
@@ -307,21 +309,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 + "<br>"
@@ -0,0 +1,70 @@
#define TRAITOR_AI /datum/traitor_class/ai
/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 = owner
kill_objective.find_target()
T.add_objective(kill_objective)
var/datum/objective/survive/exist/exist_objective = new
exist_objective.owner = 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 = owner
T.add_objective(block_objective)
if(2)
var/datum/objective/purge/purge_objective = new
purge_objective.owner = owner
T.add_objective(purge_objective)
if(3)
var/datum/objective/robot_army/robot_objective = new
robot_objective.owner = owner
T.add_objective(robot_objective)
if(4) //Protect and strand a target
var/datum/objective/protect/yandere_one = new
yandere_one.owner = owner
T.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()
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_objectives(/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
@@ -0,0 +1,25 @@
/datum/traitor_class/human/gorlex
name = "Gorlex Marauders"
employer = "Gorlex Marauders"
weight = 2
chaos = 20
TC = 30
/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/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
@@ -0,0 +1,131 @@
#define BASIC_TRAITOR /datum/traitor_class/human
/datum/traitor_class/human
name = "Traitor"
chaos = 1
/datum/traitor_class/human/forge_objectives(/datum/antagonist/traitor/T)
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 = T.owner
else
SSticker.mode.exchange_blue = T.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(T)
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 = T.owner
T.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 = T.owner
T.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
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 objectives) && !(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)
@@ -0,0 +1,35 @@
GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class
var/name = "Bad Coders Ltd."
var/employer = "The Syndicate"
var/weight = 0
var/chaos = 0
var/TC = 20
/datum/traitor_class/New()
..()
if(src.type in traitor_classes)
qdel(src)
else
traitor_classes += src.type
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/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
@@ -1,5 +1,4 @@
#define TRAITOR_HUMAN "human"
#define TRAITOR_AI "AI"
/datum/antagonist/traitor
name = "Traitor"
@@ -12,21 +11,42 @@
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 = TRAITOR_HUMAN //Set on initial assignment
can_hijack = HIJACK_HIJACKER
/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 = traitor_classes[kind]
/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 traitor_classes)
var/datum/traitor_class/class = C
var/weight = class.weight/(1+NUM_E**(-chaos_weight*class.chaos)) // just a logistic function
weights[C] = weight
set_traitor_kind(pickweightAllowZero(weights))
traitor_kind.weight /= 2 // less likely this round
SSticker.mode.traitors += owner
owner.special_role = special_role
if(give_objectives)
forge_traitor_objectives()
finalize_traitor()
traitor_kind.forge_objectives(src)
traitor_kind.finalize_traitor(src)
..()
/datum/antagonist/traitor/apply_innate_effects()
traitor_kind.apply_innate_effects(src)
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
@@ -35,6 +55,7 @@
traitor_mob.dna.remove_mutation(CLOWNMUT)
/datum/antagonist/traitor/remove_innate_effects()
traitor_kind.remove_innate_effects(src)
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
@@ -42,12 +63,7 @@
/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,"<span class='userdanger'> You are no longer the [special_role]! </span>")
@@ -67,6 +83,7 @@
objectives -= O
/datum/antagonist/traitor/proc/forge_traitor_objectives()
traitor_kind.forge_objectives(src)
switch(traitor_kind)
if(TRAITOR_AI)
forge_ai_objectives()
@@ -142,30 +159,6 @@
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
@@ -268,32 +261,24 @@
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)
var/should_base_finalize = traitor_kind.finalize_traitor(src)
if(should_base_finalize)
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)
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)
UnregisterSignal(M, COMSIG_MOVABLE_HEAR)
/datum/antagonist/traitor/proc/give_codewords()
@@ -324,8 +309,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
@@ -1,6 +1,5 @@
Nanotrasen want to make sure that their employees are on the up-and-up. Try to find any blackmail you can.
Increase productivity however you can.
You are a security ninja. Answer to the Head of Security, and follow space law.
You are a cargo ninja. Answer to the Quartermaster, and do what they say.
Nanotrasen want you to ensure maximum morale. Protect the members of the crew who are on break.
Ensure that all the paperwork is being done.
-1
View File
@@ -2,7 +2,6 @@ The Gorlex Marauders want you to teach the heads of staff a lesson they will nev
Show Nanotrasen the utility of a 40% oxygen atmosphere.
Waffle Co. wants you To cause as much humorous terrorism against Nanotrasen as possible! How? We dont care as long as its entertaining! Be as creative and exciting as possible when carrying out your dirty deeds. Have fun!
Kill one of the station's beloved pets. Make a show of it, though you don't have to reveal yourself.
The Tiger Cooperative want you to get their illegal technology spread through the station.
The Animal Rights Consortium needs you to save the innocent non-humanoid creatures aboard Citadel Station by any means necessary. Use your best judgement to decide whether an animal or xenobiological is abused, but if they are, ensure the abuser is punished. Avoid killing too many people if possible, and if you do harm any creatures, you will be terminated upon extraction.
Donk Co. wants ransom money, and you are going to get it. Your goal is to kidnap and crewmember you can get your hands on and hold them hostage until you get something of significant value. Try to work out the best deal you can. Remember that Higher Value Targets are generally going to get a better deal so try to prioritize heads of staff if possible. We do not approve of mindless killing of Nanotrasen employees, so dont do it.
The Gorlex Marauders want you to steal as many shoes as possible. Lay broken glass everywhere.