Merge remote-tracking branch 'tgstation/master' into upstream-2025-11-29

# Conflicts:
#	_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm
#	_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	_maps/map_files/tramstation/tramstation.dmm
#	code/_onclick/hud/new_player.dm
#	code/datums/components/squashable.dm
#	code/datums/diseases/advance/symptoms/heal.dm
#	code/datums/diseases/chronic_illness.dm
#	code/datums/status_effects/buffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/stamcrit.dm
#	code/game/machinery/computer/crew.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/wall_mounted.dm
#	code/game/turfs/closed/indestructible.dm
#	code/modules/admin/view_variables/filterrific.dm
#	code/modules/antagonists/heretic/influences.dm
#	code/modules/cargo/orderconsole.dm
#	code/modules/client/preferences.dm
#	code/modules/events/space_vines/vine_mutations.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/carbon/human/death.dm
#	code/modules/mob/living/carbon/human/species_types/jellypeople.dm
#	code/modules/mob/living/damage_procs.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mob_spawn/mob_spawn.dm
#	code/modules/projectiles/ammunition/energy/laser.dm
#	code/modules/projectiles/guns/ballistic/launchers.dm
#	code/modules/projectiles/guns/energy/laser.dm
#	code/modules/reagents/chemistry/machinery/chem_dispenser.dm
#	code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/medicine_reagents.dm
#	code/modules/surgery/healing.dm
#	code/modules/unit_tests/designs.dm
#	icons/mob/inhands/items_lefthand.dmi
#	icons/mob/inhands/items_righthand.dmi
#	tgui/packages/tgui/interfaces/ChemDispenser.tsx
This commit is contained in:
nevimer
2025-11-29 22:49:21 -05:00
1185 changed files with 39068 additions and 32028 deletions
@@ -59,15 +59,15 @@
outfit = /datum/outfit/job/assistant
icon_state = "corpsegreytider"
/obj/effect/mob_spawn/corpse/human/assistant/beesease_infection/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/assistant/beesease_infection/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
spawned_mob.ForceContractDisease(new /datum/disease/beesease)
/obj/effect/mob_spawn/corpse/human/assistant/brainrot_infection/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/assistant/brainrot_infection/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
spawned_mob.ForceContractDisease(new /datum/disease/brainrot)
/obj/effect/mob_spawn/corpse/human/assistant/spanishflu_infection/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/assistant/spanishflu_infection/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
spawned_mob.ForceContractDisease(new /datum/disease/fluspanish)
@@ -8,7 +8,7 @@
burn_damage = 1000
mob_species = /datum/species/skeleton
/obj/effect/mob_spawn/corpse/human/charredskeleton/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/charredskeleton/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.color = "#454545"
spawned_human.gender = NEUTER
@@ -34,7 +34,7 @@
outfit = select_outfit()
return ..()
/obj/effect/mob_spawn/corpse/human/legioninfested/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/legioninfested/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
var/obj/item/organ/legion_tumour/cancer = new()
cancer.Insert(spawned_human, special = TRUE, movement_flags = DELETE_IF_REPLACED)
@@ -75,7 +75,7 @@
/// Corpse spawner used by dwarf legions to make small corpses
/obj/effect/mob_spawn/corpse/human/legioninfested/dwarf
/obj/effect/mob_spawn/corpse/human/legioninfested/dwarf/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/legioninfested/dwarf/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.dna.add_mutation(/datum/mutation/dwarfism, MUTATION_SOURCE_MUTATOR)
@@ -123,7 +123,7 @@
/obj/effect/mob_spawn/corpse/human/legioninfested/skeleton/select_outfit()
return null
/obj/effect/mob_spawn/corpse/human/legioninfested/skeleton/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/legioninfested/skeleton/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.gender = NEUTER
@@ -134,7 +134,7 @@
brute_damage = 0
burn_damage = 1000
/obj/effect/mob_spawn/corpse/human/legioninfested/skeleton/charred/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/legioninfested/skeleton/charred/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.color = "#454545"
spawned_human.AddComponent(/datum/component/storm_hating)
@@ -54,7 +54,7 @@
outfit = /datum/outfit/centcom/centcom_intern/unarmed
mob_name = "Nameless Intern"
/obj/effect/mob_spawn/corpse/human/intern/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/human/intern/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.gender = MALE //we're making it canon babies
spawned_human.update_body()
@@ -5,13 +5,13 @@
/obj/effect/mob_spawn/corpse/ai
mob_type = /mob/living/silicon/ai/spawned
/obj/effect/mob_spawn/corpse/ai/create(mob/user, newname)
/obj/effect/mob_spawn/corpse/ai/create(mob/user, newname, apply_prefs)
var/ai_already_present = locate(/mob/living/silicon/ai) in loc
if(ai_already_present)
return
. = ..()
return CANCEL_SPAWN
return ..()
/obj/effect/mob_spawn/corpse/ai/special(mob/living/silicon/ai/spawned/dead_ai, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/ai/special(mob/living/silicon/ai/spawned/dead_ai, mob/mob_possessor, apply_prefs)
. = ..()
dead_ai.name = src.name
dead_ai.real_name = src.name
@@ -24,7 +24,7 @@
///the color of the slime you're spawning.
var/slime_species = /datum/slime_type/grey
/obj/effect/mob_spawn/corpse/slime/special(mob/living/basic/slime/spawned_slime, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/slime/special(mob/living/basic/slime/spawned_slime, mob/mob_possessor, apply_prefs)
. = ..()
spawned_slime.set_slime_type(slime_species)
@@ -33,7 +33,7 @@
//mostly for unit tests to not get alarmed (which by all means it should because this is a mess)
mob_type = /obj/item/clothing/mask/facehugger
/obj/effect/mob_spawn/corpse/facehugger/create(mob/user)
/obj/effect/mob_spawn/corpse/facehugger/create(mob/user, newname, apply_prefs)
var/obj/item/clothing/mask/facehugger/spawned_facehugger = new mob_type(loc)
spawned_facehugger.Die()
qdel(src)
@@ -14,7 +14,7 @@
flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path."
spawner_job_path = /datum/job/skeleton
/obj/effect/mob_spawn/ghost_role/human/skeleton/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/skeleton/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
to_chat(new_spawn, "<b>You have this horrible lurching feeling deep down that your binding to this world will fail if you abandon this zone... Were you reanimated to protect something?</b>")
new_spawn.AddComponent(/datum/component/stationstuck, PUNISHMENT_MURDER, "You experience a feeling like a stressed twine being pulled until it snaps. Then, merciful nothing.")
@@ -30,7 +30,7 @@
you_are_text = "By unknown powers, your rotting remains have been resurrected!"
flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path."
/obj/effect/mob_spawn/ghost_role/human/zombie/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/zombie/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
to_chat(new_spawn, "<b>You have this horrible lurching feeling deep down that your binding to this world will fail if you abandon this zone... Were you reanimated to protect something?</b>")
new_spawn.AddComponent(/datum/component/stationstuck, PUNISHMENT_MURDER, "You experience a feeling like a stressed twine being pulled until it snaps. Then, merciful nothing.")
@@ -6,8 +6,9 @@
flavour_text = "Write me some god damn flavor text!" //the flavor text will be the backstory argument called on the antagonist's greet, see hunter.dm for details
show_flavor = FALSE
var/back_story = "error"
allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE
/obj/effect/mob_spawn/ghost_role/human/fugitive/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/fugitive/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
var/datum/antagonist/fugitive_hunter/fughunter = new
fughunter.backstory = back_story
@@ -32,14 +32,7 @@
notify_flags = NOTIFY_CATEGORY_NOFLASH,
)
/obj/effect/mob_spawn/ghost_role/human/golem/name_mob(mob/living/spawned_mob, forced_name)
if(forced_name || !iscarbon(spawned_mob))
return ..()
forced_name = generate_random_name_species_based(spawned_mob.gender, TRUE, species_type = /datum/species/golem)
return ..()
/obj/effect/mob_spawn/ghost_role/human/golem/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/golem/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
if(is_path_in_list(initial_type, GLOB.golem_stack_food_directory))
var/datum/golem_food_buff/initial_buff = GLOB.golem_stack_food_directory[initial_type]
@@ -73,7 +66,7 @@
desc = "A humanoid shape, empty, lifeless, and full of potential."
prompt_name = "a free golem"
/obj/effect/mob_spawn/ghost_role/human/golem/adamantine/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/golem/adamantine/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
if(!ishuman(new_spawn))
return
@@ -110,8 +103,6 @@
to_chat(new_spawn, span_userdanger("Serve [real_owner.real_name], and assist [real_owner.p_them()] in completing [real_owner.p_their()] goals at any cost."))
/obj/effect/mob_spawn/ghost_role/human/golem/servant/name_mob(mob/living/spawned_mob, forced_name)
if(forced_name || !iscarbon(spawned_mob))
return ..()
if(owner_ref?.resolve())
forced_name = "Golem ([rand(1,999)])"
return ..()
@@ -14,6 +14,7 @@
the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... "
spawner_job_path = /datum/job/hermit
allow_custom_character = ALL
quirks_enabled = TRUE // SKYRAT EDIT ADDITION - ghost role loadouts
random_appearance = FALSE // SKYRAT EDIT ADDITION
@@ -81,6 +82,7 @@
flavour_text = "Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?"
spawner_job_path = /datum/job/beach_bum
outfit = /datum/outfit/beachbum
allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE
quirks_enabled = TRUE // SKYRAT EDIT ADDITION - ghost role loadouts
random_appearance = FALSE // SKYRAT EDIT ADDITION
@@ -89,8 +91,9 @@
flavour_text = "It's up to you to make sure nobody drowns or gets eaten by sharks and stuff."
name = "lifeguard sleeper"
outfit = /datum/outfit/beachbum/lifeguard
allow_custom_character = NONE
/obj/effect/mob_spawn/ghost_role/human/beach/lifeguard/special(mob/living/carbon/human/lifeguard, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/beach/lifeguard/special(mob/living/carbon/human/lifeguard, mob/mob_possessor, apply_prefs)
. = ..()
lifeguard.gender = FEMALE
lifeguard.update_body()
@@ -123,6 +126,7 @@
flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect."
spawner_job_path = /datum/job/space_bartender
outfit = /datum/outfit/spacebartender
allow_custom_character = ALL
random_appearance = FALSE // SKYRAT EDIT ADDITION
/datum/outfit/spacebartender
@@ -242,7 +246,7 @@
to_chat(user, span_warning("You have exhausted your usefulness to the Necropolis."))
return FALSE
/obj/effect/mob_spawn/ghost_role/human/ash_walker/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/ash_walker/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
// SKYRAT EDIT ADDITION BEGIN
spawned_human.fully_replace_character_name(null, spawned_human.generate_random_mob_name(TRUE)) // SKYRAT EDIT MOVE - Moving before parent call prevents char name randomization
quirks_enabled = TRUE // ghost role quirks
@@ -300,8 +304,9 @@
quirks_enabled = TRUE // SKYRAT EDIT ADDITION - ghost role loadouts
random_appearance = FALSE // SKYRAT EDIT ADDITION
deletes_on_zero_uses_left = FALSE
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/lavaland_syndicate/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/lavaland_syndicate/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
new_spawn.grant_language(/datum/language/codespeak, source = LANGUAGE_SPAWNER) // SKYRAT EDIT CHANGE - ORIGINAL: new_spawn.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
@@ -14,9 +14,12 @@
important_text = "Work as a team with your fellow survivors and do not abandon them."
outfit = /datum/outfit/oldeng
spawner_job_path = /datum/job/ancient_crew
allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE
/obj/effect/mob_spawn/ghost_role/human/oldstation/create_from_ghost(mob/dead/user)
/obj/effect/mob_spawn/ghost_role/human/oldstation/create(mob/mob_possessor, newname, apply_prefs)
. = ..()
if(!.)
return
notify_ghosts(
"Someone just woke up on Charlie Station! Why not join them and help out?",
source = ., //the spawned mob
@@ -102,6 +105,7 @@
outfit = /datum/outfit/syndicate_empty/battlecruiser
spawner_job_path = /datum/job/battlecruiser_crew
uses = 4
allow_custom_character = ALL
/// The antag team to apply the player to
var/datum/team/antag_team
@@ -191,6 +195,7 @@
important_text = "Work as a team with your fellow actors and the director to make entertainment for the masses."
outfit = /datum/outfit/actor
spawner_job_path = /datum/job/ghost_role
allow_custom_character = ALL
/datum/outfit/actor
name = "Actor"
@@ -215,6 +220,7 @@
important_text = "Work as a team with your fellow actors and the director to make entertainment for the masses."
outfit = /datum/outfit/actor/director
spawner_job_path = /datum/job/ghost_role
allow_custom_character = ALL
/datum/outfit/actor/director
name = "Director"
@@ -139,8 +139,9 @@
if(!silent)
to_chat(user, span_warning("\The [src] is not ready to hatch yet!"))
return FALSE
return TRUE
/obj/effect/mob_spawn/ghost_role/spider/special(mob/living/basic/spider/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/spider/special(mob/living/basic/spider/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
if (isspider(spawned_mob))
spawned_mob.directive = directive
@@ -197,21 +198,12 @@
)
flash_window = TRUE
/**
* Makes a ghost into a spider based on the type of egg cluster.
*
* Allows a ghost to get a prompt to use the egg cluster to become a spider.
*
* Arguments:
* * user - The ghost attempting to become a spider
* * newname - If set, renames the mob to this name
*/
/obj/effect/mob_spawn/ghost_role/spider/create(mob/user, newname)
/obj/effect/mob_spawn/ghost_role/spider/pre_ghost_take(mob/dead/observer/user)
var/chosen_spider = length(potentialspawns) > 1 ? get_radial_choice(user) : potentialspawns[1]
if(QDELETED(src) || QDELETED(user) || isnull(chosen_spider))
if(isnull(chosen_spider))
return FALSE
mob_type = chosen_spider
return ..()
return TRUE
/// Pick a spider type from a radial menu
/obj/effect/mob_spawn/ghost_role/spider/proc/get_radial_choice(mob/user)
@@ -12,6 +12,7 @@
though fate has other plans for you."
flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of "
spawner_job_path = /datum/job/escaped_prisoner
allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE
/obj/effect/mob_spawn/ghost_role/human/prisoner_transport/Initialize(mapload)
. = ..()
@@ -24,7 +25,7 @@
new /obj/structure/fluff/empty_sleeper/syndicate(get_turf(src))
return ..()
/obj/effect/mob_spawn/ghost_role/human/prisoner_transport/special(mob/living/carbon/human/spawned_human, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/prisoner_transport/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
spawned_human.fully_replace_character_name(null, "NTP #LL-0[rand(111,999)]") //Nanotrasen Prisoner #Lavaland-(numbers)
@@ -50,6 +51,7 @@
flavour_text = "Cater to visiting guests with your fellow staff, advertise the hotel, and make sure the manager doesn't fire you. Remember, the customer is always right!"
important_text = "Do NOT leave the hotel, as that is grounds for contract termination."
spawner_job_path = /datum/job/hotel_staff
allow_custom_character = ALL
/datum/outfit/hotelstaff
name = "Hotel Staff"
@@ -94,6 +96,7 @@
flavour_text = "You have awoken, without instruction. Death to Nanotrasen! If there are some clues around as to what you're supposed to be doing, you best follow those."
outfit = /datum/outfit/syndicate_empty
spawner_job_path = /datum/job/space_syndicate
allow_custom_character = ALL
/datum/outfit/syndicate_empty
name = "Syndicate Operative Empty"
@@ -119,6 +122,7 @@
flavour_text = "Hang out at the bar and chat with your buddies. Feel free to hop back in the cryogenics when you're done chatting."
outfit = /datum/outfit/cryobartender
spawner_job_path = /datum/job/space_bar_patron
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/space_bar_patron/attack_hand(mob/user, list/modifiers)
var/despawn = tgui_alert(usr, "Return to cryosleep? (Warning, Your mob will be deleted!)", null, list("Yes", "No"))
@@ -153,7 +157,7 @@
new/obj/structure/fluff/empty_sleeper(get_turf(src))
return ..()
/obj/effect/mob_spawn/ghost_role/human/exile/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/exile/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
new_spawn.fully_replace_character_name(null,"Wish Granter's Victim ([rand(1,999)])")
var/wish = rand(1,4)
@@ -178,6 +182,7 @@
you_are_text = "You are a Nanotrasen Private Security Officer!"
flavour_text = "If higher command has an assignment for you, it's best you follow that. Otherwise, death to The Syndicate."
outfit = /datum/outfit/nanotrasensoldier
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/commander
name = "sleeper"
@@ -187,6 +192,7 @@
you_are_text = "You are a Nanotrasen Commander!"
flavour_text = "Upper-crusty of Nanotrasen. You should be given the respect you're owed."
outfit = /datum/outfit/nanotrasencommander
allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE
//space doctor, a rat with cancer, and bessie from an old removed lavaland ruin.
@@ -199,6 +205,7 @@
flavour_text = "It's your job- no, your duty as a doctor, to care and heal those in need."
outfit = /datum/outfit/job/doctor
spawner_job_path = /datum/job/space_doctor
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/doctor/alive/equip(mob/living/carbon/human/doctor)
. = ..()
@@ -227,7 +234,7 @@
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
/obj/effect/mob_spawn/cow/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/cow/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
gender = FEMALE
@@ -244,6 +251,7 @@
flavour_text = "Monitor Nanotrasen communications and record information. All intruders should be disposed of \
swiftly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the \
caves can be a deadly place even for a trained operative such as yourself."
allow_custom_character = ALL
/datum/outfit/snowsyndie
name = "Syndicate Snow Operative"
@@ -269,8 +277,9 @@
important_text = "Obey orders given by your captain. DO NOT let the ship fall into enemy hands."
outfit = /datum/outfit/syndicatespace/syndicrew
spawner_job_path = /datum/job/syndicate_cybersun
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/syndicatespace/special(mob/living/new_spawn, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/human/syndicatespace/special(mob/living/new_spawn, mob/mob_possessor, apply_prefs)
. = ..()
new_spawn.grant_language(/datum/language/codespeak, source = LANGUAGE_SPAWNER) // SKYRAT EDIT CHANGE - ORIGINAL: new_spawn.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
var/datum/job/spawn_job = SSjob.get_job_type(spawner_job_path)
@@ -286,6 +295,7 @@
important_text = "Protect the ship and secret documents in your backpack with your own life."
outfit = /datum/outfit/syndicatespace/syndicaptain
spawner_job_path = /datum/job/syndicate_cybersun_captain
allow_custom_character = ALL
/obj/effect/mob_spawn/ghost_role/human/syndicatespace/captain/Destroy()
new /obj/structure/fluff/empty_sleeper/syndicate/captain(get_turf(src))
@@ -31,7 +31,7 @@
if(flower_bud.trait_flags & SPACEVINE_COLD_RESISTANT)
spawned_human_trap.unsuitable_cold_damage = 0
/obj/effect/mob_spawn/ghost_role/venus_human_trap/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/venus_human_trap/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
spawned_mob.mind.add_antag_datum(/datum/antagonist/venus_human_trap)
@@ -54,3 +54,4 @@
if(!silent)
to_chat(user, span_warning("\The [src] has not borne fruit yet!"))
return FALSE
return TRUE
+100 -61
View File
@@ -44,16 +44,36 @@
QDEL_NULL(outfit)
return ..()
/// Creates whatever mob the spawner makes. Return FALSE if we want to exit from here without doing that, returning NULL will be logged to admins.
/obj/effect/mob_spawn/proc/create(mob/mob_possessor, newname)
/**
* Creates whatever mob the spawner makes.
*
* * mob_possessor - The ghost/mob that is possessing this mob, if applicable
* * newname - A forced name for the mob, if applicable
* * apply_prefs - Whether we should apply the possessor's preferences to the mob, if applicable
*
* Returns
* - the created mob
* - CANCEL_SPAWN if the spawn process should be stopped
* - null if the spawn failed (and something went wrong)
*/
/obj/effect/mob_spawn/proc/create(mob/mob_possessor, newname, apply_prefs)
SHOULD_NOT_SLEEP(TRUE)
var/mob/living/spawned_mob = new mob_type(get_turf(src)) //living mobs only
special(spawned_mob, mob_possessor, apply_prefs)
name_mob(spawned_mob, newname)
special(spawned_mob, mob_possessor)
equip(spawned_mob)
spawned_mob_ref = WEAKREF(spawned_mob)
return spawned_mob
/obj/effect/mob_spawn/proc/special(mob/living/spawned_mob, mob/mob_possessor)
/**
* Any special behavior that needs to be done to the mob after it's created but before it's equipped.
*
* * spawned_mob - The mob that was created
* * mob_possessor - The ghost/mob that is possessing this mob, if applicable
* * apply_prefs - Whether we should apply the possessor's preferences to the mob, if applicable
*/
/obj/effect/mob_spawn/proc/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
SHOULD_CALL_PARENT(TRUE)
if(faction)
spawned_mob.faction = faction
@@ -112,9 +132,8 @@
var/uses = 1
/// Does the spawner delete itself when it runs out of uses?
var/deletes_on_zero_uses_left = TRUE
/// A list of the ckeys that currently are trying to access this spawner, so that they can't try to spawn more than once (in case there's sleeps).
/// Static because you only really want to be able to spawn in one spawner at a time, obviously.
var/static/list/ckeys_trying_to_spawn
///bitflag that determines if players can spawn in as their statics
var/allow_custom_character = NONE
////descriptions
@@ -152,16 +171,25 @@
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/ghost_role/attack_ghost(mob/dead/observer/user)
if(!SSticker.HasRoundStarted() || !loc)
if(!SSticker.HasRoundStarted() || isnull(loc) || QDELETED(src))
return
// We don't open the prompt more than once at a time.
// Lazylist of the ckeys that currently are trying to access any spawner, so that they can't try to spawn more than once (in case there's sleeps).
var/static/list/ckeys_trying_to_spawn
if(LAZYFIND(ckeys_trying_to_spawn, user.ckey))
return
if(uses <= 0 && !infinite_use)
to_chat(user, span_warning("This spawner is out of charges!"))
return FALSE
if(!can_ghost_take(user))
return FALSE
uses -= 1 // Remove a use EARLY to account for sleep / inputs
var/user_ckey = user.ckey // Just in case shenanigans happen, we always want to remove it from the list.
LAZYADD(ckeys_trying_to_spawn, user_ckey)
var/prompt_fail = FALSE
var/apply_prefs = FALSE
// SKYRAT EDIT ADDITION START
if(restricted_species && !(user.client?.prefs?.read_preference(/datum/preference/choiced/species) in restricted_species))
var/incorrect_species = tgui_alert(user, "Current species preference incompatible, proceed with random appearance?", "Incompatible Species", list("Yes", "No"))
@@ -174,43 +202,46 @@
var/prompt = "Become [prompt_name]?"
if(!temp_body && user.can_reenter_corpse && user.mind)
prompt += " (Warning, You can no longer be revived!)"
var/ghost_role = tgui_alert(usr, prompt, buttons = list("Yes", "No"), timeout = 10 SECONDS)
if(ghost_role != "Yes" || !loc || QDELETED(user))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
prompt_fail = tgui_alert(user, prompt, buttons = list("Yes", "No"), timeout = 10 SECONDS) != "Yes"
if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !(flags_1 & ADMIN_SPAWNED_1))
to_chat(user, span_warning("An admin has temporarily disabled non-admin ghost roles!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(uses <= 0 && !infinite_use) //just in case
to_chat(user, span_warning("This spawner is out of charges!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
var/species_pref = user.client.prefs.read_preference(/datum/preference/choiced/species) || /datum/species/human
if(!prompt_fail && user.started_as_observer && allow_custom_character && (GLOB.species_prototypes[species_pref].inherent_respiration_type & RESPIRATION_OXYGEN))
var/static_prompt = "Because you haven't taken a role so far, you may spawn in as \
[((allow_custom_character & GHOSTROLE_TAKE_PREFS_SPECIES) || species_pref == /datum/species/human) ? "" : "a human version of"] \
your customized character with a random name. Would you like to?"
apply_prefs = tgui_alert(user, static_prompt, "Custom Character", list("Yes", "No"), 10 SECONDS) == "Yes"
if(!prompt_fail && !pre_ghost_take(user))
prompt_fail = TRUE
if(prompt_fail || !can_ghost_take(user) || !create_from_ghost(user, apply_prefs, subtract_uses = FALSE))
uses += 1
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
/// Allows for modifications before the ghost is turned into a mob.
/// You can put sleeps or inputs in here, sanity checking is done for you after this proc returns.
/// Returning FALSE will cancel the spawn process.
/obj/effect/mob_spawn/ghost_role/proc/pre_ghost_take(mob/dead/observer/user)
return TRUE
/// Checks if a ghost can take this ghost role.
/obj/effect/mob_spawn/ghost_role/proc/can_ghost_take(mob/dead/observer/user)
if(is_banned_from(user.ckey, role_ban))
to_chat(user, span_warning("You are banned from this role!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
return FALL_STOP_INTERCEPTING
if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !(flags_1 & ADMIN_SPAWNED_1))
to_chat(user, span_warning("An admin has temporarily disabled non-admin ghost roles!"))
return FALSE
// SKYRAT EDIT ADDITION START
if(is_banned_from(user.ckey, BAN_GHOST_ROLE_SPAWNER)) // Ghost role bans
to_chat(user, span_warning("Error, you are banned from playing ghost roles!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
return FALSE
// SKYRAT EDIT ADDITION END
if(!allow_spawn(user, silent = FALSE))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(QDELETED(src) || QDELETED(user))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
if(uses <= 0 && !infinite_use) // Just in case something took longer than it should've and we got here after the uses went below zero.
to_chat(user, span_warning("This spawner is out of charges!"))
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey)
return
create_from_ghost(user)
return FALSE
if(!allow_spawn(user, silent = FALSE))
return FALSE
return TRUE
/**
* Uses a use and creates a mob from a passed ghost
@@ -219,49 +250,57 @@
*
* If you are manually forcing a player into this mob spawn,
* you should be using this and not directly calling [proc/create].
*
* * * user - The ghost/mob that is possessing this mob
* * * apply_prefs - Whether we should apply the possessor's preferences to the mob
* * * subtract_uses - Whether to subtract a use from the spawner.
* Set to FALSE if you want to handle uses manually elsewhere.
*/
/obj/effect/mob_spawn/ghost_role/proc/create_from_ghost(mob/dead/user)
/obj/effect/mob_spawn/ghost_role/proc/create_from_ghost(mob/dead/observer/user, apply_prefs, subtract_uses = TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
SHOULD_NOT_SLEEP(TRUE)
ASSERT(istype(user))
var/user_ckey = user.ckey // We need to do it before everything else, because after the create() the ckey will already have been transferred.
user.log_message("became a [prompt_name].", LOG_GAME)
uses -= 1 // Remove a use before trying to spawn to prevent strangeness like the spawner trying to spawn more mobs than it should be able to
if(!temp_body)
user.mind = null // dissassociate mind, don't let it follow us to the next life
var/created = create(user)
LAZYREMOVE(ckeys_trying_to_spawn, user_ckey) // We do this AFTER the create() so that we're basically sure that the user won't be in their ghost body anymore, so they can't click on the spawner again.
if(!created)
uses += 1 // Refund use because we didn't actually spawn anything
if(isnull(created)) // If we explicitly return FALSE instead of just not returning a mob, we don't want to spam the admins
CRASH("An instance of [type] didn't return anything when creating a mob, this might be broken!")
SEND_SIGNAL(src, COMSIG_GHOSTROLE_SPAWNED, created)
check_uses() // Now we check if the spawner should delete itself or not
var/mob/created = create(user, apply_prefs = apply_prefs)
if(ismob(created))
SEND_SIGNAL(src, COMSIG_GHOSTROLE_SPAWNED, created)
if(subtract_uses)
uses -= 1
check_uses()
else if(isnull(created)) // null instead of explicit CANCEL_SPAWN means something went wrong
CRASH("An instance of [type] didn't return anything when creating a mob, this might be broken!")
return created
/obj/effect/mob_spawn/ghost_role/create(mob/mob_possessor, newname)
/obj/effect/mob_spawn/ghost_role/create(mob/mob_possessor, newname, apply_prefs)
if(!mob_possessor.key) // This is in the scenario that the server is somehow lagging, or someone fucked up their code, and we try to spawn the same person in twice. We'll simply not spawn anything and CRASH(), so that we report what happened.
CRASH("Attempted to create an instance of [type] with a mob that had no ckey attached to it, which isn't supported by ghost role spawners!")
return ..()
/obj/effect/mob_spawn/ghost_role/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/ghost_role/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
if(mob_possessor)
if(mob_possessor.client && apply_prefs && allow_custom_character && ishuman(spawned_mob))
var/mob/living/carbon/human/spawned_human = spawned_mob
if(allow_custom_character & GHOSTROLE_TAKE_PREFS_APPEARANCE)
mob_possessor.client.prefs.apply_prefs_to(spawned_human, icon_updates = TRUE, do_not_apply = typesof(/datum/preference/name, /datum/preference/choiced/species))
if(allow_custom_character & GHOSTROLE_TAKE_PREFS_SPECIES)
spawned_human.set_species(mob_possessor.client.prefs.read_preference(/datum/preference/choiced/species))
spawned_human.fully_replace_character_name(spawned_human.real_name, spawned_human.generate_random_mob_name())
if(mob_possessor.mind)
mob_possessor.mind.transfer_to(spawned_mob, force_key_move = TRUE)
else
spawned_mob.PossessByPlayer(mob_possessor.key)
var/datum/mind/spawned_mind = spawned_mob.mind
if(spawned_mind)
spawned_mob.mind.set_assigned_role_with_greeting(SSjob.get_job_type(spawner_job_path))
spawned_mind.name = spawned_mob.real_name
if(show_flavor)
var/output_message = span_infoplain("<span class='big bold'>[you_are_text]</span>")
if(flavour_text != "")
@@ -310,16 +349,16 @@
if(mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP))
INVOKE_ASYNC(src, PROC_REF(create))
/obj/effect/mob_spawn/corpse/special(mob/living/spawned_mob, mob/mob_possessor)
/obj/effect/mob_spawn/corpse/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs)
. = ..()
spawned_mob.death(TRUE)
spawned_mob.adjustOxyLoss(oxy_damage)
spawned_mob.adjustBruteLoss(brute_damage)
spawned_mob.adjustFireLoss(burn_damage)
spawned_mob.adjust_oxy_loss(oxy_damage)
spawned_mob.adjust_brute_loss(brute_damage)
spawned_mob.adjust_fire_loss(burn_damage)
if (corpse_description)
spawned_mob.AddComponent(/datum/component/temporary_description, corpse_description, naive_corpse_description)
/obj/effect/mob_spawn/corpse/create(mob/mob_possessor, newname)
/obj/effect/mob_spawn/corpse/create(mob/mob_possessor, newname, apply_prefs)
. = ..()
qdel(src)
@@ -339,7 +378,7 @@
///husks the corpse if true.
var/husk = FALSE
/obj/effect/mob_spawn/corpse/human/special(mob/living/carbon/human/spawned_human)
/obj/effect/mob_spawn/corpse/human/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs)
. = ..()
if(husk)
spawned_human.Drain()