From 37db1d679d22db316ae9f1849ea5d9e79ec93f8f Mon Sep 17 00:00:00 2001 From: Duck- Date: Sun, 4 Jan 2015 18:51:57 -0500 Subject: [PATCH 01/53] Removes the ridiculous autoheal. you aren't going to heal from being clubbed with a wrench in 30 seconds in real life really also, now you won't regenerate all your hp from bullets or whatever. it was really ridiculous before. --- code/controllers/configuration.dm | 2 +- data/investigate/gravity.html | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 598d1eb4..81e0ac62 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -104,7 +104,7 @@ var/health_threshold_dead = -100 var/organ_health_multiplier = 1 - var/organ_regeneration_multiplier = 1 + var/organ_regeneration_multiplier = 0 var/bones_can_break = 0 var/limbs_can_break = 0 diff --git a/data/investigate/gravity.html b/data/investigate/gravity.html index 359e2093..fb0f61b0 100644 --- a/data/investigate/gravity.html +++ b/data/investigate/gravity.html @@ -1,6 +1,6 @@ -04:09 [0x20078fb] (92,152,1) || the gravitational generator has regained power.
-04:09 [0x20078fb] (92,152,1) || the gravitational generator is now charging.
-04:10 [0x20078fb] (92,152,1) || the gravitational generator has regained power.
-04:10 [0x20078fb] (92,152,1) || the gravitational generator is now charging.
-04:10 [0x20078fb] (92,152,1) || the gravitational generator has regained power.
-04:10 [0x20078fb] (92,152,1) || the gravitational generator is now charging.
+18:41 [0x200791e] (92,152,1) || the gravitational generator has regained power.
+18:41 [0x200791e] (92,152,1) || the gravitational generator is now charging.
+18:42 [0x200791e] (92,152,1) || the gravitational generator has regained power.
+18:42 [0x200791e] (92,152,1) || the gravitational generator is now charging.
+18:42 [0x200791e] (92,152,1) || the gravitational generator has regained power.
+18:42 [0x200791e] (92,152,1) || the gravitational generator is now charging.
From 1b7a8dd2aa7b54a52fb54c77102c151278831bc6 Mon Sep 17 00:00:00 2001 From: skull132 Date: Wed, 7 Jan 2015 21:34:13 +0200 Subject: [PATCH 02/53] IP Blacklist Update Works on SQL now. --- code/modules/admin/IsBanned.dm | 39 +++++++++++++++++++++++++--------- code/world.dm | 4 ++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index c2c40831..3c9295ae 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -20,17 +20,36 @@ world/IsBanned(key,address,computer_id) return list("reason"="Using ToR", "desc"="\nReason: The network you are using to connect has been banned.\nIf you believe this is a mistake, please request help at [config.banappeals]") if(config && config.ip_blacklist_enabled) - msg_scopes("Doing blacklist stuff") - if(!blacklist) - loadBlacklist() - if(blacklist) - if(address in blacklist) - log_access("Failed Login: [src] - Blacklisted IP") - message_admins("\blue Failed Login: [src] - Blacklisted IP") - AddBan(ckey(key), computer_id, "Bad IP", "Automated Ban", 0, 0) - return list("reason"="IP Blacklisted", "desc"="\nReason: This IP has been blacklisted from the server.\nIf you believe this is a mistake, please request help at [config.banappeals]") + var/banhe = 0 + if(!establish_db_connection()) + //No database. Make do! + error("Database connection failed while checking blacklist. Reverted to old system.") + msg_scopes("Database connection failed while checking blacklist. Reverted to old system.") + + msg_scopes("Doing blacklist stuff") + if(!blacklist) + loadBlacklist() + if(blacklist) + if(address in blacklist) + banhe = 1 + else + msg_scopes("Where is the blacklist!") else - msg_scopes("Where is the blacklist!") + //Have data, will base. + var/DBQuery/query = dbcon.NewQuery("SELECT ip FROM aurora_ipblacklist WHERE ip = '[address]'") + query.Execute() + + while(query.NextRow()) + var/bip = query.item[1] + + if(bip == address) + banhe = 1 + + if(banhe == 1) + log_access("Failed Login: [src] - Blacklisted IP") + message_admins("\blue Failed Login: [src] - Blacklisted IP") + AddBan(ckey(key), computer_id, "Bad IP", "Automated Ban", 0, 0) + return list("reason"="IP Blacklisted", "desc"="\nReason: This IP has been blacklisted from the server.\nIf you believe this is a mistake, please request help at [config.banappeals]") if(config.ban_legacy_system) diff --git a/code/world.dm b/code/world.dm index 948585b3..9517f3ef 100644 --- a/code/world.dm +++ b/code/world.dm @@ -33,8 +33,8 @@ callHook("startup") //Emergency Fix load_mods() - if(config.ip_blacklist_enabled) - loadBlacklist() +// if(config.ip_blacklist_enabled) Should no longer be necessary. +// loadBlacklist() //end-emergency fix src.update_status() From 4857641935d32d974c659185e1d9474fb5219563 Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Thu, 8 Jan 2015 18:51:23 +0000 Subject: [PATCH 03/53] Changeling Language --- code/game/gamemodes/changeling/changeling_powers.dm | 3 +++ html/changelog.html | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index d98853dd..5ef85a84 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -33,6 +33,8 @@ if(!(language in mind.changeling.absorbed_languages)) mind.changeling.absorbed_languages += language + add_language("Changeling") + return 1 //removes our changeling verbs @@ -41,6 +43,7 @@ for(var/datum/power/changeling/P in mind.changeling.purchasedpowers) if(P.isVerb) verbs -= P.verbpath + remove_language("Changeling") //Helper proc. Does all the checks and stuff for us to avoid copypasta diff --git a/html/changelog.html b/html/changelog.html index e5203d4d..8c6c8bc3 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,15 @@ should be listed in the changelog upon commit though. Thanks. --> +
+

8 January 2015

+

SoundScopes updated:

+
    +
  • Changelings start with their language again
  • +
+
+ +

5 January 2015

SoundScopes updated:

From 29924324a647d4bc8d307b436d4c276c7ced4e9a Mon Sep 17 00:00:00 2001 From: suethecake Date: Thu, 8 Jan 2015 17:46:36 -0600 Subject: [PATCH 04/53] MUTINY. O BOY. --- code/game/gamemodes/game_mode.dm | 4 + code/game/gamemodes/mutiny/auth_key.dm | 39 ++ code/game/gamemodes/mutiny/directive.dm | 32 ++ .../directives/alien_fraud_directive.dm | 41 ++ .../bluespace_contagion_directive.dm | 58 +++ .../directives/financial_crisis_directive.dm | 26 ++ .../mutiny/directives/ipc_virus_directive.dm | 96 +++++ .../tau_ceti_needs_women_directive.dm | 108 +++++ .../directives/terminations_directive.dm | 71 ++++ .../mutiny/emergency_authentication_device.dm | 105 +++++ code/game/gamemodes/mutiny/key_pinpointer.dm | 49 +++ code/game/gamemodes/mutiny/mutiny.dm | 384 ++++++++++++++++++ code/game/gamemodes/mutiny/mutiny_admin.dm | 83 ++++ code/game/gamemodes/mutiny/mutiny_fluff.dm | 198 +++++++++ code/game/gamemodes/mutiny/mutiny_hooks.dm | 27 ++ code/modules/admin/player_panel.dm | 37 ++ code/modules/admin/topic.dm | 4 + code/modules/client/preferences.dm | 3 +- code/modules/economy/Accounts_DB.dm | 296 +++++++++----- code/modules/mob/living/carbon/human/life.dm | 8 + code/modules/mob/living/login.dm | 5 + .../security levels/keycard authentication.dm | 13 +- code/setup.dm | 4 +- nano/templates/accounts_terminal.tmpl | 168 ++++++++ 24 files changed, 1757 insertions(+), 102 deletions(-) create mode 100644 code/game/gamemodes/mutiny/auth_key.dm create mode 100644 code/game/gamemodes/mutiny/directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/bluespace_contagion_directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/financial_crisis_directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/ipc_virus_directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm create mode 100644 code/game/gamemodes/mutiny/directives/terminations_directive.dm create mode 100644 code/game/gamemodes/mutiny/emergency_authentication_device.dm create mode 100644 code/game/gamemodes/mutiny/key_pinpointer.dm create mode 100644 code/game/gamemodes/mutiny/mutiny.dm create mode 100644 code/game/gamemodes/mutiny/mutiny_admin.dm create mode 100644 code/game/gamemodes/mutiny/mutiny_fluff.dm create mode 100644 code/game/gamemodes/mutiny/mutiny_hooks.dm create mode 100644 nano/templates/accounts_terminal.tmpl diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 16bb208e..0932b1fc 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; @@ -418,6 +419,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 00000000..1f7ea3c8 --- /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 00000000..857e0730 --- /dev/null +++ b/code/game/gamemodes/mutiny/directive.dm @@ -0,0 +1,32 @@ +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_remaining_orders() + return "" + +/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 00000000..580322f8 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/alien_fraud_directive.dm @@ -0,0 +1,41 @@ +datum/directive/terminations/alien_fraud + special_orders = list( + "Suspend financial accounts of all Tajaran and Unathi personnel.", + "Transfer their payrolls to the station account.", + "Terminate their employment.") + + proc/is_alien(mob/M) + var/species = M.get_species() + return species == "Tajara" || species == "Unathi" + +datum/directive/terminations/alien_fraud/get_crew_to_terminate() + var/list/aliens[0] + for(var/mob/M in player_list) + if (M.is_ready() && is_alien(M) && M != mode.head_loyalist.current) + aliens.Add(M) + return aliens + +datum/directive/terminations/alien_fraud/get_description() + return {" +

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

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

+ A manufactured and near-undetectable virus is spreading on NanoTrasen stations. + The pathogen travels by bluespace after maturing for one day and meets the Sol Health Organisation standards for a class X biological threat, warranting use of lethal force to contain an outbreak. + No treatment has yet been discovered. Personnel onboard [station_name()] have been infected. Further information is classified. +

+ "} + +datum/directive/bluespace_contagion/initialize() + var/list/candidates = get_infection_candidates() + var/list/infected_names = list() + for(var/i=0, i < INFECTION_COUNT, i++) + if(!candidates.len) + break + + var/mob/candidate = pick(candidates) + candidates.Remove(candidate) + infected.Add(candidate) + infected_names.Add("[candidate.mind.assigned_role] [candidate.mind.name]") + + special_orders = list( + "Quarantine these personnel: [list2text(infected_names, ", ")].", + "Allow one hour for a cure to be manufactured.", + "If no cure arrives after that time, execute and burn the infected.") + +datum/directive/bluespace_contagion/meets_prerequisites() + var/list/candidates = get_infection_candidates() + return candidates.len >= 7 + +datum/directive/bluespace_contagion/directives_complete() + return infected.len == 0 + +datum/directive/bluespace_contagion/get_remaining_orders() + var/text = "" + for(var/victim in infected) + text += "
  • Kill [victim]
  • " + return text + +/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 00000000..03f36c4e --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/financial_crisis_directive.dm @@ -0,0 +1,26 @@ +datum/directive/terminations/financial_crisis + special_orders = list( + "Suspend financial accounts of all civilian personnel, excluding the Head of Personnel.", + "Transfer their payrolls to the station account.", + "Terminate their employment.") + +datum/directive/terminations/financial_crisis/get_crew_to_terminate() + var/list/civilians[0] + var/list/candidates = civilian_positions - "Head of Personnel" + for(var/mob/M in player_list) + if (M.is_ready() && candidates.Find(M.mind.assigned_role)) + civilians.Add(M) + return civilians + +datum/directive/terminations/financial_crisis/get_description() + return {" +

    + [system_name()] system banks in financial crisis. Local emergency situation ongoing. + NT Funds redistributed in accordance with financial regulations covered by employee contracts, 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 00000000..e035b665 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/ipc_virus_directive.dm @@ -0,0 +1,96 @@ +datum/directive/ipc_virus + special_orders = list( + "Terminate employment of all IPC personnel.", + "Extract the Positronic Brains from IPC units.", + "Mount the Positronic Brains into Cyborgs.") + + var/list/roboticist_roles = list( + "Research Director", + "Roboticist" + ) + + var/list/brains_to_enslave = list() + var/list/cyborgs_to_make = list() + var/list/ids_to_terminate = list() + + proc/get_ipcs() + var/list/machines[0] + for(var/mob/M in player_list) + if (M.is_ready() && M.get_species() == "Machine") + machines.Add(M) + return machines + + proc/get_roboticists() + var/list/roboticists[0] + for(var/mob/M in player_list) + if (M.is_ready() && roboticist_roles.Find(M.mind.assigned_role)) + roboticists.Add(M) + return roboticists + +datum/directive/ipc_virus/initialize() + for(var/mob/living/carbon/human/H in get_ipcs()) + brains_to_enslave.Add(H.mind) + cyborgs_to_make.Add(H.mind) + ids_to_terminate.Add(H.wear_id) + +datum/directive/ipc_virus/get_description() + return {" +

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

    + "} + +datum/directive/ipc_virus/meets_prerequisites() + var/list/ipcs = get_ipcs() + var/list/roboticists = get_roboticists() + return ipcs.len > 2 && roboticists.len > 1 + +datum/directive/ipc_virus/directives_complete() + return brains_to_enslave.len == 0 && cyborgs_to_make.len == 0 && ids_to_terminate.len == 0 + +datum/directive/ipc_virus/get_remaining_orders() + var/text = "" + for(var/brain in brains_to_enslave) + text += "
  • Debrain [brain]
  • " + + for(var/brain in cyborgs_to_make) + text += "
  • Enslave [brain] as a Cyborg
  • " + + for(var/id in ids_to_terminate) + text += "
  • Terminate [id]
  • " + + return text + +/hook/debrain/proc/debrain_directive(var/obj/item/organ/brain/B) + var/datum/directive/ipc_virus/D = get_directive("ipc_virus") + if (!D) return 1 + + if(B && B.brainmob && B.brainmob.mind && 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/tau_ceti_needs_women_directive.dm b/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm new file mode 100644 index 00000000..f1aa2e91 --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/tau_ceti_needs_women_directive.dm @@ -0,0 +1,108 @@ +datum/directive/tau_ceti_needs_women + var/list/command_targets = list() + var/list/alien_targets = list() + + proc/get_target_gender() + if(!mode.head_loyalist) return FEMALE + return mode.head_loyalist.current.get_gender() == FEMALE ? MALE : FEMALE + + proc/is_target_gender(mob/M) + var/species = M.get_species() + return species != "Diona" && M.get_gender() == get_target_gender() + + proc/get_crew_of_target_gender() + var/list/targets[0] + for(var/mob/M in player_list) + if(M.is_ready() && is_target_gender(M) && !M.is_mechanical()) + targets.Add(M) + return targets + + proc/get_target_heads() + var/list/heads[0] + for(var/mob/M in get_crew_of_target_gender()) + if(command_positions.Find(M.mind.assigned_role)) + heads.Add(M) + return heads + + proc/get_target_aliens() + var/list/aliens[0] + for(var/mob/M in get_crew_of_target_gender()) + var/species = M.get_species() + if(species == "Tajara" || species == "Unathi" || species == "Skrell") + aliens.Add(M) + return aliens + + proc/count_heads_reassigned() + var/heads_reassigned = 0 + for(var/obj/item/weapon/card/id in command_targets) + if (command_targets[id]) + heads_reassigned++ + + return heads_reassigned + +datum/directive/tau_ceti_needs_women/get_description() + return {" +

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

    + "} + +datum/directive/tau_ceti_needs_women/initialize() + for(var/mob/living/carbon/human/H in get_target_heads()) + command_targets[H.wear_id] = 0 + + for(var/mob/living/carbon/human/H in get_target_aliens()) + alien_targets.Add(H.wear_id) + + special_orders = list( + "Remove [get_target_gender()] personnel from Command positions.", + "Terminate employment of all [get_target_gender()] Skrell, Tajara, and Unathi.") + +datum/directive/tau_ceti_needs_women/meets_prerequisites() + var/females = 0 + var/males = 0 + for(var/mob/M in player_list) + if(M.is_ready() && !M.is_mechanical() && M.get_species() != "Diona") + var/gender = M.get_gender() + if(gender == MALE) + males++ + else if(gender == FEMALE) + females++ + + return males >= 2 && females >= 2 + +datum/directive/tau_ceti_needs_women/directives_complete() + return command_targets.len == count_heads_reassigned() && alien_targets.len == 0 + +datum/directive/tau_ceti_needs_women/get_remaining_orders() + var/text = "" + for(var/head in command_targets) + if(!command_targets[head]) + text += "
  • Remove [head] from a Head Role
  • " + + for(var/id in alien_targets) + text += "
  • Terminate [id]
  • " + + return text + +/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 00000000..7f3744dc --- /dev/null +++ b/code/game/gamemodes/mutiny/directives/terminations_directive.dm @@ -0,0 +1,71 @@ +// This is a parent directive meant to be derived by directives that fit +// the "fire X type of employee" pattern of directives. Simply apply your +// flavor text and override get_crew_to_terminate in your child datum. +// See alien_fraud_directive.dm for an example. +datum/directive/terminations + var/list/accounts_to_revoke = list() + var/list/accounts_to_suspend = list() + var/list/ids_to_terminate = list() + + proc/get_crew_to_terminate() + return list() + +datum/directive/terminations/directives_complete() + for(var/account_number in accounts_to_suspend) + if (!accounts_to_suspend[account_number]) + return 0 + + for(var/account_number in accounts_to_revoke) + if (!accounts_to_revoke[account_number]) + return 0 + + return ids_to_terminate.len == 0 + +datum/directive/terminations/initialize() + for(var/mob/living/carbon/human/H in get_crew_to_terminate()) + var/datum/money_account/account = H.mind.initial_account + accounts_to_revoke["[account.account_number]"] = 0 + accounts_to_suspend["[account.account_number]"] = account.suspended + ids_to_terminate.Add(H.wear_id) + +datum/directive/terminations/get_remaining_orders() + var/text = "" + for(var/account_number in accounts_to_suspend) + if(!accounts_to_suspend[account_number]) + text += "
  • Suspend Account #[account_number]
  • " + + for(var/account_number in accounts_to_revoke) + if(!accounts_to_revoke[account_number]) + text += "
  • Revoke Account #[account_number]
  • " + + for(var/id in ids_to_terminate) + text += "
  • Terminate [id]
  • " + + return text + +/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/emergency_authentication_device.dm b/code/game/gamemodes/mutiny/emergency_authentication_device.dm new file mode 100644 index 00000000..5bdcf275 --- /dev/null +++ b/code/game/gamemodes/mutiny/emergency_authentication_device.dm @@ -0,0 +1,105 @@ +/obj/machinery/emergency_authentication_device + var/datum/game_mode/mutiny/mode + + name = "\improper Emergency Authentication Device" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox" + density = 1 + anchored = 1 + + var/captains_key + var/secondary_key + var/activated = 0 + + flags = FPRINT + use_power = 0 + + New(loc, mode) + src.mode = mode + ..(loc) + + proc/check_key_existence() + if(!mode.captains_key) + captains_key = 1 + + if(!mode.secondary_key) + secondary_key = 1 + + proc/get_status() + if(activated) + return "Activated" + if(captains_key && secondary_key) + return "Both Keys Authenticated" + if(captains_key) + return "Captain's Key Authenticated" + if(secondary_key) + return "Secondary Key Authenticated" + else + return "Inactive" + +/obj/machinery/emergency_authentication_device/attack_hand(mob/user) + if(activated) + user << "\blue \The [src] is already active!" + return + + if(!mode.current_directive.directives_complete()) + state("Command aborted. Communication with CentCom is prohibited until Directive X has been completed.") + return + + check_key_existence() + if(captains_key && secondary_key) + activated = 1 + user << "\blue You activate \the [src]!" + state("Command acknowledged. Initiating quantum entanglement relay to NanoTrasen High Command.") + return + + if(!captains_key && !secondary_key) + state("Command aborted. Please present the authentication keys before proceeding.") + return + + if(!captains_key) + state("Command aborted. Please present the Captain's Authentication Key.") + return + + if(!secondary_key) + state("Command aborted. Please present the Emergency Secondary Authentication Key.") + return + + // Impossible! + state("Command aborted. This unit is defective.") + +/obj/machinery/emergency_authentication_device/attackby(obj/item/weapon/O, mob/user) + if(activated) + user << "\blue \The [src] is already active!" + return + + if(!mode.current_directive.directives_complete()) + state({"Command aborted. Communication with CentCom is prohibited until Directive X has been completed."}) + return + + check_key_existence() + if(istype(O, /obj/item/weapon/mutiny/auth_key/captain) && !captains_key) + captains_key = O + user.drop_item() + O.loc = src + + state("Key received. Thank you, Captain [mode.head_loyalist].") + spawn(5) + state(secondary_key ? "Your keys have been authenticated. Communication with CentCom is now authorized." : "Please insert the Emergency Secondary Authentication Key now.") + return + + if(istype(O, /obj/item/weapon/mutiny/auth_key/secondary) && !secondary_key) + secondary_key = O + user.drop_item() + O.loc = src + + state("Key received. Thank you, Secondary Authenticator [mode.head_mutineer].") + spawn(5) + state(captains_key ? "Your keys have been authenticated. Communication with CentCom is now authorized." : "Please insert the Captain's Authentication Key now.") + return + ..() + +/obj/machinery/emergency_authentication_device/examine() + usr << {" +This is a specialized communications device that is able to instantly send a message to NanoTrasen High Command via quantum entanglement with a sister device at CentCom. +The EAD's status is [get_status()]."} diff --git a/code/game/gamemodes/mutiny/key_pinpointer.dm b/code/game/gamemodes/mutiny/key_pinpointer.dm new file mode 100644 index 00000000..c481fd5b --- /dev/null +++ b/code/game/gamemodes/mutiny/key_pinpointer.dm @@ -0,0 +1,49 @@ +/obj/item/weapon/pinpointer/advpinpointer/auth_key + name = "\improper Authentication Key Pinpointer" + desc = "Tracks the positions of the emergency authentication keys." + var/datum/game_mode/mutiny/mutiny + + New() + mutiny = ticker.mode + ..() + +/obj/item/weapon/pinpointer/advpinpointer/auth_key/attack_self() + switch(mode) + if (0) + mode = 1 + active = 1 + target = mutiny.captains_key + workobj() + usr << "\blue You calibrate \the [src] to locate the Captain's Authentication Key." + if (1) + mode = 2 + target = mutiny.secondary_key + usr << "\blue You calibrate \the [src] to locate the Emergency Secondary Authentication Key." + else + mode = 0 + active = 0 + icon_state = "pinoff" + usr << "\blue You switch \the [src] off." + +/obj/item/weapon/pinpointer/advpinpointer/auth_key/examine() + switch(mode) + if (1) + usr << "Is is calibrated for the Captain's Authentication Key." + if (2) + usr << "It is calibrated for the Emergency Secondary Authentication Key." + else + usr << "It is switched off." + +/datum/supply_packs/key_pinpointer + name = "Authentication Key Pinpointer crate" + contains = list(/obj/item/weapon/pinpointer/advpinpointer/auth_key) + cost = 250 + containertype = /obj/structure/closet/crate + containername = "Authentication Key Pinpointer crate" + access = access_heads + group = "Operations" + + New() + // This crate is only accessible during mutiny rounds + if (istype(ticker.mode,/datum/game_mode/mutiny)) + ..() diff --git a/code/game/gamemodes/mutiny/mutiny.dm b/code/game/gamemodes/mutiny/mutiny.dm new file mode 100644 index 00000000..c6ba655c --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny.dm @@ -0,0 +1,384 @@ +#define MUTINY_RECRUITMENT_COOLDOWN 5 + +datum/game_mode/mutiny + var/datum/mutiny_fluff/fluff + var/datum/directive/current_directive + var/obj/item/weapon/mutiny/auth_key/captain/captains_key + var/obj/item/weapon/mutiny/auth_key/secondary/secondary_key + var/obj/machinery/emergency_authentication_device/ead + var/datum/mind/head_loyalist + var/datum/mind/head_mutineer + var/recruit_loyalist_cooldown = 0 + var/recruit_mutineer_cooldown = 0 + var/list/loyalists = list() + var/list/mutineers = list() + var/list/body_count = list() + + name = "mutiny" + config_tag = "mutiny" + required_players = 7 + ert_disabled = 1 + + uplink_welcome = "Mutineers Uplink Console:" + uplink_uses = 0 + + New() + fluff = new(src) + + proc/reveal_directives() + spawn(rand(1 MINUTE, 3 MINUTES)) + command_alert("Incoming emergency directive: Captain's office fax machine, [station_name()].","Emergency Transmission") + spawn(rand(3 MINUTES, 5 MINUTES)) + send_pda_message() + spawn(rand(3 MINUTES, 5 MINUTES)) + fluff.announce_directives() + spawn(rand(2 MINUTES, 3 MINUTE)) + + var/list/reasons = list( + "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" + ) + command_alert("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time.","Emergency Transmission") + + // Returns an array in case we want to expand on this later. + proc/get_head_loyalist_candidates() + var/list/candidates[0] + for(var/mob/loyalist in player_list) + if(loyalist.mind && loyalist.mind.assigned_role == "Captain") + candidates.Add(loyalist.mind) + return candidates + + proc/get_head_mutineer_candidates() + var/list/candidates[0] + for(var/mob/mutineer in player_list) + if(mutineer.client.prefs.be_special & BE_MUTINEER) + for(var/job in command_positions - "Captain") + if(mutineer.mind && mutineer.mind.assigned_role == job) + candidates.Add(mutineer.mind) + return candidates + + proc/get_directive_candidates() + var/list/candidates[0] + for(var/T in typesof(/datum/directive) - /datum/directive) + var/datum/directive/D = new T(src) + if (D.meets_prerequisites()) + candidates.Add(D) + return candidates + + proc/send_pda_message() + var/obj/item/device/pda/pda = null + for(var/obj/item/device/pda/P in head_mutineer.current) + pda = P + break + + if (!pda) + return 0 + + if (!pda.silent) + playsound(pda.loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(3, pda.loc)) + O.show_message(text("\icon[pda] *[pda.ttone]*")) + + head_mutineer.current << fluff.get_pda_body() + return 1 + + proc/get_equipment_slots() + return list( + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "backpack" = slot_in_backpack, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand) + + proc/equip_head_loyalist() + equip_head(head_loyalist, "loyalist", /mob/living/carbon/human/proc/recruit_loyalist) + + proc/equip_head_mutineer() + equip_head(head_mutineer, "mutineer", /mob/living/carbon/human/proc/recruit_mutineer) + + proc/equip_head(datum/mind/head, faction, proc/recruitment_verb) + var/mob/living/carbon/human/H = head.current + H << "You are the Head [capitalize(faction)]!" + head.special_role = "head_[faction]" + + var/slots = get_equipment_slots() + switch(faction) + if("loyalist") + if(captains_key) del(captains_key) + captains_key = new(H) + H.equip_in_one_of_slots(captains_key, slots) + if("mutineer") + if(secondary_key) del(secondary_key) + secondary_key = new(H) + H.equip_in_one_of_slots(secondary_key, slots) + + H.update_icons() + H.verbs += recruitment_verb + + proc/add_loyalist(datum/mind/M) + add_faction(M, "loyalist", loyalists) + + proc/add_mutineer(datum/mind/M) + add_faction(M, "mutineer", mutineers) + + proc/add_faction(datum/mind/M, faction, list/faction_list) + if(!can_be_recruited(M, faction)) + M.current << "\red Recruitment canceled; your role has already changed." + head_mutineer.current << "\red Could not recruit [M]. Their role has changed." + return + + if(M in loyalists) + loyalists.Remove(M) + + if(M in mutineers) + mutineers.Remove(M) + + M.special_role = faction + faction_list.Add(M) + + if(faction == "mutineer") + M.current << fluff.mutineer_tag("You have joined the mutineers!") + head_mutineer.current << fluff.mutineer_tag("[M] has joined the mutineers!") + else + M.current << fluff.loyalist_tag("You have joined the loyalists!") + head_loyalist.current << fluff.loyalist_tag("[M] has joined the loyalists!") + + update_icon(M) + + proc/was_bloodbath() + var/list/remaining_loyalists = loyalists - body_count + if (!remaining_loyalists.len) + return 1 + + var/list/remaining_mutineers = mutineers - body_count + if (!remaining_mutineers.len) + return 1 + + return 0 + + proc/replace_nuke_with_ead() + for(var/obj/machinery/nuclearbomb/N in world) + ead = new(N.loc, src) + del(N) + + proc/unbolt_vault_door() + var/obj/machinery/door/airlock/vault = locate(/obj/machinery/door/airlock/vault) + vault.lock() + + proc/make_secret_transcript() + var/obj/machinery/computer/telecomms/server/S = locate(/obj/machinery/computer/telecomms/server) + if(!S) return + + var/obj/item/weapon/paper/crumpled/bloody/transcript = new(S.loc) + transcript.name = "secret transcript" + transcript.info = fluff.secret_transcript() + + proc/can_be_recruited(datum/mind/M, role) + if(!M) return 0 + if(!M.special_role) return 1 + switch(role) + if("loyalist") + return M.special_role == "mutineer" + if("mutineer") + return M.special_role == "loyalist" + + proc/round_outcome() + world << "

    Breaking News



    " + if (was_bloodbath()) + world << fluff.no_victory() + return + + var/directives_completed = current_directive.directives_complete() + var/ead_activated = ead.activated + if (directives_completed && ead_activated) + world << fluff.loyalist_major_victory() + else if (directives_completed && !ead_activated) + world << fluff.loyalist_minor_victory() + else if (!directives_completed && ead_activated) + world << fluff.mutineer_minor_victory() + else if (!directives_completed && !ead_activated) + world << fluff.mutineer_major_victory() + + world << sound('sound/machines/twobeep.ogg') + + proc/update_all_icons() + spawn(0) + for(var/datum/mind/M in mutineers) + update_icon(M) + + for(var/datum/mind/M in loyalists) + update_icon(M) + return 1 + + proc/update_icon(datum/mind/M) + if(!M.current || !M.current.client) + return 0 + + for(var/image/I in head_loyalist.current.client.images) + if(I.loc == M.current && (I.icon_state == "loyalist" || I.icon_state == "mutineer")) + del(I) + + for(var/image/I in head_mutineer.current.client.images) + if(I.loc == M.current && (I.icon_state == "loyalist" || I.icon_state == "mutineer")) + del(I) + + if(M in loyalists) + var/I = image('icons/mob/mob.dmi', loc=M.current, icon_state = "loyalist") + head_loyalist.current.client.images += I + + if(M in mutineers) + var/I = image('icons/mob/mob.dmi', loc=M.current, icon_state = "mutineer") + head_mutineer.current.client.images += I + + return 1 + +/datum/game_mode/mutiny/announce() + fluff.announce() + +/datum/game_mode/mutiny/pre_setup() + var/list/loyalist_candidates = get_head_loyalist_candidates() + if(!loyalist_candidates || loyalist_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for head loyalist." + return 0 + + var/list/mutineer_candidates = get_head_mutineer_candidates() + if(!mutineer_candidates || mutineer_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for head mutineer." + return 0 + + var/list/directive_candidates = get_directive_candidates() + if(!directive_candidates || directive_candidates.len == 0) + world << "\red Mutiny mode aborted: no valid candidates for Directive X." + return 0 + + head_loyalist = pick(loyalist_candidates) + head_mutineer = pick(mutineer_candidates) + current_directive = pick(directive_candidates) + + return 1 + +/datum/game_mode/mutiny/post_setup() + equip_head_loyalist() + equip_head_mutineer() + + loyalists.Add(head_loyalist) + mutineers.Add(head_mutineer) + + replace_nuke_with_ead() + current_directive.initialize() + unbolt_vault_door() + make_secret_transcript() + + update_all_icons() + spawn(0) + reveal_directives() + ..() + +/mob/living/carbon/human/proc/recruit_loyalist() + set name = "Recruit Loyalist" + set category = "Mutiny" + + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode || src != mode.head_loyalist.current) + return + + var/list/candidates = list() + for (var/mob/living/carbon/human/P in oview(src)) + if(!stat && P.client && mode.can_be_recruited(P.mind, "loyalist")) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + if(world.time < mode.recruit_loyalist_cooldown) + src << "\red Wait [MUTINY_RECRUITMENT_COOLDOWN] seconds before recruiting again." + return + + mode.recruit_loyalist_cooldown = world.time + (MUTINY_RECRUITMENT_COOLDOWN SECONDS) + + var/mob/living/carbon/human/M = input("Select a person to recruit", "Loyalist recruitment", null) as mob in candidates + + if (M) + src << "Attempting to recruit [M]..." + log_admin("[src]([src.ckey]) attempted to recruit [M] as a loyalist.") + message_admins("\red [src]([src.ckey]) attempted to recruit [M] as a loyalist.") + + var/choice = alert(M, "Asked by [src]: Will you help me complete Directive X?", "Loyalist recruitment", "No", "Yes") + if(choice == "Yes") + mode.add_loyalist(M.mind) + else if(choice == "No") + M << "\red You declined to join the loyalists." + mode.head_loyalist.current << "\red [M] declined to support the loyalists." + +/mob/living/carbon/human/proc/recruit_mutineer() + set name = "Recruit Mutineer" + set category = "Mutiny" + + var/datum/game_mode/mutiny/mode = get_mutiny_mode() + if (!mode || src != mode.head_mutineer.current) + return + + var/list/candidates = list() + for (var/mob/living/carbon/human/P in oview(src)) + if(!stat && P.client && mode.can_be_recruited(P.mind, "mutineer")) + candidates += P + + if(!candidates.len) + src << "\red You aren't close enough to anybody that can be recruited." + return + + if(world.time < mode.recruit_mutineer_cooldown) + src << "\red Wait [MUTINY_RECRUITMENT_COOLDOWN] seconds before recruiting again." + return + + mode.recruit_mutineer_cooldown = world.time + (MUTINY_RECRUITMENT_COOLDOWN SECONDS) + + var/mob/living/carbon/human/M = input("Select a person to recruit", "Mutineer recruitment", null) as mob in candidates + + if (M) + src << "Attempting to recruit [M]..." + log_admin("[src]([src.ckey]) attempted to recruit [M] as a mutineer.") + message_admins("\red [src]([src.ckey]) attempted to recruit [M] as a mutineer.") + + var/choice = alert(M, "Asked by [src]: Will you help me stop Directive X?", "Mutineer recruitment", "No", "Yes") + if(choice == "Yes") + mode.add_mutineer(M.mind) + else if(choice == "No") + M << "\red You declined to join the mutineers." + mode.head_mutineer.current << "\red [M] declined to support the mutineers." + +/proc/get_mutiny_mode() + if(!ticker || !istype(ticker.mode, /datum/game_mode/mutiny)) + return null + + return ticker.mode diff --git a/code/game/gamemodes/mutiny/mutiny_admin.dm b/code/game/gamemodes/mutiny/mutiny_admin.dm new file mode 100644 index 00000000..2ba08def --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny_admin.dm @@ -0,0 +1,83 @@ +/datum/game_mode/mutiny/proc/check_antagonists_ui(admins) + var/turf/captains_key_loc = captains_key ? captains_key.get_loc_turf() : "Lost or Destroyed" + var/turf/secondary_key_loc = secondary_key ? secondary_key.get_loc_turf() : "Lost or Destroyed" + var/remaining_objectives = current_directive.get_remaining_orders() + var/txt = {" +
    Context:
    +

    + [current_directive.get_description()] +

    +
    Orders:
    +
      + [fluff.get_orders()] +
    +
    +
    Remaining Objectives
    +
      + [remaining_objectives ? remaining_objectives : "
    1. None
    2. "] +
    +
    +
    Authentication:
    + Captain's Key: [captains_key_loc] + Activate
    + Secondary Key: [secondary_key_loc] + Activate
    + EAD: [ead ? ead.get_status() : "Lost or Destroyed"] + Activate
    +
    + "} + + txt += "Reassign Head Loyalist
    " + 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) + + txt += "Reassign Head Mutineer
    " + 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/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 + if("reassign_head_loyalist") + var/mob/M = get_reassignment_candidate("Loyalist") + if(M) + head_loyalist = M.mind + equip_head_loyalist() + return 1 + if("reassign_head_mutineer") + var/mob/M = get_reassignment_candidate("Mutineer") + if(M) + head_mutineer = M.mind + equip_head_mutineer() + return 1 + else + return 0 + +/datum/game_mode/mutiny/proc/get_reassignment_candidate(faction) + var/list/targets[0] + for(var/mob/living/carbon/human/H in player_list) + if(H.is_ready() && !H.is_dead()) + targets.Add(H) + + return input("Select a player to lead the [faction] faction.", "Head [faction] reassignment", null) as mob in targets diff --git a/code/game/gamemodes/mutiny/mutiny_fluff.dm b/code/game/gamemodes/mutiny/mutiny_fluff.dm new file mode 100644 index 00000000..f8634e8e --- /dev/null +++ b/code/game/gamemodes/mutiny/mutiny_fluff.dm @@ -0,0 +1,198 @@ +/datum/mutiny_fluff + var/datum/game_mode/mutiny/mode + + New(datum/game_mode/mutiny/M) + mode = M + + 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() + 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 [system_name()]. +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 terrorist action against the Company and, if found guilty by a Sol magistrate, 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 [system_name()]. +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 terrorist action against the Company and, if found guilty by a Sol magistrate, 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 [system_name()]. +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 terrorist activity, 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 [system_name()]. +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 company station in [system_name()]--[station_name()]--amid a local interstellar crisis. +What appears to have been a "legitimate" mutiny was spurred by a top secret directive sent to the station, presumably in response to the crisis within the system. +It has been revealed that the directive was invalid and fraudulent. Company officials have not released a statement about the source of the directive. +Thanks to the efforts of the resistant members of the crew, the directive was not carried out. +[mode.mutineers.len] members of the station's personnel were congratulated and awarded with the [mutineer_tag("Star of Bravery")], for their efforts in preventing the illegal directive's completion. +NanoTrasen has [mode.loyalists.len] members of the crew in holding, while it investigates the circumstances that led to the acceptance and initiation of an invalid directive. +[mode.body_count.len] are believed to have died during the coup. +

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

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

    Corporate Rival Threat Assessment

    +
    Gilthari Exports Incident Transcript
    +
    CONFIDENTIAL: PROPERTY OF NANOTRASEN
    +Location: Operator's Desk, D Deck, Polumetis Installation
    +Time: 16:11, May 24, 2558 (Sol Reckoning)
    +
    +
    + +
    \[Start of transcript\]
    +
    \[Sound of an internal airlock door opening\]
    +TM: Thank you for coming to see me, Director. I'm afraid this is urgent.
    +D: Mr. Mitchell, first you send cryptic messages to my office and then you request to have me come personally to this barely lit closet you call a workstation; all of this to talk about a computer glitch?
    +
    \[Sound of the internal airlock door shutting\]
    +TM: Do you remember Mallory?
    +D: Who?
    +TM: It's not who, it's what. The computer program we planted in the [system_name()] communications satellite.
    +D: What is so important about this computer program?
    +TM: We call her an eavesdropper. Captures network traffic, records it, and forwards the stream to the receiver autonomously.
    +D: Speak English goddamnit.
    +TM: Standard intelligence acquisition package, sir; we bug their satellite and listen. It's like we have their playbook and we know what their moves are going to be on the market before they make them.
    +D: So Mallory doesn't work?
    +TM: She worked, sir. We've had an ear on NanoTrasen's regional communications for weeks.
    +D: Any news about their Phoron refinement process?
    +TM: No sir. Our analysts believe they are using a separate channel for their most sensitive data.
    +D: So what's the problem?
    +TM: The intelligence hasn't been doing us any good. Anything that appears actionable, I send it to the analysts and they make a plan. Thing is, NanoTrasen always sees us coming.
    +D: Tim...
    +TM: I think they discovered the hack, sir. Case in point, Energine Consolidated Solutions. That subsidiary of ours that was awarded a lease on NanoTrasen's mining platform in Nyx? NanoTrasen acquired them a week before we made the announcement.
    +D: They know about they have a bug. They left her on and fed her the information for us to hear, those sneaks. How did they find it?
    +TM: Top secret communique came through. I'm not sure what happened. Either Mallory couldn't replicate the encryption scheme and garbled it going out or the transmission was already corrupted to begin with.
    +D: Either way the transmission caused NanoTrasen to look at the satellite. They found out about Mallory.
    +TM: Precisely sir. There's only so much I can do to cover our tracks from here.
    +D: I'm pulling the plug. We have assets in the sector that are capable of a job like this. Thank you for bringing this to my attention.
    +
    \[Computer device chirps\]
    +D: One last thing, did you happen to read anything from those secure transmissions?
    +TM: Just the subject, 'Directive X'.
    +D: Directive X... Now what do you suppose that means?
    +
    \[End of transcript\]
    + "} diff --git a/code/game/gamemodes/mutiny/mutiny_hooks.dm b/code/game/gamemodes/mutiny/mutiny_hooks.dm new file mode 100644 index 00000000..9e64bd2e --- /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/alien/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/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index f3e9096a..f06e70c9 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -553,7 +553,44 @@ dat += "Traitor not found!" dat += "" + 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 \ No newline at end of file diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index f0fbde2b..aac3e930 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 fdf4590b..bc9742c3 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -17,7 +17,8 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set "ninja" = "true", // 10 "vox raider" = IS_MODE_COMPILED("heist"), // 11 "diona" = 1, // 12 - "vampire" = IS_MODE_COMPILED("vampire") // 13 + "vampire" = IS_MODE_COMPILED("vampire"), // 13 + "mutineer" = IS_MODE_COMPILED("mutiny") // 14 ) var/const/MAX_SAVE_SLOTS = 20 diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index 70bf8ee1..19ea76ed 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", var/datum/nanoui/ui = null, var/force_open = 1) + 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, force_open) + 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/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index e9accbc7..0ce5fd0d 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1857,6 +1857,14 @@ holder.icon_state = "hudninja" if("Vampire") holder.icon_state = "hudvampire" + if("head_loyalist") + holder.icon_state = "loyalist" + if("loyalist") + holder.icon_state = "loyalist" + if("head_mutineer") + holder.icon_state = "mutineer" + if("mutineer") + holder.icon_state = "mutineer" hud_list[SPECIALROLE_HUD] = holder hud_updateflag = 0 diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 10c0746d..b28f4f9c 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -17,6 +17,11 @@ 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 diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 29300f15..d2e158a8 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -152,9 +152,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 7f484eed..a0d53434 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -668,6 +668,7 @@ var/list/liftable_structures = list(\ #define BE_RAIDER 2048 #define BE_PLANT 4096 #define BE_VAMPIRE 8192 +#define BE_MUTINEER 16384 var/list/be_special_flags = list( "Traitor" = BE_TRAITOR, @@ -683,7 +684,8 @@ var/list/be_special_flags = list( "Ninja" = BE_NINJA, "Raider" = BE_RAIDER, "Diona" = BE_PLANT, - "Vampire" = BE_VAMPIRE + "Vampire" = BE_VAMPIRE, + "Mutineer" = BE_MUTINEER ) #define AGE_MIN 17 //youngest a character can be diff --git a/nano/templates/accounts_terminal.tmpl b/nano/templates/accounts_terminal.tmpl new file mode 100644 index 00000000..9903878e --- /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}} From b06a3c626bce36624881c1203e513a121ba6794f Mon Sep 17 00:00:00 2001 From: Zuhayr Date: Fri, 9 Jan 2015 10:29:15 +1030 Subject: [PATCH 05/53] Redirects stamp attackby() on the clipboard to the topmost paper. --- code/modules/paperwork/clipboard.dm | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index ca128c2c..6e35ced7 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -44,8 +44,17 @@ return /obj/item/weapon/clipboard/attackby(obj/item/weapon/W as obj, mob/user as mob) - - if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/photo)) + + if(istype(W,/obj/item/weapon/stamp)) + if(!toppaper || !user.loc) + return + toppaper.loc = user.loc + toppaper.attackby(W,user) + if(toppaper) // Better safe than runtimes. + toppaper.loc = src + return + + else if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/photo)) user.drop_item() W.loc = src if(istype(W, /obj/item/weapon/paper)) @@ -107,20 +116,20 @@ else if(href_list["write"]) var/obj/item/weapon/P = locate(href_list["write"]) - + if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) && (P == toppaper) ) - + var/obj/item/I = usr.get_active_hand() - + if(istype(I, /obj/item/weapon/pen)) - + P.attackby(I, usr) else if(href_list["remove"]) var/obj/item/P = locate(href_list["remove"]) - + if(P && (P.loc == src) && (istype(P, /obj/item/weapon/paper) || istype(P, /obj/item/weapon/photo)) ) - + P.loc = usr.loc usr.put_in_hands(P) if(P == toppaper) @@ -133,9 +142,9 @@ else if(href_list["read"]) var/obj/item/weapon/paper/P = locate(href_list["read"]) - + if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) ) - + if(!(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/dead/observer) || istype(usr, /mob/living/silicon))) usr << browse("[P.name][stars(P.info)][P.stamps]", "window=[P.name]") onclose(usr, "[P.name]") From b56767639745fcbea9a6aa06c22e595be4fc6706 Mon Sep 17 00:00:00 2001 From: Zuhayr Date: Fri, 9 Jan 2015 10:59:21 +1030 Subject: [PATCH 06/53] Finishes porting binary headsets (with accompanying radio code refactor). Will probably need more testing than I managed locally. --- .../objects/items/devices/radio/headset.dm | 12 ++++++ .../game/objects/items/devices/radio/radio.dm | 42 ++++++++++--------- code/modules/mob/living/carbon/human/say.dm | 2 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 1ab358e5..db7472de 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -19,6 +19,18 @@ keyslot1 = new /obj/item/device/encryptionkey/ recalculateChannels() +/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, message, channel) + if (channel == "special") + if (translate_binary) + var/datum/language/binary = all_languages["Robot Talk"] + binary.broadcast(M, message) + if (translate_hive) + var/datum/language/hivemind = all_languages["Hivemind"] + hivemind.broadcast(M, message) + return null + + return ..() + /obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0) if (aiOverride) return ..(freq, level) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index cf63ddbc..f9fa55ba 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -54,6 +54,22 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(radio_controller) initialize() +// Interprets the message mode when talking into a radio, possibly returning a connection datum +/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode) + // If a channel isn't specified, send to common. + if(!message_mode || message_mode == "headset") + return radio_connection + + // Otherwise, if a channel is specified, look for it. + if(channels) + if (message_mode == "department") // Department radio shortcut + message_mode = channels[1] + + if (channels[message_mode]) // only broadcast if the channel is set on + return secure_radio_connections[message_mode] + + // If we were to send to a channel we don't have, drop it. + return null /obj/item/device/radio/initialize() @@ -229,6 +245,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(!(src.wires & WIRE_TRANSMIT)) // The device has to have all its wires and shit intact return + M.last_target_click = world.time if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl @@ -244,26 +261,12 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use */ //#### Grab the connection datum ####// - var/datum/radio_frequency/connection = null - if(channel == "headset") - channel = null - if(channel) // If a channel is specified, look for it. - if(channels && channels.len > 0) - if (channel == "department") - //world << "DEBUG: channel=\"[channel]\" switching to \"[channels[1]]\"" - channel = channels[1] - connection = secure_radio_connections[channel] - if (!channels[channel]) // if the channel is turned off, don't broadcast - return - else - // If we were to send to a channel we don't have, drop it. - else // If a channel isn't specified, send to common. - connection = radio_connection - channel = null + + var/datum/radio_frequency/connection = handle_message_mode(M, message, channel) if (!istype(connection)) - return + return 0 if (!connection) - return + return 0 var/turf/position = get_turf(src) @@ -279,7 +282,6 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/jobname // the mob's "job" - // --- Human: use their actual job --- if (ishuman(M)) jobname = M:get_assignment() @@ -368,7 +370,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use return - /* ###### Intercoms and station-bounced radios ###### */ + /* ###### Intercoms and station-bounced radios ###### */ var/filter_type = 2 diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 83f483dc..b679a874 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -137,7 +137,7 @@ else if(!(stunned >= 4)) if(message_mode) - if(message_mode in (radiochannels | "department")) + if(message_mode in (radiochannels | "department" | "special")) if(l_ear && istype(l_ear,/obj/item/device/radio)) l_ear.talk_into(src,message, message_mode, verb, speaking) used_radios += l_ear From 3bc7e83a9a1720417321e96a2b377f37efb0fe49 Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Fri, 9 Jan 2015 07:24:31 +0000 Subject: [PATCH 07/53] updates from others --- html/changelog.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html/changelog.html b/html/changelog.html index 8c6c8bc3..17c07b5d 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,9 +57,11 @@ should be listed in the changelog upon commit though. Thanks. -->
    -

    8 January 2015

    +

    9 January 2015

    SoundScopes updated:

      +
    • Mutany Gamemode
    • +
    • Stamps work on clipboards
    • Changelings start with their language again
    From 73abde1c3ff281d3642721456ada3c96d620bba9 Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Fri, 9 Jan 2015 17:12:02 +0000 Subject: [PATCH 08/53] Space language whisper logs --- code/modules/mob/hear_say.dm | 3 ++- code/modules/mob/living/carbon/human/whisper.dm | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 3f8899ac..2ef4e08f 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -41,7 +41,8 @@ var/track = null if(istype(src, /mob/dead/observer)) if(italics && client.prefs.toggles & CHAT_GHOSTRADIO) - return + if(!verb == ("whispers" || " quietly" || " softly")) + return if(speaker_name != speaker.real_name && speaker.real_name) speaker_name = "[speaker.real_name] ([speaker_name])" track = "(follow) " diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 06c84496..6ecfd8f7 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -6,7 +6,7 @@ usr << "\red Speech is currently admin-disabled." return - log_whisper("[src.name]/[src.key] : [message]") +// log_whisper("[src.name]/[src.key] : [message]") if (src.client) if (src.client.prefs.muted & MUTE_IC) @@ -99,7 +99,7 @@ if (!(M.client)) continue if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) - listening |= M //I want to see if this does anything for ghosts +// listening |= M //I want to see if this does anything for ghosts M.hear_say(message, verb, speaking, alt_name, italics, src) //Pass whispers on to anything inside the immediate listeners. From 43195501a095a2a70cff62ffc1fec78b96b91721 Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Fri, 9 Jan 2015 17:16:49 +0000 Subject: [PATCH 09/53] I commented in my code again --- code/modules/mob/living/carbon/human/human.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 49f0c755..a18e79a0 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -39,10 +39,12 @@ /mob/living/carbon/human/machine/New(var/new_loc) h_style = "blue IPC screen" ..(new_loc, "Machine") +*/ /mob/living/carbon/human/machine/isipc() return 1 +/* /mob/living/carbon/human/kocasslani/New(var/new_loc) h_style = "Bald" ..(new_loc, "Kocasslani") From ea25c28683a7f9190f7294273e1ef93d26952545 Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Sat, 10 Jan 2015 06:59:29 +0000 Subject: [PATCH 10/53] You just can't sting IPC's --- code/game/gamemodes/changeling/changeling_powers.dm | 2 ++ html/changelog.html | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 5ef85a84..e3f6e0c3 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -694,6 +694,8 @@ var/list/datum/dna/hivemind_bank = list() var/list/victims = list() for(var/mob/living/carbon/C in oview(changeling.sting_range)) + if(C.isipc()) + continue victims += C var/mob/living/carbon/T = input(src, "Who will we sting?") as null|anything in victims diff --git a/html/changelog.html b/html/changelog.html index 17c07b5d..33fc08dc 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -57,10 +57,11 @@ should be listed in the changelog upon commit though. Thanks. -->
    -

    9 January 2015

    +

    10 January 2015

    SoundScopes updated:

    • Mutany Gamemode
    • +
    • IPC's are now unaffected by changeling stings
    • Stamps work on clipboards
    • Changelings start with their language again
    From f159256d1683f91bf9c5b0fafc62ef18f580cadc Mon Sep 17 00:00:00 2001 From: SoundScopes Date: Sat, 10 Jan 2015 07:00:13 +0000 Subject: [PATCH 11/53] Playing Cards --- code/modules/client/preferences_gear.dm | 5 +++++ html/changelog.html | 1 + 2 files changed, 6 insertions(+) diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm index 4d5d5da6..2461e068 100644 --- a/code/modules/client/preferences_gear.dm +++ b/code/modules/client/preferences_gear.dm @@ -91,6 +91,11 @@ proc/populate_gear_list() path = /obj/item/weapon/reagent_containers/food/drinks/flask/barflask cost = 2 +/datum/gear/cards + display_name = "deck of cards" + path = /obj/item/weapon/deck + cost = 1 + /datum/gear/tie_horrible display_name = "horrible tie" path = /obj/item/clothing/tie/horrible diff --git a/html/changelog.html b/html/changelog.html index 33fc08dc..48f71716 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -60,6 +60,7 @@ should be listed in the changelog upon commit though. Thanks. -->

    10 January 2015

    SoundScopes updated: