Files
Yogstation/code/controllers/subsystem/ticker.dm
Byemoh d93428fe4b [MDB IGNORE] [PRE-ALPHA] MiniStation (#22568)
* Create MiniStation new.dmm

* ministation

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* ministayion

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* actual changes oops

* Update MiniStation.dmm

* Update MiniStation.dmm

* miniaturestation

* Update maps.txt

* more fixes

* fixes broken path

* more fixes

* Update MiniStation.dmm

* Update MiniStation.dmm

* filters

* robotics changes

* fixes pipes

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* spawns + job overrides

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* more work

* Update MiniStation.dmm

* misc stuff

* maint stuff

* uopdates

* fixes bad area and chair

* tool storage + fixes

* mining APC

* cargo + hooks up some disposals

* more disposals + launcher

* lights

* more fixes

* Update MiniStation.dmm

* Update MiniStation.dmm

* more fixes

* sink

* remove sign

* expand janitor room

* Update MiniStation.dmm

* ai stuff + more firealarms

* meeting room, etc

* fixes wire

* Update MiniStation.dmm

* fixes

* Update MiniStation.dmm

* yep!

* atmos area

* fixes some map errors

* bookbinder

* robo shutters

* stair and chem fix

* fixes bad decals

* honesly no clue what's different

* disposals + pets

* Update MiniStation.dmm

* asteroid area

* more fixes

* medbay mixup

* choom button

* wire toxin room to grid

* remove this

* more fixes

* Update ai_whale.dmm

* more cameras

* Update networking_machines.dm

* update freezer

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* new service

* hawk + tuah + backrooms

* special charters

* remove that lol

* move him a little

* Update MiniStation.dmm

* yeah

* Update MiniStation.dmm

* psych office + more fixes + robo stuff

* thermomachines

* Update MiniStation.dmm

* maints + APCs

* all this stuff

* cameras

* air alarms

* RD office and the like

* Update MiniStation.dmm

* fixes these

* glass floors

* Update MiniStation.dmm

* more firelocks

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* extra pipe moment

* Update MiniStation.dmm

* looking up and down/fixes

* neat

* wire

* Update MiniStation.dmm

* fixes some issues

* asdad

* better SM stuff

* amon gus

* fixing falling

* Update MiniStation.dmm

* Update MiniStation.dmm

* fixes this

* fixes

* Update MiniStation.dmm

* remove stairs

* Update MiniStation.dmm

* Update MiniStation.dmm

* Update MiniStation.dmm

* fixes some ai stuff

* Update MiniStation.dmm

* adds some missing bells

* maint

* more maint stuff

* ai upload and goodbye meeting room

* fixes multiz pda stuff

* fixes locker

* ministation fixes

* requested changes

* shadows

* removes shadows

* adds medhuds

* no smoking

* move up/down

* correct medhuds

* brig stuff

* cameras

* fixes

* more lights

* more fixes

* fix one turf

* idk randomstuff

* more lights

* navigation nation (thanks plant)

* fixes + better flooring

* jamie changes

* myriad changes

* some more stuff

* server room fix
2024-10-19 00:13:31 +01:00

733 lines
28 KiB
Plaintext
Executable File

#define ROUND_START_MUSIC_LIST "strings/round_start_sounds.txt"
SUBSYSTEM_DEF(ticker)
name = "Ticker"
init_order = INIT_ORDER_TICKER
priority = FIRE_PRIORITY_TICKER
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME
var/current_state = GAME_STATE_STARTUP //state of current round (used by process()) Use the defines GAME_STATE_* !
var/force_ending = 0 //Round was ended by admin intervention
// If true, there is no lobby phase, the game starts immediately.
var/start_immediately = FALSE
var/setup_done = FALSE //All game setup done including mode post setup and
var/hide_mode = 0
var/login_music //music played in pregame lobby
var/login_music_data
var/round_end_sound //music/jingle played when the world reboots
var/round_end_sound_sent = TRUE //If all clients have loaded it
var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking.
var/delay_end = 0 //if set true, the round will not restart on it's own
var/admin_delay_notice = "" //a message to display to anyone who tries to restart the world after a delay
var/ready_for_reboot = FALSE //all roundend preparation done with, all that's left is reboot
var/triai = 0 //Global holder for Triumvirate
var/tipped = 0 //Did we broadcast the tip of the day yet?
var/selected_tip // What will be the tip of the day?
var/timeLeft //pregame timer
var/start_at
var/gametime_offset = 432000 //Deciseconds to add to world.time for station time.
var/station_time_rate_multiplier = 12 //factor of station time progressal vs real time.
var/totalPlayers = 0 //used for pregame stats on statpanel
var/totalPlayersReady = 0 //used for pregame stats on statpanel
var/queue_delay = 0
var/list/queued_players = list() //used for join queues when the server exceeds the hard population cap
var/maprotatechecked = 0
var/news_report
var/late_join_disabled
var/roundend_check_paused = FALSE
var/round_start_time = 0
var/list/round_start_events
var/list/round_end_events
var/mode_result = "undefined"
var/end_state = "undefined"
var/music_available = 0
/datum/controller/subsystem/ticker/Initialize(timeofday)
load_mode()
/*var/list/byond_sound_formats = list( //yogs start - goonchat lobby music
"mid" = TRUE,
"midi" = TRUE,
"mod" = TRUE,
"it" = TRUE,
"s3m" = TRUE,
"xm" = TRUE,
"oxm" = TRUE,
"wav" = TRUE,
"ogg" = TRUE,
"raw" = TRUE,
"wma" = TRUE,
"aiff" = TRUE
)
var/list/provisional_title_music = flist("[global.config.directory]/title_music/sounds/")
var/list/music = list()
var/use_rare_music = prob(1)
for(var/S in provisional_title_music)
var/lower = lowertext(S)
var/list/L = splittext(lower,"+")
switch(L.len)
if(3) //rare+MAP+sound.ogg or MAP+rare.sound.ogg -- Rare Map-specific sounds
if(use_rare_music)
if(L[1] == "rare" && L[2] == SSmapping.config.map_name)
music += S
else if(L[2] == "rare" && L[1] == SSmapping.config.map_name)
music += S
if(2) //rare+sound.ogg or MAP+sound.ogg -- Rare sounds or Map-specific sounds
if((use_rare_music && L[1] == "rare") || (L[1] == SSmapping.config.map_name))
music += S
if(1) //sound.ogg -- common sound
music += S
var/old_login_music = trim(file2text("data/last_round_lobby_music.txt"))
if(music.len > 1)
music -= old_login_music
for(var/S in music)
var/list/L = splittext(S,".")
if(L.len >= 2)
var/ext = lowertext(L[L.len]) //pick the real extension, no 'honk.ogg.exe' nonsense here
if(byond_sound_formats[ext])
continue
music -= S
if(isemptylist(music))
music = world.file2list(ROUND_START_MUSIC_LIST, "\n")
login_music = pick(music)
else
login_music = "[global.config.directory]/title_music/sounds/[pick(music)]"*/
login_music_data = list()
login_music = choose_lobby_music()
if(!login_music)
to_chat(world, span_boldwarning("Could not load lobby music.")) //yogs end
if(!GLOB.syndicate_code_phrase)
GLOB.syndicate_code_phrase = generate_code_phrase(return_list=TRUE)
var/codewords = jointext(GLOB.syndicate_code_phrase, "|")
var/regex/codeword_match = new("([codewords])(?!\[^<\]*>)", "ig")
GLOB.syndicate_code_phrase_regex = codeword_match
if(!GLOB.syndicate_code_response)
GLOB.syndicate_code_response = generate_code_phrase(return_list=TRUE)
var/codewords = jointext(GLOB.syndicate_code_response, "|")
var/regex/codeword_match = new("([codewords])(?!\[^<\]*>)", "ig")
GLOB.syndicate_code_response_regex = codeword_match
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
if(CONFIG_GET(flag/randomize_shift_time))
gametime_offset = rand(0, 23) HOURS
else if(CONFIG_GET(flag/shift_time_realtime))
gametime_offset = world.timeofday
return SS_INIT_SUCCESS
/datum/controller/subsystem/ticker/fire()
switch(current_state)
if(GAME_STATE_STARTUP)
if(Master.initializations_finished_with_no_players_logged_in)
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, span_boldnotice("Welcome to [station_name()]!"))
send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game))
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
fire()
if(GAME_STATE_PREGAME)
//lobby stats for statpanels
if(isnull(timeLeft))
timeLeft = max(0,start_at - world.time)
totalPlayers = 0
totalPlayersReady = 0
for(var/mob/dead/new_player/player in GLOB.player_list)
++totalPlayers
if(player.ready == PLAYER_READY_TO_PLAY)
++totalPlayersReady
if(start_immediately)
timeLeft = 0
//countdown
if(timeLeft < 0)
return
timeLeft -= wait
if(timeLeft <= 300 && !tipped)
send_tip_of_the_round()
tipped = TRUE
if(timeLeft <= 0)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
if(start_immediately)
fire()
if(GAME_STATE_SETTING_UP)
if(!setup())
//setup failed
current_state = GAME_STATE_STARTUP
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
timeLeft = null
Master.SetRunLevel(RUNLEVEL_LOBBY)
SEND_SIGNAL(src, COMSIG_TICKER_ERROR_SETTING_UP)
if(GAME_STATE_PLAYING)
check_queue()
if(!roundend_check_paused && SSgamemode.check_finished(force_ending) || force_ending)
current_state = GAME_STATE_FINISHED
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
toggle_looc(TRUE) // yogs - turn LOOC on at roundend
declare_completion(force_ending)
check_maprotate()
Master.SetRunLevel(RUNLEVEL_POSTGAME)
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, span_boldannounce("Starting game..."))
var/init_start = world.timeofday
//Create and announce mode
SSgamemode.init_storyteller() //monkestation addition
CHECK_TICK
//Configure mode and assign player to special mode stuff
var/can_continue = 0
can_continue = SSgamemode.pre_setup() //Choose antagonists
CHECK_TICK
can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
CHECK_TICK
if(!GLOB.Debug2)
if(!can_continue)
log_game("failed pre_setup, cause: storytellers stuff or ssjob maybe, good luck")
to_chat(world, "<B>Error setting up [GLOB.master_mode].</B> Reverting to pre-game lobby.")
SSjob.ResetOccupations()
return 0
else
message_admins(span_notice("DEBUG: Bypassing prestart checks..."))
CHECK_TICK
if(!CONFIG_GET(flag/ooc_during_round))
toggle_ooc(FALSE) // Turn it off
if(!CONFIG_GET(flag/looc_during_round))
toggle_looc(FALSE) // yogs - Turn it off
CHECK_TICK
GLOB.start_landmarks_list = shuffle(GLOB.start_landmarks_list) //Shuffle the order of spawn points so they dont always predictably spawn bottom-up and right-to-left
create_characters() //Create player characters
collect_minds()
equip_characters()
GLOB.data_core.manifest()
transfer_characters() //transfer keys to the new mobs
for(var/I in round_start_events)
var/datum/callback/cb = I
cb.InvokeAsync()
LAZYCLEARLIST(round_start_events)
log_world("Game start took [(world.timeofday - init_start)/10]s")
round_start_time = world.time
SSdbcore.SetRoundStart()
to_chat(world, span_notice("<B>Welcome to [station_name()], enjoy your stay!</B>"))
var/random_sound = SSstation.announcer.get_rand_welcome_sound()
var/default_sound = SSstation.default_announcer.get_rand_welcome_sound()
if(istype(SSstation.announcer, /datum/centcom_announcer/default))
default_sound = random_sound
for(var/mob/P in GLOB.player_list)
if(P.client && P.client.prefs)
if(P.client.prefs.read_preference(/datum/preference/toggle/disable_alternative_announcers))
SEND_SOUND(P, sound(default_sound))
continue
SEND_SOUND(P, sound(random_sound))
current_state = GAME_STATE_PLAYING
webhook_send_roundstatus("ingame") //yogs - webhook support
Master.SetRunLevel(RUNLEVEL_GAME)
if(SSgamemode.holidays)
to_chat(world, span_notice("and..."))
for(var/holidayname in SSgamemode.holidays)
var/datum/holiday/holiday = SSgamemode.holidays[holidayname]
to_chat(world, "<h4>[holiday.greet()]</h4>")
PostSetup()
// Toggle lightswitches off in unoccupied departments
var/list/lightup_area_typecache = list()
var/minimal_access = CONFIG_GET(flag/jobs_have_minimal_access)
for(var/mob/living/carbon/human/player in GLOB.player_list)
var/role = player.mind?.assigned_role
if(!role)
continue
var/datum/job/job = SSjob.GetJob(role)
if(!job)
continue
lightup_area_typecache |= job.areas_to_light_up(minimal_access)
for(var/area/place as anything in GLOB.areas)
if(!istype(place))
continue
if(place.lights_always_start_on)
continue
if(!is_station_level(place.z))
continue
if(is_type_in_typecache(place, lightup_area_typecache))
continue
place.lightswitch = FALSE
place.update_appearance()
for(var/obj/machinery/light_switch/lswitch in place)
lswitch.update_appearance()
place.power_change()
//INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(rock_paper_scissors_puzzle))
return TRUE
/datum/controller/subsystem/ticker/proc/PostSetup()
set waitfor = FALSE
SSgamemode.post_setup()
SSgamemode.storyteller.process(STORYTELLER_WAIT_TIME * 0.1) // we want this asap
SSgamemode.storyteller.round_started = TRUE
GLOB.start_state = new /datum/station_state()
GLOB.start_state.count()
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
send2irc("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [SSgamemode.storyteller ? SSgamemode.storyteller : "error no storyteller"] has started[allmins.len ? ".":" with no active admins online!"]")
setup_done = TRUE
for(var/i in GLOB.start_landmarks_list)
var/obj/effect/landmark/start/S = i
if(istype(S)) //we can not runtime here. not in this important of a proc.
S.after_round_start()
else
stack_trace("[S] [S.type] found in start landmarks list, which isn't a start landmark!")
//These callbacks will fire after roundstart key transfer
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
if(!HasRoundStarted())
LAZYADD(round_start_events, cb)
else
cb.InvokeAsync()
//These callbacks will fire before roundend report
/datum/controller/subsystem/ticker/proc/OnRoundend(datum/callback/cb)
if(current_state >= GAME_STATE_FINISHED)
cb.InvokeAsync()
else
LAZYADD(round_end_events, cb)
/datum/controller/subsystem/ticker/proc/station_explosion_detonation(atom/bomb)
if(bomb) //BOOM
qdel(bomb)
for(var/T in GLOB.station_turfs)
if(prob(33))
SSexplosions.highturf += T
else
SSexplosions.medturf += T
/datum/controller/subsystem/ticker/proc/create_characters()
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
player.create_character(FALSE)
else
player.new_player_panel()
CHECK_TICK
/datum/controller/subsystem/ticker/proc/collect_minds()
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.new_character && P.new_character.mind)
SSticker.minds += P.new_character.mind
CHECK_TICK
/**
* Equips people that are readied up when the round starts
*
* In addition, responsible for spawning the cyborg shell if there are no roundstart cyborgs and announcing if there ISN'T a Captain present
*/
/datum/controller/subsystem/ticker/proc/equip_characters()
var/captainless = TRUE
var/no_cyborgs = TRUE
var/no_clerk = TRUE
var/no_chaplain = TRUE
for(var/mob/dead/new_player/N in GLOB.player_list)
var/mob/living/carbon/human/player = N.new_character
if(istype(player) && player.mind && player.mind.assigned_role)
if(player.mind.assigned_role == "Captain")
captainless = FALSE
if(player.mind.assigned_role == "Cyborg")
no_cyborgs = FALSE
if(player.mind.assigned_role == "Clerk")
no_clerk = FALSE
if(player.mind.assigned_role == "Chaplain")
no_chaplain = FALSE
if(player.mind.assigned_role != player.mind.special_role)
SSjob.EquipRank(N, player.mind.assigned_role, FALSE)
if(CONFIG_GET(flag/roundstart_traits) && ishuman(N.new_character))
SSquirks.AssignQuirks(N.new_character, N.client, TRUE)
CHECK_TICK
if(no_cyborgs)
var/obj/S = null
for(var/obj/effect/landmark/start/sloc in GLOB.start_landmarks_list)
if(sloc.name != "Cyborg")
S = sloc //so we can revert to spawning them on top of eachother if something goes wrong
continue
if(locate(/mob/living) in sloc.loc)
continue
S = sloc
sloc.used = TRUE
break
if(S)
new /mob/living/silicon/robot/shell(get_turf(S))
if(captainless)
for(var/mob/dead/new_player/N in GLOB.player_list)
if(N.new_character)
to_chat(N, "<FONT color='red'>No Captain is present at the start of shift. Please follow the SOP available <b><a href='https://wiki.yogstation.net/wiki/Official:Disk_Procedure'>here</a></b> to secure the disk and assign an Acting Captain.")
CHECK_TICK
if(no_clerk)
SSjob.random_clerk_init()
if(no_chaplain)
SSjob.random_chapel_init()
/datum/controller/subsystem/ticker/proc/transfer_characters()
var/list/livings = list()
for(var/mob/dead/new_player/player in GLOB.mob_list)
var/mob/living = player.transfer_character()
if(living)
qdel(player)
living.notransform = TRUE
if(living.client)
var/datum/job/player_assigned_role = SSjob.GetJob(living.mind.assigned_role)
var/atom/movable/screen/splash/S = new(null, living.client, TRUE)
S.Fade(TRUE)
living.client.init_verbs()
player_assigned_role?.after_roundstart_spawn(living, living.client) //not all people who spawn get jobs, LIKE NUKIES!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
livings += living
if(livings.len)
addtimer(CALLBACK(src, PROC_REF(release_characters), livings), 30, TIMER_CLIENT_TIME)
/datum/controller/subsystem/ticker/proc/release_characters(list/livings)
for(var/I in livings)
var/mob/living/L = I
L.notransform = FALSE
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
var/m
if(selected_tip)
m = selected_tip
else
var/list/randomtips = world.file2list("strings/tips.txt")
randomtips += world.file2list("yogstation/strings/tips.txt")//Yogs -- Yogstips, mostly stuff about Clockcult as of March 2019
var/list/memetips = world.file2list("strings/sillytips.txt")
if(randomtips.len && prob(95))
m = pick(randomtips)
else if(memetips.len)
m = pick(memetips)
if(m)
to_chat(world, span_purple(examine_block("<b>Tip of the round: </b>[html_encode(m)]")))
/datum/controller/subsystem/ticker/proc/check_queue()
if(!queued_players.len)
return
var/hard_popcap = CONFIG_GET(number/hard_popcap)
//yogs start -- fixes queue when extreme is set but not hard
if(!hard_popcap)
hard_popcap = CONFIG_GET(number/extreme_popcap)
//yogs end
if(!hard_popcap)
listclearnulls(queued_players)
for (var/mob/dead/new_player/new_player in queued_players)
to_chat(new_player, span_userdanger("The alive players limit has been released!<br><a href='?src=[REF(new_player)];late_join=override'>[html_encode(">>Join Game<<")]</a>"))
SEND_SOUND(new_player, sound('sound/misc/notice1.ogg'))
GLOB.latejoin_menu.ui_interact(new_player)
queued_players.len = 0
queue_delay = 0
return
queue_delay++
var/mob/dead/new_player/next_in_line = queued_players[1]
switch(queue_delay)
if(5) //every 5 ticks check if there is a slot available
listclearnulls(queued_players)
if(living_player_count() < hard_popcap)
if(next_in_line && next_in_line.client)
to_chat(next_in_line, span_userdanger("A slot has opened! You have approximately 20 seconds to join. <a href='?src=[REF(next_in_line)];late_join=override'>\>\>Join Game\<\<</a>"))
SEND_SOUND(next_in_line, sound('sound/misc/notice1.ogg'))
next_in_line.ui_interact(next_in_line)
return
queued_players -= next_in_line //Client disconnected, remove he
queue_delay = 0 //No vacancy: restart timer
if(25 to INFINITY) //No response from the next in line when a vacancy exists, remove he
to_chat(next_in_line, span_danger("No response received. You have been removed from the line."))
queued_players -= next_in_line
queue_delay = 0
/datum/controller/subsystem/ticker/proc/check_maprotate()
if (!CONFIG_GET(flag/maprotation))
return
if(SSticker.maprotatechecked || SSmapping.next_map_config) //we already have a map set
return
//map rotate chance defaults to 75% of the length of the round (in minutes)
if (!prob((world.time/600)*CONFIG_GET(number/maprotationchancedelta)))
return
INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping/, maprotate))
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
/datum/controller/subsystem/ticker/proc/IsRoundInProgress()
return current_state == GAME_STATE_PLAYING
/datum/controller/subsystem/ticker/Recover()
current_state = SSticker.current_state
force_ending = SSticker.force_ending
hide_mode = SSticker.hide_mode
login_music = SSticker.login_music
round_end_sound = SSticker.round_end_sound
minds = SSticker.minds
delay_end = SSticker.delay_end
triai = SSticker.triai
tipped = SSticker.tipped
selected_tip = SSticker.selected_tip
timeLeft = SSticker.timeLeft
totalPlayers = SSticker.totalPlayers
totalPlayersReady = SSticker.totalPlayersReady
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
maprotatechecked = SSticker.maprotatechecked
round_start_time = SSticker.round_start_time
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
maprotatechecked = SSticker.maprotatechecked
switch (current_state)
if(GAME_STATE_SETTING_UP)
Master.SetRunLevel(RUNLEVEL_SETUP)
if(GAME_STATE_PLAYING)
Master.SetRunLevel(RUNLEVEL_GAME)
if(GAME_STATE_FINISHED)
Master.SetRunLevel(RUNLEVEL_POSTGAME)
/datum/controller/subsystem/ticker/proc/send_news_report()
var/news_message
var/news_source = "Nanotrasen News Network"
switch(news_report)
if(NUKE_SYNDICATE_BASE)
news_message = "In a daring raid, the heroic crew of [station_name()] detonated a nuclear device in the heart of a terrorist base."
if(STATION_DESTROYED_NUKE)
news_message = "We would like to reassure all employees that the reports of a Syndicate backed nuclear attack on [station_name()] are, in fact, a hoax. Have a secure day!"
if(STATION_EVACUATED)
news_message = "The crew of [station_name()] has been evacuated amid unconfirmed reports of enemy activity."
if(BLOB_WIN)
news_message = "[station_name()] was overcome by an unknown biological outbreak, killing all crew on board. Don't let it happen to you! Remember, a clean work station is a safe work station."
if(BLOB_NUKE)
news_message = "[station_name()] is currently undergoing decontanimation after a controlled burst of radiation was used to remove a biological ooze. All employees were safely evacuated prior, and are enjoying a relaxing vacation."
if(BLOB_DESTROYED)
news_message = "[station_name()] is currently undergoing decontamination procedures after the destruction of a biological hazard. As a reminder, any crew members experiencing cramps or bloating should report immediately to security for incineration."
if(CULT_ESCAPE)
news_message = "Security Alert: A group of religious fanatics have escaped from [station_name()]."
if(CULT_FAILURE)
news_message = "Following the dismantling of a restricted cult aboard [station_name()], we would like to remind all employees that worship outside of the Chapel is strictly prohibited, and cause for termination."
if(CULT_SUMMON)
news_message = "Company officials would like to clarify that [station_name()] was scheduled to be decommissioned following meteor damage earlier this year. Earlier reports of an unknowable eldritch horror were made in error."
if(NUKE_MISS)
news_message = "The Syndicate have bungled a terrorist attack [station_name()], detonating a nuclear weapon in empty space nearby."
if(OPERATIVES_KILLED)
news_message = "Repairs to [station_name()] are underway after an elite Syndicate death squad was wiped out by the crew."
if(OPERATIVE_SKIRMISH)
news_message = "A skirmish between security forces and Syndicate agents aboard [station_name()] ended with both sides bloodied but intact."
if(REVS_WIN)
news_message = "Company officials have reassured investors that despite a union led revolt aboard [station_name()] there will be no wage increases for workers."
if(REVS_LOSE)
news_message = "[station_name()] quickly put down a misguided attempt at mutiny. Remember, unionizing is illegal!"
if(WIZARD_KILLED)
news_message = "Tensions have flared with the Space Wizard Federation following the death of one of their members aboard [station_name()]."
if(STATION_NUKED)
news_message = "[station_name()] activated its self destruct device for unknown reasons. Attempts to clone the Captain so he can be arrested and executed are underway."
if(CLOCK_SUMMON)
news_message = "The garbled messages about hailing a mouse and strange energy readings from [station_name()] have been discovered to be an ill-advised, if thorough, prank by a clown."
if(CLOCK_SILICONS)
news_message = "The project started by [station_name()] to upgrade their silicon units with advanced equipment have been largely successful, though they have thus far refused to release schematics in a violation of company policy."
if(CLOCK_PROSELYTIZATION)
news_message = "The burst of energy released near [station_name()] has been confirmed as merely a test of a new weapon. However, due to an unexpected mechanical error, their communications system has been knocked offline."
if(SHUTTLE_HIJACK)
news_message = "During routine evacuation procedures, the emergency shuttle of [station_name()] had its navigation protocols corrupted and went off course, but was recovered shortly after."
if(news_message)
send2otherserver(news_source, news_message,"News_Report")
/datum/controller/subsystem/ticker/proc/GetTimeLeft()
if(isnull(SSticker.timeLeft))
return max(0, start_at - world.time)
return timeLeft
/datum/controller/subsystem/ticker/proc/SetTimeLeft(newtime)
if(newtime >= 0 && isnull(timeLeft)) //remember, negative means delayed
start_at = world.time + newtime
else
timeLeft = newtime
//Everyone who wanted to be an observer gets made one now
/datum/controller/subsystem/ticker/proc/create_observers()
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
//Break chain since this has a sleep input in it
addtimer(CALLBACK(player, TYPE_PROC_REF(/mob/dead/new_player, make_me_an_observer)), 1)
/datum/controller/subsystem/ticker/proc/load_mode()
var/mode = trim(file2text("data/mode.txt"))
if(mode)
GLOB.master_mode = mode
else
GLOB.master_mode = "extended"
log_game("Saved mode is '[GLOB.master_mode]'")
/datum/controller/subsystem/ticker/proc/save_mode(the_mode)
var/F = file("data/mode.txt")
fdel(F)
WRITE_FILE(F, the_mode)
/// Returns if either the master mode or the forced secret ruleset matches the mode name.
/datum/controller/subsystem/ticker/proc/is_mode(mode_name)
return GLOB.master_mode == mode_name || GLOB.secret_force_mode == mode_name
/datum/controller/subsystem/ticker/proc/SetRoundEndSound(the_sound)
set waitfor = FALSE
round_end_sound_sent = FALSE
round_end_sound = fcopy_rsc(the_sound)
for(var/thing in GLOB.clients)
var/client/C = thing
if (!C)
continue
C.Export("##action=load_rsc", round_end_sound)
round_end_sound_sent = TRUE
// yogs start - Mods can reboot when last ticket is closed
/datum/controller/subsystem/ticker/proc/Reboot(reason, end_string, delay, force = FALSE)
set waitfor = FALSE
if(usr && !force)
if(!check_rights(R_ADMIN, TRUE))
return
// yogs end
if(!delay)
delay = CONFIG_GET(number/round_end_countdown) * 10
var/skip_delay = check_rights(show_msg=FALSE)
if(delay_end && !skip_delay)
to_chat(world, span_boldannounce("An admin has delayed the round end."))
return
//yogs start - yogs tickets
if(GLOB.ahelp_tickets && GLOB.ahelp_tickets.ticketAmount)
var/list/adm = get_admin_counts(R_BAN)
var/list/activemins = adm["present"]
if(activemins.len > 0 && !force) // Ignore tickets if forced
to_chat(world, span_boldannounce("Not all tickets have been resolved. Server restart delayed."))
return
else
to_chat(world, span_boldannounce("Round ended, but there were still active tickets. Please submit a player complaint if you did not receive a response."))
//yogs end - yogs tickets
to_chat(world, span_boldannounce("Rebooting World in [DisplayTimeText(delay)]. [reason]"))
webhook_send_roundstatus("endgame") //yogs - webhook support
var/start_wait = world.time
UNTIL(round_end_sound_sent || (world.time - start_wait) > (delay * 2)) //don't wait forever
var/newdelay = (delay - (world.time - start_wait) - 10 SECONDS)
if(delay > 10 SECONDS) /// JJJJJJJJJJJJJJJJJJJJAAAAAAAAANNNNNNNNKKKKKKKKK
sleep(newdelay)
if(delay_end && !skip_delay)
to_chat(world, span_boldannounce("Reboot was cancelled by an admin."))
return
play_roundend()
if(newdelay > 0)
sleep(10 SECONDS)
else
sleep(delay - (world.time - start_wait))
if(end_string)
end_state = end_string
var/statspage = CONFIG_GET(string/roundstatsurl)
var/gamelogloc = CONFIG_GET(string/gamelogurl)
if(statspage)
to_chat(world, span_info("Round statistics and logs can be viewed <a href=\"[statspage][GLOB.round_id]\">at this website!</a>"))
else if(gamelogloc)
to_chat(world, span_info("Round logs can be located <a href=\"[gamelogloc]\">at this website!</a>"))
log_game(span_boldannounce("Rebooting World. [reason]"))
world.Reboot()
/datum/controller/subsystem/ticker/proc/play_roundend()
if(!round_end_sound)
round_end_sound = pick(\
'sound/roundend/newroundsexy.ogg',
'sound/roundend/apcdestroyed.ogg',
'sound/roundend/bangindonk.ogg',
'sound/roundend/leavingtg.ogg',
'sound/roundend/its_only_game.ogg',
'sound/roundend/yeehaw.ogg',
'yogstation/sound/roundend/aww_shit.ogg', // yogs -- adds "Aww shit, here we go again"
'yogstation/sound/roundend/ass_blast_usa.ogg', // yogs -- adds "Ass Blast USA" vox
'sound/roundend/itshappening.ogg',
'yogstation/sound/roundend/bamboozeled.ogg',// yogs -- adds "We've been tricked, we've been backstabed, and we've quite possibly been bamboozeled.
'sound/roundend/yoggers.ogg', //yogs -- adds yogurt saying "Can we get a yoggers in the chat?"
'sound/roundend/it_never_happened.ogg', // jonathan frakes telling you you're wrong
'sound/roundend/disappointed.ogg',
'sound/roundend/scrunglartiy.ogg',
'sound/roundend/windowsxp.ogg',
'sound/roundend/spacemanthing.ogg',
'sound/roundend/no_more_cuss_words.ogg'\
)
SEND_SOUND(world, sound(round_end_sound))
SStitle.fadeout()
text2file(login_music, "data/last_round_lobby_music.txt")
/datum/controller/subsystem/ticker/Shutdown()
gather_newscaster() //called here so we ensure the log is created even upon admin reboot