mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Spiritual successor and extension to #17798, an almost entire rebuild of the SQL ban system backend and interface. Bantypes are removed per #8584 and #6174. All bans are now 'role bans', server bans are when a ban's role is server. Admin bans are a column, meaning it's possible to ban admins from jobs. Bans now have only an expiry datetime, duration is calculated from this when queried. unbanned column is removed as it's superfluous, checking unban status is now done through checking unban_datetime. unban_round_id column added. Each ip and computerid columns rearranged so ip is always first, like in other tables. Bans now permit a null ckey, ip and computerid. Ban checking is split into two procs now is_banned_from() does a check if a ckey is banned from one or more roles and returns true or false. This effectively replaces jobban_isbanned() used in simple if() statements. If connected a client's ban cache is checked rather than querying the DB. This makes it possible for a client connected to two or more servers to ignore any bans made on one server until their ban cache is rebuilt on the others. Could be avoided with cross-server calls to update ban caches or just the removal of the ban cache but as is I've done neither since I think it's enough of an edge case to not be worth it. The second proc is is_banned_from_with_details(), this queries the DB for a role ban on a player's ckey, ip or CID and returns the details. This replaces direct queries in IsBanned.dm and the preferences menu. The legacy ban system is removed. The interfaces for banning, unbanning and editing bans have been remade to require less clicking and easier simultaneous operations. The banning and jobban panel are combined. They also store player connection details when opened so a client disconnecting no longer stops a ban being placed. New banning panel: Key, IP and CID can all be toggled to allow excluding them from a ban. Checking Use IP and CID from last connection lets you enter only a ckey and have the DB fill these fields in for you, if possible. Temporary bans have a drop-menu which lets you select between seconds, minutes, hours, days, weeks, months and years so you don't need to calculate how many minutes a long ban would be. The ban is still converted into minutes on the DB however. Checking any of the head roles will check both of the boxes for you. The red role box indicates there is already a ban on that role for this ckey. You can apply additional role bans to stack them. New unbanning panel: Unbanning panel is now separate from the banning panel but otherwise functionally the same. Ban editing panel: Actually just a modified banning panel, all the features from it work the same here. You can now edit almost all parameters of a ban instead of just the reason. You can't edit severity as it's not really part of the ban. The panels have been tested but I've not been able to get my local server to be accessible so ban functionality isn't properly confirmed. Plenty of testing will be required as I'd rather not break bans. cl admin: Ban interface rework. The banning and unbanning panels have received a new design which is easier to use and allows multiple role bans to be made at once. prefix: Ban search and unbanning moved to unbanning panel, which is now a separate panel to the old banning panel. /cl
529 lines
20 KiB
Plaintext
529 lines
20 KiB
Plaintext
#define LINKIFY_READY(string, value) "<a href='byond://?src=[REF(src)];ready=[value]'>[string]</a>"
|
|
|
|
/mob/dead/new_player
|
|
var/ready = 0
|
|
var/spawning = 0//Referenced when you want to delete the new_player later on in the code.
|
|
|
|
flags_1 = NONE
|
|
|
|
invisibility = INVISIBILITY_ABSTRACT
|
|
|
|
density = FALSE
|
|
stat = DEAD
|
|
|
|
var/mob/living/new_character //for instant transfer once the round is set up
|
|
|
|
/mob/dead/new_player/Initialize()
|
|
if(client && SSticker.state == GAME_STATE_STARTUP)
|
|
var/obj/screen/splash/S = new(client, TRUE, TRUE)
|
|
S.Fade(TRUE)
|
|
|
|
if(length(GLOB.newplayer_start))
|
|
forceMove(pick(GLOB.newplayer_start))
|
|
else
|
|
forceMove(locate(1,1,1))
|
|
|
|
ComponentInitialize()
|
|
|
|
. = ..()
|
|
|
|
/mob/dead/new_player/prepare_huds()
|
|
return
|
|
|
|
/mob/dead/new_player/proc/new_player_panel()
|
|
var/output = "<center><p><a href='byond://?src=[REF(src)];show_preferences=1'>Setup Character</a></p>"
|
|
|
|
if(SSticker.current_state <= GAME_STATE_PREGAME)
|
|
switch(ready)
|
|
if(PLAYER_NOT_READY)
|
|
output += "<p>\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | <b>Not Ready</b> | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]</p>"
|
|
if(PLAYER_READY_TO_PLAY)
|
|
output += "<p>\[ <b>Ready</b> | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]</p>"
|
|
if(PLAYER_READY_TO_OBSERVE)
|
|
output += "<p>\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | <b> Observe </b> \]</p>"
|
|
else
|
|
output += "<p><a href='byond://?src=[REF(src)];manifest=1'>View the Crew Manifest</a></p>"
|
|
output += "<p><a href='byond://?src=[REF(src)];late_join=1'>Join Game!</a></p>"
|
|
output += "<p>[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]</p>"
|
|
|
|
if(!IsGuestKey(src.key))
|
|
if (SSdbcore.Connect())
|
|
var/isadmin = 0
|
|
if(src.client && src.client.holder)
|
|
isadmin = 1
|
|
var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")")
|
|
var/rs = REF(src)
|
|
if(query_get_new_polls.Execute())
|
|
var/newpoll = 0
|
|
if(query_get_new_polls.NextRow())
|
|
newpoll = 1
|
|
|
|
if(newpoll)
|
|
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
|
|
else
|
|
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
|
|
qdel(query_get_new_polls)
|
|
if(QDELETED(src))
|
|
return
|
|
|
|
output += "</center>"
|
|
|
|
//src << browse(output,"window=playersetup;size=210x240;can_close=0")
|
|
var/datum/browser/popup = new(src, "playersetup", "<div align='center'>New Player Options</div>", 250, 265)
|
|
popup.set_window_options("can_close=0")
|
|
popup.set_content(output)
|
|
popup.open(FALSE)
|
|
|
|
/mob/dead/new_player/Topic(href, href_list[])
|
|
if(src != usr)
|
|
return 0
|
|
|
|
if(!client)
|
|
return 0
|
|
|
|
//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["late_join"])
|
|
if(!SSticker || !SSticker.IsRoundInProgress())
|
|
to_chat(usr, "<span class='danger'>The round is either not ready, or has already finished...</span>")
|
|
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, "<span class='danger'>[CONFIG_GET(string/hard_popcap_message)]</span>")
|
|
|
|
var/queue_position = SSticker.queued_players.Find(usr)
|
|
if(queue_position == 1)
|
|
to_chat(usr, "<span class='notice'>You are next in line to join the game. You will be notified when a slot opens up.</span>")
|
|
else if(queue_position)
|
|
to_chat(usr, "<span class='notice'>There are [queue_position-1] players in front of you in the queue to join the game.</span>")
|
|
else
|
|
SSticker.queued_players += usr
|
|
to_chat(usr, "<span class='notice'>You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].</span>")
|
|
return
|
|
LateChoices()
|
|
|
|
if(href_list["manifest"])
|
|
ViewManifest()
|
|
|
|
if(href_list["SelectedJob"])
|
|
|
|
if(!GLOB.enter_allowed)
|
|
to_chat(usr, "<span class='notice'>There is an administrative lock on entering the game!</span>")
|
|
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, "<span class='warning'>Server is full.</span>")
|
|
return
|
|
|
|
AttemptLateSpawn(href_list["SelectedJob"])
|
|
return
|
|
|
|
if(!ready && href_list["preference"])
|
|
if(client)
|
|
client.prefs.process_link(src, href_list)
|
|
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, "<span class='notice'>Vote successful.</span>")
|
|
else
|
|
to_chat(usr, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
|
|
if(POLLTYPE_TEXT)
|
|
var/replytext = href_list["replytext"]
|
|
if(log_text_poll_reply(pollid, replytext))
|
|
to_chat(usr, "<span class='notice'>Feedback logging successful.</span>")
|
|
else
|
|
to_chat(usr, "<span class='danger'>Feedback logging failed, please try again or contact an administrator.</span>")
|
|
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, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
|
|
return
|
|
to_chat(usr, "<span class='notice'>Vote successful.</span>")
|
|
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, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
|
|
return
|
|
if(2)
|
|
to_chat(usr, "<span class='danger'>Maximum replies reached.</span>")
|
|
break
|
|
to_chat(usr, "<span class='notice'>Vote successful.</span>")
|
|
if(POLLTYPE_IRV)
|
|
if (!href_list["IRVdata"])
|
|
to_chat(src, "<span class='danger'>No ordering data found. Please try again or contact an administrator.</span>")
|
|
return
|
|
var/list/votelist = splittext(href_list["IRVdata"], ",")
|
|
if (!vote_on_irv_poll(pollid, votelist))
|
|
to_chat(src, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
|
|
return
|
|
to_chat(src, "<span class='notice'>Vote successful.</span>")
|
|
|
|
//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)
|
|
ready = PLAYER_NOT_READY
|
|
return FALSE
|
|
|
|
var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No")
|
|
|
|
if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes")
|
|
ready = PLAYER_NOT_READY
|
|
src << browse(null, "window=playersetup") //closes the player setup window
|
|
new_player_panel()
|
|
return FALSE
|
|
|
|
var/mob/dead/observer/observer = new()
|
|
spawning = TRUE
|
|
|
|
observer.started_as_observer = TRUE
|
|
close_spawn_windows()
|
|
var/obj/effect/landmark/observer_start/O = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list
|
|
to_chat(src, "<span class='notice'>Now teleporting.</span>")
|
|
if (O)
|
|
observer.forceMove(O.loc)
|
|
else
|
|
to_chat(src, "<span class='notice'>Teleporting failed. Ahelp an admin please</span>")
|
|
stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised")
|
|
observer.key = key
|
|
observer.client = client
|
|
observer.set_ghost_appearance()
|
|
if(observer.client && observer.client.prefs)
|
|
observer.real_name = observer.client.prefs.real_name
|
|
observer.name = observer.real_name
|
|
observer.update_icon()
|
|
observer.stop_sound_channel(CHANNEL_LOBBYMUSIC)
|
|
QDEL_NULL(mind)
|
|
qdel(src)
|
|
return TRUE
|
|
|
|
/proc/get_job_unavailable_error_message(retval, jobtitle)
|
|
switch(retval)
|
|
if(JOB_AVAILABLE)
|
|
return "[jobtitle] is available."
|
|
if(JOB_UNAVAILABLE_GENERIC)
|
|
return "[jobtitle] is unavailable."
|
|
if(JOB_UNAVAILABLE_BANNED)
|
|
return "You are currently banned from [jobtitle]."
|
|
if(JOB_UNAVAILABLE_PLAYTIME)
|
|
return "You do not have enough relevant playtime for [jobtitle]."
|
|
if(JOB_UNAVAILABLE_ACCOUNTAGE)
|
|
return "Your account is not old enough for [jobtitle]."
|
|
if(JOB_UNAVAILABLE_SLOTFULL)
|
|
return "[jobtitle] is already filled to capacity."
|
|
return "Error: Unknown job availability."
|
|
|
|
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
|
|
var/datum/job/job = SSjob.GetJob(rank)
|
|
if(!job)
|
|
return JOB_UNAVAILABLE_GENERIC
|
|
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
|
|
if(job.title == "Assistant")
|
|
if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants
|
|
return JOB_AVAILABLE
|
|
for(var/datum/job/J in SSjob.occupations)
|
|
if(J && J.current_positions < J.total_positions && J.title != job.title)
|
|
return JOB_UNAVAILABLE_SLOTFULL
|
|
else
|
|
return JOB_UNAVAILABLE_SLOTFULL
|
|
if(is_banned_from(ckey, rank))
|
|
return JOB_UNAVAILABLE_BANNED
|
|
if(QDELETED(src))
|
|
return JOB_UNAVAILABLE_GENERIC
|
|
if(!job.player_old_enough(client))
|
|
return JOB_UNAVAILABLE_ACCOUNTAGE
|
|
if(job.required_playtime_remaining(client))
|
|
return JOB_UNAVAILABLE_PLAYTIME
|
|
if(latejoin && !job.special_check_latejoin(client))
|
|
return JOB_UNAVAILABLE_GENERIC
|
|
return JOB_AVAILABLE
|
|
|
|
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
|
|
var/error = IsJobUnavailable(rank)
|
|
if(error != JOB_AVAILABLE)
|
|
alert(src, get_job_unavailable_error_message(error, rank))
|
|
return FALSE
|
|
|
|
if(SSticker.late_join_disabled)
|
|
alert(src, "An administrator has disabled late join spawning.")
|
|
return FALSE
|
|
|
|
var/arrivals_docked = TRUE
|
|
if(SSshuttle.arrivals)
|
|
close_spawn_windows() //In case we get held up
|
|
if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin))
|
|
src << alert("The arrivals shuttle is currently malfunctioning! You cannot join.")
|
|
return FALSE
|
|
|
|
if(CONFIG_GET(flag/arrivals_shuttle_require_undocked))
|
|
SSshuttle.arrivals.RequireUndocked(src)
|
|
arrivals_docked = SSshuttle.arrivals.mode != SHUTTLE_CALL
|
|
|
|
//Remove the player from the join queue if he was in one and reset the timer
|
|
SSticker.queued_players -= src
|
|
SSticker.queue_delay = 4
|
|
|
|
SSjob.AssignRole(src, rank, 1)
|
|
|
|
var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind
|
|
character.mind.quiet_round = character.client.prefs.toggles & QUIET_ROUND // yogs - Donor Features
|
|
var/equip = SSjob.EquipRank(character, rank, TRUE)
|
|
if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob.
|
|
character = equip
|
|
|
|
var/datum/job/job = SSjob.GetJob(rank)
|
|
|
|
if(job && !job.override_latejoin_spawn(character))
|
|
SSjob.SendToLateJoin(character)
|
|
if(!arrivals_docked)
|
|
var/obj/screen/splash/Spl = new(character.client, TRUE)
|
|
Spl.Fade(TRUE)
|
|
character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25)
|
|
|
|
character.update_parallax_teleport()
|
|
|
|
SSticker.minds += character.mind
|
|
|
|
var/mob/living/carbon/human/humanc
|
|
if(ishuman(character))
|
|
humanc = character //Let's retypecast the var to be human,
|
|
|
|
if(humanc) //These procs all expect humans
|
|
GLOB.data_core.manifest_inject(humanc)
|
|
if(SSshuttle.arrivals)
|
|
SSshuttle.arrivals.QueueAnnounce(humanc, rank)
|
|
else
|
|
AnnounceArrival(humanc, rank)
|
|
AddEmploymentContract(humanc)
|
|
if(GLOB.highlander)
|
|
to_chat(humanc, "<span class='userdanger'><i>THERE CAN BE ONLY ONE!!!</i></span>")
|
|
humanc.make_scottish()
|
|
|
|
if(GLOB.summon_guns_triggered)
|
|
give_guns(humanc)
|
|
if(GLOB.summon_magic_triggered)
|
|
give_magic(humanc)
|
|
|
|
GLOB.joined_player_list += character.ckey
|
|
|
|
if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc && !character.mind.quiet_round) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais. // yogs - Donor Features
|
|
if(SSshuttle.emergency)
|
|
switch(SSshuttle.emergency.mode)
|
|
if(SHUTTLE_RECALL, SHUTTLE_IDLE)
|
|
SSticker.mode.make_antag_chance(humanc)
|
|
if(SHUTTLE_CALL)
|
|
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5)
|
|
SSticker.mode.make_antag_chance(humanc)
|
|
|
|
if(humanc && CONFIG_GET(flag/roundstart_traits))
|
|
SSquirks.AssignQuirks(humanc, humanc.client, TRUE)
|
|
|
|
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
|
|
|
|
/mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee)
|
|
//TODO: figure out a way to exclude wizards/nukeops/demons from this.
|
|
for(var/C in GLOB.employmentCabinets)
|
|
var/obj/structure/filingcabinet/employment/employmentCabinet = C
|
|
if(!employmentCabinet.virgin)
|
|
employmentCabinet.addFile(employee)
|
|
|
|
|
|
/mob/dead/new_player/proc/LateChoices()
|
|
var/dat = "<div class='notice'>Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]</div>"
|
|
|
|
if(SSshuttle.emergency)
|
|
switch(SSshuttle.emergency.mode)
|
|
if(SHUTTLE_ESCAPE)
|
|
dat += "<div class='notice red'>The station has been evacuated.</div><br>"
|
|
if(SHUTTLE_CALL)
|
|
if(!SSshuttle.canRecall())
|
|
dat += "<div class='notice red'>The station is currently undergoing evacuation procedures.</div><br>"
|
|
|
|
var/available_job_count = 0
|
|
for(var/datum/job/job in SSjob.occupations)
|
|
if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE)
|
|
available_job_count++;
|
|
|
|
for(var/datum/job/prioritized_job in SSjob.prioritized_jobs)
|
|
if(prioritized_job.current_positions >= prioritized_job.total_positions)
|
|
SSjob.prioritized_jobs -= prioritized_job
|
|
|
|
if(length(SSjob.prioritized_jobs))
|
|
dat += "<div class='notice red'>The station has flagged these jobs as high priority:<br>"
|
|
var/amt = length(SSjob.prioritized_jobs)
|
|
var/amt_count
|
|
for(var/datum/job/a in SSjob.prioritized_jobs)
|
|
amt_count++
|
|
if(amt_count != amt) // checks for the last job added.
|
|
dat += " [a.title], "
|
|
else
|
|
dat += " [a.title]. </div>"
|
|
|
|
dat += "<div class='clearBoth'>Choose from the following open positions:</div><br>"
|
|
dat += "<div class='jobs'><div class='jobsColumn'>"
|
|
var/job_count = 0
|
|
for(var/datum/job/job in SSjob.occupations)
|
|
if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE)
|
|
job_count++;
|
|
if (job_count > round(available_job_count / 2))
|
|
dat += "</div><div class='jobsColumn'>"
|
|
var/position_class = "otherPosition"
|
|
if (job.title in GLOB.command_positions)
|
|
position_class = "commandPosition"
|
|
dat += "<a class='[position_class]' href='byond://?src=[REF(src)];SelectedJob=[job.title]'>[job.title] ([job.current_positions])</a><br>"
|
|
if(!job_count) //if there's nowhere to go, overflow opens up.
|
|
for(var/datum/job/job in SSjob.occupations)
|
|
if(job.title != SSjob.overflow_role)
|
|
continue
|
|
dat += "<a class='otherPosition' href='byond://?src=[REF(src)];SelectedJob=[job.title]'>[job.title] ([job.current_positions])</a><br>"
|
|
break
|
|
dat += "</div></div>"
|
|
|
|
// Removing the old window method but leaving it here for reference
|
|
//src << browse(dat, "window=latechoices;size=300x640;can_close=1")
|
|
|
|
// Added the new browser window method
|
|
var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 440, 500)
|
|
popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css')
|
|
popup.set_content(dat)
|
|
popup.open(FALSE) // FALSE is passed to open so that it doesn't use the onclose() proc
|
|
|
|
|
|
/mob/dead/new_player/proc/create_character(transfer_after)
|
|
spawning = 1
|
|
close_spawn_windows()
|
|
|
|
var/mob/living/carbon/human/H = new(loc)
|
|
|
|
var/frn = CONFIG_GET(flag/force_random_names)
|
|
if(!frn)
|
|
frn = is_banned_from(ckey, "Appearance")
|
|
if(QDELETED(src))
|
|
return
|
|
if(frn)
|
|
client.prefs.random_character()
|
|
client.prefs.real_name = client.prefs.pref_species.random_name(gender,1)
|
|
client.prefs.copy_to(H)
|
|
H.dna.update_dna_identity()
|
|
if(mind)
|
|
if(transfer_after)
|
|
mind.late_joiner = TRUE
|
|
mind.active = 0 //we wish to transfer the key manually
|
|
mind.transfer_to(H) //won't transfer key since the mind is not active
|
|
|
|
H.name = real_name
|
|
|
|
. = H
|
|
new_character = .
|
|
if(transfer_after)
|
|
transfer_character()
|
|
|
|
/mob/dead/new_player/proc/transfer_character()
|
|
. = new_character
|
|
if(.)
|
|
new_character.key = key //Manually transfer the key to log them in
|
|
new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC)
|
|
new_character = null
|
|
qdel(src)
|
|
|
|
/mob/dead/new_player/proc/ViewManifest()
|
|
var/dat = "<html><body>"
|
|
dat += "<h4>Crew Manifest</h4>"
|
|
dat += GLOB.data_core.get_manifest(OOC = 1)
|
|
|
|
src << browse(dat, "window=manifest;size=387x420;can_close=1")
|
|
|
|
/mob/dead/new_player/Move()
|
|
return 0
|
|
|
|
|
|
/mob/dead/new_player/proc/close_spawn_windows()
|
|
|
|
src << browse(null, "window=latechoices") //closes late choices window
|
|
src << browse(null, "window=playersetup") //closes the player setup window
|
|
src << browse(null, "window=preferences") //closes job selection
|
|
src << browse(null, "window=mob_occupation")
|
|
src << browse(null, "window=latechoices") //closes late job selection
|