diff --git a/baystation12.dme b/baystation12.dme index 6bded644ac..24052903c4 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -231,6 +231,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 51f56d9d12..bcd52b9e88 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 cb51229c9f..1cd7c4aad8 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 5045da7592..48f1199ef7 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 f92d8b1c7b..ac804dc4f2 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,10 +45,16 @@ 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() @@ -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 fe252c3ab3..e97c915c0b 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 0000000000..f053d7c3a3 --- /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 0000000000..823645beef --- /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 0000000000..5c76326ad3 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm @@ -0,0 +1,36 @@ +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.") + +datum/directive/terminations/alien_fraud/get_crew_to_terminate() + var/list/aliens[0] + for(var/mob/living/carbon/human/H in player_list) + if (H.species.name == "Tajaran" || H.species.name == "Unathi") + aliens.Add(H) + 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/living/carbon/human/H in player_list) + if (H.species.name == "Tajaran") + tajarans++ + if (H.species.name == "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 0000000000..65058e8015 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/bluespace_contagion_directive.dm @@ -0,0 +1,45 @@ +#define INFECTION_COUNT 5 + +datum/directive/bluespace_contagion + var/infection_count = 5 + var/list/infected = list() + +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 = player_list.Copy() + var/list/infected_names = list() + for(var/i=0, i < INFECTION_COUNT, i++) + if(!candidates.len) + break + + var/mob/living/carbon/human/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() + return player_list.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 0000000000..a6c4ce2896 --- /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/living/carbon/human/H in player_list) + if (candidates.Find(H.mind.assigned_role)) + civilians.Add(H) + 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 0000000000..0889012829 --- /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/living/carbon/human/H in player_list) + if (H.species.name == "Machine") + machines.Add(H) + return machines + + proc/get_roboticists() + var/list/roboticists[0] + for(var/mob/living/carbon/human/H in player_list) + if (roboticist_roles.Find(H.mind.assigned_role)) + roboticists.Add(H) + 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 NSS Exodus 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 0000000000..a6305a2a56 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/research_to_ripleys_directive.dm @@ -0,0 +1,65 @@ +#define MATERIALS_REQUIRED 200 + +datum/directive/research_to_ripleys + var/list/ids_to_reassign = list() + var/materials_shipped = 0 + + proc/get_researchers() + var/list/researchers[0] + for(var/mob/living/carbon/human/H in player_list) + if (H.mind.assigned_role in science_positions - "Research Director") + researchers.Add(H) + 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 0000000000..c9b86ecdf1 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm @@ -0,0 +1,83 @@ +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.gender == FEMALE ? MALE : FEMALE + + proc/get_crew_of_target_gender() + var/list/targets[0] + for(var/mob/living/carbon/human/H in player_list) + if(H.species.name != "Machine" && H.gender == get_target_gender()) + targets.Add(H) + return targets + + proc/get_target_heads() + var/list/heads[0] + for(var/mob/living/carbon/human/H in get_crew_of_target_gender()) + if(command_positions.Find(H.mind.assigned_role)) + heads.Add(H) + return heads + + proc/get_target_aliens() + var/list/aliens[0] + for(var/mob/living/carbon/human/H in get_crew_of_target_gender()) + if (H.species.name == "Tajaran" || H.species.name == "Unathi" || H.species.name == "Skrell") + aliens.Add(H) + 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/list/targets = get_crew_of_target_gender() + return targets.len >= 3 + +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 0000000000..bc9f808d90 --- /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/A in get_crew_to_terminate()) + var/datum/money_account/account = A.mind.initial_account + accounts_to_revoke["[account.account_number]"] = 0 + accounts_to_suspend["[account.account_number]"] = account.suspended + ids_to_terminate.Add(A.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 0000000000..74e990f41c --- /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/living/carbon/human/H in player_list) + uglies.Add(H) + 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 0000000000..8aee11e11f --- /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 CentComm 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 0000000000..d847f8b70b --- /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 (0) + usr << "Is is calibrated for the Captain's Authentication Key." + if (1) + 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 0000000000..acef3b1d08 --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny.dm @@ -0,0 +1,350 @@ +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/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.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.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) + loyalists.Add(M) + if (M in mutineers) + mutineers.Remove(M) + + 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/add_mutineer(datum/mind/M) + mutineers.Add(M) + if (M in loyalists) + loyalists.Remove(M) + + M.current << fluff.mutineer_tag("You have joined the mutineers!") + head_mutineer.current << fluff.mutineer_tag("[M] has joined the mutineers!") + 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/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 M.current.client.images) + if (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_turf() : "Lost or Destroyed" + var/turf/secondary_key_loc = secondary_key ? secondary_key.get_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 << "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 << "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 << "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() + + 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 && P.mind && !P.mind.special_role) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + 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 && P.mind && !P.mind.special_role) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + 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 0000000000..dc13acda62 --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny_fluff.dm @@ -0,0 +1,209 @@ +/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.

    + "} diff --git a/code/game/gamemodes/mutiny/mutiny_hooks.dm b/code/game/gamemodes/mutiny/mutiny_hooks.dm new file mode 100644 index 0000000000..4edb763ac0 --- /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 4361460980..853481b144 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 fcf525cc36..176606face 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,91 @@ 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") + 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 6dd173f447..8e0fd14023 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 1b10ee72b9..624c3824d1 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -476,6 +476,12 @@ /obj/item/proc/IsShield() return 0 +/obj/item/proc/get_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 3d3b88f4d0..6626b4d36c 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 a518cef566..ce719f6c68 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 037956d5fd..a9a5a5715e 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 87ed478705..c051a72eb1 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -1,527 +1,506 @@ /datum/admins/proc/player_panel_new()//The new one - if (!usr.client.holder) - return - var/dat = "Admin Player Panel" + if (!usr.client.holder) + return + var/dat = "Admin Player Panel" - //javascript, the part that does most of the work~ - dat += {" + //javascript, the part that does most of the work~ + dat += {" - - - - - - "} - - //body tag start + onload and onkeypress (onkeyup) javascript event calls - dat += "" - - //title + search bar - dat += {" - - - - - - - - -
    - Player panel
    - Hover over a line to see more information - Check antagonists -

    -

    - Search: -
    - - "} - - //player table header - dat += {" - - "} - - var/list/mobs = sortmobs() - var/i = 1 - for(var/mob/M in mobs) - if(M.ckey) - - var/color = "#e6e6e6" - if(i%2 == 0) - color = "#f2f2f2" - var/is_antagonist = is_special_character(M) - - var/M_job = "" - - if(isliving(M)) - - if(iscarbon(M)) //Carbon stuff - if(ishuman(M)) - M_job = M.job - else if(isslime(M)) - M_job = "slime" - else if(ismonkey(M)) - M_job = "Monkey" - else if(isalien(M)) //aliens - if(islarva(M)) - M_job = "Alien larva" - else - M_job = "Alien" - else - M_job = "Carbon-based" - - else if(issilicon(M)) //silicon - if(isAI(M)) - M_job = "AI" - else if(ispAI(M)) - M_job = "pAI" - else if(isrobot(M)) - M_job = "Cyborg" - else - M_job = "Silicon-based" - - else if(isanimal(M)) //simple animals - if(iscorgi(M)) - M_job = "Corgi" - else - M_job = "Animal" - - else - M_job = "Living" - - else if(istype(M,/mob/new_player)) - M_job = "New player" - - else if(isobserver(M)) - M_job = "Ghost" - - M_job = replacetext(M_job, "'", "") - M_job = replacetext(M_job, "\"", "") - M_job = replacetext(M_job, "\\", "") - - var/M_name = M.name - M_name = replacetext(M_name, "'", "") - M_name = replacetext(M_name, "\"", "") - M_name = replacetext(M_name, "\\", "") - var/M_rname = M.real_name - M_rname = replacetext(M_rname, "'", "") - M_rname = replacetext(M_rname, "\"", "") - M_rname = replacetext(M_rname, "\\", "") - - var/M_key = M.key - M_key = replacetext(M_key, "'", "") - M_key = replacetext(M_key, "\"", "") - M_key = replacetext(M_key, "\\", "") - - //output for each mob - dat += {" - - - - - - "} - - i++ - - - //player table ending - dat += {" -
    - - - [M_name] - [M_rname] - [M_key] ([M_job]) - -
    -
    -
    - - - - "} - - usr << browse(dat, "window=players;size=600x480") + var maintable_data = document.getElementById('maintable_data'); + var ltr = maintable_data.getElementsByTagName("tr"); + for ( var i = 0; i < ltr.length; ++i ) + { + try{ + var tr = ltr\[i\]; + if(tr.getAttribute("id").indexOf("data") != 0){ + continue; + } + var ltd = tr.getElementsByTagName("td"); + var td = ltd\[0\]; + var lsearch = td.getElementsByTagName("b"); + var search = lsearch\[0\]; + //var inner_span = li.getElementsByTagName("span")\[1\] //Should only ever contain one element. + //document.write("

    "+search.innerText+"
    "+filter+"
    "+search.innerText.indexOf(filter)) + if ( search.innerText.toLowerCase().indexOf(filter) == -1 ) + { + //document.write("a"); + //ltr.removeChild(tr); + td.innerHTML = ""; + i--; + } + }catch(err) { } + } + } + + var count = 0; + var index = -1; + var debug = document.getElementById("debug"); + + locked_tabs = new Array(); + + } + + function expand(id,job,name,real_name,image,key,ip,antagonist,ref){ + + clearAll(); + + var span = document.getElementById(id); + + body = "
    "; + + body += ""; + + body += ""+job+" "+name+"
    Real name "+real_name+"
    Played by "+key+" ("+ip+")
    " + + body += "
    "; + + body += "PP - " + body += "N - " + body += "VV - " + body += "TP - " + body += "PM - " + body += "SM - " + body += "JMP
    " + if(antagonist > 0) + body += "Antagonist"; + + body += "
    "; + + + span.innerHTML = body + } + + function clearAll(){ + var spans = document.getElementsByTagName('span'); + for(var i = 0; i < spans.length; i++){ + var span = spans\[i\]; + + var id = span.getAttribute("id"); + + if(!(id.indexOf("item")==0)) + continue; + + var pass = 1; + + for(var j = 0; j < locked_tabs.length; j++){ + if(locked_tabs\[j\]==id){ + pass = 0; + break; + } + } + + if(pass != 1) + continue; + + + + + span.innerHTML = ""; + } + } + + function addToLocked(id,link_id,notice_span_id){ + var link = document.getElementById(link_id); + var decision = link.getAttribute("name"); + if(decision == "1"){ + link.setAttribute("name","2"); + }else{ + link.setAttribute("name","1"); + removeFromLocked(id,link_id,notice_span_id); + return; + } + + var pass = 1; + for(var j = 0; j < locked_tabs.length; j++){ + if(locked_tabs\[j\]==id){ + pass = 0; + break; + } + } + if(!pass) + return; + locked_tabs.push(id); + var notice_span = document.getElementById(notice_span_id); + notice_span.innerHTML = "Locked "; + //link.setAttribute("onClick","attempt('"+id+"','"+link_id+"','"+notice_span_id+"');"); + //document.write("removeFromLocked('"+id+"','"+link_id+"','"+notice_span_id+"')"); + //document.write("aa - "+link.getAttribute("onClick")); + } + + function attempt(ab){ + return ab; + } + + function removeFromLocked(id,link_id,notice_span_id){ + //document.write("a"); + var index = 0; + var pass = 0; + for(var j = 0; j < locked_tabs.length; j++){ + if(locked_tabs\[j\]==id){ + pass = 1; + index = j; + break; + } + } + if(!pass) + return; + locked_tabs\[index\] = ""; + var notice_span = document.getElementById(notice_span_id); + notice_span.innerHTML = ""; + //var link = document.getElementById(link_id); + //link.setAttribute("onClick","addToLocked('"+id+"','"+link_id+"','"+notice_span_id+"')"); + } + + function selectTextField(){ + var filter_text = document.getElementById('filter'); + filter_text.focus(); + filter_text.select(); + } + + + + + + "} + + //body tag start + onload and onkeypress (onkeyup) javascript event calls + dat += "" + + //title + search bar + dat += {" + + + + + + + + +
    + Player panel
    + Hover over a line to see more information - Check antagonists +

    +

    + Search: +
    + + "} + + //player table header + dat += {" + + "} + + var/list/mobs = sortmobs() + var/i = 1 + for(var/mob/M in mobs) + if(M.ckey) + + var/color = "#e6e6e6" + if(i%2 == 0) + color = "#f2f2f2" + var/is_antagonist = is_special_character(M) + + var/M_job = "" + + if(isliving(M)) + + if(iscarbon(M)) //Carbon stuff + if(ishuman(M)) + M_job = M.job + else if(isslime(M)) + M_job = "slime" + else if(ismonkey(M)) + M_job = "Monkey" + else if(isalien(M)) //aliens + if(islarva(M)) + M_job = "Alien larva" + else + M_job = "Alien" + else + M_job = "Carbon-based" + + else if(issilicon(M)) //silicon + if(isAI(M)) + M_job = "AI" + else if(ispAI(M)) + M_job = "pAI" + else if(isrobot(M)) + M_job = "Cyborg" + else + M_job = "Silicon-based" + + else if(isanimal(M)) //simple animals + if(iscorgi(M)) + M_job = "Corgi" + else + M_job = "Animal" + + else + M_job = "Living" + + else if(istype(M,/mob/new_player)) + M_job = "New player" + + else if(isobserver(M)) + M_job = "Ghost" + + M_job = replacetext(M_job, "'", "") + M_job = replacetext(M_job, "\"", "") + M_job = replacetext(M_job, "\\", "") + + var/M_name = M.name + M_name = replacetext(M_name, "'", "") + M_name = replacetext(M_name, "\"", "") + M_name = replacetext(M_name, "\\", "") + var/M_rname = M.real_name + M_rname = replacetext(M_rname, "'", "") + M_rname = replacetext(M_rname, "\"", "") + M_rname = replacetext(M_rname, "\\", "") + + var/M_key = M.key + M_key = replacetext(M_key, "'", "") + M_key = replacetext(M_key, "\"", "") + M_key = replacetext(M_key, "\\", "") + + //output for each mob + dat += {" + + + + + + "} + + i++ + + + //player table ending + dat += {" +
    + + + [M_name] - [M_rname] - [M_key] ([M_job]) + +
    +
    +
    + + + + "} + + usr << browse(dat, "window=players;size=600x480") //The old one /datum/admins/proc/player_panel_old() - if (!usr.client.holder) - return - var/dat = "Player Menu" - dat += "" - //add to this if wanting to add back in IP checking - //add if you want to know their ip to the lists below - var/list/mobs = sortmobs() + if (!usr.client.holder) + return + var/dat = "Player Menu" + dat += "
    NameReal NameAssigned JobKeyOptionsPMTraitor?
    IP:(IP: [M.lastKnownIP])
    " + //add to this if wanting to add back in IP checking + //add if you want to know their ip to the lists below + var/list/mobs = sortmobs() - for(var/mob/M in mobs) - if(!M.ckey) continue + for(var/mob/M in mobs) + if(!M.ckey) continue - dat += "" - if(isAI(M)) - dat += "" - else if(isrobot(M)) - dat += "" - else if(ishuman(M)) - dat += "" - else if(istype(M, /mob/living/silicon/pai)) - dat += "" - else if(istype(M, /mob/new_player)) - dat += "" - else if(isobserver(M)) - dat += "" - else if(ismonkey(M)) - dat += "" - else if(isalien(M)) - dat += "" - else - dat += "" + dat += "" + if(isAI(M)) + dat += "" + else if(isrobot(M)) + dat += "" + else if(ishuman(M)) + dat += "" + else if(istype(M, /mob/living/silicon/pai)) + dat += "" + else if(istype(M, /mob/new_player)) + dat += "" + else if(isobserver(M)) + dat += "" + else if(ismonkey(M)) + dat += "" + else if(isalien(M)) + dat += "" + else + dat += "" - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.mind && H.mind.assigned_role) - dat += "" - else - dat += "" + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(H.mind && H.mind.assigned_role) + dat += "" + else + dat += "" - dat += {" - - - "} - switch(is_special_character(M)) - if(0) - dat += {""} - if(1) - dat += {""} - if(2) - dat += {""} + dat += {" + + + "} + switch(is_special_character(M)) + if(0) + dat += {""} + if(1) + dat += {""} + if(2) + dat += {""} - dat += "
    NameReal NameAssigned JobKeyOptionsPMTraitor?
    IP:(IP: [M.lastKnownIP])
    [M.name]AICyborg[M.real_name]pAINew PlayerGhostMonkeyAlienUnknown
    [M.name]AICyborg[M.real_name]pAINew PlayerGhostMonkeyAlienUnknown[H.mind.assigned_role]NA[H.mind.assigned_role]NA[(M.client ? "[M.client]" : "No client")]XPMTraitor?Traitor?Traitor?[(M.client ? "[M.client]" : "No client")]XPMTraitor?Traitor?Traitor?
    " + dat += "" - usr << browse(dat, "window=players;size=640x480") + usr << browse(dat, "window=players;size=640x480") /datum/admins/proc/check_antagonists() - if (ticker && ticker.current_state >= GAME_STATE_PLAYING) - var/dat = "Round Status

    Round Status

    " - dat += "Current Game Mode: [ticker.mode.name]
    " - dat += "Round Duration: [round(world.time / 36000)]:[add_zero(world.time / 600 % 60, 2)]:[world.time / 100 % 6][world.time / 100 % 10]
    " - dat += "Emergency shuttle
    " - if (!emergency_shuttle.online) - dat += "Call Shuttle
    " - else - var/timeleft = emergency_shuttle.timeleft() - switch(emergency_shuttle.location) - if(0) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " - dat += "Send Back
    " - if(1) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " - dat += "[ticker.delay_end ? "End Round Normally" : "Delay Round End"]
    " - if(ticker.mode.syndicates.len) - dat += "
    " - for(var/datum/mind/N in ticker.mode.syndicates) - var/mob/M = N.current - if(M) - dat += "" - dat += "" - else - dat += "" - dat += "
    Syndicates
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    Nuclear Operative not found!

    " - for(var/obj/item/weapon/disk/nuclear/N in world) - dat += "" - dat += "
    Nuclear Disk(s)
    [N.name], " - var/atom/disk_loc = N.loc - while(!istype(disk_loc, /turf)) - if(istype(disk_loc, /mob)) - var/mob/M = disk_loc - dat += "carried by [M.real_name] " - if(istype(disk_loc, /obj)) - var/obj/O = disk_loc - dat += "in \a [O.name] " - disk_loc = disk_loc.loc - dat += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])
    " + if (ticker && ticker.current_state >= GAME_STATE_PLAYING) + var/dat = "Round Status

    Round Status

    " + dat += "Current Game Mode: [ticker.mode.name]
    " + dat += "Round Duration: [round(world.time / 36000)]:[add_zero(world.time / 600 % 60, 2)]:[world.time / 100 % 6][world.time / 100 % 10]
    " + dat += "Emergency shuttle
    " + if (!emergency_shuttle.online) + dat += "Call Shuttle
    " + else + var/timeleft = emergency_shuttle.timeleft() + switch(emergency_shuttle.location) + if(0) + dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + dat += "Send Back
    " + if(1) + dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + dat += "[ticker.delay_end ? "End Round Normally" : "Delay Round End"]
    " + if(ticker.mode.syndicates.len) + dat += "
    " + for(var/datum/mind/N in ticker.mode.syndicates) + var/mob/M = N.current + if(M) + dat += "" + dat += "" + else + dat += "" + dat += "
    Syndicates
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    Nuclear Operative not found!

    " + for(var/obj/item/weapon/disk/nuclear/N in world) + dat += "" + dat += "
    Nuclear Disk(s)
    [N.name], " + var/atom/disk_loc = N.loc + while(!istype(disk_loc, /turf)) + if(istype(disk_loc, /mob)) + var/mob/M = disk_loc + dat += "carried by [M.real_name] " + if(istype(disk_loc, /obj)) + var/obj/O = disk_loc + dat += "in \a [O.name] " + disk_loc = disk_loc.loc + dat += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])
    " - if(ticker.mode.head_revolutionaries.len || ticker.mode.revolutionaries.len) - dat += "
    " - for(var/datum/mind/N in ticker.mode.head_revolutionaries) - var/mob/M = N.current - if(!M) - dat += "" - else - dat += "" - dat += "" - for(var/datum/mind/N in ticker.mode.revolutionaries) - var/mob/M = N.current - if(M) - dat += "" - dat += "" - dat += "
    Revolutionaries
    Head Revolutionary not found!
    [M.real_name] (Leader)[M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    " - for(var/datum/mind/N in ticker.mode.get_living_heads()) - var/mob/M = N.current - if(M) - dat += "" - dat += "" - var/turf/mob_loc = get_turf_loc(M) - dat += "" - else - dat += "" - dat += "
    Target(s)Location
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM[mob_loc.loc]
    Head not found!
    " + if(ticker.mode.head_revolutionaries.len || ticker.mode.revolutionaries.len) + dat += "
    " + for(var/datum/mind/N in ticker.mode.head_revolutionaries) + var/mob/M = N.current + if(!M) + dat += "" + else + dat += "" + dat += "" + for(var/datum/mind/N in ticker.mode.revolutionaries) + var/mob/M = N.current + if(M) + dat += "" + dat += "" + dat += "
    Revolutionaries
    Head Revolutionary not found!
    [M.real_name] (Leader)[M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
    " + for(var/datum/mind/N in ticker.mode.get_living_heads()) + var/mob/M = N.current + if(M) + dat += "" + dat += "" + var/turf/mob_loc = get_turf_loc(M) + dat += "" + else + dat += "" + dat += "
    Target(s)Location
    [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM[mob_loc.loc]
    Head not found!
    " - 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
    " + if(ticker.mode.cult.len) + 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) - dat += "" - usr << browse(dat, "window=roundstatus;size=400x500") - else - alert("The game hasn't started yet!") + 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 87588e97e8..5129bba4a0 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 12ae785a55..08615a6b25 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 70bf8ee125..f94766f16c 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 5e94ea9bc9..27b9da88e8 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 018f154b86..bebd5680ef 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) @@ -86,6 +86,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 10c0746dc6..bdd834a80d 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 fdb32e82cc..388cab4e10 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -600,6 +600,9 @@ var/list/slot_equipment_priority = list( \ /mob/proc/is_active() return (0 >= usr.stat) +/mob/proc/is_dead() + return stat == DEAD + /mob/proc/see(message) if(!is_active()) return 0 diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 9a2493f23a..6efec2f7ec 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -181,6 +181,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 1364f579bb..3e9f1e437e 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 c9edaa2844..24066cd2df 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -647,19 +647,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, @@ -674,7 +675,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 09e29051f0..6623ca98fa 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 e6a7f31082..b49b742344 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 0000000000..9903878ee6 --- /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 0000000000..3c52746ce8 --- /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}}