diff --git a/code/modules/admin/game_master.dm b/code/modules/admin/game_master/game_master.dm similarity index 63% rename from code/modules/admin/game_master.dm rename to code/modules/admin/game_master/game_master.dm index 22f51e856e..a116505f31 100644 --- a/code/modules/admin/game_master.dm +++ b/code/modules/admin/game_master/game_master.dm @@ -2,8 +2,11 @@ set name = "Game Master Panel" set category = "Game Master" - new /datum/game_master(using_client) + if(using_client.game_master_menu) + using_client.game_master_menu.tgui_interact(using_client.mob) + return + using_client.game_master_menu = new /datum/game_master(using_client) /client/proc/toggle_game_master() set name = "Game Master Panel" @@ -20,15 +23,34 @@ #define DEFAULT_XENO_AMOUNT_TO_SPAWN 1 +/// Types of click intercepts used by /datum/game_master variable current_click_intercept_action #define SPAWN_CLICK_INTERCEPT_ACTION "spawn_click_intercept_action" + /datum/game_master + /// Associated list of game master submenus organized by object_type = game_master_submenu + var/list/submenu_types = list( + /obj/structure/pipes/vents/scrubber = /datum/game_master_submenu/vents, + /obj/structure/pipes/vents/pump = /datum/game_master_submenu/vents, + ) + + /// List of current submenus + var/list/current_submenus + + /// The xeno selected to be spawned in the spawn section var/selected_xeno = DEFAULT_SPAWN_XENO_STRING - var/xenos_to_spawn = DEFAULT_XENO_AMOUNT_TO_SPAWN + + /// The amount of xenos to spawn in the spawn section + var/xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN + + /// If the spawned xeno is an AI in the spawn section var/spawn_ai = TRUE + + /// If we are currently using the click intercept for the spawn section var/spawn_click_intercept = FALSE + /// Holds what type of click intercept we are using var/current_click_intercept_action /datum/game_master/New(client/using_client) @@ -37,8 +59,14 @@ if(using_client.mob) tgui_interact(using_client.mob) + current_submenus = list() + + using_client.click_intercept = src + /datum/game_master/Destroy(force, ...) . = ..() + submenu_types = null + current_submenus = null /datum/game_master/ui_data(mob/user) . = ..() @@ -68,14 +96,11 @@ switch(action) if("toggle_click_spawn") - var/client/user_client = ui.user.client - if(user_client.click_intercept == src) - user_client.click_intercept = null + if(spawn_click_intercept) spawn_click_intercept = FALSE current_click_intercept_action = null return - user_client.click_intercept = src spawn_click_intercept = TRUE current_click_intercept_action = SPAWN_CLICK_INTERCEPT_ACTION return @@ -86,7 +111,7 @@ if("set_selected_xeno") selected_xeno = params["new_xeno"] - xenos_to_spawn = DEFAULT_XENO_AMOUNT_TO_SPAWN + xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN return if("set_xeno_spawns") @@ -94,7 +119,7 @@ if(!new_number) return - xenos_to_spawn = clamp(new_number, 1, 10) + xeno_spawn_count = clamp(new_number, 1, 10) return /datum/game_master/ui_close(mob/user) @@ -104,8 +129,6 @@ if(user_client?.click_intercept == src) user_client.click_intercept = null - qdel(src) - /datum/game_master/ui_status(mob/user, datum/ui_state/state) return UI_INTERACTIVE @@ -115,7 +138,12 @@ ui = new(user, src, "GameMaster", "Game Master Menu") ui.open() + user.client?.click_intercept = src + /datum/game_master/proc/InterceptClickOn(mob/user, params, atom/object) + + var/list/modifiers = params2list(params) + switch(current_click_intercept_action) if(SPAWN_CLICK_INTERCEPT_ACTION) var/spawning_xeno_type = RoleAuthority.get_caste_by_text(selected_xeno) @@ -126,10 +154,23 @@ var/turf/spawn_turf = get_turf(object) - for(var/i = 1 to xenos_to_spawn) + for(var/i = 1 to xeno_spawn_count) new spawning_xeno_type(spawn_turf, null, XENO_HIVE_NORMAL, !spawn_ai) - return + return TRUE + + else + if(LAZYACCESS(modifiers, MIDDLE_CLICK) && (object.type in submenu_types)) + for(var/datum/game_master_submenu/submenu in current_submenus) + if(submenu.referenced_atom == object) + submenu.tgui_interact(user) + return TRUE + + var/new_menu_type = submenu_types[object.type] + + current_submenus += new new_menu_type(user, object) + return TRUE + #undef DEFAULT_SPAWN_XENO_STRING #undef GAME_MASTER_AI_XENOS diff --git a/code/modules/admin/game_master/game_master_submenu.dm b/code/modules/admin/game_master/game_master_submenu.dm new file mode 100644 index 0000000000..2cff5d3be6 --- /dev/null +++ b/code/modules/admin/game_master/game_master_submenu.dm @@ -0,0 +1,41 @@ + + +/// Called by /datum/game_master and handles various submenus and unique interactions with atoms +/datum/game_master_submenu + + /// The atom this submenu is interacting with + var/atom/referenced_atom + + /// The name of the menu we are opening, referencing the actual code + var/tgui_menu_name + + /// The name of the window we are opening, *not* the actual code reference + var/tgui_menu_title + +/datum/game_master_submenu/New(mob/user, atom/object) + . = ..() + + referenced_atom = object + RegisterSignal(referenced_atom, COMSIG_PARENT_QDELETING, PROC_REF(referenced_atom_destroyed)) + + if(user) + tgui_interact(user) + +/datum/game_master_submenu/Destroy(force, ...) + . = ..() + referenced_atom = null + +/// Callback for when the referenced atom is destroyed so we want to destroy this datum which will close the window attached +/datum/game_master_submenu/proc/referenced_atom_destroyed() + SIGNAL_HANDLER + + qdel(src) + +/datum/game_master_submenu/ui_status(mob/user, datum/ui_state/state) + return UI_INTERACTIVE + +/datum/game_master_submenu/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui && tgui_menu_name && tgui_menu_title) + ui = new(user, src, tgui_menu_name, tgui_menu_title) + ui.open() diff --git a/code/modules/admin/game_master/game_master_submenu/vents.dm b/code/modules/admin/game_master/game_master_submenu/vents.dm new file mode 100644 index 0000000000..3049fe9a29 --- /dev/null +++ b/code/modules/admin/game_master/game_master_submenu/vents.dm @@ -0,0 +1,166 @@ + +#define DEFAULT_SPAWN_XENO_STRING XENO_CASTE_DRONE +#define GAME_MASTER_VENT_AI_XENOS list(XENO_CASTE_DRONE, XENO_CASTE_RUNNER) + +#define DEFAULT_XENO_AMOUNT_TO_SPAWN 1 + + +/// Created by /datum/game_master when right clicking on a /obj/structure/pipes/vents/scrubber or /obj/structure/pipes/vents/pump +/datum/game_master_submenu/vents + tgui_menu_name = "GameMasterSubmenuVents" + tgui_menu_title = "Vent Control" + + /// Amount of xenos we want to spawn when we hit spawn + var/xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN + + /// Current selected xeno string to spawn when we hit spawn + var/selected_xeno = DEFAULT_SPAWN_XENO_STRING + + /// Current xenos to spawn in an ambush, organized as xeno_type = number_to_spawn + var/list/current_ambush + + /// Current info on the ambush passed to the menu + var/ambush_info = "No Current Ambush" + + /// Are we currently ambushing? + var/ambushing = FALSE + +/datum/game_master_submenu/vents/New(client/using_client) + . = ..() + current_ambush = list() + + var/turf/reference_turf = get_turf(referenced_atom) + + var/list/ambush_turfs = block(locate(max(reference_turf.x - 4, 1), max(reference_turf.y - 4, 1), reference_turf.z), locate(reference_turf.x + 4, reference_turf.y + 4, reference_turf.z)) + + for(var/turf/cycled_turf as anything in ambush_turfs) + RegisterSignal(cycled_turf, COMSIG_TURF_ENTERED, PROC_REF(ambush_turf_movement)) + +/datum/game_master_submenu/vents/Destroy(force, ...) + . = ..() + current_ambush = null + +/datum/game_master_submenu/vents/ui_data(mob/user) + . = ..() + + var/list/data = list() + + data["ambush_info"] = ambush_info + + return data + + +/datum/game_master_submenu/vents/ui_static_data(mob/user) + . = ..() + + var/list/data = list() + + data["default_spawnable_xeno_string"] = DEFAULT_SPAWN_XENO_STRING + data["spawnable_xenos"] = GAME_MASTER_VENT_AI_XENOS + + return data + + +/datum/game_master_submenu/vents/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + + switch(action) + if("set_xeno_spawns") + var/new_number = text2num(params["value"]) + if(!new_number) + return + + xeno_spawn_count = clamp(new_number, 1, 10) + return + + if("set_selected_xeno") + selected_xeno = params["new_xeno"] + xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN + return + + if("ambush") + setup_ambush() + return + + if("spawn") + handle_vent_spawn() + return + + if("clear_ambush") + current_ambush = list() + ambush_info = initial(ambush_info) + return + +/// Callback for when one of our registered turfs has something move across it, tells the ambush to start if set up +/datum/game_master_submenu/vents/proc/ambush_turf_movement(turf/crossed_turf, atom/movable/entering_movable) + SIGNAL_HANDLER + + if(length(current_ambush) && !ambushing && ishuman(entering_movable)) + ambushing = TRUE + handle_vent_spawn(TRUE) + +/// Sets current ambush data for a potential ambush +/datum/game_master_submenu/vents/proc/setup_ambush() + current_ambush[selected_xeno] = xeno_spawn_count + + var/temp_string = "Current Ambush: " + for(var/ambusher_type in current_ambush) + temp_string += "[current_ambush[ambusher_type]] [ambusher_type], " + + temp_string = copytext(temp_string, 1, -2) + + ambush_info = temp_string + +#define VENT_ESCAPE_INCREMENT_TIME (1 SECONDS) + +/// Shakes the vent and creates timers to spawn multiple xenos in succession +/datum/game_master_submenu/vents/proc/handle_vent_spawn(ambush) + var/obj/structure/pipes/vents/reference_vent = referenced_atom + if(!istype(reference_vent)) + log_debug("Vent game master submenu has reference atom that is not a vent. Referenced atom: [referenced_atom]") + return + + reference_vent.animate_ventcrawl() + var/timer_increment = VENT_ESCAPE_INCREMENT_TIME + reference_vent.visible_message(SPAN_NOTICE("Something begins climbing out of [reference_vent]!")) + + if(ambush) + for(var/ambusher_type in current_ambush) + for(var/i = 1 to current_ambush[ambusher_type]) + addtimer(CALLBACK(src, PROC_REF(spawn_xeno), ambusher_type), timer_increment) + timer_increment += VENT_ESCAPE_INCREMENT_TIME + + addtimer(CALLBACK(reference_vent, TYPE_PROC_REF(/obj/structure/pipes/vents, animate_ventcrawl_reset)), timer_increment) + current_ambush = list() + ambush_info = initial(ambush_info) + ambushing = FALSE + return + + for(var/i = 1 to xeno_spawn_count) + addtimer(CALLBACK(src, PROC_REF(spawn_xeno), selected_xeno), timer_increment) + timer_increment += VENT_ESCAPE_INCREMENT_TIME + + addtimer(CALLBACK(reference_vent, TYPE_PROC_REF(/obj/structure/pipes/vents, animate_ventcrawl_reset)), timer_increment) + +#undef VENT_ESCAPE_INCREMENT_TIME + +/// Actually spawns the xeno at the vent +/datum/game_master_submenu/vents/proc/spawn_xeno(spawning_xeno_name) + var/turf/spawn_turf = get_turf(referenced_atom) + + if(!spawn_turf) + log_debug("Vent game master submenu unable to find turf of referenced atom. Referenced atom: [referenced_atom]") + return + + var/xeno_type = RoleAuthority.get_caste_by_text(spawning_xeno_name) + + if(!xeno_type) + log_debug("Vent game master submenu unable to find xeno type. Xeno type name: [spawning_xeno_name]") + return + + new xeno_type(spawn_turf, null, XENO_HIVE_NORMAL) + + +#undef DEFAULT_SPAWN_XENO_STRING +#undef GAME_MASTER_VENT_AI_XENOS +#undef DEFAULT_XENO_AMOUNT_TO_SPAWN diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 2facce7c3a..f09023408f 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -131,3 +131,6 @@ ///datum that controls the displaying and hiding of tooltips var/datum/tooltip/tooltips + + /// Holds the game master datum for this client + var/datum/game_master/game_master_menu diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 3cfb08b8da..bce5757a90 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -461,6 +461,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list( /client/Destroy() QDEL_NULL(soundOutput) QDEL_NULL(obj_window) + QDEL_NULL(game_master_menu) if(prefs) prefs.owner = null QDEL_NULL(prefs.preview_dummy) diff --git a/colonialmarines.dme b/colonialmarines.dme index 11184b01ce..aa5ca26462 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -1321,7 +1321,6 @@ s// DM Environment file for colonialmarines.dme. #include "code\modules\admin\create_object.dm" #include "code\modules\admin\create_turf.dm" #include "code\modules\admin\fax_templates.dm" -#include "code\modules\admin\game_master.dm" #include "code\modules\admin\holder2.dm" #include "code\modules\admin\IsBanned.dm" #include "code\modules\admin\NewBan.dm" @@ -1331,6 +1330,9 @@ s// DM Environment file for colonialmarines.dme. #include "code\modules\admin\STUI.dm" #include "code\modules\admin\tag.dm" #include "code\modules\admin\ToRban.dm" +#include "code\modules\admin\game_master\game_master.dm" +#include "code\modules\admin\game_master\game_master_submenu.dm" +#include "code\modules\admin\game_master\game_master_submenu\vents.dm" #include "code\modules\admin\medal_panel\medals_panel.dm" #include "code\modules\admin\medal_panel\medals_panel_tgui.dm" #include "code\modules\admin\player_panel\player_action.dm" diff --git a/tgui/packages/tgui/interfaces/GameMasterSubmenuVents.js b/tgui/packages/tgui/interfaces/GameMasterSubmenuVents.js new file mode 100644 index 0000000000..792362d62f --- /dev/null +++ b/tgui/packages/tgui/interfaces/GameMasterSubmenuVents.js @@ -0,0 +1,67 @@ +import { useBackend } from '../backend'; +import { Stack, Dropdown, Button } from '../components'; +import { Window } from '../layouts'; + +export const GameMasterSubmenuVents = (props, context) => { + const { data, act } = useBackend(context); + + return ( + + + + + + + { + act('set_xeno_spawns', { value }); + }} + /> + + + { + act('set_selected_xeno', { new_xeno }); + }} + /> + + + + +