From 764b998b1d5df5fce7df0d2ecd1b1758445a8b3e Mon Sep 17 00:00:00 2001 From: carlarctg <53100513+carlarctg@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:28:36 -0300 Subject: [PATCH] Adds a Syndicate Monkey Agent beacon uplink item (#79012) ## About The Pull Request Adds a Syndicate Monkey Agent beacon uplink item. It spawns a dapper monkey that must follow your orders. Added a monkey gun case to the uplink, which contains monkey guns! Though they aren't very powerful. Added a more modularlike subtype for antagonist spawners to reduce future hardcoding. Gave the syndicate turtleneck a monkey sprite, from SS14! ## Why It's Good For The Game I want to see the clown driving security insane with 2-3 monkeys and an incredible amount of pranking. Or an assistant killing everyone with his monkey friends while wearing a monkey suit. Or a geneticist sending out mutated monkeys to kill people. Or a scientist equipping his monkeys with bombs or xenobiology equipment and sending them out to wreak havoc. 6 TC is only enough for two monkeys, but you can get a third if you finish any kind of objective. > Added a monkey gun case to the uplink, which contains monkey guns! Though they aren't very powerful. We can't have the monkey mafia without guns, come on. The guns are weak and only usable by monkeys. Additionally, they're restricted to entertainment only. Credit to SS14 for the monky turtleneck sprite. ## Changelog :cl: add: Adds a Syndicate Monkey Agent beacon uplink item. It spawns a dapper monkey that must follow your orders. add: Added a monkey gun case to the uplink, which contains monkey guns! Though they aren't very powerful. refactor: Added a more modularlike subtype for antagonist spawners to reduce future hardcoding. sprite: Gave the syndicate turtleneck a monkey sprite, from SS14! /:cl: --------- Co-authored-by: ATH1909 <42606352+ATH1909@users.noreply.github.com> Co-authored-by: Jacquerel --- code/__DEFINES/is_helpers.dm | 4 + code/__DEFINES/role_preferences.dm | 1 + code/__DEFINES/traits.dm | 2 + code/_globalvars/lists/names.dm | 1 + code/datums/dna.dm | 2 + code/game/objects/items/boxcutter.dm | 6 + code/game/objects/items/storage/toolbox.dm | 62 ++++++++ .../antagonists/_common/antag_spawner.dm | 135 ++++++++++++++++++ .../syndicate_monkey/syndicate_monkey.dm | 37 +++++ code/modules/clothing/under/syndicate.dm | 8 ++ code/modules/hydroponics/grown/banana.dm | 15 ++ .../carbon/human/species_types/monkeys.dm | 2 - .../projectiles/guns/ballistic/automatic.dm | 28 +++- .../projectiles/guns/ballistic/pistol.dm | 12 ++ code/modules/projectiles/pins.dm | 11 ++ .../reagents/drinks/alcohol_reagents.dm | 2 +- .../reagents/drinks/drink_reagents.dm | 4 +- code/modules/uplink/uplink_items/clownops.dm | 20 +++ code/modules/uplink/uplink_items/job.dm | 22 +++ icons/mob/human/species/monkey/uniform.dmi | Bin 546 -> 2222 bytes strings/names/syndicate_monkey.txt | 19 +++ tgstation.dme | 1 + 22 files changed, 388 insertions(+), 6 deletions(-) create mode 100644 code/modules/antagonists/syndicate_monkey/syndicate_monkey.dm create mode 100644 strings/names/syndicate_monkey.txt diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index eebcf24d8e3..7f408a6805e 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -147,6 +147,10 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define iscow(A) (istype(A, /mob/living/basic/cow)) +#define isgorilla(A) (istype(A, /mob/living/basic/gorilla)) + +#define is_simian(A) (isgorilla(A) || ismonkey(A)) + /// returns whether or not the atom is either a basic mob OR simple animal #define isanimal_or_basicmob(A) (istype(A, /mob/living/simple_animal) || istype(A, /mob/living/basic)) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 6f5b97e7ab8..e16493d4d9d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -76,6 +76,7 @@ #define ROLE_SERVANT_GOLEM "Servant Golem" #define ROLE_SLAUGHTER_DEMON "Slaughter Demon" #define ROLE_WIZARD_APPRENTICE "apprentice" +#define ROLE_SYNDICATE_MONKEY "Syndicate Monkey Agent" //Spawner roles #define ROLE_ANCIENT_CREW "Ancient Crew" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 84d351b7c6e..9073fd0e068 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -282,6 +282,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NO_ZOMBIFY "no_zombify" /// Carbons with this trait can't have their DNA copied by diseases nor changelings #define TRAIT_NO_DNA_COPY "no_dna_copy" +/// Carbons with this trait cant have their dna scrambled by genetics or a disease retrovirus. +#define TRAIT_NO_DNA_SCRAMBLE "no_dna_scramble" /// Carbons with this trait can eat blood to regenerate their own blood volume, instead of injecting it #define TRAIT_DRINKS_BLOOD "drinks_blood" /// Mob is immune to clone (cellular) damage diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index 01e6f3b59dc..b2662b5e804 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -24,6 +24,7 @@ GLOBAL_LIST_INIT(nightmare_names, world.file2list("strings/names/nightmare.txt") GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.txt")) GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt")) GLOBAL_LIST_INIT(cyberauth_names, world.file2list("strings/names/cyberauth.txt")) +GLOBAL_LIST_INIT(syndicate_monkey_names, world.file2list("strings/names/syndicate_monkey.txt")) GLOBAL_LIST_INIT(verbs, world.file2list("strings/names/verbs.txt")) GLOBAL_LIST_INIT(ing_verbs, world.file2list("strings/names/ing_verbs.txt")) diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 0d77aa6b524..756c0e715ae 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -804,6 +804,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) /proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, uf=FALSE, probability) if(!M.has_dna()) CRASH("[M] does not have DNA") + if(HAS_TRAIT(M, TRAIT_NO_DNA_SCRAMBLE)) + return if(se) for(var/i=1, i <= DNA_MUTATION_BLOCKS, i++) if(prob(probability)) diff --git a/code/game/objects/items/boxcutter.dm b/code/game/objects/items/boxcutter.dm index 62273c1080f..467bc666e60 100644 --- a/code/game/objects/items/boxcutter.dm +++ b/code/game/objects/items/boxcutter.dm @@ -17,6 +17,8 @@ var/snap_time_weak_handcuffs = 0 SECONDS /// Used on Initialize, how much time to cut real handcuffs. Null means it can't. var/snap_time_strong_handcuffs = null + /// Starts open if true + var/start_extended = FALSE /obj/item/boxcutter/get_all_tool_behaviours() return list(TOOL_KNIFE) @@ -31,6 +33,7 @@ AddComponent( \ /datum/component/transforming, \ + start_transformed = start_extended, \ force_on = 10, \ throwforce_on = 4, \ throw_speed_on = throw_speed, \ @@ -53,3 +56,6 @@ else RemoveElement(/datum/element/cuffsnapping, snap_time_weak_handcuffs, snap_time_strong_handcuffs) return COMPONENT_NO_DEFAULT_MESSAGE + +/obj/item/boxcutter/extended + start_extended = TRUE diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index fd5eded2da3..f1b8c761c24 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -447,3 +447,65 @@ desc = "A gun case. Has the symbol of the Third Soviet Union stamped on the side." weapon_to_spawn = /obj/item/gun/ballistic/automatic/plastikov extra_to_spawn = /obj/item/food/rationpack //sorry comrade, cannot get you more ammo, here, have lunch + +/obj/item/storage/toolbox/guncase/monkeycase + name = "monkey gun case" + desc = "Everything a monkey needs to truly go ape-shit. There's a paw-shaped hand scanner lock on the front of the case." + +/obj/item/storage/toolbox/guncase/monkeycase/Initialize(mapload) + . = ..() + atom_storage.locked = STORAGE_SOFT_LOCKED + +/obj/item/storage/toolbox/guncase/monkeycase/attack_self(mob/user, modifiers) + if(!monkey_check(user)) + return + return ..() + +/obj/item/storage/toolbox/guncase/monkeycase/attack_self_secondary(mob/user, modifiers) + attack_self(user, modifiers) + return + +/obj/item/storage/toolbox/guncase/monkeycase/attack_hand(mob/user, list/modifiers) + if(!monkey_check(user)) + return + return ..() + +/obj/item/storage/toolbox/guncase/monkeycase/proc/monkey_check(mob/user) + if(atom_storage.locked == STORAGE_NOT_LOCKED) + return TRUE + + if(is_simian(user)) + atom_storage.locked = STORAGE_NOT_LOCKED + to_chat(user, span_notice("You place your paw on the paw scanner, and hear a soft click as [src] unlocks!")) + playsound(src, 'sound/items/click.ogg', 25, TRUE) + return TRUE + to_chat(user, span_warning("You put your hand on the hand scanner, and it rejects it with an angry chimpanzee screech!")) + playsound(src, "sound/creatures/monkey/monkey_screech_[rand(1,7)].ogg", 75, TRUE) + return FALSE + +/obj/item/storage/toolbox/guncase/monkeycase/PopulateContents() + switch(rand(1, 3)) + if(1) + // Uzi with a boxcutter. + new /obj/item/gun/ballistic/automatic/mini_uzi/chimpgun(src) + new /obj/item/ammo_box/magazine/uzim9mm(src) + new /obj/item/ammo_box/magazine/uzim9mm(src) + new /obj/item/boxcutter/extended(src) + if(2) + // Thompson with a boxcutter. + new /obj/item/gun/ballistic/automatic/tommygun/chimpgun(src) + new /obj/item/ammo_box/magazine/tommygunm45(src) + new /obj/item/ammo_box/magazine/tommygunm45(src) + new /obj/item/boxcutter/extended(src) + if(3) + // M1911 with a switchblade and an extra banana bomb. + new /obj/item/gun/ballistic/automatic/pistol/m1911/chimpgun(src) + new /obj/item/ammo_box/magazine/m45(src) + new /obj/item/ammo_box/magazine/m45(src) + new /obj/item/switchblade/extended(src) + new /obj/item/food/grown/banana/bunch/monkeybomb(src) + + // Banana bomb! Basically a tiny flashbang for monkeys. + new /obj/item/food/grown/banana/bunch/monkeybomb(src) + // Somewhere to store it all. + new /obj/item/storage/backpack/messenger(src) diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 47229ddd462..d036d424705 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -283,3 +283,138 @@ veil_msg = span_warning("You sense an adorable presence lurking just beyond the veil...") demon_type = /mob/living/basic/demon/slaughter/laughter + +/** + * A subtype meant for 'normal' antag spawner items so as to reduce the amount of required hardcoding. + */ + +/obj/item/antag_spawner/loadout + name = "generic beacon" + desc = "A single-use beacon designed to quickly launch bad code into the field." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + /// The mob type to spawn. + var/mob/living/spawn_type = /mob/living/carbon/human + /// The species type to set a human spawn to. + var/species_type = /datum/species/human + /// The applied outfit. Won't work with nonhuman spawn types. + var/datum/outfit/outfit + /// The antag datum applied + var/datum/antagonist/antag_datum + /// Style used by the droppod + var/pod_style = STYLE_SYNDICATE + /// Do we use a random subtype of the outfit? + var/use_subtypes = TRUE + /// The antag role we check if the ghosts have enabled to get the poll. + var/poll_role_check = ROLE_TRAITOR + /// The mind's special role. + var/role_to_play = ROLE_SYNDICATE_MONKEY + /// What category to ignore the poll + var/poll_ignore_category = POLL_IGNORE_SYNDICATE + /// text given when device fails to secure candidates + var/fail_text = "Unable to connect to Syndicate command. Please wait and try again later or use the beacon on your uplink to get your points refunded." + +/obj/item/antag_spawner/loadout/proc/check_usability(mob/user) + if(used) + to_chat(user, span_warning("[src] is out of power!")) + return FALSE + return TRUE + +/// Creates the drop pod the spawned_mob will be dropped by +/obj/item/antag_spawner/loadout/proc/setup_pod() + var/obj/structure/closet/supplypod/pod = new(null, pod_style) + pod.explosionSize = list(0,0,0,0) + pod.bluespace = TRUE + return pod + +/obj/item/antag_spawner/loadout/attack_self(mob/user) + if(!(check_usability(user))) + return + + to_chat(user, span_notice("You activate [src] and wait for confirmation.")) + var/list/baddie_candidates = poll_ghost_candidates("Do you want to play as a [role_to_play]?", poll_role_check, poll_role_check, 10 SECONDS, poll_ignore_category) + if(!LAZYLEN(baddie_candidates)) + to_chat(user, span_warning(fail_text)) + return + if(QDELETED(src) || !check_usability(user)) + return + used = TRUE + var/mob/dead/observer/ghostie = pick(baddie_candidates) + spawn_antag(ghostie.client, get_turf(src), user) + do_sparks(4, TRUE, src) + qdel(src) + +// For subtypes to do special things to the summoned dude. +/obj/item/antag_spawner/loadout/proc/do_special_things(mob/living/carbon/human/spawned_mob, mob/user) + return + +/obj/item/antag_spawner/loadout/spawn_antag(client/our_client, turf/T, mob/user) + var/mob/living/spawned_mob = new spawn_type() + var/obj/structure/closet/supplypod/pod = setup_pod() + our_client.prefs.safe_transfer_prefs_to(spawned_mob, is_antag = TRUE) + spawned_mob.ckey = our_client.key + var/datum/mind/op_mind = spawned_mob.mind + if(length(GLOB.newplayer_start)) // needed as hud code doesn't render huds if the atom (in this case the spawned_mob) is in nullspace, so just move the spawned_mob somewhere safe + spawned_mob.forceMove(pick(GLOB.newplayer_start)) + else + spawned_mob.forceMove(locate(1,1,1)) + + antag_datum = new() + + if(ishuman(spawned_mob)) + var/mob/living/carbon/human/human_mob = spawned_mob + human_mob.set_species(species_type) + human_mob.equipOutfit(outfit) + + op_mind.special_role = role_to_play + + do_special_things(spawned_mob, user) + + spawned_mob.forceMove(pod) + new /obj/effect/pod_landingzone(get_turf(src), pod) + +/obj/item/antag_spawner/loadout/monkey_man + name = "monkey agent beacon" + desc = "A single-use beacon designed to launch a specially-trained simian agent to the field for emergency support." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + species_type = /datum/species/monkey + outfit = /datum/outfit/syndicate_monkey + antag_datum = /datum/antagonist/syndicate_monkey + use_subtypes = FALSE + poll_role_check = ROLE_TRAITOR + role_to_play = ROLE_SYNDICATE_MONKEY + poll_ignore_category = POLL_IGNORE_SYNDICATE + fail_text = "Unable to connect to the Syndicate Banana Department. Please wait and try again later or use the beacon on your uplink to get your points refunded." + +/obj/item/antag_spawner/loadout/monkey_man/do_special_things(mob/living/carbon/human/monkey_man, mob/user) + + monkey_man.fully_replace_character_name(monkey_man.real_name, pick(GLOB.syndicate_monkey_names)) + + monkey_man.dna.add_mutation(/datum/mutation/human/clever) + // Can't make them human or nonclever. At least not with the easy and boring way out. + for(var/datum/mutation/human/mutation as anything in monkey_man.dna.mutations) + mutation.mutadone_proof = TRUE + mutation.instability = 0 + + // Extra backup! + ADD_TRAIT(monkey_man, TRAIT_NO_DNA_SCRAMBLE, SPECIES_TRAIT) + // Anything else requires enough effort that they deserve it. + + monkey_man.mind.enslave_mind_to_creator(user) + + var/obj/item/implant/explosive/imp = new(src) + imp.implant(monkey_man, user) + +/datum/outfit/syndicate_monkey + name = "Syndicate Monkey Agent Kit" + + head = /obj/item/clothing/head/fedora + mask = /obj/item/clothing/mask/cigarette/syndicate + uniform = /obj/item/clothing/under/syndicate + l_pocket = /obj/item/reagent_containers/cup/soda_cans/monkey_energy + r_pocket = /obj/item/storage/fancy/cigarettes/cigpack_syndicate + internals_slot = NONE + belt = /obj/item/lighter/skull + r_hand = /obj/item/food/grown/banana + diff --git a/code/modules/antagonists/syndicate_monkey/syndicate_monkey.dm b/code/modules/antagonists/syndicate_monkey/syndicate_monkey.dm new file mode 100644 index 00000000000..eae5d558a7c --- /dev/null +++ b/code/modules/antagonists/syndicate_monkey/syndicate_monkey.dm @@ -0,0 +1,37 @@ +/datum/antagonist/syndicate_monkey + name = "\improper Syndicate Monkey" + antagpanel_category = ANTAG_GROUP_SYNDICATE + show_in_roundend = TRUE + show_in_antagpanel = TRUE + show_name_in_check_antagonists = TRUE + count_against_dynamic_roll_chance = FALSE + show_to_ghosts = TRUE + /// The antagonist's master, used for objective + var/mob/living/monky_master + +/datum/antagonist/syndicate_monkey/on_gain() + monky_master = owner.enslaved_to?.resolve() + if(monky_master) + forge_objectives(monky_master) + return ..() + +/datum/antagonist/syndicate_monkey/Destroy() + monky_master = null + return ..() + +/datum/antagonist/syndicate_monkey/greet() + . = ..() + owner.announce_objectives() + +/datum/objective/syndicate_monkey + var/mob/living/monky_master + +/datum/objective/syndicate_monkey/check_completion() + return monky_master.stat != DEAD + +/datum/antagonist/syndicate_monkey/forge_objectives(mob/monky_master) + var/datum/objective/syndicate_monkey/objective = new + objective.monky_master = monky_master + objective.explanation_text = "You are a badass monkey syndicate agent. Protect and obey all of your master [monky_master]'s orders!" + objective.owner = owner + objectives += objective diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 19d5d3b93b2..20a5cda0592 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -8,6 +8,7 @@ alt_covers_chest = TRUE icon = 'icons/obj/clothing/under/syndicate.dmi' worn_icon = 'icons/mob/clothing/under/syndicate.dmi' + supports_variations_flags = CLOTHING_MONKEY_VARIATION /datum/armor/clothing_under/syndicate melee = 10 @@ -32,6 +33,7 @@ armor_type = /datum/armor/clothing_under/syndicate_bloodred resistance_flags = FIRE_PROOF | ACID_PROOF can_adjust = FALSE + supports_variations_flags = NONE /datum/armor/clothing_under/syndicate_bloodred melee = 10 @@ -99,6 +101,7 @@ icon_state = "tactical_suit" inhand_icon_state = "bl_suit" can_adjust = FALSE + supports_variations_flags = NONE /obj/item/clothing/under/syndicate/camo name = "camouflage fatigues" @@ -106,12 +109,14 @@ icon_state = "camogreen" inhand_icon_state = "g_suit" can_adjust = FALSE + supports_variations_flags = NONE /obj/item/clothing/under/syndicate/soviet name = "Ratnik 5 tracksuit" desc = "Badly translated labels tell you to clean this in Vodka. Great for squatting in." icon_state = "trackpants" can_adjust = FALSE + supports_variations_flags = NONE armor_type = /datum/armor/clothing_under/syndicate_soviet resistance_flags = NONE @@ -123,12 +128,14 @@ desc = "With a suit lined with this many pockets, you are ready to operate." icon_state = "syndicate_combat" can_adjust = FALSE + supports_variations_flags = NONE /obj/item/clothing/under/syndicate/rus_army name = "advanced military tracksuit" desc = "Military grade tracksuits for frontline squatting." icon_state = "rus_under" can_adjust = FALSE + supports_variations_flags = NONE armor_type = /datum/armor/clothing_under/syndicate_rus_army resistance_flags = NONE @@ -142,6 +149,7 @@ worn_icon = 'icons/mob/clothing/under/medical.dmi' icon_state = "scrubswine" can_adjust = FALSE + supports_variations_flags = NONE armor_type = /datum/armor/clothing_under/syndicate_scrubs /datum/armor/clothing_under/syndicate_scrubs diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm index 64979b048cb..3b7ddbbd83c 100644 --- a/code/modules/hydroponics/grown/banana.dm +++ b/code/modules/hydroponics/grown/banana.dm @@ -191,3 +191,18 @@ var/obj/effect/decal/cleanable/food/plant_smudge/banana_smudge = new(loc) banana_smudge.color = "#ffe02f" qdel(src) + +/obj/item/food/grown/banana/bunch/monkeybomb + desc = "Am exquisite bunch of bananas. Their otherwordly plumpness seems to be hiding something." + +/obj/item/food/grown/banana/bunch/monkeybomb/examine(mob/user) + . = ..() + if(!is_simian(user)) + . += span_notice("There's a banana label on one of the 'nanas you can't quite make out the details of.") + return + . += span_notice("The banana label on this bunch indicates that monkeys can use this as a sonic grenade with a 3 second timer!") + +/obj/item/food/grown/banana/bunch/monkeybomb/attack_self(mob/user, modifiers) + if(!is_simian(user)) + return to_chat(user, span_notice("You don't really know what to do with this.")) + else start_ripening() diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm index d4cd075407f..8fb36e7c4e2 100644 --- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm +++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm @@ -40,8 +40,6 @@ payday_modifier = 1.5 ai_controlled_species = TRUE - - /datum/species/monkey/random_name(gender,unique,lastname) var/randname = "monkey ([rand(1,999)])" diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 3e286f6c58b..70e2210a4e9 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -146,6 +146,17 @@ mag_display = TRUE rack_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' +/** + * Weak uzi for syndicate chimps. It comes in a 4 TC kit. + * Roughly 9 damage per bullet every 0.2 seconds, equaling out to downing an opponent in a bit over a second, if they have no armor. + */ +/obj/item/gun/ballistic/automatic/mini_uzi/chimpgun + name = "\improper MONK-10" + desc = "Developed by Syndicate monkeys, for syndicate Monkeys. Despite the name, this weapon resembles an Uzi significantly more than a MAC-10. Uses 9mm rounds. There's a label on the other side of the gun that says \"Do what comes natural.\"" + projectile_damage_multiplier = 0.4 + projectile_wound_bonus = -25 + pin = /obj/item/firing_pin/monkey + /obj/item/gun/ballistic/automatic/m90 name = "\improper M-90gl Carbine" desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher." @@ -218,10 +229,25 @@ bolt_type = BOLT_TYPE_OPEN empty_indicator = TRUE show_bolt_icon = FALSE + /// Rate of fire, set on initialize only + var/rof = 0.1 SECONDS /obj/item/gun/ballistic/automatic/tommygun/Initialize(mapload) . = ..() - AddComponent(/datum/component/automatic_fire, 0.1 SECONDS) + AddComponent(/datum/component/automatic_fire, rof) + +/** + * Weak tommygun for syndicate chimps. It comes in a 4 TC kit. + * Roughly 9 damage per bullet every 0.2 seconds, equaling out to downing an opponent in a bit over a second, if they have no armor. + */ +/obj/item/gun/ballistic/automatic/tommygun/chimpgun + name = "\improper Typewriter" + desc = "It was the best of times, it was the BLURST of times!? You stupid monkeys!" + fire_delay = 2 + rof = 0.2 SECONDS + projectile_damage_multiplier = 0.4 + projectile_wound_bonus = -25 + pin = /obj/item/firing_pin/monkey /obj/item/gun/ballistic/automatic/ar name = "\improper NT-ARG 'Boarder'" diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index b247bd3c2d6..270e7edd930 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -55,6 +55,18 @@ lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' bolt_drop_sound = 'sound/weapons/gun/pistol/slide_drop.ogg' +/** + * Weak 1911 for syndicate chimps. It comes in a 4 TC kit. + * 15 damage every.. second? 7 shots to kill. Not fast. + */ +/obj/item/gun/ballistic/automatic/pistol/m1911/chimpgun + name = "\improper CH1M911" + desc = "For the monkey mafioso on-the-go. Uses .45 rounds and has the distinct smell of bananas." + projectile_damage_multiplier = 0.5 + projectile_wound_bonus = -12 + pin = /obj/item/firing_pin/monkey + + /obj/item/gun/ballistic/automatic/pistol/m1911/no_mag spawnwithmagazine = FALSE diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index 6c2914b495d..45763feff04 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -373,6 +373,17 @@ suit_requirement = /obj/item/clothing/suit/bluetag tagcolor = "blue" +/obj/item/firing_pin/monkey + name = "monkeylock firing pin" + desc = "This firing pin prevents non-monkeys from firing a gun." + fail_message = "not a monkey!" + +/obj/item/firing_pin/monkey/pin_auth(mob/living/user) + if(!is_simian(user)) + playsound(get_turf(src), "sound/creatures/monkey/monkey_screech_[rand(1,7)].ogg", 75, TRUE) + return FALSE + return TRUE + /obj/item/firing_pin/Destroy() if(gun) gun.pin = null diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index be3069e7d46..7a3dc6ab8fb 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -1247,7 +1247,7 @@ /datum/reagent/consumable/ethanol/bananahonk/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) . = ..() var/obj/item/organ/internal/liver/liver = drinker.get_organ_slot(ORGAN_SLOT_LIVER) - if((liver && HAS_TRAIT(liver, TRAIT_COMEDY_METABOLISM)) || ismonkey(drinker)) + if((liver && HAS_TRAIT(liver, TRAIT_COMEDY_METABOLISM)) || is_simian(drinker)) if(drinker.heal_bodypart_damage(brute = 1 * REM * seconds_per_tick, burn = 1 * REM * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 8270b42f502..28039f8b5b6 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -116,7 +116,7 @@ /datum/reagent/consumable/banana/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() var/obj/item/organ/internal/liver/liver = affected_mob.get_organ_slot(ORGAN_SLOT_LIVER) - if((liver && HAS_TRAIT(liver, TRAIT_COMEDY_METABOLISM)) || ismonkey(affected_mob)) + if((liver && HAS_TRAIT(liver, TRAIT_COMEDY_METABOLISM)) || is_simian(affected_mob)) if(affected_mob.heal_bodypart_damage(brute = 1 * REM * seconds_per_tick, burn = 1 * REM * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH @@ -670,7 +670,7 @@ /datum/reagent/consumable/monkey_energy/on_mob_metabolize(mob/living/affected_mob) . = ..() - if(ismonkey(affected_mob)) + if(is_simian(affected_mob)) affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/reagent/monkey_energy) /datum/reagent/consumable/monkey_energy/on_mob_end_metabolize(mob/living/affected_mob) diff --git a/code/modules/uplink/uplink_items/clownops.dm b/code/modules/uplink/uplink_items/clownops.dm index b973869cf12..88cd9b6464f 100644 --- a/code/modules/uplink/uplink_items/clownops.dm +++ b/code/modules/uplink/uplink_items/clownops.dm @@ -114,6 +114,26 @@ restricted = TRUE refundable = TRUE +/datum/uplink_item/reinforcement/monkey_agent + name = "Simian Agent Reinforcements" + desc = "Call in an extremely well trained monkey secret agent from our Syndicate Banana Department. \ + They've been trained to operate machinery and can read, but they can't speak Common." + item = /obj/item/antag_spawner/loadout/monkey_man + cost = 7 + purchasable_from = UPLINK_CLOWN_OPS + restricted = TRUE + refundable = TRUE + +/datum/uplink_item/reinforcement/monkey_supplies + name = "Simian Agent Supplies" + desc = "Sometimes you need a bit more firepower than a rabid monkey. Such as a rabid, armed monkey! \ + Monkeys can unpack this kit to recieve a bag with a bargain-bin gun, ammunition, and some miscellaneous supplies." + item = /obj/item/storage/toolbox/guncase/monkeycase + cost = 4 + purchasable_from = UPLINK_CLOWN_OPS + restricted = TRUE + refundable = TRUE + /datum/uplink_item/mech/honker name = "Dark H.O.N.K." desc = "A clown combat mech equipped with bombanana peel and tearstache grenade launchers, as well as the ubiquitous HoNkER BlAsT 5000." diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm index 07d57a17427..01bef40289e 100644 --- a/code/modules/uplink/uplink_items/job.dm +++ b/code/modules/uplink/uplink_items/job.dm @@ -340,3 +340,25 @@ item = /obj/item/bee_smoker cost = 4 restricted_roles = list(JOB_BOTANIST) + +/datum/uplink_item/role_restricted/monkey_agent + name = "Simian Agent Reinforcements" + desc = "Call in an extremely well trained monkey secret agent from our Syndicate Banana Department. \ + They've been trained to operate machinery and can read, but they can't speak Common. \ + Please note that these are free-range monkeys that don't react with Mutadone." + item = /obj/item/antag_spawner/loadout/monkey_man + cost = 6 + restricted_roles = list(JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST, JOB_GENETICIST, JOB_ASSISTANT, JOB_MIME, JOB_CLOWN) + restricted = TRUE + refundable = TRUE + +/datum/uplink_item/role_restricted/monkey_supplies + name = "Simian Agent Supplies" + desc = "Sometimes you need a bit more firepower than a rabid monkey. Such as a rabid, armed monkey! \ + Monkeys can unpack this kit to recieve a bag with a bargain-bin gun, ammunition, and some miscellaneous supplies." + item = /obj/item/storage/toolbox/guncase/monkeycase + cost = 4 + limited_stock = 3 + restricted_roles = list(JOB_ASSISTANT, JOB_MIME, JOB_CLOWN) + restricted = TRUE + refundable = FALSE diff --git a/icons/mob/human/species/monkey/uniform.dmi b/icons/mob/human/species/monkey/uniform.dmi index 21d70e5694f2c88b000a366548984789e94e1d80..a835629528ae5329508bc811143bc3fbb3b48d35 100644 GIT binary patch delta 2150 zcmZvec{tPy7sr2$tznW>X2vqn&GLqbaI;@qvTs@LgzWN;ZA=p8hh`=&W0y5cwzSBu zLPqvAi6l$bhO*6&W-wgu^1RQ}^SpnY^ZnzTzs~bLpK~5@R0^Drvotq?L6J}Z0AR*f z^{tQ8^}Kd@{~0Kl@9Jj68{k^z8w z%UEC6_D2`o%~I~RGCaj)qi#VK=jmya7%jcUw9UASKXdNm;Sz(P7%3E3M_z6GcH41fMf83z zv4B5iO}N-&x;Oyt590jA4`S%>Fc$54)4|}ocd5(@0(GC#|6)zK?XWlhM#*~H*eDmj zsOW>QA+mt~0VNtz((JC}SkH8%?|g)L^h4wpmm67&W9PI}x4OSb*_0=y<>!l8Bv7{O z8?;R;lcZL$rDKYh!gl>Em`tsxh0P7F(`+g_1vBA4V^5WX+sG_MU zUQ9Adtbw}DwsA2jDL^w_XWsUr<3w|`25rhl*$6=iL*M_t^=<6rzP;hlNFE|Tqh+4D zM*-%dBe-3AR)~?2k-nZUHKx{yLX9~FKc;z|sb?6s6!u{0&VeXvYpGB1klwr zYNss#&ovcpWo6}z;W-BU48WLahM`~8^SOb_XXlzTKLDS-i$%M*g`RHRD~O&}58nLR zF0;yZ)?nT^tewG@Um(=RuOROgfj`@);{)@lMMwVR{vwP)$hTUyOj-^W9P^5Zm6#;q z`Ro!XEs4(l|Mf>5@K+J1CEqXBNxm!mP4c$JNa1VVYekwmpyK~)3N!;+^*SWc zE@C6>5$*_<`2~ac7rEffq~3wUUREya+P#}_<+1VcT-h<(%cV=`nirw!MJxwdudaol z+NP#SbLrxIYA95O?L_ikg>P!LJGCcmZzm0NLo!>9MwGJZ<-_4{b>JF_#D(M=JJAHg zDVHyC?PZYS?!I97-MZB?H6;S$azExd+P`TM4oqgvaesn|8%g)xd!=S0rQGG1@W^iz7+5Eg)@In{Ng3-pC zI5INw4Ey-vg$siIHfjp-nlz1Ct>U7{VBxX6=ESb~E*r<*MML#hVOwQ43W6TW=C)Bq%rSOG$kaRfAdggBSvVrW6N8t*6$O}Jb7Hz&9_NkgCp0FLByR^ycUqR6tyI} z*wL#yeg_nc({~E1Al@Lke3RjEj_Q{0SZ)`l!`|P9nri26_!k>CqVq)#SNH~xrp<|+ z&XIIsv|)}V$=sK8#IJN)YH8(T5|c3|rxae-ABW2ptx8e65mWgaa*BZ)TrSj8);1@U z8eGnS;tb54K3_8aL8A*x&Tf)Wll?6znPzQAq`#267Y)W zRbqlvUj$>-0mbzD)a0uu1csXwN9I#i&Ad(A3N;EFpr{{dZll*<{N@rFnu>K`N*TXYT_o}NxxCZ|C9I=r zye~kF=|Mdx92~Xt zKXe~e#OryCia{%-?hQ{Qj|~mUuuc2|J&$0kinT%_k&jRl_v&JKd85?A;03H;f3TIc zwHBMHOpogf>9Hs)FURmU*3}J1cz6(Y2Iu)l!v%|EdXP+Isu5fwCR<>iLyhU<@lony zccD%XL{?QbyIPSRlrSw0kHkarX20&GUMbVHeiI5_Jy|LHHY?Hk&k&6Xv2->;^gp3| zR|w;P|4?JJk1HX-;M1&JWxzj zMVhteGXMZ+XlM>GPAg@o$p8RU000m@X^oAI|Ns9BCq1;g-Wg7W&(F^d4GkI^8Z0a< zF)=Y8A0L@B0G_C2kN^Mx0d!JMQvg8b*k%9#0Bd?wSad{@DqMf$QdV&Fa{-$R09xxB zn#xfm#Q*>S9Z5t%R9J=WmfLQ^Fc3t2*<|BO9NYc>f9)nJs1Ml*jVi%|M_?hH*#km? zwJ{?je~kTg-YQu8IL_ldP4h(FshpQ(VV#FaDI|*FydgX@bJqAF@W~?qQ6|7$6n*CC zP8&OyamoreZWVt)H1d>J8qYav{9jVGZGRxXA#O@-rBxuPts!noueEj{=(RxJTJOCz ztx>uRABS2f&+`tKAAx{TjTms5yfK8D1~ zZhi