Pun Pun Station Trait, But it's only active on Monkey Day (14 December, every round) (#86091)

## About The Pull Request
This is a remake of https://github.com/tgstation/tgstation/pull/84501
with a few small changes to avoid mapping issues. This time it's
restricted to a once-in-a-year event, mainly because neither Jacq and
Lemon were ok with the possibility of Pun Pun being posibly playable all
year, however Jacq said he's fine with it being available on the
holiday, while Lemon isn't around on Discord atm so I don't know what
he's to say.

The trait makes pun pun playable and gives them the job of being a
busser (waiter) in the cafeteria. They're clever and can use tools most
other monkeys can't, but cannot be humanized, while also being unable to
speak Galactic Common. Should they roll traitor, they'll be able to buy
syndicate monkey reinforcements and equipment, which it can also use,
being a monkey itself.

## Why It's Good For The Game
This is a fairly unique job, like the Cargo Gorilla, due to the fact
it's not a conventional humanoid crew member nor the AI or a cyborg. I
thought this was a fun idea, though I met some obstacles and the
original PR was DNM'ed and then closed. However, the trait perfectly
fits the theme of the holiday, making a good compromise since Pun Pun
will stay AI-controlled the rest of the year, as Jacquarel and Lemon
want it to be. It can also be added by an admin through VV, whether
they're planning some shenanigean or just want to add something more to
the round.

## Changelog

🆑
add: Pun Pun is a playable crewmember during Monkey Day (14 December).
/🆑
This commit is contained in:
Ghom
2024-09-12 18:35:37 +02:00
committed by GitHub
parent 71fe9165d3
commit 06ba9d93eb
22 changed files with 194 additions and 42 deletions
+1
View File
@@ -81,6 +81,7 @@
#define SECHUD_ASSISTANT "hudassistant"
#define SECHUD_ATMOSPHERIC_TECHNICIAN "hudatmospherictechnician"
#define SECHUD_BARTENDER "hudbartender"
#define SECHUD_BUSSER "hudbusser"
#define SECHUD_BITAVATAR "hudbitavatar"
#define SECHUD_BITRUNNER "hudbitrunner"
#define SECHUD_BOTANIST "hudbotanist"
+27 -25
View File
@@ -93,6 +93,7 @@
#define JOB_LAWYER "Lawyer"
#define JOB_CHAPLAIN "Chaplain"
#define JOB_PSYCHOLOGIST "Psychologist"
#define JOB_PUN_PUN "Pun Pun"
//ERTs
#define JOB_ERT_DEATHSQUAD "Death Commando"
#define JOB_ERT_COMMANDER "Emergency Response Team Commander"
@@ -136,31 +137,32 @@
#define JOB_DISPLAY_ORDER_LAWYER 12
#define JOB_DISPLAY_ORDER_CHAPLAIN 13
#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 14
#define JOB_DISPLAY_ORDER_AI 15
#define JOB_DISPLAY_ORDER_CYBORG 16
#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 17
#define JOB_DISPLAY_ORDER_STATION_ENGINEER 18
#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 19
#define JOB_DISPLAY_ORDER_QUARTERMASTER 20
#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 21
#define JOB_DISPLAY_ORDER_SHAFT_MINER 22
#define JOB_DISPLAY_ORDER_BITRUNNER 23
#define JOB_DISPLAY_ORDER_CARGO_GORILLA 24
#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 25
#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 26
#define JOB_DISPLAY_ORDER_PARAMEDIC 27
#define JOB_DISPLAY_ORDER_CHEMIST 28
#define JOB_DISPLAY_ORDER_CORONER 29
#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 30
#define JOB_DISPLAY_ORDER_SCIENTIST 31
#define JOB_DISPLAY_ORDER_ROBOTICIST 32
#define JOB_DISPLAY_ORDER_GENETICIST 33
#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 34
#define JOB_DISPLAY_ORDER_VETERAN_ADVISOR 35
#define JOB_DISPLAY_ORDER_WARDEN 36
#define JOB_DISPLAY_ORDER_DETECTIVE 37
#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 38
#define JOB_DISPLAY_ORDER_PRISONER 39
#define JOB_DISPLAY_ORDER_PUN_PUN 15
#define JOB_DISPLAY_ORDER_AI 16
#define JOB_DISPLAY_ORDER_CYBORG 17
#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 18
#define JOB_DISPLAY_ORDER_STATION_ENGINEER 19
#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 20
#define JOB_DISPLAY_ORDER_QUARTERMASTER 21
#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 22
#define JOB_DISPLAY_ORDER_SHAFT_MINER 23
#define JOB_DISPLAY_ORDER_BITRUNNER 24
#define JOB_DISPLAY_ORDER_CARGO_GORILLA 25
#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 26
#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 27
#define JOB_DISPLAY_ORDER_PARAMEDIC 28
#define JOB_DISPLAY_ORDER_CHEMIST 29
#define JOB_DISPLAY_ORDER_CORONER 30
#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 31
#define JOB_DISPLAY_ORDER_SCIENTIST 32
#define JOB_DISPLAY_ORDER_ROBOTICIST 33
#define JOB_DISPLAY_ORDER_GENETICIST 34
#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 35
#define JOB_DISPLAY_ORDER_VETERAN_ADVISOR 36
#define JOB_DISPLAY_ORDER_WARDEN 37
#define JOB_DISPLAY_ORDER_DETECTIVE 38
#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 39
#define JOB_DISPLAY_ORDER_PRISONER 40
#define DEPARTMENT_UNASSIGNED "No Department"
@@ -164,6 +164,8 @@ PROCESSING_SUBSYSTEM_DEF(station)
///Creates a given trait of a specific type, while also removing any blacklisted ones from the future pool.
/datum/controller/subsystem/processing/station/proc/setup_trait(datum/station_trait/trait_type)
if(locate(trait_type) in station_traits)
return
var/datum/station_trait/trait_instance = new trait_type()
station_traits += trait_instance
log_game("Station Trait: [trait_instance.name] chosen for this round.")
+23
View File
@@ -156,6 +156,29 @@
)
job = /datum/job/bartender
/datum/id_trim/job/pun_pun
assignment = "busser"
trim_state = "trim_busser"
department_color = COLOR_SERVICE_LIME
subdepartment_color = COLOR_SERVICE_LIME
sechud_icon_state = SECHUD_BUSSER
minimal_access = list(
ACCESS_MINERAL_STOREROOM,
ACCESS_SERVICE,
ACCESS_THEATRE,
)
extra_access = list(
ACCESS_HYDROPONICS,
ACCESS_KITCHEN,
ACCESS_BAR,
)
template_access = list(
ACCESS_CAPTAIN,
ACCESS_CHANGE_IDS,
ACCESS_HOP,
)
job = /datum/job/pun_pun
/datum/id_trim/job/bitrunner
assignment = JOB_BITRUNNER
trim_state = "trim_bitrunner"
+13
View File
@@ -243,6 +243,19 @@
qdel(thing_on_table)
new /obj/machinery/fax/auto_name(picked_turf)
/datum/station_trait/job/pun_pun
name = "Pun Pun is a Crewmember"
button_desc = "Ook ook ah ah, sign up to play as the bartender's monkey."
weight = 0 //Unrollable by default, available all day during monkey day.
report_message = "We've evaluated the bartender's monkey to have the mental capacity of the average crewmember. As such, we made them one."
show_in_report = TRUE
can_roll_antag = CAN_ROLL_ALWAYS
job_to_add = /datum/job/pun_pun
/datum/station_trait/job/pun_pun/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
. = ..()
overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "pun_pun_on" : "pun_pun_off"
#undef CAN_ROLL_ALWAYS
#undef CAN_ROLL_PROTECTED
#undef CAN_ROLL_NEVER
@@ -285,6 +285,7 @@
/datum/job/paramedic = /obj/item/organ/internal/cyberimp/eyes/hud/medical,
/datum/job/prisoner = /obj/item/organ/internal/eyes/robotic/shield,
/datum/job/psychologist = /obj/item/organ/internal/ears/cybernetic/whisper,
/datum/job/pun_pun = /obj/item/organ/internal/cyberimp/arm/strongarm,
/datum/job/quartermaster = /obj/item/organ/internal/stomach/cybernetic/tier3,
/datum/job/research_director = /obj/item/organ/internal/cyberimp/bci,
/datum/job/roboticist = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic,
+5
View File
@@ -306,6 +306,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
GLOB.newplayer_start += loc
return INITIALIZE_HINT_QDEL
/obj/effect/landmark/start/pun_pun
name = JOB_PUN_PUN
icon = 'icons/mob/human/human.dmi'
icon_state = "monkey"
/obj/effect/landmark/latejoin
name = "JoinLate"
@@ -424,15 +424,7 @@
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.make_clever_and_no_dna_scramble()
monkey_man.mind.enslave_mind_to_creator(user)
+8
View File
@@ -694,6 +694,14 @@
begin_day = 14
begin_month = DECEMBER
/datum/holiday/monkey/celebrate()
. = ..()
SSstation.setup_trait(/datum/station_trait/job/pun_pun)
//SSevents should initialize before SSatoms but who knows if it'll ever change.
if(GLOB.the_one_and_only_punpun)
new /obj/effect/landmark/start/pun_pun(GLOB.the_one_and_only_punpun.loc)
qdel(GLOB.the_one_and_only_punpun)
/datum/holiday/doomsday
name = "Mayan Doomsday Anniversary"
begin_day = 21
@@ -0,0 +1,53 @@
///Special job, active during monkey day.
/datum/job/pun_pun
title = JOB_PUN_PUN
description = "Assist the service department by serving drinks and food and entertaining the crew."
department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
supervisors = "the Bartender"
spawn_type = /mob/living/carbon/human/species/monkey/punpun
outfit = /datum/outfit/job/pun_pun
config_tag = "PUN_PUN"
random_spawns_possible = FALSE
paycheck = PAYCHECK_LOWER
paycheck_department = ACCOUNT_CIV
display_order = JOB_DISPLAY_ORDER_PUN_PUN
departments_list = list(/datum/job_department/service)
exclusive_mail_goodies = TRUE
mail_goodies = list(
/obj/item/food/grown/banana = 4,
/obj/effect/spawner/random/entertainment/money_medium = 3,
/obj/item/clothing/head/helmet/monkey_sentience = 1,
/obj/item/book/granter/sign_language = 1,
/obj/item/food/monkeycube = 1,
)
rpg_title = "Homunculus"
allow_bureaucratic_error = FALSE
job_flags = (STATION_JOB_FLAGS|STATION_TRAIT_JOB_FLAGS)&~JOB_ASSIGN_QUIRKS
/datum/job/pun_pun/get_spawn_mob(client/player_client, atom/spawn_point)
if (!player_client)
return
var/mob/living/monky = new spawn_type(get_turf(spawn_point))
if(!GLOB.the_one_and_only_punpun)
GLOB.the_one_and_only_punpun = monky
return monky
/datum/job/pun_pun/after_spawn(mob/living/carbon/human/monkey, client/player_client)
. = ..()
monkey.make_clever_and_no_dna_scramble()
/datum/outfit/job/pun_pun
name = "Pun Pun"
jobtype = /datum/job/pun_pun
id_trim = /datum/id_trim/job/pun_pun
belt = /obj/item/modular_computer/pda/pun_pun
uniform = /obj/item/clothing/under/suit/waiter
backpack_contents = list(
/obj/item/gun/ballistic/shotgun/monkey = 1,
/obj/item/storage/box/beanbag = 1,
)
shoes = null //monkeys cannot equip shoes
@@ -1074,6 +1074,17 @@
if(use_random_name)
fully_replace_character_name(real_name, generate_random_mob_name())
///Proc used to prevent syndicate monkeys and player-selectable Pun Pun able to use objects while stuck in monkey mode.
/mob/living/carbon/human/proc/make_clever_and_no_dna_scramble()
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 dna.mutations)
mutation.mutadone_proof = TRUE
mutation.instability = 0
// Extra backup!
ADD_TRAIT(src, TRAIT_NO_DNA_SCRAMBLE, SPECIES_TRAIT)
/mob/living/carbon/human/species/abductor
race = /datum/species/abductor
@@ -48,6 +48,11 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
var/memory_saved = FALSE
/mob/living/carbon/human/species/monkey/punpun/Initialize(mapload)
// 1 Pun Pun should exist
REGISTER_REQUIRED_MAP_ITEM(1, 1)
if(mapload && (locate(/datum/station_trait/job/pun_pun) in SSstation.station_traits))
new /obj/effect/landmark/start/pun_pun(loc) //Pun Pun is a crewmember, and may late-join.
return INITIALIZE_HINT_QDEL
Read_Memory()
var/name_to_use = name
@@ -65,8 +70,8 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
if(!GLOB.the_one_and_only_punpun && mapload)
GLOB.the_one_and_only_punpun = src
// 1 Pun Pun should exist
REGISTER_REQUIRED_MAP_ITEM(1, 1)
else if(GLOB.the_one_and_only_punpun)
ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT) //faaaaaaake!
fully_replace_character_name(real_name, name_to_use)
@@ -425,6 +425,14 @@
/datum/computer_file/program/borg_monitor,
)
/obj/item/modular_computer/pda/pun_pun
name = "monkey PDA"
greyscale_colors = "#ffcc66#914800"
starting_programs = list(
/datum/computer_file/program/bounty_board,
/datum/computer_file/program/emojipedia,
)
/**
* Non-roles
*/
@@ -53,7 +53,10 @@
ammo_type = /obj/item/ammo_casing/shotgun/incapacitate
max_ammo = 3
/obj/item/ammo_box/magazine/internal/shot/musket
/obj/item/ammo_box/magazine/internal/shot/single
name = "single-barrel shotgun internal magazine"
max_ammo = 1
/obj/item/ammo_box/magazine/internal/shot/single/musket
name = "donk co musket internal magazine"
ammo_type = /obj/item/ammo_casing/shotgun/fletchette
max_ammo = 1
@@ -287,7 +287,6 @@
can_be_sawn_off = TRUE
pb_knockback = 3 // it's a super shotgun!
/obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(mob/user)
. = ..()
if(.)
@@ -343,6 +342,30 @@
return hook.try_fire_gun(target, user, params)
return ..()
///An underpowered shotgun given to Pun Pun when the station job trait roll.
/obj/item/gun/ballistic/shotgun/monkey
name = "\improper Barback's Shot"
desc = "A chimp-sized, single-shot and break-action shotgun with an unpractical stock."
icon_state = "chimp_shottie"
inhand_icon_state = "shotgun"
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
force = 8
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = NONE
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/shot/single
obj_flags = UNIQUE_RENAME
w_class = WEIGHT_CLASS_NORMAL
weapon_weight = WEAPON_MEDIUM
semi_auto = TRUE
bolt_type = BOLT_TYPE_NO_BOLT
spread = 10
projectile_damage_multiplier = 0.5
projectile_wound_bonus = -25
recoil = 1
pin = /obj/item/firing_pin/monkey
pb_knockback = 1
/obj/item/gun/ballistic/shotgun/musket
name = "\improper Donk Co. Musket"
icon = 'icons/obj/weapons/guns/ballistic.dmi'
@@ -356,4 +379,4 @@
bolt_type = BOLT_TYPE_LOCKING
bolt_wording = "bolt"
internal_magazine = TRUE
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/shot/musket
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/shot/single/musket
+2 -2
View File
@@ -364,7 +364,7 @@
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_roles = list(JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST, JOB_GENETICIST, JOB_ASSISTANT, JOB_MIME, JOB_CLOWN, JOB_PUN_PUN)
restricted = TRUE
refundable = TRUE
@@ -375,7 +375,7 @@
item = /obj/item/storage/toolbox/guncase/monkeycase
cost = 4
limited_stock = 3
restricted_roles = list(JOB_ASSISTANT, JOB_MIME, JOB_CLOWN)
restricted_roles = list(JOB_ASSISTANT, JOB_MIME, JOB_CLOWN, JOB_PUN_PUN)
restricted = TRUE
refundable = FALSE
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

+1
View File
@@ -4450,6 +4450,7 @@
#include "code\modules\jobs\job_types\station_trait\bridge_assistant.dm"
#include "code\modules\jobs\job_types\station_trait\cargo_gorilla.dm"
#include "code\modules\jobs\job_types\station_trait\human_ai.dm"
#include "code\modules\jobs\job_types\station_trait\pun_pun.dm"
#include "code\modules\jobs\job_types\station_trait\veteran_advisor.dm"
#include "code\modules\keybindings\bindings_atom.dm"
#include "code\modules\keybindings\bindings_client.dm"
@@ -54,5 +54,6 @@ export const JOB2ICON = {
'Station Engineer': 'gears',
'Syndicate Operative': 'dragon',
'Veteran Security Advisor': 'ribbon',
'Pun Pun': 'paw',
Warden: 'handcuffs',
} as const;