var/global/antag_add_failed // Used in antag type voting.
var/global/list/additional_antag_types = list()
/datum/game_mode
var/name = "invalid"
var/round_description = "How did you even vote this in?"
var/extended_round_description = "This roundtype should not be spawned, let alone votable. Someone contact a developer and tell them the game's broken again."
var/config_tag = null
var/votable = 1
var/probability = 0
var/required_players = 0 // Minimum players for round to start if voted in.
var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret
var/required_enemies = 0 // Minimum antagonists for round to start.
var/newscaster_announcements = null
var/end_on_antag_death = 0 // Round will end when all antagonists are dead.
var/ert_disabled = 0 // ERT cannot be called.
var/deny_respawn = 0 // Disable respawn during this round.
var/shuttle_delay = 1 // Shuttle transit time is multiplied by this.
var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled?
var/list/antag_tags = list() // Core antag templates to spawn.
var/list/antag_templates // Extra antagonist types to include.
var/list/latejoin_templates = list()
var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists?
var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count.
var/require_all_templates = 0 // Will only start if all templates are checked and can spawn.
var/station_was_nuked = 0 // See nuclearbomb.dm and malfunction.dm.
var/explosion_in_progress = 0 // Sit back and relax
var/waittime_l = 600 // Lower bound on time before intercept arrives (in tenths of seconds)
var/waittime_h = 1800 // Upper bound on time before intercept arrives (in tenths of seconds)
var/event_delay_mod_moderate // Modifies the timing of random events.
var/event_delay_mod_major // As above.
/datum/game_mode/New()
..()
// Enforce some formatting.
// This will probably break something.
name = capitalize(lowertext(name))
config_tag = lowertext(config_tag)
/datum/game_mode/Topic(href, href_list[])
if(..())
return
if(href_list["toggle"])
switch(href_list["toggle"])
if("respawn")
deny_respawn = !deny_respawn
if("ert")
ert_disabled = !ert_disabled
announce_ert_disabled()
if("shuttle_recall")
auto_recall_shuttle = !auto_recall_shuttle
if("autotraitor")
round_autoantag = !round_autoantag
message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.")
else if(href_list["set"])
var/choice = ""
switch(href_list["set"])
if("shuttle_delay")
choice = input("Enter a new shuttle delay multiplier") as num
if(!choice || choice < 1 || choice > 20)
return
shuttle_delay = choice
if("antag_scaling")
choice = input("Enter a new antagonist cap scaling coefficient.") as num
if(isnull(choice) || choice < 0 || choice > 100)
return
antag_scaling_coeff = choice
if("event_modifier_moderate")
choice = input("Enter a new moderate event time modifier.") as num
if(isnull(choice) || choice < 0 || choice > 100)
return
event_delay_mod_moderate = choice
refresh_event_modifiers()
if("event_modifier_severe")
choice = input("Enter a new moderate event time modifier.") as num
if(isnull(choice) || choice < 0 || choice > 100)
return
event_delay_mod_major = choice
refresh_event_modifiers()
message_admins("Admin [key_name_admin(usr)] set game mode option '[href_list["set"]]' to [choice].")
else if(href_list["debug_antag"])
if(href_list["debug_antag"] == "self")
usr.client.debug_variables(src)
return
var/datum/antagonist/antag = all_antag_types[href_list["debug_antag"]]
if(antag)
usr.client.debug_variables(antag)
message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.")
else if(href_list["remove_antag_type"])
if(antag_tags && (href_list["remove_antag_type"] in antag_tags))
usr << "Cannot remove core mode antag type."
return
var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]]
if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types))
antag_templates -= antag
additional_antag_types -= antag.id
message_admins("Admin [key_name_admin(usr)] removed [antag.role_text] template from game mode.")
else if(href_list["add_antag_type"])
var/choice = input("Which type do you wish to add?") as null|anything in all_antag_types
if(!choice)
return
var/datum/antagonist/antag = all_antag_types[choice]
if(antag)
if(!islist(ticker.mode.antag_templates))
ticker.mode.antag_templates = list()
ticker.mode.antag_templates |= antag
message_admins("Admin [key_name_admin(usr)] added [antag.role_text] template to game mode.")
// I am very sure there's a better way to do this, but I'm not sure what it might be. ~Z
spawn(1)
for(var/datum/admins/admin in world)
if(usr.client == admin.owner)
admin.show_game_mode(usr)
return
/datum/game_mode/proc/announce() //to be called when round starts
world << "The current game mode is [capitalize(name)]!"
if(round_description) world << "[round_description]"
if(round_autoantag) world << "Antagonists will be added to the round automagically as needed."
if(antag_templates && antag_templates.len)
var/antag_summary = "Possible antagonist types: "
var/i = 1
for(var/datum/antagonist/antag in antag_templates)
if(i > 1)
if(i == antag_templates.len)
antag_summary += " and "
else
antag_summary += ", "
antag_summary += "[antag.role_text_plural]"
i++
antag_summary += "."
if(antag_templates.len > 1 && master_mode != "secret")
world << "[antag_summary]"
else
message_admins("[antag_summary]")
///can_start()
///Checks to see if the game can be setup and ran with the current number of players or whatnot.
/datum/game_mode/proc/can_start(var/do_not_spawn)
var/playerC = 0
for(var/mob/new_player/player in player_list)
if((player.client)&&(player.ready))
playerC++
if(master_mode=="secret")
if(playerC < required_players_secret)
return 0
else
if(playerC < required_players)
return 0
if(!(antag_templates && antag_templates.len))
return 1
var/enemy_count = 0
if(antag_tags && antag_tags.len)
for(var/antag_tag in antag_tags)
var/datum/antagonist/antag = all_antag_types[antag_tag]
if(!antag)
continue
var/list/potential = list()
if(antag.flags & ANTAG_OVERRIDE_JOB)
potential = antag.pending_antagonists
else
potential = antag.candidates
if(islist(potential))
if(require_all_templates && potential.len < antag.initial_spawn_req)
return 0
enemy_count += potential.len
if(enemy_count >= required_enemies)
return 1
return 0
/datum/game_mode/proc/refresh_event_modifiers()
if(event_delay_mod_moderate || event_delay_mod_major)
event_manager.report_at_round_end = 1
if(event_delay_mod_moderate)
var/datum/event_container/EModerate = event_manager.event_containers[EVENT_LEVEL_MODERATE]
EModerate.delay_modifier = event_delay_mod_moderate
if(event_delay_mod_moderate)
var/datum/event_container/EMajor = event_manager.event_containers[EVENT_LEVEL_MAJOR]
EMajor.delay_modifier = event_delay_mod_major
/datum/game_mode/proc/pre_setup()
for(var/datum/antagonist/antag in antag_templates)
antag.build_candidate_list() //compile a list of all eligible candidates
//antag roles that replace jobs need to be assigned before the job controller hands out jobs.
if(antag.flags & ANTAG_OVERRIDE_JOB)
antag.attempt_spawn() //select antags to be spawned
///post_setup()
/datum/game_mode/proc/post_setup()
refresh_event_modifiers()
spawn (ROUNDSTART_LOGOUT_REPORT_TIME)
display_roundstart_logout_report()
spawn (rand(waittime_l, waittime_h))
send_intercept()
spawn(rand(100,150))
announce_ert_disabled()
//Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those.
for(var/datum/antagonist/antag in antag_templates)
if(!(antag.flags & ANTAG_OVERRIDE_JOB))
antag.attempt_spawn() //select antags to be spawned
antag.finalize_spawn() //actually spawn antags
if(antag.is_latejoin_template())
latejoin_templates |= antag
if(emergency_shuttle && auto_recall_shuttle)
emergency_shuttle.auto_recall = 1
feedback_set_details("round_start","[time2text(world.realtime)]")
if(ticker && ticker.mode)
feedback_set_details("game_mode","[ticker.mode]")
feedback_set_details("server_ip","[world.internet_address]:[world.port]")
return 1
/datum/game_mode/proc/fail_setup()
for(var/datum/antagonist/antag in antag_templates)
antag.reset()
/datum/game_mode/proc/announce_ert_disabled()
if(!ert_disabled)
return
var/list/reasons = list(
"political instability",
"quantum fluctuations",
"hostile raiders",
"derelict station debris",
"REDACTED",
"ancient alien artillery",
"solar magnetic storms",
"sentient time-travelling killbots",
"gravitational anomalies",
"wormholes to another dimension",
"a telescience mishap",
"radiation flares",
"supermatter dust",
"leaks into a negative reality",
"antiparticle clouds",
"residual bluespace energy",
"suspected criminal operatives",
"malfunctioning von Neumann probe swarms",
"shadowy interlopers",
"a stranded Vox arkship",
"haywire IPC constructs",
"rogue Unathi exiles",
"artifacts of eldritch horror",
"a brain slug infestation",
"killer bugs that lay eggs in the husks of the living",
"a deserted transport carrying xenomorph specimens",
"an emissary for the gestalt requesting a security detail",
"a Tajaran slave rebellion",
"radical Skrellian transevolutionaries",
"classified security operations"
)
command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission")
/datum/game_mode/proc/check_finished()
if(emergency_shuttle.returned() || station_was_nuked)
return 1
if(end_on_antag_death && antag_templates && antag_templates.len)
for(var/datum/antagonist/antag in antag_templates)
if(!antag.antags_are_dead())
return 0
if(config.continous_rounds)
emergency_shuttle.auto_recall = 0
return 0
return 1
return 0
/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case.
return
/datum/game_mode/proc/declare_completion()
var/is_antag_mode = (antag_templates && antag_templates.len)
check_victory()
if(is_antag_mode)
sleep(10)
for(var/datum/antagonist/antag in antag_templates)
sleep(10)
antag.check_victory()
antag.print_player_summary()
sleep(10)
print_ownerless_uplinks()
var/clients = 0
var/surviving_humans = 0
var/surviving_total = 0
var/ghosts = 0
var/escaped_humans = 0
var/escaped_total = 0
var/escaped_on_pod_1 = 0
var/escaped_on_pod_2 = 0
var/escaped_on_pod_3 = 0
var/escaped_on_pod_5 = 0
var/escaped_on_shuttle = 0
var/list/area/escape_locations = list(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom)
for(var/mob/M in player_list)
if(M.client)
clients++
if(ishuman(M))
if(M.stat != DEAD)
surviving_humans++
if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations)
escaped_humans++
if(M.stat != DEAD)
surviving_total++
if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations)
escaped_total++
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape/centcom)
escaped_on_shuttle++
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom)
escaped_on_pod_1++
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom)
escaped_on_pod_2++
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom)
escaped_on_pod_3++
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom)
escaped_on_pod_5++
if(isobserver(M))
ghosts++
var/text = ""
if(surviving_total > 0)
text += "
There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]"
text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
"
else
text += "There were no survivors ([ghosts] ghosts)."
world << text
if(clients > 0)
feedback_set("round_end_clients",clients)
if(ghosts > 0)
feedback_set("round_end_ghosts",ghosts)
if(surviving_humans > 0)
feedback_set("survived_human",surviving_humans)
if(surviving_total > 0)
feedback_set("survived_total",surviving_total)
if(escaped_humans > 0)
feedback_set("escaped_human",escaped_humans)
if(escaped_total > 0)
feedback_set("escaped_total",escaped_total)
if(escaped_on_shuttle > 0)
feedback_set("escaped_on_shuttle",escaped_on_shuttle)
if(escaped_on_pod_1 > 0)
feedback_set("escaped_on_pod_1",escaped_on_pod_1)
if(escaped_on_pod_2 > 0)
feedback_set("escaped_on_pod_2",escaped_on_pod_2)
if(escaped_on_pod_3 > 0)
feedback_set("escaped_on_pod_3",escaped_on_pod_3)
if(escaped_on_pod_5 > 0)
feedback_set("escaped_on_pod_5",escaped_on_pod_5)
send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.")
return 0
/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere.
return 0
/datum/game_mode/proc/send_intercept()
var/intercepttext = "Cent. Com. Update Requested status information: