Files
vgstation13/code/game/gamemodes/gameticker.dm
ShiftyRail 8fed681165 Re-adds blob to the rotation [WiP] (#20915)
Re-adds blob to the rotation
2019-02-18 14:13:05 -03:00

725 lines
26 KiB
Plaintext

var/datum/controller/gameticker/ticker
/datum/controller/gameticker
var/remaining_time = 0
var/const/restart_timeout = 60 SECONDS //Right now, this is padded out by the end credit's audio starting time (at the time of writing this, 10 seconds)
var/current_state = GAME_STATE_PREGAME
var/gamestart_time = -1 //In seconds. Set by ourselves in setup()
var/shuttledocked_time = -1 //In seconds. Set by emergency_shuttle/proc/shuttle_phase()
var/gameend_time = -1 //In seconds. Set by ourselves in process()
var/pregame_timeleft = 0
var/delay_end = 0 //if set to nonzero, the round will not restart on its own
var/hide_mode = 0
var/datum/gamemode/mode = null
var/event_time = null
var/event = 0
var/login_music // music played in pregame lobby
var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking.
var/Bible_icon_state // icon_state the OFFICIAL chaplain has chosen for his bible
var/Bible_item_state // item_state the OFFICIAL chaplain has chosen for his bible
var/Bible_name // name of the bible
var/Bible_deity_name = "Space Jesus" // Default deity
var/datum/religion/chap_rel // Official religion of chappy
var/list/datum/religion/religions = list() // Religion(s) in the game
var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
var/hardcore_mode = 0 //If set to nonzero, hardcore mode is enabled (current hardcore mode features: damage from hunger)
//Use the hardcore_mode_on macro - if(hardcore_mode_on) to_chat(user,"You're hardcore!")
var/datum/rune_controller/rune_controller
var/triai = 0 //Global holder for Triumvirate
var/explosion_in_progress
var/station_was_nuked
var/list/datum/role/antag_types = list() // Associative list of all the antag types in the round (List[id] = roleNumber1) //Seems to be totally unused?
// Hack
var/obj/machinery/media/jukebox/superjuke/thematic/theme = null
#define LOBBY_TICKING 1
#define LOBBY_TICKING_RESTARTED 2
/datum/controller/gameticker/proc/pregame()
var/oursong = file(pick(
"sound/music/space.ogg",
"sound/music/traitor.ogg",
"sound/music/space_oddity.ogg",
"sound/music/title1.ogg",
"sound/music/title2.ogg",
"sound/music/clown.ogg",
"sound/music/robocop.ogg",
"sound/music/gaytony.ogg",
"sound/music/rocketman.ogg",
"sound/music/2525.ogg",
"sound/music/moonbaseoddity.ogg",
"sound/music/whatisthissong.ogg",
"sound/music/space_asshole.ogg",
"sound/music/starman.ogg",
"sound/music/dawsonschristian.ogg",
"sound/music/carmenmirandasghost.ogg",
))
if(SNOW_THEME)
var/path = "sound/music/xmas/"
var/list/filenames = flist(path)
for(var/filename in filenames)
if(copytext(filename, length(filename)) == "/")
filenames -= filename
login_music = file("[path][pick(filenames)]")
else
login_music = fcopy_rsc(oursong)
send2maindiscord("**Server is loaded** and in pre-game lobby at `[config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]`")
do
#ifdef GAMETICKER_LOBBY_DURATION
var/delay_timetotal = GAMETICKER_LOBBY_DURATION
#else
var/delay_timetotal = DEFAULT_LOBBY_TIME
#endif
pregame_timeleft = world.timeofday + delay_timetotal
to_chat(world, "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>")
to_chat(world, "Please, setup your character and select ready. Game will start in [(pregame_timeleft - world.timeofday) / 10] seconds.")
while(current_state <= GAME_STATE_PREGAME)
for(var/i=0, i<10, i++)
sleep(1)
vote.process()
watchdog.check_for_update()
//if(watchdog.waiting)
// to_chat(world, "<span class='notice'>Server update detected, restarting momentarily.</span>")
//watchdog.signal_ready()
//return
if (world.timeofday < (863800 - delay_timetotal) && pregame_timeleft > 863950) // having a remaining time > the max of time of day is bad....
pregame_timeleft -= 864000
if(!going && !remaining_time)
remaining_time = pregame_timeleft - world.timeofday
if(going == LOBBY_TICKING_RESTARTED)
pregame_timeleft = world.timeofday + remaining_time
going = LOBBY_TICKING
remaining_time = 0
if(going && world.timeofday >= pregame_timeleft)
current_state = GAME_STATE_SETTING_UP
while (!setup())
#undef LOBBY_TICKING
#undef LOBBY_TICKING_RESTARTED
/datum/controller/gameticker/proc/StartThematic(var/playlist)
if(!theme)
theme = new(locate(1,1,CENTCOMM_Z))
theme.playlist_id=playlist
theme.playing=1
theme.update_music()
theme.update_icon()
/datum/controller/gameticker/proc/StopThematic()
theme.playing=0
theme.update_music()
theme.update_icon()
/datum/controller/gameticker/proc/setup()
//Create and announce mode
if(master_mode=="secret")
src.hide_mode = 1
var/list/datum/gamemode/runnable_modes
if((master_mode=="random"))
runnable_modes = config.get_runnable_modes()
if (runnable_modes.len==0)
current_state = GAME_STATE_PREGAME
to_chat(world, "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby.")
return 0
if(secret_force_mode != "secret")
var/datum/gamemode/M = config.pick_mode(secret_force_mode)
if(M.can_start())
src.mode = config.pick_mode(secret_force_mode)
job_master.ResetOccupations()
if(!src.mode)
src.mode = pickweight(runnable_modes)
if(src.mode)
var/mtype = src.mode.type
src.mode = new mtype
else if (master_mode=="secret")
mode = config.pick_mode("Dynamic Mode") //Huzzah
else
src.mode = config.pick_mode(master_mode)
//log_startup_progress("gameticker.mode is [src.mode.name].")
src.mode = new mode.type
if (!src.mode.can_start())
to_chat(world, "<B>Unable to start [mode.name].</B> Not enough players, [mode.minimum_player_count] players needed. Reverting to pre-game lobby.")
del(mode)
current_state = GAME_STATE_PREGAME
job_master.ResetOccupations()
return 0
//Configure mode and assign player to special mode stuff
job_master.DivideOccupations() //Distribute jobs
var/can_continue = src.mode.Setup()//Setup special modes
if(!can_continue)
current_state = GAME_STATE_PREGAME
to_chat(world, "<B>Error setting up [master_mode].</B> Reverting to pre-game lobby.")
log_admin("The gamemode setup for [mode.name] errored out.")
world.log << "The gamemode setup for [mode.name] errored out."
del(mode)
job_master.ResetOccupations()
return 0
if(hide_mode)
var/list/modes = new
for (var/datum/gamemode/M in runnable_modes)
modes+=M.name
modes = sortList(modes)
if(Holiday == APRIL_FOOLS_DAY)
to_chat(world, "<B>The current game mode is - [pick("Chivalry","Crab Battle","Bay Transfer","Dwarf Fortress","Ian Says","Admins Funhouse","Meteor","Xenoarchaeology Appreciation","Clowns versus [pick("Mimes","Assistants","the Universe")]","Dino wars","Malcolm in the Middle","Six hours of extended where one person with all the access refuses to call the shuttle while everyone else goes braindead","Monkey Study","Nations","Nations by Hasbro","High roleplay Extended","DarkRP","Babies Day out","Ians Day out","Shortstaffed medical")]!</B>")
else
to_chat(world, "<B>The current game mode is - Secret!</B>")
to_chat(world, "<B>Possibilities:</B> [english_list(modes)]")
gamestart_time = world.time / 10
init_PDAgames_leaderboard()
create_characters() //Create player characters and transfer them
collect_minds()
equip_characters()
current_state = GAME_STATE_PLAYING
// Update new player panels so they say join instead of ready up.
for(var/mob/new_player/player in player_list)
player.new_player_panel_proc()
//here to initialize the random events nicely at round start
setup_economy()
#if UNIT_TESTS_AUTORUN
run_unit_tests()
#endif
spawn(0)//Forking here so we dont have to wait for this to finish
mode.PostSetup()
//Cleanup some stuff
for(var/obj/effect/landmark/start/S in landmarks_list)
//Deleting Startpoints but we need the ai point to AI-ize people later and the Trader point to throw new ones
if (S.name != "AI" && S.name != "Trader")
qdel(S)
var/list/obj/effect/landmark/spacepod/random/L = list()
for(var/obj/effect/landmark/spacepod/random/SS in landmarks_list)
if(istype(SS))
L += SS
if(L.len)
var/obj/effect/landmark/spacepod/random/S = pick(L)
new /obj/spacepod/random(S.loc)
for(var/obj in L)
if(istype(obj, /obj/effect/landmark/spacepod/random))
qdel(obj)
to_chat(world, "<FONT color='blue'><B>Enjoy the game!</B></FONT>")
send2maindiscord("**The game has started**")
// world << sound('sound/AI/welcome.ogg')// Skie //Out with the old, in with the new. - N3X15
if(!config.shut_up_automatic_diagnostic_and_announcement_system)
var/welcome_sentence=list('sound/AI/vox_login.ogg')
welcome_sentence += pick(
'sound/AI/vox_reminder1.ogg',
'sound/AI/vox_reminder2.ogg',
'sound/AI/vox_reminder3.ogg',
'sound/AI/vox_reminder4.ogg',
'sound/AI/vox_reminder5.ogg',
'sound/AI/vox_reminder6.ogg',
'sound/AI/vox_reminder7.ogg',
'sound/AI/vox_reminder8.ogg',
'sound/AI/vox_reminder9.ogg',
'sound/AI/vox_reminder10.ogg',
'sound/AI/vox_reminder11.ogg',
'sound/AI/vox_reminder12.ogg',
'sound/AI/vox_reminder13.ogg',
'sound/AI/vox_reminder14.ogg',
'sound/AI/vox_reminder15.ogg')
for(var/sound in welcome_sentence)
play_vox_sound(sound,STATION_Z,null)
//Holiday Round-start stuff ~Carn
Holiday_Game_Start()
//mode.Clean_Antags()
//start_events() //handles random events and space dust.
//new random event system is handled from the MC.
if(0 == admins.len)
send2adminirc("Round has started with no admins online.")
send2admindiscord("**Round has started with no admins online.**", TRUE)
Master.RoundStart()
if(config.sql_enabled)
spawn(3000)
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE
stat_collection.round_start_time = world.realtime
spawn(5 MINUTES) // poll every 5 minutes
population_poll_loop()
wageSetup()
post_roundstart()
return 1
/datum/controller/gameticker
//station_explosion used to be a variable for every mob's hud. Which was a waste!
//Now we have a general cinematic centrally held within the gameticker....far more efficient!
var/obj/abstract/screen/cinematic = null
//Plus it provides an easy way to make cinematics for other events. Just use this as a template :)
/datum/controller/gameticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
if( cinematic )
return //already a cinematic in progress!
for (var/datum/html_interface/hi in html_interfaces)
hi.closeAll()
//initialise our cinematic screen object
cinematic = new(src)
cinematic.icon = 'icons/effects/station_explosion.dmi'
cinematic.icon_state = "station_intact"
cinematic.plane = HUD_PLANE
cinematic.mouse_opacity = 0
cinematic.screen_loc = "1,0"
var/obj/structure/bed/temp_buckle = new(src)
//Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
if(station_missed)
for(var/mob/living/M in living_mob_list)
M.locked_to = temp_buckle //buckles the mob so it can't do anything
if(M.client)
M.client.screen += cinematic //show every client the cinematic
else //nuke kills everyone on the station to prevent "hurr-durr I survived"
for(var/mob/living/M in living_mob_list)
M.locked_to = temp_buckle
if(M.client)
M.client.screen += cinematic
if(!(M.z)) //inside a crate or something
var/turf/T = get_turf(M)
if(T && T.z==map.zMainStation) //we don't use M.death(0) because it calls a for(/mob) loop and
M.nuke_act()
else if(M.z == map.zMainStation) //on the station.
M.nuke_act()
//Now animate the cinematic
switch(station_missed)
if(1) //nuke was nearby but (mostly) missed
if( mode && !override )
override = mode.name
switch( override )
if("nuclear emergency") //Nuke wasn't on station when it blew up
flick("intro_nuke",cinematic)
sleep(35)
world << sound('sound/effects/explosionfar.ogg')
flick("station_intact_fade_red",cinematic)
cinematic.icon_state = "summary_nukefail"
else
flick("intro_nuke",cinematic)
sleep(35)
world << sound('sound/effects/explosionfar.ogg')
//flick("end",cinematic)
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
sleep(50)
world << sound('sound/effects/explosionfar.ogg')
else //station was destroyed
if( mode && !override )
override = mode.name
switch( override )
if("nuclear emergency") //Nuke Ops successfully bombed the station
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red",cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_nukewin"
if("AI malfunction") //Malf (screen,explosion,summary)
flick("intro_malf",cinematic)
sleep(76)
flick("station_explode_fade_red",cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_malf"
else //Station nuked (nuke,explosion,summary)
flick("intro_nuke",cinematic)
sleep(35)
flick("station_explode_fade_red", cinematic)
world << sound('sound/effects/explosionfar.ogg')
cinematic.icon_state = "summary_selfdes"
//If its actually the end of the round, wait for it to end.
//Otherwise if its a verb it will continue on afterwards.
sleep(300)
if(cinematic)
qdel(cinematic) //end the cinematic
if(temp_buckle)
qdel(temp_buckle) //release everybody
return
/datum/controller/gameticker/proc/create_characters()
for(var/mob/new_player/player in player_list)
if(player.ready && player.mind)
if(player.mind.assigned_role=="AI")
player.close_spawn_windows()
player.AIize()
else if(player.mind.assigned_role=="Cyborg")
player.create_roundstart_cyborg()
else if(!player.mind.assigned_role)
continue
else
var/mob/living/carbon/human/new_character = player.create_character()
switch(new_character.mind.assigned_role)
if("MODE","Mobile MMI","Trader")
//No injection
else
data_core.manifest_inject(new_character)
player.FuckUpGenes(new_character)
qdel(player)
/datum/controller/gameticker/proc/collect_minds()
for(var/mob/living/player in player_list)
if(player.mind)
ticker.minds += player.mind
/datum/controller/gameticker/proc/equip_characters()
var/captainless=1
for(var/mob/living/carbon/human/player in player_list)
if(player && player.mind && player.mind.assigned_role)
if(player.mind.assigned_role == "Captain")
captainless=0
if(player.mind.assigned_role != "MODE")
job_master.EquipRank(player, player.mind.assigned_role, 0)
EquipCustomItems(player)
if(captainless)
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player))
to_chat(M, "Captainship not forced on anyone.")
for(var/mob/M in player_list)
if(!istype(M,/mob/new_player))
M.store_position()//updates the players' origin_ vars so they retain their location when the round starts.
/datum/controller/gameticker/proc/process()
if(current_state != GAME_STATE_PLAYING)
return 0
mode.process()
if(world.time > nanocoins_lastchange)
nanocoins_lastchange = world.time + rand(3000,15000)
nanocoins_rates = (rand(1,30))/10
/*emergency_shuttle.process()*/
watchdog.check_for_update()
var/force_round_end=0
// If server's empty, force round end.
if(watchdog.waiting && player_list.len == 0)
force_round_end=1
var/mode_finished = mode.check_finished() || (emergency_shuttle.location == 2 && emergency_shuttle.alert == 1) || force_round_end
if(!explosion_in_progress && mode_finished)
current_state = GAME_STATE_FINISHED
spawn
declare_completion()
end_credits.on_round_end()
gameend_time = world.time / 10
if(config.map_voting)
//testing("Vote picked [chosen_map]")
vote.initiate_vote("map","The Server", popup = 1, weighted_vote = config.weighted_votes)
var/options = jointext(vote.choices, " ")
feedback_set("map vote choices", options)
else
var/list/maps = get_maps()
var/list/choices=list()
for(var/key in maps)
choices.Add(key)
var/mapname=pick(choices)
vote.chosen_map = maps[mapname] // Hack, but at this point I could not give a shit.
watchdog.chosen_map = copytext(mapname,1,(length(mapname)))
log_game("Server chose [watchdog.chosen_map]!")
spawn(50)
if (station_was_nuked)
feedback_set_details("end_proper","nuke")
if(!delay_end && !watchdog.waiting)
to_chat(world, "<span class='notice'><B>Rebooting due to destruction of station in [restart_timeout/10] seconds</B></span>")
else
feedback_set_details("end_proper","\proper completion")
if(!delay_end && !watchdog.waiting)
to_chat(world, "<span class='notice'><B>Restarting in [restart_timeout/10] seconds</B></span>")
if(blackbox)
if(config.map_voting)
spawn(restart_timeout + 1)
blackbox.save_all_data_to_sql()
else
blackbox.save_all_data_to_sql()
stat_collection.Process()
if (watchdog.waiting)
to_chat(world, "<span class='notice'><B>Server will shut down for an automatic update in [config.map_voting ? "[(restart_timeout/10)] seconds." : "a few seconds."]</B></span>")
if(config.map_voting)
sleep(restart_timeout) //waiting for a mapvote to end
if(!delay_end)
watchdog.signal_ready()
else
to_chat(world, "<span class='notice'><B>An admin has delayed the round end</B></span>")
delay_end = 2
else if(!delay_end)
sleep(restart_timeout)
if(!delay_end)
CallHook("Reboot",list())
world.Reboot()
else
to_chat(world, "<span class='notice'><B>An admin has delayed the round end</B></span>")
delay_end = 2
else
to_chat(world, "<span class='notice'><B>An admin has delayed the round end</B></span>")
delay_end = 2
return 1
/datum/controller/gameticker/proc/init_PDAgames_leaderboard()
init_snake_leaderboard()
init_minesweeper_leaderboard()
/datum/controller/gameticker/proc/init_snake_leaderboard()
for(var/x=1;x<=PDA_APP_SNAKEII_MAXSPEED;x++)
snake_station_highscores += x
snake_station_highscores[x] = list()
snake_best_players += x
snake_best_players[x] = list()
var/list/templist1 = snake_station_highscores[x]
var/list/templist2 = snake_best_players[x]
for(var/y=1;y<=PDA_APP_SNAKEII_MAXLABYRINTH;y++)
templist1 += y
templist1[y] = 0
templist2 += y
templist2[y] = "none"
/datum/controller/gameticker/proc/init_minesweeper_leaderboard()
minesweeper_station_highscores["beginner"] = 999
minesweeper_station_highscores["intermediate"] = 999
minesweeper_station_highscores["expert"] = 999
minesweeper_best_players["beginner"] = "none"
minesweeper_best_players["intermediate"] = "none"
minesweeper_best_players["expert"] = "none"
/datum/controller/gameticker/proc/declare_completion()
scoreboard()
return 1
/*
/datum/controller/gameticker/proc/ert_declare_completion()
var/text = ""
if( ticker.mode.ert.len )
var/icon/logo = icon('icons/logos.dmi', "ert-logo")
end_icons += logo
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <FONT size = 2><B>The emergency responders were:</B></FONT> <img src="logo_[tempstate].png">"}
for(var/datum/mind/ert in ticker.mode.ert)
if(ert.current)
var/icon/flat = getFlatIcon(ert.current, SOUTH, 1, 1)
end_icons += flat
tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[ert.key]</b> was <b>[ert.name]</b> ("}
if(ert.current.stat == DEAD)
text += "died"
flat.Turn(90)
end_icons[tempstate] = flat
else
text += "survived"
if(ert.current.real_name != ert.name)
text += " as [ert.current.real_name]"
else
var/icon/sprotch = icon('icons/effects/blood.dmi', "floor1-old")
end_icons += sprotch
tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> [ert.key] was [ert.name] ("}
text += "body destroyed"
text += ")"
text += "<BR><HR>"
return text
/datum/controller/gameticker/proc/deathsquad_declare_completion()
var/text = ""
if( ticker.mode.deathsquad.len )
var/icon/logo = icon('icons/logos.dmi', "death-logo")
end_icons += logo
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <FONT size = 2><B>The death commando were:</B></FONT> <img src="logo_[tempstate].png">"}
for(var/datum/mind/deathsquad in ticker.mode.deathsquad)
if(deathsquad.current)
var/icon/flat = getFlatIcon(deathsquad.current, SOUTH, 1, 1)
end_icons += flat
tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[deathsquad.key]</b> was <b>[deathsquad.name]</b> ("}
if(deathsquad.current.stat == DEAD)
text += "died"
flat.Turn(90)
end_icons[tempstate] = flat
else
text += "survived"
if(deathsquad.current.real_name != deathsquad.name)
text += " as [deathsquad.current.real_name]"
else
var/icon/sprotch = icon('icons/effects/blood.dmi', "floor1-old")
end_icons += sprotch
tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> [deathsquad.key] was [deathsquad.name] ("}
text += "body destroyed"
text += ")"
text += "<BR><HR>"
return text
*/
/datum/controller/gameticker/proc/bomberman_declare_completion()
var/icon/bomberhead = icon('icons/obj/clothing/hats.dmi', "bomberman")
end_icons += bomberhead
var/tempstatebomberhead = end_icons.len
var/icon/bronze = icon('icons/obj/bomberman.dmi', "bronze")
end_icons += bronze
var/tempstatebronze = end_icons.len
var/icon/silver = icon('icons/obj/bomberman.dmi', "silver")
end_icons += silver
var/tempstatesilver = end_icons.len
var/icon/gold = icon('icons/obj/bomberman.dmi', "gold")
end_icons += gold
var/tempstategold = end_icons.len
var/icon/platinum = icon('icons/obj/bomberman.dmi', "platinum")
end_icons += platinum
var/tempstateplatinum = end_icons.len
var/list/bronze_tier = list()
for (var/mob/living/carbon/M in player_list)
if(locate(/obj/item/weapon/bomberman/) in M)
bronze_tier += M
var/list/silver_tier = list()
for (var/mob/M in bronze_tier)
if(M.z == map.zCentcomm)
silver_tier += M
bronze_tier -= M
var/list/gold_tier = list()
for (var/mob/M in silver_tier)
var/turf/T = get_turf(M)
if(istype(T.loc, /area/shuttle/escape/centcom))
gold_tier += M
silver_tier -= M
var/list/platinum_tier = list()
for (var/mob/living/carbon/human/M in gold_tier)
if(istype(M.wear_suit, /obj/item/clothing/suit/space/bomberman) && istype(M.head, /obj/item/clothing/head/helmet/space/bomberman))
var/obj/item/clothing/suit/space/bomberman/C1 = M.wear_suit
var/obj/item/clothing/head/helmet/space/bomberman/C2 = M.head
if(C1.never_removed && C2.never_removed)
platinum_tier += M
gold_tier -= M
var/list/special_tier = list()
for (var/mob/living/silicon/robot/mommi/M in player_list)
if(istype(M.head_state, /obj/item/clothing/head/helmet/space/bomberman) && istype(M.tool_state, /obj/item/weapon/bomberman/))
special_tier += M
var/text = {"<img src="logo_[tempstatebomberhead].png"> <font size=5><b>Bomberman Mode Results</b></font> <img src="logo_[tempstatebomberhead].png">"}
if(!platinum_tier.len && !gold_tier.len && !silver_tier.len && !bronze_tier.len)
text += "<br><span class='danger'>DRAW!</span>"
if(platinum_tier.len)
text += {"<br><img src="logo_[tempstateplatinum].png"> <b>Platinum Trophy</b> (never removed his clothes, kept his bomb dispenser until the end, and escaped on the shuttle):"}
for (var/mob/M in platinum_tier)
var/icon/flat = getFlatIcon(M, SOUTH, 1, 1)
end_icons += flat
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[M.key]</b> as <b>[M.real_name]</b>"}
if(gold_tier.len)
text += {"<br><img src="logo_[tempstategold].png"> <b>Gold Trophy</b> (kept his bomb dispenser until the end, and escaped on the shuttle):"}
for (var/mob/M in gold_tier)
var/icon/flat = getFlatIcon(M, SOUTH, 1, 1)
end_icons += flat
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[M.key]</b> as <b>[M.real_name]</b>"}
if(silver_tier.len)
text += {"<br><img src="logo_[tempstatesilver].png"> <b>Silver Trophy</b> (kept his bomb dispenser until the end, and escaped in a pod):"}
for (var/mob/M in silver_tier)
var/icon/flat = getFlatIcon(M, SOUTH, 1, 1)
end_icons += flat
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[M.key]</b> as <b>[M.real_name]</b>"}
if(bronze_tier.len)
text += {"<br><img src="logo_[tempstatebronze].png"> <b>Bronze Trophy</b> (kept his bomb dispenser until the end):"}
for (var/mob/M in bronze_tier)
var/icon/flat = getFlatIcon(M, SOUTH, 1, 1)
end_icons += flat
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[M.key]</b> as <b>[M.real_name]</b>"}
if(special_tier.len)
text += "<br><b>Special Mention</b> to those adorable MoMMis:"
for (var/mob/M in special_tier)
var/icon/flat = getFlatIcon(M, SOUTH, 1, 1)
end_icons += flat
var/tempstate = end_icons.len
text += {"<br><img src="logo_[tempstate].png"> <b>[M.key]</b> as <b>[M.name]</b>"}
return text
/datum/controller/gameticker/proc/achievement_declare_completion()
var/text = "<br><FONT size = 5><b>Additionally, the following players earned achievements:</b></FONT>"
var/icon/cup = icon('icons/obj/drinks.dmi', "golden_cup")
end_icons += cup
var/tempstate = end_icons.len
for(var/winner in achievements)
text += {"<br><img src="logo_[tempstate].png"> [winner]"}
return text
/datum/controller/gameticker/proc/get_all_heads()
var/list/heads = list()
for(var/mob/player in mob_list)
if(player.mind && (player.mind.assigned_role in command_positions))
heads += player.mind
return heads
/datum/controller/gameticker/proc/get_assigned_head_roles()
var/list/roles = list()
for(var/mob/player in mob_list)
if(player.mind && (player.mind.assigned_role in command_positions))
roles += player.mind.assigned_role
return roles
/datum/controller/gameticker/proc/post_roundstart()
//Handle all the cyborg syncing
var/list/active_ais = active_ais()
if(active_ais.len)
for(var/mob/living/silicon/robot/R in cyborg_list)
if(!R.connected_ai)
R.connect_AI(select_active_ai_with_fewest_borgs())
to_chat(R, R.connected_ai?"<b>You have synchronized with an AI. Their name will be stated shortly. Other AIs can be ignored.</b>":"<b>You are not synchronized with an AI, and therefore are not required to heed the instructions of any unless you are synced to them.</b>")
R.lawsync()
/world/proc/has_round_started()
return ticker && ticker.current_state >= GAME_STATE_PLAYING