diff --git a/code/datums/browser.dm b/code/datums/browser.dm index f6bff564d7..7335945b66 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -85,6 +85,9 @@ //" This is here because else the rest of the file looks like a string in notepad++. /datum/browser/proc/get_footer() return {" +
+
+
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index f219489c40..c350f2534d 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -39,6 +39,79 @@ /mob/dead/new_player/prepare_huds() return +// Drawing basic UI panel +/mob/dead/new_player/proc/new_player_panel() + if (client?.interviewee) + return + + var/datum/asset/asset_datum = get_asset_datum(/datum/asset/simple/lobby) + asset_datum.send(client) + var/list/output = list() + if(client?.prefs) + output += "

Welcome, [client.prefs.be_random_name ? "random name player" : client.prefs.real_name]

" + output += "

Setup Character

" + + if(SSticker.current_state <= GAME_STATE_PREGAME) + /* + switch(ready) + if(PLAYER_NOT_READY) + 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 \]

" + */ + output += "

Please be patient, the game is starting soon!

" + output += "

(Refresh)

" + output += "

(Fix Chat Window)

" + else + output += "

View the Crew Manifest

" + output += "

Join Game!

" + output += "

[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]

" + output += "

(Fix Chat Window)

" + + if(!IsGuestKey(src.key)) + output += playerpolls() + + output += "
" + + var/datum/browser/popup = new(src, "playersetup", "
New Player Options
", 250, 265) + popup.set_window_options("can_close=0") + popup.set_content(output.Join()) + popup.open(FALSE) + +/mob/dead/new_player/proc/playerpolls() + var/list/output = list() + if (SSdbcore.Connect()) + var/isadmin = FALSE + if(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 id NOT IN ( + SELECT pollid FROM [format_table_name("poll_vote")] + WHERE ckey = :ckey + ) + AND id NOT IN ( + SELECT pollid FROM [format_table_name("poll_textreply")] + WHERE ckey = :ckey + ) + "}, list("isadmin" = isadmin, "ckey" = ckey)) + var/rs = REF(src) + if(!query_get_new_polls.Execute()) + qdel(query_get_new_polls) + return + if(query_get_new_polls.NextRow()) + output += "

Show Player Polls (NEW!)

" + else + output += "

Show Player Polls

" + qdel(query_get_new_polls) + if(QDELETED(src)) + return + return output + /mob/dead/new_player/Topic(href, href_list) if (usr != src) return @@ -57,6 +130,231 @@ var/datum/poll_question/poll = locate(href_list["votepollref"]) in GLOB.polls vote_on_poll_handler(poll, href_list) + //don't let people get to this unless they are specifically not verified (Age gate checker) + if(href_list["Month"] && (CONFIG_GET(flag/age_verification) && !check_rights_for(client, R_ADMIN) && !(client.ckey in GLOB.bunker_passthrough))) + var/player_month = text2num(href_list["Month"]) + var/player_year = text2num(href_list["Year"]) + + var/current_time = world.realtime + var/current_month = text2num(time2text(current_time, "MM")) + var/current_year = text2num(time2text(current_time, "YYYY")) + + var/player_total_months = (player_year * 12) + player_month + + var/current_total_months = (current_year * 12) + current_month + + var/months_in_eighteen_years = 18 * 12 + + var/month_difference = current_total_months - player_total_months + if(month_difference > months_in_eighteen_years) + age_gate_result = TRUE // they're fine + else + if(month_difference < months_in_eighteen_years) + age_gate_result = FALSE + else + //they could be 17 or 18 depending on the /day/ they were born in + var/current_day = text2num(time2text(current_time, "DD")) + var/days_in_months = list(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + if((player_year % 4) == 0) // leap year so february actually has 29 days + days_in_months[2] = 29 + var/total_days_in_player_month = days_in_months[player_month] + var/list/days = list() + for(var/number in 1 to total_days_in_player_month) + days += number + var/player_day = input(src, "What day of the month were you born in.") as anything in days + if(player_day <= current_day) + //their birthday has passed + age_gate_result = TRUE + else + //it has NOT been their 18th birthday yet + age_gate_result = FALSE + + //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) + + if(href_list["show_preferences"]) + client.prefs.ShowChoices(src) + return 1 + + if(href_list["ready"]) + var/tready = text2num(href_list["ready"]) + //Avoid updating ready if we're after PREGAME (they should use latejoin instead) + //This is likely not an actual issue but I don't have time to prove that this + //no longer is required + if(SSticker.current_state <= GAME_STATE_PREGAME) + ready = tready + //if it's post initialisation and they're trying to observe we do the needful + if(!SSticker.current_state < GAME_STATE_PREGAME && tready == PLAYER_READY_TO_OBSERVE) + ready = tready + make_me_an_observer() + return + + if(href_list["refresh"]) + src << browse(null, "window=playersetup") //closes the player setup window + new_player_panel() + + if(href_list["refresh_chat"]) //fortuna addition. asset delivery pain + client.nuke_chat() + + if(href_list["late_join"]) + if(!SSticker || !SSticker.IsRoundInProgress()) + to_chat(usr, "The round is either not ready, or has already finished...") + return + + if(href_list["late_join"] == "override") + LateChoices() + return + + if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) + to_chat(usr, "[CONFIG_GET(string/hard_popcap_message)]") + + var/queue_position = SSticker.queued_players.Find(usr) + if(queue_position == 1) + to_chat(usr, "You are next in line to join the game. You will be notified when a slot opens up.") + else if(queue_position) + to_chat(usr, "There are [queue_position-1] players in front of you in the queue to join the game.") + else + SSticker.queued_players += usr + to_chat(usr, "You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].") + return + + if(GLOB.data_core.get_record_by_name(client.prefs.real_name)) + alert(src, "This character name is already in use. Choose another.") + return + + LateChoices() + + if(href_list["manifest"]) + ViewManifest() + + if(href_list["SelectedJob"]) + if(!SSticker || !SSticker.IsRoundInProgress()) + var/msg = "[key_name(usr)] attempted to join the round using a href that shouldn't be available at this moment!" + log_admin(msg) + message_admins(msg) + to_chat(usr, "The round is either not ready, or has already finished...") + return + + if(!GLOB.enter_allowed) + to_chat(usr, "There is an administrative lock on entering the game!") + return + + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) + if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) + to_chat(usr, "Server is full.") + return + + AttemptLateSpawn(href_list["SelectedJob"]) + return + + if(href_list["JoinAsGhostRole"]) + if(!GLOB.enter_allowed) + to_chat(usr, " There is an administrative lock on entering the game!") + + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) + if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) + to_chat(usr, "Server is full.") + return + + var/obj/effect/mob_spawn/MS = pick(GLOB.mob_spawners[href_list["JoinAsGhostRole"]]) + if(MS.attack_ghost(src, latejoinercalling = TRUE)) + SSticker.queued_players -= src + SSticker.queue_delay = 4 + qdel(src) + + else if(!href_list["late_join"]) + new_player_panel() + + if(href_list["showpoll"]) + handle_player_polling() + return + + if(href_list["pollid"]) + var/pollid = href_list["pollid"] + if(istext(pollid)) + pollid = text2num(pollid) + if(isnum(pollid) && ISINTEGER(pollid)) + src.poll_player(pollid) + return + + if(href_list["votepollid"] && href_list["votetype"]) + var/pollid = text2num(href_list["votepollid"]) + var/votetype = href_list["votetype"] + //lets take data from the user to decide what kind of poll this is, without validating it + //what could go wrong + switch(votetype) + if(POLLTYPE_OPTION) + var/optionid = text2num(href_list["voteoptionid"]) + if(vote_on_poll(pollid, optionid)) + to_chat(usr, "Vote successful.") + else + to_chat(usr, "Vote failed, please try again or contact an administrator.") + if(POLLTYPE_TEXT) + var/replytext = href_list["replytext"] + if(log_text_poll_reply(pollid, replytext)) + to_chat(usr, "Feedback logging successful.") + else + to_chat(usr, "Feedback logging failed, please try again or contact an administrator.") + if(POLLTYPE_RATING) + var/id_min = text2num(href_list["minid"]) + var/id_max = text2num(href_list["maxid"]) + + if( (id_max - id_min) > 100 ) //Basic exploit prevention + //(protip, this stops no exploits) + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") + return + + for(var/optionid = id_min; optionid <= id_max; optionid++) + if(!isnull(href_list["o[optionid]"])) //Test if this optionid was replied to + var/rating + if(href_list["o[optionid]"] == "abstain") + rating = null + else + rating = text2num(href_list["o[optionid]"]) + if(!isnum(rating) || !ISINTEGER(rating)) + return + + if(!vote_on_numval_poll(pollid, optionid, rating)) + to_chat(usr, "Vote failed, please try again or contact an administrator.") + return + to_chat(usr, "Vote successful.") + if(POLLTYPE_MULTI) + var/id_min = text2num(href_list["minoptionid"]) + var/id_max = text2num(href_list["maxoptionid"]) + + if( (id_max - id_min) > 100 ) //Basic exploit prevention + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") + return + + for(var/optionid = id_min; optionid <= id_max; optionid++) + if(!isnull(href_list["option_[optionid]"])) //Test if this optionid was selected + var/i = vote_on_multi_poll(pollid, optionid) + switch(i) + if(0) + continue + if(1) + to_chat(usr, "Vote failed, please try again or contact an administrator.") + return + if(2) + to_chat(usr, "Maximum replies reached.") + break + to_chat(usr, "Vote successful.") + if(POLLTYPE_IRV) + if (!href_list["IRVdata"]) + to_chat(src, "No ordering data found. Please try again or contact an administrator.") + return + var/list/votelist = splittext(href_list["IRVdata"], ",") + if (!vote_on_irv_poll(pollid, votelist)) + to_chat(src, "Vote failed, please try again or contact an administrator.") + return + to_chat(src, "Vote successful.") + //When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT) /mob/dead/new_player/proc/make_me_an_observer() if(QDELETED(src) || !src.client) diff --git a/html/browser/common.css b/html/browser/common.css index eb6fed9a9e..e904cd8f13 100644 --- a/html/browser/common.css +++ b/html/browser/common.css @@ -1,22 +1,25 @@ +@import url(https://fonts.googleapis.com/css?family=VT323); body { padding: 0; margin: 0; - background-color: #272727; + background: #383838; + color: #00dd00; font-size: 12px; - color: #ffffff; + font-family: 'VT323', Courier; + color: #2fc22f; line-height: 170%; } hr { - background-color: #40628a; + background-color: #000000; height: 1px; } a, button, a:link, a:visited, a:active, .linkOn, .linkOff { - color: #ffffff; + color: #00dd00; text-decoration: none; background: #40628a; border: 1px solid #161616; @@ -25,10 +28,9 @@ a, button, a:link, a:visited, a:active, .linkOn, .linkOff cursor:default; } -a:hover -{ - color: #40628a; - background: #ffffff; +a:hover { + background-color: #5DC966; + color: black; } a.white, a.white:link, a.white:visited, a.white:active @@ -131,9 +133,9 @@ h4 { clear: both; padding: 6px 8px 6px 8px; - border-bottom: 2px solid #161616; + border-bottom: 2px solid #00dd00; background: #383838; - color: #98B0C3; + color: #00dd00; font-size: 16px; } @@ -146,9 +148,11 @@ h4 .uiContent { + animation: scroll 0.25s 1; clear: both; padding: 8px; font-family: Verdana, Geneva, sans-serif; + } .good @@ -173,7 +177,7 @@ h4 .dark { - color: #272727; + color: #383838; } .notice @@ -220,7 +224,7 @@ div.notice width: 138px; float: left; overflow: hidden; - color: #98B0C3; + color: #383838; } .statusValue @@ -326,15 +330,15 @@ div.notice } .slider:before { - position: absolute; - content: ""; - height: 18px; - width: 18px; - left: 4px; - bottom: 4px; - background-color: #98B0C3; - transition: .4s; -} + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 4px; + bottom: 4px; + background-color: #383838; + transition: .4s; + } .slider.red:before { background-color: #d6858b; @@ -380,6 +384,22 @@ input:checked + .slider:before { margin-left: 60px; } +ul { + list-style: none; +} + +ul a:before, +p a:before { + color: #00dd00; + content: ' > '; +} + +ul a:after, +p a:after { + color: #00dd00; + content: ' '; +} + ul.sparse { padding-bottom:20px; } @@ -409,3 +429,129 @@ ul.sparse { display: inline-block; vertical-align: middle; } + +.overlay { + height: 1px; + position: absolute; + top: 0; + left: 0; + width: 1px; +} + +.overlay:before { + background: linear-gradient(#101010 50%, rgba(16, 16, 16, 0.02) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.03)); + background-size: 10% 3px, 6px 10%; + content: ""; + display: block; + pointer-events: none; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index:-1; +} + +.overlay:after { + background: rgba(16, 16, 16, 0.01); + content: ""; + display: block; + pointer-events: none; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index:-1; + +} +.scanline { + animation: scrollfade 10s 5s infinite; + pointer-events: none; + background: linear-gradient(to bottom, rgba(0,221,0,0) 0%,rgba(0,221,0,1) 25%,rgba(0,221,0,0) 50%); + filter: progid:DXImageTransform.Microsoft.Gradient( startColorstr='#0000dd00', endColorstr='#0000dd00',GradientType=0 ); + display: block; + height: 20px; + opacity: 0.05; + position: absolute; + left: 0; + right: 0; + top: -5%; + +} + +@keyframes scroll { + 0% { height:0;opacity: 0.15795; } + 5% { opacity: 0.31511 } + 10% { opacity: 0.94554 } + 15% { opacity: 0.2469 } + 20% { opacity: 0.62031 } + 25% { opacity: 0.0293 } + 30% { opacity: 0.00899 } + 35% { opacity: 0.5344 } + 40% { opacity: 0.12778 } + 45% { opacity: 0.52042 } + 50% { opacity: 0.3823 } + 55% { opacity: 0.2198 } + 60% { opacity: 0.9383 } + 65% { opacity: 0.86615 } + 70% { opacity: 0.68695 } + 75% { opacity: 0.55749 } + 80% { opacity: 0.96984 } + 85% { opacity: 0.0361 } + 90% { opacity: 0.24467 } + 95% { opacity: 0.08351 } + 100% { height: 100%;opacity: 0.54813; } +} +@keyframes scrollfade { + 0% { height:0;opacity: 0.15795; } + + 100% { height: 100%;opacity: 0.01; } +} +@keyframes blinky { + 0% { opacity: 0.015795 } + 5% { opacity: 0.031511 } + 10% { opacity: 0.094554 } + 15% { opacity: 0.02469 } + 20% { opacity: 0.062031 } + 25% { opacity: 0.0293 } + 30% { opacity: 0.00899 } + 35% { opacity: 0.05344 } + 40% { opacity: 0.012778 } + 45% { opacity: 0.052042 } + 50% { opacity: 0.03823 } + 55% { opacity: 0.02198 } + 60% { opacity: 0.09383 } + 65% { opacity: 0.086615 } + 70% { opacity: 0.068695 } + 75% { opacity: 0.055749 } + 80% { opacity: 0.096984 } + 85% { opacity: 0.00361 } + 90% { opacity: 0.024467 } + 95% { opacity: 0.08351 } + 100% { opacity: 0.054813 } + } +@keyframes type { + from { width: 0; } +} + +@keyframes type2 { + 0%{width: 0;} + 50%{width: 0;} + 100%{ width: 100; } +} + +@keyframes blink { + to{opacity: .0;} +} + +@keyframes scan { + from { top: 0 } + to { top: 100% } +} +.wrapper { + animation: scroll 5s 1; + margin: 0; + overflow: hidden; + padding: 0; +}