Merge pull request #10623 from Putnam3145/antag-overhaul

Better antag objectives: for real this time
This commit is contained in:
kevinz000
2020-02-06 16:25:22 -07:00
committed by GitHub
46 changed files with 740 additions and 211 deletions
+1
View File
@@ -5,6 +5,7 @@
#define NO_ASSASSIN (1<<0)
#define WAROPS_ALWAYS_ALLOWED (1<<1)
#define USE_PREF_WEIGHTS (1<<2)
#define ONLY_RULESET (1<<0)
#define HIGHLANDER_RULESET (1<<1)
+1
View File
@@ -38,6 +38,7 @@
#define ROLE_BLOODSUCKER "bloodsucker"
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
#define ROLE_GHOSTCAFE "ghostcafe"
#define ROLE_MINOR_ANTAG "minorantag"
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
//(in game days played) to play that role
+3 -1
View File
@@ -1,6 +1,8 @@
#define PLURALITY_VOTING 0
#define APPROVAL_VOTING 1
#define RANKED_CHOICE_VOTING 2
#define SCHULZE_VOTING 2
#define SCORE_VOTING 3
#define MAJORITY_JUDGEMENT_VOTING 4
#define INSTANT_RUNOFF_VOTING 5
GLOBAL_LIST_INIT(vote_score_options,list("Bad","Poor","Acceptable","Good","Great"))
+20 -7
View File
@@ -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"))
@@ -111,7 +111,14 @@
if(A.objectives.len)
for(var/datum/objective/O in A.objectives)
var/result = O.check_completion() ? "SUCCESS" : "FAIL"
var/result = "UNKNOWN"
var/actual_result = O.check_completion()
if(actual_result >= 1)
result = "SUCCESS"
else if(actual_result <= 0)
result = "FAIL"
else
result = "[actual_result*100]%"
antag_info["objectives"] += list(list("objective_type"=O.type,"text"=O.explanation_text,"result"=result))
SSblackbox.record_feedback("associative", "antagonists", 1, antag_info)
@@ -550,10 +557,16 @@
var/list/objective_parts = list()
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
else
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
return objective_parts.Join("<br>")
@@ -364,6 +364,29 @@
runnable_modes[M] = final_weight
return runnable_modes
/datum/controller/configuration/proc/get_runnable_storytellers()
var/list/datum/dynamic_storyteller/runnable_storytellers = new
var/list/probabilities = Get(/datum/config_entry/keyed_list/storyteller_weight)
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
for(var/T in storyteller_cache)
var/datum/dynamic_storyteller/S = T
var/config_tag = initial(S.config_tag)
var/final_weight = initial(S.weight)
if(probabilities[config_tag]<=0)
continue
final_weight = probabilities[config_tag]
if(SSpersistence.saved_storytellers.len == 3 && repeated_mode_adjust.len == 3)
var/name = initial(S.name)
var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3)
var/adjustment = 0
while(recent_round)
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0)
final_weight *= ((100-adjustment)/100)
runnable_storytellers[S] = final_weight
return runnable_storytellers
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
var/list/datum/game_mode/runnable_modes = new
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
@@ -88,3 +88,7 @@
/datum/config_entry/number/dynamic_warops_cost
config_entry_value = 10
min_val = 0
/datum/config_entry/keyed_list/storyteller_weight
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
+3 -4
View File
@@ -13,7 +13,7 @@ SUBSYSTEM_DEF(persistence)
var/list/saved_messages = list()
var/list/saved_modes = list(1,2,3)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_storytellers = list("foo","bar","baz","foo again","bar again")
var/list/saved_storytellers = list("foo","bar","baz")
var/list/saved_maps
var/list/saved_trophies = list()
var/list/spawned_objects = list()
@@ -191,6 +191,7 @@ SUBSYSTEM_DEF(persistence)
if(!json)
return
saved_storytellers = json["data"]
saved_storytellers.len = 3
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
var/json_file = file("data/RecentMaps.json")
@@ -429,9 +430,7 @@ SUBSYSTEM_DEF(persistence)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
saved_storytellers.len = 5
saved_storytellers[5] = saved_storytellers[4]
saved_storytellers[4] = saved_storytellers[3]
saved_storytellers.len = 3
saved_storytellers[3] = saved_storytellers[2]
saved_storytellers[2] = saved_storytellers[1]
saved_storytellers[1] = mode.storyteller.name
@@ -35,7 +35,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/list/my_quirks = cli.prefs.all_quirks.Copy()
var/list/cut
if(job?.blacklisted_quirks)
cut = filter_quirks(my_quirks, job)
cut = filter_quirks(my_quirks, job.blacklisted_quirks)
for(var/V in my_quirks)
var/datum/quirk/Q = quirks[V]
if(Q)
@@ -63,11 +63,11 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
for(var/i in quirk_names)
. += quirk_points_by_name(i)
/datum/controller/subsystem/processing/quirks/proc/filter_quirks(list/our_quirks, datum/job/job)
/datum/controller/subsystem/processing/quirks/proc/filter_quirks(list/our_quirks, list/blacklisted_quirks)
var/list/cut = list()
var/list/banned_names = list()
var/pointscut = 0
for(var/i in job.blacklisted_quirks)
for(var/i in blacklisted_quirks)
var/name = quirk_name_by_path(i)
if(name)
banned_names += name
+2
View File
@@ -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()
+81 -54
View File
@@ -188,22 +188,44 @@ SUBSYSTEM_DEF(vote)
choices[score_name]++
/datum/controller/subsystem/vote/proc/calculate_scores(var/blackbox_text)
var/list/scores_by_choice = list()
for(var/choice in choices)
scores_by_choice += "[choice]"
scores_by_choice["[choice]"] = list()
scores += "[choice]"
scores["[choice]"] = 0
for(var/ckey in voted)
var/list/this_vote = voted[ckey]
for(var/choice in choices)
if("[choice]" in this_vote && "[choice]" in scores_by_choice)
sorted_insert(scores_by_choice["[choice]"],this_vote["[choice]"],/proc/cmp_numeric_asc)
var/middle_score = round(GLOB.vote_score_options.len/2,1)
for(var/score_name in scores_by_choice)
var/list/score = scores_by_choice[score_name]
for(var/S in score)
scores[score_name] += S-middle_score
for(var/choice in this_vote)
scores["[choice]"] += this_vote["[choice]"]
var/min_score = 100
var/max_score = -100
for(var/score_name in scores) // normalize the scores from 0-1
max_score=max(max_score,scores[score_name])
min_score=min(min_score,scores[score_name])
for(var/score_name in scores)
scores[score_name] = (scores[score_name]-min_score)/(max_score-min_score)
SSblackbox.record_feedback("nested tally","voting",scores[score_name],list(blackbox_text,"Total scores",score_name))
/datum/controller/subsystem/vote/proc/get_runoff_results(var/blackbox_text)
var/already_lost_runoff = list()
var/list/cur_choices = choices.Copy()
for(var/ckey in voted)
choices[choices[voted[ckey][1]]]++ // jesus christ how horrifying
for(var/_this_var_unused_ignore_it in 1 to choices.len) // if it takes more than this something REALLY wrong happened
for(var/ckey in voted)
cur_choices[cur_choices[voted[ckey][1]]]++ // jesus christ how horrifying
var/least_vote = 100000
var/least_voted
for(var/i in 1 to cur_choices.len)
var/option = cur_choices[i]
if(cur_choices[option] > voted.len/2)
return list(option)
else if(cur_choices[option] < least_vote && !(option in already_lost_runoff))
least_vote = cur_choices[option]
least_voted = i
already_lost_runoff += cur_choices[least_voted]
for(var/ckey in voted)
voted[ckey] -= least_voted
for(var/option in cur_choices)
cur_choices[option] = 0
/datum/controller/subsystem/vote/proc/announce_result()
var/vote_title_text
@@ -214,24 +236,22 @@ SUBSYSTEM_DEF(vote)
else
text += "<b>[capitalize(mode)] Vote</b>"
vote_title_text = "[capitalize(mode)] Vote"
if(vote_system == RANKED_CHOICE_VOTING)
if(vote_system == SCHULZE_VOTING)
calculate_condorcet_votes(vote_title_text)
if(vote_system == SCORE_VOTING)
calculate_majority_judgement_vote(vote_title_text)
calculate_scores(vote_title_text)
var/list/winners = get_result()
if(vote_system == MAJORITY_JUDGEMENT_VOTING)
calculate_majority_judgement_vote(vote_title_text) // nothing uses this at the moment
var/list/winners = vote_system == INSTANT_RUNOFF_VOTING ? get_runoff_results() : get_result()
var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic"
if(winners.len > 0)
if(was_roundtype_vote)
stored_gamemode_votes = list()
if(!obfuscated && vote_system == RANKED_CHOICE_VOTING)
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
if(mode == "mode tiers")
for(var/score_name in scores)
var/score = scores[score_name]
if(!score)
score = 0
text = "\n<b>[score_name]:</b> [obfuscated ? "???" : score]"
if(!obfuscated)
if(vote_system == SCHULZE_VOTING)
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
if(vote_system == MAJORITY_JUDGEMENT_VOTING)
text += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!"
else
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
@@ -249,6 +269,15 @@ SUBSYSTEM_DEF(vote)
text += "\n<b>Vote Result: [obfuscated ? "???" : .]</b>" //CIT CHANGE - adds obfuscated votes
else
text += "\n<b>Did not vote:</b> [GLOB.clients.len-voted.len]"
else if(vote_system == SCORE_VOTING)
for(var/score_name in scores)
var/score = scores[score_name]
if(!score)
score = 0
if(was_roundtype_vote)
stored_gamemode_votes[score_name] = score
text = "\n<b>[score_name]:</b> [obfuscated ? "???" : score]"
. = 1
else
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
log_vote(text)
@@ -258,7 +287,7 @@ SUBSYSTEM_DEF(vote)
if(APPROVAL_VOTING,PLURALITY_VOTING)
for(var/i=1,i<=choices.len,i++)
SSblackbox.record_feedback("nested tally","voting",choices[choices[i]],list(vote_title_text,choices[i]))
if(RANKED_CHOICE_VOTING)
if(SCHULZE_VOTING,INSTANT_RUNOFF_VOTING)
for(var/i=1,i<=voted.len,i++)
var/list/myvote = voted[voted[i]]
if(islist(myvote))
@@ -266,13 +295,18 @@ SUBSYSTEM_DEF(vote)
SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]]))
if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
var/admintext = "Obfuscated results"
if(vote_system == RANKED_CHOICE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
else if(vote_system == SCORE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!"
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
admintext += "\n<b>[choices[i]]:</b> [votes]"
if(vote_system != SCORE_VOTING)
if(vote_system == SCHULZE_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
else if(vote_system == MAJORITY_JUDGEMENT_VOTING)
admintext += "\nIt should be noted that this is not a raw tally of votes but the number of runoffs done by majority judgement!"
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
admintext += "\n<b>[choices[i]]:</b> [votes]"
else
for(var/i=1,i<=scores.len,i++)
var/score = scores[scores[i]]
admintext += "\n<b>[scores[i]]:</b> [score]"
message_admins(admintext)
return .
@@ -316,15 +350,13 @@ SUBSYSTEM_DEF(vote)
if("dynamic")
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
if(. == "Secret")
GLOB.master_mode = "secret"
SSticker.save_mode(.)
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
else
GLOB.master_mode = "dynamic"
var/datum/dynamic_storyteller/S = config.pick_storyteller(.)
GLOB.dynamic_storyteller_type = S
GLOB.master_mode = "dynamic"
var/list/runnable_storytellers = config.get_runnable_storytellers()
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
runnable_storytellers[S] *= scores[initial(S.name)]
var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers)
GLOB.dynamic_storyteller_type = S
if("map")
var/datum/map_config/VM = config.maplist[.]
message_admins("The map has been voted for and will change to: [VM.map_name]")
@@ -375,7 +407,7 @@ SUBSYSTEM_DEF(vote)
voted[usr.ckey] = list(vote)
choices[choices[vote]]++
return vote
if(RANKED_CHOICE_VOTING)
if(SCHULZE_VOTING,INSTANT_RUNOFF_VOTING)
if(usr.ckey in voted)
if(vote in voted[usr.ckey])
voted[usr.ckey] -= vote
@@ -384,7 +416,7 @@ SUBSYSTEM_DEF(vote)
voted[usr.ckey] = list()
voted[usr.ckey] += vote
saved -= usr.ckey
if(SCORE_VOTING)
if(SCORE_VOTING,MAJORITY_JUDGEMENT_VOTING)
if(!(usr.ckey in voted))
voted += usr.ckey
voted[usr.ckey] = list()
@@ -444,15 +476,10 @@ SUBSYSTEM_DEF(vote)
if("dynamic")
for(var/T in config.storyteller_cache)
var/datum/dynamic_storyteller/S = T
var/recent_rounds = 0
for(var/i in 1 to SSpersistence.saved_storytellers.len)
if(SSpersistence.saved_storytellers[i] == initial(S.name))
recent_rounds++
if(recent_rounds < initial(S.weight))
var/list/probabilities = CONFIG_GET(keyed_list/storyteller_weight)
if(probabilities[initial(S.config_tag)] > 0)
choices.Add(initial(S.name))
choice_descs.Add(initial(S.desc))
choices.Add("Secret")
choice_descs.Add("Standard secret. Switches mode if it wins.")
if("custom")
question = stripped_input(usr,"What is the vote for?")
if(!question)
@@ -514,9 +541,9 @@ SUBSYSTEM_DEF(vote)
. += "<h3>Vote one.</h3>"
if(APPROVAL_VOTING)
. += "<h3>Vote any number of choices.</h3>"
if(RANKED_CHOICE_VOTING)
if(SCHULZE_VOTING,INSTANT_RUNOFF_VOTING)
. += "<h3>Vote by order of preference. Revoting will demote to the bottom. 1 is your favorite, and higher numbers are worse.</h3>"
if(SCORE_VOTING)
if(SCORE_VOTING,MAJORITY_JUDGEMENT_VOTING)
. += "<h3>Grade the candidates by how much you like them.</h3>"
. += "<h3>No-votes have no power--your opinion is only heard if you vote!</h3>"
. += "Time Left: [DisplayTimeText(end_time-world.time)]<hr><ul>"
@@ -536,7 +563,7 @@ SUBSYSTEM_DEF(vote)
if(choice_descs.len >= i)
. += "<li>[choice_descs[i]]</li>"
. += "</ul><hr>"
if(RANKED_CHOICE_VOTING)
if(SCHULZE_VOTING,INSTANT_RUNOFF_VOTING)
var/list/myvote = voted[C.ckey]
for(var/i=1,i<=choices.len,i++)
var/vote = (islist(myvote) ? (myvote.Find(i)) : 0)
@@ -553,7 +580,7 @@ SUBSYSTEM_DEF(vote)
. += "(Saved!)"
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
if(SCORE_VOTING)
if(SCORE_VOTING,MAJORITY_JUDGEMENT_VOTING)
var/list/myvote = voted[C.ckey]
for(var/i=1,i<=choices.len,i++)
. += "<li><b>[choices[i]]</b>"
@@ -656,7 +683,7 @@ SUBSYSTEM_DEF(vote)
voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode]
if(islist(voted[usr.ckey]))
var/malformed = FALSE
if(vote_system == SCORE_VOTING)
if(vote_system == SCORE_VOTING || vote_system == MAJORITY_JUDGEMENT_VOTING)
for(var/thing in voted[usr.ckey])
if(!(thing in choices))
malformed = TRUE
@@ -670,7 +697,7 @@ SUBSYSTEM_DEF(vote)
to_chat(usr,"Your saved vote was malformed! Start over!")
voted -= usr.ckey
else
if(vote_system == SCORE_VOTING)
if(vote_system == SCORE_VOTING || vote_system == MAJORITY_JUDGEMENT_VOTING)
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
else
submit_vote(round(text2num(href_list["vote"])))
+1
View File
@@ -458,6 +458,7 @@
var/list/allowed_types = list(
/datum/objective/assassinate,
/datum/objective/assassinate/once,
/datum/objective/maroon,
/datum/objective/debrain,
/datum/objective/protect,
+1
View File
@@ -8,6 +8,7 @@
var/gain_text
var/lose_text
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
var/antag_removal_text // Text will be given to the quirk holder if they get an antag that has it blacklisted.
var/mood_quirk = FALSE //if true, this quirk affects mood and is unavailable if moodlets are disabled
var/mob_trait //if applicable, apply and remove this mob trait
var/mob/living/quirk_holder
+6 -10
View File
@@ -6,6 +6,7 @@
value = -2
gain_text = "<span class='danger'>You feel your vigor slowly fading away.</span>"
lose_text = "<span class='notice'>You feel vigorous again.</span>"
antag_removal_text = "Your antagonistic nature has removed your blood deficiency."
medical_record_text = "Patient requires regular treatment for blood loss due to low production of blood."
/datum/quirk/blooddeficiency/on_process()
@@ -38,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
@@ -76,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,
@@ -188,11 +192,7 @@
gain_text = "<span class='danger'>You feel repulsed by the thought of violence!</span>"
lose_text = "<span class='notice'>You think you can defend yourself again.</span>"
medical_record_text = "Patient is unusually pacifistic and cannot bring themselves to cause physical harm."
/datum/quirk/nonviolent/on_process()
if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
to_chat(quirk_holder, "<span class='boldannounce'>Your antagonistic nature has caused you to renounce your pacifism.</span>")
qdel(src)
antag_removal_text = "Your antagonistic nature has caused you to renounce your pacifism."
/datum/quirk/paraplegic
name = "Paraplegic"
@@ -355,6 +355,7 @@
gain_text = "<span class='danger'>You find yourself unable to speak!</span>"
lose_text = "<span class='notice'>You feel a growing strength in your vocal chords.</span>"
medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity."
antag_removal_text = "Your antagonistic nature has caused your voice to be heard."
var/datum/brain_trauma/severe/mute/mute
/datum/quirk/mute/add()
@@ -366,11 +367,6 @@
var/mob/living/carbon/human/H = quirk_holder
H?.cure_trauma_type(mute, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute/on_process()
if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
to_chat(quirk_holder, "<span class='boldannounce'>Your antagonistic nature has caused your voice to be heard.</span>")
qdel(src)
/datum/quirk/unstable
name = "Unstable"
desc = "Due to past troubles, you are unable to recover your sanity if you lose it. Be very careful managing your mood!"
+12 -3
View File
@@ -239,12 +239,20 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
. += "<b>Peaceful Waypoint</b></center><BR>"
. += "Your station orbits deep within controlled, core-sector systems and serves as a waypoint for routine traffic through Nanotrasen's trade empire. Due to the combination of high security, interstellar traffic, and low strategic value, it makes any direct threat of violence unlikely. Your primary enemies will be incompetence and bored crewmen: try to organize team-building events to keep staffers interested and productive. However, even deep in our territory there may be subversive elements, especially for such a high-value target as your station. Keep an eye out, but don't expect much trouble."
set_security_level(SEC_LEVEL_GREEN)
for(var/T in subtypesof(/datum/station_goal))
var/datum/station_goal/G = new T
if(!(G in station_goals))
station_goals += G
if(21 to 79)
var/perc_green = 100-round(100*((threat_level-21)/(79-21)))
if(prob(perc_green))
. += "<b>Core Territory</b></center><BR>"
. += "Your station orbits within reliably mundane, secure space. Although Nanotrasen has a firm grip on security in your region, the valuable resources and strategic position aboard your station make it a potential target for infiltrations. Monitor crew for non-loyal behavior, but expect a relatively tame shift free of large-scale destruction. We expect great things from your station."
set_security_level(SEC_LEVEL_GREEN)
for(var/T in subtypesof(/datum/station_goal))
var/datum/station_goal/G = new T
if(!(G in station_goals))
station_goals += G
else if(prob(perc_green))
. += "<b>Contested System</b></center><BR>"
. += "Your station's orbit passes along the edge of Nanotrasen's sphere of influence. While subversive elements remain the most likely threat against your station, hostile organizations are bolder here, where our grip is weaker. Exercise increased caution against elite Syndicate strike forces, or Executives forbid, some kind of ill-conceived unionizing attempt."
@@ -273,7 +281,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
if(GLOB.security_level >= SEC_LEVEL_BLUE)
priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", "intercept")
else
priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. Have a secure shift!", "Security Report", "commandreport")
priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", "commandreport")
// Yes, this is copy pasted from game_mode
/datum/game_mode/dynamic/check_finished(force_ending)
@@ -346,7 +354,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
generate_threat()
storyteller.start_injection_cooldowns()
SSevents.frequency_lower = storyteller.event_frequency_lower // 6 minutes by default
SSevents.frequency_upper = storyteller.event_frequency_upper // 20 minutes by default
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -395,7 +404,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
/datum/game_mode/dynamic/post_setup(report)
update_playercounts()
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
..()
@@ -47,6 +47,8 @@
assigned += M.mind
M.mind.special_role = antag_flag
M.mind.add_antag_datum(antag_datum)
log_admin("[M.name] was made into a [name] by dynamic.")
message_admins("[M.name] was made into a [name] by dynamic.")
return TRUE
//////////////////////////////////////////////
@@ -72,12 +74,6 @@
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
. = ..()
for(var/datum/mind/M in assigned)
log_admin("[M.name] was made into a traitor by dynamic.")
message_admins("[M.name] was made into a traitor by dynamic.")
//////////////////////////////////////////////
// //
// REVOLUTIONARY PROVOCATEUR //
@@ -225,3 +221,25 @@
log_admin("[M.name] was made into a bloodsucker by dynamic.")
message_admins("[M.name] was made into a bloodsucker by dynamic.")
return TRUE
//////////////////////////////////////////////
// //
// COLLECTOR //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/collector
name = "Contraband Collector"
config_tag = "latejoin_collector"
antag_datum = /datum/antagonist/collector
antag_flag = ROLE_MINOR_ANTAG
restricted_roles = list("AI", "Cyborg")
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 1
weight = 5
cost = 1
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
flags = TRAITOR_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 2)
@@ -494,7 +494,7 @@
high_population_requirement = 50
repeatable_weight_decrease = 2
repeatable = TRUE
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
@@ -1,5 +1,6 @@
/datum/dynamic_storyteller
var/name = "none"
var/config_tag = null
var/desc = "A coder's idiocy."
var/list/property_weights = list()
var/curve_centre = 0
@@ -7,6 +8,8 @@
var/forced_threat_level = -1
var/flags = 0
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
var/event_frequency_lower = 6 MINUTES
var/event_frequency_upper = 20 MINUTES
var/datum/game_mode/dynamic/mode = null
/**
@@ -20,14 +23,6 @@ Property weights are:
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
*/
/datum/dynamic_storyteller/New()
..()
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
GLOB.dynamic_curve_centre = curve_centre
GLOB.dynamic_curve_width = curve_width
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
@@ -42,7 +37,28 @@ Property weights are:
return
/datum/dynamic_storyteller/proc/on_start()
return
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
GLOB.dynamic_curve_centre = curve_centre
GLOB.dynamic_curve_width = curve_width
if(flags & USE_PREF_WEIGHTS)
var/voters = 0
var/mean = 0
for(var/client/c in GLOB.clients)
var/vote = c.prefs.preferred_chaos
if(vote)
voters += 1
switch(vote)
if(CHAOS_NONE)
mean -= 5
if(CHAOS_LOW)
mean -= 2.5
if(CHAOS_HIGH)
mean += 2.5
if(CHAOS_MAX)
mean += 5
GLOB.dynamic_curve_centre += (mean/voters)
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/get_midround_cooldown()
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
@@ -154,12 +170,15 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga
name = "Chaotic"
config_tag = "chaotic"
curve_centre = 10
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
property_weights = list("extended" = -1, "chaos" = 10)
weight = 2
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED
var/refund_cooldown
var/refund_cooldown = 0
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
return ..() / 4
@@ -169,12 +188,13 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga/do_process()
if(refund_cooldown < world.time)
mode.refund_threat(10)
mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 300 SECONDS
mode.refund_threat(20)
mode.log_threat("Cowabunga it is. Refunded 20 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 600 SECONDS
/datum/dynamic_storyteller/team
name = "Teamwork"
config_tag = "teamwork"
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
curve_centre = 2
curve_width = 1.5
@@ -187,6 +207,7 @@ Property weights are:
/datum/dynamic_storyteller/conversion
name = "Conversion"
config_tag = "conversion"
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
curve_centre = 3
curve_width = 1
@@ -196,24 +217,33 @@ Property weights are:
/datum/dynamic_storyteller/classic
name = "Random"
config_tag = "random"
desc = "Chaos: varies. Variation: highest. No special weights attached."
weight = 6
flags = USE_PREF_WEIGHTS
curve_width = 4
/datum/dynamic_storyteller/memes
name = "Story"
config_tag = "story"
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
weight = 4
flags = USE_PREF_WEIGHTS
curve_width = 4
property_weights = list("story_potential" = 10)
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
config_tag = "intrigue"
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
weight = 4
flags = USE_PREF_WEIGHTS
curve_width = 4
property_weights = list("trust" = -5)
/datum/dynamic_storyteller/liteextended
name = "Calm"
config_tag = "calm"
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
curve_centre = -5
curve_width = 0.5
@@ -226,10 +256,12 @@ Property weights are:
/datum/dynamic_storyteller/extended
name = "Extended"
config_tag = "extended"
desc = "Chaos: none. Variation: none. Likely antags: none."
curve_centre = -20
weight = 2
weight = 0
curve_width = 0.5
/datum/dynamic_storyteller/extended/on_start()
..()
GLOB.dynamic_forced_extended = TRUE
+190 -35
View File
@@ -9,9 +9,10 @@ GLOBAL_LIST_EMPTY(objectives)
var/explanation_text = "Nothing" //What that person is supposed to do.
var/team_explanation_text //For when there are multiple owners.
var/datum/mind/target = null //If they are focused on a particular person.
var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter.
var/completed = 0 //currently only used for custom objectives.
var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
var/target_amount = FALSE //If they are focused on a particular number. Steal objectives have their own counter.
var/completed = FALSE //currently only used for custom objectives.
var/completable = TRUE //Whether this objective shows greentext when completed
var/martyr_compatible = FALSE //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
/datum/objective/New(var/text)
GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
@@ -172,6 +173,26 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/assassinate/admin_edit(mob/admin)
admin_simple_target_pick(admin)
/datum/objective/assassinate/once
name = "kill once"
var/won = FALSE
/datum/objective/assassinate/once/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Kill [target.name], the [!target_role_type ? target.assigned_role : target.special_role]. You only need to kill them once; if they come back, you've still succeeded."
START_PROCESSING(SSprocessing,src)
else
explanation_text = "Free Objective"
/datum/objective/assassinate/once/check_completion()
return won || ..()
/datum/objective/assassinate/once/process()
won = check_completion()
if(won)
STOP_PROCESSING(SSprocessing,src)
/datum/objective/assassinate/internal
var/stolen = 0 //Have we already eliminated this target?
@@ -365,6 +386,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)
/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
@@ -582,7 +625,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"
@@ -786,40 +828,10 @@ GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/destroy/internal
var/stolen = FALSE //Have we already eliminated this target?
/datum/objective/steal_five_of_type
name = "steal five of"
explanation_text = "Steal at least five items!"
var/list/wanted_items = list(/obj/item)
/datum/objective/steal_five_of_type/New()
..()
wanted_items = typecacheof(wanted_items)
/datum/objective/steal_five_of_type/summon_guns
name = "steal guns"
explanation_text = "Steal at least five guns!"
wanted_items = list(/obj/item/gun)
/datum/objective/steal_five_of_type/summon_magic
name = "steal magic"
explanation_text = "Steal at least five magical artefacts!"
wanted_items = list(/obj/item/spellbook, /obj/item/gun/magic, /obj/item/clothing/suit/space/hardsuit/wizard, /obj/item/scrying, /obj/item/antag_spawner/contract, /obj/item/necromantic_stone)
/datum/objective/steal_five_of_type/check_completion()
var/list/datum/mind/owners = get_owners()
var/stolen_count = 0
for(var/datum/mind/M in owners)
if(!isliving(M.current))
continue
var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
for(var/obj/I in all_items) //Check for wanted items
if(is_type_in_typecache(I, wanted_items))
stolen_count++
return stolen_count >= 5
//Created by admin tools
/datum/objective/custom
name = "custom"
completable = FALSE
/datum/objective/custom/admin_edit(mob/admin)
var/expl = stripped_input(admin, "Custom objective:", "Objective", explanation_text)
@@ -997,4 +1009,147 @@ GLOBAL_LIST_EMPTY(possible_items_special)
command_staff_only = TRUE
/datum/objective/hoard
name = "hoard"
var/obj/item/hoarded_item = null
/datum/objective/hoard/get_target()
return hoarded_item
/datum/objective/hoard/proc/set_target(obj/item/I)
if(I)
hoarded_item = I
explanation_text = "Keep [I] on your person at all times."
return hoarded_item
else
explanation_text = "Free objective"
return
/datum/objective/hoard/check_completion()
var/list/datum/mind/owners = get_owners()
if(!hoarded_item)
return TRUE
for(var/datum/mind/M in owners)
if(!isliving(M.current))
continue
var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
for(var/obj/I in all_items) //Check for items
if(I == hoarded_item)
return TRUE
return FALSE
/datum/objective/hoard/heirloom
name = "steal heirloom"
/datum/objective/hoard/heirloom/find_target()
set_target(pick(GLOB.family_heirlooms))
GLOBAL_LIST_EMPTY(traitor_contraband)
GLOBAL_LIST_EMPTY(cult_contraband)
/datum/objective/hoard/collector
name = "Hoard contraband"
/datum/objective/collector/New()
..()
if(!GLOB.traitor_contraband.len)//Only need to fill the list when it's needed.
GLOB.traitor_contraband = list(/obj/item/card/emag/empty,/obj/item/clothing/glasses/phantomthief,/obj/item/clothing/gloves/chameleon/broken)
if(!GLOB.cult_contraband.len)
GLOB.cult_contraband = list(/obj/item/clockwork/slab,/obj/item/clockwork/component/belligerent_eye,/obj/item/clockwork/component/belligerent_eye/lens_gem,/obj/item/shuttle_curse,/obj/item/cult_shift)
/datum/objective/hoard/collector/find_target()
var/obj/item/I
var/I_type
if(prob(50))
I_type = pick_n_take(GLOB.traitor_contraband) // always unique unless it's run out, in which case we refill it anyway
else
I_type = pick_n_take(GLOB.cult_contraband)
I = new I_type
I.forceMove(get_turf(owner))
if(ishuman(owner))
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)
// 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
var/flavor_file
/datum/objective/flavor/proc/get_flavor_list()
return world.file2list(flavor_file)
/datum/objective/flavor/proc/forge_objective()
var/flavor_list = get_flavor_list()
explanation_text = pick(flavor_list)
/datum/objective/flavor/traitor
name = "traitor flavor"
flavor_file = "strings/flavor_objectives/traitor.txt"
/datum/objective/flavor/traitor/get_flavor_list()
. = ..()
switch(owner.assigned_role)
if("Station Engineer", "Atmospheric Technician")
. += world.file2list("strings/flavor_objectives/traitor/engineering.txt")
if("Medical Doctor","Chemist","Virologist","Geneticist")
. += world.file2list("strings/flavor_objectives/traitor/medical.txt")
if("Scientist","Roboticist","Geneticist")
. += world.file2list("strings/flavor_objectives/traitor/science.txt")
if("Assistant")
. += world.file2list("strings/flavor_objectives/traitor/assistant.txt")
/datum/objective/flavor/ninja_helping
flavor_file = "strings/flavor_objectives/ninja_helping.txt"
/datum/objective/flavor/ninja_syndie
flavor_file = "strings/flavor_objectives/ninja_syndie.txt"
/datum/objective/flavor/wizard
flavor_file = "strings/flavor_objectives/wizard.txt"
+107
View File
@@ -0,0 +1,107 @@
/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 100
/datum/sabotage_objective/processing/process()
check_condition_processing()
if(won >= 100)
STOP_PROCESSING(SSprocessing,src)
/datum/sabotage_objective/processing/check_conditions()
return won
/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
won = max(won,sink.power_drained/1e9)
sink_found = sink_found_this_time
count = 0
return FALSE
/obj/item/paper/guides/antag/supermatter_sabotage
info = "Ways to sabotage a supermatter:<br>\
<ul>\
<li>Set the air alarm's operating mode to anything that isn't 'draught' (yes, anything, though 'off' works best). Or just smash the air alarm, that works too.</li>\
<li>Wrench a pipe (the junction to the cold loop is most effective, but some setups will robust through this no issue; best to try for multiple)</li>\
<li>Pump in as much carbon dioxide, oxygen, plasma or tritium as you can find (this will likely also cause a singularity or tesla delamination, so watch out!)</li>\
<li>Unset the filters on the cooling loop, or, perhaps more insidious, set them to oxygen/plasma.</li>\
<li>Deactivate the digital valve that sends the exhaust gases to space (note: only works on box station; others you must unwrench).</li>\
<li>There are many other ways; be creative!</li>\
</ul>"
/datum/sabotage_objective/processing/supermatter
name = "Sabotage the supermatter so that it goes under 50% integrity. If it is delaminated, you will fail."
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) // you can win this with a wishgranter... lol.
won = max(1-((S.get_integrity()-50)/50),won)
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 5-(max(SSticker.station_integrity*4,320)/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
+4 -1
View File
@@ -128,6 +128,9 @@
return
. = ..()
/obj/item/card/emag/empty
uses = 0
/obj/item/emagrecharge
name = "electromagnet charging device"
desc = "A small cell with two prongs lazily jabbed into it. It looks like it's made for charging the small batteries found in electromagnetic devices, sadly this can't be recharged like a normal cell."
@@ -538,4 +541,4 @@ update_label("John Doe", "Clowny")
id_color = "#0000FF"
/obj/item/card/id/knight/captain
id_color = "#FFD700"
id_color = "#FFD700"
+3
View File
@@ -244,3 +244,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
/obj/item/storage/book/bible/syndicate/add_blood_DNA(list/blood_dna)
return FALSE
/obj/item/storage/book/bible/syndicate/empty
uses = 0
+1 -1
View File
@@ -214,4 +214,4 @@ GLOBAL_VAR(antag_prototypes)
var/datum/browser/panel = new(usr, "traitorpanel", "", 600, 600)
panel.set_content(out)
panel.open()
return
return
@@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
/datum/antagonist/New()
GLOB.antagonists += src
@@ -70,6 +71,7 @@ GLOBAL_LIST_EMPTY(antagonists)
greet()
apply_innate_effects()
give_antag_moodies()
remove_blacklisted_quirks()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
@@ -117,6 +119,17 @@ GLOBAL_LIST_EMPTY(antagonists)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
/datum/antagonist/proc/remove_blacklisted_quirks()
var/mob/living/L = owner
if(istype(L))
var/list/cut = list()
cut = SSquirks.filter_quirks(L.roundstart_quirks,blacklisted_quirks)
for(var/q in cut)
var/datum/quirk/Q = q
if(Q.antag_removal_text)
to_chat(L, "<span class='boldannounce'>[Q.antag_removal_text]</span>")
L.remove_quirk(Q.type)
//Returns the team antagonist belongs to if any.
/datum/antagonist/proc/get_team()
return
@@ -134,7 +147,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
+10 -4
View File
@@ -36,11 +36,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text]"
objective_count++
if(win)
report += "<span class='greentext'>The [name] was successful!</span>"
+10 -4
View File
@@ -108,11 +108,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text]"
objective_count++
if(win)
parts += "<span class='greentext'>The blood brothers were successful!</span>"
@@ -54,8 +54,10 @@
var/honorific
if(owner.current.gender == FEMALE)
honorific = "Ms."
else
else if(owner.current.gender == MALE)
honorific = "Mr."
else
honorific = "Mx."
if(GLOB.possible_changeling_IDs.len)
changelingID = pick(GLOB.possible_changeling_IDs)
GLOB.possible_changeling_IDs -= changelingID
@@ -552,11 +554,17 @@
if(objectives.len)
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = 0
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(changelingwin)
@@ -0,0 +1,19 @@
/datum/antagonist/collector
name = "Contraband Collector"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = FALSE
blacklisted_quirks = list() // no blacklist, these guys are harmless
/datum/antagonist/collector/proc/forge_objectives()
var/datum/objective/hoard/collector/O = new
O.owner = owner
O.find_target()
objectives += O
/datum/antagonist/collector/on_gain()
forge_objectives()
. = ..()
/datum/antagonist/collector/greet()
to_chat(owner, "<B>You are a contraband collector!</B>")
owner.announce_objectives()
+9 -3
View File
@@ -425,10 +425,16 @@
parts += "<b>The cultists' objectives were:</b>"
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(members.len)
@@ -44,11 +44,17 @@
var/objectives_text = ""
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
else
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE
result += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
result += objectives_text
+9 -16
View File
@@ -63,10 +63,10 @@
possible_targets.Cut(index,index+1)
if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/assassinate/O = new /datum/objective/assassinate()
var/datum/objective/assassinate/once/O = new /datum/objective/assassinate()
O.owner = owner
O.target = M
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]. You may let [M.p_they()] live, if they come back from death."
objectives += O
else //protect
var/datum/objective/protect/O = new /datum/objective/protect()
@@ -74,23 +74,16 @@
O.target = M
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
objectives += O
if(4) //debrain/capture
if(!possible_targets.len) continue
var/selected = rand(1,possible_targets.len)
var/datum/mind/M = possible_targets[selected]
var/is_bad_guy = possible_targets[M]
possible_targets.Cut(selected,selected+1)
if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy)
var/datum/objective/debrain/O = new /datum/objective/debrain()
if(4) //flavor
if(helping_station)
var/datum/objective/flavor/ninja_helping/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
O.target = M
O.explanation_text = "Steal the brain of [M.current.real_name]."
O.forge_objective()
objectives += O
else //capture
var/datum/objective/capture/O = new /datum/objective/capture()
else
var/datum/objective/flavor/ninja_syndie/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
O.gen_amount_goal()
O.forge_objective()
objectives += O
else
break
@@ -2,6 +2,7 @@
name = "Survivalist"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
blacklisted_quirks = list(/datum/quirk/nonviolent) // mutes are allowed
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
@@ -19,20 +20,8 @@
owner.announce_objectives()
/datum/antagonist/survivalist/guns
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/guns/forge_objectives()
var/datum/objective/steal_five_of_type/summon_guns/guns = new
guns.owner = owner
objectives += guns
..()
greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, and don't let anyone take them!"
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
greet_message = "Grow your newfound talent! Grab as many magical artefacts as possible, by any means necessary. Kill anyone who gets in your way."
/datum/antagonist/survivalist/magic/forge_objectives()
var/datum/objective/steal_five_of_type/summon_magic/magic = new
magic.owner = owner
objectives += magic
..()
greet_message = "This magic stuff is... so powerful. You want more. More! They want your power. They can't have it! Don't let them have it!"
@@ -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)
@@ -187,22 +191,37 @@
maroon_objective.owner = owner
maroon_objective.find_target()
add_objective(maroon_objective)
else
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
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
@@ -369,11 +388,17 @@
if(objectives.len)//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(uplink_true)
@@ -35,7 +35,7 @@
var/objectives_complete = TRUE
if(objectives.len)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
+13 -6
View File
@@ -61,9 +61,9 @@
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/new_objective = new("Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.")
new_objective.completed = TRUE //So they can greentext without admin intervention.
var/datum/objective/flavor/wizard/new_objective = new
new_objective.owner = owner
new_objective.forge_objective()
objectives += new_objective
if (!(locate(/datum/objective/escape) in objectives))
@@ -94,6 +94,7 @@
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
owner.announce_objectives()
to_chat(owner, "<B>These are merely guidelines! The federation are your masters, but you forge your own path!</B>")
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
@@ -265,11 +266,17 @@
var/count = 1
var/wizardwin = 1
for(var/datum/objective/objective in objectives)
if(objective.check_completion())
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = 0
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(wizardwin)
@@ -102,6 +102,12 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE)
var/gun_type = pick(GLOB.summoned_guns)
var/obj/item/gun/G = new gun_type(get_turf(H))
G.unlock()
var/datum/antagonist/survivalist/guns/our_antag_datum = H.mind.has_antag_datum(/datum/antagonist/survivalist/guns)
if(our_antag_datum)
var/datum/objective/hoard/O = new()
O.owner = H
O.set_target(G)
our_antag_datum.objectives += O
playsound(get_turf(H),'sound/magic/summon_guns.ogg', 50, 1)
var/in_hand = H.put_in_hands(G) // not always successful
@@ -128,6 +134,13 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE)
var/obj/item/M = new magic_type(get_turf(H))
playsound(get_turf(H),'sound/magic/summon_magic.ogg', 50, 1)
var/datum/antagonist/survivalist/magic/our_antag_datum = H.mind.has_antag_datum(/datum/antagonist/survivalist/magic)
if(istype(our_antag_datum))
var/datum/objective/hoard/O = new()
O.owner = H
O.set_target(M)
our_antag_datum.objectives += O
var/in_hand = H.put_in_hands(M)
to_chat(H, "<span class='warning'>\A [M] appears [in_hand ? "in your hand" : "at your feet"]!</span>")
+14
View File
@@ -1,3 +1,13 @@
## Dynamic storytellers weights: how likely each storyteller is to show up. This is adjusted by voting and recency.
STORYTELLER_WEIGHT CHAOTIC 2
STORYTELLER_WEIGHT TEAMWORK 4
STORYTELLER_WEIGHT CONVERSION 2
STORYTELLER_WEIGHT RANDOM 6
STORYTELLER_WEIGHT INTRIGUE 6
STORYTELLER_WEIGHT STORY 6
STORYTELLER_WEIGHT CALM 6
STORYTELLER_WEIGHT EXTENDED 0
## Injection delays: how long (in minutes) will pass before a midround or latejoin antag is injected.
DYNAMIC_MIDROUND_DELAY_MIN 5
DYNAMIC_MIDROUND_DELAY_MAX 15
@@ -92,6 +102,7 @@ DYNAMIC_WEIGHT RADIATION_STORM 1
DYNAMIC_WEIGHT LATEJOIN_TRAITOR 7
DYNAMIC_WEIGHT LATEJOIN_REVOLUTION 2
DYNAMIC_WEIGHT LATEJOIN_BLOODSUCKER 4
DYNAMIC_WEIGHT LATEJOIN_COLLECTOR 5
## Threat cost. This is decreased from the mode's threat when the rule is executed.
DYNAMIC_COST TRAITOR 10
@@ -153,6 +164,7 @@ DYNAMIC_COST RADIATION_STORM 3
DYNAMIC_COST LATEJOIN_TRAITOR 5
DYNAMIC_COST LATEJOIN_REVOLUTION 20
DYNAMIC_COST LATEJOIN_BLOODSUCKER 10
DYNAMIC_COST LATEJOIN_COLLECTOR 1
## Rule will not be generated with threat levels below requirement at a pop value. Pop values are determined by dynamic's pop-per-requirement.
## By default it's 0-8, 9-17, 18-26, 27-35, 36-44, 45-53, 54-60, 61-69, 70-78, 79+.
@@ -210,6 +222,7 @@ DYNAMIC_REQUIREMENTS RADIATION_STORM 5 5 5 5 5 5 5 5 5 5
DYNAMIC_REQUIREMENTS LATEJOIN_TRAITOR 40 30 20 15 15 15 15 15 15 15
DYNAMIC_REQUIREMENTS LATEJOIN_REVOLUTION 101 101 70 40 40 40 40 40 40 40
DYNAMIC_REQUIREMENTS LATEJOIN_BLOODSUCKER 40 30 20 15 15 15 15 15 15 15
DYNAMIC_REQUIREMENTS LATEJOIN_COLLECTOR 10 10 10 10 10 10 10 10 10 10
## An alternative, static requirement used instead when pop is over mode's high_pop_limit.
DYNAMIC_HIGH_POPULATION_REQUIREMENT TRAITOR 50
@@ -267,6 +280,7 @@ DYNAMIC_HIGH_POPULATION_REQUIREMENT RADIATION_STORM 5
DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_TRAITOR 15
DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_REVOLUTION 50
DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_BLOODSUCKER 15
DYNAMIC_HIGH_POPULATION_REQUIREMENT LATEJOIN_COLLECTOR 10
## Dynamic traitor stuff
+1
View File
@@ -151,6 +151,7 @@ h1.alert, h2.alert {color: #000000;}
.extremelybig {font-size: 5;}
.greentext {color: #00FF00; font-size: 3;}
.redtext {color: #FF0000; font-size: 3;}
.yellowtext {color: #FFCC00; font-size: 3;}
.clown {color: #FF69Bf; font-size: 3; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
.spooky {color: #FF9100;}
@@ -0,0 +1,6 @@
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.
@@ -0,0 +1,5 @@
The Gorlex Marauders want you to teach the heads of staff a lesson they will never forget.
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!
The Tiger Cooperative want you to kill one of the station's beloved pets. Make a show of it, though you don't have to reveal yourself.
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.
+8
View File
@@ -0,0 +1,8 @@
The Gorlex Marauders want you to teach the heads of staff a lesson they will never forget.
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.
@@ -0,0 +1 @@
The Tiger Cooperative want you to slow down the process of research as much as possible.
+4
View File
@@ -0,0 +1,4 @@
Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.
The Wizard Federation have sent you to take over the station by force. Kill the captain and take their place. The magocracy starts now.
You are the slipperiest wizard there ever was. Be loud, cause a ruckus, start a manhunt, and keep it going for as long as you can.
The crew must be shown the wonders of magic. Do some tricks for them. If they stop moving, that means they're in awe.
+2
View File
@@ -582,6 +582,7 @@
#include "code\game\gamemodes\game_mode.dm"
#include "code\game\gamemodes\objective.dm"
#include "code\game\gamemodes\objective_items.dm"
#include "code\game\gamemodes\objective_sabotage.dm"
#include "code\game\gamemodes\bloodsucker\bloodsucker.dm"
#include "code\game\gamemodes\bloodsucker\hunter.dm"
#include "code\game\gamemodes\brother\traitor_bro.dm"
@@ -1391,6 +1392,7 @@
#include "code\modules\antagonists\clockcult\clock_structures\traps\brass_skewer.dm"
#include "code\modules\antagonists\clockcult\clock_structures\traps\power_null.dm"
#include "code\modules\antagonists\clockcult\clock_structures\traps\steam_vent.dm"
#include "code\modules\antagonists\collector\collector.dm"
#include "code\modules\antagonists\cult\blood_magic.dm"
#include "code\modules\antagonists\cult\cult.dm"
#include "code\modules\antagonists\cult\cult_comms.dm"