Skip to content

Commit

Permalink
[MIRROR] Adds a Syndicate Monkey Agent beacon uplink item [MDB IGNORE…
Browse files Browse the repository at this point in the history
…] (#24550) (#245)

* 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:

---------




* Adds a Syndicate Monkey Agent beacon uplink item

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: carlarctg <[email protected]>
Co-authored-by: ATH1909 <42606352+ATH1909@ users.noreply.github.com>
Co-authored-by: Jacquerel <hnevard@ gmail.com>
  • Loading branch information
5 people authored Oct 25, 2023
1 parent f4e4ed8 commit ab96633
Show file tree
Hide file tree
Showing 22 changed files with 388 additions and 6 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/role_preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,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"
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NO_TRANSFORMATION_STING "no_transformation_sting"
/// 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
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/lists/names.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
2 changes: 2 additions & 0 deletions code/datums/dna.dm
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,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))
Expand Down
6 changes: 6 additions & 0 deletions code/game/objects/items/boxcutter.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -31,6 +33,7 @@

AddComponent( \
/datum/component/transforming, \
start_transformed = start_extended, \
force_on = 10, \
throwforce_on = 4, \
throw_speed_on = throw_speed, \
Expand All @@ -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
62 changes: 62 additions & 0 deletions code/game/objects/items/storage/toolbox.dm
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,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)
135 changes: 135 additions & 0 deletions code/modules/antagonists/_common/antag_spawner.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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

37 changes: 37 additions & 0 deletions code/modules/antagonists/syndicate_monkey/syndicate_monkey.dm
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions code/modules/clothing/under/syndicate.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -99,19 +101,22 @@
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"
desc = "A green military camouflage uniform."
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

Expand All @@ -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

Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions code/modules/hydroponics/grown/banana.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Loading

0 comments on commit ab96633

Please sign in to comment.