diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 3b63dd4a5ca..6ed9272a9ff 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -10,6 +10,9 @@ #define NUKE_RESULT_HIJACK_DISK 9 #define NUKE_RESULT_HIJACK_NO_DISK 10 +/// Min players requireed for nukes to declare war +#define CHALLENGE_MIN_PLAYERS 50 + //fugitive end results #define FUGITIVE_RESULT_BADASS_HUNTER 0 #define FUGITIVE_RESULT_POSTMORTEM_HUNTER 1 diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1b601bb0cc9..b52405e204a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1766,6 +1766,10 @@ var/obj/item/nuclear_challenge/button = locate(href_list["force_war"]) button.force_war() + else if(href_list["give_reinforcement"]) + var/datum/team/nuclear/nuketeam = locate(href_list["give_reinforcement"]) in GLOB.antagonist_teams + nuketeam.admin_spawn_reinforcement(usr) + else if (href_list["interview"]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm index eeb4d255bf6..f3d2bde6fb7 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm @@ -1,6 +1,5 @@ #define CHALLENGE_TELECRYSTALS 280 #define CHALLENGE_TIME_LIMIT (5 MINUTES) -#define CHALLENGE_MIN_PLAYERS 50 #define CHALLENGE_SHUTTLE_DELAY (25 MINUTES) // 25 minutes, so the ops have at least 5 minutes before the shuttle is callable. GLOBAL_LIST_EMPTY(jam_on_wardec) @@ -54,7 +53,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) ///Admin only proc to bypass checks and force a war declaration. Button on antag panel. /obj/item/nuclear_challenge/proc/force_war() - var/are_you_sure = tgui_alert(usr, "Are you sure you wish to force a war declaration?", "Declare war?", list("Yes", "No")) + var/are_you_sure = tgui_alert(usr, "Are you sure you wish to force a war declaration?[GLOB.player_list.len < CHALLENGE_MIN_PLAYERS ? " Note, the player count is under the required limit." : ""]", "Declare war?", list("Yes", "No")) if(are_you_sure != "Yes") return @@ -67,9 +66,14 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) war_declaration = tgui_input_text(usr, "Insert your custom declaration", "Declaration", multiline = TRUE, encode = FALSE) if(!war_declaration) - to_chat(usr, span_warning("Invalid war declaration.")) + tgui_alert(usr, "Invalid war declaration.", "Poor Choice of Words") return + for(var/obj/item/circuitboard/computer/syndicate_shuttle/board as anything in GLOB.syndicate_shuttle_boards) + if(board.challenge) + tgui_alert(usr, "War has already been declared!", "War Was Declared") + return + war_was_declared(memo = war_declaration) /obj/item/nuclear_challenge/proc/war_was_declared(mob/living/user, memo) @@ -148,6 +152,9 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) if(board.moved) to_chat(user, span_boldwarning("The shuttle has already been moved! You have forfeit the right to declare war.")) return FALSE + if(board.challenge) + to_chat(user, span_boldwarning("War has already been declared!")) + return FALSE return TRUE /obj/item/nuclear_challenge/clownops @@ -189,5 +196,4 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) #undef CHALLENGE_TELECRYSTALS #undef CHALLENGE_TIME_LIMIT -#undef CHALLENGE_MIN_PLAYERS #undef CHALLENGE_SHUTTLE_DELAY diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index c9ad46b1415..7690ab19efd 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -521,12 +521,116 @@ disk_loc = disk_loc.loc disk_report += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])FLW" disk_report += "" - var/common_part = ..() - var/challenge_report - var/obj/item/nuclear_challenge/war_button = war_button_ref?.resolve() - if(war_button) - challenge_report += "War not declared. \[Force war\]" - return common_part + disk_report + challenge_report + + var/post_report + + var/war_declared = FALSE + for(var/obj/item/circuitboard/computer/syndicate_shuttle/board as anything in GLOB.syndicate_shuttle_boards) + if(board.challenge) + war_declared = TRUE + + var/force_war_button = "" + + if(war_declared) + post_report += "War declared." + force_war_button = "\[Force war\]" + else + post_report += "War not declared." + var/obj/item/nuclear_challenge/war_button = war_button_ref?.resolve() + if(war_button) + force_war_button = "\[Force war\]" + else + force_war_button = "\[Cannot declare war, challenge button missing!\]" + + post_report += "\n[force_war_button]" + post_report += "\n\[Send Reinforcement\]" + + var/final_report = ..() + final_report += disk_report + final_report += post_report + return final_report + +#define SPAWN_AT_BASE "Nuke base" +#define SPAWN_AT_INFILTRATOR "Infiltrator" + +/datum/team/nuclear/proc/admin_spawn_reinforcement(mob/admin) + if(!check_rights_for(admin.client, R_ADMIN)) + return + + var/infil_or_nukebase = tgui_alert( + admin, + "Spawn them at the nuke base, or in the Infiltrator?", + "Where to reinforce?", + list(SPAWN_AT_BASE, SPAWN_AT_INFILTRATOR, "Cancel"), + ) + + if(!infil_or_nukebase || infil_or_nukebase == "Cancel") + return + + var/tc_to_spawn = tgui_input_number(admin, "How much TC to spawn with?", "TC", 0, 100) + + var/list/nuke_candidates = poll_ghost_candidates( + "Do you want to play as an emergency syndicate reinforcement?", + ROLE_OPERATIVE, + ROLE_OPERATIVE, + 30 SECONDS, + POLL_IGNORE_SYNDICATE, + ) + + nuke_candidates -= admin // may be easy to fat-finger say yes. so just don't + + if(!length(nuke_candidates)) + tgui_alert(admin, "No candidates found.", "Recruitment Shortage", list("OK")) + return + + + var/turf/spawn_loc + if(infil_or_nukebase == SPAWN_AT_INFILTRATOR) + var/area/spawn_in + // Prioritize EVA then hallway, if neither can be found default to the first area we can find + for(var/area_type in list(/area/shuttle/syndicate/eva, /area/shuttle/syndicate/hallway, /area/shuttle/syndicate)) + spawn_in = locate(area_type) in GLOB.areas // I'd love to use areas_by_type but the Infiltrator is a unique area + if(spawn_in) + break + + var/list/turf/options = list() + for(var/turf/open/open_turf in spawn_in?.get_contained_turfs()) + if(open_turf.is_blocked_turf()) + continue + options += open_turf + + if(length(options)) + spawn_loc = pick(options) + else + infil_or_nukebase = SPAWN_AT_BASE + + if(infil_or_nukebase == SPAWN_AT_BASE) + spawn_loc = pick(GLOB.nukeop_start) + + var/mob/dead/observer/picked = pick(nuke_candidates) + var/mob/living/carbon/human/nukie = new(spawn_loc) + picked.client.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE) + nukie.key = picked.key + + var/datum/antagonist/nukeop/antag_datum = new() + antag_datum.send_to_spawnpoint = FALSE + antag_datum.nukeop_outfit = /datum/outfit/syndicate/reinforcement + + nukie.mind.add_antag_datum(antag_datum, src) + + var/datum/component/uplink/uplink = nukie.mind.find_syndicate_uplink() + uplink?.set_telecrystals(tc_to_spawn) + + // add some pizzazz + do_sparks(4, FALSE, spawn_loc) + new /obj/effect/temp_visual/teleport_abductor/syndi_teleporter(spawn_loc) + playsound(spawn_loc, SFX_SPARKS, 50, TRUE) + playsound(spawn_loc, 'sound/effects/phasein.ogg', 50, TRUE) + + tgui_alert(admin, "Reinforcement spawned at [infil_or_nukebase] with [tc_to_spawn].", "Reinforcements have arrived", list("God speed")) + +#undef SPAWN_AT_BASE +#undef SPAWN_AT_INFILTRATOR /// Returns whether or not syndicate operatives escaped. /proc/is_infiltrator_docked_at_syndiebase()