From 41a8a70aaeab704e8705acb3e644db203c2eadec Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 00:56:23 -0500 Subject: [PATCH 01/68] Adds support for dynamic mathing --- code/__DEFINES/maths.dm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index 077297bb0d03..6dd9fcc61ecc 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -197,5 +197,12 @@ region_y2["[i]"] = TRUE return list(region_x1 & region_x2, region_y1 & region_y2) + +#define EXP_DISTRIBUTION(desired_mean) ( -(1/(1/desired_mean)) * log(rand(1, 1000) * 0.001) ) + +#define LORENTZ_DISTRIBUTION(x, s) ( s*tan(TODEGREES(PI*(rand()-0.5))) + x ) +#define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-y)/s)) + 1/2 ) + +#define RULE_OF_THREE(a, b, x) ((a*x)/b) // ) From 50d3bd01a120effcc2ad67170e4cfbb59aedc8df Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 00:57:37 -0500 Subject: [PATCH 02/68] Adds Dynamic Main Files. --- code/game/gamemodes/dynamic/dynamic.dm | 730 +++++++++++++++++ .../gamemodes/dynamic/dynamic_rulesets.dm | 180 +++++ .../dynamic/dynamic_rulesets_latejoin.dm | 115 +++ .../dynamic/dynamic_rulesets_midround.dm | 456 +++++++++++ .../dynamic/dynamic_rulesets_roundstart.dm | 731 ++++++++++++++++++ code/game/gamemodes/dynamic/readme.md | 67 ++ 6 files changed, 2279 insertions(+) create mode 100644 code/game/gamemodes/dynamic/dynamic.dm create mode 100644 code/game/gamemodes/dynamic/dynamic_rulesets.dm create mode 100644 code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm create mode 100644 code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm create mode 100644 code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm create mode 100644 code/game/gamemodes/dynamic/readme.md diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm new file mode 100644 index 000000000000..c67286e5a245 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -0,0 +1,730 @@ +#define CURRENT_LIVING_PLAYERS 1 +#define CURRENT_LIVING_ANTAGS 2 +#define CURRENT_DEAD_PLAYERS 3 +#define CURRENT_OBSERVERS 4 + +#define ONLY_RULESET 1 +#define HIGHLANDER_RULESET 2 +#define TRAITOR_RULESET 4 +#define MINOR_RULESET 8 + +#define RULESET_STOP_PROCESSING 1 + +// -- Injection delays +GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (5 MINUTES)) +GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (25 MINUTES)) + +GLOBAL_VAR_INIT(dynamic_midround_delay_min, (15 MINUTES)) +GLOBAL_VAR_INIT(dynamic_midround_delay_max, (35 MINUTES)) + +// Are HIGHLANDER_RULESETs allowed to stack? +GLOBAL_VAR_INIT(dynamic_no_stacking, TRUE) +// A number between -5 and +5. +// A negative value will give a more peaceful round and +// a positive value will give a round with higher threat. +GLOBAL_VAR_INIT(dynamic_curve_centre, 0) +// A number between 0.5 and 4. +// Higher value will favour extreme rounds and +// lower value rounds closer to the average. +GLOBAL_VAR_INIT(dynamic_curve_width, 1.8) +// If enabled only picks a single starting rule and executes only autotraitor midround ruleset. +GLOBAL_VAR_INIT(dynamic_classic_secret, FALSE) +// How many roundstart players required for high population override to take effect. +GLOBAL_VAR_INIT(dynamic_high_pop_limit, 55) +// If enabled does not accept or execute any rulesets. +GLOBAL_VAR_INIT(dynamic_forced_extended, FALSE) +// How high threat is required for HIGHLANDER_RULESETs stacking. +// This is independent of dynamic_no_stacking. +GLOBAL_VAR_INIT(dynamic_stacking_limit, 90) +// List of forced roundstart rulesets. +GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset) +// Forced threat level, setting this to zero or higher forces the roundstart threat to the value. +GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) + +/datum/game_mode/dynamic + name = "dynamic mode" + config_tag = "dynamic" + report_type = "dynamic" + + announce_span = "danger" + announce_text = "Dynamic mode!" // This needs to be changed maybe + + reroll_friendly = FALSE; + + // Threat logging vars + /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations + var/threat_level = 0 + /// Set at the beginning of the round. Spent by the mode to "purchase" rules. + var/threat = 0 + /// Running information about the threat. Can store text or datum entries. + var/list/threat_log = list() + /// List of roundstart rules used for selecting the rules. + var/list/roundstart_rules = list() + /// List of latejoin rules used for selecting the rules. + var/list/latejoin_rules = list() + /// List of midround rules used for selecting the rules. + var/list/midround_rules = list() + /** # Pop range per requirement. + * If the value is five the range is: + * 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+ + * If it is six the range is: + * 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+ + * If it is seven the range is: + * 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+ + */ + var/pop_per_requirement = 6 + /// The requirement used for checking if a second rule should be selected. + var/list/second_rule_req = list(100, 100, 80, 70, 60, 50, 30, 20, 10, 0) + /// The requirement used for checking if a third rule should be selected. + var/list/third_rule_req = list(100, 100, 100, 90, 80, 70, 60, 50, 40, 30) + /// Threat requirement for a second ruleset when high pop override is in effect. + var/high_pop_second_rule_req = 40 + /// Threat requirement for a third ruleset when high pop override is in effect. + var/high_pop_third_rule_req = 60 + /// Number of players who were ready on roundstart. + var/roundstart_pop_ready = 0 + /// List of candidates used on roundstart rulesets. + var/list/candidates = list() + /// Rules that are processed, rule_process is called on the rules in this list. + var/list/current_rules = list() + /// List of executed rulesets. + var/list/executed_rules = list() + /// Associative list of current players, in order: living players, living antagonists, dead players and observers. + var/list/list/current_players = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS) + /// When world.time is over this number the mode tries to inject a latejoin ruleset. + var/latejoin_injection_cooldown = 0 + /// When world.time is over this number the mode tries to inject a midround ruleset. + var/midround_injection_cooldown = 0 + /// When TRUE GetInjectionChance returns 100. + var/forced_injection = FALSE + /// Forced ruleset to be executed for the next latejoin. + var/datum/dynamic_ruleset/latejoin/forced_latejoin_rule = null + /// When current_players was updated last time. + var/pop_last_updated = 0 + /// How many percent of the rounds are more peaceful. + var/peaceful_percentage = 50 + /// If a highlander executed. + var/highlander_executed = FALSE + /// If a only ruleset has been executed. + var/only_ruleset_executed = FALSE + +/datum/game_mode/dynamic/admin_panel() + var/list/dat = list("Game Mode Panel

Game Mode Panel

") + dat += "Dynamic Mode \[VV\]\[Refresh\]
" + dat += "Threat Level: [threat_level]
" + + dat += "Threat to Spend: [threat] \[Adjust\] \[View Log\]
" + dat += "
" + dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].
" + dat += "On average, [peaceful_percentage]% of the rounds are more peaceful.
" + dat += "Forced extended: [GLOB.dynamic_forced_extended ? "On" : "Off"]
" + dat += "Classic secret (only autotraitor): [GLOB.dynamic_classic_secret ? "On" : "Off"]
" + dat += "No stacking (only one round-ender): [GLOB.dynamic_no_stacking ? "On" : "Off"]
" + dat += "Stacking limit: [GLOB.dynamic_stacking_limit] \[Adjust\]" + dat += "
" + dat += "Executed rulesets: " + if (executed_rules.len > 0) + dat += "
" + for (var/datum/dynamic_ruleset/DR in executed_rules) + dat += "[DR.ruletype] - [DR.name]
" + else + dat += "none.
" + dat += "
Injection Timers: ([get_injection_chance(TRUE)]% chance)
" + dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
" + dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
" + usr << browse(dat.Join(), "window=gamemode_panel;size=500x500") + +/datum/game_mode/dynamic/Topic(href, href_list) + if (..()) // Sanity, maybe ? + return + if(!check_rights(R_ADMIN)) + message_admins("[usr.key] has attempted to override the game mode panel!") + log_admin("[key_name(usr)] tried to use the game mode panel without authorization.") + return + if (href_list["forced_extended"]) + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + else if (href_list["no_stacking"]) + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + else if (href_list["classic_secret"]) + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + else if (href_list["adjustthreat"]) + var/threatadd = input("Specify how much threat to add (negative to subtract). This can inflate the threat level.", "Adjust Threat", 0) as null|num + if(!threatadd) + return + if(threatadd > 0) + create_threat(threatadd) + else + spend_threat(-threatadd) + else if (href_list["injectlate"]) + latejoin_injection_cooldown = 0 + forced_injection = TRUE + message_admins("[key_name(usr)] forced a latejoin injection.", 1) + else if (href_list["injectmid"]) + midround_injection_cooldown = 0 + forced_injection = TRUE + message_admins("[key_name(usr)] forced a midround injection.", 1) + else if (href_list["threatlog"]) + show_threatlog(usr) + else if (href_list["stacking_limit"]) + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + + admin_panel() // Refreshes the window + +// Checks if there are HIGHLANDER_RULESETs and calls the rule's round_result() proc +/datum/game_mode/dynamic/set_round_result() + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + if(rule.check_finished()) // Only the rule that actually finished the round sets round result. + return rule.round_result() + // If it got to this part, just pick one highlander if it exists + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + return rule.round_result() + return ..() + +/datum/game_mode/dynamic/send_intercept() + . = "Central Command Status Summary
" + switch(round(threat_level)) + if(0 to 19) + update_playercounts() + if(!current_players[CURRENT_LIVING_ANTAGS].len) + . += "Peaceful Waypoint
" + . += "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." + else + . += "Core Territory
" + . += "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." + if(20 to 39) + . += "Anomalous Exogeology
" + . += "Although your station lies within what is generally considered Nanotrasen-controlled space, the course of its orbit has caused it to cross unusually close to exogeological features with anomalous readings. Although these features offer opportunities for our research department, it is known that these little understood readings are often correlated with increased activity from competing interstellar organizations and individuals, among them the Wizard Federation and Cult of the Geometer of Blood - all known competitors for Anomaly Type B sites. Exercise elevated caution." + if(40 to 65) + . += "Contested System
" + . += "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." + if(66 to 79) + . += "Uncharted Space
" + . += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets." + if(80 to 99) + . += "Black Orbit
" + . += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece." + if(100) + . += "Impending Doom
" + . += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.
" + . += "Good luck." + + if(station_goals.len) + . += "
Special Orders for [station_name()]:" + for(var/datum/station_goal/G in station_goals) + G.on_report() + . += G.get_report() + + print_command_report(., "Central Command Status Summary", announce=FALSE) + priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", 'sound/ai/intercept.ogg') + if(GLOB.security_level < SEC_LEVEL_BLUE) + set_security_level(SEC_LEVEL_BLUE) + +// Yes, this is copy pasted from game_mode +/datum/game_mode/dynamic/check_finished(force_ending) + if(!SSticker.setup_done || !gamemode_ready) + return FALSE + if(replacementmode && round_converted == 2) + return replacementmode.check_finished() + if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)) + return TRUE + if(station_was_nuked) + return TRUE + if(force_ending) + return TRUE + for(var/datum/dynamic_ruleset/rule in executed_rules) + if(rule.flags & HIGHLANDER_RULESET) + return rule.check_finished() + +/datum/game_mode/dynamic/proc/show_threatlog(mob/admin) + if(!SSticker.HasRoundStarted()) + alert("The round hasn't started yet!") + return + + if(!check_rights(R_ADMIN)) + return + + var/list/out = list("Threat LogThreat Log
Starting Threat: [threat_level]
") + + for(var/entry in threat_log) + if(istext(entry)) + out += "[entry]
" + + out += "Remaining threat/threat_level: [threat]/[threat_level]" + + usr << browse(out.Join(), "window=threatlog;size=700x500") + +/// Generates the threat level using lorentz distribution and assigns peaceful_percentage. +/datum/game_mode/dynamic/proc/generate_threat() + var/relative_threat = LORENTZ_DISTRIBUTION(GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width) + threat_level = round(lorentz_to_threat(relative_threat), 0.1) + + peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100 + + threat = threat_level + +/datum/game_mode/dynamic/can_start() + message_admins("Dynamic mode parameters for the round:") + message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].") + message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].") + log_game("DYNAMIC: Dynamic mode parameters for the round:") + log_game("DYNAMIC: Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].") + log_game("DYNAMIC: Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].") + if(GLOB.dynamic_forced_threat_level >= 0) + threat_level = round(GLOB.dynamic_forced_threat_level, 0.1) + threat = threat_level + else + generate_threat() + + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + latejoin_injection_cooldown = round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time + + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + midround_injection_cooldown = round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time + message_admins("Dynamic Mode initialized with a Threat Level of... [threat_level]!") + log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!") + return TRUE + +/datum/game_mode/dynamic/pre_setup() + for (var/rule in subtypesof(/datum/dynamic_ruleset)) + var/datum/dynamic_ruleset/ruleset = new rule() + // Simple check if the ruleset should be added to the lists. + if(ruleset.name == "") + continue + switch(ruleset.ruletype) + if("Roundstart") + roundstart_rules += ruleset + if ("Latejoin") + latejoin_rules += ruleset + if ("Midround") + if (ruleset.weight) + midround_rules += ruleset + for(var/mob/dead/new_player/player in GLOB.player_list) + if(player.ready == PLAYER_READY_TO_PLAY && player.mind) + roundstart_pop_ready++ + candidates.Add(player) + log_game("DYNAMIC: Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.") + if (candidates.len <= 0) + return TRUE + if (roundstart_rules.len <= 0) + return TRUE + + if(GLOB.dynamic_forced_roundstart_ruleset.len > 0) + rigged_roundstart() + else + roundstart() + + var/starting_rulesets = "" + for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules) + starting_rulesets += "[DR.name], " + candidates.Cut() + return TRUE + +/datum/game_mode/dynamic/post_setup(report) + update_playercounts() + + for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules) + rule.candidates.Cut() // The rule should not use candidates at this point as they all are null. + if(rule.delay > 0) + addtimer(CALLBACK(rule, /datum/dynamic_ruleset/roundstart.proc/execute), rule.delay) + else + if(!rule.execute()) + stack_trace("The starting rule \"[rule.name]\" failed to execute.") + ..() + +/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it. +/datum/game_mode/dynamic/proc/rigged_roundstart() + message_admins("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.") + log_game("DYNAMIC: [GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.") + for (var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset) + message_admins("Drafting players for forced ruleset [rule.name].") + log_game("DYNAMIC: Drafting players for forced ruleset [rule.name].") + rule.mode = src + rule.candidates = candidates.Copy() + rule.trim_candidates() + rule.pop_per_requirement = rule.pop_per_requirement > 0 ? rule.pop_per_requirement : (src.pop_per_requirement > 0 ? src.pop_per_requirement : 6) //i hate myself for this + if (rule.ready(TRUE)) + picking_roundstart_rule(list(rule), forced = TRUE) + +/datum/game_mode/dynamic/proc/roundstart() + if (GLOB.dynamic_forced_extended) + log_game("DYNAMIC: Starting a round of forced extended.") + return TRUE + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules) + if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required + rule.candidates = candidates.Copy() + rule.trim_candidates() + if (rule.ready() && rule.candidates.len > 0) + drafted_rules[rule] = rule.weight + + var/indice_pop = min(10,round(roundstart_pop_ready/pop_per_requirement)+1) + var/extra_rulesets_amount = 0 + if (GLOB.dynamic_classic_secret) + extra_rulesets_amount = 0 + else + if (roundstart_pop_ready > GLOB.dynamic_high_pop_limit) + message_admins("High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") + log_game("DYNAMIC: High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") + if (threat_level > high_pop_second_rule_req) + extra_rulesets_amount++ + if (threat_level > high_pop_third_rule_req) + extra_rulesets_amount++ + else + if (threat_level >= second_rule_req[indice_pop]) + extra_rulesets_amount++ + if (threat_level >= third_rule_req[indice_pop]) + extra_rulesets_amount++ + + if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules)) + if (extra_rulesets_amount > 0) // We've got enough population and threat for a second rulestart rule + for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules) + if (rule.cost > threat) + drafted_rules -= rule + if (drafted_rules.len > 0 && picking_roundstart_rule(drafted_rules)) + if (extra_rulesets_amount > 1) // We've got enough population and threat for a third rulestart rule + for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules) + if (rule.cost > threat) + drafted_rules -= rule + picking_roundstart_rule(drafted_rules) + else + return FALSE + return TRUE + +/// Picks a random roundstart rule from the list given as an argument and executes it. +/datum/game_mode/dynamic/proc/picking_roundstart_rule(list/drafted_rules = list(), forced = FALSE) + var/datum/dynamic_ruleset/roundstart/starting_rule = pickweight(drafted_rules) + if(!starting_rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(starting_rule.blocking_rules, executed_rules)) + drafted_rules -= starting_rule + if(drafted_rules.len <= 0) + return FALSE + starting_rule = pickweight(drafted_rules) + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(starting_rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + drafted_rules -= starting_rule + if(drafted_rules.len <= 0) + return FALSE + starting_rule = pickweight(drafted_rules) + // With low pop and high threat there might be rulesets that get executed with no valid candidates. + else if(starting_rule.ready()) + drafted_rules -= starting_rule + if(drafted_rules.len <= 0) + return FALSE + starting_rule = pickweight(drafted_rules) + + message_admins("Picking a ruleset [starting_rule.name]") + log_game("DYNAMIC: Picking a ruleset [starting_rule.name]") + + roundstart_rules -= starting_rule + drafted_rules -= starting_rule + + starting_rule.trim_candidates() + if (starting_rule.pre_execute()) + spend_threat(starting_rule.cost) + threat_log += "[worldtime2text()]: Roundstart [starting_rule.name] spent [starting_rule.cost]" + if(starting_rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(starting_rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + executed_rules += starting_rule + if (starting_rule.persistent) + current_rules += starting_rule + for (var/datum/dynamic_ruleset/roundstart/rule in drafted_rules) + if (!rule.ready()) + drafted_rules -= rule // And removing rules that are no longer elligible + return TRUE + else + stack_trace("The starting rule \"[starting_rule.name]\" failed to pre_execute.") + return FALSE + +/// Picks a random midround OR latejoin rule from the list given as an argument and executes it. +/// Also this could be named better. +/datum/game_mode/dynamic/proc/picking_midround_latejoin_rule(list/drafted_rules = list(), forced = FALSE) + var/datum/dynamic_ruleset/rule = pickweight(drafted_rules) + if(!rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(rule.blocking_rules, executed_rules)) + drafted_rules -= rule + if(drafted_rules.len <= 0) + return FALSE + rule = pickweight(drafted_rules) + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + drafted_rules -= rule + if(drafted_rules.len <= 0) + return FALSE + rule = pickweight(drafted_rules) + + if(!rule.repeatable) + if(rule.ruletype == "Latejoin") + latejoin_rules = remove_from_list(latejoin_rules, rule.type) + else if(rule.type == "Midround") + midround_rules = remove_from_list(midround_rules, rule.type) + + if (rule.execute()) + log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].") + spend_threat(rule.cost) + threat_log += "[worldtime2text()]: [rule.ruletype] [rule.name] spent [rule.cost]" + if(rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + if(rule.ruletype == "Latejoin") + var/mob/M = pick(rule.candidates) + message_admins("[key_name(M)] joined the station, and was selected by the [rule.name] ruleset.") + log_game("DYNAMIC: [key_name(M)] joined the station, and was selected by the [rule.name] ruleset.") + executed_rules += rule + rule.candidates.Cut() + if (rule.persistent) + current_rules += rule + return TRUE + else + stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.") + return FALSE + +/// An experimental proc to allow admins to call rules on the fly or have rules call other rules. +/datum/game_mode/dynamic/proc/picking_specific_rule(ruletype, forced = FALSE) + var/datum/dynamic_ruleset/midround/new_rule + if(ispath(ruletype)) + new_rule = new ruletype() // You should only use it to call midround rules though. + else if(istype(ruletype, /datum/dynamic_ruleset)) + new_rule = ruletype + else + return FALSE + + if(!new_rule) + return FALSE + + if(!forced) + if(only_ruleset_executed) + return FALSE + // Check if a blocking ruleset has been executed. + else if(check_blocking(new_rule.blocking_rules, executed_rules)) + return FALSE + // Check if the ruleset is highlander and if a highlander ruleset has been executed + else if(new_rule.flags & HIGHLANDER_RULESET) + if(threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(highlander_executed) + return FALSE + + update_playercounts() + if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat))) + new_rule.trim_candidates() + if (new_rule.ready(forced)) + spend_threat(new_rule.cost) + threat_log += "[worldtime2text()]: Forced rule [new_rule.name] spent [new_rule.cost]" + if (new_rule.execute()) // This should never fail since ready() returned 1 + if(new_rule.flags & HIGHLANDER_RULESET) + highlander_executed = TRUE + else if(new_rule.flags & ONLY_RULESET) + only_ruleset_executed = TRUE + log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!") + executed_rules += new_rule + if (new_rule.persistent) + current_rules += new_rule + return TRUE + else if (forced) + log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of elligible players.") + return FALSE + +/datum/game_mode/dynamic/process() + if (pop_last_updated < world.time - (60 SECONDS)) + pop_last_updated = world.time + update_playercounts() + + for (var/datum/dynamic_ruleset/rule in current_rules) + if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing. + current_rules -= rule + + if (midround_injection_cooldown < world.time) + if (GLOB.dynamic_forced_extended) + return + + // Somehow it managed to trigger midround multiple times so this was moved here. + // There is no way this should be able to trigger an injection twice now. + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + midround_injection_cooldown = (round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time) + + // Time to inject some threat into the round + if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone + return + + message_admins("DYNAMIC: Checking for midround injection.") + log_game("DYNAMIC: Checking for midround injection.") + + update_playercounts() + if (get_injection_chance()) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/midround/rule in midround_rules) + if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + rule.trim_candidates() + if (rule.ready()) + drafted_rules[rule] = rule.get_weight() + if (drafted_rules.len > 0) + picking_midround_latejoin_rule(drafted_rules) + +/// Updates current_players. +/datum/game_mode/dynamic/proc/update_playercounts() + current_players[CURRENT_LIVING_PLAYERS] = list() + current_players[CURRENT_LIVING_ANTAGS] = list() + current_players[CURRENT_DEAD_PLAYERS] = list() + current_players[CURRENT_OBSERVERS] = list() + for (var/mob/M in GLOB.player_list) + if (istype(M, /mob/dead/new_player)) + continue + if (M.stat != DEAD) + current_players[CURRENT_LIVING_PLAYERS].Add(M) + if (M.mind && (M.mind.special_role || M.mind.antag_datums?.len > 0)) + current_players[CURRENT_LIVING_ANTAGS].Add(M) + else + if (istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if (O.started_as_observer) // Observers + current_players[CURRENT_OBSERVERS].Add(M) + continue + current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) + +/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection. +/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE) + if(forced_injection) + forced_injection = !dry_run + return 100 + var/chance = 0 + // If the high pop override is in effect, we reduce the impact of population on the antag injection chance + var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) + var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) + if (!current_players[CURRENT_LIVING_ANTAGS].len) + chance += 50 // No antags at all? let's boost those odds! + else + var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len + if (current_pop_per_antag > max_pop_per_antag) + chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag)) + else + chance += 25-10*(max_pop_per_antag-current_pop_per_antag) + if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len) + chance -= 30 // More than half the crew died? ew, let's calm down on antags + if (threat > 70) + chance += 15 + if (threat < 30) + chance -= 15 + return round(max(0,chance)) + +/// Removes type from the list +/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) + for(var/I in type_list) + if(istype(I, type)) + type_list -= I + return type_list + +/// Checks if a type in blocking_list is in rule_list. +/datum/game_mode/dynamic/proc/check_blocking(list/blocking_list, list/rule_list) + if(blocking_list.len > 0) + for(var/blocking in blocking_list) + for(var/datum/executed in rule_list) + if(blocking == executed.type) + return TRUE + return FALSE + +/// Checks if client age is age or older. +/datum/game_mode/dynamic/proc/check_age(client/C, age) + enemy_minimum_age = age + if(get_remaining_days(C) == 0) + enemy_minimum_age = initial(enemy_minimum_age) + return TRUE // Available in 0 days = available right now = player is old enough to play. + enemy_minimum_age = initial(enemy_minimum_age) + return FALSE + +/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer) + if (GLOB.dynamic_forced_extended) + return + if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left + return + + update_playercounts() + + if (forced_latejoin_rule) + forced_latejoin_rule.candidates = list(newPlayer) + forced_latejoin_rule.trim_candidates() + log_game("DYNAMIC: Forcing ruleset [forced_latejoin_rule]") + if (forced_latejoin_rule.ready(TRUE)) + picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE) + forced_latejoin_rule = null + + else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance())) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules) + if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + // No stacking : only one round-enter, unless > stacking_limit threat. + if (threat < GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(rule.flags & HIGHLANDER_RULESET && highlander_executed) + continue + + rule.candidates = list(newPlayer) + rule.trim_candidates() + if (rule.ready()) + drafted_rules[rule] = rule.get_weight() + + if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules)) + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + latejoin_injection_cooldown = round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time + +/// Refund threat, but no more than threat_level. +/datum/game_mode/dynamic/proc/refund_threat(regain) + threat = min(threat_level,threat+regain) + +/// Generate threat and increase the threat_level if it goes beyond, capped at 100 +/datum/game_mode/dynamic/proc/create_threat(gain) + threat = min(100, threat+gain) + if(threat > threat_level) + threat_level = threat + +/// Expend threat, can't fall under 0. +/datum/game_mode/dynamic/proc/spend_threat(cost) + threat = max(threat-cost,0) + +/// Turns the value generated by lorentz distribution to threat value between 0 and 100. +/datum/game_mode/dynamic/proc/lorentz_to_threat(x) + switch (x) + if (-INFINITY to -20) + return rand(0, 10) + if (-20 to -10) + return RULE_OF_THREE(-40, -20, x) + 50 + if (-10 to -5) + return RULE_OF_THREE(-30, -10, x) + 50 + if (-5 to -2.5) + return RULE_OF_THREE(-20, -5, x) + 50 + if (-2.5 to -0) + return RULE_OF_THREE(-10, -2.5, x) + 50 + if (0 to 2.5) + return RULE_OF_THREE(10, 2.5, x) + 50 + if (2.5 to 5) + return RULE_OF_THREE(20, 5, x) + 50 + if (5 to 10) + return RULE_OF_THREE(30, 10, x) + 50 + if (10 to 20) + return RULE_OF_THREE(40, 20, x) + 50 + if (20 to INFINITY) + return rand(90, 100) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm new file mode 100644 index 000000000000..637efcac9815 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -0,0 +1,180 @@ +/datum/dynamic_ruleset + /// For admin logging and round end screen. + var/name = "" + /// For admin logging and round end screen, do not change this unless making a new rule type. + var/ruletype = "" + /// If set to TRUE, the rule won't be discarded after being executed, and dynamic will call rule_process() every time it ticks. + var/persistent = FALSE + /// If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules) + var/repeatable = FALSE + /// If set higher than 0 decreases weight by itself causing the ruleset to appear less often the more it is repeated. + var/repeatable_weight_decrease = 2 + /// List of players that are being drafted for this rule + var/list/mob/candidates = list() + /// List of players that were selected for this rule + var/list/datum/mind/assigned = list() + /// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag + var/antag_flag = null + /// The antagonist datum that is assigned to the mobs mind on ruleset execution. + var/datum/antagonist/antag_datum = null + /// The required minimum account age for this ruleset. + var/minimum_required_age = 7 + /// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles. + var/list/protected_roles = list() + /// If set, rule will deny candidates from those roles always. + var/list/restricted_roles = list() + /// If set, rule will only accept candidates from those roles, IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/list/exclusive_roles = list() + /// If set, there needs to be a certain amount of players doing those roles (among the players who won't be drafted) for the rule to be drafted IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/list/enemy_roles = list() + /// If enemy_roles was set, this is the amount of enemy job workers needed per threat_level range (0-10,10-20,etc) IMPORTANT: DOES NOT WORK ON ROUNDSTART RULESETS. + var/required_enemies = list(1,1,0,0,0,0,0,0,0,0) + /// The rule needs this many candidates (post-trimming) to be executed (example: Cult needs 4 players at round start) + var/required_candidates = 0 + /// 1 -> 9, probability for this rule to be picked against other rules + var/weight = 5 + /// Threat cost for this rule, this is decreased from the mode's threat when the rule is executed. + var/cost = 0 + /// A flag that determines how the ruleset is handled + /// HIGHLANDER_RULESET are rulesets can end the round. + /// TRAITOR_RULESET and MINOR_RULESET can't end the round and have no difference right now. + var/flags = 0 + /// Pop range per requirement. If zero defaults to mode's pop_per_requirement. + var/pop_per_requirement = 0 + /// Requirements are the threat level requirements per pop range. + /// With the default values, The rule will never get drafted below 10 threat level (aka: "peaceful extended"), and it requires a higher threat level at lower pops. + var/list/requirements = list(40,30,20,10,10,10,10,10,10,10) + /// An alternative, static requirement used instead when pop is over mode's high_pop_limit. + var/high_population_requirement = 10 + /// Reference to the mode, use this instead of SSticker.mode. + var/datum/game_mode/dynamic/mode = null + /// If a role is to be considered another for the purpose of banning. + var/antag_flag_override = null + /// If a ruleset type which is in this list has been executed, then the ruleset will not be executed. + var/list/blocking_rules = list() + /// The minimum amount of players required for the rule to be considered. + var/minimum_players = 0 + /// The maximum amount of players required for the rule to be considered. + /// Anything below zero or exactly zero is ignored. + var/maximum_players = 0 + + +/datum/dynamic_ruleset/New() + ..() + if(CONFIG_GET(flag/protect_roles_from_antagonist)) + restricted_roles += protected_roles + if(CONFIG_GET(flag/protect_assistant_from_antagonist)) + restricted_roles += "Assistant" + + if (istype(SSticker.mode, /datum/game_mode/dynamic)) + mode = SSticker.mode + else if (GLOB.master_mode != "dynamic") // This is here to make roundstart forced ruleset function. + qdel(src) + + +/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart + ruletype = "Roundstart" + /// Delay for when execute will get called from the time of post_setup. + /// Make sure your ruleset works with execute being called during the game when using this. + var/delay = 0 + +// Can be drafted when a player joins the server +/datum/dynamic_ruleset/latejoin + ruletype = "Latejoin" + +/// By default, a rule is acceptable if it satisfies the threat level/population requirements. +/// If your rule has extra checks, such as counting security officers, do that in ready() instead +/datum/dynamic_ruleset/proc/acceptable(population = 0, threat_level = 0) + pop_per_requirement = pop_per_requirement > 0 ? pop_per_requirement : (mode.pop_per_requirement > 0 ? mode.pop_per_requirement : 6) //sorry ike + + if(minimum_players > population) + return FALSE + if(maximum_players > 0 && population > maximum_players) + return FALSE + if (population >= GLOB.dynamic_high_pop_limit) + return (threat_level >= high_population_requirement) + else + var/indice_pop = min(10,round(population/pop_per_requirement)+1) //fuck + return (threat_level >= requirements[indice_pop]) + +/// This is called if persistent variable is true everytime SSTicker ticks. +/datum/dynamic_ruleset/proc/rule_process() + return + +/// Called on game mode pre_setup for roundstart rulesets. +/// Do everything you need to do before job is assigned here. +/// IMPORTANT: ASSIGN special_role HERE +/datum/dynamic_ruleset/proc/pre_execute() + return TRUE + +/// Called on post_setup on roundstart and when the rule executes on midround and latejoin. +/// Give your candidates or assignees equipment and antag datum here. +/datum/dynamic_ruleset/proc/execute() + for(var/datum/mind/M in assigned) + M.add_antag_datum(antag_datum) + return TRUE + +/// Here you can perform any additional checks you want. (such as checking the map etc) +/// Remember that on roundstart no one knows what their job is at this point. +/// IMPORTANT: If ready() returns TRUE, that means pre_execute() or execute() should never fail! +/datum/dynamic_ruleset/proc/ready(forced = 0) + if (required_candidates > candidates.len) + return FALSE + return TRUE + +/// Gets weight of the ruleset +/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0 +/// Note: If you don't want repeatable rulesets to decrease their weight use the weight variable directly +/datum/dynamic_ruleset/proc/get_weight() + if(repeatable && weight > 1 && repeatable_weight_decrease > 0) + for(var/datum/dynamic_ruleset/DR in mode.executed_rules) + if(istype(DR, type)) + weight = max(weight-repeatable_weight_decrease,1) + return weight + +/// Here you can remove candidates that do not meet your requirements. +/// This means if their job is not correct or they have disconnected you can remove them from candidates here. +/// Usually this does not need to be changed unless you need some specific requirements from your candidates. +/datum/dynamic_ruleset/proc/trim_candidates() + return + +/// Set mode result and news report here. +/// Only called if ruleset is flagged as HIGHLANDER_RULESET +/datum/dynamic_ruleset/proc/round_result() + +/// Checks if round is finished, return true to end the round. +/// Only called if ruleset is flagged as HIGHLANDER_RULESET +/datum/dynamic_ruleset/proc/check_finished() + return FALSE + +////////////////////////////////////////////// +// // +// ROUNDSTART RULESETS // +// // +////////////////////////////////////////////// + +/// Checks if candidates are connected and if they are banned or don't want to be the antagonist. +/datum/dynamic_ruleset/roundstart/trim_candidates() + for(var/mob/dead/new_player/P in candidates) + if (!P.client || !P.mind) // Are they connected? + candidates.Remove(P) + continue + if(!mode.check_age(P.client, minimum_required_age)) + candidates.Remove(P) + continue + if(P.mind.special_role) // We really don't want to give antag to an antag. + candidates.Remove(P) + continue + if(antag_flag_override) + if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) + candidates.Remove(P) + continue + else + if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) + candidates.Remove(P) + continue + +/// Do your checks if the ruleset is ready to be executed here. +/// Should ignore certain checks if forced is TRUE +/datum/dynamic_ruleset/roundstart/ready(forced = FALSE) + return ..() diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm new file mode 100644 index 000000000000..c7b92a997f80 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -0,0 +1,115 @@ +////////////////////////////////////////////// +// // +// LATEJOIN RULESETS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/trim_candidates() + for(var/mob/P in candidates) + if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected? + candidates.Remove(P) + continue + if(!mode.check_age(P.client, minimum_required_age)) + candidates.Remove(P) + continue + if(antag_flag_override) + if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) + candidates.Remove(P) + continue + else + if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) + candidates.Remove(P) + continue + if (P.mind.assigned_role in restricted_roles) // Does their job allow for it? + candidates.Remove(P) + continue + if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + candidates.Remove(P) + continue + +/datum/dynamic_ruleset/latejoin/ready(forced = 0) + if (!forced) + var/job_check = 0 + if (enemy_roles.len > 0) + for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS]) + if (M.stat == DEAD) + continue // Dead players cannot count as opponents + if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles))) + job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it + + var/threat = round(mode.threat_level/10) + if (job_check < required_enemies[threat]) + return FALSE + return ..() + +/datum/dynamic_ruleset/latejoin/execute() + var/mob/M = pick(candidates) + assigned += M.mind + M.mind.special_role = antag_flag + M.mind.add_antag_datum(antag_datum) + return TRUE + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/infiltrator + name = "Syndicate Infiltrator" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_TRAITOR + protected_roles = list("Security Officer", "Warden", "Head of Personnel", "Detective", "Head of Security", "Captain") + restricted_roles = list("AI","Cyborg") + required_candidates = 1 + weight = 7 + cost = 5 + requirements = list(40,30,20,10,10,10,10,10,10,10) + high_population_requirement = 10 + repeatable = TRUE + flags = TRAITOR_RULESET + +////////////////////////////////////////////// +// // +// REVOLUTIONARY PROVOCATEUR // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/provocateur + name = "Provocateur" + antag_datum = /datum/antagonist/rev/head + antag_flag = ROLE_REV_HEAD + antag_flag_override = ROLE_REV + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director") + enemy_roles = list("AI", "Cyborg", "Security Officer","Detective","Head of Security", "Captain", "Warden") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 2 + cost = 20 + requirements = list(101,101,70,40,30,20,20,20,20,20) + high_population_requirement = 50 + flags = HIGHLANDER_RULESET + var/required_heads = 3 + +/datum/dynamic_ruleset/latejoin/provocateur/ready(forced=FALSE) + if (forced) + required_heads = 1 + if(!..()) + return FALSE + var/head_check = 0 + for(var/mob/player in mode.current_players[CURRENT_LIVING_PLAYERS]) + if (player.mind.assigned_role in GLOB.command_positions) + head_check++ + return (head_check >= required_heads) + +/datum/dynamic_ruleset/latejoin/provocateur/execute() + var/mob/M = pick(candidates) + assigned += M.mind + M.mind.special_role = antag_flag + var/datum/antagonist/rev/head/new_head = new() + new_head.give_flash = TRUE + new_head.give_hud = TRUE + new_head.remove_clumsy = TRUE + new_head = M.mind.add_antag_datum(new_head) + new_head.rev_team.max_headrevs = 1 // Only one revhead if it is latejoin. + return TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm new file mode 100644 index 000000000000..276d49ebf0aa --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -0,0 +1,456 @@ +////////////////////////////////////////////// +// // +// MIDROUND RULESETS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround // Can be drafted once in a while during a round + ruletype = "Midround" + /// If the ruleset should be restricted from ghost roles. + var/restrict_ghost_roles = TRUE + /// What mob type the ruleset is restricted to. + var/required_type = /mob/living/carbon/human + var/list/living_players = list() + var/list/living_antags = list() + var/list/dead_players = list() + var/list/list_observers = list() + +/datum/dynamic_ruleset/midround/from_ghosts + weight = 0 + required_type = /mob/dead/observer + /// Whether the ruleset should call generate_ruleset_body or not. + var/makeBody = TRUE + +/datum/dynamic_ruleset/midround/trim_candidates() + living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS]) + living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS]) + dead_players = trim_list(mode.current_players[CURRENT_DEAD_PLAYERS]) + list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS]) + +/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list()) + var/list/trimmed_list = L.Copy() + for(var/mob/M in trimmed_list) + if (!istype(M, required_type)) + trimmed_list.Remove(M) + continue + if (!M.client) // Are they connected? + trimmed_list.Remove(M) + continue + if(!mode.check_age(M.client, minimum_required_age)) + trimmed_list.Remove(M) + continue + if(antag_flag_override) + if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE))) + candidates.Remove(M) + continue + else + if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE))) + candidates.Remove(M) + continue + if (M.mind) + if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? + trimmed_list.Remove(M) + continue + if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded? + trimmed_list.Remove(M) + continue + if ((exclusive_roles.len > 0) && !(M.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job? + trimmed_list.Remove(M) + continue + return trimmed_list + +// You can then for example prompt dead players in execute() to join as strike teams or whatever +// Or autotator someone + +// IMPORTANT, since /datum/dynamic_ruleset/midround may accept candidates from both living, dead, and even antag players, you need to manually check whether there are enough candidates +// (see /datum/dynamic_ruleset/midround/autotraitor/ready(var/forced = FALSE) for example) +/datum/dynamic_ruleset/midround/ready(forced = FALSE) + if (!forced) + var/job_check = 0 + if (enemy_roles.len > 0) + for (var/mob/M in living_players) + if (M.stat == DEAD) + continue // Dead players cannot count as opponents + if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles))) + job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it + + var/threat = round(mode.threat_level/10) + if (job_check < required_enemies[threat]) + return FALSE + return TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/execute() + var/list/possible_candidates = list() + possible_candidates.Add(dead_players) + possible_candidates.Add(list_observers) + send_applications(possible_candidates) + if(assigned.len > 0) + return TRUE + else + return FALSE + +/// This sends a poll to ghosts if they want to be a ghost spawn from a ruleset. +/datum/dynamic_ruleset/midround/from_ghosts/proc/send_applications(list/possible_volunteers = list()) + if (possible_volunteers.len <= 0) // This shouldn't happen, as ready() should return FALSE if there is not a single valid candidate + message_admins("Possible volunteers was 0. This shouldn't appear, because of ready(), unless you forced it!") + return + message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") + log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.") + + candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300) + + if(!candidates || candidates.len <= 0) + message_admins("The ruleset [name] received no applications.") + log_game("DYNAMIC: The ruleset [name] received no applications.") + mode.refund_threat(cost) + mode.threat_log += "[worldtime2text()]: Rule [name] refunded [cost] (no applications)" + mode.executed_rules -= src + return + + message_admins("[candidates.len] players volunteered for the ruleset [name].") + log_game("DYNAMIC: [candidates.len] players volunteered for [name].") + review_applications() + +/// Here is where you can check if your ghost applicants are valid for the ruleset. +/// Called by send_applications(). +/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications() + for (var/i = 1, i <= required_candidates, i++) + if(candidates.len <= 0) + if(i == 1) + // We have found no candidates so far and we are out of applicants. + mode.refund_threat(cost) + mode.threat_log += "[worldtime2text()]: Rule [name] refunded [cost] (all applications invalid)" + mode.executed_rules -= src + break + var/mob/applicant = pick(candidates) + candidates -= applicant + if(!isobserver(applicant)) + if(applicant.stat == DEAD) // Not an observer? If they're dead, make them one. + applicant = applicant.ghostize(FALSE) + else // Not dead? Disregard them, pick a new applicant + i-- + continue + + if(!applicant) + i-- + continue + + var/mob/new_character = applicant + + if (makeBody) + new_character = generate_ruleset_body(applicant) + + finish_setup(new_character, i) + assigned += applicant + notify_ghosts("[new_character] has been picked for the ruleset [name]!", source = new_character, action = NOTIFY_ORBIT, header="Something Interesting!") + +/datum/dynamic_ruleset/midround/from_ghosts/proc/generate_ruleset_body(mob/applicant) + var/mob/living/carbon/human/new_character = makeBody(applicant) + new_character.dna.remove_all_mutations() + return new_character + +/datum/dynamic_ruleset/midround/from_ghosts/proc/finish_setup(mob/new_character, index) + var/datum/antagonist/new_role = new antag_datum() + setup_role(new_role) + new_character.mind.add_antag_datum(new_role) + new_character.mind.special_role = antag_flag + +/datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role) + return + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/autotraitor + name = "Syndicate Sleeper Agent" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_TRAITOR + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") + restricted_roles = list("Cyborg", "AI", "Positronic Brain") + required_candidates = 1 + weight = 7 + cost = 10 + requirements = list(50,40,30,20,10,10,10,10,10,10) + repeatable = TRUE + high_population_requirement = 10 + flags = TRAITOR_RULESET + +/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0) + var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len + var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len + var/max_traitors = round(player_count / 10) + 1 + if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low + return ..() + else + return FALSE + +/datum/dynamic_ruleset/midround/autotraitor/trim_candidates() + ..() + for(var/mob/living/player in living_players) + if(issilicon(player)) // Your assigned role doesn't change when you are turned into a silicon. + living_players -= player + continue + if(is_centcom_level(player.z)) + living_players -= player // We don't autotator people in CentCom + continue + if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) + living_players -= player // We don't autotator people with roles already + +/datum/dynamic_ruleset/midround/autotraitor/ready(forced = FALSE) + if (required_candidates > living_players.len) + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/autotraitor/execute() + var/mob/M = pick(living_players) + assigned += M + living_players -= M + var/datum/antagonist/traitor/newTraitor = new + M.mind.add_antag_datum(newTraitor) + return TRUE + + +////////////////////////////////////////////// +// // +// Malfunctioning AI // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/malf + name = "Malfunctioning AI" + antag_datum = /datum/antagonist/traitor + antag_flag = ROLE_MALF + enemy_roles = list("Security Officer", "Warden","Detective","Head of Security", "Captain", "Scientist", "Chemist", "Research Director", "Chief Engineer") + exclusive_roles = list("AI") + required_enemies = list(4,4,4,4,4,4,2,2,2,0) + required_candidates = 1 + weight = 3 + cost = 35 + requirements = list(101,101,80,70,60,60,50,50,40,40) + high_population_requirement = 35 + required_type = /mob/living/silicon/ai + var/ion_announce = 33 + var/removeDontImproveChance = 10 + +/datum/dynamic_ruleset/midround/malf/trim_candidates() + ..() + candidates = candidates[CURRENT_LIVING_PLAYERS] + for(var/mob/living/player in candidates) + if(!isAI(player)) + candidates -= player + continue + if(is_centcom_level(player.z)) + candidates -= player + continue + if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) + candidates -= player + +/datum/dynamic_ruleset/midround/malf/execute() + if(!candidates || !candidates.len) + return FALSE + var/mob/living/silicon/ai/M = pick_n_take(candidates) + assigned += M.mind + var/datum/antagonist/traitor/AI = new + M.mind.special_role = antag_flag + M.mind.add_antag_datum(AI) + if(prob(ion_announce)) + priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/ai/ionstorm.ogg') + if(prob(removeDontImproveChance)) + M.replace_random_law(generate_ion_law(), list(LAW_INHERENT, LAW_SUPPLIED, LAW_ION)) + else + M.add_ion_law(generate_ion_law()) + return TRUE + +////////////////////////////////////////////// +// // +// WIZARD (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/wizard + name = "Wizard" + antag_datum = /datum/antagonist/wizard + antag_flag = ROLE_WIZARD + enemy_roles = list("Security Officer","Detective","Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 1 + cost = 20 + requirements = list(90,90,70,40,30,20,10,10,10,10) + high_population_requirement = 50 + repeatable = TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE) + if (required_candidates > (dead_players.len + list_observers.len)) + return FALSE + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/wizard/finish_setup(mob/new_character, index) + ..() + new_character.forceMove(pick(GLOB.wizardstart)) + +////////////////////////////////////////////// +// // +// NUCLEAR OPERATIVES (MIDROUND) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear + name = "Nuclear Assault" + antag_flag = ROLE_OPERATIVE + antag_datum = /datum/antagonist/nukeop + enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain") + required_enemies = list(3,3,3,3,3,2,1,1,0,0) + required_candidates = 5 + weight = 5 + cost = 35 + requirements = list(90,90,90,80,60,40,30,20,10,10) + high_population_requirement = 10 + var/operative_cap = list(2,2,3,3,4,5,5,5,5,5) + var/datum/team/nuclear/nuke_team + flags = HIGHLANDER_RULESET + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/acceptable(population=0, threat=0) + if (locate(/datum/dynamic_ruleset/roundstart/nuclear) in mode.executed_rules) + return FALSE // Unavailable if nuke ops were already sent at roundstart + var/indice_pop = min(10,round(living_players.len/5)+1) + required_candidates = operative_cap[indice_pop] + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE) + if (required_candidates > (dead_players.len + list_observers.len)) + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index) + new_character.mind.special_role = "Nuclear Operative" + new_character.mind.assigned_role = "Nuclear Operative" + if (index == 1) // Our first guy is the leader + var/datum/antagonist/nukeop/leader/new_role = new + nuke_team = new_role.nuke_team + new_character.mind.add_antag_datum(new_role) + else + return ..() + +////////////////////////////////////////////// +// // +// BLOB (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/blob + name = "Blob" + antag_datum = /datum/antagonist/blob + antag_flag = ROLE_BLOB + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 4 + cost = 10 + requirements = list(101,101,101,80,60,50,30,20,10,10) + high_population_requirement = 50 + repeatable = TRUE + +/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant) + var/body = applicant.become_overmind() + return body + +////////////////////////////////////////////// +// // +// XENOMORPH (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph + name = "Alien Infestation" + antag_datum = /datum/antagonist/xeno + antag_flag = ROLE_ALIEN + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 3 + cost = 10 + requirements = list(101,101,101,70,50,40,20,15,10,10) + high_population_requirement = 50 + repeatable = TRUE + var/list/vents = list() + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute() + // 50% chance of being incremented by one + required_candidates += prob(50) + for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines) + if(QDELETED(temp_vent)) + continue + if(is_station_level(temp_vent.loc.z) && !temp_vent.welded) + var/datum/pipeline/temp_vent_parent = temp_vent.parents[1] + if(!temp_vent_parent) + continue // No parent vent + // Stops Aliens getting stuck in small networks. + // See: Security, Virology + if(temp_vent_parent.other_atmosmch.len > 20) + vents += temp_vent + if(!vents.len) + return FALSE + . = ..() + +/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant) + var/obj/vent = pick_n_take(vents) + var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc) + new_xeno.key = applicant.key + message_admins("[ADMIN_LOOKUPFLW(new_xeno)] has been made into an alien by the midround ruleset.") + log_game("DYNAMIC: [key_name(new_xeno)] was spawned as an alien by the midround ruleset.") + return new_xeno + +////////////////////////////////////////////// +// // +// NIGHTMARE (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare + name = "Nightmare" + antag_datum = /datum/antagonist/nightmare + antag_flag = "Nightmare" + antag_flag_override = ROLE_ALIEN + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") + required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_candidates = 1 + weight = 3 + cost = 10 + requirements = list(101,101,101,70,50,40,20,15,10,10) + high_population_requirement = 50 + repeatable = TRUE + var/list/spawn_locs = list() + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute() + for(var/X in GLOB.xeno_spawn) + var/turf/T = X + var/light_amount = T.get_lumcount() + if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) + spawn_locs += T + if(!spawn_locs.len) + return FALSE + . = ..() + +/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant) + var/datum/mind/player_mind = new /datum/mind(applicant.key) + player_mind.active = TRUE + + var/mob/living/carbon/human/S = new (pick(spawn_locs)) + player_mind.transfer_to(S) + player_mind.assigned_role = "Nightmare" + player_mind.special_role = "Nightmare" + player_mind.add_antag_datum(/datum/antagonist/nightmare) + S.set_species(/datum/species/shadow/nightmare) + + playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) + message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Nightmare by the midround ruleset.") + log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.") + return S diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm new file mode 100644 index 000000000000..d7ec30eef7c5 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -0,0 +1,731 @@ + +////////////////////////////////////////////// +// // +// SYNDICATE TRAITORS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/traitor + name = "Traitors" + persistent = TRUE + antag_flag = ROLE_TRAITOR + antag_datum = /datum/antagonist/traitor/ + minimum_required_age = 0 + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") + restricted_roles = list("Cyborg") + required_candidates = 1 + weight = 5 + cost = 10 + requirements = list(10,10,10,10,10,10,10,10,10,10) + high_population_requirement = 10 + var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec) + +/datum/dynamic_ruleset/roundstart/traitor/pre_execute() + var/traitor_scaling_coeff = 10 - max(0,round(mode.threat_level/10)-5) // Above 50 threat level, coeff goes down by 1 for every 10 levels + var/num_traitors = min(round(mode.candidates.len / traitor_scaling_coeff) + 1, candidates.len) + for (var/i = 1 to num_traitors) + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.special_role = ROLE_TRAITOR + M.mind.restricted_roles = restricted_roles + return TRUE + +/datum/dynamic_ruleset/roundstart/traitor/rule_process() + if (autotraitor_cooldown > 0) + autotraitor_cooldown-- + else + autotraitor_cooldown = 450 // 15 minutes + message_admins("Checking if we can turn someone into a traitor.") + log_game("DYNAMIC: Checking if we can turn someone into a traitor.") + mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autotraitor) + +////////////////////////////////////////// +// // +// BLOOD BROTHERS // +// // +////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/traitorbro + name = "Blood Brothers" + antag_flag = ROLE_BROTHER + antag_datum = /datum/antagonist/brother/ + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") + restricted_roles = list("Cyborg", "AI") + required_candidates = 2 + weight = 4 + cost = 10 + requirements = list(40,30,30,20,20,15,15,15,10,10) + high_population_requirement = 15 + var/list/datum/team/brother_team/pre_brother_teams = list() + var/const/team_amount = 2 // Hard limit on brother teams if scaling is turned off + var/const/min_team_size = 2 + +/datum/dynamic_ruleset/roundstart/traitorbro/pre_execute() + var/num_teams = team_amount + var/bsc = CONFIG_GET(number/brother_scaling_coeff) + if(bsc) + num_teams = max(1, round(mode.roundstart_pop_ready / bsc)) + + for(var/j = 1 to num_teams) + if(candidates.len < min_team_size || candidates.len < required_candidates) + break + var/datum/team/brother_team/team = new + var/team_size = prob(10) ? min(3, candidates.len) : 2 + for(var/k = 1 to team_size) + var/mob/bro = pick_n_take(candidates) + assigned += bro.mind + team.add_member(bro.mind) + bro.mind.special_role = "brother" + bro.mind.restricted_roles = restricted_roles + pre_brother_teams += team + return TRUE + +/datum/dynamic_ruleset/roundstart/traitorbro/execute() + for(var/datum/team/brother_team/team in pre_brother_teams) + team.pick_meeting_area() + team.forge_brother_objectives() + for(var/datum/mind/M in team.members) + M.add_antag_datum(/datum/antagonist/brother, team) + team.update_name() + mode.brother_teams += pre_brother_teams + return TRUE + +////////////////////////////////////////////// +// // +// CHANGELINGS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/changeling + name = "Changelings" + antag_flag = ROLE_CHANGELING + antag_datum = /datum/antagonist/changeling + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") + restricted_roles = list("AI", "Cyborg") + required_candidates = 1 + weight = 3 + cost = 30 + requirements = list(80,70,60,50,40,20,20,10,10,10) + high_population_requirement = 10 + var/team_mode_probability = 30 + +/datum/dynamic_ruleset/roundstart/changeling/pre_execute() + var/num_changelings = min(round(mode.candidates.len / 10) + 1, candidates.len) + for (var/i = 1 to num_changelings) + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.restricted_roles = restricted_roles + M.mind.special_role = ROLE_CHANGELING + return TRUE + +/datum/dynamic_ruleset/roundstart/changeling/execute() + var/team_mode = FALSE + if(prob(team_mode_probability)) + team_mode = TRUE + var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective) + var/list/possible_team_objectives = list() + for(var/T in team_objectives) + var/datum/objective/changeling_team_objective/CTO = T + if(assigned.len >= initial(CTO.min_lings)) + possible_team_objectives += T + + if(possible_team_objectives.len && prob(20*assigned.len)) + GLOB.changeling_team_objective_type = pick(possible_team_objectives) + for(var/datum/mind/changeling in assigned) + var/datum/antagonist/changeling/new_antag = new antag_datum() + new_antag.team_mode = team_mode + changeling.add_antag_datum(new_antag) + return TRUE + +////////////////////////////////////////////// +// // +// WIZARDS // +// // +////////////////////////////////////////////// + +// Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round. +/datum/dynamic_ruleset/roundstart/wizard + name = "Wizard" + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 1 + weight = 2 + cost = 30 + requirements = list(90,90,70,40,30,20,10,10,10,10) + high_population_requirement = 10 + var/list/roundstart_wizards = list() + +/datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0) + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/roundstart/wizard/pre_execute() + if(GLOB.wizardstart.len == 0) + return FALSE + + var/mob/M = pick_n_take(candidates) + if (M) + assigned += M.mind + M.mind.assigned_role = ROLE_WIZARD + M.mind.special_role = ROLE_WIZARD + + return TRUE + +/datum/dynamic_ruleset/roundstart/wizard/execute() + for(var/datum/mind/M in assigned) + M.current.forceMove(pick(GLOB.wizardstart)) + M.add_antag_datum(new antag_datum()) + return TRUE + +////////////////////////////////////////////// +// // +// BLOOD CULT // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/bloodcult + name = "Blood Cult" + antag_flag = ROLE_CULTIST + antag_datum = /datum/antagonist/cult + minimum_required_age = 14 + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel") + required_candidates = 2 + weight = 3 + cost = 30 + requirements = list(100,90,80,60,40,30,10,10,10,10) + high_population_requirement = 10 + flags = HIGHLANDER_RULESET + var/cultist_cap = list(2,2,2,3,3,4,4,4,4,4) + var/datum/team/cult/main_cult + +/datum/dynamic_ruleset/roundstart/bloodcult/ready(forced = FALSE) + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + required_candidates = cultist_cap[indice_pop] + . = ..() + +/datum/dynamic_ruleset/roundstart/bloodcult/pre_execute() + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + var/cultists = cultist_cap[indice_pop] + for(var/cultists_number = 1 to cultists) + if(candidates.len <= 0) + break + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.special_role = ROLE_CULTIST + M.mind.restricted_roles = restricted_roles + return TRUE + +/datum/dynamic_ruleset/roundstart/bloodcult/execute() + main_cult = new + for(var/datum/mind/M in assigned) + var/datum/antagonist/cult/new_cultist = new antag_datum() + new_cultist.cult_team = main_cult + new_cultist.give_equipment = TRUE + M.add_antag_datum(new_cultist) + main_cult.setup_objectives() + return TRUE + +/datum/dynamic_ruleset/roundstart/bloodcult/round_result() + ..() + if(main_cult.check_cult_victory()) + SSticker.mode_result = "win - cult win" + SSticker.news_report = CULT_SUMMON + else + SSticker.mode_result = "loss - staff stopped the cult" + SSticker.news_report = CULT_FAILURE + +////////////////////////////////////////////// +// // +// NUCLEAR OPERATIVES // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/nuclear + name = "Nuclear Emergency" + antag_flag = ROLE_OPERATIVE + antag_datum = /datum/antagonist/nukeop + var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 5 + weight = 3 + cost = 40 + requirements = list(90,90,90,80,60,40,30,20,10,10) + high_population_requirement = 10 + flags = HIGHLANDER_RULESET + var/operative_cap = list(2,2,2,3,3,3,4,4,5,5) + var/datum/team/nuclear/nuke_team + +/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE) + var/indice_pop = min(10,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + required_candidates = operative_cap[indice_pop] + . = ..() + +/datum/dynamic_ruleset/roundstart/nuclear/pre_execute() + // If ready() did its job, candidates should have 5 or more members in it + + var/indice_pop = min(10,round(mode.roundstart_pop_ready/5)+1) + var/operatives = operative_cap[indice_pop] + for(var/operatives_number = 1 to operatives) + if(candidates.len <= 0) + break + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.assigned_role = "Nuclear Operative" + M.mind.special_role = "Nuclear Operative" + return TRUE + +/datum/dynamic_ruleset/roundstart/nuclear/execute() + var/leader = TRUE + for(var/datum/mind/M in assigned) + if (leader) + leader = FALSE + var/datum/antagonist/nukeop/leader/new_op = M.add_antag_datum(antag_leader_datum) + nuke_team = new_op.nuke_team + else + var/datum/antagonist/nukeop/new_op = new antag_datum() + M.add_antag_datum(new_op) + return TRUE + +/datum/dynamic_ruleset/roundstart/nuclear/round_result() + var result = nuke_team.get_result() + switch(result) + if(NUKE_RESULT_FLUKE) + SSticker.mode_result = "loss - syndicate nuked - disk secured" + SSticker.news_report = NUKE_SYNDICATE_BASE + if(NUKE_RESULT_NUKE_WIN) + SSticker.mode_result = "win - syndicate nuke" + SSticker.news_report = STATION_NUKED + if(NUKE_RESULT_NOSURVIVORS) + SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time" + SSticker.news_report = STATION_NUKED + if(NUKE_RESULT_WRONG_STATION) + SSticker.mode_result = "halfwin - blew wrong station" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_WRONG_STATION_DEAD) + SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD) + SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_CREW_WIN) + SSticker.mode_result = "loss - evacuation - disk secured" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_DISK_LOST) + SSticker.mode_result = "halfwin - evacuation - disk not secured" + SSticker.news_report = OPERATIVE_SKIRMISH + if(NUKE_RESULT_DISK_STOLEN) + SSticker.mode_result = "halfwin - detonation averted" + SSticker.news_report = OPERATIVE_SKIRMISH + else + SSticker.mode_result = "halfwin - interrupted" + SSticker.news_report = OPERATIVE_SKIRMISH + +////////////////////////////////////////////// +// // +// REVS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/revs + name = "Revolution" + persistent = TRUE + antag_flag = ROLE_REV_HEAD + antag_flag_override = ROLE_REV + antag_datum = /datum/antagonist/rev/head + minimum_required_age = 14 + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director") + required_candidates = 3 + weight = 2 + delay = 7 MINUTES + cost = 35 + requirements = list(101,101,70,40,30,20,10,10,10,10) + high_population_requirement = 10 + flags = HIGHLANDER_RULESET + // I give up, just there should be enough heads with 35 players... + minimum_players = 35 + var/datum/team/revolution/revolution + var/finished = 0 + +/datum/dynamic_ruleset/roundstart/revs/pre_execute() + var/max_canditates = 3 + for(var/i = 1 to max_canditates) + if(candidates.len <= 0) + break + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.restricted_roles = restricted_roles + M.mind.special_role = antag_flag + return TRUE + +/datum/dynamic_ruleset/roundstart/revs/execute() + revolution = new() + for(var/datum/mind/M in assigned) + var/datum/antagonist/rev/head/new_head = new antag_datum() + new_head.give_flash = TRUE + new_head.give_hud = TRUE + new_head.remove_clumsy = TRUE + M.add_antag_datum(new_head,revolution) + revolution.update_objectives() + revolution.update_heads() + SSshuttle.registerHostileEnvironment(src) + return TRUE + +/datum/dynamic_ruleset/roundstart/revs/rule_process() +/datum/dynamic_ruleset/roundstart/revs/rule_process() + if(!revolution) + log_game("DYNAMIC: Something went horrifically wrong with [name] - and the antag datum could not be created. Notify coders.") + return + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/datum/dynamic_ruleset/roundstart/revs/check_finished() + if(CONFIG_GET(keyed_list/continuous)["revolution"]) + if(finished) + SSshuttle.clearHostileEnvironment(src) + return ..() + if(finished != 0) + return TRUE + else + return ..() + +/datum/dynamic_ruleset/roundstart/revs/proc/check_rev_victory() + for(var/datum/objective/mutiny/objective in revolution.objectives) + if(!(objective.check_completion())) + return FALSE + return TRUE + +/datum/dynamic_ruleset/roundstart/revs/proc/check_heads_victory() + for(var/datum/mind/rev_mind in revolution.head_revolutionaries()) + var/turf/T = get_turf(rev_mind.current) + if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(T.z)) + if(ishuman(rev_mind.current) || ismonkey(rev_mind.current)) + return FALSE + return TRUE + +/datum/dynamic_ruleset/roundstart/revs/round_result() + if(finished == 1) + SSticker.mode_result = "win - heads killed" + SSticker.news_report = REVS_WIN + else if(finished == 2) + SSticker.mode_result = "loss - rev heads killed" + SSticker.news_report = REVS_LOSE + +// Admin only rulesets. The threat requirement is 101 so it is not possible to roll them. + +////////////////////////////////////////////// +// // +// EXTENDED // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/extended + name = "Extended" + antag_flag = null + antag_datum = null + restricted_roles = list() + required_candidates = 0 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + +/datum/dynamic_ruleset/roundstart/extended/pre_execute() + message_admins("Starting a round of extended.") + log_game("Starting a round of extended.") + mode.spend_threat(mode.threat) + return TRUE + +////////////////////////////////////////////// +// // +// CLOCKCULT // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/clockcult + name = "Clockcult" + antag_flag = ROLE_SERVANT_OF_RATVAR + antag_datum = /datum/antagonist/clockcult + restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel") + required_candidates = 4 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + flags = HIGHLANDER_RULESET + var/ark_time + +/datum/dynamic_ruleset/roundstart/clockcult/pre_execute() + var/list/errorList = list() + var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE) + if(errorList.len) + message_admins("Reebe failed to load!") + log_game("Reebe failed to load!") + return FALSE + for(var/datum/parsed_map/PM in reebes) + PM.initTemplateBounds() + + var/starter_servants = 4 + var/number_players = mode.roundstart_pop_ready + if(number_players > 30) + number_players -= 30 + starter_servants += round(number_players / 10) + starter_servants = min(starter_servants, 8) + for (var/i in 1 to starter_servants) + var/mob/servant = pick_n_take(candidates) + assigned += servant.mind + servant.mind.assigned_role = ROLE_SERVANT_OF_RATVAR + servant.mind.special_role = ROLE_SERVANT_OF_RATVAR + ark_time = 30 + round((number_players / 5)) + ark_time = min(ark_time, 35) + return TRUE + +/datum/dynamic_ruleset/roundstart/clockcult/execute() + var/list/spread_out_spawns = GLOB.servant_spawns.Copy() + for(var/datum/mind/servant in assigned) + var/mob/S = servant.current + if(!spread_out_spawns.len) + spread_out_spawns = GLOB.servant_spawns.Copy() + log_game("[key_name(servant)] was made an initial servant of Ratvar") + var/turf/T = pick_n_take(spread_out_spawns) + S.forceMove(T) + greet_servant(S) + equip_servant(S) + add_servant_of_ratvar(S, TRUE) + var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar //that's a mouthful + G.final_countdown(ark_time) + return TRUE + +/datum/dynamic_ruleset/roundstart/clockcult/proc/greet_servant(mob/M) //Description of their role + if(!M) + return 0 + to_chat(M, "You are a servant of Ratvar, the Clockwork Justiciar!") + to_chat(M, "You have approximately [ark_time] minutes until the Ark activates.") + to_chat(M, "Unlock Script scripture by converting a new servant.") + to_chat(M, "Application scripture will be unlocked halfway until the Ark's activation.") + M.playsound_local(get_turf(M), 'sound/ambience/antag/clockcultalr.ogg', 100, FALSE, pressure_affected = FALSE) + return 1 + +/datum/dynamic_ruleset/roundstart/clockcult/proc/equip_servant(mob/living/M) //Grants a clockwork slab to the mob, with one of each component + if(!M || !ishuman(M)) + return FALSE + var/mob/living/carbon/human/L = M + L.equipOutfit(/datum/outfit/servant_of_ratvar) + var/obj/item/clockwork/slab/S = new + var/slot = "At your feet" + var/list/slots = list("In your left pocket" = SLOT_L_STORE, "In your right pocket" = SLOT_R_STORE, "In your backpack" = SLOT_IN_BACKPACK, "On your belt" = SLOT_BELT) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + slot = H.equip_in_one_of_slots(S, slots) + if(slot == "In your backpack") + slot = "In your [H.back.name]" + if(slot == "At your feet") + if(!S.forceMove(get_turf(L))) + qdel(S) + if(S && !QDELETED(S)) + to_chat(L, "There is a paper in your backpack! It'll tell you if anything's changed, as well as what to expect.") + to_chat(L, "[slot] is a clockwork slab, a multipurpose tool used to construct machines and invoke ancient words of power. If this is your first time \ + as a servant, you can find a concise tutorial in the Recollection category of its interface.") + to_chat(L, "If you want more information, you can read the wiki page to learn more.") + return TRUE + return FALSE + +/datum/dynamic_ruleset/roundstart/clockcult/round_result() + if(GLOB.clockwork_gateway_activated) + SSticker.news_report = CLOCK_SUMMON + SSticker.mode_result = "win - servants completed their objective (summon ratvar)" + else + SSticker.news_report = CULT_FAILURE + SSticker.mode_result = "loss - servants failed their objective (summon ratvar)" + +////////////////////////////////////////////// +// // +// CLOWN OPS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/nuclear/clown_ops + name = "Clown Ops" + antag_datum = /datum/antagonist/nukeop/clownop + antag_leader_datum = /datum/antagonist/nukeop/leader/clownop + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + +/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute() + . = ..() + if(.) + for(var/obj/machinery/nuclearbomb/syndicate/S in GLOB.nuke_list) + var/turf/T = get_turf(S) + if(T) + qdel(S) + new /obj/machinery/nuclearbomb/syndicate/bananium(T) + for(var/datum/mind/V in assigned) + V.assigned_role = "Clown Operative" + V.special_role = "Clown Operative" + +////////////////////////////////////////////// +// // +// DEVIL // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/devil + name = "Devil" + antag_flag = ROLE_DEVIL + antag_datum = /datum/antagonist/devil + restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI") + required_candidates = 1 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/devil_limit = 4 // Hard limit on devils if scaling is turned off + +/datum/dynamic_ruleset/roundstart/devil/pre_execute() + var/tsc = CONFIG_GET(number/traitor_scaling_coeff) + var/num_devils = 1 + + if(tsc) + num_devils = max(required_candidates, min(round(mode.roundstart_pop_ready / (tsc * 3)) + 2, round(mode.roundstart_pop_ready / (tsc * 1.5)))) + else + num_devils = max(required_candidates, min(mode.roundstart_pop_ready, devil_limit)) + + for(var/j = 0, j < num_devils, j++) + if (!candidates.len) + break + var/mob/devil = pick_n_take(candidates) + assigned += devil.mind + devil.mind.special_role = ROLE_DEVIL + devil.mind.restricted_roles = restricted_roles + + log_game("[key_name(devil)] has been selected as a devil") + return TRUE + +/datum/dynamic_ruleset/roundstart/devil/execute() + for(var/datum/mind/devil in assigned) + add_devil(devil.current, ascendable = TRUE) + add_devil_objectives(devil,2) + return TRUE + +/datum/dynamic_ruleset/roundstart/devil/proc/add_devil_objectives(datum/mind/devil_mind, quantity) + var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target) + var/datum/antagonist/devil/D = devil_mind.has_antag_datum(/datum/antagonist/devil) + for(var/i = 1 to quantity) + var/type = pick(validtypes) + var/datum/objective/devil/objective = new type(null) + objective.owner = devil_mind + D.objectives += objective + if(!istype(objective, /datum/objective/devil/buy_target)) + validtypes -= type + else + objective.find_target() + +////////////////////////////////////////////// +// // +// MONKEY // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/monkey + name = "Monkey" + antag_flag = ROLE_MONKEY + antag_datum = /datum/antagonist/monkey/leader + restricted_roles = list("Cyborg", "AI") + required_candidates = 1 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/players_per_carrier = 30 + var/monkeys_to_win = 1 + var/escaped_monkeys = 0 + var/datum/team/monkey/monkey_team + +/datum/dynamic_ruleset/roundstart/monkey/pre_execute() + var/carriers_to_make = max(round(mode.roundstart_pop_ready / players_per_carrier, 1), 1) + + for(var/j = 0, j < carriers_to_make, j++) + if (!candidates.len) + break + var/mob/carrier = pick_n_take(candidates) + assigned += carrier.mind + carrier.mind.special_role = "Monkey Leader" + carrier.mind.restricted_roles = restricted_roles + log_game("[key_name(carrier)] has been selected as a Jungle Fever carrier") + return TRUE + +/datum/dynamic_ruleset/roundstart/monkey/execute() + for(var/datum/mind/carrier in assigned) + var/datum/antagonist/monkey/M = add_monkey_leader(carrier) + if(M) + monkey_team = M.monkey_team + return TRUE + +/datum/dynamic_ruleset/roundstart/monkey/proc/check_monkey_victory() + if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME) + return FALSE + var/datum/disease/D = new /datum/disease/transformation/jungle_fever() + for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list) + if (M.HasDisease(D)) + if(M.onCentCom() || M.onSyndieBase()) + escaped_monkeys++ + if(escaped_monkeys >= monkeys_to_win) + return TRUE + else + return FALSE + +// This does not get called. Look into making it work. +/datum/dynamic_ruleset/roundstart/monkey/round_result() + if(check_monkey_victory()) + SSticker.mode_result = "win - monkey win" + else + SSticker.mode_result = "loss - staff stopped the monkeys" + +////////////////////////////////////////////// +// // +// METEOR // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/meteor + name = "Meteor" + persistent = TRUE + required_candidates = 0 + weight = 3 + cost = 0 + requirements = list(101,101,101,101,101,101,101,101,101,101) + high_population_requirement = 101 + var/meteordelay = 2000 + var/nometeors = 0 + var/rampupdelta = 5 + +/datum/dynamic_ruleset/roundstart/meteor/rule_process() + if(nometeors || meteordelay > world.time - SSticker.round_start_time) + return + + var/list/wavetype = GLOB.meteors_normal + var/meteorminutes = (world.time - SSticker.round_start_time - meteordelay) / 10 / 60 + + if (prob(meteorminutes)) + wavetype = GLOB.meteors_threatening + + if (prob(meteorminutes/2)) + wavetype = GLOB.meteors_catastrophic + + var/ramp_up_final = clamp(round(meteorminutes/rampupdelta), 1, 10) + + spawn_meteors(ramp_up_final, wavetype) diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md new file mode 100644 index 000000000000..ca62ff14136f --- /dev/null +++ b/code/game/gamemodes/dynamic/readme.md @@ -0,0 +1,67 @@ +# DYNAMIC + +## ROUNDSTART + +Dynamic rolls threat based on a special sauce formula: +"dynamic_curve_width \* tan((3.1416 \* (rand() - 0.5) \* 57.2957795)) + dynamic_curve_centre" + +Latejoin and midround injection cooldowns are set using exponential distribution between +5 minutes and 25 for latejoin +15 minutes and 35 for midround +this value is then added to world.time and assigned to the injection cooldown variables. + +rigged_roundstart() is called instead if there are forced rules (an admin set the mode) + +can_start() -> pre_setup() -> roundstart() OR rigged_roundstart() -> picking_roundstart_rule(drafted_rules) -> post_setup() + +## PROCESS + +Calls rule_process on every rule which is in the current_rules list. +Every sixty seconds, update_playercounts() +Midround injection time is checked against world.time to see if an injection should happen. +If midround injection time is lower than world.time, it updates playercounts again, then tries to inject and generates a new cooldown regardless of whether a rule is picked. + +## LATEJOIN + +make_antag_chance(newPlayer) -> [For each latespawn rule...] +-> acceptable(living players, threat_level) -> trim_candidates() -> ready(forced=FALSE) +**If true, add to drafted rules +**NOTE that acceptable uses threat_level not threat! +**NOTE Latejoin timer is ONLY reset if at least one rule was drafted. +**NOTE the new_player.dm AttemptLateSpawn() calls OnPostSetup for all roles (unless assigned role is MODE) + +[After collecting all draftble rules...] +-> picking_latejoin_ruleset(drafted_rules) -> spend threat -> ruleset.execute() + +## MIDROUND + +process() -> [For each midround rule...] +-> acceptable(living players, threat_level) -> trim_candidates() -> ready(forced=FALSE) +[After collecting all draftble rules...] +-> picking_midround_ruleset(drafted_rules) -> spend threat -> ruleset.execute() + +## FORCED + +For latejoin, it simply sets forced_latejoin_rule +make_antag_chance(newPlayer) -> trim_candidates() -> ready(forced=TRUE) **NOTE no acceptable() call + +For midround, calls the below proc with forced = TRUE +picking_specific_rule(ruletype,forced) -> forced OR acceptable(living_players, threat_level) -> trim_candidates() -> ready(forced) -> spend threat -> execute() +**NOTE specific rule can be called by RS traitor->MR autotraitor w/ forced=FALSE +**NOTE that due to short circuiting acceptable() need not be called if forced. + +## RULESET + +acceptable(population,threat) just checks if enough threat_level for population indice. +**NOTE that we currently only send threat_level as the second arg, not threat. +ready(forced) checks if enough candidates and calls the map's map_ruleset(dynamic_ruleset) at the parent level + +trim_candidates() varies significantly according to the ruleset type +Roundstart: All candidates are new_player mobs. Check them for standard stuff: connected, desire role, not banned, etc. +**NOTE Roundstart deals with both candidates (trimmed list of valid players) and mode.candidates (everyone readied up). Don't confuse them! +Latejoin: Only one candidate, the latejoiner. Standard checks. +Midround: Instead of building a single list candidates, candidates contains four lists: living, dead, observing, and living antags. Standard checks in trim_list(list). + +Midround - Rulesets have additional types +/from_ghosts: execute() -> send_applications() -> review_applications() -> finish_setup(mob/newcharacter, index) -> setup_role(role) +**NOTE: execute() here adds dead players and observers to candidates list From 0c819034e901391a61cc29036e85491531e759ed Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:01:29 -0500 Subject: [PATCH 03/68] Adds Dynamic loading to yogstation.dme --- yogstation.dme | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yogstation.dme b/yogstation.dme index 7f3d0979e1c4..962d9a29fcc2 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -609,6 +609,11 @@ #include "code\game\gamemodes\devil\game_mode.dm" #include "code\game\gamemodes\devil\objectives.dm" #include "code\game\gamemodes\devil\devil agent\devil_agent.dm" +#include "code\game\gamemodes\dynamic\dynamic.dm" +#include "code\game\gamemodes\dynamic\dynamic_rulesets.dm" +#include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm" +#include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm" +#include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\hivemind\hivemind.dm" #include "code\game\gamemodes\hivemind\objectives.dm" From 5dea2fc0bf79e6884d932df9c34fb750f11d4662 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:05:46 -0500 Subject: [PATCH 04/68] Probably adds dynamic related changes. --- code/__HELPERS/roundend.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index f1adbf3c07fb..94b08e13643e 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -638,4 +638,3 @@ var/datum/DBQuery/Q = SSdbcore.New("UPDATE [format_table_name("misc")] SET value = '[SSshuttle.points]' WHERE key = 'cargorecord'") Q.Execute() qdel(Q) - From 05c0882cb34f78ff48f90568bc35bb4a1f64ee38 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:15:23 -0500 Subject: [PATCH 05/68] Fixes code I didn't add, won't fix my retardation. --- code/__HELPERS/roundend.dm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 94b08e13643e..2a75af856602 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -301,8 +301,18 @@ //ignore this comment, it fixes the broken sytax parsing caused by the " above else parts += "[GLOB.TAB]Nobody died this shift!" + if(istype(SSticker.mode, /datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + parts += "[FOURSPACES]Threat level: [mode.threat_level]" + parts += "[FOURSPACES]Threat left: [mode.threat]" //yes + parts += "[FOURSPACES]Executed rules:" + for(var/datum/dynamic_ruleset/rule in mode.executed_rules) + parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost] threat" return parts.Join("
") + return parts.Join("
") + + /client/proc/roundend_report_file() return "data/roundend_reports/[ckey].html" @@ -638,3 +648,4 @@ var/datum/DBQuery/Q = SSdbcore.New("UPDATE [format_table_name("misc")] SET value = '[SSshuttle.points]' WHERE key = 'cargorecord'") Q.Execute() qdel(Q) + From e5aae0ceb616c036e0ee4c8ee2c506545330bbab Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:19:17 -0500 Subject: [PATCH 06/68] Adds "/datum/game_mode/proc/admin_panel()" because it's apparently important. --- code/game/gamemodes/game_mode.dm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 489180e37dda..c237c8286d69 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -51,9 +51,15 @@ var/gamemode_ready = FALSE //Is the gamemode all set up and ready to start checking for ending conditions. var/setup_error //What stopepd setting up the mode. +/// Associative list of current players, in order: living players, living antagonists, dead players and observers. + //var/list/list/current_players = list(CURRENT_LIVING_PLAYERS = list(), CURRENT_LIVING_ANTAGS = list(), CURRENT_DEAD_PLAYERS = list(), CURRENT_OBSERVERS = list()) + /datum/game_mode/proc/announce() //Shows the gamemode's name and a fast description. to_chat(world, "The gamemode is: [name]!") to_chat(world, "[announce_text]") + +/datum/game_mode/proc/admin_panel() + return ///Checks to see if the game can be setup and ran with the current number of players or whatnot. @@ -583,3 +589,7 @@ SSticker.news_report = STATION_EVACUATED if(SSshuttle.emergency.is_hijacked()) SSticker.news_report = SHUTTLE_HIJACK + + +/datum/game_mode/proc/AdminPanelEntry() + return From de2085792304bda8b2535395c54434959137aa95 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:20:28 -0500 Subject: [PATCH 07/68] Adds dynamic related code. --- code/modules/admin/admin.dm | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 3215037fd54f..cd732f0701c3 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -418,6 +418,25 @@ "} if(GLOB.master_mode == "secret") dat += "(Force Secret Mode)
" + + if(GLOB.master_mode == "dynamic") + if(SSticker.current_state <= GAME_STATE_PREGAME) + dat += "(Force Roundstart Rulesets)
" + if (GLOB.dynamic_forced_roundstart_ruleset.len > 0) + for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset) + dat += {"-> [rule.name] <-
"} + dat += "(Clear Rulesets)
" + dat += "(Dynamic mode options)
" + else if (SSticker.IsRoundInProgress()) + dat += "(Force Next Latejoin Ruleset)
" + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + if (mode.forced_latejoin_rule) + dat += {"-> [mode.forced_latejoin_rule.name] <-
"} + dat += "(Execute Midround Ruleset!)
" + dat += "
" + if(SSticker.IsRoundInProgress()) + dat += "(Game Mode Panel)
" dat += {"
@@ -504,6 +523,43 @@ log_admin("Announce: [key_name(usr)] : [message]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + var/dat = {" +

Dynamic Mode Options


+
+

Common options

+ All these options can be changed midround.
+
+ Force extended: - Option is [GLOB.dynamic_forced_extended ? "ON" : "OFF"]. +
This will force the round to be extended. No rulesets will be drafted.
+
+ No stacking: - Option is [GLOB.dynamic_no_stacking ? "ON" : "OFF"]. +
Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted.
+
+ Classic secret mode: - Option is [GLOB.dynamic_classic_secret ? "ON" : "OFF"]. +
Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn.
+
+
+ Forced threat level: Current value : [GLOB.dynamic_forced_threat_level]. +
The value threat is set to if it is higher than -1.
+
+ High population limit: Current value : [GLOB.dynamic_high_pop_limit]. +
The threshold at which "high population override" will be in effect.
+
+ Stacking threeshold: Current value : [GLOB.dynamic_stacking_limit]. +
The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens.
+

Advanced parameters

+ Curve centre: -> [GLOB.dynamic_curve_centre] <-
+ Curve width: -> [GLOB.dynamic_curve_width] <-
+ Latejoin injection delay:
+ Minimum: -> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <- Minutes
+ Midround injection delay:
+ Minimum: -> [GLOB.dynamic_midround_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_midround_delay_max / 60 / 10] <- Minutes
+ "} + + usr << browse(dat, "window=dyn_mode_options;size=900x650") + /datum/admins/proc/set_admin_notice() set category = "Special Verbs" set name = "Set Admin Notice" @@ -826,6 +882,44 @@ browser.set_content(dat.Join()) browser.open() +/datum/admins/proc/dynamic_mode_options(mob/user) + var/dat = {" +

Dynamic Mode Options


+
+

Common options

+ All these options can be changed midround.
+
+ Force extended: - Option is [GLOB.dynamic_forced_extended ? "ON" : "OFF"]. +
This will force the round to be extended. No rulesets will be drafted.
+
+ No stacking: - Option is [GLOB.dynamic_no_stacking ? "ON" : "OFF"]. +
Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted.
+
+ Classic secret mode: - Option is [GLOB.dynamic_classic_secret ? "ON" : "OFF"]. +
Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn.
+
+
+ Forced threat level: Current value : [GLOB.dynamic_forced_threat_level]. +
The value threat is set to if it is higher than -1.
+
+ High population limit: Current value : [GLOB.dynamic_high_pop_limit]. +
The threshold at which "high population override" will be in effect.
+
+ Stacking threeshold: Current value : [GLOB.dynamic_stacking_limit]. +
The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens.
+

Advanced parameters

+ Curve centre: -> [GLOB.dynamic_curve_centre] <-
+ Curve width: -> [GLOB.dynamic_curve_width] <-
+ Latejoin injection delay:
+ Minimum: -> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <- Minutes
+ Midround injection delay:
+ Minimum: -> [GLOB.dynamic_midround_delay_min / 60 / 10] <- Minutes
+ Maximum: -> [GLOB.dynamic_midround_delay_max / 60 / 10] <- Minutes
+ "} + + user << browse(dat, "window=dyn_mode_options;size=900x650") + /datum/admins/proc/create_or_modify_area() set category = "Debug" set name = "Create or modify area" From 9f58dd0fb05e6cd8e4a89743b1fd9120936cfe1d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:21:58 -0500 Subject: [PATCH 08/68] Adds more dynamic related code. --- code/modules/admin/topic.dm | 577 ++++++++++++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 99e1b3bae1a5..2211736acd4b 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -236,6 +236,296 @@ else if(href_list["editrights"]) edit_rights_topic(href_list) + else if(href_list["gamemode_panel"]) + if(!check_rights(R_ADMIN)) + return + SSticker.mode.admin_panel() + + else if(href_list["f_dynamic_roundstart"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null) + var/roundstart_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) + var/datum/dynamic_ruleset/roundstart/newrule = new rule() + roundstart_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules + if (added_rule) + GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") + message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1) + Game() + + else if(href_list["f_dynamic_roundstart_clear"]) + if(!check_rights(R_ADMIN)) + return + GLOB.dynamic_forced_roundstart_ruleset = list() + Game() + log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.") + message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1) + + else if(href_list["f_dynamic_roundstart_remove"]) + if(!check_rights(R_ADMIN)) + return + var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"]) + GLOB.dynamic_forced_roundstart_ruleset -= rule + Game() + log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.") + message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1) + + else if(href_list["f_dynamic_latejoin"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/latejoin_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin)) + var/datum/dynamic_ruleset/latejoin/newrule = new rule() + latejoin_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = latejoin_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.") + message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1) + Game() + + else if(href_list["f_dynamic_latejoin_clear"]) + if(!check_rights(R_ADMIN)) + return + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = null + Game() + log_admin("[key_name(usr)] cleared the forced latejoin ruleset.") + message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1) + + else if(href_list["f_dynamic_midround"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/midround_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/midround)) + var/datum/dynamic_ruleset/midround/newrule = new rule() + midround_rules[newrule.name] = rule + var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + log_admin("[key_name(usr)] executed the [added_rule] ruleset.") + message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1) + mode.picking_specific_rule(midround_rules[added_rule],1) + + else if (href_list["f_dynamic_options"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_centre"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num + if (new_centre < -5 || new_centre > 5) + return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") + message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) + GLOB.dynamic_curve_centre = new_centre + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_width"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num + if (new_width < 0.5 || new_width > 4) + return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") + message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) + GLOB.dynamic_curve_width = new_width + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_latejoin_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_latejoin_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_midround_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_midround_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_force_extended"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_no_stacking"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_classic_secret"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_stacking_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_high_pop_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num + if (new_value < 0) + return alert(usr, "Only positive values allowed!", null, null, null, null) + GLOB.dynamic_high_pop_limit = new_value + + log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_forced_threat"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num + if (new_value > 100) + return alert(usr, "The value must be be under 100.", null, null, null, null) + GLOB.dynamic_forced_threat_level = new_value + + log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + dynamic_mode_options(usr) + else if(href_list["call_shuttle"]) if(!check_rights(R_ADMIN)) return @@ -631,6 +921,293 @@ else if(href_list["f_secret"]) return HandleFSecret() + + else if(href_list["f_dynamic_roundstart"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null) + var/roundstart_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) + var/datum/dynamic_ruleset/roundstart/newrule = new rule() + roundstart_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules + if (added_rule) + GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") + message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1) + Game() + + else if(href_list["f_dynamic_roundstart_clear"]) + if(!check_rights(R_ADMIN)) + return + GLOB.dynamic_forced_roundstart_ruleset = list() + Game() + log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.") + message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1) + + else if(href_list["f_dynamic_roundstart_remove"]) + if(!check_rights(R_ADMIN)) + return + var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"]) + GLOB.dynamic_forced_roundstart_ruleset -= rule + Game() + log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.") + message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1) + + else if(href_list["f_dynamic_latejoin"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/latejoin_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin)) + var/datum/dynamic_ruleset/latejoin/newrule = new rule() + latejoin_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = latejoin_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.") + message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1) + Game() + + else if(href_list["f_dynamic_latejoin_clear"]) + if(!check_rights(R_ADMIN)) + return + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = null + Game() + log_admin("[key_name(usr)] cleared the forced latejoin ruleset.") + message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1) + + else if(href_list["f_dynamic_midround"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/midround_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/midround)) + var/datum/dynamic_ruleset/midround/newrule = new rule() + midround_rules[newrule.name] = rule + var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + log_admin("[key_name(usr)] executed the [added_rule] ruleset.") + message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1) + mode.picking_specific_rule(midround_rules[added_rule],1) + + else if (href_list["f_dynamic_options"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_centre"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num + if (new_centre < -5 || new_centre > 5) + return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") + message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) + GLOB.dynamic_curve_centre = new_centre + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_width"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num + if (new_width < 0.5 || new_width > 4) + return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") + message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) + GLOB.dynamic_curve_width = new_width + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_latejoin_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_latejoin_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_midround_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_midround_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_force_extended"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_no_stacking"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_classic_secret"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_stacking_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_high_pop_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num + if (new_value < 0) + return alert(usr, "Only positive values allowed!", null, null, null, null) + GLOB.dynamic_high_pop_limit = new_value + + log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_forced_threat"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num + if (new_value > 100) + return alert(usr, "The value must be be under 100.", null, null, null, null) + GLOB.dynamic_forced_threat_level = new_value + + log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + dynamic_mode_options(usr) + + else if(href_list["c_mode2"]) if(!check_rights(R_ADMIN|R_SERVER)) From 2e2268c36a4d3fa59a3dfdf8a4cec38407a90d40 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 01:58:50 -0500 Subject: [PATCH 09/68] Fixes error: possible unreachable code here on line 313 --- code/__HELPERS/roundend.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 2a75af856602..fb6568519ab8 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -310,9 +310,6 @@ parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost] threat" return parts.Join("
") - return parts.Join("
") - - /client/proc/roundend_report_file() return "data/roundend_reports/[ckey].html" From 7ba5e8d6394b756e1dbcf56bf1dfe131bffc761e Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 02:00:39 -0500 Subject: [PATCH 10/68] Fixes copy and paste error. code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm, line 390, column 2: error: possible unreachable code here --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index d7ec30eef7c5..fddbe579438a 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -387,12 +387,6 @@ finished = 2 return - if(check_rev_victory()) - finished = 1 - else if(check_heads_victory()) - finished = 2 - return - /datum/dynamic_ruleset/roundstart/revs/check_finished() if(CONFIG_GET(keyed_list/continuous)["revolution"]) if(finished) From 51d8a454bfd54c8fabd60f456d8254fcac71022d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 02:07:54 -0500 Subject: [PATCH 11/68] Adds a testing change to fix the lint --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 276d49ebf0aa..4a243306ea52 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -48,7 +48,7 @@ candidates.Remove(M) continue if (M.mind) - if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? + if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? trimmed_list.Remove(M) continue if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded? From 2732704c4390e1a6990c334f0c77948b0e26c0b9 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 02:11:08 -0500 Subject: [PATCH 12/68] Undos paranthesis --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 4a243306ea52..276d49ebf0aa 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -48,7 +48,7 @@ candidates.Remove(M) continue if (M.mind) - if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? + if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? trimmed_list.Remove(M) continue if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded? From 9af97c6f0cd2f9c343420bf47fc0a730fb32a4c6 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 5 May 2020 02:59:42 -0500 Subject: [PATCH 13/68] LintFixTry#2 --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 276d49ebf0aa..600b18def97f 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -48,7 +48,7 @@ candidates.Remove(M) continue if (M.mind) - if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role? + if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role? trimmed_list.Remove(M) continue if (M.mind.assigned_role in restricted_roles || HAS_TRAIT(M, TRAIT_MINDSHIELD)) // Does their job allow it or are they mindshielded? From d08066f337f162bff727f074efd9350e3503bea5 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 13 May 2020 21:10:45 -0500 Subject: [PATCH 14/68] Adds Shadowling --- .../dynamic/dynamic_rulesets_roundstart.dm | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index fddbe579438a..5201f6561978 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -723,3 +723,26 @@ var/ramp_up_final = clamp(round(meteorminutes/rampupdelta), 1, 10) spawn_meteors(ramp_up_final, wavetype) + +////////////////////////////////////////////// +// // +// SHADOWLINGS // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/shadowling + name = "Shadowling" + antag_flag = ROLE_SHADOWLING + antag_datum = datum/antagonist/shadowling + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") + restricted_roles = list("Cyborg", "AI") + required_candidates = 2 + weight = 3 + cost = 30 + requirements = list(90,90,90,80,60,40,30,20,10,10) + high_population_requirement = 10 + flags = HIGHLANDER_RULESET + minimum_players = 30 + +//Xoxeyos Here, I've added this Shadowling shit in, I have no idea what I'm doing, if there were mistakes made +//feel free to make changes, if it crashes, or just doesn't give anyone roles. From 64abf5721288a791414bf23f7b88d1fd9d3c8cba Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 13 May 2020 21:18:46 -0500 Subject: [PATCH 15/68] adds a slash --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 5201f6561978..caaabfb9d348 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -733,7 +733,7 @@ /datum/dynamic_ruleset/roundstart/shadowling name = "Shadowling" antag_flag = ROLE_SHADOWLING - antag_datum = datum/antagonist/shadowling + antag_datum = /datum/antagonist/shadowling protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("Cyborg", "AI") required_candidates = 2 From 3b23b3fd6d28e4c231a3579fcdb9b4f63ce398cd Mon Sep 17 00:00:00 2001 From: Theos Date: Thu, 14 May 2020 02:44:34 -0400 Subject: [PATCH 16/68] adds some logging --- code/game/gamemodes/dynamic/dynamic.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index c67286e5a245..e9e49e278559 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -142,15 +142,19 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) log_admin("[key_name(usr)] tried to use the game mode panel without authorization.") return if (href_list["forced_extended"]) + log_admin("[key_name(usr)] has toggled forced extended.") GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended else if (href_list["no_stacking"]) + log_admin("[key_name(usr)] has toggled stacking.") GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking else if (href_list["classic_secret"]) + log_admin("[key_name(usr)] has toggled classic secret.") GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret else if (href_list["adjustthreat"]) var/threatadd = input("Specify how much threat to add (negative to subtract). This can inflate the threat level.", "Adjust Threat", 0) as null|num if(!threatadd) return + log_admin("[key_name(usr)] has changed the threat by [threatadd].") if(threatadd > 0) create_threat(threatadd) else From a06fcf6d2150561308ff490bc8384d40b5163f17 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Thu, 14 May 2020 02:12:47 -0500 Subject: [PATCH 17/68] Adds IMMERSION Thank you Theos for the help! --- code/game/gamemodes/dynamic/dynamic.dm | 49 +++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index e9e49e278559..84a92d595f25 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -188,31 +188,48 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) /datum/game_mode/dynamic/send_intercept() . = "Central Command Status Summary
" + var/title = "You are Vader's puppet." + // communications title for threat management + var/desc = "REACH OUT WITH THE FORCE BOY, AND TEAR THAT STAR DESTROYER FROM THE SKY!" + // description for threat management switch(round(threat_level)) if(0 to 19) update_playercounts() if(!current_players[CURRENT_LIVING_ANTAGS].len) - . += "Peaceful Waypoint
" - . += "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." + title = "Peaceful Waypoint" + . += "[title]
" + desc = "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." + . += desc else - . += "Core Territory
" - . += "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." + title = "Core Territory" + . += "[title]
" + desc = "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." + . += desc if(20 to 39) - . += "Anomalous Exogeology
" - . += "Although your station lies within what is generally considered Nanotrasen-controlled space, the course of its orbit has caused it to cross unusually close to exogeological features with anomalous readings. Although these features offer opportunities for our research department, it is known that these little understood readings are often correlated with increased activity from competing interstellar organizations and individuals, among them the Wizard Federation and Cult of the Geometer of Blood - all known competitors for Anomaly Type B sites. Exercise elevated caution." + title = "Anomalous Exogeology" + . += "[title]
" + desc = "Although your station lies within what is generally considered Nanotrasen-controlled space, the course of its orbit has caused it to cross unusually close to exogeological features with anomalous readings. Although these features offer opportunities for our research department, it is known that these little understood readings are often correlated with increased activity from competing interstellar organizations and individuals, among them the Wizard Federation and Cult of the Geometer of Blood - all known competitors for Anomaly Type B sites. Exercise elevated caution." + . += desc if(40 to 65) - . += "Contested System
" - . += "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." + title = "Contested System" + . += "[title]
" + desc = "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." + . += desc if(66 to 79) - . += "Uncharted Space
" - . += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets." + title = "Uncharted Space" + . += "[title]
" + desc = "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets." + . += desc if(80 to 99) - . += "Black Orbit
" - . += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece." + title = "Black Orbit" + . += "[title]
" + desc = "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece." + . += desc if(100) - . += "Impending Doom
" - . += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.
" - . += "Good luck." + title = "Impending Doom" + . += "[title]
" + desc = "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station. Good luck." + . += desc if(station_goals.len) . += "
Special Orders for [station_name()]:" @@ -221,7 +238,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) . += G.get_report() print_command_report(., "Central Command Status Summary", announce=FALSE) - priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", 'sound/ai/intercept.ogg') + priority_announce(desc, title, 'sound/ai/intercept.ogg') if(GLOB.security_level < SEC_LEVEL_BLUE) set_security_level(SEC_LEVEL_BLUE) From bd16eeaae81059b4ee5026d81a8845572ec4c746 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Thu, 14 May 2020 02:22:07 -0500 Subject: [PATCH 18/68] FIXES IMMERSION --- code/game/gamemodes/dynamic/dynamic.dm | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 84a92d595f25..90493332a432 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -197,39 +197,29 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) update_playercounts() if(!current_players[CURRENT_LIVING_ANTAGS].len) title = "Peaceful Waypoint" - . += "[title]
" desc = "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." - . += desc else title = "Core Territory" - . += "[title]
" desc = "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." - . += desc if(20 to 39) title = "Anomalous Exogeology" - . += "[title]
" desc = "Although your station lies within what is generally considered Nanotrasen-controlled space, the course of its orbit has caused it to cross unusually close to exogeological features with anomalous readings. Although these features offer opportunities for our research department, it is known that these little understood readings are often correlated with increased activity from competing interstellar organizations and individuals, among them the Wizard Federation and Cult of the Geometer of Blood - all known competitors for Anomaly Type B sites. Exercise elevated caution." - . += desc if(40 to 65) title = "Contested System" - . += "[title]
" desc = "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." - . += desc if(66 to 79) title = "Uncharted Space" - . += "[title]
" desc = "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets." - . += desc if(80 to 99) title = "Black Orbit" - . += "[title]
" desc = "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece." - . += desc if(100) title = "Impending Doom" - . += "[title]
" desc = "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station. Good luck." - . += desc + + + . += "[title]
" + . += desc if(station_goals.len) . += "
Special Orders for [station_name()]:" From 4ddeb49f2d5a5bb276d6331fd7d0a16ed0ae556c Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:06:54 -0500 Subject: [PATCH 19/68] Adds Mindshield Implants for Dynamic --- code/game/objects/items/storage/uplink_kits.dm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 93f4f3e0e032..1a3fec04617a 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -420,6 +420,12 @@ /obj/item/storage/box/syndie_kit/imp_radio/PopulateContents() new /obj/item/implanter/radio/syndicate(src) + +/obj/item/storage/box/syndie_kit/imp_mindshield + name = "mindshield implant box" + +/obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() + new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space name = "boxed space suit and helmet" From c70dd8ea21cde2cfb9c16c9cd826366eb28542ed Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:08:35 -0500 Subject: [PATCH 20/68] Adds the mindshield implant into dynamic --- code/modules/uplink/uplink_items.dm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 1704154459ce..8fdaa8048c30 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1586,6 +1586,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) surplus = 0 restricted = TRUE +/datum/uplink_item/implants/mindshield + name = "Syndicate Brainwash Denial Implant" + desc = "An implant injected into the body, to deny brainwashing attempts." + item = /obj/item/storage/box/syndie_kit/mindshield/tot + cost = 2 + /datum/uplink_item/implants/xray name = "X-ray Vision Implant" desc = "These cybernetic eyes will give you X-ray vision. Comes with an autosurgeon." @@ -1915,4 +1921,4 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "Strong flavor, dense smoke, infused with omnizine." item = /obj/item/storage/box/fancy/cigarettes/cigpack_syndicate cost = 2 - illegal_tech = FALSE \ No newline at end of file + illegal_tech = FALSE From 574275c83f719762d62676628a6f18479ca01a63 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:10:29 -0500 Subject: [PATCH 21/68] Adds the mindshield stuff yadayadayda --- .../items/implants/implant_mindshieldtot.dm | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 code/game/objects/items/implants/implant_mindshieldtot.dm diff --git a/code/game/objects/items/implants/implant_mindshieldtot.dm b/code/game/objects/items/implants/implant_mindshieldtot.dm new file mode 100644 index 000000000000..f0af2a0b3cf4 --- /dev/null +++ b/code/game/objects/items/implants/implant_mindshieldtot.dm @@ -0,0 +1,102 @@ +/obj/item/implant/mindshield/tot + name = "mindshield implant" + desc = "Protects against brainwashing." + activated = 0 + +/obj/item/implant/mindshield/tot/get_data() + var/dat = {"Implant Specifications:
+ Name: Syndicate Brainwash Denial Implant
+ Life: Thirty Days.
+ Important Notes: Agents injected with this device are much more resistant to brainwashing.
+
+ Implant Details:
+ Function: Contains a small pod of nanobots that protects the host's mental functions from manipulation.
+ Special Features: Will prevent and cure most forms of brainwashing.
+ Integrity: Implant will last so long as the nanobots are inside the bloodstream."} + return dat + + +/obj/item/implant/mindshield/tot/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE) + if(..()) + if(!target.mind) + ADD_TRAIT(target, TRAIT_MINDSHIELD, "implant") + target.sec_hud_set_implants() + return FALSE + + if(target.mind.has_antag_datum(/datum/antagonist/brainwashed)) + target.mind.remove_antag_datum(/datum/antagonist/brainwashed) + + var/datum/antagonist/hivemind/host = target.mind.has_antag_datum(/datum/antagonist/hivemind) //Releases the target from mind control beforehand + if(host) + var/datum/mind/M = host.owner + if(M) + var/obj/effect/proc_holder/spell/target_hive/hive_control/the_spell = locate(/obj/effect/proc_holder/spell/target_hive/hive_control) in M.spell_list + if(the_spell && the_spell.active) + the_spell.release_control() + + if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.has_antag_datum(/datum/antagonist/hivemind) || target.mind.unconvertable) + if(!silent) + target.visible_message("[target] seems to resist the implant!", "You feel something interfering with your mental conditioning, but you resist it!") + removed(target, 1) + qdel(src) + return FALSE + + var/datum/antagonist/hivevessel/woke = target.is_wokevessel() + if(is_hivemember(target)) + for(var/datum/antagonist/hivemind/hive in GLOB.antagonists) + if(hive.hivemembers.Find(target)) + var/mob/living/carbon/C = hive.owner.current.get_real_hivehost() + if(C) + C.apply_status_effect(STATUS_EFFECT_HIVE_TRACKER, target, woke?TRACKER_AWAKENED_TIME:TRACKER_MINDSHIELD_TIME) + target.apply_status_effect(STATUS_EFFECT_HIVE_TRACKER, C, TRACKER_DEFAULT_TIME) + if(C.mind) //If you were using mind control, too bad + C.apply_status_effect(STATUS_EFFECT_HIVE_RADAR) + to_chat(C, "We detect a surge of psionic energy from a far away vessel before they disappear from the hive. Whatever happened, there's a good chance they're after us now.") + to_chat(target, "You hear supernatural wailing echo throughout your mind as you are finally set free. Deep down, you can feel the lingering presence of those who enslaved you... as can they!") + target.apply_status_effect(STATUS_EFFECT_HIVE_RADAR) + remove_hivemember(target) + + if(woke) + woke.one_mind.remove_member(target.mind) + target.mind.remove_antag_datum(/datum/antagonist/hivevessel) + + var/datum/antagonist/rev/rev = target.mind.has_antag_datum(/datum/antagonist/rev) + if(rev) + rev.remove_revolutionary(FALSE, user) + if(target.mind.has_antag_datum(/datum/antagonist/gang/boss)) + if(!silent) + target.visible_message("[target] seems to resist the implant!", "You feel something interfering with your mental conditioning, but you resist it!") + return FALSE + if(target.mind.has_antag_datum(/datum/antagonist/gang)) + target.mind.remove_antag_datum(/datum/antagonist/gang) + if(target.mind.has_antag_datum(/datum/antagonist/veil)) + target.mind.remove_antag_datum(/datum/antagonist/veil) + if(!silent) + if(target.mind in SSticker.mode.cult) + to_chat(target, "You feel something interfering with your mental conditioning, but you resist it!") + else + to_chat(target, "You feel a sense of peace and security. You are now protected from brainwashing.") + ADD_TRAIT(target, TRAIT_MINDSHIELD, "implant") + target.sec_hud_set_implants() + return TRUE + return FALSE + +/obj/item/implant/mindshield/tot/removed(mob/target, silent = FALSE, special = 0) + if(..()) + if(isliving(target)) + var/mob/living/L = target + REMOVE_TRAIT(L, TRAIT_MINDSHIELD, "implant") + L.sec_hud_set_implants() + if(target.stat != DEAD && !silent) + to_chat(target, "Your mind suddenly feels terribly vulnerable. You are no longer safe from brainwashing.") + return 1 + return 0 + +/obj/item/implanter/mindshield/tot + name = "implanter (mindshield)" + imp_type = /obj/item/implant/mindshield/tot + +/obj/item/implantcase/mindshield/tot + name = "implant case - 'Mindshield'" + desc = "A glass case containing a mindshield implant." + imp_type = /obj/item/implant/mindshield/tot From f77c9f369b1cd08f4a2f63abedafea25b6e377ee Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:16:18 -0500 Subject: [PATCH 22/68] adds another slash --- code/game/objects/items/storage/uplink_kits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 1a3fec04617a..bc7354edbe1c 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -425,7 +425,7 @@ name = "mindshield implant box" /obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() - new /obj/item/implanter/mindshield/tot(src) + new /obj/item/implanter/mindshield/tot/(src) /obj/item/storage/box/syndie_kit/space name = "boxed space suit and helmet" From bf9b2acae77977f8877ccfe5b3480ff77a9f773b Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:23:13 -0500 Subject: [PATCH 23/68] removes said slash --- code/game/objects/items/storage/uplink_kits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index bc7354edbe1c..1a3fec04617a 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -425,7 +425,7 @@ name = "mindshield implant box" /obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() - new /obj/item/implanter/mindshield/tot/(src) + new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space name = "boxed space suit and helmet" From ef02015df7259ccfd7f706224a3b8d53ee6e8e0a Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:50:25 -0500 Subject: [PATCH 24/68] test addes imp_ before mindshield on line 427 --- code/game/objects/items/storage/uplink_kits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 1a3fec04617a..16b5dc9ec10b 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -424,7 +424,7 @@ /obj/item/storage/box/syndie_kit/imp_mindshield name = "mindshield implant box" -/obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() +/obj/item/storage/box/syndie_kit/imp_mindshield/tot/PopulateContents() new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space From e9bbb09e139be50d73901a5da0ad2e527deffdd1 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 05:57:45 -0500 Subject: [PATCH 25/68] redo --- code/game/objects/items/storage/uplink_kits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 16b5dc9ec10b..3f684a17af01 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -424,7 +424,7 @@ /obj/item/storage/box/syndie_kit/imp_mindshield name = "mindshield implant box" -/obj/item/storage/box/syndie_kit/imp_mindshield/tot/PopulateContents() +/obj/item/storage/box/syndie_kit/mindshield/totPopulateContents() new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space From 187504fcaf04d86ac2b9c36629c97a4419719d3e Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 06:01:31 -0500 Subject: [PATCH 26/68] test --- code/game/objects/items/storage/uplink_kits.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 3f684a17af01..2f361260f311 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -421,10 +421,10 @@ /obj/item/storage/box/syndie_kit/imp_radio/PopulateContents() new /obj/item/implanter/radio/syndicate(src) -/obj/item/storage/box/syndie_kit/imp_mindshield +/obj/item/storage/box/syndie_kit/mindshield/tot name = "mindshield implant box" -/obj/item/storage/box/syndie_kit/mindshield/totPopulateContents() +/obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space From 9de54ee5cad583e25b2bd7d5fa484c893a0c8370 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 15:53:59 -0500 Subject: [PATCH 27/68] Going to try and do this again #1 --- code/game/objects/items/storage/uplink_kits.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 2f361260f311..d33bc4d1b90c 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -421,10 +421,10 @@ /obj/item/storage/box/syndie_kit/imp_radio/PopulateContents() new /obj/item/implanter/radio/syndicate(src) -/obj/item/storage/box/syndie_kit/mindshield/tot +/obj/item/storage/box/syndie_kit/imp_mindshield name = "mindshield implant box" -/obj/item/storage/box/syndie_kit/mindshield/tot/PopulateContents() +/obj/item/storage/box/syndie_kit/imp_mindshield/PopulateContents() new /obj/item/implanter/mindshield/tot(src) /obj/item/storage/box/syndie_kit/space From fec6a55e16aaf1c71c7c019ce25013fc0dbe4a95 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 15:57:52 -0500 Subject: [PATCH 28/68] syndie_kit/mindshield/ --- code/modules/uplink/uplink_items.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 8fdaa8048c30..31cfb6a4e127 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1589,7 +1589,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/implants/mindshield name = "Syndicate Brainwash Denial Implant" desc = "An implant injected into the body, to deny brainwashing attempts." - item = /obj/item/storage/box/syndie_kit/mindshield/tot + item = /obj/item/storage/box/syndie_kit/mindshield/ cost = 2 /datum/uplink_item/implants/xray From 94088cd54abdeb4421fa9645d5022462e6ac7702 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:05:13 -0500 Subject: [PATCH 29/68] gets rid of an error --- code/modules/uplink/uplink_items.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 31cfb6a4e127..8fdaa8048c30 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1589,7 +1589,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/implants/mindshield name = "Syndicate Brainwash Denial Implant" desc = "An implant injected into the body, to deny brainwashing attempts." - item = /obj/item/storage/box/syndie_kit/mindshield/ + item = /obj/item/storage/box/syndie_kit/mindshield/tot cost = 2 /datum/uplink_item/implants/xray From 8d083b40ee9a7b7d4f8552a936ca26ee58ad241d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:12:47 -0500 Subject: [PATCH 30/68] renames to imp_mindshield --- code/modules/uplink/uplink_items.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 8fdaa8048c30..c5081fca55f8 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1589,7 +1589,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/implants/mindshield name = "Syndicate Brainwash Denial Implant" desc = "An implant injected into the body, to deny brainwashing attempts." - item = /obj/item/storage/box/syndie_kit/mindshield/tot + item = /obj/item/storage/box/syndie_kit/imp_mindshield cost = 2 /datum/uplink_item/implants/xray From 3d2d0cb2392704bf2988215d44b67b288b2439e8 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:24:46 -0500 Subject: [PATCH 31/68] Maybe I'm just super retarded. --- code/game/objects/items/implants/implant_mindshieldtot.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/implants/implant_mindshieldtot.dm b/code/game/objects/items/implants/implant_mindshieldtot.dm index f0af2a0b3cf4..afcbf9dd5f6a 100644 --- a/code/game/objects/items/implants/implant_mindshieldtot.dm +++ b/code/game/objects/items/implants/implant_mindshieldtot.dm @@ -92,11 +92,11 @@ return 1 return 0 -/obj/item/implanter/mindshield/tot +/obj/item/implanter/mindshield/ name = "implanter (mindshield)" imp_type = /obj/item/implant/mindshield/tot -/obj/item/implantcase/mindshield/tot +/obj/item/implantcase/mindshield/ name = "implant case - 'Mindshield'" desc = "A glass case containing a mindshield implant." imp_type = /obj/item/implant/mindshield/tot From 2da7fa7f0e32a191525d3d9a2ff4e7525bfd4882 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:31:02 -0500 Subject: [PATCH 32/68] v --- code/game/objects/items/implants/implant_mindshieldtot.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/implants/implant_mindshieldtot.dm b/code/game/objects/items/implants/implant_mindshieldtot.dm index afcbf9dd5f6a..b444f21673f0 100644 --- a/code/game/objects/items/implants/implant_mindshieldtot.dm +++ b/code/game/objects/items/implants/implant_mindshieldtot.dm @@ -92,11 +92,11 @@ return 1 return 0 -/obj/item/implanter/mindshield/ +/obj/item/implanter/mindshield/tot/ name = "implanter (mindshield)" imp_type = /obj/item/implant/mindshield/tot -/obj/item/implantcase/mindshield/ +/obj/item/implantcase/mindshield/tot/ name = "implant case - 'Mindshield'" desc = "A glass case containing a mindshield implant." imp_type = /obj/item/implant/mindshield/tot From ee8225222e4ca01ca2e7ee11e80dafa69c6981ed Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:40:28 -0500 Subject: [PATCH 33/68] #include "code\game\objects\items\implants\implant_mindshieldtot.dm" DOES THIS FIX IT DOES THIS FIX IT --- yogstation.dme | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yogstation.dme b/yogstation.dme index 6d62bdd75129..96ba485348b1 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -991,6 +991,7 @@ #include "code\game\objects\items\implants\implanter.dm" #include "code\game\objects\items\implants\implantpad.dm" #include "code\game\objects\items\implants\implantuplink.dm" +#include "code\game\objects\items\implants\implant_mindshieldtot.dm" #include "code\game\objects\items\melee\energy.dm" #include "code\game\objects\items\melee\misc.dm" #include "code\game\objects\items\melee\transforming.dm" @@ -2766,7 +2767,7 @@ #include "code\modules\station_goals\station_goal.dm" #include "code\modules\surgery\amputation.dm" #include "code\modules\surgery\brain_surgery.dm" -#include "code\modules\surgery\cavity_implant.dm" +#include "code\modules\surgery\cavity_.dm" #include "code\modules\surgery\core_removal.dm" #include "code\modules\surgery\coronary_bypass.dm" #include "code\modules\surgery\dental_implant.dm" From 29fd811e2245667aa4d938e999d25bb17943a60e Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:43:33 -0500 Subject: [PATCH 34/68] oops --- yogstation.dme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yogstation.dme b/yogstation.dme index 96ba485348b1..7160915db6e8 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -2767,7 +2767,7 @@ #include "code\modules\station_goals\station_goal.dm" #include "code\modules\surgery\amputation.dm" #include "code\modules\surgery\brain_surgery.dm" -#include "code\modules\surgery\cavity_.dm" +#include "code\modules\surgery\cavity_implant.dm" #include "code\modules\surgery\core_removal.dm" #include "code\modules\surgery\coronary_bypass.dm" #include "code\modules\surgery\dental_implant.dm" From 7dc5515b72276039dbfc56ff273bef1a6aadb1cc Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 16 May 2020 16:51:41 -0500 Subject: [PATCH 35/68] recompile --- code/game/objects/items/implants/implant_mindshieldtot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/implants/implant_mindshieldtot.dm b/code/game/objects/items/implants/implant_mindshieldtot.dm index b444f21673f0..75a121d7d37f 100644 --- a/code/game/objects/items/implants/implant_mindshieldtot.dm +++ b/code/game/objects/items/implants/implant_mindshieldtot.dm @@ -95,7 +95,7 @@ /obj/item/implanter/mindshield/tot/ name = "implanter (mindshield)" imp_type = /obj/item/implant/mindshield/tot - + /obj/item/implantcase/mindshield/tot/ name = "implant case - 'Mindshield'" desc = "A glass case containing a mindshield implant." From 6e4729b5d38a7df529ee22e04c37c0c11770e5c7 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Mon, 18 May 2020 00:42:05 -0500 Subject: [PATCH 36/68] Part of getting rid of weights, relies on dice rolls --- code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index c7b92a997f80..b2220fccefaa 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -62,7 +62,7 @@ protected_roles = list("Security Officer", "Warden", "Head of Personnel", "Detective", "Head of Security", "Captain") restricted_roles = list("AI","Cyborg") required_candidates = 1 - weight = 7 + weight = 1 cost = 5 requirements = list(40,30,20,10,10,10,10,10,10,10) high_population_requirement = 10 @@ -84,7 +84,7 @@ enemy_roles = list("AI", "Cyborg", "Security Officer","Detective","Head of Security", "Captain", "Warden") required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 - weight = 2 + weight = 1 cost = 20 requirements = list(101,101,70,40,30,20,20,20,20,20) high_population_requirement = 50 From 55de676348c3274ac5431d87e5884243857577f7 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Mon, 18 May 2020 00:43:48 -0500 Subject: [PATCH 37/68] Adds dice roll, adds the ability for clock cult to roll --- .../dynamic/dynamic_rulesets_roundstart.dm | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index caaabfb9d348..a524fec02cf1 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -14,7 +14,7 @@ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("Cyborg") required_candidates = 1 - weight = 5 + weight = 1 cost = 10 requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 @@ -52,7 +52,7 @@ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("Cyborg", "AI") required_candidates = 2 - weight = 4 + weight = 1 cost = 10 requirements = list(40,30,30,20,20,15,15,15,10,10) high_population_requirement = 15 @@ -103,7 +103,7 @@ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("AI", "Cyborg") required_candidates = 1 - weight = 3 + weight = 1 cost = 30 requirements = list(80,70,60,50,40,20,20,10,10,10) high_population_requirement = 10 @@ -151,7 +151,7 @@ minimum_required_age = 14 restricted_roles = list("Head of Security", "Captain") // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 1 - weight = 2 + weight = 1 cost = 30 requirements = list(90,90,70,40,30,20,10,10,10,10) high_population_requirement = 10 @@ -195,7 +195,7 @@ minimum_required_age = 14 restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel") required_candidates = 2 - weight = 3 + weight = 1 cost = 30 requirements = list(100,90,80,60,40,30,10,10,10,10) high_population_requirement = 10 @@ -253,7 +253,7 @@ minimum_required_age = 14 restricted_roles = list("Head of Security", "Captain") // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted required_candidates = 5 - weight = 3 + weight = 1 cost = 40 requirements = list(90,90,90,80,60,40,30,20,10,10) high_population_requirement = 10 @@ -341,7 +341,7 @@ minimum_required_age = 14 restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director") required_candidates = 3 - weight = 2 + weight = 1 delay = 7 MINUTES cost = 35 requirements = list(101,101,70,40,30,20,10,10,10,10) @@ -433,7 +433,7 @@ antag_datum = null restricted_roles = list() required_candidates = 0 - weight = 3 + weight = 1 cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 @@ -456,9 +456,9 @@ antag_datum = /datum/antagonist/clockcult restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel") required_candidates = 4 - weight = 3 + weight = 1 cost = 0 - requirements = list(101,101,101,101,101,101,101,101,101,101) + requirements = list(100,90,80,60,60,30,10,10,10,10) high_population_requirement = 101 flags = HIGHLANDER_RULESET var/ark_time @@ -583,7 +583,7 @@ antag_datum = /datum/antagonist/devil restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI") required_candidates = 1 - weight = 3 + weight = 1 cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 @@ -640,7 +640,7 @@ antag_datum = /datum/antagonist/monkey/leader restricted_roles = list("Cyborg", "AI") required_candidates = 1 - weight = 3 + weight = 1 cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 @@ -737,7 +737,7 @@ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("Cyborg", "AI") required_candidates = 2 - weight = 3 + weight = 1 cost = 30 requirements = list(90,90,90,80,60,40,30,20,10,10) high_population_requirement = 10 From 630a7c2f3178e30c17d7b865bb7da80e2a0a5ca1 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Mon, 18 May 2020 00:45:26 -0500 Subject: [PATCH 38/68] Readjusts weight and costs, no more weight --- .../dynamic/dynamic_rulesets_midround.dm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 600b18def97f..d93ea5640fe7 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -171,7 +171,7 @@ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") restricted_roles = list("Cyborg", "AI", "Positronic Brain") required_candidates = 1 - weight = 7 + weight = 1 cost = 10 requirements = list(50,40,30,20,10,10,10,10,10,10) repeatable = TRUE @@ -227,7 +227,7 @@ exclusive_roles = list("AI") required_enemies = list(4,4,4,4,4,4,2,2,2,0) required_candidates = 1 - weight = 3 + weight = 1 cost = 35 requirements = list(101,101,80,70,60,60,50,50,40,40) high_population_requirement = 35 @@ -309,7 +309,7 @@ enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain") required_enemies = list(3,3,3,3,3,2,1,1,0,0) required_candidates = 5 - weight = 5 + weight = 1 cost = 35 requirements = list(90,90,90,80,60,40,30,20,10,10) high_population_requirement = 10 @@ -352,8 +352,8 @@ enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 - weight = 4 - cost = 10 + weight = 1 + cost = 30 requirements = list(101,101,101,80,60,50,30,20,10,10) high_population_requirement = 50 repeatable = TRUE @@ -375,8 +375,8 @@ enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 - weight = 3 - cost = 10 + weight = 1 + cost = 20 requirements = list(101,101,101,70,50,40,20,15,10,10) high_population_requirement = 50 repeatable = TRUE @@ -422,8 +422,8 @@ enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") required_enemies = list(2,2,1,1,1,1,1,0,0,0) required_candidates = 1 - weight = 3 - cost = 10 + weight = 1 + cost = 5 requirements = list(101,101,101,70,50,40,20,15,10,10) high_population_requirement = 50 repeatable = TRUE From 4dbf28eec9fa07545cdb2ea512f72f875ae66677 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Mon, 18 May 2020 00:54:17 -0500 Subject: [PATCH 39/68] ahem fuck you travis or whatever the hell your name is - recompile --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index d93ea5640fe7..337371a8dfe8 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -1,4 +1,4 @@ -////////////////////////////////////////////// +////////////////////////////////////////////// // // // MIDROUND RULESETS // // // From 9d46761230adb8e2bb06e01f53807a23e82092b5 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 19 May 2020 16:41:18 -0500 Subject: [PATCH 40/68] Oh god oh fuck clock cult cost 0 for a while, oh god oh fuck meteors are now in, only coming in at threat 100 and higher. Shadowling requirement shifted, --- .../dynamic/dynamic_rulesets_roundstart.dm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index a524fec02cf1..1b977bca401b 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -457,8 +457,8 @@ restricted_roles = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel") required_candidates = 4 weight = 1 - cost = 0 - requirements = list(100,90,80,60,60,30,10,10,10,10) + cost = 40 + requirements = list(100,90,80,70,60,50,30,20,10,10) high_population_requirement = 101 flags = HIGHLANDER_RULESET var/ark_time @@ -581,10 +581,10 @@ name = "Devil" antag_flag = ROLE_DEVIL antag_datum = /datum/antagonist/devil - restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI") + restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI", "Cyborg") required_candidates = 1 weight = 1 - cost = 0 + cost = 60 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 var/devil_limit = 4 // Hard limit on devils if scaling is turned off @@ -699,10 +699,10 @@ name = "Meteor" persistent = TRUE required_candidates = 0 - weight = 3 - cost = 0 - requirements = list(101,101,101,101,101,101,101,101,101,101) - high_population_requirement = 101 + weight = 1 + cost = 90 + requirements = list(100,100,100,100,100,100,100,100,100,100) + high_population_requirement = 100 var/meteordelay = 2000 var/nometeors = 0 var/rampupdelta = 5 @@ -739,7 +739,7 @@ required_candidates = 2 weight = 1 cost = 30 - requirements = list(90,90,90,80,60,40,30,20,10,10) + requirements = list(90,80,80,70,60,40,30,30,20,10) high_population_requirement = 10 flags = HIGHLANDER_RULESET minimum_players = 30 From 743e50a22c236555072c8b13c5ed16c43845658e Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 19 May 2020 16:43:06 -0500 Subject: [PATCH 41/68] recompile --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 1b977bca401b..826509950c30 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -1,5 +1,5 @@ -////////////////////////////////////////////// +////////////////////////////////////////////// // // // SYNDICATE TRAITORS // // // From 2ab746fcf9449c1e747f9a0f934627a2f783cf6a Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 19 May 2020 17:15:14 -0500 Subject: [PATCH 42/68] Rev Fix --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 826509950c30..88a807f3bd83 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -344,7 +344,7 @@ weight = 1 delay = 7 MINUTES cost = 35 - requirements = list(101,101,70,40,30,20,10,10,10,10) + requirements = list(70,70,60,40,30,20,10,10,10,10) high_population_requirement = 10 flags = HIGHLANDER_RULESET // I give up, just there should be enough heads with 35 players... From 7a5e451d77d6c09c13897e6be5fcd2334fe3bbbe Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 19 May 2020 17:24:14 -0500 Subject: [PATCH 43/68] Allows more midrounds to play --- .../gamemodes/dynamic/dynamic_rulesets_midround.dm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 337371a8dfe8..5b41e44f8707 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -229,7 +229,7 @@ required_candidates = 1 weight = 1 cost = 35 - requirements = list(101,101,80,70,60,60,50,50,40,40) + requirements = list(100,100,80,70,60,60,50,50,45,40) high_population_requirement = 35 required_type = /mob/living/silicon/ai var/ion_announce = 33 @@ -307,7 +307,7 @@ antag_flag = ROLE_OPERATIVE antag_datum = /datum/antagonist/nukeop enemy_roles = list("AI", "Cyborg", "Security Officer", "Warden","Detective","Head of Security", "Captain") - required_enemies = list(3,3,3,3,3,2,1,1,0,0) + required_enemies = list(3,3,3,3,2,2,1,1,0,0) required_candidates = 5 weight = 1 cost = 35 @@ -354,7 +354,7 @@ required_candidates = 1 weight = 1 cost = 30 - requirements = list(101,101,101,80,60,50,30,20,10,10) + requirements = list(100,100,100,80,60,50,45,30,20,20) high_population_requirement = 50 repeatable = TRUE @@ -377,7 +377,7 @@ required_candidates = 1 weight = 1 cost = 20 - requirements = list(101,101,101,70,50,40,20,15,10,10) + requirements = list(100,100,100,70,50,40,30,25,20,10) high_population_requirement = 50 repeatable = TRUE var/list/vents = list() @@ -420,11 +420,11 @@ antag_flag = "Nightmare" antag_flag_override = ROLE_ALIEN enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain") - required_enemies = list(2,2,1,1,1,1,1,0,0,0) + required_enemies = list(2,2,1,1,1,1,0,0,0,0) required_candidates = 1 weight = 1 cost = 5 - requirements = list(101,101,101,70,50,40,20,15,10,10) + requirements = list(90,85,80,70,50,40,30,25,20,10) high_population_requirement = 50 repeatable = TRUE var/list/spawn_locs = list() From 6a97e02835846165786a62b160b7361b3cbee2c2 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 19 May 2020 17:28:35 -0500 Subject: [PATCH 44/68] Clockwork additions and whatnot --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 88a807f3bd83..e44f28e94111 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -458,8 +458,8 @@ required_candidates = 4 weight = 1 cost = 40 - requirements = list(100,90,80,70,60,50,30,20,10,10) - high_population_requirement = 101 + requirements = list(100,90,80,70,60,50,30,30,30,30) + high_population_requirement = 50 flags = HIGHLANDER_RULESET var/ark_time From fdf099d412aff0e93dc5e8d2cd5e6e332984ac28 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Fri, 22 May 2020 23:24:36 -0500 Subject: [PATCH 45/68] Adds Vampire, and some changes to the requirements, and whatnot. --- .../dynamic/dynamic_rulesets_roundstart.dm | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index e44f28e94111..942278acac83 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -197,7 +197,7 @@ required_candidates = 2 weight = 1 cost = 30 - requirements = list(100,90,80,60,40,30,10,10,10,10) + requirements = list(100,80,70,60,40,30,30,20,10,10) high_population_requirement = 10 flags = HIGHLANDER_RULESET var/cultist_cap = list(2,2,2,3,3,4,4,4,4,4) @@ -461,6 +461,7 @@ requirements = list(100,90,80,70,60,50,30,30,30,30) high_population_requirement = 50 flags = HIGHLANDER_RULESET + minimum_players = 38 var/ark_time /datum/dynamic_ruleset/roundstart/clockcult/pre_execute() @@ -736,9 +737,9 @@ antag_datum = /datum/antagonist/shadowling protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") restricted_roles = list("Cyborg", "AI") - required_candidates = 2 + required_candidates = 3 weight = 1 - cost = 30 + cost = 35 requirements = list(90,80,80,70,60,40,30,30,20,10) high_population_requirement = 10 flags = HIGHLANDER_RULESET @@ -746,3 +747,22 @@ //Xoxeyos Here, I've added this Shadowling shit in, I have no idea what I'm doing, if there were mistakes made //feel free to make changes, if it crashes, or just doesn't give anyone roles. + +////////////////////////////////////////////// +// // +// VAMPIRE // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/vampire + name = "Vampire" + antag_flag = ROLE_VAMPIRE + antag_datum = /datum/antagonist/vampire + protected_jobs = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") + restricted_jobs = list("AI", "Cyborg") + required_candidates = 3 + weight = 1 + cost = 25 + requirements = list(80,70,60,50,50,45,30,30,20,25) + minimum_players = 30 + From fd98fda23f5f79031b8474266ca4e3fab8b25639 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Fri, 22 May 2020 23:34:19 -0500 Subject: [PATCH 46/68] Fixed Jobs to Roles --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 942278acac83..ebb5e68e4e6a 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -758,8 +758,8 @@ name = "Vampire" antag_flag = ROLE_VAMPIRE antag_datum = /datum/antagonist/vampire - protected_jobs = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") - restricted_jobs = list("AI", "Cyborg") + protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") + restricted_roles = list("AI", "Cyborg") required_candidates = 3 weight = 1 cost = 25 From 419600a0485bc8ec503745116f0e20b5b4abaf26 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Fri, 22 May 2020 23:46:13 -0500 Subject: [PATCH 47/68] Adds Vampire to Late Join --- .../dynamic/dynamic_rulesets_latejoin.dm | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index b2220fccefaa..7b29e3f62f54 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -16,7 +16,7 @@ if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE))) candidates.Remove(P) continue - else + else if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE))) candidates.Remove(P) continue @@ -86,7 +86,7 @@ required_candidates = 1 weight = 1 cost = 20 - requirements = list(101,101,70,40,30,20,20,20,20,20) + requirements = list(80,75,60,60,55,50,40,30,20,20) high_population_requirement = 50 flags = HIGHLANDER_RULESET var/required_heads = 3 @@ -113,3 +113,23 @@ new_head = M.mind.add_antag_datum(new_head) new_head.rev_team.max_headrevs = 1 // Only one revhead if it is latejoin. return TRUE + + +////////////////////////////////////////////// +// // +// VAMPIRE // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/vampire + name = "Vampiric Infiltrator" + antag_flag = ROLE_VAMPIRE + antag_datum = /datum/antagonist/vampire + protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") + restricted_roles = list("AI", "Cyborg") + required_candidates = 3 + weight = 1 + cost = 15 + requirements = list(80,70,60,50,50,45,30,30,20,25) + minimum_players = 30 + repeatable = TRUE From 242ceca2f2a224e3192bdd601058159abd3a6112 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 23 May 2020 20:01:41 -0500 Subject: [PATCH 48/68] Adds vampire cooldown to roundstart --- .../dynamic/dynamic_rulesets_roundstart.dm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index ebb5e68e4e6a..7a469f39166b 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -765,4 +765,23 @@ cost = 25 requirements = list(80,70,60,50,50,45,30,30,20,25) minimum_players = 30 + var/autovamp_cooldown = 450 // 15 minutes (ticks once per 2 sec) +/datum/dynamic_ruleset/roundstart/vampire/pre_execute() + var/traitor_scaling_coeff = 10 - max(0,round(mode.threat_level/10)-5) // Above 50 threat level, coeff goes down by 1 for every 10 levels + var/num_traitors = min(round(mode.candidates.len / traitor_scaling_coeff) + 1, candidates.len) + for (var/i = 1 to num_traitors) + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.special_role = ROLE_VAMPIRE + M.mind.restricted_roles = restricted_roles + return TRUE + +/datum/dynamic_ruleset/roundstart/vampire/rule_process() + if (autovamp_cooldown > 0) + autovamp_cooldown-- + else + autovamp_cooldown = 450 // 15 minutes + message_admins("Checking if we can turn someone into a vampire.") + log_game("DYNAMIC: Checking if we can turn someone into a vampire.") + mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autovamp) From c07129c7a06114c861754c41bdf9549016a6ebdf Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 23 May 2020 20:03:04 -0500 Subject: [PATCH 49/68] Adds Traitor_Ruleset to Vampire --- code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 7b29e3f62f54..1fcfa629bd47 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -133,3 +133,5 @@ requirements = list(80,70,60,50,50,45,30,30,20,25) minimum_players = 30 repeatable = TRUE + flags = TRAITOR_RULESET + From 090ca874ef7f3dbee9b7a7301b0ee7937e7efb82 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 23 May 2020 20:14:39 -0500 Subject: [PATCH 50/68] Adds Vamps to midround --- .../dynamic/dynamic_rulesets_midround.dm | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 5b41e44f8707..93daf141da58 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -454,3 +454,55 @@ message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Nightmare by the midround ruleset.") log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.") return S + +////////////////////////////////////////////// +// // +// VAMPIRE // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/autovamp + name = "Vampire" + antag_flag = ROLE_VAMPIRE + antag_datum = /datum/antagonist/vampire + protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") + restricted_roles = list("Cyborg", "AI") + required_candidates = 3 + weight = 1 + cost = 25 + requirements = list(80,70,60,50,50,45,30,30,20,25) + minimum_players = 30 + +/datum/dynamic_ruleset/midround/autovamp/acceptable(population = 0, threat = 0) + var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len + var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len + var/max_vamp = round(player_count / 10) + 1 + if ((antag_count < max_vamp) && prob(mode.threat_level))//adding vampire if the antag population is getting low + return ..() + else + return FALSE + +/datum/dynamic_ruleset/midround/autovamp/trim_candidates() + ..() + for(var/mob/living/player in living_players) + if(issilicon(player)) // Your assigned role doesn't change when you are turned into a silicon. + living_players -= player + continue + if(is_centcom_level(player.z)) + living_players -= player // We don't autotator people in CentCom + continue + if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) + living_players -= player // We don't autovamp people with roles already + +/datum/dynamic_ruleset/midround/autovamp/ready(forced = FALSE) + if (required_candidates > living_players.len) + return FALSE + return ..() + +/datum/dynamic_ruleset/midround/autovamp/execute() + var/mob/M = pick(living_players) + assigned += M + living_players -= M + var/datum/antagonist/vampire/newVampire = new + M.mind.add_antag_datum(newVampire) + return TRUE From 6a8810f980dec8cb502108e479e9cebbe8e4331f Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 23 May 2020 20:21:06 -0500 Subject: [PATCH 51/68] recompile --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 93daf141da58..e8286da0a6bb 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -455,7 +455,7 @@ log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.") return S -////////////////////////////////////////////// +////////////////////////////////////////////// // // // VAMPIRE // // // From 42e87b3d595b0a31799b4ab212e07e421df13f72 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 23 May 2020 23:56:41 -0500 Subject: [PATCH 52/68] Shadowling Modification --- .../dynamic/dynamic_rulesets_roundstart.dm | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 7a469f39166b..1394102acea3 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -1,5 +1,5 @@ -////////////////////////////////////////////// +////////////////////////////////////////////// // // // SYNDICATE TRAITORS // // // @@ -120,7 +120,7 @@ /datum/dynamic_ruleset/roundstart/changeling/execute() var/team_mode = FALSE - if(prob(team_mode_probability)) + if(prob(team_mode_probability)) team_mode = TRUE var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective) var/list/possible_team_objectives = list() @@ -167,13 +167,13 @@ /datum/dynamic_ruleset/roundstart/wizard/pre_execute() if(GLOB.wizardstart.len == 0) return FALSE - + var/mob/M = pick_n_take(candidates) if (M) assigned += M.mind M.mind.assigned_role = ROLE_WIZARD M.mind.special_role = ROLE_WIZARD - + return TRUE /datum/dynamic_ruleset/roundstart/wizard/execute() @@ -181,7 +181,7 @@ M.current.forceMove(pick(GLOB.wizardstart)) M.add_antag_datum(new antag_datum()) return TRUE - + ////////////////////////////////////////////// // // // BLOOD CULT // @@ -226,7 +226,7 @@ var/datum/antagonist/cult/new_cultist = new antag_datum() new_cultist.cult_team = main_cult new_cultist.give_equipment = TRUE - M.add_antag_datum(new_cultist) + M.add_antag_datum(new_cultist) main_cult.setup_objectives() return TRUE @@ -375,7 +375,7 @@ revolution.update_heads() SSshuttle.registerHostileEnvironment(src) return TRUE - + /datum/dynamic_ruleset/roundstart/revs/rule_process() /datum/dynamic_ruleset/roundstart/revs/rule_process() if(!revolution) @@ -590,7 +590,7 @@ high_population_requirement = 101 var/devil_limit = 4 // Hard limit on devils if scaling is turned off -/datum/dynamic_ruleset/roundstart/devil/pre_execute() +/datum/dynamic_ruleset/roundstart/devil/pre_execute() var/tsc = CONFIG_GET(number/traitor_scaling_coeff) var/num_devils = 1 @@ -701,7 +701,7 @@ persistent = TRUE required_candidates = 0 weight = 1 - cost = 90 + cost = 75 requirements = list(100,100,100,100,100,100,100,100,100,100) high_population_requirement = 100 var/meteordelay = 2000 @@ -744,7 +744,30 @@ high_population_requirement = 10 flags = HIGHLANDER_RULESET minimum_players = 30 + var/shadowling_cap = list(3,3,3,3,3,3,3,3,3,4) + var/datum/team/shadowling/shadowling + +/datum/dynamic_ruleset/roundstart/shadowling/pre_execute() + var/indice_pop = min(60,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + var/shadowlings = shadowling_cap[indice_pop] + for(var/shadowling_number = 1 to shadowlings) + if(candidates.len <= 0) + break + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.special_role = ROLE_SHADOWLING + M.mind.restricted_roles = restricted_roles + return TRUE +/datum/dynamic_ruleset/roundstart/shadowling/proc/check_shadow_death() + for(var/SM in get_antag_minds(/datum/antagonist/shadowling)) + var/datum/mind/shadow_mind = SM + if(istype(shadow_mind)) + var/turf/T = get_turf(shadow_mind.current) + if((shadow_mind) && (shadow_mind.current.stat != DEAD) && T && is_station_level(T.z) && ishuman(shadow_mind.current)) + return FALSE + return FALSE + //Xoxeyos Here, I've added this Shadowling shit in, I have no idea what I'm doing, if there were mistakes made //feel free to make changes, if it crashes, or just doesn't give anyone roles. @@ -759,7 +782,7 @@ antag_flag = ROLE_VAMPIRE antag_datum = /datum/antagonist/vampire protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") - restricted_roles = list("AI", "Cyborg") + restricted_roles = list("Cyborg", "AI") required_candidates = 3 weight = 1 cost = 25 From 16582ea6325a8eec1078521d3745d1da2c5f118d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sun, 24 May 2020 22:09:00 -0500 Subject: [PATCH 53/68] Revs are now under the flag TRAITOR_RULESET, shadowling change, attempts to keep the round from ending if all the shadowlings die. --- .../game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 1394102acea3..1a19a83d7706 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -346,7 +346,7 @@ cost = 35 requirements = list(70,70,60,40,30,20,10,10,10,10) high_population_requirement = 10 - flags = HIGHLANDER_RULESET + flags = TRAITOR_RULESET // I give up, just there should be enough heads with 35 players... minimum_players = 35 var/datum/team/revolution/revolution @@ -760,12 +760,6 @@ return TRUE /datum/dynamic_ruleset/roundstart/shadowling/proc/check_shadow_death() - for(var/SM in get_antag_minds(/datum/antagonist/shadowling)) - var/datum/mind/shadow_mind = SM - if(istype(shadow_mind)) - var/turf/T = get_turf(shadow_mind.current) - if((shadow_mind) && (shadow_mind.current.stat != DEAD) && T && is_station_level(T.z) && ishuman(shadow_mind.current)) - return FALSE return FALSE //Xoxeyos Here, I've added this Shadowling shit in, I have no idea what I'm doing, if there were mistakes made From e549d7a6474ef4f1e80d9bcef81c3dd8be923f30 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 02:53:40 -0500 Subject: [PATCH 54/68] Makes mindshield limited stock, now costs 1 tc, very small chance to show up in surplus crate, isn't illegal tech, player mininum is 30 --- code/modules/uplink/uplink_items.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index c5081fca55f8..986f72f2053d 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1590,7 +1590,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) name = "Syndicate Brainwash Denial Implant" desc = "An implant injected into the body, to deny brainwashing attempts." item = /obj/item/storage/box/syndie_kit/imp_mindshield - cost = 2 + limited_stock = 3 + player_minimum = 30 + illegal_tech = FALSE // This is a cheap knockoff of NT tech. + surplus = 5 + cost = 1 /datum/uplink_item/implants/xray name = "X-ray Vision Implant" From babebe469f8c669935e179acbe96c4d907e53331 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 04:37:36 -0500 Subject: [PATCH 55/68] Adds cargo crate discounts Thank you Jamie and Gamer for helping! --- code/game/gamemodes/dynamic/dynamic.dm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 90493332a432..362c549b95a7 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -107,7 +107,11 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) var/highlander_executed = FALSE /// If a only ruleset has been executed. var/only_ruleset_executed = FALSE - + /// CRATE DISCOUNT + var/discountedcrates = list( /datum/supply_pack/security/laser, + /datum/supply_pack/security/helmets, + /datum/supply_pack/security/vending/security) + /datum/game_mode/dynamic/admin_panel() var/list/dat = list("Game Mode Panel

Game Mode Panel

") dat += "Dynamic Mode \[VV\]\[Refresh\]
" @@ -232,6 +236,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(GLOB.security_level < SEC_LEVEL_BLUE) set_security_level(SEC_LEVEL_BLUE) + if(ISINRANGE(threat_level, 50, 80)) + for(var/pack in SSshuttle.supply_packs) + var/datum/supply_pack/P = SSshuttle.supply_packs[pack] + for(P in discountedcrates) + P.cost = P.cost /= 2 + // Yes, this is copy pasted from game_mode /datum/game_mode/dynamic/check_finished(force_ending) if(!SSticker.setup_done || !gamemode_ready) From fb3dec11ecfdae2f21ac6e4c64f8d54eb845fedc Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 04:43:26 -0500 Subject: [PATCH 56/68] Adds the party crate to discount --- code/game/gamemodes/dynamic/dynamic.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 362c549b95a7..bfe42540dca0 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -110,7 +110,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) /// CRATE DISCOUNT var/discountedcrates = list( /datum/supply_pack/security/laser, /datum/supply_pack/security/helmets, - /datum/supply_pack/security/vending/security) + /datum/supply_pack/security/vending/security, + /datum/supply_pack/service/party) /datum/game_mode/dynamic/admin_panel() var/list/dat = list("Game Mode Panel

Game Mode Panel

") From ebccce651ee76f75f28a5ef44aeec0e77098d2dc Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 15:45:06 -0500 Subject: [PATCH 57/68] Adds Zombies to the midround --- .../dynamic/dynamic_rulesets_midround.dm | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index e8286da0a6bb..227ea496530d 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -506,3 +506,52 @@ var/datum/antagonist/vampire/newVampire = new M.mind.add_antag_datum(newVampire) return TRUE + +////////////////////////////////////////////// +// // +// ZOMBIE (GHOST) // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/midround/from_ghosts/zombie + name = "Zombie" + enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain", "Chief Medical Officer") + required_enemies = list(2,2,1,1,1,1,0,0,0,0,) + required_candidates = 1 + weight = 1 + cost = 20 + requirements = list(90,85,80,75,70,65,60,55) + repeatable = TRUE + var/list/spawn_locs = list() + +/datum/round_event/ghost_role/zombie/spawn_role() + var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + if(!candidates.len) + return NOT_ENOUGH_PLAYERS + + var/mob/dead/selected = pick(candidates) + + var/datum/mind/player_mind = new /datum/mind(selected.key) + player_mind.active = TRUE + + var/list/spawn_locs = list() + for(var/X in GLOB.xeno_spawn) + var/turf/T = X + var/light_amount = T.get_lumcount() + if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) + spawn_locs += T + + if(!spawn_locs.len) + message_admins("No valid spawn locations found, aborting...") + return MAP_ERROR + + var/mob/living/carbon/human/S = new ((pick(spawn_locs))) + player_mind.transfer_to(S) + player_mind.assigned_role = "Zombie" + player_mind.special_role = "Zombie" + S.set_species(/datum/species/zombie/infectious) + playsound(S, 'sound/hallucinations/growl1.ogg', 50, 1, -1) + message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Zombie by an event.") + log_game("[key_name(S)] was spawned as a Zombie by an event.") + spawned_mobs += S + return SUCCESSFUL_SPAWN From 27b14451422eb249ada96093dbe24a7218d6fb82 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 15:53:21 -0500 Subject: [PATCH 58/68] Recompile From 7b0b176ad1357c2c2c81875f2620c6d3fe9f8070 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 16:03:28 -0500 Subject: [PATCH 59/68] FUCK YOU RECOMPILE ALREADY --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 227ea496530d..13323dce4b8f 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -507,7 +507,7 @@ M.mind.add_antag_datum(newVampire) return TRUE -////////////////////////////////////////////// +////////////////////////////////////////////// // // // ZOMBIE (GHOST) // // // @@ -555,3 +555,4 @@ log_game("[key_name(S)] was spawned as a Zombie by an event.") spawned_mobs += S return SUCCESSFUL_SPAWN + From 990a1065efc6a43e8c0fe44b3e7a1c327d85742e Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Tue, 26 May 2020 17:41:09 -0500 Subject: [PATCH 60/68] recompile --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 13323dce4b8f..b24cbea8dea0 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -507,7 +507,7 @@ M.mind.add_antag_datum(newVampire) return TRUE -////////////////////////////////////////////// +////////////////////////////////////////////// // // // ZOMBIE (GHOST) // // // From 4c4712aeba134191804df235bc9c6908cdf15337 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 27 May 2020 17:52:40 -0500 Subject: [PATCH 61/68] Jungle Fever can now roll. --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 1a19a83d7706..60d8ffb3dfd2 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -642,10 +642,9 @@ restricted_roles = list("Cyborg", "AI") required_candidates = 1 weight = 1 - cost = 0 - requirements = list(101,101,101,101,101,101,101,101,101,101) - high_population_requirement = 101 - var/players_per_carrier = 30 + cost = 70 + requirements = list(100,100,95,90,85,80,80,80,80,70) + var/players_per_carrier = 25 var/monkeys_to_win = 1 var/escaped_monkeys = 0 var/datum/team/monkey/monkey_team From d87f939e9510cae8090dbcf61a7a9555d5a664d7 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Thu, 28 May 2020 13:59:37 -0500 Subject: [PATCH 62/68] Only need one vampire candidate now. --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index b24cbea8dea0..d524e47ebe5e 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -467,7 +467,7 @@ antag_datum = /datum/antagonist/vampire protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") restricted_roles = list("Cyborg", "AI") - required_candidates = 3 + required_candidates = 1 weight = 1 cost = 25 requirements = list(80,70,60,50,50,45,30,30,20,25) From be9d8e9c43ff3d53a15fc367a712703288c4cd0f Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sat, 30 May 2020 21:22:01 -0500 Subject: [PATCH 63/68] Fix --- code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index d524e47ebe5e..b7d3cced66a4 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -470,7 +470,7 @@ required_candidates = 1 weight = 1 cost = 25 - requirements = list(80,70,60,50,50,45,30,30,20,25) + requirements = list(80,70,60,50,50,45,30,30,25,25) minimum_players = 30 /datum/dynamic_ruleset/midround/autovamp/acceptable(population = 0, threat = 0) From 50d06279dd8e7c6fe740e53b8945c99ee84e8712 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Sun, 31 May 2020 00:11:09 -0500 Subject: [PATCH 64/68] Ah shit it seems you're utterly fucked. --- .../dynamic/dynamic_rulesets_roundstart.dm | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 60d8ffb3dfd2..caf1c685191e 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -801,3 +801,95 @@ message_admins("Checking if we can turn someone into a vampire.") log_game("DYNAMIC: Checking if we can turn someone into a vampire.") mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autovamp) + +////////////////////////////////////////////// +// // +// RAGIN' MAGES // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/wizard/raging + name = "Ragin' Mages" + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 4 + weight = 1 + cost = 70 + requirements = list(100,95,90,80,75,75,70,60,60,55) + +/datum/dynamic_ruleset/roundstart/wizard/raging/acceptable(population=0, threat=0) + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/roundstart/wizard/raging/pre_execute() + if(GLOB.wizardstart.len == 0) + return FALSE + + var/mob/M = pick_n_take(candidates) + if (M) + assigned += M.mind + M.mind.assigned_role = ROLE_WIZARD + M.mind.special_role = ROLE_WIZARD + + return TRUE + +/datum/dynamic_ruleset/roundstart/wizard/raging/execute() + for(var/datum/mind/M in assigned) + M.current.forceMove(pick(GLOB.wizardstart)) + M.add_antag_datum(new antag_datum()) + return TRUE + +////////////////////////////////////////////// +// // +// BULLSHIT MAGES // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/wizard/raging/bullshit + name = "Bullshit Mages" + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + minimum_required_age = 14 + restricted_roles = list("Head of Security", "Captain") // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + required_candidates = 4 + weight = 1 + cost = 80 + minimum_players = 40 + requirements = list(100,100,100,100,95,95,90,80,85,75) + var/mage_cap = 999 + var/bullshit_mode = 1 + +/datum/dynamic_ruleset/roundstart/wizard/raging/bullshit/acceptable(population=0, threat=0) + if(GLOB.wizardstart.len == 0) + log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.") + return FALSE + return ..() + +/datum/dynamic_ruleset/roundstart/wizard/raging/bullshit/pre_execute() + var/indice_pop = min(45,round(mode.roundstart_pop_ready/2)+1) + var/mages = mage_cap[indice_pop] + for(var/mages_number = 1 to mages) + if(GLOB.wizardstart.len == 0) + return FALSE + + var/mob/M = pick_n_take(candidates) + if (M) + assigned += M.mind + M.mind.assigned_role = ROLE_WIZARD + M.mind.special_role = ROLE_WIZARD + log_admin("Shit is about to get wild. -Bullshit Wizards") + + return TRUE + +/datum/dynamic_ruleset/roundstart/wizard/raging/bullshit/execute() + for(var/datum/mind/M in assigned) + M.current.forceMove(pick(GLOB.wizardstart)) + M.add_antag_datum(new antag_datum()) + return TRUE + From f452ece35f2dfcfe5b61f25d6810b8857ef7f88d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:25:39 -0500 Subject: [PATCH 65/68] Update dynamic_rulesets_roundstart.dm If set to TRUE, dynamic mode will be able to draft this ruleset again later on. (doesn't apply for roundstart rules(?)) --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index caf1c685191e..fe26640443e6 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -146,6 +146,7 @@ // Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round. /datum/dynamic_ruleset/roundstart/wizard name = "Wizard" + persistent = TRUE antag_flag = ROLE_WIZARD antag_datum = /datum/antagonist/wizard minimum_required_age = 14 @@ -772,6 +773,7 @@ /datum/dynamic_ruleset/roundstart/vampire name = "Vampire" + persistent = TRUE antag_flag = ROLE_VAMPIRE antag_datum = /datum/antagonist/vampire protected_roles = list("Head of Security", "Captain", "Security Officer", "Chaplain", "Detective", "Warden", "Head of Personnel") From bcbc917245debe8a932458662a64c432d1527e2b Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:32:47 -0500 Subject: [PATCH 66/68] No Highpop req for shadowling --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index fe26640443e6..d6c66f6d2768 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -741,7 +741,6 @@ weight = 1 cost = 35 requirements = list(90,80,80,70,60,40,30,30,20,10) - high_population_requirement = 10 flags = HIGHLANDER_RULESET minimum_players = 30 var/shadowling_cap = list(3,3,3,3,3,3,3,3,3,4) From b2dcd363c8b3365c8af0073ccdc33132a33f3c5d Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Wed, 3 Jun 2020 23:33:27 -0500 Subject: [PATCH 67/68] Adds Darkspawn --- .../dynamic/dynamic_rulesets_roundstart.dm | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index d6c66f6d2768..b42f225bbb4b 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -780,7 +780,7 @@ required_candidates = 3 weight = 1 cost = 25 - requirements = list(80,70,60,50,50,45,30,30,20,25) + requirements = list(80,70,60,50,50,45,30,30,25,20) minimum_players = 30 var/autovamp_cooldown = 450 // 15 minutes (ticks once per 2 sec) @@ -894,3 +894,33 @@ M.add_antag_datum(new antag_datum()) return TRUE +////////////////////////////////////////////// +// // +// DARKSPAWN // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/darkspawn + name = "Darkspawn" + antag_flag = ROLE_DARKSPAWN + antag_datum = /datum/antagonist/darkspawn/ + minimum_required_age = 20 + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel") + restricted_roles = list("AI", "Cyborg") + required_candidates = 3 + weight = 1 + cost = 30 + var/darkspawn_cap = list(3,3,3,3,3,3,3,3,4,5) + requirements = list(80,75,70,65,50,30,30,30,25,20) + +/datum/dynamic_ruleset/roundstart/darkspawn/pre_execute() + var/indice_pop = min(30,round(mode.roundstart_pop_ready/pop_per_requirement)+1) + var/darkspawns = darkspawn_cap[indice_pop] + for(var/darkspawn_number = 1 to darkspawns) + if(candidates.len <= 0) + break + var/mob/M = pick_n_take(candidates) + assigned += M.mind + M.mind.special_role = ROLE_DARKSPAWN + M.mind.restricted_roles = restricted_roles + return TRUE From ba1db11f6ef38b29cc1f5f5d798220e5c2f28918 Mon Sep 17 00:00:00 2001 From: Xoxeyos <39686570+Xoxeyos@users.noreply.github.com> Date: Thu, 4 Jun 2020 14:45:49 -0500 Subject: [PATCH 68/68] Adds some logging. --- code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index b42f225bbb4b..ca41e790ea05 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -28,6 +28,7 @@ assigned += M.mind M.mind.special_role = ROLE_TRAITOR M.mind.restricted_roles = restricted_roles + log_game("[key_name(M)] has been selected as a Traitor") return TRUE /datum/dynamic_ruleset/roundstart/traitor/rule_process() @@ -756,6 +757,7 @@ assigned += M.mind M.mind.special_role = ROLE_SHADOWLING M.mind.restricted_roles = restricted_roles + log_game("[key_name(M)] has been selected as a Shadowling") return TRUE /datum/dynamic_ruleset/roundstart/shadowling/proc/check_shadow_death() @@ -923,4 +925,5 @@ assigned += M.mind M.mind.special_role = ROLE_DARKSPAWN M.mind.restricted_roles = restricted_roles + log_game("[key_name(M)] has been selected as a Darkspawn") return TRUE