diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index a147773cd1..cb32e435ca 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -2280,7 +2280,7 @@ /turf/open/floor/plasteel, /area/tdome/arena_source) "fX" = ( -/turf/closed/indestructible/riveted, +/turf/closed/indestructible/start_area, /area/start) "fY" = ( /obj/effect/landmark/start/new_player, diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1039a07982..b3dbc46806 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -539,7 +539,7 @@ #define COMSIG_MECHA_EQUIPMENT_CLICK "mecha_action_equipment_click" /// Prevents click from happening. #define COMPONENT_CANCEL_EQUIPMENT_CLICK (1<<0) - + // /mob/living/carbon/human signals #define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target) #define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker) diff --git a/code/__DEFINES/dcs/signals/signals_subsystem.dm b/code/__DEFINES/dcs/signals/signals_subsystem.dm new file mode 100644 index 0000000000..c39b59262b --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_subsystem.dm @@ -0,0 +1,25 @@ +// Subsystem signals. Format: +// When the signal is called: (signal arguments) +// All signals send the source datum of the signal as the first argument + +///Subsystem signals +///From base of datum/controller/subsystem/Initialize: (start_timeofday) +#define COMSIG_SUBSYSTEM_POST_INITIALIZE "subsystem_post_initialize" + +///Called when the ticker enters the pre-game phase +#define COMSIG_TICKER_ENTER_PREGAME "comsig_ticker_enter_pregame" + +///Called when the ticker sets up the game for start +#define COMSIG_TICKER_ENTER_SETTING_UP "comsig_ticker_enter_setting_up" + +///Called when the ticker fails to set up the game for start +#define COMSIG_TICKER_ERROR_SETTING_UP "comsig_ticker_error_setting_up" + +/// Called when the round has started, but before GAME_STATE_PLAYING +#define COMSIG_TICKER_ROUND_STARTING "comsig_ticker_round_starting" + +// Point of interest signals +/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_added : (atom/new_poi) +#define COMSIG_ADDED_POINT_OF_INTEREST "added_point_of_interest" +/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_removed : (atom/old_poi) +#define COMSIG_REMOVED_POINT_OF_INTEREST "removed_point_of_interest" diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm index 2550084207..fd841ba258 100644 --- a/code/__DEFINES/layers_planes.dm +++ b/code/__DEFINES/layers_planes.dm @@ -187,6 +187,9 @@ #define ABOVE_HUD_LAYER 30 #define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE" +#define LOBBY_BACKGROUND_LAYER 3 +#define LOBBY_BUTTON_LAYER 4 + #define SPLASHSCREEN_LAYER 90 #define SPLASHSCREEN_PLANE 90 #define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index c8c084fc18..36f427a1bb 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -5,7 +5,6 @@ // Ready states at roundstart for mob/dead/new_player #define PLAYER_NOT_READY 0 #define PLAYER_READY_TO_PLAY 1 -#define PLAYER_READY_TO_OBSERVE 2 // movement intent defines for the m_intent var #define MOVE_INTENT_WALK "walk" diff --git a/code/__HELPERS/clients.dm b/code/__HELPERS/clients.dm new file mode 100644 index 0000000000..3b61cf1e1c --- /dev/null +++ b/code/__HELPERS/clients.dm @@ -0,0 +1,12 @@ +///Returns whether or not a player is a guest using their ckey as an input +/proc/is_guest_key(key) + if(findtext(key, "Guest-", 1, 7) != 1) //was findtextEx + return FALSE + + var/i, ch, len = length(key) + + for(i = 7, i <= len, ++i) //we know the first 6 chars are Guest- + ch = text2ascii(key, i) + if (ch < 48 || ch > 57) //0-9 + return FALSE + return TRUE diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm new file mode 100644 index 0000000000..b426db1aac --- /dev/null +++ b/code/_onclick/hud/new_player.dm @@ -0,0 +1,333 @@ +/datum/hud/new_player + +/datum/hud/new_player/proc/populate_buttons(mob/dead/new_player/owner) + var/list/buttons = subtypesof(/atom/movable/screen/lobby) + for(var/button_type in buttons) + var/atom/movable/screen/lobby/lobbyscreen = new button_type() + lobbyscreen.SlowInit() + lobbyscreen.hud = src + static_inventory += lobbyscreen + if(istype(lobbyscreen, /atom/movable/screen/lobby/button)) + var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen + lobby_button.owner = REF(owner) + show_hud(hud_version) + +/atom/movable/screen/lobby + plane = SPLASHSCREEN_PLANE + layer = LOBBY_BUTTON_LAYER + screen_loc = "TOP,CENTER" + +/// Run sleeping actions after initialize +/atom/movable/screen/lobby/proc/SlowInit() + return + +/atom/movable/screen/lobby/background + layer = LOBBY_BACKGROUND_LAYER + icon = 'icons/hud/lobby/background.dmi' + icon_state = "background" + screen_loc = "TOP,CENTER:-61" + +/atom/movable/screen/lobby/button + ///Is the button currently enabled? + var/enabled = TRUE + ///Is the button currently being hovered over with the mouse? + var/highlighted = FALSE + /// The ref of the mob that owns this button. Only the owner can click on it. + var/owner + +/atom/movable/screen/lobby/button/Click(location, control, params) + if(owner != REF(usr)) + return + + . = ..() + + if(!enabled) + return + flick("[base_icon_state]_pressed", src) + update_appearance(UPDATE_ICON) + return TRUE + +/atom/movable/screen/lobby/button/MouseEntered(location,control,params) + if(owner != REF(usr)) + return + + . = ..() + highlighted = TRUE + update_appearance(UPDATE_ICON) + +/atom/movable/screen/lobby/button/MouseExited() + if(owner != REF(usr)) + return + + . = ..() + highlighted = FALSE + update_appearance(UPDATE_ICON) + +/atom/movable/screen/lobby/button/update_icon(updates) + . = ..() + if(!enabled) + icon_state = "[base_icon_state]_disabled" + return + else if(highlighted) + icon_state = "[base_icon_state]_highlighted" + return + icon_state = base_icon_state + +/atom/movable/screen/lobby/button/proc/set_button_status(status) + if(status == enabled) + return FALSE + enabled = status + update_appearance(UPDATE_ICON) + return TRUE + +///Prefs menu +/atom/movable/screen/lobby/button/character_setup + screen_loc = "TOP:-70,CENTER:-54" + icon = 'icons/hud/lobby/character_setup.dmi' + icon_state = "character_setup" + base_icon_state = "character_setup" + +/atom/movable/screen/lobby/button/character_setup/Click(location, control, params) + . = ..() + if(!.) + return + hud.mymob.client.prefs.current_tab = SETTINGS_TAB + hud.mymob.client.prefs.ShowChoices(hud.mymob) + +///Button that appears before the game has started +/atom/movable/screen/lobby/button/ready + screen_loc = "TOP:-8,CENTER:-65" + icon = 'icons/hud/lobby/ready.dmi' + icon_state = "not_ready" + base_icon_state = "not_ready" + var/ready = FALSE + +/atom/movable/screen/lobby/button/ready/Initialize(mapload) + . = ..() + switch(SSticker.current_state) + if(GAME_STATE_PREGAME, GAME_STATE_STARTUP) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/hide_ready_button) + if(GAME_STATE_SETTING_UP) + set_button_status(FALSE) + RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/show_ready_button) + else + set_button_status(FALSE) + +/atom/movable/screen/lobby/button/ready/proc/hide_ready_button() + SIGNAL_HANDLER + set_button_status(FALSE) + UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP) + RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/show_ready_button) + +/atom/movable/screen/lobby/button/ready/proc/show_ready_button() + SIGNAL_HANDLER + set_button_status(TRUE) + UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/hide_ready_button) + +/atom/movable/screen/lobby/button/ready/Click(location, control, params) + . = ..() + if(!.) + return + var/mob/dead/new_player/new_player = hud.mymob + ready = !ready + if(ready) + new_player.ready = PLAYER_READY_TO_PLAY + base_icon_state = "ready" + else + new_player.ready = PLAYER_NOT_READY + base_icon_state = "not_ready" + update_appearance(UPDATE_ICON) + +///Shown when the game has started +/atom/movable/screen/lobby/button/join + screen_loc = "TOP:-13,CENTER:-58" + icon = 'icons/hud/lobby/join.dmi' + icon_state = "" //Default to not visible + base_icon_state = "join_game" + enabled = FALSE + +/atom/movable/screen/lobby/button/join/Initialize(mapload) + . = ..() + switch(SSticker.current_state) + if(GAME_STATE_PREGAME, GAME_STATE_STARTUP) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/show_join_button) + if(GAME_STATE_SETTING_UP) + set_button_status(TRUE) + RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/hide_join_button) + else + set_button_status(TRUE) + +/atom/movable/screen/lobby/button/join/Click(location, control, params) + . = ..() + if(!.) + return + if(!SSticker?.IsRoundInProgress()) + to_chat(hud.mymob, span_boldwarning("The round is either not ready, or has already finished...")) + return + + //Determines Relevent Population Cap + var/relevant_cap + var/hpc = CONFIG_GET(number/hard_popcap) + var/epc = CONFIG_GET(number/extreme_popcap) + if(hpc && epc) + relevant_cap = min(hpc, epc) + else + relevant_cap = max(hpc, epc) + + var/mob/dead/new_player/new_player = hud.mymob + + if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(new_player.key) in GLOB.admin_datums))) + to_chat(new_player, span_danger("[CONFIG_GET(string/hard_popcap_message)]")) + + var/queue_position = SSticker.queued_players.Find(new_player) + if(queue_position == 1) + to_chat(new_player, span_notice("You are next in line to join the game. You will be notified when a slot opens up.")) + else if(queue_position) + to_chat(new_player, span_notice("There are [queue_position-1] players in front of you in the queue to join the game.")) + else + SSticker.queued_players += new_player + to_chat(new_player, span_notice("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].")) + return + new_player.LateChoices() + +/atom/movable/screen/lobby/button/join/proc/show_join_button() + SIGNAL_HANDLER + set_button_status(TRUE) + UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP) + RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/hide_join_button) + +/atom/movable/screen/lobby/button/join/proc/hide_join_button() + SIGNAL_HANDLER + set_button_status(FALSE) + UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP) + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/show_join_button) + +/atom/movable/screen/lobby/button/observe + screen_loc = "TOP:-40,CENTER:-54" + icon = 'icons/hud/lobby/observe.dmi' + icon_state = "observe_disabled" + base_icon_state = "observe" + enabled = FALSE + +/atom/movable/screen/lobby/button/observe/Initialize(mapload) + . = ..() + if(SSticker.current_state > GAME_STATE_STARTUP) + set_button_status(TRUE) + else + RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, .proc/enable_observing) + +/atom/movable/screen/lobby/button/observe/Click(location, control, params) + . = ..() + if(!.) + return + var/mob/dead/new_player/new_player = hud.mymob + new_player.make_me_an_observer() + +/atom/movable/screen/lobby/button/observe/proc/enable_observing() + SIGNAL_HANDLER + flick("[base_icon_state]_enabled", src) + set_button_status(TRUE) + UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, .proc/enable_observing) + +/atom/movable/screen/lobby/button/settings + icon = 'icons/hud/lobby/bottom_buttons.dmi' + icon_state = "settings" + base_icon_state = "settings" + screen_loc = "TOP:-122,CENTER:+30" + +/atom/movable/screen/lobby/button/settings/Click(location, control, params) + . = ..() + if(!.) + return + + hud.mymob.client.prefs.current_tab = GAME_PREFERENCES_TAB + hud.mymob.client.prefs.ShowChoices(hud.mymob) + +/atom/movable/screen/lobby/button/changelog_button + icon = 'icons/hud/lobby/bottom_buttons.dmi' + icon_state = "changelog" + base_icon_state = "changelog" + screen_loc ="TOP:-122,CENTER:+58" + + +/atom/movable/screen/lobby/button/crew_manifest + icon = 'icons/hud/lobby/bottom_buttons.dmi' + icon_state = "crew_manifest" + base_icon_state = "crew_manifest" + screen_loc = "TOP:-122,CENTER:+2" + +/atom/movable/screen/lobby/button/crew_manifest/Click(location, control, params) + . = ..() + if(!.) + return + var/mob/dead/new_player/new_player = hud.mymob + new_player.ViewManifest() + +/atom/movable/screen/lobby/button/changelog_button/Click(location, control, params) + . = ..() + usr.client?.changelog() + +/atom/movable/screen/lobby/button/poll + icon = 'icons/hud/lobby/bottom_buttons.dmi' + icon_state = "poll" + base_icon_state = "poll" + screen_loc = "TOP:-122,CENTER:-26" + + var/new_poll = FALSE + +/atom/movable/screen/lobby/button/poll/SlowInit(mapload) + . = ..() + if(!usr) + return + var/mob/dead/new_player/new_player = usr + if(is_guest_key(new_player.key)) + set_button_status(FALSE) + return + if(!SSdbcore.Connect()) + set_button_status(FALSE) + return + var/isadmin = FALSE + if(new_player.client?.holder) + isadmin = TRUE + var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({" + SELECT id FROM [format_table_name("poll_question")] + WHERE (adminonly = 0 OR :isadmin = 1) + AND Now() BETWEEN starttime AND endtime + AND deleted = 0 + AND id NOT IN ( + SELECT pollid FROM [format_table_name("poll_vote")] + WHERE ckey = :ckey + AND deleted = 0 + ) + AND id NOT IN ( + SELECT pollid FROM [format_table_name("poll_textreply")] + WHERE ckey = :ckey + AND deleted = 0 + ) + "}, list("isadmin" = isadmin, "ckey" = new_player.ckey)) + if(!query_get_new_polls.Execute()) + qdel(query_get_new_polls) + set_button_status(FALSE) + return + if(query_get_new_polls.NextRow()) + new_poll = TRUE + else + new_poll = FALSE + update_appearance(UPDATE_OVERLAYS) + qdel(query_get_new_polls) + if(QDELETED(new_player)) + set_button_status(FALSE) + return + +/atom/movable/screen/lobby/button/poll/update_overlays() + . = ..() + if(new_poll) + . += mutable_appearance('icons/hud/lobby/poll_overlay.dmi', "new_poll") + +/atom/movable/screen/lobby/button/poll/Click(location, control, params) + . = ..() + if(!.) + return + var/mob/dead/new_player/new_player = hud.mymob + new_player.handle_player_polling() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index a0157894c5..d171366760 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -164,8 +164,8 @@ SUBSYSTEM_DEF(ticker) to_chat(world, "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() + SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME) + fire() if(GAME_STATE_PREGAME) //lobby stats for statpanels @@ -203,6 +203,7 @@ SUBSYSTEM_DEF(ticker) for(var/client/C in SSvote.voting) C << browse(null, "window=vote;can_close=0") SSvote.reset() + SEND_SIGNAL(src, COMSIG_TICKER_ENTER_SETTING_UP) current_state = GAME_STATE_SETTING_UP Master.SetRunLevel(RUNLEVEL_SETUP) if(start_immediately) @@ -215,6 +216,7 @@ SUBSYSTEM_DEF(ticker) 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) mode.process(wait * 0.1) @@ -378,8 +380,6 @@ SUBSYSTEM_DEF(ticker) LAZYOR(player.client.prefs.characters_joined_as, player.new_character.real_name) else stack_trace("WARNING: Either a player did not have a new_character, did not have a client, or did not have preferences. This is VERY bad.") - else - player.new_player_panel() CHECK_TICK /datum/controller/subsystem/ticker/proc/collect_minds() @@ -633,13 +633,6 @@ SUBSYSTEM_DEF(ticker) 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, /mob/dead/new_player.proc/make_me_an_observer), 1) - /datum/controller/subsystem/ticker/proc/load_mode() var/mode = trim(file2text("data/mode.txt")) if(mode) diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm index 3cc3d9dd99..1dbfb1540c 100644 --- a/code/controllers/subsystem/title.dm +++ b/code/controllers/subsystem/title.dm @@ -10,7 +10,7 @@ SUBSYSTEM_DEF(title) /datum/controller/subsystem/title/Initialize() if(file_path && icon) - return ..() + return if(fexists("data/previous_title.dat")) var/previous_path = file2text("data/previous_title.dat") @@ -31,13 +31,16 @@ SUBSYSTEM_DEF(title) if(length(title_screens)) file_path = "[global.config.directory]/title_screens/images/[pick(title_screens)]" - if(!file_path || !fexists(file_path)) - file_path = "icons/default_title.dmi" + if(!file_path) + file_path = "icons/runtime/default_title.dmi" - if(fexists(file_path)) - icon = new(fcopy_rsc(file_path)) - if(splash_turf) - splash_turf.icon = icon + ASSERT(fexists(file_path)) + + icon = new(fcopy_rsc(file_path)) + + if(splash_turf) + splash_turf.icon = icon + splash_turf.handle_generic_titlescreen_sizes() return ..() diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index e57b0c9806..ef3c6a91d9 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -73,16 +73,26 @@ /turf/closed/indestructible/splashscreen name = "Space Station 13" - icon = 'icons/blank_title.png' + desc = null + icon = 'icons/blanks/blank_title.png' icon_state = "" - layer = FLY_LAYER + plane = SPLASHSCREEN_PLANE bullet_bounce_sound = null -/turf/closed/indestructible/splashscreen/New() +INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen) + +/turf/closed/indestructible/splashscreen/Initialize(mapload) + . = ..() SStitle.splash_turf = src if(SStitle.icon) icon = SStitle.icon - ..() + handle_generic_titlescreen_sizes() + +///helper proc that will center the screen if the icon is changed to a generic width, to make admins have to fudge around with pixel_x less. returns null +/turf/closed/indestructible/splashscreen/proc/handle_generic_titlescreen_sizes() + var/icon/size_check = icon(SStitle.icon, icon_state) + var/width = size_check.Width() + pixel_x = (672 - width) * 0.5 //The title screen is mapped with the expectation that it's 672x480. Should probably turn the title screen size into a define some time! /turf/closed/indestructible/splashscreen/vv_edit_var(var_name, var_value) . = ..() @@ -90,7 +100,13 @@ switch(var_name) if(NAMEOF(src, icon)) SStitle.icon = icon + handle_generic_titlescreen_sizes() +/turf/closed/indestructible/start_area + name = null + desc = null + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + /turf/closed/indestructible/riveted icon = 'icons/turf/walls/riveted.dmi' icon_state = "riveted" diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index 887556500c..dbcc2cb2b4 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -9,6 +9,12 @@ ..() + if(!age_verify()) + return + + if(!client) //client can end up null as a result of age_verify() kicking those who fail + return + var/motd = global.config.motd if(motd) to_chat(src, "
Welcome, [client ? client.prefs.real_name : "Unknown User"]
" - output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | Not Ready | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]
" - if(PLAYER_READY_TO_PLAY) - output += "\[ Ready | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]
" - if(PLAYER_READY_TO_OBSERVE) - output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | Observe \]
" - else - output += "" - output += "" - output += "[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]
" - - if(!IsGuestKey(src.key)) - output += playerpolls() - - output += "Show Player Polls (NEW!)
" - else - output += "" - qdel(query_get_new_polls) - if(QDELETED(src)) - return - return output - /mob/dead/new_player/proc/age_gate() var/list/dat = list("