mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-01-16 04:23:34 +00:00
* TGUI upgrade project: Port initial TGUI 4.0 changes from TG (#23440) * tgui4.0 * bugfix for un-interaction * fix for input not resetting on close * NTOS restore * fix all interfaces having scrollbars, fix colours * bundle update * dep bumps * bumps the deps as much as possible * button regression fix * TGUI test map rev 1 * fix theme and some component regression * fix login screen regression * fixes regression with uplink cart * bundle * fix regressions * fix the input issue, again * regression fixes, stylesheet edition, hash restore * fixes GPS BSOD * draggable control regression * dev server dep regression * byondUI regression fix * section regression fix * fix secure storage weirdness * Fixed mining vendor scrolling * Arthri review pass 1 * mining vendor double-scroll * fix for RPD, AirAlarm, and Radio * arthri review pass 2 * arthri review pass 3 * sanitize var setting, fix colours, delete unused file * adds CI to branch * god damn AI making random changes * I should remember to compile when changes are made * fixes scrollbar issues * fix camera console, fix flexGrow for sections * CI fix * nanomap fix * Update code/modules/atmospherics/machinery/airalarm.dm * restore margin * style semicolon * TGUIv4 - Moves TGUI Window Sizing from DM to JS (#23524) * Initial commit * Transferring the remaining windows to JS * Resizing part 1 * Resizing part 2 * Some reverts and polish * Forgot that * undeployed nuke window size --------- Co-authored-by: Aylong <alexanderkitsa@gmail.com> * TGUIv4: Remove force_open (#23537) * First state fixes * Removes "force_open" * TGUIv4 - TGUI Version 4.1 (#23547) * Reduced Logging Changes * setSharedState early return for non-interactive UI * TGUI version bump to 4.1 * Fix sending updates for non-interactive UIs * Scalablue UI Stylesheet Changes * CSS Tgui Bundle * JavaScript TGUI Style Changes * Update tgui/packages/tgui/styles/base.scss Co-authored-by: Aylong <69762909+Aylong220@users.noreply.github.com> * Additional changes to Input and Button scalability * TGUI logging proc update * Indentation fix for logging proc * Update code/__HELPERS/_logging.dm Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * Additional log_tgui changes --------- Co-authored-by: Aylong <69762909+Aylong220@users.noreply.github.com> Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * dev server hotfix (#23584) * ui_interact object constructor argument check (#23594) * feat: add smart asset cache, add CDN support for assets, properly group assets (#23585) * feat: add smart asset cache, add CDN support for assets, properly group assets * fix: make tgui actually work * fix: keeping local name for `tgui.bundle.js` and `tgui.bundle.css` for debug and dev server functioning * fix: make `fontawesome` assets finally work * fix: make proper identation for `if` * fix: add `resolveAsset` to `NanoMap.js` * refactor: update `claw_game` and `chess` to new asset framework * refactor/tweak: don't use string concatenation for browser `content` and `head_content`, use list instead. Don't use `common.css` for paper UI, keep local name for stamp image assets, replace hard ref of browser to `atom` (rare case) to `UID` * refactor: remove redundant debug logs * refactor: remove space betwee `if` and `(` * refactor: remove one more redundant log, properly reload UI resources * rafactor: change names of asset files * fix: adjust existing UIs to properly use assets * fix: properly pass args to to `onclose` proc * Update code/modules/asset_cache/transports/asset_transport.dm Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * Update code/modules/asset_cache/transports/asset_transport.dm Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * TGUI 4.2: TGchat + Refactor Asset Delivery + Yarn Berry (#23643) * tgchat * Little cleanup * Refactor Asset Delivery (Part 2) * Little tweaks * fix code styling issues * fix file name duplications * Browser window options fix @gaxeer * transfer valve fixes * yarn berry initial * vsc tasks * yarn berry working * node 20 * fix dev server (lol wrong yarn) * bloody regressions * fixes that damn scrolltracking bug * Some tweaks and flexGrow deletions It still doesn't work as it should, which makes me nervous * remove unneeded deps * 514 regression fix * change stuff to not conflict with other servers * name it as requested --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * revert test_TGUI map addition * TGUI 4.3: Stack, Webpack 5 & UI Tweaks, Yarn 3.6.4, TS support, Jest (#23677) * Stack & UI's Refactor Almost final, polish required Stack & UI's Refactor (Part 1) Stack & UI's Refactor (Part 2) Stack & UI's Refactor (Part 3) * Prettier Maybe bad * ClearChat & some tweaks * Adds a Chat Reliability Layer https://github.com/tgstation/tgstation/pull/79479 * Fix chat BSOD https://github.com/tgstation/tgstation/pull/79821 * WebPack 5 (Didn't work) I hate this shit * Yarn 3.6.4 * make it all work * revert snowflake fix * Stories * adds TS support * re-enable test and prettier * update yarn sdk's * Fix some box regression * ping/reply * Fixes regressions and some things * Zebra and Fix chat button transfer +rebuild * make VSC use the proper local typescript lib * Popper Tooltips https://github.com/tgstation/tgstation/pull/58980 * Popper Tooltips performancy fixes * Dropdown v2 https://github.com/tgstation/tgstation/pull/75164 Without Icon.tsx * BB test map * run build --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * we moved these * change test values * ignore scss files * change hash function * TGUIv4: TGchat fix traitor codeword highlighting (#23720) * Codeword highlighting for TGUIv4 * null safeties * Apply suggestions from code review Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> --------- Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * TGUIv4 - updates standalone build tooling (#23721) * swap `xxhash64` for `md4` * remove hash function * remove old file * TGUI 4.3+4.4: Newscaster fix, `inline-block` replacements and other fixes (#23722) * TGUI 4.3 fixes Newscaster fix + inline-blocks * Be gone Box * Mod Chat -> Mentor Chat * Some warning filters * Rebuild * Delete tgui-common.bundle.js * please work * try it like this * 4.3.1 * 4.3.2 * fix * i forgot to build * 4.4.0 * give me more diff details please so I can fix you * its not binary pls git * test without svgs * removes an un-needed SVG file * inlined SVGs test * fix code styling * comment these --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * TGUIv4 - Removes resizable and improves drag code (#23719) * Initial commit * bundle rebuild * Fixes Radio Sizing * Updates Radio sizing to fix wrapping issues * fix dev server --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * fix hotkeys not being passed to BYOND * Apply suggestions from code review * bump deps * forgot these deps * ci fix * ChemDispenser fixes and HoloControl cooldown * Station Traits TGUI +Rebuild * CI * fix CI for real this time * I have no idea * fix station trait panel * Update code/controllers/subsystem/SSping.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/controllers/subsystem/SSticker.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/controllers/subsystem/SSping.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * deterministic module IDs * TGUI fixes. Again... (#23762) * TGUI Fixes: Part 1 ORM icon for reinforced glass now showing correctly Mining vendor moved to Stack Exofab UI buttons descended from the heavens to the earth * TGUI Fixes: Part 2 DestTagger now works, forgot import LabeledList PowerMonitor graph displays correct Request Console updated to 2.0, BUT, problem with message priority, it was there before the changes, maybe even before the project, I'll have to check it out * TGUI Fixes: Part 2 Fixed `onEnter` input Fixed Cloning Console storage (Im dumbass) Some windows resizing Fixed bad picture in security and medical records console Fixed scrollable dropdown Added translucent color for buttons * Fucking uplink DONE, and Button.Input fix * NT Recruiter and Uplink polish * Del LabeledList from dest. tagger * ChemMaster and OreRedemption log deletion * Update test_tiny.dmm * Translucent button story * Filter for SQL error * del: `log_debug` in `hear_say.dm` * Some little fixes and SpecMenu Stack * Panel.js: mx -> mr * fix ghost spawners * null check --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * fix material name in protolathe * fix various UI interacts * card machine runtime fix * remove legacy folders * TGUIv4 - TGchat Theme Adjustments (#23772) * Initial commit * Changes rule grouping * Robot class style changes and webpack config * Reverts webpack/font changes and subs robot font * revert a state changed in error * fix uninteractable UIs due to chunky fingers * TGUIv4: More fixes, tweaks and fixes for tweaks (#23781) * I fuckin hate PDA * Vending * Add `|` to highlight splitters and some light mode tweaks * Teleporter rewrite * APC and AiAirlock (God forgive me) * clarify highlight syntax --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * TGUIv4 - Abductor Experiment Machine TGUI (#23776) * Experiment Console TGUI * Update for TGUIv4 * UI Size adjustment * ui state and ghost attack update * remove this --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * fix * TGUIv4 - Adjustments, Fixes, and Tweaks (#23785) * Photocopier layout adjustments * Autolathe Text and Button spacing adjustment * Hotkey fix for moving bug * Removes rule sets for visited links * Fixes ordering multiples on cargo console * fix possible wonky json payloads * fix admin log input list stuff * TGUIv4: Reconnect fix and some other fixes + tweaks (#23790) * Fix reconnecting and transfer it to TSX * RPD polish * RCD, AccessList, GuestPass * Focus reset fix when camera switched. God forgive me again... * Purple box and identation fixes For votes and health scanner * ChemMaster and ChemDispenser ChemMaster - translucent pills buttons and no grow produce section ChemDuspenser - dynamic height * ShuttleManipulator Tabs * RCD buttons * make input bar mode more prominent * fix chem master icons * tab fix * Properly sanitizes loaded messages in tgui chat * TGUIv4 - Adjustments, Fixes, and Tweaks v2 (#23795) * Fixes icon spacing for nanobank tabs * Tweaks Dropdown styling * Fixes sending stuff to old chat * OpenDream TGUI fix * Resizes Station Traits window * Adjusts health analyzer messages for chat tabs * Revert "Properly sanitizes loaded messages in tgui chat" This reverts commit4c32a7094a. * Revert "Revert "Properly sanitizes loaded messages in tgui chat"" This reverts commit24afa55922. * Changes how tgui handles static data * Reenable no-undef rule. Enable Format On Save for VSCode (#23803) * Enable Format On Save for VSCode * Re-enable and fix no-undef --------- Co-authored-by: Arthri <41360489+a@users.noreply.github.com> * pda scanner message improvements * fix mentor/admin PMs being sent to the "unsorted" category * fix garbled custom vote messages * moves cyborg analyser to use chatboxes * fixes regression * Prevent F5 reload * TGUIv4: Themes polish and some new ones + misc fixes (#23814) * Little themes tweaks * NTOS chat theme * Update tgui-panel.bundle.js * Capitalize themes * Paradise theme v1.0 * Little StationTraitsPanel cleanup * Update member_content.dmi * NoCapitalize * Rebuild * Delete Paradise.scss * Create paradise.scss * Forgot * Rewritten Security Records Console For Jesus fucking christ... * Vending and Wires sizes tweaks * ThermoMachine * Syndicate Theme * Little darker * Rewritten Medical Records Console * PDA Nanobank fix * Syndicate Theme Darker * Rebuild TGUI * Wires section grow fix * fixes the enshittified paradise icon * Sirryan+Warrior reviews * bloody define comments * comment correction * unprivate these * errant . begone (how did this not cause a compiler error) * TGUIv4 - Adjustments, Fixes, and Tweaks v3 (#23807) * Fixes ERT Manager silenced message function * Adjusts progress bar on ExosuitFabricator * Sets Dropdown selected to align left * Removes the use of self_state select interfaces * Exosuit Fabricator style adjustments * fix sextractor UI * remove empty tochat string * fix human air alarm interactions * fix modals * add F12/IEChooser --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> --------- Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Aylong <alexanderkitsa@gmail.com> Co-authored-by: Aylong <69762909+Aylong220@users.noreply.github.com> Co-authored-by: Gaxeer <44334376+Gaxeer@users.noreply.github.com> Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Co-authored-by: Arthri <41360489+Arthri@users.noreply.github.com> Co-authored-by: Arthri <41360489+a@users.noreply.github.com>
870 lines
33 KiB
Plaintext
870 lines
33 KiB
Plaintext
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
|
|
offline_implications = "The game is no longer aware of when the round ends. Immediate server restart recommended."
|
|
cpu_display = SS_CPUDISPLAY_LOW
|
|
wait = 1 SECONDS
|
|
|
|
/// Time the game should start, relative to world.time
|
|
var/round_start_time = 0
|
|
/// Time that the round started
|
|
var/time_game_started = 0
|
|
/// Default timeout for if world.Reboot() doesnt have a time specified
|
|
var/const/restart_timeout = 75 SECONDS
|
|
/// Current status of the game. See code\__DEFINES\game.dm
|
|
var/current_state = GAME_STATE_STARTUP
|
|
/// Do we want to force-start as soon as we can
|
|
var/force_start = FALSE
|
|
/// Do we want to force-end as soon as we can
|
|
var/force_ending = FALSE
|
|
/// Leave here at FALSE ! setup() will take care of it when needed for Secret mode -walter0o
|
|
var/hide_mode = FALSE
|
|
/// Our current game mode
|
|
var/datum/game_mode/mode = null
|
|
/// The current pick of lobby music played in the lobby
|
|
var/login_music
|
|
/// List of all minds in the game. Used for objective tracking
|
|
var/list/datum/mind/minds = list()
|
|
/// icon_state the chaplain has chosen for his bible
|
|
var/Bible_icon_state
|
|
/// item_state the chaplain has chosen for his bible
|
|
var/Bible_item_state
|
|
/// Name of the bible
|
|
var/Bible_name
|
|
/// Name of the bible deity
|
|
var/Bible_deity_name
|
|
/// Cult data. Here instead of cult for adminbus purposes
|
|
var/datum/cult_info/cultdat = null
|
|
/// If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
|
|
var/random_players = FALSE
|
|
/// Did we broadcast the tip of the round yet?
|
|
var/tipped = FALSE
|
|
/// What will be the tip of the round?
|
|
var/selected_tip
|
|
/// This is used for calculations for the statpanel
|
|
var/pregame_timeleft
|
|
/// If set to TRUE, the round will not restart on it's own
|
|
var/delay_end = FALSE
|
|
/// Global holder for triple AI mode
|
|
var/triai = FALSE
|
|
/// Holder for inital autotransfer vote timer
|
|
var/next_autotransfer = 0
|
|
/// Used for station explosion cinematic
|
|
var/obj/screen/cinematic = null
|
|
/// Spam Prevention. Announce round end only once.
|
|
var/round_end_announced = FALSE
|
|
/// Is the ticker currently processing? If FALSE, roundstart is delayed
|
|
var/ticker_going = TRUE
|
|
/// Gamemode result (For things like cult or nukies which can end multiple ways)
|
|
var/mode_result = "undefined"
|
|
/// Server end state (Did we end properly or reboot or nuke or what)
|
|
var/end_state = "undefined"
|
|
/// Time the real reboot kicks in
|
|
var/real_reboot_time = 0
|
|
/// Datum used to generate the end of round scoreboard.
|
|
var/datum/scoreboard/score = null
|
|
/// List of ckeys who had antag rolling issues flagged
|
|
var/list/flagged_antag_rollers = list()
|
|
|
|
/datum/controller/subsystem/ticker/Initialize()
|
|
login_music = pick(\
|
|
'sound/music/thunderdome.ogg',\
|
|
'sound/music/space.ogg',\
|
|
'sound/music/title1.ogg',\
|
|
'sound/music/title2.ogg',\
|
|
'sound/music/title3.ogg',)
|
|
|
|
|
|
/datum/controller/subsystem/ticker/fire()
|
|
switch(current_state)
|
|
if(GAME_STATE_STARTUP)
|
|
// This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails
|
|
round_start_time = world.time + (GLOB.configuration.general.lobby_time SECONDS)
|
|
to_chat(world, "<B><span class='darkmblue'>Welcome to the pre-game lobby!</span></B>")
|
|
to_chat(world, "Please, setup your character and select ready. Game will start in [GLOB.configuration.general.lobby_time] seconds")
|
|
current_state = GAME_STATE_PREGAME
|
|
fire() // TG says this is a good idea
|
|
for(var/mob/new_player/N in GLOB.player_list)
|
|
if(N.client)
|
|
N.new_player_panel_proc() // to enable the observe option
|
|
if(GAME_STATE_PREGAME)
|
|
if(!SSticker.ticker_going) // This has to be referenced like this, and I dont know why. If you dont put SSticker. it will break
|
|
return
|
|
|
|
// This is so we dont have sleeps in controllers, because that is a bad, bad thing
|
|
if(!delay_end)
|
|
pregame_timeleft = max(0, round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed
|
|
else
|
|
pregame_timeleft = max(0, pregame_timeleft - wait) // If roundstart was delayed, we should resume the countdown where it left off
|
|
|
|
if(pregame_timeleft <= 600 && !tipped) // 60 seconds
|
|
send_tip_of_the_round()
|
|
tipped = TRUE
|
|
|
|
if(pregame_timeleft <= 0 || force_start)
|
|
current_state = GAME_STATE_SETTING_UP
|
|
Master.SetRunLevel(RUNLEVEL_SETUP)
|
|
if(GAME_STATE_SETTING_UP)
|
|
if(!setup()) // Setup failed
|
|
current_state = GAME_STATE_STARTUP
|
|
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
|
if(GAME_STATE_PLAYING)
|
|
delay_end = FALSE // reset this in case round start was delayed
|
|
mode.process()
|
|
|
|
if(world.time > next_autotransfer)
|
|
SSvote.start_vote(new /datum/vote/crew_transfer)
|
|
next_autotransfer = world.time + GLOB.configuration.vote.autotransfer_interval_time
|
|
|
|
var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked
|
|
if(GLOB.configuration.gamemode.disable_certain_round_early_end)
|
|
mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result
|
|
else
|
|
game_finished |= mode.check_finished()
|
|
if(game_finished || force_ending)
|
|
current_state = GAME_STATE_FINISHED
|
|
if(GAME_STATE_FINISHED)
|
|
if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME && !mode.station_was_nuked)
|
|
event_blackbox(outcome = ROUND_END_CREW_TRANSFER)
|
|
current_state = GAME_STATE_FINISHED
|
|
Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know
|
|
auto_toggle_ooc(TRUE) // Turn it on
|
|
declare_completion()
|
|
addtimer(CALLBACK(src, PROC_REF(call_reboot)), 5 SECONDS)
|
|
// Start a map vote IF
|
|
// - Map rotate doesnt have a mode for today and map voting is enabled
|
|
// - Map rotate has a mode for the day and it ISNT full random
|
|
if(((!SSmaprotate.setup_done) && GLOB.configuration.vote.enable_map_voting) || (SSmaprotate.setup_done && (SSmaprotate.rotation_mode != MAPROTATION_MODE_FULL_RANDOM)))
|
|
SSvote.start_vote(new /datum/vote/map)
|
|
else
|
|
// Pick random map
|
|
var/list/pickable_types = list()
|
|
for(var/x in subtypesof(/datum/map))
|
|
var/datum/map/M = x
|
|
if(initial(M.voteable) && length(GLOB.clients) >= initial(M.min_players_random))
|
|
pickable_types += M
|
|
|
|
var/datum/map/target_map = pick(pickable_types)
|
|
SSmapping.next_map = new target_map
|
|
to_chat(world, "<span class='interface'>Map for next round: [SSmapping.next_map.fluff_name] ([SSmapping.next_map.technical_name])</span>")
|
|
|
|
/datum/controller/subsystem/ticker/proc/call_reboot()
|
|
if(mode.station_was_nuked)
|
|
reboot_helper("Station destroyed by Nuclear Device.", "nuke")
|
|
else
|
|
reboot_helper("Round ended.", "proper completion")
|
|
|
|
/datum/controller/subsystem/ticker/proc/setup()
|
|
cultdat = setupcult()
|
|
score = new()
|
|
|
|
// Create and announce mode
|
|
if(GLOB.master_mode == "secret")
|
|
hide_mode = TRUE
|
|
|
|
var/list/datum/game_mode/runnable_modes
|
|
|
|
if(GLOB.master_mode == "random" || GLOB.master_mode == "secret")
|
|
runnable_modes = GLOB.configuration.gamemode.get_runnable_modes()
|
|
if(!length(runnable_modes))
|
|
to_chat(world, "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby.")
|
|
force_start = FALSE
|
|
current_state = GAME_STATE_PREGAME
|
|
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
|
return FALSE
|
|
if(GLOB.secret_force_mode != "secret")
|
|
var/datum/game_mode/M = GLOB.configuration.gamemode.pick_mode(GLOB.secret_force_mode)
|
|
if(M.can_start())
|
|
mode = GLOB.configuration.gamemode.pick_mode(GLOB.secret_force_mode)
|
|
SSjobs.ResetOccupations()
|
|
if(!mode)
|
|
mode = pickweight(runnable_modes)
|
|
if(mode)
|
|
var/mtype = mode.type
|
|
mode = new mtype
|
|
else
|
|
mode = GLOB.configuration.gamemode.pick_mode(GLOB.master_mode)
|
|
|
|
if(!mode.can_start())
|
|
to_chat(world, "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.")
|
|
mode = null
|
|
current_state = GAME_STATE_PREGAME
|
|
force_start = FALSE
|
|
SSjobs.ResetOccupations()
|
|
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
|
return FALSE
|
|
|
|
// Randomise characters now. This avoids rare cases where a human is set as a changeling then they randomise to an IPC
|
|
for(var/mob/new_player/player in GLOB.player_list)
|
|
if(player.client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
|
|
player.client.prefs.load_random_character_slot(player.client)
|
|
|
|
// Lets check if people who ready should or shouldnt be
|
|
for(var/mob/new_player/P in GLOB.player_list)
|
|
// Not logged in
|
|
if(!P.client)
|
|
continue
|
|
// Not ready
|
|
if(!P.ready)
|
|
continue
|
|
// Not set to return if nothing available
|
|
if(P.client.prefs.active_character.alternate_option != RETURN_TO_LOBBY)
|
|
continue
|
|
|
|
var/has_antags = (length(P.client.prefs.be_special) > 0)
|
|
if(!P.client.prefs.active_character.check_any_job())
|
|
to_chat(P, "<span class='danger'>You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.</span>")
|
|
if(has_antags)
|
|
// We add these to a list so we can deal with them as a batch later
|
|
// A lot of DB tracking stuff needs doing, so we may as well async it
|
|
flagged_antag_rollers |= P.ckey
|
|
|
|
P.ready = FALSE
|
|
|
|
//Configure mode and assign player to special mode stuff
|
|
mode.pre_pre_setup()
|
|
var/can_continue = FALSE
|
|
can_continue = mode.pre_setup() //Setup special modes. This also does the antag fishing checks.
|
|
|
|
if(!can_continue)
|
|
QDEL_NULL(mode)
|
|
to_chat(world, "<B>Error setting up [GLOB.master_mode].</B> Reverting to pre-game lobby.")
|
|
current_state = GAME_STATE_PREGAME
|
|
force_start = FALSE
|
|
SSjobs.ResetOccupations()
|
|
Master.SetRunLevel(RUNLEVEL_LOBBY)
|
|
return FALSE
|
|
|
|
// Enable highpop slots just before we distribute jobs.
|
|
var/playercount = length(GLOB.clients)
|
|
var/highpop_trigger = 80
|
|
|
|
if(playercount >= highpop_trigger)
|
|
log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - loading highpop job config")
|
|
SSjobs.LoadJobs(TRUE)
|
|
else
|
|
log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config")
|
|
|
|
SSjobs.DivideOccupations() //Distribute jobs
|
|
|
|
if(hide_mode)
|
|
var/list/modes = new
|
|
for(var/datum/game_mode/M in runnable_modes)
|
|
modes += M.name
|
|
modes = sortList(modes)
|
|
to_chat(world, "<B>The current game mode is - Secret!</B>")
|
|
to_chat(world, "<B>Possibilities:</B> [english_list(modes)]")
|
|
else
|
|
mode.announce()
|
|
|
|
// Behold, a rough way of figuring out what takes 10 years
|
|
var/watch = start_watch()
|
|
create_characters() // Create player characters and transfer clients
|
|
log_debug("Creating characters took [stop_watch(watch)]s")
|
|
|
|
// Gather everyones minds
|
|
for(var/mob/living/player in GLOB.player_list)
|
|
if(player.mind)
|
|
minds += player.mind
|
|
|
|
watch = start_watch()
|
|
equip_characters() // Apply outfits and loadouts to the characters
|
|
log_debug("Equipping characters took [stop_watch(watch)]s")
|
|
|
|
watch = start_watch()
|
|
GLOB.data_core.manifest() // Create the manifest
|
|
log_debug("Manifest creation took [stop_watch(watch)]s")
|
|
SEND_SIGNAL(src, COMSIG_TICKER_ROUND_STARTING, world.time)
|
|
|
|
// Update the MC and state to game playing
|
|
current_state = GAME_STATE_PLAYING
|
|
Master.SetRunLevel(RUNLEVEL_GAME)
|
|
|
|
// Generate the list of empty playable AI cores in the world
|
|
for(var/obj/effect/landmark/start/ai/A in GLOB.landmarks_list)
|
|
if(locate(/mob/living) in get_turf(A))
|
|
continue
|
|
GLOB.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(A))
|
|
|
|
|
|
// Setup pregenerated newsfeeds
|
|
setup_news_feeds()
|
|
|
|
// Generate code phrases and responses
|
|
if(!GLOB.syndicate_code_phrase)
|
|
var/temp_syndicate_code_phrase = generate_code_phrase(return_list = TRUE)
|
|
|
|
var/codewords = jointext(temp_syndicate_code_phrase, "|")
|
|
var/regex/codeword_match = new("([codewords])", "ig")
|
|
|
|
GLOB.syndicate_code_phrase_regex = codeword_match
|
|
temp_syndicate_code_phrase = jointext(temp_syndicate_code_phrase, ", ")
|
|
GLOB.syndicate_code_phrase = temp_syndicate_code_phrase
|
|
|
|
|
|
if(!GLOB.syndicate_code_response)
|
|
var/temp_syndicate_code_response = generate_code_phrase(return_list = TRUE)
|
|
|
|
var/codewords = jointext(temp_syndicate_code_response, "|")
|
|
var/regex/codeword_match = new("([codewords])", "ig")
|
|
|
|
GLOB.syndicate_code_response_regex = codeword_match
|
|
temp_syndicate_code_response = jointext(temp_syndicate_code_response, ", ")
|
|
GLOB.syndicate_code_response = temp_syndicate_code_response
|
|
|
|
// Run post setup stuff
|
|
mode.post_setup()
|
|
|
|
// Delete starting landmarks (not AI ones because we need those for AI-ize)
|
|
for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
|
|
if(!istype(S, /obj/effect/landmark/start/ai))
|
|
qdel(S)
|
|
|
|
SSdbcore.SetRoundStart()
|
|
to_chat(world, "<span class='darkmblue'><B>Enjoy the game!</B></span>")
|
|
SEND_SOUND(world, sound('sound/AI/welcome.ogg'))
|
|
|
|
if(SSholiday.holidays)
|
|
to_chat(world, "<span class='darkmblue'>and...</span>")
|
|
for(var/holidayname in SSholiday.holidays)
|
|
var/datum/holiday/holiday = SSholiday.holidays[holidayname]
|
|
to_chat(world, "<h4>[holiday.greet()]</h4>")
|
|
|
|
GLOB.discord_manager.send2discord_simple_noadmins("**\[Info]** Round has started")
|
|
auto_toggle_ooc(FALSE) // Turn it off
|
|
time_game_started = world.time
|
|
|
|
// Sets the auto shuttle vote to happen after the config duration
|
|
next_autotransfer = world.time + GLOB.configuration.vote.autotransfer_initial_time
|
|
|
|
for(var/mob/new_player/N in GLOB.mob_list)
|
|
if(N.client)
|
|
N.new_player_panel_proc()
|
|
|
|
SSnightshift.check_nightshift(TRUE)
|
|
|
|
#ifdef UNIT_TESTS
|
|
// Run map tests first in case unit tests futz with map state
|
|
GLOB.test_runner.RunMap()
|
|
GLOB.test_runner.Run()
|
|
#endif
|
|
|
|
// Do this 10 second after roundstart because of roundstart lag, and make it more visible
|
|
addtimer(CALLBACK(src, PROC_REF(handle_antagfishing_reporting)), 10 SECONDS)
|
|
return TRUE
|
|
|
|
|
|
/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(nuke_site = NUKE_SITE_ON_STATION, override = null)
|
|
if(cinematic)
|
|
return //already a cinematic in progress!
|
|
|
|
auto_toggle_ooc(TRUE) // Turn it on
|
|
//initialise our cinematic screen object
|
|
cinematic = new /obj/screen(src)
|
|
cinematic.icon = 'icons/effects/station_explosion.dmi'
|
|
cinematic.icon_state = "station_intact"
|
|
cinematic.layer = 21
|
|
cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
cinematic.screen_loc = "1,1"
|
|
|
|
if(nuke_site == NUKE_SITE_ON_STATION)
|
|
// Kill everyone on z-level 1 except for mobs in freezers and
|
|
// malfunctioning AIs.
|
|
for(var/mob/M in GLOB.mob_list)
|
|
if(M.stat != DEAD)
|
|
var/turf/T = get_turf(M)
|
|
if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer) && !(issilicon(M) && override == "AI malfunction"))
|
|
to_chat(M, "<span class='danger'><B>The blast wave from the explosion tears you atom from atom!</B></span>")
|
|
var/mob/ghost = M.ghostize()
|
|
M.dust() //no mercy
|
|
if(ghost && ghost.client) //Play the victims an uninterrupted cinematic.
|
|
ghost.client.screen += cinematic
|
|
CHECK_TICK
|
|
if(M && M.client) //Play the survivors a cinematic.
|
|
M.client.screen += cinematic
|
|
else
|
|
for(var/mob/M in GLOB.mob_list)
|
|
if(M.client)
|
|
M.client.screen += cinematic //show every client the cinematic
|
|
|
|
switch(nuke_site)
|
|
//Now animate the cinematic
|
|
if(NUKE_SITE_ON_STATION)
|
|
// 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)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.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)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
|
|
cinematic.icon_state = "summary_malf"
|
|
else //Station nuked (nuke,explosion,summary)
|
|
flick("intro_nuke", cinematic)
|
|
sleep(35)
|
|
flick("station_explode_fade_red", cinematic)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
|
|
cinematic.icon_state = "summary_selfdes"
|
|
|
|
if(NUKE_SITE_ON_STATION_ZLEVEL)
|
|
// 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)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
|
|
flick("station_intact_fade_red", cinematic)
|
|
cinematic.icon_state = "summary_nukefail"
|
|
if("fake") //The round isn't over, we're just freaking people out for fun
|
|
flick("intro_nuke", cinematic)
|
|
sleep(35)
|
|
SEND_SOUND(world, sound('sound/items/bikehorn.ogg'))
|
|
flick("summary_selfdes", cinematic)
|
|
else
|
|
flick("intro_nuke", cinematic)
|
|
sleep(35)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
|
|
if(NUKE_SITE_OFF_STATION_ZLEVEL, NUKE_SITE_INVALID)
|
|
// nuke was nowhere nearby
|
|
// TODO: a really distant explosion animation
|
|
sleep(50)
|
|
SEND_SOUND(world, sound('sound/effects/explosion_distant.ogg'))
|
|
|
|
//If its actually the end of the round, wait for it to end.
|
|
//Otherwise if its a verb it will continue on afterwards.
|
|
spawn(300)
|
|
QDEL_NULL(cinematic) //end the cinematic
|
|
|
|
|
|
|
|
/datum/controller/subsystem/ticker/proc/create_characters()
|
|
for(var/mob/new_player/player in GLOB.player_list)
|
|
if(player.ready && player.mind)
|
|
if(player.mind.assigned_role == "AI")
|
|
player.close_spawn_windows()
|
|
var/mob/living/silicon/ai/ai_character = player.AIize()
|
|
ai_character.moveToAILandmark()
|
|
else if(!player.mind.assigned_role)
|
|
continue
|
|
else
|
|
player.create_character()
|
|
qdel(player)
|
|
|
|
/datum/controller/subsystem/ticker/proc/equip_characters()
|
|
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
|
if(player && player.mind && player.mind.assigned_role && player.mind.assigned_role != player.mind.special_role)
|
|
SSjobs.AssignRank(player, player.mind.assigned_role, FALSE)
|
|
SSjobs.EquipRank(player, player.mind.assigned_role, FALSE)
|
|
equip_cuis(player)
|
|
|
|
/datum/controller/subsystem/ticker/proc/equip_cuis(mob/living/carbon/human/H)
|
|
if(!H.client)
|
|
return // If they are spawning without a client (somehow), they *cant* have a CUI list
|
|
for(var/datum/custom_user_item/cui in H.client.cui_entries)
|
|
// Skip items with invalid character names
|
|
if((cui.characer_name != H.real_name) && !cui.all_characters_allowed)
|
|
continue
|
|
|
|
var/ok = FALSE
|
|
|
|
if(!cui.all_jobs_allowed)
|
|
var/alt_blocked = FALSE
|
|
if(H.mind.role_alt_title)
|
|
if(!(H.mind.role_alt_title in cui.allowed_jobs))
|
|
alt_blocked = TRUE
|
|
if(!(H.mind.assigned_role in cui.allowed_jobs) || alt_blocked)
|
|
continue
|
|
|
|
var/obj/item/I = new cui.object_typepath()
|
|
var/name_override = cui.item_name_override
|
|
var/desc_override = cui.item_desc_override
|
|
|
|
if(name_override)
|
|
I.name = name_override
|
|
if(desc_override)
|
|
I.desc = desc_override
|
|
|
|
if(isstorage(H.back)) // Try to place it in something on the mob's back
|
|
var/obj/item/storage/S = H.back
|
|
if(length(S.contents) < S.storage_slots)
|
|
I.forceMove(H.back)
|
|
ok = TRUE
|
|
to_chat(H, "<span class='notice'>Your [I.name] has been added to your [H.back.name].</span>")
|
|
|
|
if(!ok)
|
|
for(var/obj/item/storage/S in H.contents) // Try to place it in any item that can store stuff, on the mob.
|
|
if(length(S.contents) < S.storage_slots)
|
|
I.forceMove(S)
|
|
ok = TRUE
|
|
to_chat(H, "<span class='notice'>Your [I.name] has been added to your [S.name].</span>")
|
|
break
|
|
|
|
if(!ok) // Finally, since everything else failed, place it on the ground
|
|
var/turf/T = get_turf(H)
|
|
if(T)
|
|
I.forceMove(T)
|
|
to_chat(H, "<span class='notice'>Your [I.name] is on the [T.name] below you.</span>")
|
|
else
|
|
to_chat(H, "<span class='notice'>Your [I.name] couldnt spawn anywhere on you or even on the floor below you. Please file a bug report.</span>")
|
|
qdel(I)
|
|
|
|
|
|
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
|
|
var/m
|
|
if(selected_tip)
|
|
m = selected_tip
|
|
else
|
|
var/list/randomtips = file2list("strings/tips.txt")
|
|
var/list/memetips = 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 class='purple'><b>Tip of the round: </b>[html_encode(m)]</span>")
|
|
|
|
/datum/controller/subsystem/ticker/proc/declare_completion()
|
|
GLOB.nologevent = TRUE //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point.
|
|
set_observer_default_invisibility(0) //spooks things up
|
|
//Round statistics report
|
|
var/datum/station_state/ending_station_state = new /datum/station_state()
|
|
ending_station_state.count()
|
|
var/station_integrity = min(round( 100.0 * GLOB.start_state.score(ending_station_state), 0.1), 100.0)
|
|
|
|
var/list/end_of_round_info = list()
|
|
end_of_round_info += "<BR>[TAB]Shift Duration: <B>[round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]</B>"
|
|
end_of_round_info += "<BR>[TAB]Station Integrity: <B>[mode.station_was_nuked ? "<font color='red'>Destroyed</font>" : "[station_integrity]%"]</B>"
|
|
end_of_round_info += "<BR>"
|
|
|
|
//Silicon laws report
|
|
for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list)
|
|
var/ai_ckey = safe_get_ckey(aiPlayer)
|
|
|
|
if(aiPlayer.stat != DEAD)
|
|
end_of_round_info += "<b>[aiPlayer.name] (Played by: [ai_ckey])'s laws at the end of the game were:</b>"
|
|
else
|
|
end_of_round_info += "<b>[aiPlayer.name] (Played by: [ai_ckey])'s laws when it was deactivated were:</b>"
|
|
aiPlayer.laws_sanity_check()
|
|
for(var/datum/ai_law/law as anything in aiPlayer.laws.sorted_laws)
|
|
if(law == aiPlayer.laws.zeroth_law)
|
|
end_of_round_info += "<span class='danger'>[law.get_index()]. [law.law]</span>"
|
|
else
|
|
end_of_round_info += "[law.get_index()]. [law.law]"
|
|
|
|
if(length(aiPlayer.connected_robots))
|
|
end_of_round_info += "<b>The AI's loyal minions were:</b> "
|
|
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
|
|
var/robo_ckey = safe_get_ckey(robo)
|
|
end_of_round_info += "[robo.name][robo.stat ? " (Deactivated)" : ""] (Played by: [robo_ckey])"
|
|
|
|
var/dronecount = 0
|
|
|
|
for(var/mob/living/silicon/robot/robo in GLOB.mob_list)
|
|
|
|
if(isdrone(robo))
|
|
dronecount++
|
|
continue
|
|
|
|
var/robo_ckey = safe_get_ckey(robo)
|
|
|
|
if(!robo.connected_ai)
|
|
if(robo.stat != DEAD)
|
|
end_of_round_info += "<b>[robo.name] (Played by: [robo_ckey]) survived as an AI-less borg! Its laws were:</b>"
|
|
else
|
|
end_of_round_info += "<b>[robo.name] (Played by: [robo_ckey]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:</b>"
|
|
|
|
robo.laws_sanity_check()
|
|
for(var/datum/ai_law/law as anything in robo.laws.sorted_laws)
|
|
if(law == robo.laws.zeroth_law)
|
|
end_of_round_info += "<span class='danger'>[law.get_index()]. [law.law]</span>"
|
|
else
|
|
end_of_round_info += "[law.get_index()]. [law.law]"
|
|
|
|
if(dronecount)
|
|
end_of_round_info += "<b>There [dronecount > 1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount > 1 ? "drones" : "drone"] this round.</b>"
|
|
|
|
if(length(mode.eventmiscs))
|
|
for(var/datum/mind/eventmind in mode.eventmiscs)
|
|
end_of_round_info += printeventplayer(eventmind)
|
|
end_of_round_info += printobjectives(eventmind)
|
|
end_of_round_info += "<br>"
|
|
|
|
mode.declare_completion()//To declare normal completion.
|
|
|
|
end_of_round_info += mode.get_end_of_round_antagonist_statistics()
|
|
|
|
for(var/datum/team/team in GLOB.antagonist_teams)
|
|
team.on_round_end()
|
|
|
|
// Save the data before end of the round griefing
|
|
SSpersistent_data.save()
|
|
to_chat(world, end_of_round_info.Join("<br>"))
|
|
|
|
// Display the scoreboard window
|
|
score.scoreboard()
|
|
|
|
// Declare the completion of the station goals
|
|
mode.declare_station_goal_completion()
|
|
|
|
//Ask the event manager to print round end information
|
|
SSevents.RoundEnd()
|
|
|
|
//make big obvious note in game logs that round ended
|
|
log_game("///////////////////////////////////////////////////////")
|
|
log_game("///////////////////// ROUND ENDED /////////////////////")
|
|
log_game("///////////////////////////////////////////////////////")
|
|
|
|
// Add AntagHUD to everyone, see who was really evil the whole time!
|
|
for(var/datum/atom_hud/antag/H in GLOB.huds)
|
|
for(var/m in GLOB.player_list)
|
|
var/mob/M = m
|
|
H.add_hud_to(M)
|
|
|
|
var/static/list/base_encouragement_messages = list(
|
|
"Keep on keeping on!",
|
|
"Great job!",
|
|
"Keep up the good work!",
|
|
"Nice going!"
|
|
)
|
|
|
|
var/static/list/special_encouragement_messages = list(
|
|
"Outstanding!",
|
|
"This is going on the fridge!",
|
|
"Looks like you're popular!",
|
|
"That's what we like to see!",
|
|
"Hell yeah, brother!",
|
|
"Honestly, quite incredible!"
|
|
)
|
|
|
|
// Tell people how many kudos they got this round
|
|
// Besides, what's another loop over the /entire player list/
|
|
var/kudos_message
|
|
for(var/mob/M in GLOB.player_list)
|
|
var/kudos = M.mind?.kudos_received_from
|
|
if(length(kudos))
|
|
kudos_message = pick(length(kudos) > 5 ? special_encouragement_messages : base_encouragement_messages)
|
|
to_chat(M, "<span class='green big'>You received <b>[length(M.mind.kudos_received_from)]</b> kudos from other players this round! [kudos_message]</span>")
|
|
|
|
// Seal the blackbox, stop collecting info
|
|
SSblackbox.Seal()
|
|
SSdbcore.SetRoundEnd()
|
|
|
|
return TRUE
|
|
|
|
/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/proc/setup_news_feeds()
|
|
var/datum/feed_channel/newChannel = new /datum/feed_channel
|
|
newChannel.channel_name = "Station Announcements Log"
|
|
newChannel.author = "Automated Announcement Listing"
|
|
newChannel.icon = "bullhorn"
|
|
newChannel.frozen = TRUE
|
|
newChannel.admin_locked = TRUE
|
|
GLOB.news_network.channels += newChannel
|
|
|
|
newChannel = new /datum/feed_channel
|
|
newChannel.channel_name = "Public Station Announcements"
|
|
newChannel.author = "Automated Announcement Listing"
|
|
newChannel.icon = "users"
|
|
newChannel.is_public = TRUE
|
|
GLOB.news_network.channels += newChannel
|
|
|
|
newChannel = new /datum/feed_channel
|
|
newChannel.channel_name = "Nyx Daily"
|
|
newChannel.author = "CentComm Minister of Information"
|
|
newChannel.icon = "meteor"
|
|
newChannel.frozen = TRUE
|
|
newChannel.admin_locked = TRUE
|
|
GLOB.news_network.channels += newChannel
|
|
|
|
newChannel = new /datum/feed_channel
|
|
newChannel.channel_name = "The Gibson Gazette"
|
|
newChannel.author = "Editor Mike Hammers"
|
|
newChannel.icon = "star"
|
|
newChannel.frozen = TRUE
|
|
newChannel.admin_locked = TRUE
|
|
GLOB.news_network.channels += newChannel
|
|
|
|
for(var/loc_type in subtypesof(/datum/trade_destination))
|
|
var/datum/trade_destination/D = new loc_type
|
|
GLOB.weighted_randomevent_locations[D] = D.viable_random_events.len
|
|
GLOB.weighted_mundaneevent_locations[D] = D.viable_mundane_events.len
|
|
|
|
// Easy handler to make rebooting the world not a massive sleep in world/Reboot()
|
|
/datum/controller/subsystem/ticker/proc/reboot_helper(reason, end_string, delay)
|
|
// Admins delayed round end. Just alert and dont bother with anything else.
|
|
if(delay_end)
|
|
to_chat(world, "<span class='boldannounce'>An admin has delayed the round end.</span>")
|
|
return
|
|
|
|
if(!isnull(delay))
|
|
// Delay time was present. Use that.
|
|
delay = max(0, delay)
|
|
else
|
|
// Use default restart timeout
|
|
delay = restart_timeout
|
|
|
|
to_chat(world, "<span class='boldannounce'>Rebooting world in [delay/10] [delay > 10 ? "seconds" : "second"]. [reason]</span>")
|
|
|
|
real_reboot_time = world.time + delay
|
|
UNTIL(world.time > real_reboot_time) // Hold it here
|
|
|
|
// And if we re-delayed, bail again
|
|
if(delay_end)
|
|
to_chat(world, "<span class='boldannounce'>Reboot was cancelled by an admin.</span>")
|
|
return
|
|
|
|
if(end_string)
|
|
end_state = end_string
|
|
|
|
// Play a haha funny noise
|
|
var/round_end_sound = pick(GLOB.round_end_sounds)
|
|
var/sound_length = GLOB.round_end_sounds[round_end_sound]
|
|
SEND_SOUND(world, sound(round_end_sound))
|
|
sleep(sound_length)
|
|
|
|
world.Reboot()
|
|
|
|
// Timers invoke this async
|
|
/datum/controller/subsystem/ticker/proc/handle_antagfishing_reporting()
|
|
// This needs the DB
|
|
if(!SSdbcore.IsConnected())
|
|
return
|
|
// Dont need to do anything
|
|
if(!length(flagged_antag_rollers))
|
|
return
|
|
|
|
// Records themselves
|
|
var/list/datum/antag_record/records = list()
|
|
// Queries to load data (executed as async batch)
|
|
var/list/datum/db_query/load_queries = list()
|
|
// Queries to save data (executed as async batch)
|
|
var/list/datum/db_query/save_queries = list()
|
|
|
|
|
|
for(var/ckey in flagged_antag_rollers)
|
|
var/datum/antag_record/AR = new /datum/antag_record(ckey)
|
|
records[ckey] = AR
|
|
load_queries[ckey] = AR.get_load_query()
|
|
|
|
// Explanation for parameters:
|
|
// TRUE: We want warnings if these fail
|
|
// FALSE: Do NOT qdel() queries here, otherwise they wont be read. At all.
|
|
// TRUE: This is an assoc list, so it needs to prepare for that
|
|
SSdbcore.MassExecute(load_queries, TRUE, FALSE, TRUE)
|
|
|
|
// Report on things
|
|
var/list/log_text = list("The following players attempted to roll antag with no jobs (total infractions listed)")
|
|
|
|
for(var/ckey in flagged_antag_rollers)
|
|
var/datum/antag_record/AR = records[ckey]
|
|
AR.handle_data(load_queries[ckey])
|
|
save_queries[ckey] = AR.get_save_query()
|
|
|
|
log_text += "<small>- <a href='?priv_msg=[ckey]'>[ckey]</a>: [AR.infraction_count]</small>"
|
|
|
|
log_text += "Investigation advised if there are a high number of infractions"
|
|
|
|
message_admins(log_text.Join("<br>"))
|
|
|
|
// Now do a ton of saves
|
|
SSdbcore.MassExecute(save_queries, TRUE, TRUE, TRUE)
|
|
|
|
// And cleanup
|
|
QDEL_LIST_ASSOC_VAL(load_queries)
|
|
records.Cut()
|
|
flagged_antag_rollers.Cut()
|
|
|
|
/// This proc is for recording biohazard events, and blackboxing if they lived, died, or ended the round. This currently applies to: Terror spiders, Xenomorphs, and Blob.
|
|
/datum/controller/subsystem/ticker/proc/event_blackbox(outcome = ROUND_END_CREW_TRANSFER)
|
|
for(var/I in SSevents.biohazards_this_round)
|
|
switch(I)
|
|
if(TS_INFESTATION_GREEN_SPIDER, TS_INFESTATION_PRINCE_SPIDER, TS_INFESTATION_WHITE_SPIDER, TS_INFESTATION_PRINCESS_SPIDER, TS_INFESTATION_QUEEN_SPIDER)
|
|
var/output = "unknown spider type"
|
|
switch(I)
|
|
if(TS_INFESTATION_GREEN_SPIDER)
|
|
output = "Green Terrors"
|
|
if(TS_INFESTATION_PRINCE_SPIDER)
|
|
output = "Prince Terror"
|
|
if(TS_INFESTATION_WHITE_SPIDER)
|
|
output = "White Terrors"
|
|
if(TS_INFESTATION_PRINCESS_SPIDER)
|
|
output = "Princess Terrors"
|
|
if(TS_INFESTATION_QUEEN_SPIDER)
|
|
output = "Queen Terrors"
|
|
var/spiders = 0
|
|
for(var/mob/living/simple_animal/hostile/poison/terror_spider/S in GLOB.ts_spiderlist)
|
|
if(S.ckey)
|
|
spiders++
|
|
if(spiders >= 5 || (output == "Prince Terror" && spiders == 1)) //If a prince lives, record as win.
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard nuclear victories", 1, output)
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to normal round end", 1, output)
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to admin round end", 1, output)
|
|
else
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies station nuked", 1, output)
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies normal end", 1, output)
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies admin round end", 1, output)
|
|
if("Xenomorphs")
|
|
if(length(SSticker.mode.xenos) > 5)
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard nuclear victories", 1, "Xenomorphs")
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to normal round end", 1, "Xenomorphs")
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to admin round end", 1, "Xenomorphs")
|
|
else
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies station nuked", 1, "Xenomorphs")
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies normal end", 1, "Xenomorphs")
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies admin round end", 1, "Xenomorphs")
|
|
|
|
if("Blob")
|
|
if(length(SSticker.mode.blob_overminds))
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard nuclear victories", 1, "Blob")
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to normal round end", 1, "Blob")
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard survives to admin round end", 1, "Blob")
|
|
else
|
|
switch(outcome)
|
|
if(ROUND_END_NUCLEAR)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies station nuked", 1, "Blob")
|
|
if(ROUND_END_CREW_TRANSFER)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies normal end", 1, "Blob")
|
|
if(ROUND_END_FORCED)
|
|
SSblackbox.record_feedback("tally", "Biohazard dies admin round end", 1, "Blob")
|
|
|