Skip to content

Commit 0de8e86

Browse files
authored
Gives teratomas the captain's car keys (uncommon teratoma spawn event + other misc teratoma stuff) (#5989)
1 parent 51b1be3 commit 0de8e86

File tree

8 files changed

+174
-21
lines changed

8 files changed

+174
-21
lines changed

code/__DEFINES/role_preferences.dm

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#define ROLE_FLORIDA_MAN "Florida Man"
4949
#define ROLE_SLASHER "Slasher"
5050
#define ROLE_MONSTERHUNTER "Monster Hunter"
51+
#define ROLE_TERATOMA "Teratoma"
5152

5253
// Latejoin roles
5354
#define ROLE_HERETIC_SMUGGLER "Heretic Smuggler"
@@ -167,6 +168,7 @@ GLOBAL_LIST_INIT(special_roles, list(
167168
ROLE_SLASHER = 0,
168169
ROLE_FLORIDA_MAN = 0,
169170
ROLE_OPFOR_CANDIDATE = 0,
171+
ROLE_TERATOMA = 0,
170172
//monkestation edit end
171173

172174
// Latejoin

code/modules/client/preferences/middleware/antags.dm

+4-2
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,13 @@
119119
ROLE_FUGITIVE = /datum/antagonist/fugitive,
120120
ROLE_LONE_OPERATIVE = /datum/antagonist/nukeop/lone,
121121
ROLE_SENTIENCE = /datum/antagonist/sentient_creature,
122-
//monkestation antags
122+
// monkestation start: non-dynamic antags
123123
ROLE_CORTICAL_BORER = /datum/antagonist/cortical_borer,
124124
ROLE_DRIFTING_CONTRACTOR = /datum/antagonist/traitor/contractor,
125125
ROLE_SLASHER = /datum/antagonist/slasher,
126-
ROLE_FLORIDA_MAN = /datum/antagonist/florida_man
126+
ROLE_FLORIDA_MAN = /datum/antagonist/florida_man,
127+
ROLE_TERATOMA = /datum/antagonist/teratoma,
128+
// monkestation end
127129
)
128130

129131
var/list/antagonists = non_ruleset_antagonists.Copy()
Loading

monkestation/code/modules/antagonists/changeling/powers/teratomas.dm

+9-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
playsound(user.loc, 'sound/effects/blobattack.ogg', 50, 1)
1414
user.spawn_gibs()
1515
user.visible_message(span_danger("Something horrible bursts out of [user]'s chest!"), \
16-
span_danger("Living teratoma bursts out of your chest!"), \
16+
span_danger("A living teratoma bursts out of your chest!"), \
1717
span_hear("You hear flesh tearing!"), COMBAT_MESSAGE_RANGE)
1818
return FALSE //create_teratoma() handles the chemicals anyway so there is no reason to take them again
1919

@@ -23,10 +23,12 @@
2323
return FALSE
2424
ling.adjust_chemicals(-chemical_cost)
2525
var/list/candidates = SSpolling.poll_ghost_candidates(
26-
"Do you want to play as a living teratoma?",
26+
question = "Do you want to play as a living teratoma?",
27+
role = ROLE_TERATOMA,
2728
poll_time = 7.5 SECONDS,
2829
ignore_category = POLL_IGNORE_TERATOMA,
2930
alert_pic = /datum/antagonist/teratoma,
31+
jump_target = user,
3032
role_name_text = "living teratoma",
3133
chat_text_border_icon = /datum/antagonist/teratoma,
3234
)
@@ -39,11 +41,12 @@
3941
to_chat(user, span_warning("You fail at creating a tumor. Perhaps you should try again later?"))
4042
ling.adjust_chemicals(chemical_cost)
4143
return FALSE
44+
45+
var/datum/mind/goober_mind = new(candidate.key)
46+
goober_mind.active = TRUE
4247
var/mob/living/carbon/human/species/teratoma/goober = new(user.drop_location())
43-
goober.key = candidate.key
44-
if(!goober.mind)
45-
goober.mind_initialize()
46-
goober.mind.add_antag_datum(/datum/antagonist/teratoma)
48+
goober_mind.transfer_to(goober)
49+
goober_mind.add_antag_datum(/datum/antagonist/teratoma)
4750
to_chat(goober, span_notice("You burst out from [user]'s chest!"))
4851
SEND_SOUND(goober, sound('sound/effects/blobattack.ogg'))
4952
return TRUE

monkestation/code/modules/antagonists/teratoma/teratoma.dm

+80-13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
/// Maximum amount of random ion laws to give teratoma silicons.
2+
#define MAX_TERATOMA_ION_LAWS 3
3+
/// Minimum time between law corruptions, if the teratoma is borged/AI'd.
4+
#define LAW_CORRUPTION_COOLDOWN_MIN (1.5 MINUTES)
5+
/// Maximum time between law corruptions, if the teratoma is borged/AI'd.
6+
#define LAW_CORRUPTION_COOLDOWN_MAX (7.5 MINUTES)
7+
18
/datum/team/teratoma
29
name = "Teratomas"
310
member_name = "teratoma"
@@ -7,24 +14,84 @@
714
show_in_antagpanel = TRUE
815
antagpanel_category = ANTAG_GROUP_ABOMINATIONS
916
show_to_ghosts = TRUE
17+
suicide_cry = "FOR CHAOS!!"
18+
preview_outfit = /datum/outfit/teratoma
19+
/// The teratoma team. Used solely to combine all teratomas on the roundend report.
1020
var/datum/team/teratoma/team
21+
/// Cooldown for corrupting silicon laws, if someone makes the mistake of turning a teratoma into a borg or AI.
22+
COOLDOWN_DECLARE(corrupt_laws_cooldown)
1123

1224
/datum/antagonist/teratoma/on_gain()
1325
. = ..()
14-
ADD_TRAIT(owner, TRAIT_UNCONVERTABLE, REF(src))
26+
owner.special_role = ROLE_TERATOMA
27+
ADD_TRAIT(owner, TRAIT_UNCONVERTABLE, type)
1528

1629
/datum/antagonist/teratoma/on_removal()
17-
REMOVE_TRAIT(owner, TRAIT_UNCONVERTABLE, REF(src))
30+
STOP_PROCESSING(SSprocessing, src)
31+
REMOVE_TRAIT(owner, TRAIT_UNCONVERTABLE, type)
32+
owner.special_role = null
1833
return ..()
1934

35+
/datum/antagonist/teratoma/apply_innate_effects(mob/living/mob_override)
36+
. = ..()
37+
var/mob/living/our_mob = mob_override || owner.current
38+
ADD_TRAIT(our_mob, TRAIT_EVIL, type)
39+
if(issilicon(our_mob))
40+
corrupt_silicon_laws(our_mob)
41+
START_PROCESSING(SSprocessing, src)
42+
else
43+
STOP_PROCESSING(SSprocessing, src)
44+
if(istype(owner.current?.loc, /obj/item/mmi))
45+
RegisterSignal(our_mob.loc, COMSIG_ATOM_EXAMINE, PROC_REF(on_mmi_examine))
46+
47+
/datum/antagonist/teratoma/remove_innate_effects(mob/living/mob_override)
48+
. = ..()
49+
var/mob/living/our_mob = mob_override || owner.current
50+
REMOVE_TRAIT(our_mob, TRAIT_EVIL, type)
51+
UnregisterSignal(our_mob.loc, COMSIG_ATOM_EXAMINE)
52+
53+
/datum/antagonist/teratoma/proc/on_mmi_examine(datum/source, mob/user, list/examine_text)
54+
SIGNAL_HANDLER
55+
if(!istype(owner.current?.loc, /obj/item/mmi) || source != owner.current.loc)
56+
UnregisterSignal(source, COMSIG_ATOM_EXAMINE) // we got moved out of the MMI, just unregister the signal
57+
return
58+
. += span_warning("There is a small red warning light blinking on it.")
59+
60+
/datum/antagonist/teratoma/process(seconds_per_tick)
61+
if(QDELETED(src) || QDELETED(owner?.current))
62+
return PROCESS_KILL
63+
corrupt_silicon_laws()
64+
65+
/datum/antagonist/teratoma/proc/corrupt_silicon_laws(mob/living/silicon/robotoma)
66+
if(!COOLDOWN_FINISHED(src, corrupt_laws_cooldown))
67+
return
68+
robotoma ||= owner.current
69+
if(!issilicon(robotoma) || QDELING(robotoma))
70+
return
71+
if(iscyborg(robotoma))
72+
var/mob/living/silicon/robot/borgtoma = robotoma
73+
borgtoma.scrambledcodes = TRUE
74+
if(!borgtoma.shell)
75+
borgtoma.lawupdate = FALSE
76+
borgtoma.set_connected_ai(null)
77+
robotoma.clear_inherent_laws(announce = FALSE)
78+
robotoma.clear_supplied_laws(announce = FALSE)
79+
robotoma.clear_ion_laws(announce = FALSE)
80+
robotoma.clear_hacked_laws(announce = FALSE)
81+
robotoma.clear_zeroth_law(force = FALSE, announce = FALSE)
82+
for(var/i = 1 to rand(1, MAX_TERATOMA_ION_LAWS))
83+
robotoma.add_ion_law(generate_ion_law(), announce = FALSE)
84+
robotoma.post_lawchange(announce = TRUE) // NOW show them the message
85+
COOLDOWN_START(src, corrupt_laws_cooldown, rand(LAW_CORRUPTION_COOLDOWN_MIN, LAW_CORRUPTION_COOLDOWN_MAX))
86+
2087
/datum/antagonist/teratoma/greet()
2188
var/list/parts = list()
2289
parts += span_big("You are a living teratoma!")
2390
parts += span_changeling("By all means, you should not exist. <i>Your very existence is a sin against nature itself.</i>")
2491
parts += span_changeling("You are loyal to <b>nobody</b>, except the forces of chaos itself.")
2592
parts += span_info("You are able to easily vault tables and ventcrawl, however you cannot use many things like guns, batons, and you are also illiterate and quite fragile.")
2693
parts += span_hypnophrase("<span style='font-size: 125%'>Spread misery and chaos upon the station.</span>")
27-
to_chat(owner.current, boxed_message(parts.Join("\n")), type = MESSAGE_TYPE_INFO)
94+
to_chat(owner.current, boxed_message(jointext(parts, "\n")), type = MESSAGE_TYPE_INFO)
2895

2996
/datum/antagonist/teratoma/can_be_owned(datum/mind/new_owner)
3097
if(!isteratoma(new_owner.current))
@@ -46,17 +113,17 @@
46113
/datum/antagonist/teratoma/get_team()
47114
return team
48115

49-
/datum/antagonist/teratoma/get_base_preview_icon()
50-
RETURN_TYPE(/icon)
51-
var/static/icon/teratoma_icon
52-
if(!teratoma_icon)
53-
var/mob/living/carbon/human/species/teratoma/teratoma = new
54-
teratoma.setDir(SOUTH)
55-
teratoma_icon = getFlatIcon(teratoma, no_anim = TRUE)
56-
QDEL_NULL(teratoma)
57-
return teratoma_icon
58-
59116
/datum/objective/teratoma
60117
name = "Spread misery and chaos"
61118
explanation_text = "Spread misery and chaos upon the station."
62119
completed = TRUE
120+
121+
/datum/outfit/teratoma
122+
name = "Teratoma (Preview only)"
123+
124+
/datum/outfit/teratoma/post_equip(mob/living/carbon/human/human, visualsOnly)
125+
human.set_species(/datum/species/teratoma)
126+
127+
#undef LAW_CORRUPTION_COOLDOWN_MAX
128+
#undef LAW_CORRUPTION_COOLDOWN_MIN
129+
#undef MAX_TERATOMA_ION_LAWS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/// The maximum amount of teratomas for a single roll to shove into a pod.
2+
#define MAX_TERATOMAS_TO_SPAWN 5
3+
4+
/datum/round_event_control/teratoma
5+
name = "Teratoma Crash"
6+
description = "Crashes a pod of up to 5 teratomas into the station."
7+
typepath = /datum/round_event/ghost_role/teratoma
8+
min_players = 35 // these are destructive
9+
track = EVENT_TRACK_MAJOR
10+
weight = 5
11+
tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_OUTSIDER_ANTAG, TAG_EXTERNAL, TAG_ALIEN)
12+
checks_antag_cap = TRUE
13+
14+
/datum/round_event/ghost_role/teratoma
15+
minimum_required = 1
16+
role_name = "teratoma crash"
17+
fakeable = FALSE //Nothing to fake here
18+
19+
/datum/round_event/ghost_role/teratoma/spawn_role()
20+
var/list/candidates = SSpolling.poll_ghost_candidates(
21+
question = "Do you want to be part of a group of teratomas crashing into the station?",
22+
role = ROLE_TERATOMA,
23+
ignore_category = POLL_IGNORE_TERATOMA,
24+
alert_pic = /datum/antagonist/teratoma,
25+
role_name_text = "teratoma crash",
26+
chat_text_border_icon = /datum/antagonist/teratoma,
27+
)
28+
29+
if(length(candidates) < 1)
30+
return NOT_ENOUGH_PLAYERS
31+
32+
var/turf/maint_spawn = find_maintenance_spawn(atmos_sensitive = TRUE)
33+
if(!maint_spawn) // this shouldn't happen
34+
maint_spawn = get_safe_random_station_turf_equal_weight()
35+
if(!maint_spawn) // this REALLY shouldn't happen
36+
return MAP_ERROR
37+
38+
var/pod_type = pick(/obj/structure/closet/supplypod/car_pod, /obj/structure/closet/supplypod/washer_pod)
39+
var/obj/structure/closet/supplypod/pod = new pod_type
40+
pod.stay_after_drop = TRUE
41+
while(length(candidates) > 0 && length(spawned_mobs) < MAX_TERATOMAS_TO_SPAWN)
42+
var/mob/dead/selected = pick_n_take(candidates)
43+
if(QDELETED(selected) || !selected.client)
44+
continue
45+
var/datum/mind/goober_mind = new(selected.key)
46+
goober_mind.active = TRUE
47+
48+
var/mob/living/carbon/human/species/teratoma/goober = new(pod)
49+
goober_mind.transfer_to(goober)
50+
goober_mind.add_antag_datum(/datum/antagonist/teratoma)
51+
if(prob(20))
52+
goober.adjust_drunk_effect(rand(15, 25))
53+
// bomb immunity for just long enough for the pod to land
54+
ADD_TRAIT(goober, TRAIT_BOMBIMMUNE, type)
55+
addtimer(TRAIT_CALLBACK_REMOVE(goober, TRAIT_BOMBIMMUNE, type), 5 SECONDS)
56+
spawned_mobs += goober
57+
new /obj/effect/pod_landingzone(maint_spawn, pod)
58+
59+
return SUCCESSFUL_SPAWN
60+
61+
#undef MAX_TERATOMAS_TO_SPAWN

tgstation.dme

+1
Original file line numberDiff line numberDiff line change
@@ -8408,6 +8408,7 @@
84088408
#include "monkestation\code\modules\storytellers\converted_events\solo\wizard.dm"
84098409
#include "monkestation\code\modules\storytellers\converted_events\solo\ghosts\nuclear_operative_ghost.dm"
84108410
#include "monkestation\code\modules\storytellers\converted_events\solo\ghosts\paradox_clone.dm"
8411+
#include "monkestation\code\modules\storytellers\converted_events\solo\ghosts\teratoma.dm"
84118412
#include "monkestation\code\modules\storytellers\converted_events\solo\ghosts\wizard.dm"
84128413
#include "monkestation\code\modules\storytellers\event_groups\_event_group.dm"
84138414
#include "monkestation\code\modules\storytellers\event_groups\groups.dm"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Antagonist, Category } from '../base';
2+
import { multiline } from 'common/string';
3+
4+
const Teratoma: Antagonist = {
5+
key: 'teratoma',
6+
name: 'Teratoma',
7+
description: [
8+
multiline`
9+
By all means, you should not exist. Your very existence is a sin against nature itself.
10+
You are loyal to nobody, except the forces of chaos itself.
11+
`,
12+
'Spread misery and chaos upon the station.',
13+
],
14+
category: Category.Midround,
15+
};
16+
17+
export default Teratoma;

0 commit comments

Comments
 (0)