diff --git a/code/datums/helper_datums/input.dm b/code/datums/helper_datums/input.dm
new file mode 100644
index 00000000000..3bcf9509e37
--- /dev/null
+++ b/code/datums/helper_datums/input.dm
@@ -0,0 +1,115 @@
+/proc/input_async(mob/user=usr, prompt, list/choices)
+ var/datum/async_input/A = new(choices, prompt)
+ A.show(user)
+ return A
+
+/proc/input_ranked_async(mob/user=usr, prompt="Order by greatest to least preference", list/choices)
+ var/datum/async_input/ranked/A = new(choices, prompt)
+ A.show(user)
+ return A
+
+/datum/async_input
+ var/datum/browser/popup
+ var/list/choices
+ var/flash = TRUE
+ var/immediate_submit = FALSE
+ var/prompt
+ var/result = null
+ var/style = "text-align: center;"
+ var/window_id
+ var/height = 200
+ var/width = 400
+
+/datum/async_input/New(list/new_choices, new_prompt="Pick an option:", new_window_id="async_input")
+ choices = new_choices
+ prompt = new_prompt
+ window_id = new_window_id
+
+/datum/async_input/proc/close()
+ if(popup)
+ popup.close()
+ return result
+
+/datum/async_input/proc/show(mob/user)
+ var/dat = create_ui(user)
+ popup = new(user, window_id, , width, height, src)
+ popup.set_content(dat)
+ if(flash && result == null)
+ window_flash(user.client)
+ popup.open()
+
+/datum/async_input/proc/create_ui(mob/user)
+ var/dat = "
"
+ dat += render_prompt(user)
+ dat += render_choices(user)
+ dat += "
"
+ dat += "
"
+ dat += button("Submit", "submit=1", , result == null && !immediate_submit)
+ dat += "
"
+ return dat
+
+/datum/async_input/proc/render_prompt(mob/user)
+ return "[prompt]
"
+
+/datum/async_input/proc/render_choices(mob/user)
+ var/dat = " "
+ for(var/choice in choices)
+ dat += button(choice, "choice=[choice]", choice == result)
+ dat += " "
+ return dat
+
+/datum/async_input/proc/button(label, topic, on=FALSE, disabled=FALSE)
+ var/class = ""
+ if(on)
+ class = "linkOn"
+ if(disabled)
+ class = "linkOff"
+ topic = ""
+ return "[label]"
+
+/datum/async_input/Topic(href, href_list)
+ if(href_list["submit"] || href_list["close"])
+ close()
+ return
+
+ if(href_list["choice"])
+ result = href_list["choice"]
+ show(usr)
+ return
+
+/datum/async_input/ranked
+ height = 400
+ immediate_submit = TRUE
+
+/datum/async_input/ranked/render_choices(mob/user)
+ var/dat = ""
+ dat += "
"
+ for(var/i = 1, i <= choices.len, i++)
+ var/choice = choices[i]
+ dat += ""
+ dat += "| [button("+", i != 1 ? "upvote=[i]" : "", , i == 1)] | "
+ dat += "[button("-", i != choices.len ? "downvote=[i]" : "", , i == choices.len)] | "
+ dat += "[i]. [choice] | "
+ dat += "
"
+ dat += "
"
+ dat += "
"
+ return dat
+
+/datum/async_input/ranked/Topic(href, href_list)
+ if(!href_list["close"])
+ // Mark that user interacted with interface
+ result = choices
+
+ if(href_list["upvote"])
+ var/index = text2num(href_list["upvote"])
+ choices.Swap(index, index - 1)
+ show(usr)
+ return
+
+ if(href_list["downvote"])
+ var/index = text2num(href_list["downvote"])
+ choices.Swap(index, index + 1)
+ show(usr)
+ return
+
+ ..()
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 0bac5824780..8fc1ef6d592 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -55,13 +55,9 @@ var/ert_request_answered = FALSE
to_chat(src, "Upon using the antagHUD you forfeited the ability to join the round.")
return 0
- if(response_team_members.len > 6)
- to_chat(src, "The emergency response team is already full!")
- return 0
-
return 1
-/proc/trigger_armed_response_team(var/datum/response_team/response_team_type, commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots)
+/proc/trigger_armed_response_team(datum/response_team/response_team_type, commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots)
response_team_members = list()
active_team = response_team_type
active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots)
@@ -71,7 +67,7 @@ var/ert_request_answered = FALSE
if(!ert_candidates.len)
active_team.cannot_send_team()
send_emergency_team = FALSE
- return 0
+ return
// Respawnable players get first dibs
for(var/mob/dead/observer/M in ert_candidates)
@@ -87,38 +83,58 @@ var/ert_request_answered = FALSE
if(!response_team_members.len)
active_team.cannot_send_team()
send_emergency_team = FALSE
- return 0
+ return
- var/index = 1
+ var/list/ert_gender_prefs = list()
for(var/mob/M in response_team_members)
- if(index > emergencyresponseteamspawn.len)
- index = 1
+ ert_gender_prefs.Add(input_async(M, "Please select a gender (10 seconds):", list("Male", "Female")))
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/get_ert_role_prefs, response_team_members, ert_gender_prefs), 100)
+/proc/get_ert_role_prefs(list/response_team_members, list/ert_gender_prefs)
+ var/list/ert_role_prefs = list()
+ for(var/datum/async_input/A in ert_gender_prefs)
+ A.close()
+ for(var/mob/M in response_team_members)
+ ert_role_prefs.Add(input_ranked_async(M, "Please order ERT roles from most to least preferred (15 seconds):", active_team.get_slot_list()))
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/dispatch_response_team, response_team_members, ert_gender_prefs, ert_role_prefs), 150)
+
+/proc/dispatch_response_team(list/response_team_members, list/ert_gender_prefs, list/ert_role_prefs)
+ var/spawn_index = 1
+
+ for(var/i = 1, i <= response_team_members.len, i++)
+ if(spawn_index > emergencyresponseteamspawn.len)
+ break
+ if(!active_team.get_slot_list().len)
+ break
+ var/gender_pref = ert_gender_prefs[i].result
+ var/role_pref = ert_role_prefs[i].close()
+ var/mob/M = response_team_members[i]
if(!M || !M.client)
continue
- log_debug("Spawning as ERT: [M.ckey] ([M])")
- var/client/C = M.client
- var/mob/living/new_commando = C.create_response_team(emergencyresponseteamspawn[index])
- if(!M || !new_commando)
+ if(!gender_pref || !role_pref)
+ // Player was afk and did not select
continue
- new_commando.mind.key = M.key
- new_commando.key = M.key
- new_commando.update_icons()
- index++
-
+ for(var/role in role_pref)
+ if(active_team.check_slot_available(role))
+ var/mob/living/new_commando = M.client.create_response_team(gender_pref, role, emergencyresponseteamspawn[spawn_index])
+ active_team.reduceSlots(role)
+ spawn_index++
+ if(!M || !new_commando)
+ break
+ new_commando.mind.key = M.key
+ new_commando.key = M.key
+ new_commando.update_icons()
+ break
send_emergency_team = FALSE
- active_team.announce_team()
- return 1
-/client/proc/create_response_team(var/turf/spawn_location)
- var/class = 0
- while(!class)
- class = input(src, "Which loadout would you like to choose?") in active_team.get_slot_list()
- if(!active_team.check_slot_available(class)) // Because the prompt does not update automatically when a slot gets filled.
- class = 0
+ if(active_team.count)
+ active_team.announce_team()
+ return
+ // Everyone who said yes was afk
+ active_team.cannot_send_team()
- if(class == "Cyborg")
- active_team.reduceCyborgSlots()
+/client/proc/create_response_team(new_gender, role, turf/spawn_location)
+ if(role == "Cyborg")
var/cyborg_unlock = active_team.getCyborgUnlock()
var/mob/living/silicon/robot/ert/R = new /mob/living/silicon/robot/ert(spawn_location, cyborg_unlock)
return R
@@ -126,8 +142,6 @@ var/ert_request_answered = FALSE
var/mob/living/carbon/human/M = new(null)
var/obj/item/organ/external/head/head_organ = M.get_organ("head")
- var/new_gender = alert(src, "Please select your gender.", "ERT Character Generation", "Male", "Female")
-
if(new_gender)
if(new_gender == "Male")
M.change_gender(MALE)
@@ -169,21 +183,24 @@ var/ert_request_answered = FALSE
ticker.mode.ert += M.mind
M.forceMove(spawn_location)
- SSjobs.CreateMoneyAccount(M, class, null)
+ SSjobs.CreateMoneyAccount(M, role, null)
- active_team.equip_officer(class, M)
+ active_team.equip_officer(role, M)
return M
/datum/response_team
- var/command_slots = 1
- var/engineer_slots = 3
- var/medical_slots = 3
- var/security_slots = 3
- var/janitor_slots = 0
- var/paranormal_slots = 0
- var/cyborg_slots = 0
+ var/list/slots = list(
+ Commander = 0,
+ Security = 0,
+ Engineer = 0,
+ Medic = 0,
+ Janitor = 0,
+ Paranormal = 0,
+ Cyborg = 0
+ )
+ var/count = 0
var/command_outfit
var/engineering_outfit
@@ -193,88 +210,56 @@ var/ert_request_answered = FALSE
var/paranormal_outfit
var/cyborg_unlock = 0
-/datum/response_team/proc/setSlots(com, sec, med, eng, jan, par, cyb)
- command_slots = com == null ? command_slots : com
- security_slots = sec == null ? security_slots : sec
- medical_slots = med == null ? medical_slots : med
- engineer_slots = eng == null ? engineer_slots : eng
- janitor_slots = jan == null ? janitor_slots : jan
- paranormal_slots = par == null ? paranormal_slots : par
- cyborg_slots = cyb == null ? cyborg_slots : cyb
+/datum/response_team/proc/setSlots(com=1, sec=3, med=3, eng=3, jan=0, par=0, cyb=0)
+ slots["Commander"] = com
+ slots["Security"] = sec
+ slots["Medic"] = med
+ slots["Engineer"] = eng
+ slots["Janitor"] = jan
+ slots["Paranormal"] = par
+ slots["Cyborg"] = cyb
-/datum/response_team/proc/reduceCyborgSlots()
- cyborg_slots--
+/datum/response_team/proc/reduceSlots(role)
+ slots[role]--
+ count++
/datum/response_team/proc/getCyborgUnlock()
return cyborg_unlock
/datum/response_team/proc/get_slot_list()
var/list/slots_available = list()
- if(command_slots)
- slots_available |= "Commander"
- if(security_slots)
- slots_available |= "Security"
- if(engineer_slots)
- slots_available |= "Engineer"
- if(medical_slots)
- slots_available |= "Medic"
- if(janitor_slots)
- slots_available |= "Janitor"
- if(paranormal_slots)
- slots_available |= "Paranormal"
- if(cyborg_slots)
- slots_available |= "Cyborg"
+ for(var/role in slots)
+ if(slots[role])
+ slots_available.Add(role)
return slots_available
-/datum/response_team/proc/check_slot_available(var/slot)
- switch(slot)
- if("Commander")
- return command_slots
- if("Security")
- return security_slots
- if("Engineer")
- return engineer_slots
- if("Medic")
- return medical_slots
- if("Janitor")
- return janitor_slots
- if("Paranormal")
- return paranormal_slots
- if("Cyborg")
- return cyborg_slots
- return 0
+/datum/response_team/proc/check_slot_available(role)
+ return slots[role]
/datum/response_team/proc/equip_officer(var/officer_type, var/mob/living/carbon/human/M)
switch(officer_type)
if("Engineer")
- engineer_slots -= 1
M.equipOutfit(engineering_outfit)
M.job = "ERT Engineering"
if("Security")
- security_slots -= 1
M.equipOutfit(security_outfit)
M.job = "ERT Security"
if("Medic")
- medical_slots -= 1
M.equipOutfit(medical_outfit)
M.job = "ERT Medical"
if("Janitor")
- janitor_slots -= 1
M.equipOutfit(janitor_outfit)
M.job = "ERT Janitor"
if("Paranormal")
- paranormal_slots -= 1
M.equipOutfit(paranormal_outfit)
M.job = "ERT Paranormal"
M.mind.isholy = TRUE
if("Commander")
- command_slots = 0
-
// Override name and age for the commander
M.rename_character(null, "[pick("Lieutenant", "Captain", "Major")] [pick(GLOB.last_names)]")
M.age = rand(35,45)
diff --git a/paradise.dme b/paradise.dme
index f86c7c479c6..792e90013fb 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -337,6 +337,7 @@
#include "code\datums\helper_datums\global_iterator.dm"
#include "code\datums\helper_datums\hotkey_modes.dm"
#include "code\datums\helper_datums\icon_snapshot.dm"
+#include "code\datums\helper_datums\input.dm"
#include "code\datums\helper_datums\map_template.dm"
#include "code\datums\helper_datums\teleport.dm"
#include "code\datums\helper_datums\topic_input.dm"