lets add traitor classes
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user