diff --git a/baystation12.dme b/baystation12.dme index a4dce912325..8b391a9e3ea 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -236,6 +236,20 @@ #include "code\game\gamemodes\malfunction\malfunction.dm" #include "code\game\gamemodes\meteor\meteor.dm" #include "code\game\gamemodes\meteor\meteors.dm" +#include "code\game\gamemodes\mutiny\auth_key.dm" +#include "code\game\gamemodes\mutiny\directive.dm" +#include "code\game\gamemodes\mutiny\emergency_authentication_device.dm" +#include "code\game\gamemodes\mutiny\key_pinpointer.dm" +#include "code\game\gamemodes\mutiny\mutiny.dm" +#include "code\game\gamemodes\mutiny\mutiny_fluff.dm" +#include "code\game\gamemodes\mutiny\mutiny_hooks.dm" +#include "code\game\gamemodes\mutiny\directives\alien_fraud_directive.dm" +#include "code\game\gamemodes\mutiny\directives\bluespace_contagion_directive.dm" +#include "code\game\gamemodes\mutiny\directives\financial_crisis_directive.dm" +#include "code\game\gamemodes\mutiny\directives\ipc_virus_directive.dm" +#include "code\game\gamemodes\mutiny\directives\research_to_ripleys_directive.dm" +#include "code\game\gamemodes\mutiny\directives\tau_ceti_needs_women_directive.dm" +#include "code\game\gamemodes\mutiny\directives\terminations_directive.dm" #include "code\game\gamemodes\ninja\ninja.dm" #include "code\game\gamemodes\nuclear\nuclear.dm" #include "code\game\gamemodes\nuclear\nuclearbomb.dm" diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index 51f56d9d122..bcd52b9e888 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -1,3 +1,9 @@ +#define SECOND *10 +#define SECONDS *10 + +#define MINUTE *600 +#define MINUTES *600 + //Returns the world time in english proc/worldtime2text(time = world.time) return "[round(time / 36000)+12]:[(time / 600 % 60) < 10 ? add_zero(time / 600 % 60, 1) : time / 600 % 60]" diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index cb51229c9fb..1cd7c4aad8a 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -15,3 +15,73 @@ * Called in gameticker.dm when a round ends. */ /hook/roundend + +/** + * Death hook. + * Called in death.dm when someone dies. + * Parameters: var/mob/living/carbon/human, var/gibbed + */ +/hook/death + +/** + * Cloning hook. + * Called in cloning.dm when someone is brought back by the wonders of modern science. + * Parameters: var/mob/living/carbon/human + */ +/hook/clone + +/** + * Debrained hook. + * Called in brain_item.dm when someone gets debrained. + * Parameters: var/obj/item/brain + */ +/hook/debrain + +/** + * Borged hook. + * Called in robot_parts.dm when someone gets turned into a cyborg. + * Parameters: var/mob/living/silicon/robot + */ +/hook/borgify + +/** + * Podman hook. + * Called in podmen.dm when someone is brought back as a Diona. + * Parameters: var/mob/living/carbon/monkey/diona + */ +/hook/harvest_podman + +/** + * Payroll revoked hook. + * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/revoke_payroll + +/** + * Account suspension hook. + * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/change_account_status + +/** + * Employee reassignment hook. + * Called in card.dm when someone's card is reassigned at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/reassign_employee + +/** + * Employee terminated hook. + * Called in card.dm when someone's card is terminated at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/terminate_employee + +/** + * Crate sold hook. + * Called in supplyshuttle.dm when a crate is sold on the shuttle. + * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle + */ +/hook/sell_crate diff --git a/code/controllers/hooks.dm b/code/controllers/hooks.dm index 5045da75925..48f1199ef76 100644 --- a/code/controllers/hooks.dm +++ b/code/controllers/hooks.dm @@ -23,7 +23,7 @@ * @param hook Identifier of the hook to call. * @returns 1 if all hooked code runs successfully, 0 otherwise. */ -/proc/callHook(hook) +/proc/callHook(hook, list/args=null) var/hook_path = text2path("/hook/[hook]") if(!hook_path) error("Invalid hook '/hook/[hook]' called.") @@ -32,7 +32,7 @@ var/caller = new hook_path var/status = 1 for(var/P in typesof("[hook_path]/proc")) - if(!call(caller, P)()) + if(!call(caller, P)(arglist(args))) error("Hook '[P]' failed or runtimed.") status = 0 diff --git a/code/controllers/shuttle_controller.dm b/code/controllers/shuttle_controller.dm index f92d8b1c7b1..f889a37e17e 100644 --- a/code/controllers/shuttle_controller.dm +++ b/code/controllers/shuttle_controller.dm @@ -36,7 +36,7 @@ datum/shuttle_controller if(direction == -1) setdirection(1) else - settimeleft(SHUTTLEARRIVETIME*coeff) + settimeleft(get_shuttle_arrive_time()*coeff) online = 1 if(always_fake_recall) fake_recall = rand(300,500) //turning on the red lights in hallways @@ -45,15 +45,21 @@ datum/shuttle_controller if(istype(A, /area/hallway)) A.readyalert() + proc/get_shuttle_arrive_time() + // During mutiny rounds, the shuttle takes twice as long. + if(ticker && istype(ticker.mode,/datum/game_mode/mutiny)) + return SHUTTLEARRIVETIME * 2 + + return SHUTTLEARRIVETIME + datum/shuttle_controller/proc/shuttlealert(var/X) alert = X - datum/shuttle_controller/proc/recall() if(direction == 1) var/timeleft = timeleft() if(alert == 0) - if(timeleft >= 600) + if(timeleft >= get_shuttle_arrive_time()) return captain_announce("The emergency shuttle has been recalled.") world << sound('sound/AI/shuttlerecalled.ogg') @@ -78,9 +84,9 @@ datum/shuttle_controller/proc/timeleft() if(direction == 1 || direction == 2) return timeleft else - return SHUTTLEARRIVETIME-timeleft + return get_shuttle_arrive_time()-timeleft else - return SHUTTLEARRIVETIME + return get_shuttle_arrive_time() // sets the time left to a given delay (in seconds) datum/shuttle_controller/proc/settimeleft(var/delay) @@ -95,7 +101,7 @@ datum/shuttle_controller/proc/setdirection(var/dirn) direction = dirn // if changing direction, flip the timeleft by SHUTTLEARRIVETIME var/ticksleft = endtime - world.timeofday - endtime = world.timeofday + (SHUTTLEARRIVETIME*10 - ticksleft) + endtime = world.timeofday + (get_shuttle_arrive_time()*10 - ticksleft) return datum/shuttle_controller/proc/process() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index fe252c3ab32..e97c915c0ba 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -28,6 +28,7 @@ var/required_enemies = 0 var/recommended_enemies = 0 var/newscaster_announcements = null + var/ert_disabled = 0 var/uplink_welcome = "Syndicate Uplink Console:" var/uplink_uses = 10 var/uplink_items = {"Highly Visible and Dangerous Weapons; @@ -407,6 +408,9 @@ Implants; heads += player.mind return heads +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + /datum/game_mode/New() newscaster_announcements = pick(newscaster_standard_feeds) diff --git a/code/game/gamemodes/mutiny/auth_key.dm b/code/game/gamemodes/mutiny/auth_key.dm new file mode 100644 index 00000000000..1f7ea3c888b --- /dev/null +++ b/code/game/gamemodes/mutiny/auth_key.dm @@ -0,0 +1,39 @@ +/obj/item/weapon/mutiny/auth_key + name = "authentication key" + desc = "Better keep this safe." + icon = 'icons/obj/items.dmi' + icon_state = "nucleardisk" + item_state = "card-id" + w_class = 1 + + var/time_entered_space + var/obj/item/device/radio/radio + + New() + radio = new(src) + spawn(20 SECONDS) + keep_alive() + ..() + + proc/keep_alive() + var/in_space = istype(loc, /turf/space) + if (!in_space && time_entered_space) + // Recovered before the key was lost + time_entered_space = null + else if (in_space && !time_entered_space) + // The key has left the station + time_entered_space = world.time + else if (in_space && time_entered_space + (10 SECONDS) < world.time) + // Time is up + radio.autosay("This device has left the station's perimeter. Triggering emergency activation failsafe.", name) + del(src) + return + + spawn(10 SECONDS) + keep_alive() + +/obj/item/weapon/mutiny/auth_key/captain + name = "Captain's Authentication Key" + +/obj/item/weapon/mutiny/auth_key/secondary + name = "Emergency Secondary Authentication Key" diff --git a/code/game/gamemodes/mutiny/directive.dm b/code/game/gamemodes/mutiny/directive.dm new file mode 100644 index 00000000000..3cb9071dc73 --- /dev/null +++ b/code/game/gamemodes/mutiny/directive.dm @@ -0,0 +1,29 @@ +datum/directive + var/datum/game_mode/mutiny/mode + var/list/special_orders + + New(var/datum/game_mode/mutiny/M) + mode = M + + proc/get_description() + return {" +

+ NanoTrasen's reasons for the following directives are classified. +

+ "} + + proc/meets_prerequisites() + return 0 + + proc/directives_complete() + return 1 + + proc/initialize() + return 1 + +/proc/get_directive(type) + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if(!mode || !mode.current_directive || !istype(mode.current_directive, text2path("/datum/directive/[type]"))) + return null + + return mode.current_directive diff --git a/code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm b/code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm new file mode 100644 index 00000000000..16dcd1de009 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm @@ -0,0 +1,41 @@ +datum/directive/terminations/alien_fraud + special_orders = list( + "Suspend financial accounts of all Tajaran and Unathi personnel.", + "Transfer their payrolls to the station account.", + "Terminate their employment.") + + proc/is_alien(mob/M) + var/species = M.get_species() + return species == "Tajaran" || species == "Unathi" + +datum/directive/terminations/alien_fraud/get_crew_to_terminate() + var/list/aliens[0] + for(var/mob/M in player_list) + if (is_alien(M) && M.is_ready()) + aliens.Add(M) + return aliens + +datum/directive/terminations/alien_fraud/get_description() + return {" +

+ An extensive conspiracy network aimed at defrauding NanoTrasen of large amounts of funds has been uncovered + operating within Tau Ceti. Human personnel are not suspected to be involved. Further information is classified. +

+ "} + +datum/directive/terminations/alien_fraud/meets_prerequisites() + // There must be at least one Tajaran and at least one Unathi, but the total + // of the Tajarans and Unathi combined can't be more than 1/3rd of the crew. + var/tajarans = 0 + var/unathi = 0 + for(var/mob/M in player_list) + var/species = M.get_species() + if(species == "Tajaran") + tajarans++ + if(species == "Unathi") + unathi++ + + if (!tajarans || !unathi) + return 0 + + return (tajarans + unathi) <= (player_list.len / 3) diff --git a/code/game/gamemodes/mutiny/directives/bluespace_contagion_directive.dm b/code/game/gamemodes/mutiny/directives/bluespace_contagion_directive.dm new file mode 100644 index 00000000000..9209ee09db1 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/bluespace_contagion_directive.dm @@ -0,0 +1,52 @@ +#define INFECTION_COUNT 5 + +datum/directive/bluespace_contagion + var/list/infected = list() + + proc/get_infection_candidates() + var/list/candidates[0] + for(var/mob/M in player_list) + if (!M.is_mechanical() && M.is_ready()) + candidates.Add(M) + return candidates + +datum/directive/bluespace_contagion/get_description() + return {" +

+ A manufactured and near-undetectable virus is spreading on NanoTrasen stations. + The pathogen travels by bluespace after maturing for one day. + No treatment has yet been discovered. Personnel onboard [station_name()] have been infected. Further information is classified. +

+ "} + +datum/directive/bluespace_contagion/initialize() + var/list/candidates = get_infection_candidates() + var/list/infected_names = list() + for(var/i=0, i < INFECTION_COUNT, i++) + if(!candidates.len) + break + + var/mob/candidate = pick(candidates) + candidates.Remove(candidate) + infected.Add(candidate) + infected_names.Add("[candidate.mind.assigned_role] [candidate.mind.name]") + + special_orders = list( + "Quarantine these personnel: [list2text(infected_names, ", ")].", + "Allow one hour for a cure to be manufactured.", + "If no cure arrives after that time, execute the infected.") + +datum/directive/bluespace_contagion/meets_prerequisites() + var/list/candidates = get_infection_candidates() + return candidates.len >= 7 + +datum/directive/bluespace_contagion/directives_complete() + return infected.len == 0 + +/hook/death/proc/infected_killed(mob/living/carbon/human/deceased, gibbed) + var/datum/directive/bluespace_contagion/D = get_directive("bluespace_contagion") + if(!D) return 1 + + if(deceased in D.infected) + D.infected.Remove(deceased) + return 1 diff --git a/code/game/gamemodes/mutiny/directives/financial_crisis_directive.dm b/code/game/gamemodes/mutiny/directives/financial_crisis_directive.dm new file mode 100644 index 00000000000..ddcca165e82 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/financial_crisis_directive.dm @@ -0,0 +1,26 @@ +datum/directive/terminations/financial_crisis + special_orders = list( + "Suspend financial accounts of all civilian personnel, excluding the Head of Personnel.", + "Transfer their payrolls to the station account.", + "Terminate their employment.") + +datum/directive/terminations/financial_crisis/get_crew_to_terminate() + var/list/civilians[0] + var/list/candidates = civilian_positions - "Head of Personnel" + for(var/mob/M in player_list) + if (candidates.Find(M.mind.assigned_role) && M.is_ready()) + civilians.Add(M) + return civilians + +datum/directive/terminations/financial_crisis/get_description() + return {" +

+ Tau Ceti system banks in financial crisis. Local emergency situation ongoing. + NT Funds redistributed, impact upon civilian department expected. + Further information is classified. +

+ "} + +datum/directive/terminations/financial_crisis/meets_prerequisites() + var/list/civilians = get_crew_to_terminate() + return civilians.len >= 5 diff --git a/code/game/gamemodes/mutiny/directives/ipc_virus_directive.dm b/code/game/gamemodes/mutiny/directives/ipc_virus_directive.dm new file mode 100644 index 00000000000..7ff288f4549 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/ipc_virus_directive.dm @@ -0,0 +1,83 @@ +datum/directive/ipc_virus + special_orders = list( + "Terminate employment of all IPC personnel.", + "Extract the Positronic Brains from IPC units.", + "Mount the Positronic Brains into Cyborgs.") + + var/list/roboticist_roles = list( + "Research Director", + "Roboticist" + ) + + var/list/brains_to_enslave = list() + var/list/cyborgs_to_make = list() + var/list/ids_to_terminate = list() + + proc/get_ipcs() + var/list/machines[0] + for(var/mob/M in player_list) + if (M.get_species() == "Machine" && M.is_ready()) + machines.Add(M) + return machines + + proc/get_roboticists() + var/list/roboticists[0] + for(var/mob/M in player_list) + if (roboticist_roles.Find(M.mind.assigned_role) && M.is_ready()) + roboticists.Add(M) + return roboticists + +datum/directive/ipc_virus/initialize() + for(var/mob/living/carbon/human/H in get_ipcs()) + brains_to_enslave.Add(H.mind) + cyborgs_to_make.Add(H.mind) + ids_to_terminate.Add(H.wear_id) + +datum/directive/ipc_virus/get_description() + return {" +

+ IPC units have been found to be infected with a violent and undesired virus in Virgus Ferrorus system. + Risk to [station_name()] IPC units has not been assessed. Further information is classified. +

+ "} + +datum/directive/ipc_virus/meets_prerequisites() + var/list/ipcs = get_ipcs() + var/list/roboticists = get_roboticists() + return ipcs.len > 2 && roboticists.len > 1 + +datum/directive/ipc_virus/directives_complete() + return brains_to_enslave.len == 0 && cyborgs_to_make.len == 0 && ids_to_terminate.len == 0 + +/hook/debrain/proc/debrain_directive(obj/item/brain/B) + var/datum/directive/ipc_virus/D = get_directive("ipc_virus") + if (!D) return 1 + + if(D.brains_to_enslave.Find(B.brainmob.mind)) + D.brains_to_enslave.Remove(B.brainmob.mind) + + return 1 + +/hook/borgify/proc/borgify_directive(mob/living/silicon/robot/cyborg) + var/datum/directive/ipc_virus/D = get_directive("ipc_virus") + if (!D) return 1 + + if(D.cyborgs_to_make.Find(cyborg.mind)) + D.cyborgs_to_make.Remove(cyborg.mind) + + // In case something glitchy happened and the victim got + // borged without us tracking the brain removal, go ahead + // and update that list too. + if(D.brains_to_enslave.Find(cyborg.mind)) + D.brains_to_enslave.Remove(cyborg.mind) + + return 1 + +/hook/terminate_employee/proc/ipc_termination(obj/item/weapon/card/id) + var/datum/directive/ipc_virus/D = get_directive("ipc_virus") + if (!D) return 1 + + if(D.ids_to_terminate && D.ids_to_terminate.Find(id)) + D.ids_to_terminate.Remove(id) + + return 1 diff --git a/code/game/gamemodes/mutiny/directives/research_to_ripleys_directive.dm b/code/game/gamemodes/mutiny/directives/research_to_ripleys_directive.dm new file mode 100644 index 00000000000..f8da35524ee --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/research_to_ripleys_directive.dm @@ -0,0 +1,68 @@ +#define MATERIALS_REQUIRED 200 + +datum/directive/research_to_ripleys + var/list/ids_to_reassign = list() + var/materials_shipped = 0 + + proc/is_researcher(mob/M) + return M.mind.assigned_role in science_positions - "Research Director" + + proc/get_researchers() + var/list/researchers[0] + for(var/mob/M in player_list) + if (is_researcher(M) && M.is_ready()) + researchers.Add(M) + return researchers + + proc/count_researchers_reassigned() + var/researchers_reassigned = 0 + for(var/obj/item/weapon/card/id in ids_to_reassign) + if (ids_to_reassign[id]) + researchers_reassigned++ + + return researchers_reassigned + +datum/directive/research_to_ripleys/get_description() + return {" +

+ The NanoTrasen Tau Ceti Manufactory faces an ore deficit. Financial crisis imminent. [station_name()] has been reassigned as a mining platform. + The Research Director is to assist the Head of Personnel in coordinating assets. + Weapons department reports solid sales. Further information is classified. +

+ "} + +datum/directive/research_to_ripleys/meets_prerequisites() + var/list/researchers = get_researchers() + return researchers.len > 3 + +datum/directive/research_to_ripleys/initialize() + for(var/mob/living/carbon/human/R in get_researchers()) + ids_to_reassign[R.wear_id] = 0 + + special_orders = list( + "Reassign all research personnel, excluding the Research Director, to Shaft Miner.", + "Deliver [MATERIALS_REQUIRED] sheets of metal or minerals via the supply shuttle to CentCom.") + +datum/directive/research_to_ripleys/directives_complete() + if (materials_shipped < MATERIALS_REQUIRED) return 0 + return count_researchers_reassigned() == ids_to_reassign.len + +/hook/reassign_employee/proc/research_reassignments(obj/item/weapon/card/id/id_card) + var/datum/directive/research_to_ripleys/D = get_directive("research_to_ripleys") + if(!D) return 1 + + if(D.ids_to_reassign && D.ids_to_reassign.Find(id_card)) + D.ids_to_reassign[id_card] = id_card.assignment == "Shaft Miner" ? 1 : 0 + + return 1 + +/hook/sell_crate/proc/deliver_materials(obj/structure/closet/crate/sold, area/shuttle) + var/datum/directive/research_to_ripleys/D = get_directive("research_to_ripleys") + if(!D) return 1 + + for(var/atom/A in sold) + if(istype(A, /obj/item/stack/sheet/mineral) || istype(A, /obj/item/stack/sheet/metal)) + var/obj/item/stack/S = A + D.materials_shipped += S.amount + + return 1 diff --git a/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm b/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm new file mode 100644 index 00000000000..7cf112cdc1f --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm @@ -0,0 +1,97 @@ +datum/directive/tau_ceti_needs_women + var/list/command_targets = list() + var/list/alien_targets = list() + + proc/get_target_gender() + if(!mode.head_loyalist) return FEMALE + return mode.head_loyalist.current.get_gender() == FEMALE ? MALE : FEMALE + + proc/is_target_gender(mob/M) + var/species = M.get_species() + return species != "Diona" && M.get_gender() == get_target_gender() + + proc/get_crew_of_target_gender() + var/list/targets[0] + for(var/mob/M in player_list) + if(is_target_gender(M) && !M.is_mechanical() && M.is_ready()) + targets.Add(M) + return targets + + proc/get_target_heads() + var/list/heads[0] + for(var/mob/M in get_crew_of_target_gender()) + if(command_positions.Find(M.mind.assigned_role)) + heads.Add(M) + return heads + + proc/get_target_aliens() + var/list/aliens[0] + for(var/mob/M in get_crew_of_target_gender()) + var/species = M.get_species() + if(species == "Tajaran" || species == "Unathi" || species == "Skrell") + aliens.Add(M) + return aliens + + proc/count_heads_reassigned() + var/heads_reassigned = 0 + for(var/obj/item/weapon/card/id in command_targets) + if (command_targets[id]) + heads_reassigned++ + + return heads_reassigned + +datum/directive/tau_ceti_needs_women/get_description() + return {" +

+ Recent evidence suggests [get_target_gender()] aptitudes may be effected by radiation from Tau Ceti. + Effects were measured under laboratory and station conditions. Humans remain more trusted than Xeno. Further information is classified. +

+ "} + +datum/directive/tau_ceti_needs_women/initialize() + for(var/mob/living/carbon/human/H in get_target_heads()) + command_targets[H.wear_id] = 0 + + for(var/mob/living/carbon/human/H in get_target_aliens()) + alien_targets.Add(H.wear_id) + + special_orders = list( + "Remove [get_target_gender()] personnel from Command positions.", + "Terminate employment of all [get_target_gender()] Skrell, Tajara, and Unathi.") + +datum/directive/tau_ceti_needs_women/meets_prerequisites() + var/females = 0 + var/males = 0 + for(var/mob/M in player_list) + if(!M.is_mechanical() && M.get_species() != "Diona" && M.is_ready()) + var/gender = M.get_gender() + if(gender == MALE) + males++ + else if(gender == FEMALE) + females++ + + return males >= 2 && females >= 2 + +datum/directive/tau_ceti_needs_women/directives_complete() + return command_targets.len == count_heads_reassigned() && alien_targets.len == 0 + +/hook/reassign_employee/proc/command_reassignments(obj/item/weapon/card/id/id_card) + var/datum/directive/tau_ceti_needs_women/D = get_directive("tau_ceti_needs_women") + if(!D) return 1 + + if(D.command_targets && D.command_targets.Find(id_card)) + D.command_targets[id_card] = command_positions.Find(id_card.assignment) ? 0 : 1 + + return 1 + +/hook/terminate_employee/proc/gender_target_termination_directive(obj/item/weapon/card/id) + var/datum/directive/tau_ceti_needs_women/D = get_directive("tau_ceti_needs_women") + if (!D) return 1 + + if(D.alien_targets && D.alien_targets.Find(id)) + D.alien_targets.Remove(id) + + if(D.command_targets && D.command_targets.Find(id)) + D.command_targets[id] = 1 + + return 1 diff --git a/code/game/gamemodes/mutiny/directives/terminations_directive.dm b/code/game/gamemodes/mutiny/directives/terminations_directive.dm new file mode 100644 index 00000000000..610e994e533 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/terminations_directive.dm @@ -0,0 +1,56 @@ +// This is a parent directive meant to be derived by directives that fit +// the "fire X type of employee" pattern of directives. Simply apply your +// flavor text and override get_crew_to_terminate in your child datum. +// See alien_fraud_directive.dm for an example. +datum/directive/terminations + var/list/accounts_to_revoke = list() + var/list/accounts_to_suspend = list() + var/list/ids_to_terminate = list() + + proc/get_crew_to_terminate() + return list() + +datum/directive/terminations/directives_complete() + for(var/account_number in accounts_to_suspend) + if (!accounts_to_suspend[account_number]) + return 0 + + for(var/account_number in accounts_to_revoke) + if (!accounts_to_revoke[account_number]) + return 0 + + return ids_to_terminate.len == 0 + +datum/directive/terminations/initialize() + for(var/mob/living/carbon/human/H in get_crew_to_terminate()) + var/datum/money_account/account = H.mind.initial_account + accounts_to_revoke["[account.account_number]"] = 0 + accounts_to_suspend["[account.account_number]"] = account.suspended + ids_to_terminate.Add(H.wear_id) + +/hook/revoke_payroll/proc/payroll_directive(datum/money_account/account) + var/datum/directive/terminations/D = get_directive("terminations") + if (!D) return 1 + + if(D.accounts_to_revoke && D.accounts_to_revoke.Find("[account.account_number]")) + D.accounts_to_revoke["[account.account_number]"] = 1 + + return 1 + +/hook/change_account_status/proc/suspension_directive(datum/money_account/account) + var/datum/directive/terminations/D = get_directive("terminations") + if (!D) return 1 + + if(D.accounts_to_suspend && D.accounts_to_suspend.Find("[account.account_number]")) + D.accounts_to_suspend["[account.account_number]"] = account.suspended + + return 1 + +/hook/terminate_employee/proc/termination_directive(obj/item/weapon/card/id) + var/datum/directive/terminations/D = get_directive("terminations") + if (!D) return 1 + + if(D.ids_to_terminate && D.ids_to_terminate.Find(id)) + D.ids_to_terminate.Remove(id) + + return 1 diff --git a/code/game/gamemodes/mutiny/directives/test_directive.dm b/code/game/gamemodes/mutiny/directives/test_directive.dm new file mode 100644 index 00000000000..e19521cd1f0 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/test_directive.dm @@ -0,0 +1,23 @@ +// For testing +datum/directive/terminations/test + special_orders = list( + "Suspend financial accounts of all ugly personnel.", + "Transfer their payrolls to the station account.", + "Terminate their employment.") + +datum/directive/terminations/test/get_crew_to_terminate() + var/list/uglies[0] + for(var/mob/M in player_list) + uglies.Add(M) + return uglies + +datum/directive/terminations/test/get_description() + return {" +

+ Wow. Much ugly. So painful. + Many terminations. Very classified. +

+ "} + +datum/directive/terminations/test/meets_prerequisites() + return 1 diff --git a/code/game/gamemodes/mutiny/emergency_authentication_device.dm b/code/game/gamemodes/mutiny/emergency_authentication_device.dm new file mode 100644 index 00000000000..5bdcf27576e --- /dev/null +++ b/code/game/gamemodes/mutiny/emergency_authentication_device.dm @@ -0,0 +1,105 @@ +/obj/machinery/emergency_authentication_device + var/datum/game_mode/mutiny/mode + + name = "\improper Emergency Authentication Device" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox" + density = 1 + anchored = 1 + + var/captains_key + var/secondary_key + var/activated = 0 + + flags = FPRINT + use_power = 0 + + New(loc, mode) + src.mode = mode + ..(loc) + + proc/check_key_existence() + if(!mode.captains_key) + captains_key = 1 + + if(!mode.secondary_key) + secondary_key = 1 + + proc/get_status() + if(activated) + return "Activated" + if(captains_key && secondary_key) + return "Both Keys Authenticated" + if(captains_key) + return "Captain's Key Authenticated" + if(secondary_key) + return "Secondary Key Authenticated" + else + return "Inactive" + +/obj/machinery/emergency_authentication_device/attack_hand(mob/user) + if(activated) + user << "\blue \The [src] is already active!" + return + + if(!mode.current_directive.directives_complete()) + state("Command aborted. Communication with CentCom is prohibited until Directive X has been completed.") + return + + check_key_existence() + if(captains_key && secondary_key) + activated = 1 + user << "\blue You activate \the [src]!" + state("Command acknowledged. Initiating quantum entanglement relay to NanoTrasen High Command.") + return + + if(!captains_key && !secondary_key) + state("Command aborted. Please present the authentication keys before proceeding.") + return + + if(!captains_key) + state("Command aborted. Please present the Captain's Authentication Key.") + return + + if(!secondary_key) + state("Command aborted. Please present the Emergency Secondary Authentication Key.") + return + + // Impossible! + state("Command aborted. This unit is defective.") + +/obj/machinery/emergency_authentication_device/attackby(obj/item/weapon/O, mob/user) + if(activated) + user << "\blue \The [src] is already active!" + return + + if(!mode.current_directive.directives_complete()) + state({"Command aborted. Communication with CentCom is prohibited until Directive X has been completed."}) + return + + check_key_existence() + if(istype(O, /obj/item/weapon/mutiny/auth_key/captain) && !captains_key) + captains_key = O + user.drop_item() + O.loc = src + + state("Key received. Thank you, Captain [mode.head_loyalist].") + spawn(5) + state(secondary_key ? "Your keys have been authenticated. Communication with CentCom is now authorized." : "Please insert the Emergency Secondary Authentication Key now.") + return + + if(istype(O, /obj/item/weapon/mutiny/auth_key/secondary) && !secondary_key) + secondary_key = O + user.drop_item() + O.loc = src + + state("Key received. Thank you, Secondary Authenticator [mode.head_mutineer].") + spawn(5) + state(captains_key ? "Your keys have been authenticated. Communication with CentCom is now authorized." : "Please insert the Captain's Authentication Key now.") + return + ..() + +/obj/machinery/emergency_authentication_device/examine() + usr << {" +This is a specialized communications device that is able to instantly send a message to NanoTrasen High Command via quantum entanglement with a sister device at CentCom. +The EAD's status is [get_status()]."} diff --git a/code/game/gamemodes/mutiny/key_pinpointer.dm b/code/game/gamemodes/mutiny/key_pinpointer.dm new file mode 100644 index 00000000000..c481fd5bbc8 --- /dev/null +++ b/code/game/gamemodes/mutiny/key_pinpointer.dm @@ -0,0 +1,49 @@ +/obj/item/weapon/pinpointer/advpinpointer/auth_key + name = "\improper Authentication Key Pinpointer" + desc = "Tracks the positions of the emergency authentication keys." + var/datum/game_mode/mutiny/mutiny + + New() + mutiny = ticker.mode + ..() + +/obj/item/weapon/pinpointer/advpinpointer/auth_key/attack_self() + switch(mode) + if (0) + mode = 1 + active = 1 + target = mutiny.captains_key + workobj() + usr << "\blue You calibrate \the [src] to locate the Captain's Authentication Key." + if (1) + mode = 2 + target = mutiny.secondary_key + usr << "\blue You calibrate \the [src] to locate the Emergency Secondary Authentication Key." + else + mode = 0 + active = 0 + icon_state = "pinoff" + usr << "\blue You switch \the [src] off." + +/obj/item/weapon/pinpointer/advpinpointer/auth_key/examine() + switch(mode) + if (1) + usr << "Is is calibrated for the Captain's Authentication Key." + if (2) + usr << "It is calibrated for the Emergency Secondary Authentication Key." + else + usr << "It is switched off." + +/datum/supply_packs/key_pinpointer + name = "Authentication Key Pinpointer crate" + contains = list(/obj/item/weapon/pinpointer/advpinpointer/auth_key) + cost = 250 + containertype = /obj/structure/closet/crate + containername = "Authentication Key Pinpointer crate" + access = access_heads + group = "Operations" + + New() + // This crate is only accessible during mutiny rounds + if (istype(ticker.mode,/datum/game_mode/mutiny)) + ..() diff --git a/code/game/gamemodes/mutiny/mutiny.dm b/code/game/gamemodes/mutiny/mutiny.dm new file mode 100644 index 00000000000..7f7ebe80fc5 --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny.dm @@ -0,0 +1,400 @@ +#define MUTINY_RECRUITMENT_COOLDOWN 5 + +datum/game_mode/mutiny + var/datum/mutiny_fluff/fluff + var/datum/directive/current_directive + var/obj/item/weapon/mutiny/auth_key/captain/captains_key + var/obj/item/weapon/mutiny/auth_key/secondary/secondary_key + var/obj/machinery/emergency_authentication_device/ead + var/datum/mind/head_loyalist + var/datum/mind/head_mutineer + var/recruit_loyalist_cooldown = 0 + var/recruit_mutineer_cooldown = 0 + var/list/loyalists = list() + var/list/mutineers = list() + var/list/body_count = list() + + name = "mutiny" + config_tag = "mutiny" + required_players = 7 + ert_disabled = 1 + + uplink_welcome = "Mutineers Uplink Console:" + uplink_uses = 0 + + New() + fluff = new(src) + + proc/reveal_directives() + spawn(rand(1 MINUTE, 3 MINUTES)) + fluff.announce_incoming_fax() + spawn(rand(3 MINUTES, 5 MINUTES)) + send_pda_message() + spawn(rand(3 MINUTES, 5 MINUTES)) + fluff.announce_directives() + spawn(rand(2 MINUTES, 3 MINUTE)) + fluff.announce_ert_unavailable() + + // Returns an array in case we want to expand on this later. + proc/get_head_loyalist_candidates() + var/list/candidates[0] + for(var/mob/loyalist in player_list) + if(loyalist.mind && loyalist.mind.assigned_role == "Captain") + candidates.Add(loyalist.mind) + return candidates + + proc/get_head_mutineer_candidates() + var/list/candidates[0] + for(var/mob/mutineer in player_list) + if(mutineer.client.prefs.be_special & BE_MUTINEER) + for(var/job in command_positions - "Captain") + if(mutineer.mind && mutineer.mind.assigned_role == job) + candidates.Add(mutineer.mind) + return candidates + + proc/get_directive_candidates() + var/list/candidates[0] + for(var/T in typesof(/datum/directive) - /datum/directive) + var/datum/directive/D = new T(src) + if (D.meets_prerequisites()) + candidates.Add(D) + return candidates + + proc/send_pda_message() + var/obj/item/device/pda/pda = null + for(var/obj/item/device/pda/P in head_mutineer.current) + pda = P + break + + if (!pda) + return 0 + + if (!pda.silent) + playsound(pda.loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(3, pda.loc)) + O.show_message(text("\icon[pda] *[pda.ttone]*")) + + head_mutineer.current << fluff.get_pda_body() + return 1 + + proc/get_equipment_slots() + return list( + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "backpack" = slot_in_backpack, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand) + + proc/equip_head_loyalist() + var/mob/living/carbon/human/H = head_loyalist.current + captains_key = new(H) + H.equip_in_one_of_slots(captains_key, get_equipment_slots()) + H.update_icons() + H.verbs += /mob/living/carbon/human/proc/recruit_loyalist + + proc/equip_head_mutineer() + var/mob/living/carbon/human/H = head_mutineer.current + secondary_key = new(H) + H.equip_in_one_of_slots(secondary_key, get_equipment_slots()) + H.update_icons() + H.verbs += /mob/living/carbon/human/proc/recruit_mutineer + + proc/add_loyalist(datum/mind/M) + add_faction(M, "loyalist", loyalists) + + proc/add_mutineer(datum/mind/M) + add_faction(M, "mutineer", mutineers) + + proc/add_faction(datum/mind/M, faction, list/faction_list) + if(!can_be_recruited(M, faction)) + M.current << "\red Recruitment canceled; your role has already changed." + head_mutineer.current << "\red Could not recruit [M]. Their role has changed." + return + + if(M in loyalists) + loyalists.Remove(M) + + if(M in mutineers) + mutineers.Remove(M) + + M.special_role = faction + faction_list.Add(M) + + if(faction == "mutineer") + M.current << fluff.mutineer_tag("You have joined the mutineers!") + head_mutineer.current << fluff.mutineer_tag("[M] has joined the mutineers!") + else + M.current << fluff.loyalist_tag("You have joined the loyalists!") + head_loyalist.current << fluff.loyalist_tag("[M] has joined the loyalists!") + + update_icon(M) + + proc/was_bloodbath() + var/list/remaining_loyalists = loyalists - body_count + if (!remaining_loyalists.len) + return 1 + + var/list/remaining_mutineers = mutineers - body_count + if (!remaining_mutineers.len) + return 1 + + return 0 + + proc/replace_nuke_with_ead() + for(var/obj/machinery/nuclearbomb/N in world) + ead = new(N.loc, src) + del(N) + + proc/unbolt_vault_door() + var/obj/machinery/door/airlock/vault = locate(/obj/machinery/door/airlock/vault) + vault.locked = 0 + + proc/make_secret_transcript() + var/obj/machinery/computer/telecomms/server/S = locate(/obj/machinery/computer/telecomms/server) + if(!S) return + + var/obj/item/weapon/paper/crumpled/bloody/transcript = new(S.loc) + transcript.name = "secret transcript" + transcript.info = fluff.secret_transcript() + + proc/can_be_recruited(datum/mind/M, role) + if(!M) return 0 + if(!M.special_role) return 1 + switch(role) + if("loyalist") + return M.special_role == "mutineer" + if("mutineer") + return M.special_role == "loyalist" + + proc/round_outcome() + world << "

Breaking News



" + if (was_bloodbath()) + world << fluff.no_victory() + return + + var/directives_completed = current_directive.directives_complete() + var/ead_activated = ead.activated + if (directives_completed && ead_activated) + world << fluff.loyalist_major_victory() + else if (directives_completed && !ead_activated) + world << fluff.loyalist_minor_victory() + else if (!directives_completed && ead_activated) + world << fluff.mutineer_minor_victory() + else if (!directives_completed && !ead_activated) + world << fluff.mutineer_major_victory() + + world << sound('sound/machines/twobeep.ogg') + + proc/update_all_icons() + spawn(0) + for(var/datum/mind/M in mutineers) + update_icon(M) + + for(var/datum/mind/M in loyalists) + update_icon(M) + return 1 + + proc/update_icon(datum/mind/M) + if(!M.current || !M.current.client) + return 0 + + for(var/image/I in head_loyalist.current.client.images) + if(I.loc == M.current && (I.icon_state == "loyalist" || I.icon_state == "mutineer")) + del(I) + + for(var/image/I in head_mutineer.current.client.images) + if(I.loc == M.current && (I.icon_state == "loyalist" || I.icon_state == "mutineer")) + del(I) + + if(M in loyalists) + var/I = image('icons/mob/mob.dmi', loc=M.current, icon_state = "loyalist") + head_loyalist.current.client.images += I + + if(M in mutineers) + var/I = image('icons/mob/mob.dmi', loc=M.current, icon_state = "mutineer") + head_mutineer.current.client.images += I + + return 1 + + proc/check_antagonists_ui(admins) + var/turf/captains_key_loc = captains_key ? captains_key.get_loc_turf() : "Lost or Destroyed" + var/turf/secondary_key_loc = secondary_key ? secondary_key.get_loc_turf() : "Lost or Destroyed" + var/txt = {" +
Context:
+

+ [current_directive.get_description()] +

+
Orders:
+
    + [fluff.get_orders()] +
+
+
Authentication:
+ Captain's Key: [captains_key_loc] + Activate
+ Secondary Key: [secondary_key_loc] + Activate
+ EAD: [ead ? ead.get_status() : "Lost or Destroyed"] + Activate
+
+ "} + + if(head_loyalist) + txt += check_role_table("Head Loyalist", list(head_loyalist), admins, 0) + + var/list/loyal_crew = loyalists - head_loyalist + if(loyal_crew.len) + txt += check_role_table("Loyalists", loyal_crew, admins, 0) + + if(head_mutineer) + txt += check_role_table("Head Mutineer", list(head_mutineer), admins, 0) + + var/list/mutiny_crew = mutineers - head_mutineer + if(mutiny_crew.len) + txt += check_role_table("Mutineers", mutiny_crew, admins, 0) + + if(body_count.len) + txt += check_role_table("Casualties", body_count, admins, 0) + + return txt + +/datum/game_mode/mutiny/announce() + fluff.announce() + +/datum/game_mode/mutiny/pre_setup() + var/list/loyalist_candidates = get_head_loyalist_candidates() + if(!loyalist_candidates || loyalist_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for head loyalist." + return 0 + + var/list/mutineer_candidates = get_head_mutineer_candidates() + if(!mutineer_candidates || mutineer_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for head mutineer." + return 0 + + var/list/directive_candidates = get_directive_candidates() + if(!directive_candidates || directive_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for Directive X." + return 0 + + head_loyalist = pick(loyalist_candidates) + head_mutineer = pick(mutineer_candidates) + current_directive = pick(directive_candidates) + + return 1 + +/datum/game_mode/mutiny/post_setup() + head_loyalist.current << "You are the Head Loyalist!" + head_loyalist.special_role = "head_loyalist" + equip_head_loyalist() + + head_mutineer.current << "You are the Head Mutineer!" + head_mutineer.special_role = "head_mutineer" + equip_head_mutineer() + + loyalists.Add(head_loyalist) + mutineers.Add(head_mutineer) + + replace_nuke_with_ead() + current_directive.initialize() + unbolt_vault_door() + make_secret_transcript() + + update_all_icons() + spawn(0) + reveal_directives() + ..() + +/datum/game_mode/mutiny/check_antagonists_topic(href, href_list[]) + switch(href_list["choice"]) + if("activate_captains_key") + ead.captains_key = 1 + return 1 + if("activate_secondary_key") + ead.secondary_key = 1 + return 1 + if("activate_ead") + ead.activated = 1 + return 1 + else + return 0 + +/mob/living/carbon/human/proc/recruit_loyalist() + set name = "Recruit Loyalist" + set category = "Mutiny" + + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode || src != mode.head_loyalist.current) + return + + var/list/candidates = list() + for (var/mob/living/carbon/human/P in oview(src)) + if(!stat && P.client && mode.can_be_recruited(P.mind, "loyalist")) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + if(world.time < mode.recruit_loyalist_cooldown) + src << "\red Wait [MUTINY_RECRUITMENT_COOLDOWN] seconds before recruiting again." + return + + mode.recruit_loyalist_cooldown = world.time + (MUTINY_RECRUITMENT_COOLDOWN SECONDS) + + var/mob/living/carbon/human/M = input("Select a person to recruit", "Loyalist recruitment", null) as mob in candidates + + if (M) + src << "Attempting to recruit [M]..." + log_admin("[src]([src.ckey]) attempted to recruit [M] as a loyalist.") + message_admins("\red [src]([src.ckey]) attempted to recruit [M] as a loyalist.") + + var/choice = alert(M, "Asked by [src]: Will you help me complete Directive X?", "Loyalist recruitment", "No", "Yes") + if(choice == "Yes") + mode.add_loyalist(M.mind) + else if(choice == "No") + M << "\red You declined to join the loyalists." + mode.head_loyalist.current << "\red [M] declined to support the loyalists." + +/mob/living/carbon/human/proc/recruit_mutineer() + set name = "Recruit Mutineer" + set category = "Mutiny" + + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode || src != mode.head_mutineer.current) + return + + var/list/candidates = list() + for (var/mob/living/carbon/human/P in oview(src)) + if(!stat && P.client && mode.can_be_recruited(P.mind, "mutineer")) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + if(world.time < mode.recruit_mutineer_cooldown) + src << "\red Wait [MUTINY_RECRUITMENT_COOLDOWN] seconds before recruiting again." + return + + mode.recruit_mutineer_cooldown = world.time + (MUTINY_RECRUITMENT_COOLDOWN SECONDS) + + var/mob/living/carbon/human/M = input("Select a person to recruit", "Mutineer recruitment", null) as mob in candidates + + if (M) + src << "Attempting to recruit [M]..." + log_admin("[src]([src.ckey]) attempted to recruit [M] as a mutineer.") + message_admins("\red [src]([src.ckey]) attempted to recruit [M] as a mutineer.") + + var/choice = alert(M, "Asked by [src]: Will you help me stop Directive X?", "Mutineer recruitment", "No", "Yes") + if(choice == "Yes") + mode.add_mutineer(M.mind) + else if(choice == "No") + M << "\red You declined to join the mutineers." + mode.head_mutineer.current << "\red [M] declined to support the mutineers." + +/proc/get_mutiny_mode() + if(!ticker || !istype(ticker.mode, /datum/game_mode/mutiny)) + return null + + return ticker.mode diff --git a/code/game/gamemodes/mutiny/mutiny_fluff.dm b/code/game/gamemodes/mutiny/mutiny_fluff.dm new file mode 100644 index 00000000000..185e2ef8721 --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny_fluff.dm @@ -0,0 +1,251 @@ +/datum/mutiny_fluff + var/datum/game_mode/mutiny/mode + + New(datum/game_mode/mutiny/M) + mode = M + + proc/centcom_announce(text) + world << {" +
:-:=:-: CENTRAL COMMAND ANNOUNCEMENT :-:=:-:

+[text] + "} + world << sound('sound/AI/commandreport.ogg') + + proc/announce_incoming_fax() + centcom_announce({" +Incoming Emergency Directive: +Captain's Office Fax Machine, [station_name()] + "}) + + proc/announce_directives() + for (var/obj/machinery/faxmachine/fax in world) + if (fax.department == "Captain's Office") + var/obj/item/weapon/paper/directive_x = new(fax.loc) + directive_x.name = "emergency action message" + directive_x.info = get_fax_body() + + proc/get_fax_body() + return {" +
NOT A DRILL . . . EMERGENCY DIRECTIVE . . . NOT A DRILL
+

+ TO: Captain [mode.head_loyalist], Commanding Officer, [station_name()]
+ FROM: NanoTrasen Emergency Messaging Relay
+ DATE: [time2text(world.realtime, "MM/DD")]/[game_year]
+ SUBJECT: Directive X
+

+ + [mode.current_directive.get_description()] + +
+
Emergency Authentication Protocol
+

+ A member of your Command Staff is this shift's designated Emergency Secondary Authenticator.
+ This Emergency Secondary Authenticator is uniquely aware of their role and possesses the Emergency Secondary Authentication Key.
+ As Captain, you possess the Captain's Authentication Key.
+ The Emergency Authentication Device is located in the vault of your station, and requires simultaneous activation of the Authentication Keys.
+ An Authentication Key Pinpointer can be delivered via Cargo Bay to assist recovery of the Authentication Keys should they be lost aboard the station.
+ A key's destruction or removal from the station's perimeter will automatically and irreversibly activate the Emergency Authentication Device. +

+
+ +
Orders
+

+ Captain [mode.head_loyalist], you are to immediately initiate the following procedure; codenamed Directive X: +

+
    + [get_orders()] +
+
+ +
Authentication
+ + Encoded Authentication String: T0JCJUwoIVFDQA==
+ Emergency Action Code: O B B _ L _ _ Q C _
+ ERROR: DECODING INCOMPLETE (40% LOSS) +
+
+
+ +
NOT A DRILL . . . EMERGENCY DIRECTIVE . . . NOT A DRILL
+ "} + + proc/get_orders() + var/text = "
  • Immediate external transmission and signals silence. Evacuation and Cargo services will remain available. All ERT teams are engaged elsewhere. Do not communicate with Central Command under any circumstances.
  • " + for(var/order in mode.current_directive.special_orders) + text += "
  • [order]
  • " + + text += "
  • Upon completion of this Directive, Captain [mode.head_loyalist] and the Emergency Secondary Authenticator must utilise the Captain's Authentication Key and the Emergency Secondary Authentication Key to activate the Emergency Authentication Device.
  • " + return text + + proc/get_pda_body() + return {"← From Anonymous Channel:

    \"You must read this! NanoTrasen Chain of Command COMPROMISED. Command Encryptions BROKEN. [station_name()] Captain [mode.head_loyalist] will receive orders that must NOT BE BROUGHT TO FRUITION! + +They don't care about us they only care about WEALTH and POWER... Share this message with people you trust. + +Be safe, friend.\" (Unable to Reply)

    "} + + + proc/announce_ert_unavailable() + // I might have gotten a little carried away. + centcom_announce({" +

    The presence of [pick( + "political instability", + "quantum fluctuations", + "hostile raiders", + "derelict station debris", + "REDACTED", + "ancient alien artillery", + "solar magnetic storms", + "sentient time-travelling killbots", + "gravitational anomalies", + "wormholes to another dimension", + "a telescience mishap", + "radiation flares", + "supermatter dust", + "leaks into a negative reality", + "antiparticle clouds", + "residual bluespace energy", + "suspected syndicate operatives", + "malfunctioning von Neumann probe swarms", + "shadowy interlopers", + "a stranded Vox arkship", + "haywire IPC constructs", + "rogue Unathi exiles", + "artifacts of eldritch horror", + "a brain slug infestation", + "killer bugs that lay eggs in the husks of the living", + "a deserted transport carrying xenomorph specimens", + "an emissary for the gestalt requesting a security detail", + "a Tajaran slave rebellion", + "radical Skrellian transevolutionaries", + "classified security operations", + "science-defying raw elemental chaos")] +in the region is tying up all available local emergency resources; +emergency response teams can not be called at this time.

    + "}) + + proc/announce() + world << "The current game mode is - Mutiny!" + world << {" +

    The crew will be divided by their sense of ethics when a morally turbulent emergency directive arrives with an incomplete command validation code.

    +The [loyalist_tag("Head Loyalist")] is the Captain, who carries the [loyalist_tag("Captain's Authentication Key")] at all times.
    +The [mutineer_tag("Head Mutineer")] is a random Head of Staff who carries the [mutineer_tag("Emergency Secondary Authentication Key")].

    +Both keys are required to activate the Emergency Authentication Device (EAD) in the vault, signalling to NanoTrasen that the directive is complete. +
    +

    +Loyalists - Follow the Head Loyalist in carrying out [loyalist_tag("NanoTrasen's directives")] then activate the EAD.
    +Mutineers - Prevent the completion of the [mutineer_tag("improperly validated directives")] and the activation of the EAD. +

    + "} + + proc/loyalist_tag(text) + return "[text]" + + proc/mutineer_tag(text) + return "[text]" + + proc/their(datum/mind/head) + if (head.current.gender == MALE) + return "his" + else if (head.current.gender == FEMALE) + return "her" + + return "their" + + proc/loyalist_major_victory() + return {" +NanoTrasen has praised the efforts of Captain [mode.head_loyalist] and loyal members of [their(mode.head_loyalist)] crew, who recently managed to put down a mutiny--amid a local interstellar crisis--aboard the [station_name()], a research station in Tau Ceti. +The mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +Despite the mutiny, the crew was successful in implementing the directive and activating their on-board emergency authentication device. +[mode.mutineers.len] members of the station's personnel were charged with sedition against the company and if found guilty will be sentenced to life incarceration. +NanoTrasen will be awarding [mode.loyalists.len] members of the crew with the [loyalist_tag("Star of Loyalty")], following their successful efforts, at a ceremony this coming Thursday. +[mode.body_count.len] are believed to have died during the coup. +

    NanoTrasen's image will forever be haunted by the fact that a mutiny took place on one of its own stations.

    + "} + + proc/loyalist_minor_victory() + return {" +NanoTrasen has praised the efforts of Captain [mode.head_loyalist] and loyal members of [their(mode.head_loyalist)] crew, who recently managed to put down a mutiny--amid a local interstellar crisis--aboard the [station_name()], a research station in Tau Ceti. +The mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +Despite the mutiny, the crew was successful in implementing the directive. Unfortunately, they failed to notify Central Command of their successes due to a breach in the chain of command. +[mode.mutineers.len] members of the station's personnel were charged with sedition against the Company and if found guilty will be sentenced to life incarceration. +NanoTrasen will be awarding [mode.loyalists.len] members of the crew with the [loyalist_tag("Star of Loyalty")], following their mostly successful efforts, at a ceremony this coming Thursday. +[mode.body_count.len] are believed to have died during the coup. +

    NanoTrasen's image will forever be haunted by the fact that a mutiny took place on one of its own stations.

    + "} + + proc/no_victory() + return {" +NanoTrasen has been thrust into turmoil following an apparent mutiny by key personnel aboard the [station_name()], a research station in Tau Ceti. +The mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +No further information has yet emerged from the station or its crew, who are presumed to be in holding with NanoTrasen investigators. +NanoTrasen officials refuse to comment. +Sources indicate that [mode.mutineers.len] members of the station's personnel are currently under investigation for mutiny, and [mode.loyalists.len] crew are currently providing evidence to investigators, believed to be the 'loyal' station personnel. +[mode.body_count.len] are believed to have died during the coup. +

    NanoTrasen's image will forever be haunted by the fact that a mutiny took place on one of its own stations.

    + "} + + proc/mutineer_minor_victory() + return {" +Reports have emerged that an impromptu mutiny has taken place, amid a local interstellar crisis, aboard the [station_name()], a research station in Tau Ceti. +The mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +Information at present indicates that the top-secret directive--which has since been retracted--was invalid due to a broken authentication code. Members of the crew, including an unidentified Head of Staff, prevented the directive from being accomplished. +[mode.mutineers.len] members of the station's personnel were released from interrogations today, following a mutiny investigation. +NanoTrasen has reprimanded [mode.loyalists.len] members of the crew for failing to follow command validation procedures. +[mode.body_count.len] are believed to have died during the coup. +

    Even though the directive was not successfully implemented, NanoTrasen's image will forever be haunted by the fact that its authentication protocol was breached with such magnitude and that a mutiny was the result.

    + "} + + proc/mutineer_major_victory() + return {" +NanoTrasen has praised the efforts of [mode.head_mutineer.assigned_role] [mode.head_mutineer] and several other members of the crew, who recently seized control of a research station in Tau Ceti--[station_name()]--amid a local interstellar crisis. +What appears to have been a "legitimate" mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +It has been revealed that the directive was invalid and fraudulent. Company officials have not released a statement about the source of the directive. +Thanks to the efforts of the resistant members of the crew, the directive was not carried out. +[mode.mutineers.len] members of the station's personnel were congratulated and awarded with the [mutineer_tag("Star of Bravery")], for their efforts in preventing the illegal directive's completion. +NanoTrasen has [mode.loyalists.len] members of the crew in holding, while it investigates the circumstances that led to the acceptance and initiation of an invalid directive. +[mode.body_count.len] are believed to have died during the coup. +

    Even though the directive was not successfully implemented, NanoTrasen's image will forever be haunted by the fact that its authentication protocol was breached with such magnitude and that a mutiny was the result.

    + "} + + proc/secret_transcript() + return {" +

    Corporate Rival Threat Assessment

    +
    Gilthari Exports Incident Transcript
    +
    CONFIDENTIAL: PROPERTY OF NANOTRASEN
    +Location: Operator's Desk, D Deck, Polumetis Installation
    +Time: 16:11, May 24, 2558 (Sol Reckoning)
    +
    +
    + +
    \[Start of transcript\]
    +
    \[Sound of an internal airlock door opening\]
    +TM: Thank you for coming to see me, Director. I'm afraid this is urgent.
    +D: Mr. Mitchell, first you send cryptic messages to my office and then you request to have me come personally to this barely lit closet you call a workstation; all of this to talk about a computer glitch?
    +
    \[Sound of the internal airlock door shutting\]
    +TM: Do you remember Mallory?
    +D: Who?
    +TM: It's not who, it's what. The computer program we planted in the Tau Ceti communications satellite.
    +D: What is so important about this computer program?
    +TM: We call her an eavesdropper. Captures network traffic, records it, and forwards the stream to the receiver autonomously.
    +D: Speak English goddamnit.
    +TM: Standard intelligence acquisition package, sir; we bug their satellite and listen. It's like we have their playbook and we know what their moves are going to be on the market before they make them.
    +D: So Mallory doesn't work?
    +TM: She worked, sir. We've had an ear on NanoTrasen's regional communications for weeks.
    +D: Any news about their Phoron refinement process?
    +TM: No sir. Our analysts believe they are using a separate channel for their most sensitive data.
    +D: So what's the problem?
    +TM: The intelligence hasn't been doing us any good. Anything that appears actionable, I send it to the analysts and they make a plan. Thing is, NanoTrasen always sees us coming.
    +D: Tim...
    +TM: I think they discovered the hack, sir. Case in point, Energine Consolidated Solutions. That subsidiary of ours that was awarded a lease on NanoTrasen's mining platform in Nyx? NanoTrasen acquired them a week before we made the announcement.
    +D: They know about they have a bug. They left her on and fed her the information for us to hear, those sneaks. How did they find it?
    +TM: Top secret communique came through. I'm not sure what happened. Either Mallory couldn't replicate the encryption scheme and garbled it going out or the transmission was already corrupted to begin with.
    +D: Either way the transmission caused NanoTrasen to look at the satellite. They found out about Mallory.
    +TM: Precisely sir. There's only so much I can do to cover our tracks from here.
    +D: I'm pulling the plug. We have assets in the sector that are capable of a job like this. Thank you for bringing this to my attention.
    +
    \[Computer device chirps\]
    +D: One last thing, did you happen to read anything from those secure transmissions?
    +TM: Just the subject, 'Directive X'.
    +D: Directive X... Now what do you suppose that means?
    +
    \[End of transcript\]
    + "} diff --git a/code/game/gamemodes/mutiny/mutiny_hooks.dm b/code/game/gamemodes/mutiny/mutiny_hooks.dm new file mode 100644 index 00000000000..5bcddade430 --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny_hooks.dm @@ -0,0 +1,27 @@ +/hook/death/proc/track_kills(mob/living/carbon/human/deceased, gibbed) + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode) return 1 + + mode.body_count.Add(deceased.mind) + return 1 + +/hook/clone/proc/update_icon(mob/living/carbon/human/H) + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode) return 1 + + mode.update_icon(H.mind) + return 1 + +/hook/harvest_podman/proc/update_icon(mob/living/carbon/monkey/diona/D) + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode) return 1 + + mode.update_icon(D.mind) + return 1 + +/hook/roundend/proc/report_mutiny_news() + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode) return 1 + + mode.round_outcome() + return 1 diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 4361460980f..853481b1440 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -183,6 +183,7 @@ H << "Consciousness slowly creeps over you as your body regenerates.
    So this is what cloning feels like?
    " // -- Mode/mind specific stuff goes here + callHook("clone", list(H)) switch(ticker.mode.name) if("revolution") diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index fcf525cc364..78e0b16c641 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -2,247 +2,124 @@ /obj/machinery/computer/card name = "Identification Computer" - desc = "You can use this to change ID's." + desc = "Terminal for programming NanoTrasen employee ID cards to access parts of the station." icon_state = "id" req_access = list(access_change_ids) circuit = "/obj/item/weapon/circuitboard/card" var/obj/item/weapon/card/id/scan = null var/obj/item/weapon/card/id/modify = null - var/authenticated = 0.0 var/mode = 0.0 var/printing = null + proc/is_centcom() + return istype(src, /obj/machinery/computer/card/centcom) -/obj/machinery/computer/card/attackby(O as obj, user as mob)//TODO:SANITY - if(istype(O, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/idcard = O - if(access_change_ids in idcard.access) - if(!scan) - usr.drop_item() - idcard.loc = src - scan = idcard - else if(!modify) - usr.drop_item() - idcard.loc = src - modify = idcard - else - if(!modify) - usr.drop_item() - idcard.loc = src - modify = idcard - else - ..() + proc/is_authenticated() + return scan ? check_access(scan) : 0 + proc/get_target_rank() + return modify && modify.assignment ? modify.assignment : "Unassigned" + + proc/format_jobs(list/jobs) + var/list/formatted = list() + for(var/job in jobs) + formatted.Add(list(list( + "display_name" = replacetext(job, " ", " "), + "target_rank" = get_target_rank(), + "job" = job))) + + return formatted + +/obj/machinery/computer/card/attackby(obj/item/weapon/card/id/id_card, mob/user) + if(!istype(id_card)) + return ..() + + if(!scan && access_change_ids in id_card.access) + user.drop_item() + id_card.loc = src + scan = id_card + else if(!modify) + user.drop_item() + id_card.loc = src + modify = id_card + + nanomanager.update_uis(src) + attack_hand(user) /obj/machinery/computer/card/attack_ai(var/mob/user as mob) return attack_hand(user) - /obj/machinery/computer/card/attack_paw(var/mob/user as mob) return attack_hand(user) +/obj/machinery/computer/card/attack_hand(mob/user as mob) + if(..()) return + if(stat & (NOPOWER|BROKEN)) return + ui_interact(user) -/obj/machinery/computer/card/attack_hand(var/mob/user as mob) - if(..()) - return - +/obj/machinery/computer/card/ui_interact(mob/user, ui_key="main", datum/nanoui/ui=null) user.set_machine(src) - var/dat - if (!( ticker )) - return - if (mode) // accessing crew manifest - dat += "

    Crew Manifest

    " - dat += "Entries cannot be modified from this terminal.

    " - if(data_core) - dat += data_core.get_manifest(0) // make it monochrome - dat += "
    " - dat += "Print
    " - dat += "
    " - dat += "Access ID modification console.
    " + var/data[0] + data["src"] = "\ref[src]" + data["station_name"] = station_name() + data["mode"] = mode + data["printing"] = printing + data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["target_name"] = modify ? modify.name : "-----" + data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" + data["target_rank"] = get_target_rank() + data["scan_name"] = scan ? scan.name : "-----" + data["authenticated"] = is_authenticated() + data["has_modify"] = !!modify + data["account_number"] = modify ? modify.associated_account_number : null + data["centcom_access"] = is_centcom() + data["all_centcom_access"] = null + data["regions"] = null - /*var/crew = "" - var/list/L = list() - for (var/datum/data/record/t in data_core.general) - var/R = t.fields["name"] + " - " + t.fields["rank"] - L += R - for(var/R in sortList(L)) - crew += "[R]
    "*/ - //dat = "Crew Manifest:
    Please use security record computer to modify entries.

    [crew]Print

    Access ID modification console.
    " - else - var/header = "
    Identification Card Modifier
    " + data["engineering_jobs"] = format_jobs(engineering_positions) + data["medical_jobs"] = format_jobs(medical_positions) + data["science_jobs"] = format_jobs(science_positions) + data["security_jobs"] = format_jobs(security_positions) + data["civilian_jobs"] = format_jobs(civilian_positions) + data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) - var/target_name - var/target_owner - var/target_rank - if(modify) - target_name = modify.name - else - target_name = "--------" - if(modify && modify.registered_name) - target_owner = modify.registered_name - else - target_owner = "--------" - if(modify && modify.assignment) - target_rank = modify.assignment - else - target_rank = "Unassigned" + if (modify && is_centcom()) + var/list/all_centcom_access = list() + for(var/access in get_all_centcom_access()) + all_centcom_access.Add(list(list( + "desc" = replacetext(get_centcom_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in modify.access) ? 1 : 0))) - var/scan_name - if(scan) - scan_name = scan.name - else - scan_name = "--------" + data["all_centcom_access"] = all_centcom_access + else if (modify) + var/list/regions = list() + for(var/i = 1; i <= 7; i++) + var/list/accesses = list() + for(var/access in get_region_accesses(i)) + if (get_access_desc(access)) + accesses.Add(list(list( + "desc" = replacetext(get_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in modify.access) ? 1 : 0))) - if(!authenticated) - header += "
    Please insert the cards into the slots
    " - header += "Target: [target_name]
    " - header += "Confirm Identity: [scan_name]
    " - else - header += "

    " - header += "Remove [target_name] || " - header += "Remove [scan_name]
    " - header += "Access Crew Manifest || " - header += "Log Out
    " + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) - header += "
    " - - var/jobs_all = "" - var/counter = 0 - jobs_all += "" - - jobs_all += ""//Captain in special because he is head of heads ~Intercross21 - jobs_all += "" - jobs_all += "" - - counter = 0 - jobs_all += ""//Orange - for(var/job in engineering_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Green - for(var/job in medical_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Purple - for(var/job in science_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - counter = 0 - jobs_all += ""//Grey - for(var/job in civilian_positions) - counter++ - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - if(istype(src,/obj/machinery/computer/card/centcom)) - counter = 0 - jobs_all += ""//Brown - for(var/job in get_all_centcom_jobs()) - if(counter >= 6) - jobs_all += "" - counter = 0 - jobs_all += "" - - var/body - if (authenticated && modify) - var/carddesc = {""} - carddesc += "" - carddesc += "" - carddesc += "" - carddesc += "registered_name:" - carddesc += "" - carddesc += "" - - carddesc += "" - carddesc += "" - carddesc += "" - carddesc += "Stored account number:" - carddesc += "" - carddesc += "" - - carddesc += "Assignment: " - var/jobs = "[target_rank]" //CHECK THIS - var/accesses = "" - if(istype(src,/obj/machinery/computer/card/centcom)) - accesses += "
    Central Command:
    " - for(var/A in get_all_centcom_access()) - if(A in modify.access) - accesses += "[replacetext(get_centcom_access_desc(A), " ", " ")] " - else - accesses += "[replacetext(get_centcom_access_desc(A), " ", " ")] " - else - accesses += "
    Access
    " - accesses += "
    Command
    SpecialCaptainCustom
    Engineering
    [replacetext(job, " ", " ")]
    Medical
    [replacetext(job, " ", " ")]
    Science
    [replacetext(job, " ", " ")]
    Civilian
    [replacetext(job, " ", " ")]
    CentComm
    [replacetext(job, " ", " ")]
    " - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "" - for(var/i = 1; i <= 7; i++) - accesses += "" - accesses += "
    [get_region_accesses_name(i)]:
    " - for(var/A in get_region_accesses(i)) - if(A in modify.access) - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - else - accesses += "[replacetext(get_access_desc(A), " ", " ")] " - accesses += "
    " - accesses += "
    " - body = "[carddesc]
    [jobs]

    [accesses]" //CHECK THIS - else - body = "{Log in}

    " - body += "Access Crew Manifest" - dat = "[header][body]

    " - user << browse(dat, "window=id_com;size=900x520") - onclose(user, "id_com") - return + data["regions"] = regions + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) + if (!ui) + ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 600, 700) + ui.set_initial_data(data) + ui.open() /obj/machinery/computer/card/Topic(href, href_list) if(..()) - return - usr.set_machine(src) + return 1 + switch(href_list["choice"]) if ("modify") if (modify) @@ -262,7 +139,6 @@ usr.drop_item() I.loc = src modify = I - authenticated = 0 if ("scan") if (scan) @@ -280,26 +156,19 @@ usr.drop_item() I.loc = src scan = I - authenticated = 0 - if ("auth") - if ((!( authenticated ) && (scan || (istype(usr, /mob/living/silicon))) && (modify || mode))) - if (check_access(scan)) - authenticated = 1 - else if ((!( authenticated ) && (istype(usr, /mob/living/silicon))) && (!modify)) - usr << "You can't modify an ID without an ID inserted to modify. Once one is in the modify slot on the computer, you can log in." - if ("logout") - authenticated = 0 + if("access") if(href_list["allowed"]) - if(authenticated) + if(is_authenticated()) var/access_type = text2num(href_list["access_target"]) var/access_allowed = text2num(href_list["allowed"]) - if(access_type in (istype(src,/obj/machinery/computer/card/centcom)?get_all_centcom_access() : get_all_accesses())) + if(access_type in (is_centcom() ? get_all_centcom_access() : get_all_accesses())) modify.access -= access_type - if(access_allowed == 1) + if(!access_allowed) modify.access += access_type + if ("assign") - if (authenticated) + if (is_authenticated() && modify) var/t1 = href_list["assign_target"] if(t1 == "Custom") var/temp_t = copytext(sanitize(input("Enter a custom job assignment.","Assignment")),1,45) @@ -307,69 +176,92 @@ if(temp_t && modify) modify.assignment = temp_t else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break - if(!jobdatum) - usr << "\red No log exists for this job." - return + var/list/access = list() + if(is_centcom()) + access = get_centcom_access(t1) + else + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + usr << "\red No log exists for this job: [t1]" + return + + access = jobdatum.get_access() + + modify.access = access + modify.assignment = t1 + modify.rank = t1 + + callHook("reassign_employee", list(modify)) - modify.access = ( istype(src,/obj/machinery/computer/card/centcom) ? get_centcom_access(t1) : jobdatum.get_access() ) - if (modify) - modify.assignment = t1 - modify.rank = t1 if ("reg") - if (authenticated) + if (is_authenticated()) var/t2 = modify - //var/t1 = input(usr, "What name?", "ID computer", null) as text - if ((authenticated && modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) + if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) var/temp_name = reject_bad_name(href_list["reg"]) if(temp_name) modify.registered_name = temp_name else src.visible_message("[src] buzzes rudely.") + nanomanager.update_uis(src) + if ("account") - if (authenticated) + if (is_authenticated()) var/t2 = modify - //var/t1 = input(usr, "What name?", "ID computer", null) as text - if ((authenticated && modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) + if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) var/account_num = text2num(href_list["account"]) modify.associated_account_number = account_num + nanomanager.update_uis(src) + if ("mode") mode = text2num(href_list["mode_target"]) + if ("print") - if (!( printing )) + if (!printing) printing = 1 - sleep(50) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( loc ) - /*var/t1 = "Crew Manifest:
    " - var/list/L = list() - for (var/datum/data/record/t in data_core.general) - var/R = t.fields["name"] + " - " + t.fields["rank"] - L += R - for(var/R in sortList(L)) - t1 += "[R]
    "*/ + spawn(50) + printing = null + nanomanager.update_uis(src) - var/t1 = "

    Crew Manifest

    " - t1 += "
    " - if(data_core) - t1 += data_core.get_manifest(0) // make it monochrome + var/obj/item/weapon/paper/P = new(loc) + if (mode) + P.name = text("crew manifest ([])", worldtime2text()) + P.info = {"

    Crew Manifest

    +
    + [data_core ? data_core.get_manifest(0) : ""] + "} + else if (modify) + P.name = "access report" + P.info = {"

    Access Report

    + Prepared By: [scan.registered_name ? scan.registered_name : "Unknown"]
    + For: [modify.registered_name ? modify.registered_name : "Unregistered"]
    +
    + Assignment: [modify.assignment]
    + Account Number: #[modify.associated_account_number]
    + Blood Type: [modify.blood_type]

    + Access:
    + "} + + for(var/A in modify.access) + P.info += " [get_access_desc(A)]" + + if ("terminate") + if (is_authenticated()) + modify.assignment = "Terminated" + modify.access = list() + + callHook("terminate_employee", list(modify)) - P.info = t1 - P.name = text("Crew Manifest ([])", worldtime2text()) - printing = null if (modify) modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") - updateUsrDialog() - return - + return 1 /obj/machinery/computer/card/centcom name = "CentCom Identification Computer" circuit = "/obj/item/weapon/circuitboard/card/centcom" req_access = list(access_cent_captain) - diff --git a/code/game/machinery/podmen.dm b/code/game/machinery/podmen.dm index 6dd173f4478..8e0fd140237 100644 --- a/code/game/machinery/podmen.dm +++ b/code/game/machinery/podmen.dm @@ -129,6 +129,8 @@ Growing it to term with nothing injected will grab a ghost from the observers. * podman.dna.real_name = podman.real_name // Update mode specific HUD icons. + callHook("harvest_podman", list(podman)) + switch(ticker.mode.name) if ("revolution") if (podman.mind in ticker.mode:revolutionaries) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 94376231559..b0897829e2e 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -485,6 +485,12 @@ /obj/item/proc/IsShield() return 0 +/obj/item/proc/get_loc_turf() + var/atom/L = loc + while(L && !istype(L, /turf/)) + L = L.loc + return loc + /obj/item/proc/eyestab(mob/living/carbon/M as mob, mob/living/carbon/user as mob) var/mob/living/carbon/human/H = M diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 3d3b88f4d06..6626b4d36c7 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -166,7 +166,7 @@ var/obj/item/device/mmi/M = W if(check_completion()) if(!istype(loc,/turf)) - user << "\red You can't put the [W] in, the frame has to be standing on the ground to be perfectly precise." + user << "\red You can't put \the [W] in, the frame has to be standing on the ground to be perfectly precise." return if(!M.brainmob) user << "\red Sticking an empty [W] into the frame would sort of defeat the purpose." @@ -179,7 +179,7 @@ ghost_can_reenter = 1 break if(!ghost_can_reenter) - user << "The [W] is completely unresponsive; there's no point." + user << "\The [W] is completely unresponsive; there's no point." return if(M.brainmob.stat == DEAD) @@ -222,6 +222,7 @@ cell_component.installed = 1 feedback_inc("cyborg_birth",1) + callHook("borgify", list(O)) O.Namepick() del(src) @@ -294,4 +295,4 @@ user << "\red You slide [W] into the dataport on [src] and short out the safeties." sabotaged = 1 return - ..() \ No newline at end of file + ..() diff --git a/code/game/response_team.dm b/code/game/response_team.dm index a518cef566e..ce719f6c68d 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -329,4 +329,4 @@ proc/trigger_armed_response_team(var/force = 0) /*client/verb/ResponseTeam() set category = "Admin" if(!send_emergency_team) - send_emergency_team = 1*/ \ No newline at end of file + send_emergency_team = 1*/ diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 037956d5fd7..a9a5a5715e3 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -241,6 +241,8 @@ var/list/mechtoys = list( // Must be in a crate! if(istype(MA,/obj/structure/closet/crate)) + callHook("sell_crate", list(MA, shuttle)) + points += points_per_crate var/find_slip = 1 diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 34c844487c1..9eb85cf34dc 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -332,7 +332,7 @@ var/list/mobs = sortmobs() for(var/mob/M in mobs) - if(!M.ckey) continue + if(!M.ckey) continue dat += "[M.name]" if(isAI(M)) @@ -452,83 +452,62 @@ dat += "Head not found!" dat += "" - if(ticker.mode.changelings.len > 0) - dat += "
    " - for(var/datum/mind/changeling in ticker.mode.changelings) - var/mob/M = changeling.current - if(M) - dat += "" - dat += "" - dat += "" - else - dat += "" - dat += "
    Changelings
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PMShow Objective
    Changeling not found!
    " + if(ticker.mode.changelings.len) + dat += check_role_table("Changelings", ticker.mode.changelings, src) - if(ticker.mode.wizards.len > 0) - dat += "
    " - for(var/datum/mind/wizard in ticker.mode.wizards) - var/mob/M = wizard.current - if(M) - dat += "" - dat += "" - dat += "" - else - dat += "" - dat += "
    Wizards
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PMShow Objective
    Wizard not found!
    " + if(ticker.mode.wizards.len) + dat += check_role_table("Wizards", ticker.mode.wizards, src) - if(ticker.mode.raiders.len > 0) - dat += "
    " - for(var/datum/mind/raider in ticker.mode.raiders) - var/mob/M = raider.current - if(M) - dat += "" - dat += "" - dat += "" - dat += "
    Raiders
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PMShow Objective
    " + if(ticker.mode.raiders.len) + dat += check_role_table("Raiders", ticker.mode.raiders, src) - if(ticker.mode.ninjas.len > 0) - dat += "
    " - for(var/datum/mind/ninja in ticker.mode.ninjas) - var/mob/M = ninja.current - if(M) - dat += "" - dat += "" - dat += "" - else - dat += "" - dat += "
    Ninjas
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PMShow Objective
    Ninja not found!
    " + if(ticker.mode.ninjas.len) + dat += check_role_table("Ninjas", ticker.mode.ninjas, src) if(ticker.mode.cult.len) - dat += "
    " - for(var/datum/mind/N in ticker.mode.cult) - var/mob/M = N.current - if(M) - dat += "" - dat += "" - dat += "
    Cultists
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    " + dat += check_role_table("Cultists", ticker.mode.cult, src, 0) - /*if(istype(ticker.mode, /datum/game_mode/anti_revolution) && ticker.mode:heads.len) //comment out anti-revolution - dat += "
    " - for(var/datum/mind/N in ticker.mode:heads) - var/mob/M = N.current - if(M) - dat += "" - dat += "" - dat += "
    Corrupt Heads
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    " -*/ - if(ticker.mode.traitors.len > 0) - dat += "
    " - for(var/datum/mind/traitor in ticker.mode.traitors) - var/mob/M = traitor.current - if(M) - dat += "" - dat += "" - dat += "" - else - dat += "" - dat += "
    Traitors
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PMShow Objective
    Traitor not found!
    " + if(ticker.mode.traitors.len) + dat += check_role_table("Traitors", ticker.mode.traitors, src) + + var/datum/game_mode/mutiny/mutiny = get_mutiny_mode() + if(mutiny) + dat += mutiny.check_antagonists_ui(src) dat += "" usr << browse(dat, "window=roundstatus;size=400x500") else alert("The game hasn't started yet!") + +/proc/check_role_table(name, list/members, admins, show_objectives=1) + var/txt = "
    " + for(var/datum/mind/M in members) + txt += check_role_table_row(M.current, admins, show_objectives) + txt += "
    [name]
    " + return txt + +/proc/check_role_table_row(mob/M, admins=src, show_objectives) + if (!istype(M)) + return "Not found!" + + var/txt = {" + + + [M.real_name] + [M.client ? "" : " (logged out)"] + [M.is_dead() ? " (DEAD)" : ""] + + + PM + + "} + + if (show_objectives) + txt += {" + + Show Objective + + "} + + txt += "" + return txt diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 9336b7ed3c1..2834b4d4045 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -6,6 +6,10 @@ message_admins("[usr.key] has attempted to override the admin panel!") return + if(ticker.mode && ticker.mode.check_antagonists_topic(href, href_list)) + check_antagonists() + return + if(href_list["makeAntag"]) switch(href_list["makeAntag"]) if("1") diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index f674faf74e6..d47ec592949 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -14,9 +14,10 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set "pAI candidate" = 1, // -- TLE // 7 "cultist" = IS_MODE_COMPILED("cult"), // 8 "infested monkey" = IS_MODE_COMPILED("monkey"), // 9 - "ninja" = "true", // 10 - "vox raider" = IS_MODE_COMPILED("heist"), // 11 + "ninja" = "true", // 10 + "vox raider" = IS_MODE_COMPILED("heist"), // 11 "diona" = 1, // 12 + "mutineer" = IS_MODE_COMPILED("mutiny"), // 13 ) var/const/MAX_SAVE_SLOTS = 10 diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index 70bf8ee125f..f94766f16cb 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -1,6 +1,6 @@ /obj/machinery/account_database - name = "Accounts uplink console" + name = "Accounts uplink terminal" desc = "Access transaction logs, account data and all kinds of other financial records." icon = 'icons/obj/computer.dmi' icon_state = "aiupload" @@ -9,115 +9,134 @@ var/receipt_num var/machine_id = "" var/obj/item/weapon/card/id/held_card - var/access_level = 0 var/datum/money_account/detailed_account_view var/creating_new_account = 0 + proc/get_access_level() + if (!held_card) + return 0 + if(access_cent_captain in held_card.access) + return 2 + else if(access_hop in held_card.access || access_captain in held_card.access) + return 1 + + proc/create_transation(target, reason, amount) + var/datum/transaction/T = new() + T.target_name = target + T.purpose = reason + T.amount = amount + T.date = current_date_string + T.time = worldtime2text() + T.source_terminal = machine_id + return T + + proc/accounting_letterhead(report_name) + return {" +

    [report_name]

    +
    [station_name()] Accounting Report
    +
    + Generated By: [held_card.registered_name], [held_card.assignment]
    + "} + /obj/machinery/account_database/New() - ..() machine_id = "[station_name()] Acc. DB #[num_financial_terminals++]" + ..() + +/obj/machinery/account_database/attackby(obj/O, mob/user) + if(!istype(O, /obj/item/weapon/card/id)) + return ..() + + if(!held_card) + user.drop_item() + O.loc = src + held_card = O + + nanomanager.update_uis(src) + + attack_hand(user) /obj/machinery/account_database/attack_hand(mob/user as mob) - if(get_dist(src,user) <= 1) - var/dat = "Accounts Database
    " - dat += "[machine_id]
    " - dat += "Confirm identity: [held_card ? held_card : "-----"]
    " + if(stat & (NOPOWER|BROKEN)) return + ui_interact(user) - if(access_level > 0) - dat += "You may not edit accounts at this terminal, only create and view them.
    " - if(creating_new_account) - dat += "
    " - dat += "Return to accounts list" - dat += "
    " - dat += "" - dat += "" - dat += "Holder name:
    " - dat += "Initial funds: (subtracted from station account)
    " - dat += "New accounts are automatically assigned a secret number and pin, which are printed separately in a sealed package.
    " - dat += "
    " - dat += "
    " - else - if(detailed_account_view) - dat += "
    " - dat += "Return to accounts list
    " - dat += "Account number: #[detailed_account_view.account_number]
    " - dat += "Account holder: [detailed_account_view.owner_name]
    " - dat += "Account balance: $[detailed_account_view.money]
    " - if(access_level > 1) - dat += "Silently add funds (no transaction log)
    " - dat += "Silently remove funds (no transaction log)
    " - dat += "[detailed_account_view.suspended ? "Unsuspend account" : "Suspend account"]
    " - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - for(var/datum/transaction/T in detailed_account_view.transaction_log) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    DateTimeTargetPurposeValueSource terminal ID
    [T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
    " - else - dat += "Create new account

    " - dat += "" - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    #[D.account_number][D.owner_name][D.suspended ? "SUSPENDED" : ""]View in detail
    " +/obj/machinery/account_database/ui_interact(mob/user, ui_key="main", datum/nanoui/ui=null) + user.set_machine(src) - user << browse(dat,"window=account_db;size=700x650") - else - user << browse(null,"window=account_db") + var/data[0] + data["src"] = "\ref[src]" + data["id_inserted"] = !!held_card + data["id_card"] = held_card ? text("[held_card.registered_name], [held_card.assignment]") : "-----" + data["access_level"] = get_access_level() + data["machine_id"] = machine_id + data["creating_new_account"] = creating_new_account + data["detailed_account_view"] = !!detailed_account_view + data["station_account_number"] = station_account.account_number + data["transactions"] = null + data["accounts"] = null -/obj/machinery/account_database/attackby(O as obj, user as mob)//TODO:SANITY - if(istype(O, /obj/item/weapon/card)) - var/obj/item/weapon/card/id/idcard = O - if(!held_card) - usr.drop_item() - idcard.loc = src - held_card = idcard + if (detailed_account_view) + data["account_number"] = detailed_account_view.account_number + data["owner_name"] = detailed_account_view.owner_name + data["money"] = detailed_account_view.money + data["suspended"] = detailed_account_view.suspended - if(access_cent_captain in idcard.access) - access_level = 2 - else if(access_hop in idcard.access || access_captain in idcard.access) - access_level = 1 - else - ..() + var/list/trx[0] + for (var/datum/transaction/T in detailed_account_view.transaction_log) + trx.Add(list(list(\ + "date" = T.date, \ + "time" = T.time, \ + "target_name" = T.target_name, \ + "purpose" = T.purpose, \ + "amount" = T.amount, \ + "source_terminal" = T.source_terminal))) -/obj/machinery/account_database/Topic(var/href, var/href_list) + if (trx.len > 0) + data["transactions"] = trx + + var/list/accounts[0] + for(var/i=1, i<=all_money_accounts.len, i++) + var/datum/money_account/D = all_money_accounts[i] + accounts.Add(list(list(\ + "account_number"=D.account_number,\ + "owner_name"=D.owner_name,\ + "suspended"=D.suspended ? "SUSPENDED" : "",\ + "account_index"=i))) + + if (accounts.len > 0) + data["accounts"] = accounts + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) + if (!ui) + ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640) + ui.set_initial_data(data) + ui.open() + +/obj/machinery/account_database/Topic(href, href_list) + if(..()) + return 1 + + var/datum/nanoui/ui = nanomanager.get_open_ui(usr, src, "main") if(href_list["choice"]) switch(href_list["choice"]) if("create_account") creating_new_account = 1 + if("add_funds") var/amount = input("Enter the amount you wish to add", "Silently add funds") as num if(detailed_account_view) detailed_account_view.money += amount + if("remove_funds") var/amount = input("Enter the amount you wish to remove", "Silently remove funds") as num if(detailed_account_view) detailed_account_view.money -= amount + if("toggle_suspension") if(detailed_account_view) - if(detailed_account_view.suspended) - detailed_account_view.suspended = 0 - else - detailed_account_view.suspended = 1 + detailed_account_view.suspended = !detailed_account_view.suspended + callHook("change_account_status", list(detailed_account_view)) + if("finalise_create_account") var/account_name = href_list["holder_name"] var/starting_funds = max(text2num(href_list["starting_funds"]), 0) @@ -127,14 +146,11 @@ station_account.money -= starting_funds //create a transaction log entry - var/datum/transaction/T = new() - T.target_name = account_name - T.purpose = "New account funds initialisation" - T.amount = "([starting_funds])" - T.date = current_date_string - T.time = worldtime2text() - T.source_terminal = machine_id - station_account.transaction_log.Add(T) + var/trx = create_transation(account_name, "New account activation", "([starting_funds])") + station_account.transaction_log.Add(trx) + + creating_new_account = 0 + ui.close() creating_new_account = 0 if("insert_card") @@ -144,7 +160,6 @@ if(ishuman(usr) && !usr.get_active_hand()) usr.put_in_hands(held_card) held_card = null - access_level = 0 else var/obj/item/I = usr.get_active_hand() @@ -154,16 +169,103 @@ C.loc = src held_card = C - if(access_cent_captain in C.access) - access_level = 2 - else if(access_hop in C.access || access_captain in C.access) - access_level = 1 if("view_account_detail") var/index = text2num(href_list["account_index"]) if(index && index <= all_money_accounts.len) detailed_account_view = all_money_accounts[index] + if("view_accounts_list") detailed_account_view = null creating_new_account = 0 - src.attack_hand(usr) + if("revoke_payroll") + var/funds = detailed_account_view.money + var/account_trx = create_transation(station_account.owner_name, "Revoke payroll", "([funds])") + var/station_trx = create_transation(detailed_account_view.owner_name, "Revoke payroll", funds) + + station_account.money += funds + detailed_account_view.money = 0 + + detailed_account_view.transaction_log.Add(account_trx) + station_account.transaction_log.Add(station_trx) + + callHook("revoke_payroll", list(detailed_account_view)) + + if("print") + var/text + var/obj/item/weapon/paper/P = new(loc) + if (detailed_account_view) + P.name = "account #[detailed_account_view.account_number] details" + var/title = "Account #[detailed_account_view.account_number] Details" + text = {" + [accounting_letterhead(title)] + Holder: [detailed_account_view.owner_name]
    + Balance: $[detailed_account_view.money]
    + Status: [detailed_account_view.suspended ? "Suspended" : "Active"]
    + Transactions: ([detailed_account_view.transaction_log.len])
    + + + + + + + + + + + + "} + + for (var/datum/transaction/T in detailed_account_view.transaction_log) + text += {" + + + + + + + + "} + + text += {" + +
    TimestampTargetReasonValueTerminal
    [T.date] [T.time][T.target_name][T.purpose][T.amount][T.source_terminal]
    + "} + + else + P.name = "financial account list" + text = {" + [accounting_letterhead("Financial Account List")] + + + + + + + + + + + + "} + + for(var/i=1, i<=all_money_accounts.len, i++) + var/datum/money_account/D = all_money_accounts[i] + text += {" + + + + + + + "} + + text += {" + +
    Account NumberHolderBalanceStatus
    #[D.account_number][D.owner_name]$[D.money][D.suspended ? "Suspended" : "Active"]
    + "} + + P.info = text + state("The terminal prints out a report.") + + return 1 diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm index 5e94ea9bc97..27b9da88e88 100644 --- a/code/modules/mob/living/carbon/brain/brain_item.dm +++ b/code/modules/mob/living/carbon/brain/brain_item.dm @@ -32,8 +32,9 @@ brainmob.timeofhostdeath = H.timeofdeath if(H.mind) H.mind.transfer_to(brainmob) + brainmob << "\blue You feel slightly disoriented. That's normal when you're just a brain." - return + callHook("debrain", list(brainmob)) /obj/item/brain/examine() // -- TLE set src in oview(12) @@ -95,4 +96,4 @@ del(src) else ..() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 42cd5dce982..292f74dbc57 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -58,12 +58,12 @@ dizziness = 0 jitteriness = 0 - + hud_updateflag |= 1 << HEALTH_HUD hud_updateflag |= 1 << STATUS_HUD handle_hud_list() - + //Handle species-specific deaths. if(species) species.handle_death(src) @@ -87,6 +87,8 @@ verbs -= /mob/living/carbon/proc/release_control + callHook("death", list(src, gibbed)) + //Check for heist mode kill count. if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist) ) ) //Check for last assailant's mutantrace. diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 10c0746dc65..bdd834a80dc 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -17,41 +17,8 @@ if("nuclear emergency") if(mind in ticker.mode:syndicates) ticker.mode.update_all_synd_icons() + if("mutiny") + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if(mode) + mode.update_all_icons() return . - -//This stuff needs to be merged from cloning.dm but I'm not in the mood to be shouted at for breaking all the things :< ~Carn - /* clones - switch(ticker.mode.name) - if("revolution") - if(src.occupant.mind in ticker.mode:revolutionaries) - ticker.mode:update_all_rev_icons() //So the icon actually appears - if(src.occupant.mind in ticker.mode:head_revolutionaries) - ticker.mode:update_all_rev_icons() - if("nuclear emergency") - if (src.occupant.mind in ticker.mode:syndicates) - ticker.mode:update_all_synd_icons() - if("cult") - if (src.occupant.mind in ticker.mode:cult) - ticker.mode:add_cultist(src.occupant.mind) - ticker.mode:update_all_cult_icons() //So the icon actually appears - */ - - /* Plantpeople - switch(ticker.mode.name) - if ("revolution") - if (podman.mind in ticker.mode:revolutionaries) - ticker.mode:add_revolutionary(podman.mind) - ticker.mode:update_all_rev_icons() //So the icon actually appears - if (podman.mind in ticker.mode:head_revolutionaries) - ticker.mode:update_all_rev_icons() - if ("nuclear emergency") - if (podman.mind in ticker.mode:syndicates) - ticker.mode:update_all_synd_icons() - if ("cult") - if (podman.mind in ticker.mode:cult) - ticker.mode:add_cultist(podman.mind) - ticker.mode:update_all_cult_icons() //So the icon actually appears - if ("changeling") - if (podman.mind in ticker.mode:changelings) - podman.make_changeling() - */ \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7905a8f3633..60335b8df2c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -601,6 +601,20 @@ var/list/slot_equipment_priority = list( \ /mob/proc/is_active() return (0 >= usr.stat) +/mob/proc/is_dead() + return stat == DEAD + +/mob/proc/is_mechanical() + if(mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI")) + return 1 + return istype(src, /mob/living/silicon) || get_species() == "Machine" + +/mob/proc/is_ready() + return !!client + +/mob/proc/get_gender() + return gender + /mob/proc/see(message) if(!is_active()) return 0 diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index a59aac39277..db71474a91b 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -355,7 +355,8 @@ if(client.prefs.species) chosen_species = all_species[client.prefs.species] if(chosen_species) - if(is_alien_whitelisted(src, client.prefs.species) || !config.usealienwhitelist || !(chosen_species.flags & IS_WHITELISTED) || (client.holder.rights & R_ADMIN) )// Have to recheck admin due to no usr at roundstart. Latejoins are fine though. + // Have to recheck admin due to no usr at roundstart. Latejoins are fine though. + if(is_species_whitelisted(chosen_species) || has_admin_rights()) new_character = new(loc, client.prefs.species) if(!new_character) @@ -413,11 +414,36 @@ Move() return 0 - proc/close_spawn_windows() src << browse(null, "window=latechoices") //closes late choices window src << browse(null, "window=playersetup") //closes the player setup window + proc/has_admin_rights() + return client.holder.rights & R_ADMIN + + proc/is_species_whitelisted(datum/species/S) + if(!S) return 1 + return is_alien_whitelisted(src, S.name) || !config.usealienwhitelist || !(S.flags & IS_WHITELISTED) + +/mob/new_player/get_species() + var/datum/species/chosen_species + if(client.prefs.species) + chosen_species = all_species[client.prefs.species] + + if(!chosen_species) + return "Human" + + if(is_species_whitelisted(chosen_species) || has_admin_rights()) + return chosen_species.name + + return "Human" + +/mob/new_player/get_gender() + if(!client || !client.prefs) ..() + return client.prefs.gender + +/mob/new_player/is_ready() + return ready && !!client /mob/new_player/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null) return diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 6c5e1749ccc..d9f0a403b34 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -233,6 +233,8 @@ O.mmi = new /obj/item/device/mmi(O) O.mmi.transfer_identity(src)//Does not transfer key/client. + callHook("borgify", list(O)) + O.Namepick() spawn(0)//To prevent the proc from returning null. diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 1364f579bbf..3e9f1e437e5 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -71,7 +71,7 @@ dat += "
  • Red alert
  • " if(!config.ert_admin_call_only) dat += "
  • Emergency Response Team
  • " - + dat += "
  • Grant Emergency Maintenance Access
  • " dat += "
  • Revoke Emergency Maintenance Access
  • " dat += "" @@ -154,9 +154,16 @@ revoke_maint_all_access() feedback_inc("alert_keycard_auth_maintRevoke",1) if("Emergency Response Team") - if(!config.ert_admin_call_only) - trigger_armed_response_team(1) - feedback_inc("alert_keycard_auth_ert",1) + if(is_ert_blocked()) + usr << "\red All emergency response teams are dispatched and can not be called at this time." + return + + trigger_armed_response_team(1) + feedback_inc("alert_keycard_auth_ert",1) + +/obj/machinery/keycard_auth/proc/is_ert_blocked() + if(config.ert_admin_call_only) return 1 + return ticker.mode && ticker.mode.ert_disabled var/global/maint_all_access = 0 diff --git a/code/setup.dm b/code/setup.dm index b72d30401b4..6d0be632adc 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -652,19 +652,20 @@ var/list/TAGGERLOCATIONS = list("Disposals", #define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC) -#define BE_TRAITOR 1 -#define BE_OPERATIVE 2 -#define BE_CHANGELING 4 -#define BE_WIZARD 8 -#define BE_MALF 16 -#define BE_REV 32 -#define BE_ALIEN 64 -#define BE_PAI 128 -#define BE_CULTIST 256 -#define BE_MONKEY 512 -#define BE_NINJA 1024 -#define BE_RAIDER 2048 -#define BE_PLANT 4096 +#define BE_TRAITOR 1 +#define BE_OPERATIVE 2 +#define BE_CHANGELING 4 +#define BE_WIZARD 8 +#define BE_MALF 16 +#define BE_REV 32 +#define BE_ALIEN 64 +#define BE_PAI 128 +#define BE_CULTIST 256 +#define BE_MONKEY 512 +#define BE_NINJA 1024 +#define BE_RAIDER 2048 +#define BE_PLANT 4096 +#define BE_MUTINEER 8192 var/list/be_special_flags = list( "Traitor" = BE_TRAITOR, @@ -679,7 +680,8 @@ var/list/be_special_flags = list( "Monkey" = BE_MONKEY, "Ninja" = BE_NINJA, "Raider" = BE_RAIDER, - "Diona" = BE_PLANT + "Diona" = BE_PLANT, + "Mutineer" = BE_MUTINEER ) #define AGE_MIN 17 //youngest a character can be diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 09e29051f07..6623ca98fae 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/nano/css/shared.css b/nano/css/shared.css index e6a7f310824..b49b7423442 100644 --- a/nano/css/shared.css +++ b/nano/css/shared.css @@ -70,6 +70,15 @@ a.icon img, .linkOn.icon img, .linkOff.icon img, .selected.icon img, .disabled.i width: 18px; height: 18px; } +.linkDanger, a.linkDanger:link, a.linkDanger:visited, a.linkDanger:active { + color: #ffffff; + background-color: #ff0000; + border-color: #aa0000; +} +.linkDanger:hover { + background-color: #ff6666; +} + ul { padding: 4px 0 0 10px; margin: 0; @@ -450,15 +459,15 @@ div.notice { } /* Table stuffs for power monitor */ -table.pmon -{ +table.pmon +{ border:2px solid RoyalBlue; } -table.pmon td, table.pmon th -{ +table.pmon td, table.pmon th +{ border-bottom:1px dotted black; -padding:0px 5px 0px 5px; +padding:0px 5px 0px 5px; } diff --git a/nano/templates/accounts_terminal.tmpl b/nano/templates/accounts_terminal.tmpl new file mode 100644 index 00000000000..9903878ee6b --- /dev/null +++ b/nano/templates/accounts_terminal.tmpl @@ -0,0 +1,168 @@ +
    +
    + Machine: +
    +
    + {{:machine_id}} +
    +
    +
    +
    + ID: +
    +
    + {{:~link(id_card, 'eject', {'choice' : "insert_card"}, null, id_inserted ? 'fixedLeftWidest' : 'fixedLeft')}} +
    +
    + +{{if access_level > 0}} +
    +

    Menu

    +
    + {{:~link('Home', 'home', {'choice' : 'view_accounts_list'}, !creating_new_account && !detailed_account_view ? 'disabled' : null, 'fixedLeft')}} + {{:~link('New Account', 'gear', {'choice' : 'create_account'}, creating_new_account ? 'disabled' : null, 'fixedLeft')}} + {{:~link('Print', 'print', {'choice' : 'print'}, creating_new_account ? 'disabled' : null, 'fixedLeft')}} + + {{if creating_new_account}} +
    +

    Create Account

    +
    + +
    + + +
    +
    + Account Holder: +
    +
    + +
    +
    +
    +
    + Initial Deposit: +
    +
    + +
    +
    +
    + +
    +
    + {{else}} + {{if detailed_account_view}} +
    +

    Account Details

    +
    + +
    +
    + Account Number: +
    +
    + #{{:account_number}} +
    +
    + +
    +
    + Holder: +
    +
    + {{:owner_name}} +
    +
    + +
    +
    + Balance: +
    +
    + ${{:~formatNumber(money)}} +
    +
    + +
    +
    + Status: +
    +
    + + {{: suspended ? "Suspended" : "Active"}} + +
    +
    +
    + {{:~link(suspended ? "Unsuspend" : "Suspend", 'gear', {'choice' : 'toggle_suspension'})}} +
    + +
    + {{if transactions}} + + + + + + + + + + + + {{for transactions}} + + + + + + + + {{/for}} + +
    TimestampTargetReasonValueTerminal
    {{:date}} {{:time}}{{:target_name}}{{:purpose}}{{:amount}}{{:source_terminal}}
    + {{else}} + This account has no financial transactions on record for today. + {{/if}} +
    + +
    +

    CentCom Administrator

    +
    +
    +
    + Payroll: +
    + {{:~link('Revoke', 'transferthick-e-w', {'choice' : 'revoke_payroll'}, account_number == station_account_number ? 'disabled' : null, 'linkDanger')}} +
    + {{if access_level >= 2}} +
    +
    + Silent Fund Adjustment: +
    + {{:~link('Add', 'plus', {'choice' : 'add_funds'})}} + {{:~link('Remove', 'minus', {'choice' : 'remove_funds'})}} +
    + {{/if}} + {{else}} + +
    +

    NanoTrasen Accounts

    +
    + {{if accounts}} + + {{for accounts}} + + + + + + {{/for}} +
    {{:~link('#' + account_number, '', {'choice' : 'view_account_detail', 'account_index' : account_index})}}{{:owner_name}}{{:suspended}}
    + {{else}} + There are no accounts available. + {{/if}} + {{/if}} + {{/if}} +{{/if}} diff --git a/nano/templates/identification_computer.tmpl b/nano/templates/identification_computer.tmpl new file mode 100644 index 00000000000..3c52746ce8f --- /dev/null +++ b/nano/templates/identification_computer.tmpl @@ -0,0 +1,236 @@ +{{if printing}} +
    The computer is currently busy.
    +
    +
    Printing...
    +
    +

    + Thank you for your patience! +

    +{{else}} + {{:~link('Access Modification', 'home', {'choice' : 'mode', 'mode_target' : 0}, !mode ? 'disabled' : null)}} + {{:~link('Crew Manifest', 'folder-open', {'choice' : 'mode', 'mode_target' : 1}, mode ? 'disabled' : null)}} + {{:~link('Print', 'print', {'choice' : 'print'}, (mode || has_modify) ? null : 'disabled')}} + + {{if mode}} +
    +

    Crew Manifest

    +
    +
    + {{:manifest}} +
    + {{else}} +
    +

    Access Modification

    +
    + + {{if !authenticated}} + Please insert the IDs into the terminal to proceed.
    + {{/if}} + +
    +
    + Target Identity: +
    +
    + {{:~link(target_name, 'eject', {'choice' : 'modify'})}} +
    +
    +
    +
    + Authorized Identity: +
    +
    + {{:~link(scan_name, 'eject', {'choice' : 'scan'})}} +
    +
    +
    + + {{if authenticated}} + + + {{if has_modify}} +
    +

    Details

    +
    + +
    +
    +
    + + + Registered Name: +
    +
    + + +
    +
    +
    + +
    +
    +
    + + + Account Number: +
    +
    + + +
    +
    +
    + +
    +
    + Terminations: +
    +
    + {{:~link('Terminate ' + target_owner, 'gear', {'choice' : 'terminate'}, target_rank == "Terminated" ? 'disabled' : null, target_rank == "Terminated" ? 'disabled' : 'linkDanger')}} +
    +
    + +
    +

    Assignment

    +
    + + +
    + +
    + + {{if centcom_access}} +
    +

    Central Command

    +
    +
    + {{for all_centcom_access}} +
    + {{:~link(desc, '', {'choice' : 'access', 'access_target' : ref, 'allowed' : allowed}, null, allowed ? 'selected' : null)}} +
    + {{/for}} +
    + {{else}} +
    +

    {{:station_name}}

    +
    +
    + {{for regions}} +
    +
    {{:name}}
    + {{for accesses}} +
    + {{:~link(desc, '', {'choice' : 'access', 'access_target' : ref, 'allowed' : allowed}, null, allowed ? 'selected' : null)}} +
    + {{/for}} +
    + {{/for}} +
    + {{/if}} + {{/if}} + {{/if}} + {{/if}} +{{/if}}