diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index b24fdc145d2..3cbe66b4a41 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -275,3 +275,20 @@ GLOBAL_LIST_INIT(ai_employers, list(
#define OBJECTIVE_WEIGHT_HUGE 20
#define REVENANT_NAME_FILE "revenant_names.json"
+
+/// Antag panel groups
+#define ANTAG_GROUP_ABDUCTORS "Abductors"
+#define ANTAG_GROUP_ABOMINATIONS "Extradimensional Abominations"
+#define ANTAG_GROUP_ARACHNIDS "Arachnid Infestation"
+#define ANTAG_GROUP_ASHWALKERS "Ash Walkers"
+#define ANTAG_GROUP_BIOHAZARDS "Biohazards"
+#define ANTAG_GROUP_CLOWNOPS "Clown Operatives"
+#define ANTAG_GROUP_ERT "Emergency Response Team"
+#define ANTAG_GROUP_HORRORS "Eldritch Horrors"
+#define ANTAG_GROUP_LEVIATHANS "Spaceborne Leviathans"
+#define ANTAG_GROUP_NINJAS "Ninja Clan"
+#define ANTAG_GROUP_OVERGROWTH "Invasive Overgrowth"
+#define ANTAG_GROUP_PIRATES "Pirate Crew"
+#define ANTAG_GROUP_SYNDICATE "Syndicate"
+#define ANTAG_GROUP_WIZARDS "Wizard Federation"
+#define ANTAG_GROUP_XENOS "Xenomorph Infestation"
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index e9eed8e3efc..263e867756f 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -295,7 +295,6 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list
var/target_role_type = FALSE
var/human_check = TRUE
-
/datum/objective/protect/check_completion()
var/obj/item/organ/internal/brain/brain_target
if(human_check)
diff --git a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
index abbf89e0936..deb8403fa23 100644
--- a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
+++ b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm
@@ -14,31 +14,36 @@
return
else
ticks -= releasedelay
- var/turf/open/T = get_turf(src)
- if(istype(T))
- T.atmos_spawn_air("o2=5;plasma=5;TEMP=1000")
+ var/turf/open/tile = get_turf(src)
+ if(istype(tile))
+ tile.atmos_spawn_air("o2=5;plasma=5;TEMP=1000")
/obj/effect/anomaly/pyro/detonate()
INVOKE_ASYNC(src, PROC_REF(makepyroslime))
/obj/effect/anomaly/pyro/proc/makepyroslime()
- var/turf/open/T = get_turf(src)
- if(istype(T))
- T.atmos_spawn_air("o2=500;plasma=500;TEMP=1000") //Make it hot and burny for the new slime
- var/new_colour = pick("red", "orange")
- var/mob/living/simple_animal/slime/S = new(T, new_colour)
- S.rabid = TRUE
- S.amount_grown = SLIME_EVOLUTION_THRESHOLD
- S.Evolve()
- var/datum/action/innate/slime/reproduce/A = new
- A.Grant(S)
+ var/turf/open/tile = get_turf(src)
+ if(istype(tile))
+ tile.atmos_spawn_air("o2=500;plasma=500;TEMP=1000") //Make it hot and burny for the new slime
- var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 10 SECONDS, S, POLL_IGNORE_PYROSLIME)
- if(LAZYLEN(candidates))
- var/mob/dead/observer/chosen = pick(candidates)
- S.key = chosen.key
- S.mind.special_role = ROLE_PYROCLASTIC_SLIME
- var/policy = get_policy(ROLE_PYROCLASTIC_SLIME)
- if (policy)
- to_chat(S, policy)
- S.log_message("was made into a slime by pyroclastic anomaly", LOG_GAME)
+ var/new_colour = pick("red", "orange")
+ var/mob/living/simple_animal/slime/pyro = new(tile, new_colour)
+ pyro.rabid = TRUE
+ pyro.amount_grown = SLIME_EVOLUTION_THRESHOLD
+ pyro.Evolve()
+ var/datum/action/innate/slime/reproduce/repro_action = new
+ repro_action.Grant(pyro)
+
+ var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENCE, null, 10 SECONDS, pyro, POLL_IGNORE_PYROSLIME)
+ if(!LAZYLEN(candidates))
+ return
+
+ var/mob/dead/observer/chosen = pick(candidates)
+ pyro.key = chosen.key
+ pyro.mind.special_role = ROLE_PYROCLASTIC_SLIME
+ pyro.mind.add_antag_datum(/datum/antagonist/pyro_slime)
+ var/policy = get_policy(ROLE_PYROCLASTIC_SLIME)
+
+ if (policy)
+ to_chat(pyro, policy)
+ pyro.log_message("was made into a slime by pyroclastic anomaly", LOG_GAME)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index f41e98b611b..2432ef3aa61 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -73,14 +73,92 @@ GLOBAL_LIST_EMPTY(antagonists)
owner = null
return ..()
+/datum/antagonist/Topic(href,href_list)
+ if(!check_rights(R_ADMIN))
+ return
+
+ //Some commands might delete/modify this datum clearing or changing owner
+ var/datum/mind/persistent_owner = owner
+
+ var/commands = get_admin_commands()
+ var/admin_command = href_list["command"]
+ if(!(admin_command in commands))
+ return
+ var/datum/callback/call_async = commands[admin_command]
+ call_async.Invoke(usr)
+ persistent_owner.traitor_panel()
+
+//This one is created by admin tools for custom objectives
+/datum/antagonist/custom
+ antagpanel_category = "Custom"
+ show_name_in_check_antagonists = TRUE //They're all different
+ var/datum/team/custom_team
+
+/datum/antagonist/custom/create_team(datum/team/team)
+ custom_team = team
+
+/datum/antagonist/custom/get_team()
+ return custom_team
+
+/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
+ var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
+ if(!custom_name)
+ return
+ name = custom_name
+ ..()
+
+///ANTAGONIST UI STUFF
+
+/datum/antagonist/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, ui_name, name)
+ ui.open()
+
+/datum/antagonist/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/antagonist/ui_static_data(mob/user)
+ var/list/data = list()
+ data["antag_name"] = name
+ data["objectives"] = get_objectives()
+ return data
+
+//button for antags to review their descriptions/info
+/datum/action/antag_info
+ name = "Open Antag Information:"
+ button_icon_state = "round_end"
+ show_to_observers = FALSE
+
+/datum/action/antag_info/New(Target)
+ . = ..()
+ name += " [target]"
+
+/datum/action/antag_info/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+
+ target.ui_interact(owner)
+
+/datum/action/antag_info/IsAvailable(feedback = FALSE)
+ if(!target)
+ stack_trace("[type] was used without a target antag datum!")
+ return FALSE
+ . = ..()
+ if(!.)
+ return
+ if(!owner.mind || !(target in owner.mind.antag_datums))
+ return FALSE
+ return TRUE
+
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
. = TRUE
var/datum/mind/tested = new_owner || owner
if(tested.has_antag_datum(type))
return FALSE
- for(var/i in tested.antag_datums)
- var/datum/antagonist/A = i
- if(is_type_in_typecache(src, A.typecache_datum_blacklist))
+ for(var/datum/antagonist/badguy as anything in tested.antag_datums)
+ if(is_type_in_typecache(src, badguy.typecache_datum_blacklist))
return FALSE
//This will be called in add_antag_datum before owner assignment.
@@ -171,12 +249,12 @@ GLOBAL_LIST_EMPTY(antagonists)
* Proc that checks the sent mob aganst the banlistfor this antagonist.
* Returns FALSE if no mob is sent, or the mob is not found to be banned.
*
- * * mob/M: The mob that you are looking for on the banlist.
+ * * mob/player: The mob that you are looking for on the banlist.
*/
-/datum/antagonist/proc/is_banned(mob/M)
- if(!M)
+/datum/antagonist/proc/is_banned(mob/player)
+ if(!player)
return FALSE
- . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M))
+ . = (is_banned_from(player.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(player))
/**
* Proc that replaces a player who cannot play a specific antagonist due to being banned via a poll, and alerts the player of their being on the banlist.
@@ -332,9 +410,9 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/antag_panel_data()
return ""
-/datum/antagonist/proc/enabled_in_preferences(datum/mind/M)
+/datum/antagonist/proc/enabled_in_preferences(datum/mind/noggin)
if(job_rank)
- if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special))
+ if(noggin.current && noggin.current.client && (job_rank in noggin.current.client.prefs.be_special))
return TRUE
else
return FALSE
@@ -379,21 +457,6 @@ GLOBAL_LIST_EMPTY(antagonists)
return finish_preview_icon(render_preview_outfit(preview_outfit))
-/datum/antagonist/Topic(href,href_list)
- if(!check_rights(R_ADMIN))
- return
-
- //Some commands might delete/modify this datum clearing or changing owner
- var/datum/mind/persistent_owner = owner
-
- var/commands = get_admin_commands()
- for(var/admin_command in commands)
- if(href_list["command"] == admin_command)
- var/datum/callback/C = commands[admin_command]
- C.Invoke(usr)
- persistent_owner.traitor_panel()
- return
-
/datum/antagonist/proc/edit_memory(mob/user)
var/new_memo = tgui_input_text(user, "Write a new memory", "Antag Memory", antag_memory, multiline = TRUE)
if (isnull(new_memo))
@@ -431,37 +494,6 @@ GLOBAL_LIST_EMPTY(antagonists)
SET_PLANE_EXPLICIT(hud, ABOVE_GAME_PLANE, hud_loc)
return hud
-//This one is created by admin tools for custom objectives
-/datum/antagonist/custom
- antagpanel_category = "Custom"
- show_name_in_check_antagonists = TRUE //They're all different
- var/datum/team/custom_team
-
-/datum/antagonist/custom/create_team(datum/team/team)
- custom_team = team
-
-/datum/antagonist/custom/get_team()
- return custom_team
-
-/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
- var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
- if(custom_name)
- name = custom_name
- else
- return
- ..()
-
-///ANTAGONIST UI STUFF
-
-/datum/antagonist/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, ui_name, name)
- ui.open()
-
-/datum/antagonist/ui_state(mob/user)
- return GLOB.always_state
-
///generic helper to send objectives as data through tgui.
/datum/antagonist/proc/get_objectives()
var/objective_count = 1
@@ -477,39 +509,7 @@ GLOBAL_LIST_EMPTY(antagonists)
objective_count++
return objective_data
-/datum/antagonist/ui_static_data(mob/user)
- var/list/data = list()
- data["antag_name"] = name
- data["objectives"] = get_objectives()
- return data
+/// Used to create objectives for the antagonist.
+/datum/antagonist/proc/forge_objectives()
+ return
-//button for antags to review their descriptions/info
-
-/datum/action/antag_info
- name = "Open Antag Information:"
- button_icon_state = "round_end"
- show_to_observers = FALSE
-
-/datum/action/antag_info/New(Target)
- . = ..()
- name += " [target]"
-
-/datum/action/antag_info/Trigger(trigger_flags)
- . = ..()
- if(!.)
- return
-
- target.ui_interact(owner)
-
-/datum/action/antag_info/IsAvailable(feedback = FALSE)
- if(!target)
- stack_trace("[type] was used without a target antag datum!")
- return FALSE
- . = ..()
- if(!.)
- return
- if(!owner.mind)
- return FALSE
- if(!(target in owner.mind.antag_datums))
- return FALSE
- return TRUE
diff --git a/code/modules/antagonists/abductor/abductee/abductee.dm b/code/modules/antagonists/abductor/abductee/abductee.dm
index c42ac8d8330..f1e657a558e 100644
--- a/code/modules/antagonists/abductor/abductee/abductee.dm
+++ b/code/modules/antagonists/abductor/abductee/abductee.dm
@@ -7,7 +7,7 @@
/datum/antagonist/abductee
name = "\improper Abductee"
roundend_category = "abductees"
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_ABDUCTORS
antag_hud_name = "abductee"
/datum/antagonist/abductee/on_gain()
@@ -21,5 +21,5 @@
/datum/antagonist/abductee/proc/give_objective()
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
- var/datum/objective/abductee/O = new objtype()
- objectives += O
+ var/datum/objective/abductee/objective = new objtype()
+ objectives += objective
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index b3029a10ddb..0699f28c091 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -3,7 +3,7 @@
/datum/antagonist/abductor
name = "\improper Abductor"
roundend_category = "abductors"
- antagpanel_category = "Abductor"
+ antagpanel_category = ANTAG_GROUP_ABDUCTORS
job_rank = ROLE_ABDUCTOR
antag_hud_name = "abductor"
show_in_antagpanel = FALSE //should only show subtypes
diff --git a/code/modules/antagonists/ashwalker/ashwalker.dm b/code/modules/antagonists/ashwalker/ashwalker.dm
index 27e329788fd..773e7894f37 100644
--- a/code/modules/antagonists/ashwalker/ashwalker.dm
+++ b/code/modules/antagonists/ashwalker/ashwalker.dm
@@ -8,7 +8,7 @@
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
prevent_roundtype_conversion = FALSE
- antagpanel_category = "Ash Walkers"
+ antagpanel_category = ANTAG_GROUP_ASHWALKERS
suicide_cry = "I HAVE NO IDEA WHAT THIS THING DOES!!"
count_against_dynamic_roll_chance = FALSE
var/datum/team/ashwalkers/ashie_team
diff --git a/code/modules/antagonists/battlecruiser/battlecruiser.dm b/code/modules/antagonists/battlecruiser/battlecruiser.dm
new file mode 100644
index 00000000000..8560300698f
--- /dev/null
+++ b/code/modules/antagonists/battlecruiser/battlecruiser.dm
@@ -0,0 +1,63 @@
+/datum/team/battlecruiser
+ name = "\improper Battlecruiser Crew"
+ member_name = "crewmember"
+ /// The central objective of this battlecruiser
+ var/core_objective = /datum/objective/nuclear
+ /// The assigned nuke of this team
+ var/obj/machinery/nuclearbomb/nuke
+
+/datum/team/battlecruiser/proc/update_objectives()
+ if(core_objective)
+ var/datum/objective/objective = new core_objective()
+ objective.team = src
+ objectives += objective
+
+/datum/antagonist/battlecruiser
+ name = "Battlecruiser Crewmember"
+ show_to_ghosts = TRUE
+ roundend_category = "battlecruiser syndicate operatives"
+ suicide_cry = "FOR THE SYNDICATE!!!"
+ antag_hud_name = "battlecruiser_crew"
+ antagpanel_category = ANTAG_GROUP_SYNDICATE
+ job_rank = ROLE_BATTLECRUISER_CREW
+ /// Team to place the crewmember on.
+ var/datum/team/battlecruiser/battlecruiser_team
+
+/datum/antagonist/battlecruiser/get_team()
+ return battlecruiser_team
+
+/datum/antagonist/battlecruiser/greet()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0, use_reverb = FALSE)
+ to_chat(owner, span_big("You are a [name]!"))
+ owner.announce_objectives()
+
+/datum/antagonist/battlecruiser/ally
+ name = "Battlecruiser Ally"
+ show_to_ghosts = FALSE
+
+/datum/antagonist/battlecruiser/captain
+ name = "Battlecruiser Captain"
+ antag_hud_name = "battlecruiser_lead"
+ job_rank = ROLE_BATTLECRUISER_CAPTAIN
+
+/datum/antagonist/battlecruiser/create_team(datum/team/battlecruiser/team)
+ if(!team)
+ return
+ if(!istype(team))
+ stack_trace("Wrong team type passed to [type] initialization.")
+ battlecruiser_team = team
+
+/datum/antagonist/battlecruiser/apply_innate_effects(mob/living/mob_override)
+ add_team_hud(mob_override || owner.current, /datum/antagonist/battlecruiser)
+
+/datum/antagonist/battlecruiser/on_gain()
+ if(!battlecruiser_team)
+ return ..()
+
+ objectives |= battlecruiser_team.objectives
+ if(battlecruiser_team.nuke)
+ var/obj/machinery/nuclearbomb/nuke = battlecruiser_team.nuke
+ antag_memory += "[nuke] Code: [nuke.r_code]
"
+ owner.add_memory(/datum/memory/key/nuke_code, nuclear_code = nuke.r_code)
+ to_chat(owner, "The nuclear authorization code is: [nuke.r_code]")
+ return ..()
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob_antag.dm
similarity index 97%
rename from code/modules/antagonists/blob/blob.dm
rename to code/modules/antagonists/blob/blob_antag.dm
index e465bc85509..63033c6e3ee 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob_antag.dm
@@ -1,8 +1,9 @@
/datum/antagonist/blob
name = "\improper Blob"
roundend_category = "blobs"
- antagpanel_category = "Biohazards"
+ antagpanel_category = ANTAG_GROUP_BIOHAZARDS
show_to_ghosts = TRUE
+ show_in_antagpanel = FALSE
job_rank = ROLE_BLOB
ui_name = "AntagInfoBlob"
/// Action to release a blob infection
@@ -140,6 +141,8 @@
/// A subtype of blob meant to represent the infective version.
/datum/antagonist/blob/infection
+ name = "\improper Blob Infection"
+ show_in_antagpanel = TRUE
job_rank = ROLE_BLOB_INFECTION
/datum/antagonist/blob/infection/get_preview_icon()
diff --git a/code/modules/antagonists/blob/blob_minion.dm b/code/modules/antagonists/blob/blob_minion.dm
new file mode 100644
index 00000000000..9ba8e7089b9
--- /dev/null
+++ b/code/modules/antagonists/blob/blob_minion.dm
@@ -0,0 +1,42 @@
+/datum/antagonist/blob_minion
+ name = "\improper Blob Minion"
+ antagpanel_category = ANTAG_GROUP_BIOHAZARDS
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ show_in_antagpanel = FALSE
+ /// The blob core that this minion is attached to
+ var/datum/weakref/overmind
+
+/datum/antagonist/blob_minion/New(mob/camera/blob/overmind)
+ . = ..()
+ src.overmind = WEAKREF(overmind)
+
+/datum/antagonist/blob_minion/on_gain()
+ forge_objectives()
+ . = ..()
+
+/datum/antagonist/blob_minion/greet()
+ . = ..()
+ owner.announce_objectives()
+/datum/objective/blob_minion
+ name = "protect the blob core"
+ explanation_text = "Protect the blob core at all costs."
+ var/datum/weakref/overmind
+
+/datum/objective/blob_minion/check_completion()
+ var/mob/camera/blob/resolved_overmind = overmind.resolve()
+ if(!resolved_overmind)
+ return FALSE
+ return resolved_overmind.stat != DEAD
+
+/datum/antagonist/blob_minion/forge_objectives()
+ var/datum/objective/blob_minion/objective = new
+ objective.owner = owner
+ objective.overmind = overmind
+ objectives += objective
+
+/datum/antagonist/blob_minion/blobbernaut
+ name = "\improper Blobbernaut"
+
+/datum/antagonist/blob_minion/blob_zombie
+ name = "\improper Blob Zombie"
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index ff9b03559a0..40ee7a47043 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -633,9 +633,7 @@
add_new_profile(owner.current)
-
-/// Generate objectives for our changeling.
-/datum/antagonist/changeling/proc/forge_objectives()
+/datum/antagonist/changeling/forge_objectives()
//OBJECTIVES - random traitor objectives. Unique objectives "steal brain" and "identity theft".
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/clown_ops/clownop.dm
similarity index 97%
rename from code/modules/antagonists/nukeop/clownop.dm
rename to code/modules/antagonists/clown_ops/clownop.dm
index e1a1ae49e18..8b308970911 100644
--- a/code/modules/antagonists/nukeop/clownop.dm
+++ b/code/modules/antagonists/clown_ops/clownop.dm
@@ -2,7 +2,7 @@
/datum/antagonist/nukeop/clownop
name = ROLE_CLOWN_OPERATIVE
roundend_category = "clown operatives"
- antagpanel_category = "ClownOp"
+ antagpanel_category = ANTAG_GROUP_CLOWNOPS
nukeop_outfit = /datum/outfit/syndicate/clownop
suicide_cry = "HAPPY BIRTHDAY!!"
@@ -43,7 +43,7 @@
/datum/antagonist/nukeop/leader/clownop
name = "Clown Operative Leader"
roundend_category = "clown operatives"
- antagpanel_category = "ClownOp"
+ antagpanel_category = ANTAG_GROUP_CLOWNOPS
nukeop_outfit = /datum/outfit/syndicate/clownop/leader
challengeitem = /obj/item/nuclear_challenge/clownops
suicide_cry = "HAPPY BIRTHDAY!!"
diff --git a/code/modules/antagonists/cult/constructs.dm b/code/modules/antagonists/cult/constructs.dm
new file mode 100644
index 00000000000..3da08a54402
--- /dev/null
+++ b/code/modules/antagonists/cult/constructs.dm
@@ -0,0 +1,6 @@
+/datum/antagonist/cult/construct
+ name = "\improper Cult Construct"
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ antagpanel_category = ANTAG_GROUP_HORRORS
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index 2be20b88ce1..9d5af7ab83e 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -1,7 +1,7 @@
/datum/antagonist/disease
name = "Sentient Disease"
roundend_category = "diseases"
- antagpanel_category = "Biohazards"
+ antagpanel_category = ANTAG_GROUP_BIOHAZARDS
show_to_ghosts = TRUE
var/disease_name = ""
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 744089dc72a..ab69df5a61d 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -9,6 +9,7 @@
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
+ antagpanel_category = ANTAG_GROUP_ERT
suicide_cry = "FOR NANOTRASEN!!"
count_against_dynamic_roll_chance = FALSE
var/datum/team/ert/ert_team
@@ -222,7 +223,7 @@
role = "Synthetic Bounty Hunter"
outfit = /datum/outfit/bountysynth/ert
-/datum/antagonist/ert/proc/forge_objectives()
+/datum/antagonist/ert/forge_objectives()
if(ert_team)
objectives |= ert_team.objectives
diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm
index 6cbc7530481..5bb0f893f54 100644
--- a/code/modules/antagonists/fugitive/fugitive.dm
+++ b/code/modules/antagonists/fugitive/fugitive.dm
@@ -41,7 +41,7 @@
forge_objectives()
. = ..()
-/datum/antagonist/fugitive/proc/forge_objectives() //this isn't the actual survive objective because it's about who in the team survives
+/datum/antagonist/fugitive/forge_objectives() //this isn't the actual survive objective because it's about who in the team survives
var/datum/objective/survive = new /datum/objective
survive.owner = owner
survive.explanation_text = "Avoid capture from the fugitive hunters."
diff --git a/code/modules/antagonists/fugitive/hunter.dm b/code/modules/antagonists/fugitive/hunter.dm
index 62773196478..1d267b90624 100644
--- a/code/modules/antagonists/fugitive/hunter.dm
+++ b/code/modules/antagonists/fugitive/hunter.dm
@@ -15,7 +15,7 @@
forge_objectives()
. = ..()
-/datum/antagonist/fugitive_hunter/proc/forge_objectives() //this isn't an actual objective because it's about round end rosters
+/datum/antagonist/fugitive_hunter/forge_objectives() //this isn't an actual objective because it's about round end rosters
var/datum/objective/capture = new /datum/objective
capture.owner = owner
capture.explanation_text = "Capture the fugitives in the station and put them into the bluespace capture machine on your ship."
diff --git a/code/modules/antagonists/greentext/greentext.dm b/code/modules/antagonists/greentext/greentext.dm
index cfc87bdccc5..d06977d2a63 100644
--- a/code/modules/antagonists/greentext/greentext.dm
+++ b/code/modules/antagonists/greentext/greentext.dm
@@ -5,7 +5,7 @@
suicide_cry = "FOR THE GREENTEXT!!" // This can never actually show up, but not including it is a missed opportunity
count_against_dynamic_roll_chance = FALSE
-/datum/antagonist/greentext/proc/forge_objectives()
+/datum/antagonist/greentext/forge_objectives()
var/datum/objective/succeed_objective = new /datum/objective("Succeed")
succeed_objective.completed = TRUE //YES!
succeed_objective.owner = owner
diff --git a/code/modules/antagonists/heretic/heretic_monsters.dm b/code/modules/antagonists/heretic/heretic_monsters.dm
index 6651f6e082c..3f3dd320357 100644
--- a/code/modules/antagonists/heretic/heretic_monsters.dm
+++ b/code/modules/antagonists/heretic/heretic_monsters.dm
@@ -2,7 +2,7 @@
/datum/antagonist/heretic_monster
name = "\improper Eldritch Horror"
roundend_category = "Heretics"
- antagpanel_category = "Heretic Beast"
+ antagpanel_category = ANTAG_GROUP_HORRORS
antag_moodlet = /datum/mood_event/heretics
job_rank = ROLE_HERETIC
antag_hud_name = "heretic_beast"
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 79d33039512..9998e1939b8 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -26,7 +26,7 @@
if(L.has_quirk(/datum/quirk/nonviolent))
ADD_TRAIT(L, TRAIT_PACIFISM, ROUNDSTART_TRAIT)
-/datum/antagonist/highlander/proc/forge_objectives()
+/datum/antagonist/highlander/forge_objectives()
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.set_target(new /datum/objective_item/steal/nukedisc)
diff --git a/code/modules/antagonists/malf_ai/datum_malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
similarity index 100%
rename from code/modules/antagonists/malf_ai/datum_malf_ai.dm
rename to code/modules/antagonists/malf_ai/malf_ai.dm
diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm
index 62829c34050..7cdf6019719 100644
--- a/code/modules/antagonists/morph/morph_antag.dm
+++ b/code/modules/antagonists/morph/morph_antag.dm
@@ -3,4 +3,5 @@
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
show_in_antagpanel = FALSE
+ antagpanel_category = ANTAG_GROUP_ABOMINATIONS
ui_name = "AntagInfoMorph"
diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm
index 8a10c662ad1..10dfd662dab 100644
--- a/code/modules/antagonists/nightmare/nightmare.dm
+++ b/code/modules/antagonists/nightmare/nightmare.dm
@@ -1,6 +1,6 @@
/datum/antagonist/nightmare
name = "\improper Nightmare"
- antagpanel_category = "Nightmare"
+ antagpanel_category = ANTAG_GROUP_ABOMINATIONS
job_rank = ROLE_NIGHTMARE
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
@@ -9,8 +9,39 @@
suicide_cry = "FOR THE DARKNESS!!"
preview_outfit = /datum/outfit/nightmare
+/datum/antagonist/nightmare/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/antagonist/nightmare/on_gain()
+ forge_objectives()
+ . = ..()
+
/datum/outfit/nightmare
name = "Nightmare (Preview only)"
/datum/outfit/nightmare/post_equip(mob/living/carbon/human/human, visualsOnly)
human.set_species(/datum/species/shadow/nightmare)
+
+/datum/objective/nightmare_fluff
+
+/datum/objective/nightmare_fluff/New()
+ var/list/explanation_texts = list(
+ "Consume the last glimmer of light from the space station.",
+ "Bring judgment upon the daywalkers.",
+ "Extinguish the flame of this hellscape.",
+ "Reveal the true nature of the shadows.",
+ "From the shadows, all shall perish.",
+ "Conjure nightfall by blade or by flame.",
+ "Bring the darkness to the light."
+ )
+ explanation_text = pick(explanation_texts)
+ ..()
+
+/datum/objective/nightmare_fluff/check_completion()
+ return owner.current.stat != DEAD
+
+/datum/antagonist/nightmare/forge_objectives()
+ var/datum/objective/nightmare_fluff/objective = new
+ objective.owner = owner
+ objectives += objective
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index be217191f1c..021838334ff 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -1,7 +1,7 @@
/datum/antagonist/nukeop
name = ROLE_NUCLEAR_OPERATIVE
roundend_category = "syndicate operatives" //just in case
- antagpanel_category = "NukeOp"
+ antagpanel_category = ANTAG_GROUP_SYNDICATE
job_rank = ROLE_OPERATIVE
antag_hud_name = "synd"
antag_moodlet = /datum/mood_event/focused
@@ -118,7 +118,7 @@
else
to_chat(owner, "Unfortunately the syndicate was unable to provide you with nuclear authorization code.")
-/datum/antagonist/nukeop/proc/forge_objectives()
+/datum/antagonist/nukeop/forge_objectives()
if(nuke_team)
objectives |= nuke_team.objectives
diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/obsessed/obsessed.dm
similarity index 99%
rename from code/modules/antagonists/creep/creep.dm
rename to code/modules/antagonists/obsessed/obsessed.dm
index ce098bc5f07..dc64a4bc12b 100644
--- a/code/modules/antagonists/creep/creep.dm
+++ b/code/modules/antagonists/obsessed/obsessed.dm
@@ -71,7 +71,7 @@
carried_item.add_mob_blood(H)//Oh yes, there will be blood...
H.regenerate_icons()
-/datum/antagonist/obsessed/proc/forge_objectives(datum/mind/obsessionmind)
+/datum/antagonist/obsessed/forge_objectives(datum/mind/obsessionmind)
var/list/objectives_left = list("spendtime", "polaroid", "hug")
var/datum/objective/assassinate/obsessed/kill = new
kill.owner = owner
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index 14aa971cdc2..d499a8034d8 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -2,6 +2,7 @@
name = "\improper Space Pirate"
job_rank = ROLE_TRAITOR
roundend_category = "space pirates"
+ antagpanel_category = ANTAG_GROUP_PIRATES
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
suicide_cry = "FOR ME MATEYS!!"
diff --git a/code/modules/antagonists/pyro_slime/pyro_slime.dm b/code/modules/antagonists/pyro_slime/pyro_slime.dm
new file mode 100644
index 00000000000..aed278d261d
--- /dev/null
+++ b/code/modules/antagonists/pyro_slime/pyro_slime.dm
@@ -0,0 +1,26 @@
+/datum/antagonist/pyro_slime
+ name = "\improper Pyroclastic Anomaly"
+ antagpanel_category = ANTAG_GROUP_ABOMINATIONS
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+
+/datum/antagonist/pyro_slime/on_gain()
+ forge_objectives()
+ . = ..()
+
+/datum/antagonist/pyro_slime/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/objective/pyro_slime
+ explanation_text = "All I know fire. I speak in tongues of flame. Why is everyone so cold?"
+
+/datum/objective/pyro_slime/check_completion()
+ return owner.current.stat != DEAD
+
+/datum/antagonist/pyro_slime/forge_objectives()
+ var/datum/objective/pyro_slime/objective = new
+ objective.owner = owner
+ objectives += objective
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 35c8032a201..8f15508989c 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -3,22 +3,23 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ antagpanel_category = ANTAG_GROUP_HORRORS
/datum/antagonist/revenant/greet()
. = ..()
owner.announce_objectives()
-/datum/antagonist/revenant/proc/forge_objectives()
- var/datum/objective/revenant/objective = new
- objective.owner = owner
- objectives += objective
- var/datum/objective/revenant_fluff/objective2 = new
- objective2.owner = owner
- objectives += objective2
-
/datum/antagonist/revenant/on_gain()
forge_objectives()
. = ..()
/datum/antagonist/revenant/get_preview_icon()
return finish_preview_icon(icon('icons/mob/simple/mob.dmi', "revenant_idle"))
+
+/datum/antagonist/revenant/forge_objectives()
+ var/datum/objective/revenant/objective = new
+ objective.owner = owner
+ objectives += objective
+ var/datum/objective/revenant_fluff/objective2 = new
+ objective2.owner = owner
+ objectives += objective2
diff --git a/code/modules/antagonists/revolution/enemy_of_the_revolution.dm b/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
index f26387a3ef5..93a205a02cd 100644
--- a/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
+++ b/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
@@ -8,7 +8,7 @@
show_in_antagpanel = FALSE
suicide_cry = "FOR NANOTRASEN, NOW AND FOREVER!!"
-/datum/antagonist/enemy_of_the_revolution/proc/forge_objectives()
+/datum/antagonist/enemy_of_the_revolution/forge_objectives()
var/datum/objective/survive/survive = new
survive.owner = owner
survive.explanation_text = "The station has been overrun by revolutionaries, stay alive until the end."
diff --git a/code/modules/antagonists/revolution/enemy_of_the_state.dm b/code/modules/antagonists/revolution/enemy_of_the_state.dm
index 34d815ab9ad..837646c76d7 100644
--- a/code/modules/antagonists/revolution/enemy_of_the_state.dm
+++ b/code/modules/antagonists/revolution/enemy_of_the_state.dm
@@ -10,7 +10,7 @@
hijack_speed = 2 //not like they have much to do
suicide_cry = "FOR THE ETERNAL REVOLUTION!!"
-/datum/antagonist/enemy_of_the_state/proc/forge_objectives()
+/datum/antagonist/enemy_of_the_state/forge_objectives()
var/datum/objective/exile/exile_choice = new
exile_choice.owner = owner
diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm
index 530d4f53093..d9d6608537d 100644
--- a/code/modules/antagonists/space_dragon/carp_rift.dm
+++ b/code/modules/antagonists/space_dragon/carp_rift.dm
@@ -237,6 +237,8 @@
ckey_list += user.ckey
newcarp.key = user.key
newcarp.set_name()
+ var/datum/antagonist/space_carp/carp_antag = new(src)
+ newcarp.mind.add_antag_datum(carp_antag)
dragon.carp += newcarp.mind
to_chat(newcarp, span_boldwarning("You have arrived in order to assist the space dragon with securing the rifts. Do not jeopardize the mission, and protect the rifts at all costs!"))
carp_stored--
diff --git a/code/modules/antagonists/space_dragon/space_carp.dm b/code/modules/antagonists/space_dragon/space_carp.dm
new file mode 100644
index 00000000000..0d06ea3991d
--- /dev/null
+++ b/code/modules/antagonists/space_dragon/space_carp.dm
@@ -0,0 +1,36 @@
+/datum/antagonist/space_carp
+ name = "\improper Space Carp"
+ antagpanel_category = ANTAG_GROUP_LEVIATHANS
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ /// The rift to protect
+ var/datum/weakref/rift
+
+/datum/antagonist/space_carp/New(obj/structure/carp_rift/rift)
+ . = ..()
+ src.rift = WEAKREF(rift)
+
+/datum/antagonist/space_carp/on_gain()
+ forge_objectives()
+ . = ..()
+
+/datum/antagonist/space_carp/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/objective/space_carp
+ explanation_text = "Protect the rift to summon more carps."
+ var/datum/weakref/rift
+
+/datum/objective/space_carp/check_completion()
+ if(!rift.resolve())
+ return FALSE
+ return TRUE
+
+/datum/antagonist/space_carp/forge_objectives()
+ var/datum/objective/space_carp/objective = new
+ objective.owner = owner
+ objective.rift = rift
+ objectives += objective
diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm
index f208d4c7f27..5abceb35f0a 100644
--- a/code/modules/antagonists/space_dragon/space_dragon.dm
+++ b/code/modules/antagonists/space_dragon/space_dragon.dm
@@ -1,9 +1,9 @@
/datum/antagonist/space_dragon
name = "\improper Space Dragon"
roundend_category = "space dragons"
- antagpanel_category = "Space Dragon"
+ antagpanel_category = ANTAG_GROUP_LEVIATHANS
job_rank = ROLE_SPACE_DRAGON
- show_in_antagpanel = TRUE
+ show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
/// All space carps created by this antagonist space dragon
@@ -35,8 +35,8 @@
owner.announce_objectives()
SEND_SOUND(owner.current, sound('sound/magic/demon_attack1.ogg'))
-/datum/antagonist/space_dragon/proc/forge_objectives()
- var/datum/objective/summon_carp/summon = new()
+/datum/antagonist/space_dragon/forge_objectives()
+ var/datum/objective/summon_carp/summon = new
summon.dragon = src
objectives += summon
diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm
index e2da123a237..130741ab930 100644
--- a/code/modules/antagonists/space_ninja/space_ninja.dm
+++ b/code/modules/antagonists/space_ninja/space_ninja.dm
@@ -1,6 +1,6 @@
/datum/antagonist/ninja
name = "\improper Space Ninja"
- antagpanel_category = "Space Ninja"
+ antagpanel_category = ANTAG_GROUP_NINJAS
job_rank = ROLE_NINJA
antag_hud_name = "space_ninja"
hijack_speed = 1
diff --git a/code/modules/antagonists/spiders/spiders.dm b/code/modules/antagonists/spiders/spiders.dm
new file mode 100644
index 00000000000..6d0b86d24d7
--- /dev/null
+++ b/code/modules/antagonists/spiders/spiders.dm
@@ -0,0 +1,37 @@
+/datum/antagonist/spider
+ name = "\improper Spider"
+ antagpanel_category = ANTAG_GROUP_ARACHNIDS
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ /// Orders given to us by the queen
+ var/directive
+
+/datum/antagonist/spider/New(directive)
+ . = ..()
+ src.directive = directive
+
+/datum/antagonist/spider/on_gain()
+ forge_objectives(directive)
+ . = ..()
+
+/datum/antagonist/spider/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/objective/spider
+ explanation_text = "Spread the infestation."
+
+/datum/objective/spider/New(directive)
+ ..()
+ if(directive)
+ explanation_text = "Your queen has given you a directive! Follow it at all costs: [directive]"
+
+/datum/objective/spider/check_completion()
+ return owner.current.stat != DEAD
+
+/datum/antagonist/spider/forge_objectives()
+ var/datum/objective/spider/objective = new(directive)
+ objective.owner = owner
+ objectives += objective
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 66ffe4eaf0a..e4642efcbab 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -5,7 +5,7 @@
suicide_cry = "FOR MYSELF!!"
var/greet_message = ""
-/datum/antagonist/survivalist/proc/forge_objectives()
+/datum/antagonist/survivalist/forge_objectives()
var/datum/objective/survive/survive = new
survive.owner = owner
objectives += survive
diff --git a/code/modules/antagonists/valentines/heartbreaker.dm b/code/modules/antagonists/valentines/heartbreaker.dm
index 11d8b17c069..30e3147e526 100644
--- a/code/modules/antagonists/valentines/heartbreaker.dm
+++ b/code/modules/antagonists/valentines/heartbreaker.dm
@@ -5,7 +5,7 @@
show_name_in_check_antagonists = TRUE
suicide_cry = "FOR LONELINESS!!"
-/datum/antagonist/heartbreaker/proc/forge_objectives()
+/datum/antagonist/heartbreaker/forge_objectives()
var/datum/objective/martyr/normiesgetout = new
normiesgetout.owner = owner
objectives += normiesgetout
diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm
index 67f824e2077..e76292e3d96 100644
--- a/code/modules/antagonists/valentines/valentine.dm
+++ b/code/modules/antagonists/valentines/valentine.dm
@@ -7,7 +7,7 @@
var/datum/mind/date
count_against_dynamic_roll_chance = FALSE
-/datum/antagonist/valentine/proc/forge_objectives()
+/datum/antagonist/valentine/forge_objectives()
var/datum/objective/protect/protect_objective = new /datum/objective/protect
protect_objective.owner = owner
protect_objective.target = date
diff --git a/code/modules/antagonists/venus_human_trap/venus_human_trap.dm b/code/modules/antagonists/venus_human_trap/venus_human_trap.dm
new file mode 100644
index 00000000000..c84f20d0596
--- /dev/null
+++ b/code/modules/antagonists/venus_human_trap/venus_human_trap.dm
@@ -0,0 +1,26 @@
+/datum/antagonist/venus_human_trap
+ name = "\improper Venus Human Trap"
+ antagpanel_category = ANTAG_GROUP_OVERGROWTH
+ show_in_roundend = FALSE
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+
+/datum/antagonist/venus_human_trap/on_gain()
+ forge_objectives()
+ . = ..()
+
+/datum/antagonist/venus_human_trap/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/objective/venus_human_trap
+ explanation_text = "Spread the infestation."
+
+/datum/objective/venus_human_trap/check_completion()
+ return owner.current.stat != DEAD
+
+/datum/antagonist/venus_human_trap/forge_objectives()
+ var/datum/objective/venus_human_trap/objective = new
+ objective.owner = owner
+ objectives += objective
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 82caf79143d..bfac673535a 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -5,7 +5,7 @@
hijack_speed = 2 //You literally are here to do nothing else. Might as well be fast about it.
suicide_cry = "HAHAHAHAHA!!"
-/datum/antagonist/wishgranter/proc/forge_objectives()
+/datum/antagonist/wishgranter/forge_objectives()
var/datum/objective/hijack/hijack = new
hijack.owner = owner
objectives += hijack
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 25c492eeb36..038785817d2 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -511,7 +511,7 @@
newstruct.key = target.key
var/atom/movable/screen/alert/bloodsense/BS
if(newstruct.mind && ((stoner && IS_CULTIST(stoner)) || cultoverride) && SSticker?.mode)
- newstruct.mind.add_antag_datum(/datum/antagonist/cult)
+ newstruct.mind.add_antag_datum(/datum/antagonist/cult/construct)
if(IS_CULTIST(stoner) || cultoverride)
to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow [stoner ? stoner.p_their() : "their"] orders and help [stoner ? stoner.p_them() : "them"] complete [stoner ? stoner.p_their() : "their"] goals at all costs.")
else if(stoner)
diff --git a/code/modules/antagonists/slaughter/imp_antag.dm b/code/modules/antagonists/wizard/imp_antag.dm
similarity index 95%
rename from code/modules/antagonists/slaughter/imp_antag.dm
rename to code/modules/antagonists/wizard/imp_antag.dm
index b27a68c72c8..3419dca1018 100644
--- a/code/modules/antagonists/slaughter/imp_antag.dm
+++ b/code/modules/antagonists/wizard/imp_antag.dm
@@ -9,6 +9,7 @@
show_in_antagpanel = FALSE
show_in_roundend = FALSE
ui_name = "AntagInfoDemon"
+ antagpanel_category = ANTAG_GROUP_WIZARDS
/datum/antagonist/imp/on_gain()
. = ..()
diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/wizard/slaughter_antag.dm
similarity index 94%
rename from code/modules/antagonists/slaughter/slaughter_antag.dm
rename to code/modules/antagonists/wizard/slaughter_antag.dm
index b158d430487..c9aea4478c3 100644
--- a/code/modules/antagonists/slaughter/slaughter_antag.dm
+++ b/code/modules/antagonists/wizard/slaughter_antag.dm
@@ -5,6 +5,7 @@
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
+ antagpanel_category = ANTAG_GROUP_WIZARDS
var/fluff = "You're a Demon of Wrath, often dragged into reality by wizards to terrorize their enemies."
var/objective_verb = "Kill"
var/datum/mind/summoner
@@ -18,7 +19,7 @@
owner.announce_objectives()
to_chat(owner, span_warning("You have a powerful alt-attack that slams people backwards that you can activate by right-clicking your target!"))
-/datum/antagonist/slaughter/proc/forge_objectives()
+/datum/antagonist/slaughter/forge_objectives()
if(summoner)
var/datum/objective/assassinate/new_objective = new /datum/objective/assassinate
new_objective.owner = owner
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 66a1d77ae42..08240fefb58 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -4,7 +4,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/datum/antagonist/wizard
name = "\improper Space Wizard"
roundend_category = "wizards/witches"
- antagpanel_category = "Wizard"
+ antagpanel_category = ANTAG_GROUP_WIZARDS
job_rank = ROLE_WIZARD
antag_hud_name = "wizard"
antag_moodlet = /datum/mood_event/focused
@@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/datum/antagonist/wizard_minion
name = "Wizard Minion"
- antagpanel_category = "Wizard"
+ antagpanel_category = "Wizard Federation"
antag_hud_name = "apprentice"
show_in_roundend = FALSE
show_name_in_check_antagonists = TRUE
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index a7863fc0f83..53ff49dec73 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -12,6 +12,7 @@
name = "\improper Xenomorph"
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
+ antagpanel_category = ANTAG_GROUP_XENOS
prevent_roundtype_conversion = FALSE
show_to_ghosts = TRUE
var/datum/team/xeno/xeno_team
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index 299adfb7fae..93495de2038 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -104,6 +104,7 @@ GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new)
if (antag_datum.show_to_ghosts)
was_antagonist = TRUE
serialized["antag"] = antag_datum.name
+ serialized["antag_group"] = antag_datum.antagpanel_category
antagonists += list(serialized)
break
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
deleted file mode 100644
index 8e167ba1986..00000000000
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ /dev/null
@@ -1,539 +0,0 @@
-
-/mob/living/simple_animal/hostile/construct
- name = "Construct"
- real_name = "Construct"
- desc = ""
- gender = NEUTER
- mob_biotypes = NONE
- speak_emote = list("hisses")
- response_help_continuous = "thinks better of touching"
- response_help_simple = "think better of touching"
- response_disarm_continuous = "flails at"
- response_disarm_simple = "flail at"
- response_harm_continuous = "punches"
- response_harm_simple = "punch"
- speak_chance = 1
- icon = 'icons/mob/nonhuman-player/cult.dmi'
- speed = 0
- combat_mode = TRUE
- stop_automated_movement = 1
- status_flags = CANPUSH
- attack_sound = 'sound/weapons/punch1.ogg'
- see_in_dark = 7
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- maxbodytemp = INFINITY
- healable = 0
- faction = list("cult")
- pressure_resistance = 100
- unique_name = 1
- AIStatus = AI_OFF //normal constructs don't have AI
- loot = list(/obj/item/ectoplasm)
- del_on_death = TRUE
- initial_language_holder = /datum/language_holder/construct
- death_message = "collapses in a shattered heap."
- var/list/construct_spells = list()
- var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this."
- var/master = null
- var/seeking = FALSE
- /// Whether this construct can repair other constructs or cult buildings.
- var/can_repair = FALSE
- /// Whether this construct can repair itself. Works independently of can_repair.
- var/can_repair_self = FALSE
- /// Theme controls color. THEME_CULT is red THEME_WIZARD is purple and THEME_HOLY is blue
- var/theme = THEME_CULT
-
-/mob/living/simple_animal/hostile/construct/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/simple_flying)
- ADD_TRAIT(src, TRAIT_HEALS_FROM_CULT_PYLONS, INNATE_TRAIT)
- ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
- for(var/spell in construct_spells)
- var/datum/action/new_spell = new spell(src)
- new_spell.Grant(src)
-
- var/spellnum = 1
- for(var/datum/action/spell as anything in actions)
- if(!(spell.type in construct_spells))
- continue
-
- var/pos = 2 + spellnum * 31
- if(construct_spells.len >= 4)
- pos -= 31 * (construct_spells.len - 4)
- spell.default_button_position = "6:[pos],4:-2" // Set the default position to this random position
- spellnum++
- update_action_buttons()
-
- if(icon_state)
- add_overlay("glow_[icon_state]_[theme]")
-
-/mob/living/simple_animal/hostile/construct/Login()
- . = ..()
- if(!. || !client)
- return FALSE
- to_chat(src, playstyle_string)
-
-/mob/living/simple_animal/hostile/construct/examine(mob/user)
- var/t_He = p_they(TRUE)
- var/t_s = p_s()
- var/text_span
- switch(theme)
- if(THEME_CULT)
- text_span = "cult"
- if(THEME_WIZARD)
- text_span = "purple"
- if(THEME_HOLY)
- text_span = "blue"
- . = list("This is [icon2html(src, user)] \a [src]!\n[desc]")
- if(health < maxHealth)
- if(health >= maxHealth/2)
- . += span_warning("[t_He] look[t_s] slightly dented.")
- else
- . += span_warning("[t_He] look[t_s] severely dented!")
- . += ""
-
-/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/user, list/modifiers)
- if(isconstruct(user)) //is it a construct?
- var/mob/living/simple_animal/hostile/construct/doll = user
- if(!doll.can_repair || (doll == src && !doll.can_repair_self))
- return ..()
- if(theme != doll.theme)
- return ..()
- if(health < maxHealth)
- adjustHealth(-5)
- if(src != user)
- Beam(user, icon_state="sendbeam", time = 4)
- user.visible_message(span_danger("[user] repairs some of \the [src]'s dents."), \
- span_cult("You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health."))
- else
- user.visible_message(span_danger("[user] repairs some of [p_their()] own dents."), \
- span_cult("You repair some of your own dents, leaving you at [user.health]/[user.maxHealth] health."))
- else
- if(src != user)
- to_chat(user, span_cult("You cannot repair [src]'s dents, as [p_they()] [p_have()] none!"))
- else
- to_chat(user, span_cult("You cannot repair your own dents, as you have none!"))
- else if(src != user)
- return ..()
-
-/mob/living/simple_animal/hostile/construct/narsie_act()
- return
-
-/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
- return 0
-
-/////////////////Juggernaut///////////////
-/mob/living/simple_animal/hostile/construct/juggernaut
- name = "Juggernaut"
- real_name = "Juggernaut"
- desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
- icon_state = "juggernaut"
- icon_living = "juggernaut"
- maxHealth = 150
- health = 150
- response_harm_continuous = "harmlessly punches"
- response_harm_simple = "harmlessly punch"
- harm_intent_damage = 0
- obj_damage = 90
- melee_damage_lower = 25
- melee_damage_upper = 25
- attack_verb_continuous = "smashes their armored gauntlet into"
- attack_verb_simple = "smash your armored gauntlet into"
- speed = 2.5
- environment_smash = ENVIRONMENT_SMASH_WALLS
- attack_sound = 'sound/weapons/punch3.ogg'
- status_flags = 0
- mob_size = MOB_SIZE_LARGE
- force_threshold = 10
- construct_spells = list(
- /datum/action/cooldown/spell/forcewall/cult,
- /datum/action/cooldown/spell/basic_projectile/juggernaut,
- /datum/action/innate/cult/create_rune/wall,
- )
- playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand heavy punishment, \
- create shield walls, rip apart enemies and walls alike, and even deflect energy weapons."
-
-/mob/living/simple_animal/hostile/construct/juggernaut/hostile //actually hostile, will move around, hit things
- AIStatus = AI_ON
- environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP
-
-/mob/living/simple_animal/hostile/construct/juggernaut/bullet_act(obj/projectile/P)
- if(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam))
- var/reflectchance = 40 - round(P.damage/3)
- if(prob(reflectchance))
- apply_damage(P.damage * 0.5, P.damage_type)
- visible_message(span_danger("The [P.name] is reflected by [src]'s armored shell!"), \
- span_userdanger("The [P.name] is reflected by your armored shell!"))
-
- // Find a turf near or on the original location to bounce to
- if(P.starting)
- var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
- var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
- var/turf/curloc = get_turf(src)
-
- // redirect the projectile
- P.original = locate(new_x, new_y, P.z)
- P.starting = curloc
- P.firer = src
- P.yo = new_y - curloc.y
- P.xo = new_x - curloc.x
- var/new_angle_s = P.Angle + rand(120,240)
- while(new_angle_s > 180) // Translate to regular projectile degrees
- new_angle_s -= 360
- P.set_angle(new_angle_s)
-
- return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
-
- return ..()
-
-//////////////////////////Juggernaut-alts////////////////////////////
-/mob/living/simple_animal/hostile/construct/juggernaut/angelic
- theme = THEME_HOLY
- loot = list(/obj/item/ectoplasm/angelic)
-
-/mob/living/simple_animal/hostile/construct/juggernaut/mystic
- theme = THEME_WIZARD
- loot = list(/obj/item/ectoplasm/mystic)
-
-/mob/living/simple_animal/hostile/construct/juggernaut/noncult
-
-////////////////////////Wraith/////////////////////////////////////////////
-/mob/living/simple_animal/hostile/construct/wraith
- name = "Wraith"
- real_name = "Wraith"
- desc = "A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines."
- icon_state = "wraith"
- icon_living = "wraith"
- maxHealth = 65
- health = 65
- melee_damage_lower = 20
- melee_damage_upper = 20
- retreat_distance = 2 //AI wraiths will move in and out of combat
- attack_verb_continuous = "slashes"
- attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
- attack_vis_effect = ATTACK_EFFECT_SLASH
- construct_spells = list(
- /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift,
- /datum/action/innate/cult/create_rune/tele,
- )
- playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, \
- can phase through walls, and your attacks will lower the cooldown on phasing."
-
- // Accomplishing various things gives you a refund on jaunt, to jump in and out.
- /// The seconds refunded per attack
- var/attack_refund = 1 SECONDS
- /// The seconds refunded when putting a target into critical
- var/crit_refund = 5 SECONDS
-
-/mob/living/simple_animal/hostile/construct/wraith/AttackingTarget() //refund jaunt cooldown when attacking living targets
- var/prev_stat
- var/mob/living/living_target = target
-
- if(isliving(living_target) && !IS_CULTIST(living_target))
- prev_stat = living_target.stat
-
- . = ..()
-
- if(. && isnum(prev_stat))
- var/datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/jaunt = locate() in actions
- if(!jaunt)
- return
-
- var/total_refund = 0 SECONDS
- // they're dead, and you killed them - full refund
- if(QDELETED(living_target) || (living_target.stat == DEAD && prev_stat != DEAD))
- total_refund += jaunt.cooldown_time
- // you knocked them into critical
- else if(HAS_TRAIT(living_target, TRAIT_CRITICAL_CONDITION) && prev_stat == CONSCIOUS)
- total_refund += crit_refund
-
- if(living_target.stat != DEAD && prev_stat != DEAD)
- total_refund += attack_refund
-
- jaunt.next_use_time -= total_refund
- jaunt.build_all_button_icons()
-
-/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things
- AIStatus = AI_ON
-
-//////////////////////////Wraith-alts////////////////////////////
-/mob/living/simple_animal/hostile/construct/wraith/angelic
- theme = THEME_HOLY
- construct_spells = list(
- /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/angelic,
- /datum/action/innate/cult/create_rune/tele,
- )
- loot = list(/obj/item/ectoplasm/angelic)
-
-/mob/living/simple_animal/hostile/construct/wraith/mystic
- theme = THEME_WIZARD
- construct_spells = list(
- /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/mystic,
- /datum/action/innate/cult/create_rune/tele,
- )
- loot = list(/obj/item/ectoplasm/mystic)
-
-/mob/living/simple_animal/hostile/construct/wraith/noncult
-
-/////////////////////////////Artificer/////////////////////////
-/mob/living/simple_animal/hostile/construct/artificer
- name = "Artificer"
- real_name = "Artificer"
- desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies."
- icon_state = "artificer"
- icon_living = "artificer"
- maxHealth = 50
- health = 50
- response_harm_continuous = "viciously beats"
- response_harm_simple = "viciously beat"
- harm_intent_damage = 5
- obj_damage = 60
- melee_damage_lower = 5
- melee_damage_upper = 5
- retreat_distance = 10
- minimum_distance = 10 //AI artificers will flee like fuck
- attack_verb_continuous = "rams"
- attack_verb_simple = "ram"
- environment_smash = ENVIRONMENT_SMASH_WALLS
- attack_sound = 'sound/weapons/punch2.ogg'
- construct_spells = list(
- /datum/action/cooldown/spell/conjure/cult_floor,
- /datum/action/cooldown/spell/conjure/cult_wall,
- /datum/action/cooldown/spell/conjure/soulstone,
- /datum/action/cooldown/spell/conjure/construct/lesser,
- /datum/action/cooldown/spell/aoe/magic_missile/lesser,
- /datum/action/innate/cult/create_rune/revive,
- )
- playstyle_string = "You are an Artificer. You are incredibly weak and fragile, \
- but you are able to construct fortifications, use magic missile, and repair allied constructs, shades, \
- and yourself (by clicking on them). Additionally, and most important of all, you can create new constructs \
- by producing soulstones to capture souls, and shells to place those soulstones into."
-
- can_repair = TRUE
- can_repair_self = TRUE
- ///The health HUD applied to this mob.
- var/health_hud = DATA_HUD_MEDICAL_ADVANCED
-
-/mob/living/simple_animal/hostile/construct/artificer/Initialize(mapload)
- . = ..()
- var/datum/atom_hud/datahud = GLOB.huds[health_hud]
- datahud.show_to(src)
-
-/mob/living/simple_animal/hostile/construct/artificer/Found(atom/A) //what have we found here?
- if(isconstruct(A)) //is it a construct?
- var/mob/living/simple_animal/hostile/construct/C = A
- if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is
- return TRUE
- return FALSE
-
-/mob/living/simple_animal/hostile/construct/artificer/CanAttack(atom/the_target)
- if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
- return FALSE
- if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible
- return TRUE //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs
- return FALSE
-
-/mob/living/simple_animal/hostile/construct/artificer/MoveToTarget(list/possible_targets)
- ..()
- if(isliving(target))
- var/mob/living/L = target
- if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it
- LoseTarget()
- return
- if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you
- retreat_distance = null
- minimum_distance = 1
-
-/mob/living/simple_animal/hostile/construct/artificer/Aggro()
- ..()
- if(isconstruct(target)) //oh the target is a construct no need to flee
- retreat_distance = null
- minimum_distance = 1
-
-/mob/living/simple_animal/hostile/construct/artificer/LoseAggro()
- ..()
- retreat_distance = initial(retreat_distance)
- minimum_distance = initial(minimum_distance)
-
-/mob/living/simple_animal/hostile/construct/artificer/hostile //actually hostile, will move around, hit things, heal other constructs
- AIStatus = AI_ON
- environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP
-
-/////////////////////////////Artificer-alts/////////////////////////
-/mob/living/simple_animal/hostile/construct/artificer/angelic
- desc = "A bulbous construct dedicated to building and maintaining holy armies."
- theme = THEME_HOLY
- loot = list(/obj/item/ectoplasm/angelic)
- construct_spells = list(
- /datum/action/cooldown/spell/conjure/soulstone/purified,
- /datum/action/cooldown/spell/conjure/construct/lesser,
- /datum/action/cooldown/spell/aoe/magic_missile/lesser,
- /datum/action/innate/cult/create_rune/revive,
- )
-/mob/living/simple_animal/hostile/construct/artificer/mystic
- theme = THEME_WIZARD
- loot = list(/obj/item/ectoplasm/mystic)
- construct_spells = list(
- /datum/action/cooldown/spell/conjure/cult_floor,
- /datum/action/cooldown/spell/conjure/cult_wall,
- /datum/action/cooldown/spell/conjure/soulstone/mystic,
- /datum/action/cooldown/spell/conjure/construct/lesser,
- /datum/action/cooldown/spell/aoe/magic_missile/lesser,
- /datum/action/innate/cult/create_rune/revive,
- )
-
-/mob/living/simple_animal/hostile/construct/artificer/noncult
- construct_spells = list(
- /datum/action/cooldown/spell/conjure/cult_floor,
- /datum/action/cooldown/spell/conjure/cult_wall,
- /datum/action/cooldown/spell/conjure/soulstone/noncult,
- /datum/action/cooldown/spell/conjure/construct/lesser,
- /datum/action/cooldown/spell/aoe/magic_missile/lesser,
- /datum/action/innate/cult/create_rune/revive,
- )
-
-/////////////////////////////Harvester/////////////////////////
-/mob/living/simple_animal/hostile/construct/harvester
- name = "Harvester"
- real_name = "Harvester"
- desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon."
- icon_state = "harvester"
- icon_living = "harvester"
- maxHealth = 40
- health = 40
- sight = SEE_MOBS
- melee_damage_lower = 15
- melee_damage_upper = 20
- attack_verb_continuous = "butchers"
- attack_verb_simple = "butcher"
- attack_sound = 'sound/weapons/bladeslice.ogg'
- attack_vis_effect = ATTACK_EFFECT_SLASH
- construct_spells = list(
- /datum/action/cooldown/spell/aoe/area_conversion,
- /datum/action/cooldown/spell/forcewall/cult,
- )
- playstyle_string = "You are a Harvester. You are incapable of directly killing humans, but your attacks will remove their limbs: \
- Bring those who still cling to this world of illusion back to the Geometer so they may know Truth. Your form and any you are pulling can pass through runed walls effortlessly."
- can_repair = TRUE
-
-
-/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM)
- . = ..()
- if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls
- var/atom/movable/stored_pulling = pulling
- if(stored_pulling)
- stored_pulling.setDir(get_dir(stored_pulling.loc, loc))
- stored_pulling.forceMove(loc)
- forceMove(AM)
- if(stored_pulling)
- start_pulling(stored_pulling, supress_message = TRUE) //drag anything we're pulling through the wall with us by magic
-
-/mob/living/simple_animal/hostile/construct/harvester/AttackingTarget()
- if(iscarbon(target))
- var/mob/living/carbon/C = target
- if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
- return ..() //ATTACK!
- var/list/parts = list()
- var/undismembermerable_limbs = 0
- for(var/X in C.bodyparts)
- var/obj/item/bodypart/BP = X
- if(BP.body_part != HEAD && BP.body_part != CHEST)
- if(BP.dismemberable)
- parts += BP
- else
- undismembermerable_limbs++
- if(!LAZYLEN(parts))
- if(undismembermerable_limbs) //they have limbs we can't remove, and no parts we can, attack!
- return ..()
- C.Paralyze(60)
- visible_message(span_danger("[src] knocks [C] down!"))
- to_chat(src, span_cultlarge("\"Bring [C.p_them()] to me.\""))
- return FALSE
- do_attack_animation(C)
- var/obj/item/bodypart/BP = pick(parts)
- BP.dismember()
- return FALSE
- . = ..()
-
-/mob/living/simple_animal/hostile/construct/harvester/Initialize(mapload)
- . = ..()
- var/datum/action/innate/seek_prey/seek = new()
- seek.Grant(src)
- seek.Activate()
-
-///////////////////////Master-Tracker///////////////////////
-
-/datum/action/innate/seek_master
- name = "Seek your Master"
- desc = "You and your master share a soul-link that informs you of their location"
- background_icon_state = "bg_demon"
- overlay_icon_state = "bg_demon_border"
-
- buttontooltipstyle = "cult"
- button_icon_state = "cult_mark"
- var/tracking = FALSE
- var/mob/living/simple_animal/hostile/construct/the_construct
-
-
-/datum/action/innate/seek_master/Grant(mob/living/C)
- the_construct = C
- ..()
-
-/datum/action/innate/seek_master/Activate()
- var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult)
- if(!C)
- return
- var/datum/objective/eldergod/summon_objective = locate() in C.cult_team.objectives
-
- if(summon_objective.check_completion())
- the_construct.master = C.cult_team.blood_target
-
- if(!the_construct.master)
- to_chat(the_construct, "You have no master to seek!")
- the_construct.seeking = FALSE
- return
- if(tracking)
- tracking = FALSE
- the_construct.seeking = FALSE
- to_chat(the_construct, "You are no longer tracking your master.")
- return
- else
- tracking = TRUE
- the_construct.seeking = TRUE
- to_chat(the_construct, "You are now tracking your master.")
-
-
-/datum/action/innate/seek_prey
- name = "Seek the Harvest"
- desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
- button_icon = 'icons/mob/actions/actions_cult.dmi'
- background_icon_state = "bg_demon"
- overlay_icon_state = "bg_demon_border"
-
- buttontooltipstyle = "cult"
- button_icon_state = "cult_mark"
-
-/datum/action/innate/seek_prey/Activate()
- if(GLOB.cult_narsie == null)
- return
- var/mob/living/simple_animal/hostile/construct/harvester/the_construct = owner
- if(the_construct.seeking)
- desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
- button_icon_state = "cult_mark"
- the_construct.seeking = FALSE
- to_chat(the_construct, "You are now tracking Nar'Sie, return to reap the harvest!")
- return
- else
- if(LAZYLEN(GLOB.cult_narsie.souls_needed))
- the_construct.master = pick(GLOB.cult_narsie.souls_needed)
- var/mob/living/real_target = the_construct.master //We can typecast this way because Narsie only allows /mob/living into the souls list
- to_chat(the_construct, "You are now tracking your prey, [real_target.real_name] - harvest [real_target.p_them()]!")
- else
- to_chat(the_construct, "Nar'Sie has completed her harvest!")
- return
- desc = "Activate to track Nar'Sie!"
- button_icon_state = "sintouch"
- the_construct.seeking = TRUE
diff --git a/code/modules/antagonists/blob/blob_mob.dm b/code/modules/mob/living/simple_animal/hostile/blob.dm
similarity index 90%
rename from code/modules/antagonists/blob/blob_mob.dm
rename to code/modules/mob/living/simple_animal/hostile/blob.dm
index c9abde76e86..eb352e16a3f 100644
--- a/code/modules/antagonists/blob/blob_mob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/blob.dm
@@ -1,8 +1,3 @@
-
-////////////////
-// BASE TYPE //
-////////////////
-
//Do not spawn
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/nonhuman-player/blob.dmi'
@@ -100,12 +95,3 @@
var/link = FOLLOW_LINK(creature, src)
to_chat(creature, "[link] [rendered]")
-/mob/living/simple_animal/hostile/blob/proc/create_objectives(datum/antagonist/antagonist)
- if(!antagonist)
- return FALSE
- var/datum/objective/protect/new_objective = new
- new_objective.owner = mind
- new_objective.target = overmind
- new_objective.explanation_text = "Protect the core!"
- antagonist.objectives = list(new_objective)
- return TRUE
diff --git a/code/modules/antagonists/blob/minions/blobbernaut.dm b/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm
similarity index 89%
rename from code/modules/antagonists/blob/minions/blobbernaut.dm
rename to code/modules/mob/living/simple_animal/hostile/blobbernaut.dm
index b40aba1ed85..b9fdb0cea80 100644
--- a/code/modules/antagonists/blob/minions/blobbernaut.dm
+++ b/code/modules/mob/living/simple_animal/hostile/blobbernaut.dm
@@ -1,13 +1,3 @@
-/datum/antagonist/blobbernaut
- name = "\improper Blobbernaut"
- antagpanel_category = "Biohazards"
- show_name_in_check_antagonists = TRUE
- show_to_ghosts = TRUE
-
-/////////////////
-// BLOBBERNAUT //
-/////////////////
-
/mob/living/simple_animal/hostile/blob/blobbernaut
name = "blobbernaut"
desc = "A hulking, mobile chunk of blobmass."
@@ -38,12 +28,10 @@
/mob/living/simple_animal/hostile/blob/blobbernaut/mind_initialize()
. = ..()
- if(independent)
- return FALSE
- if(mind.has_antag_datum(/datum/antagonist/blobbernaut))
- return FALSE
- var/datum/antagonist/blobbernaut/blobbernaut = mind.add_antag_datum(/datum/antagonist/blobbernaut)
- create_objectives(blobbernaut)
+ if(independent | !overmind)
+ return
+ var/datum/antagonist/blob_minion/blobbernaut/naut = new(overmind)
+ mind.add_antag_datum(naut)
/mob/living/simple_animal/hostile/blob/blobbernaut/add_cell_sample()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBBERNAUT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
diff --git a/code/modules/antagonists/blob/minions/blobspore.dm b/code/modules/mob/living/simple_animal/hostile/blobspore.dm
similarity index 93%
rename from code/modules/antagonists/blob/minions/blobspore.dm
rename to code/modules/mob/living/simple_animal/hostile/blobspore.dm
index c1d8f5595b0..0325def0e80 100644
--- a/code/modules/antagonists/blob/minions/blobspore.dm
+++ b/code/modules/mob/living/simple_animal/hostile/blobspore.dm
@@ -1,13 +1,3 @@
-/datum/antagonist/blobspore
- name = "\improper Blob Spore"
- antagpanel_category = "Biohazards"
- show_name_in_check_antagonists = TRUE
- show_to_ghosts = TRUE
-
-////////////////
-// BLOB SPORE //
-////////////////
-
/mob/living/simple_animal/hostile/blob/blobspore
name = "blob spore"
desc = "A floating, fragile spore."
@@ -54,12 +44,10 @@
/mob/living/simple_animal/hostile/blob/blobspore/mind_initialize()
. = ..()
- if(independent)
+ if(independent || !overmind)
return FALSE
- if(mind.has_antag_datum(/datum/antagonist/blobspore))
- return FALSE
- var/datum/antagonist/blobspore/blobspore = mind.add_antag_datum(/datum/antagonist/blobspore)
- create_objectives(blobspore)
+ var/datum/antagonist/blob_minion/blob_zombie/zombie = new(overmind)
+ mind.add_antag_datum(zombie)
/mob/living/simple_animal/hostile/blob/blobspore/Life(delta_time = SSMOBS_DT, times_fired)
if(!is_zombie && isturf(loc))
diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm b/code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm
new file mode 100644
index 00000000000..743931c508d
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm
@@ -0,0 +1,117 @@
+/mob/living/simple_animal/hostile/construct/artificer
+ name = "Artificer"
+ real_name = "Artificer"
+ desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies."
+ icon_state = "artificer"
+ icon_living = "artificer"
+ maxHealth = 50
+ health = 50
+ response_harm_continuous = "viciously beats"
+ response_harm_simple = "viciously beat"
+ harm_intent_damage = 5
+ obj_damage = 60
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+ retreat_distance = 10
+ minimum_distance = 10 //AI artificers will flee like fuck
+ attack_verb_continuous = "rams"
+ attack_verb_simple = "ram"
+ environment_smash = ENVIRONMENT_SMASH_WALLS
+ attack_sound = 'sound/weapons/punch2.ogg'
+ construct_spells = list(
+ /datum/action/cooldown/spell/conjure/cult_floor,
+ /datum/action/cooldown/spell/conjure/cult_wall,
+ /datum/action/cooldown/spell/conjure/soulstone,
+ /datum/action/cooldown/spell/conjure/construct/lesser,
+ /datum/action/cooldown/spell/aoe/magic_missile/lesser,
+ /datum/action/innate/cult/create_rune/revive,
+ )
+ playstyle_string = "You are an Artificer. You are incredibly weak and fragile, \
+ but you are able to construct fortifications, use magic missile, and repair allied constructs, shades, \
+ and yourself (by clicking on them). Additionally, and most important of all, you can create new constructs \
+ by producing soulstones to capture souls, and shells to place those soulstones into."
+
+ can_repair = TRUE
+ can_repair_self = TRUE
+ ///The health HUD applied to this mob.
+ var/health_hud = DATA_HUD_MEDICAL_ADVANCED
+
+/mob/living/simple_animal/hostile/construct/artificer/Initialize(mapload)
+ . = ..()
+ var/datum/atom_hud/datahud = GLOB.huds[health_hud]
+ datahud.show_to(src)
+
+/mob/living/simple_animal/hostile/construct/artificer/Found(atom/thing) //what have we found here?
+ if(!isconstruct(thing)) //is it a construct?
+ return FALSE
+ var/mob/living/simple_animal/hostile/construct/cultie = thing
+ if(cultie.health < cultie.maxHealth) //is it hurt? let's go heal it if it is
+ return TRUE
+
+/mob/living/simple_animal/hostile/construct/artificer/CanAttack(atom/the_target)
+ if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
+ return FALSE
+ if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible
+ return TRUE //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs
+ return FALSE
+
+/mob/living/simple_animal/hostile/construct/artificer/MoveToTarget(list/possible_targets)
+ ..()
+ if(!isliving(target))
+ return
+
+ var/mob/living/victim = target
+ if(isconstruct(victim) && victim.health >= victim.maxHealth) //is this target an unhurt construct? stop trying to heal it
+ LoseTarget()
+ return
+ if(victim.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you
+ retreat_distance = null
+ minimum_distance = 1
+
+/mob/living/simple_animal/hostile/construct/artificer/Aggro()
+ ..()
+ if(isconstruct(target)) //oh the target is a construct no need to flee
+ retreat_distance = null
+ minimum_distance = 1
+
+/mob/living/simple_animal/hostile/construct/artificer/LoseAggro()
+ ..()
+ retreat_distance = initial(retreat_distance)
+ minimum_distance = initial(minimum_distance)
+
+/mob/living/simple_animal/hostile/construct/artificer/hostile //actually hostile, will move around, hit things, heal other constructs
+ AIStatus = AI_ON
+ environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP
+
+/////////////////////////////Artificer-alts/////////////////////////
+/mob/living/simple_animal/hostile/construct/artificer/angelic
+ desc = "A bulbous construct dedicated to building and maintaining holy armies."
+ theme = THEME_HOLY
+ loot = list(/obj/item/ectoplasm/angelic)
+ construct_spells = list(
+ /datum/action/cooldown/spell/conjure/soulstone/purified,
+ /datum/action/cooldown/spell/conjure/construct/lesser,
+ /datum/action/cooldown/spell/aoe/magic_missile/lesser,
+ /datum/action/innate/cult/create_rune/revive,
+ )
+/mob/living/simple_animal/hostile/construct/artificer/mystic
+ theme = THEME_WIZARD
+ loot = list(/obj/item/ectoplasm/mystic)
+ construct_spells = list(
+ /datum/action/cooldown/spell/conjure/cult_floor,
+ /datum/action/cooldown/spell/conjure/cult_wall,
+ /datum/action/cooldown/spell/conjure/soulstone/mystic,
+ /datum/action/cooldown/spell/conjure/construct/lesser,
+ /datum/action/cooldown/spell/aoe/magic_missile/lesser,
+ /datum/action/innate/cult/create_rune/revive,
+ )
+
+/mob/living/simple_animal/hostile/construct/artificer/noncult
+ construct_spells = list(
+ /datum/action/cooldown/spell/conjure/cult_floor,
+ /datum/action/cooldown/spell/conjure/cult_wall,
+ /datum/action/cooldown/spell/conjure/soulstone/noncult,
+ /datum/action/cooldown/spell/conjure/construct/lesser,
+ /datum/action/cooldown/spell/aoe/magic_missile/lesser,
+ /datum/action/innate/cult/create_rune/revive,
+ )
diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/constructs.dm b/code/modules/mob/living/simple_animal/hostile/constructs/constructs.dm
new file mode 100644
index 00000000000..bb4ae37efbb
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/constructs/constructs.dm
@@ -0,0 +1,135 @@
+/mob/living/simple_animal/hostile/construct
+ name = "Construct"
+ real_name = "Construct"
+ desc = ""
+ gender = NEUTER
+ mob_biotypes = NONE
+ speak_emote = list("hisses")
+ response_help_continuous = "thinks better of touching"
+ response_help_simple = "think better of touching"
+ response_disarm_continuous = "flails at"
+ response_disarm_simple = "flail at"
+ response_harm_continuous = "punches"
+ response_harm_simple = "punch"
+ speak_chance = 1
+ icon = 'icons/mob/nonhuman-player/cult.dmi'
+ speed = 0
+ combat_mode = TRUE
+ stop_automated_movement = 1
+ status_flags = CANPUSH
+ attack_sound = 'sound/weapons/punch1.ogg'
+ see_in_dark = 7
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ maxbodytemp = INFINITY
+ healable = 0
+ faction = list("cult")
+ pressure_resistance = 100
+ unique_name = 1
+ AIStatus = AI_OFF //normal constructs don't have AI
+ loot = list(/obj/item/ectoplasm)
+ del_on_death = TRUE
+ initial_language_holder = /datum/language_holder/construct
+ death_message = "collapses in a shattered heap."
+ /// List of spells that this construct can cast
+ var/list/construct_spells = list()
+ /// Flavor text shown to players when they spawn as this construct
+ var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this."
+ /// The construct's master
+ var/master = null
+ /// Whether this construct is currently seeking nar nar
+ var/seeking = FALSE
+ /// Whether this construct can repair other constructs or cult buildings.
+ var/can_repair = FALSE
+ /// Whether this construct can repair itself. Works independently of can_repair.
+ var/can_repair_self = FALSE
+ /// Theme controls color. THEME_CULT is red THEME_WIZARD is purple and THEME_HOLY is blue
+ var/theme = THEME_CULT
+
+/mob/living/simple_animal/hostile/construct/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/simple_flying)
+ ADD_TRAIT(src, TRAIT_HEALS_FROM_CULT_PYLONS, INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
+ for(var/spell in construct_spells)
+ var/datum/action/new_spell = new spell(src)
+ new_spell.Grant(src)
+
+ var/spell_count = 1
+ for(var/datum/action/spell as anything in actions)
+ if(!(spell.type in construct_spells))
+ continue
+
+ var/pos = 2 + spell_count * 31
+ if(construct_spells.len >= 4)
+ pos -= 31 * (construct_spells.len - 4)
+ spell.default_button_position = "6:[pos],4:-2" // Set the default position to this random position
+ spell_count++
+ update_action_buttons()
+
+ if(icon_state)
+ add_overlay("glow_[icon_state]_[theme]")
+
+/mob/living/simple_animal/hostile/construct/Login()
+ . = ..()
+ if(!. || !client)
+ return FALSE
+ to_chat(src, playstyle_string)
+
+/mob/living/simple_animal/hostile/construct/examine(mob/user)
+ var/pronoun = p_they(TRUE)
+ var/plural = p_s()
+ var/text_span
+ switch(theme)
+ if(THEME_CULT)
+ text_span = "cult"
+ if(THEME_WIZARD)
+ text_span = "purple"
+ if(THEME_HOLY)
+ text_span = "blue"
+ . = list("This is [icon2html(src, user)] \a [src]!\n[desc]")
+ if(health < maxHealth)
+ if(health >= maxHealth/2)
+ . += span_warning("[pronoun] look[plural] slightly dented.")
+ else
+ . += span_warning("[pronoun] look[plural] severely dented!")
+ . += ""
+
+/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/user, list/modifiers)
+ if(!isconstruct(user))
+ if(src != user)
+ return ..()
+ return
+
+ var/mob/living/simple_animal/hostile/construct/doll = user
+ if(!doll.can_repair || (doll == src && !doll.can_repair_self))
+ return ..()
+ if(theme != doll.theme)
+ return ..()
+
+ if(health >= maxHealth)
+ if(src != user)
+ to_chat(user, span_cult("You cannot repair [src]'s dents, as [p_they()] [p_have()] none!"))
+ else
+ to_chat(user, span_cult("You cannot repair your own dents, as you have none!"))
+ return
+
+ adjustHealth(-5)
+ if(src == user)
+ user.visible_message(span_danger("[user] repairs some of [p_their()] own dents."), \
+ span_cult("You repair some of your own dents, leaving you at [user.health]/[user.maxHealth] health."))
+ return
+
+ Beam(user, icon_state="sendbeam", time = 4)
+ user.visible_message(span_danger("[user] repairs some of \the [src]'s dents."), \
+ span_cult("You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health."))
+
+
+/mob/living/simple_animal/hostile/construct/narsie_act()
+ return
+
+/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
+ return FALSE
+
diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/harvester.dm b/code/modules/mob/living/simple_animal/hostile/constructs/harvester.dm
new file mode 100644
index 00000000000..b66488d9abb
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/constructs/harvester.dm
@@ -0,0 +1,151 @@
+/mob/living/simple_animal/hostile/construct/harvester
+ name = "Harvester"
+ real_name = "Harvester"
+ desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon."
+ icon_state = "harvester"
+ icon_living = "harvester"
+ maxHealth = 40
+ health = 40
+ sight = SEE_MOBS
+ melee_damage_lower = 15
+ melee_damage_upper = 20
+ attack_verb_continuous = "butchers"
+ attack_verb_simple = "butcher"
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_vis_effect = ATTACK_EFFECT_SLASH
+ construct_spells = list(
+ /datum/action/cooldown/spell/aoe/area_conversion,
+ /datum/action/cooldown/spell/forcewall/cult,
+ )
+ playstyle_string = "You are a Harvester. You are incapable of directly killing humans, \
+ but your attacks will remove their limbs: Bring those who still cling to this world \
+ of illusion back to the Geometer so they may know Truth. Your form and any you are \
+ pulling can pass through runed walls effortlessly."
+ can_repair = TRUE
+
+
+/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/thing)
+ . = ..()
+ if(!istype(thing, /turf/closed/wall/mineral/cult) || thing == loc)
+ return // we can go through cult walls
+ var/atom/movable/stored_pulling = pulling
+
+ if(stored_pulling)
+ stored_pulling.setDir(get_dir(stored_pulling.loc, loc))
+ stored_pulling.forceMove(loc)
+ forceMove(thing)
+
+ if(stored_pulling)
+ start_pulling(stored_pulling, supress_message = TRUE) //drag anything we're pulling through the wall with us by magic
+
+/mob/living/simple_animal/hostile/construct/harvester/AttackingTarget()
+ if(!iscarbon(target))
+ return ..()
+
+ var/mob/living/carbon/victim = target
+ if(HAS_TRAIT(victim, TRAIT_NODISMEMBER))
+ return ..() //ATTACK!
+
+ var/list/parts = list()
+ var/strong_limbs = 0
+
+ for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ if(limb.body_part == HEAD || limb.body_part == CHEST)
+ continue
+ if(limb.dismemberable)
+ parts += limb
+ else
+ strong_limbs++
+
+ if(!LAZYLEN(parts))
+ if(strong_limbs) // they have limbs we can't remove, and no parts we can, attack!
+ return ..()
+ victim.Paralyze(60)
+ visible_message(span_danger("[src] knocks [victim] down!"))
+ to_chat(src, span_cultlarge("\"Bring [victim.p_them()] to me.\""))
+ return FALSE
+
+ do_attack_animation(victim)
+ var/obj/item/bodypart/limb = pick(parts)
+ limb.dismember()
+ return FALSE
+
+/mob/living/simple_animal/hostile/construct/harvester/Initialize(mapload)
+ . = ..()
+ var/datum/action/innate/seek_prey/seek = new()
+ seek.Grant(src)
+ seek.Activate()
+
+/datum/action/innate/seek_master
+ name = "Seek your Master"
+ desc = "You and your master share a soul-link that informs you of their location"
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "bg_demon_border"
+
+ buttontooltipstyle = "cult"
+ button_icon_state = "cult_mark"
+ /// Where is nar nar? Are we even looking?
+ var/tracking = FALSE
+ /// The construct we're attached to
+ var/mob/living/simple_animal/hostile/construct/the_construct
+
+/datum/action/innate/seek_master/Grant(mob/living/player)
+ the_construct = player
+ ..()
+
+/datum/action/innate/seek_master/Activate()
+ var/datum/antagonist/cult/cult_status = owner.mind.has_antag_datum(/datum/antagonist/cult)
+ if(!cult_status)
+ return
+ var/datum/objective/eldergod/summon_objective = locate() in cult_status.cult_team.objectives
+
+ if(summon_objective.check_completion())
+ the_construct.master = cult_status.cult_team.blood_target
+
+ if(!the_construct.master)
+ to_chat(the_construct, span_cultitalic("You have no master to seek!"))
+ the_construct.seeking = FALSE
+ return
+ if(tracking)
+ tracking = FALSE
+ the_construct.seeking = FALSE
+ to_chat(the_construct, span_cultitalic("You are no longer tracking your master."))
+ return
+ else
+ tracking = TRUE
+ the_construct.seeking = TRUE
+ to_chat(the_construct, span_cultitalic("You are now tracking your master."))
+
+
+/datum/action/innate/seek_prey
+ name = "Seek the Harvest"
+ desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "bg_demon_border"
+
+ buttontooltipstyle = "cult"
+ button_icon_state = "cult_mark"
+
+/datum/action/innate/seek_prey/Activate()
+ if(GLOB.cult_narsie == null)
+ return
+ var/mob/living/simple_animal/hostile/construct/harvester/the_construct = owner
+
+ if(the_construct.seeking)
+ desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
+ button_icon_state = "cult_mark"
+ the_construct.seeking = FALSE
+ to_chat(the_construct, span_cultitalic("You are now tracking Nar'Sie, return to reap the harvest!"))
+ return
+
+ if(!LAZYLEN(GLOB.cult_narsie.souls_needed))
+ to_chat(the_construct, span_cultitalic("Nar'Sie has completed her harvest!"))
+ return
+
+ the_construct.master = pick(GLOB.cult_narsie.souls_needed)
+ var/mob/living/real_target = the_construct.master //We can typecast this way because Narsie only allows /mob/living into the souls list
+ to_chat(the_construct, span_cultitalic("You are now tracking your prey, [real_target.real_name] - harvest [real_target.p_them()]!"))
+ desc = "Activate to track Nar'Sie!"
+ button_icon_state = "sintouch"
+ the_construct.seeking = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm b/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm
new file mode 100644
index 00000000000..3eb80cf7a88
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm
@@ -0,0 +1,75 @@
+/mob/living/simple_animal/hostile/construct/juggernaut
+ name = "Juggernaut"
+ real_name = "Juggernaut"
+ desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
+ icon_state = "juggernaut"
+ icon_living = "juggernaut"
+ maxHealth = 150
+ health = 150
+ response_harm_continuous = "harmlessly punches"
+ response_harm_simple = "harmlessly punch"
+ harm_intent_damage = 0
+ obj_damage = 90
+ melee_damage_lower = 25
+ melee_damage_upper = 25
+ attack_verb_continuous = "smashes their armored gauntlet into"
+ attack_verb_simple = "smash your armored gauntlet into"
+ speed = 2.5
+ environment_smash = ENVIRONMENT_SMASH_WALLS
+ attack_sound = 'sound/weapons/punch3.ogg'
+ status_flags = 0
+ mob_size = MOB_SIZE_LARGE
+ force_threshold = 10
+ construct_spells = list(
+ /datum/action/cooldown/spell/forcewall/cult,
+ /datum/action/cooldown/spell/basic_projectile/juggernaut,
+ /datum/action/innate/cult/create_rune/wall,
+ )
+ playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand heavy punishment, \
+ create shield walls, rip apart enemies and walls alike, and even deflect energy weapons."
+
+/mob/living/simple_animal/hostile/construct/juggernaut/hostile //actually hostile, will move around, hit things
+ AIStatus = AI_ON
+ environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP
+
+/mob/living/simple_animal/hostile/construct/juggernaut/bullet_act(obj/projectile/bullet)
+ if(!istype(bullet, /obj/projectile/energy) && !istype(bullet, /obj/projectile/beam))
+ return ..()
+ if(!prob(40 - round(bullet.damage / 3))) // reflect chance
+ return ..()
+
+ apply_damage(bullet.damage * 0.5, bullet.damage_type)
+ visible_message(span_danger("The [bullet.name] is reflected by [src]'s armored shell!"), \
+ span_userdanger("The [bullet.name] is reflected by your armored shell!"))
+
+ if(!bullet.starting)
+ return BULLET_ACT_FORCE_PIERCE
+ // Find a turf near or on the original location to bounce to
+ var/new_x = bullet.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/new_y = bullet.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
+ var/turf/current_tile = get_turf(src)
+
+ // redirect the projectile
+ bullet.original = locate(new_x, new_y, bullet.z)
+ bullet.starting = current_tile
+ bullet.firer = src
+ bullet.yo = new_y - current_tile.y
+ bullet.xo = new_x - current_tile.x
+ var/new_angle_s = bullet.Angle + rand(120,240)
+ while(new_angle_s > 180) // Translate to regular projectile degrees
+ new_angle_s -= 360
+ bullet.set_angle(new_angle_s)
+
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
+
+
+//////////////////////////Juggernaut-alts////////////////////////////
+/mob/living/simple_animal/hostile/construct/juggernaut/angelic
+ theme = THEME_HOLY
+ loot = list(/obj/item/ectoplasm/angelic)
+
+/mob/living/simple_animal/hostile/construct/juggernaut/mystic
+ theme = THEME_WIZARD
+ loot = list(/obj/item/ectoplasm/mystic)
+
+/mob/living/simple_animal/hostile/construct/juggernaut/noncult
diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm b/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm
new file mode 100644
index 00000000000..e7ef22a9e07
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm
@@ -0,0 +1,78 @@
+/mob/living/simple_animal/hostile/construct/wraith
+ name = "Wraith"
+ real_name = "Wraith"
+ desc = "A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines."
+ icon_state = "wraith"
+ icon_living = "wraith"
+ maxHealth = 65
+ health = 65
+ melee_damage_lower = 20
+ melee_damage_upper = 20
+ retreat_distance = 2 //AI wraiths will move in and out of combat
+ attack_verb_continuous = "slashes"
+ attack_verb_simple = "slash"
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_vis_effect = ATTACK_EFFECT_SLASH
+ construct_spells = list(
+ /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift,
+ /datum/action/innate/cult/create_rune/tele,
+ )
+ playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, \
+ can phase through walls, and your attacks will lower the cooldown on phasing."
+
+ // Accomplishing various things gives you a refund on jaunt, to jump in and out.
+ /// The seconds refunded per attack
+ var/attack_refund = 1 SECONDS
+ /// The seconds refunded when putting a target into critical
+ var/crit_refund = 5 SECONDS
+
+/mob/living/simple_animal/hostile/construct/wraith/AttackingTarget() //refund jaunt cooldown when attacking living targets
+ var/prev_stat
+ var/mob/living/living_target = target
+
+ if(isliving(living_target) && !IS_CULTIST(living_target))
+ prev_stat = living_target.stat
+
+ . = ..()
+ if(!. || !isnum(prev_stat))
+ return
+
+ var/datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/jaunt = locate() in actions
+ if(!jaunt)
+ return
+
+ var/total_refund = 0 SECONDS
+ // they're dead, and you killed them - full refund
+ if(QDELETED(living_target) || (living_target.stat == DEAD && prev_stat != DEAD))
+ total_refund += jaunt.cooldown_time
+ // you knocked them into critical
+ else if(HAS_TRAIT(living_target, TRAIT_CRITICAL_CONDITION) && prev_stat == CONSCIOUS)
+ total_refund += crit_refund
+
+ if(living_target.stat != DEAD && prev_stat != DEAD)
+ total_refund += attack_refund
+
+ jaunt.next_use_time -= total_refund
+ jaunt.build_all_button_icons()
+
+/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things
+ AIStatus = AI_ON
+
+//////////////////////////Wraith-alts////////////////////////////
+/mob/living/simple_animal/hostile/construct/wraith/angelic
+ theme = THEME_HOLY
+ construct_spells = list(
+ /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/angelic,
+ /datum/action/innate/cult/create_rune/tele,
+ )
+ loot = list(/obj/item/ectoplasm/angelic)
+
+/mob/living/simple_animal/hostile/construct/wraith/mystic
+ theme = THEME_WIZARD
+ construct_spells = list(
+ /datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/mystic,
+ /datum/action/innate/cult/create_rune/tele,
+ )
+ loot = list(/obj/item/ectoplasm/mystic)
+
+/mob/living/simple_animal/hostile/construct/wraith/noncult
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 4b2b92bda6f..7766da05152 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -76,9 +76,8 @@
. = ..()
if(!. || !client)
return FALSE
- if(directive)
- to_chat(src, span_spider("Your mother left you a directive! Follow it at all costs."))
- to_chat(src, span_spider("[directive]"))
+ var/datum/antagonist/spider/spider_antag = new(directive)
+ mind.add_antag_datum(spider_antag)
GLOB.spidermobs[src] = TRUE
/mob/living/simple_animal/hostile/giant_spider/Destroy()
diff --git a/code/modules/mob/living/simple_animal/heretic_monsters.dm b/code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm
similarity index 100%
rename from code/modules/mob/living/simple_animal/heretic_monsters.dm
rename to code/modules/mob/living/simple_animal/hostile/heretic_monsters.dm
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/mob/living/simple_animal/hostile/morph.dm
similarity index 100%
rename from code/modules/antagonists/morph/morph.dm
rename to code/modules/mob/living/simple_animal/hostile/morph.dm
diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/mob/living/simple_animal/hostile/slaughter_demon.dm
similarity index 100%
rename from code/modules/antagonists/slaughter/slaughter.dm
rename to code/modules/mob/living/simple_animal/hostile/slaughter_demon.dm
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/mob/living/simple_animal/revenant.dm
similarity index 99%
rename from code/modules/antagonists/revenant/revenant.dm
rename to code/modules/mob/living/simple_animal/revenant.dm
index a2613f4477f..ad6e642624a 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/mob/living/simple_animal/revenant.dm
@@ -525,7 +525,7 @@
/datum/objective/revenant_fluff
/datum/objective/revenant_fluff/New()
- var/list/explanationTexts = list(
+ var/list/explanation_texts = list(
"Assist and exacerbate existing threats at critical moments.", \
"Impersonate or be worshipped as a god.", \
"Cause as much chaos and anger as you can without being killed.", \
@@ -538,8 +538,9 @@
"Make the captain as miserable as possible.", \
"Prevent the use of energy weapons where possible.",
)
- explanation_text = pick(explanationTexts)
+ explanation_text = pick(explanation_texts)
..()
/datum/objective/revenant_fluff/check_completion()
return TRUE
+
diff --git a/code/modules/mob_spawn/ghost_roles/space_roles.dm b/code/modules/mob_spawn/ghost_roles/space_roles.dm
index 1c0d6e58d41..162c21d0d49 100644
--- a/code/modules/mob_spawn/ghost_roles/space_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/space_roles.dm
@@ -132,66 +132,6 @@
mob_mind.add_antag_datum(antag_datum_to_give, antag_team)
antag_team.players_spawned += (spawned_mob.ckey)
-/datum/team/battlecruiser
- name = "\improper Battlecruiser Crew"
- member_name = "crewmember"
- /// The central objective of this battlecruiser
- var/core_objective = /datum/objective/nuclear
- /// The assigned nuke of this team
- var/obj/machinery/nuclearbomb/nuke
-
-/datum/team/battlecruiser/proc/update_objectives()
- if(core_objective)
- var/datum/objective/objective = new core_objective()
- objective.team = src
- objectives += objective
-
-/datum/antagonist/battlecruiser
- name = "Battlecruiser Crewmember"
- show_to_ghosts = TRUE
- roundend_category = "battlecruiser syndicate operatives"
- suicide_cry = "FOR THE SYNDICATE!!!"
- antag_hud_name = "battlecruiser_crew"
- job_rank = ROLE_BATTLECRUISER_CREW
- var/datum/team/battlecruiser/battlecruiser_team
-
-/datum/antagonist/battlecruiser/get_team()
- return battlecruiser_team
-
-/datum/antagonist/battlecruiser/greet()
- owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0, use_reverb = FALSE)
- to_chat(owner, span_big("You are a [name]!"))
- owner.announce_objectives()
-
-/datum/antagonist/battlecruiser/ally
- name = "Battlecruiser Ally"
- show_to_ghosts = FALSE
-
-/datum/antagonist/battlecruiser/captain
- name = "Battlecruiser Captain"
- antag_hud_name = "battlecruiser_lead"
- job_rank = ROLE_BATTLECRUISER_CAPTAIN
-
-/datum/antagonist/battlecruiser/create_team(datum/team/battlecruiser/team)
- if(!team)
- return
- if(!istype(team))
- stack_trace("Wrong team type passed to [type] initialization.")
- battlecruiser_team = team
-
-/datum/antagonist/battlecruiser/apply_innate_effects(mob/living/mob_override)
- add_team_hud(mob_override || owner.current, /datum/antagonist/battlecruiser)
-
-/datum/antagonist/battlecruiser/on_gain()
- if(battlecruiser_team)
- objectives |= battlecruiser_team.objectives
- if(battlecruiser_team.nuke)
- var/obj/machinery/nuclearbomb/nuke = battlecruiser_team.nuke
- antag_memory += "[nuke] Code: [nuke.r_code]
"
- owner.add_memory(/datum/memory/key/nuke_code, nuclear_code = nuke.r_code)
- to_chat(owner, "The nuclear authorization code is: [nuke.r_code]")
- return ..()
-
/datum/outfit/syndicate_empty/battlecruiser
name = "Syndicate Battlecruiser Ship Operative"
belt = /obj/item/storage/belt/military/assault
diff --git a/tgstation.dme b/tgstation.dme
index 335b415e644..e334ca0c2c1 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2338,8 +2338,9 @@
#include "code\modules\antagonists\abductor\machinery\experiment.dm"
#include "code\modules\antagonists\abductor\machinery\pad.dm"
#include "code\modules\antagonists\ashwalker\ashwalker.dm"
-#include "code\modules\antagonists\blob\blob.dm"
-#include "code\modules\antagonists\blob\blob_mob.dm"
+#include "code\modules\antagonists\battlecruiser\battlecruiser.dm"
+#include "code\modules\antagonists\blob\blob_antag.dm"
+#include "code\modules\antagonists\blob\blob_minion.dm"
#include "code\modules\antagonists\blob\overmind.dm"
#include "code\modules\antagonists\blob\powers.dm"
#include "code\modules\antagonists\blob\blobstrains\_blobstrain.dm"
@@ -2359,8 +2360,6 @@
#include "code\modules\antagonists\blob\blobstrains\replicating_foam.dm"
#include "code\modules\antagonists\blob\blobstrains\shifting_fragments.dm"
#include "code\modules\antagonists\blob\blobstrains\synchronous_mesh.dm"
-#include "code\modules\antagonists\blob\minions\blobbernaut.dm"
-#include "code\modules\antagonists\blob\minions\blobspore.dm"
#include "code\modules\antagonists\blob\structures\_blob.dm"
#include "code\modules\antagonists\blob\structures\core.dm"
#include "code\modules\antagonists\blob\structures\factory.dm"
@@ -2396,9 +2395,10 @@
#include "code\modules\antagonists\changeling\powers\transform.dm"
#include "code\modules\antagonists\clown_ops\bananium_bomb.dm"
#include "code\modules\antagonists\clown_ops\clown_weapons.dm"
+#include "code\modules\antagonists\clown_ops\clownop.dm"
#include "code\modules\antagonists\clown_ops\outfits.dm"
-#include "code\modules\antagonists\creep\creep.dm"
#include "code\modules\antagonists\cult\blood_magic.dm"
+#include "code\modules\antagonists\cult\constructs.dm"
#include "code\modules\antagonists\cult\cult.dm"
#include "code\modules\antagonists\cult\cult_bastard_sword.dm"
#include "code\modules\antagonists\cult\cult_comms.dm"
@@ -2489,8 +2489,7 @@
#include "code\modules\antagonists\highlander\highlander.dm"
#include "code\modules\antagonists\hypnotized\hypnotized.dm"
#include "code\modules\antagonists\magic_servant\servant.dm"
-#include "code\modules\antagonists\malf_ai\datum_malf_ai.dm"
-#include "code\modules\antagonists\morph\morph.dm"
+#include "code\modules\antagonists\malf_ai\malf_ai.dm"
#include "code\modules\antagonists\morph\morph_antag.dm"
#include "code\modules\antagonists\nightmare\nightmare.dm"
#include "code\modules\antagonists\nightmare\nightmare_equipment.dm"
@@ -2503,7 +2502,6 @@
#include "code\modules\antagonists\ninja\ninja_stars.dm"
#include "code\modules\antagonists\ninja\ninjaDrainAct.dm"
#include "code\modules\antagonists\ninja\outfit.dm"
-#include "code\modules\antagonists\nukeop\clownop.dm"
#include "code\modules\antagonists\nukeop\nukeop.dm"
#include "code\modules\antagonists\nukeop\outfits.dm"
#include "code\modules\antagonists\nukeop\equipment\borgchameleon.dm"
@@ -2514,14 +2512,15 @@
#include "code\modules\antagonists\nukeop\equipment\nuclear_bomb\beer_nuke.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_bomb\self_destruct.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_bomb\syndicate_nuke.dm"
+#include "code\modules\antagonists\obsessed\obsessed.dm"
#include "code\modules\antagonists\pirate\pirate.dm"
#include "code\modules\antagonists\pirate\pirate_event.dm"
#include "code\modules\antagonists\pirate\pirate_gangs.dm"
#include "code\modules\antagonists\pirate\pirate_outfits.dm"
#include "code\modules\antagonists\pirate\pirate_roles.dm"
#include "code\modules\antagonists\pirate\pirate_special_items.dm"
+#include "code\modules\antagonists\pyro_slime\pyro_slime.dm"
#include "code\modules\antagonists\revenant\haunted_item.dm"
-#include "code\modules\antagonists\revenant\revenant.dm"
#include "code\modules\antagonists\revenant\revenant_abilities.dm"
#include "code\modules\antagonists\revenant\revenant_antag.dm"
#include "code\modules\antagonists\revenant\revenant_blight.dm"
@@ -2534,12 +2533,11 @@
#include "code\modules\antagonists\separatist\objectives.dm"
#include "code\modules\antagonists\separatist\separatist.dm"
#include "code\modules\antagonists\shade\shade_minion.dm"
-#include "code\modules\antagonists\slaughter\imp_antag.dm"
-#include "code\modules\antagonists\slaughter\slaughter.dm"
-#include "code\modules\antagonists\slaughter\slaughter_antag.dm"
#include "code\modules\antagonists\space_dragon\carp_rift.dm"
+#include "code\modules\antagonists\space_dragon\space_carp.dm"
#include "code\modules\antagonists\space_dragon\space_dragon.dm"
#include "code\modules\antagonists\space_ninja\space_ninja.dm"
+#include "code\modules\antagonists\spiders\spiders.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
#include "code\modules\antagonists\traitor\balance_helper.dm"
#include "code\modules\antagonists\traitor\datum_traitor.dm"
@@ -2574,7 +2572,10 @@
#include "code\modules\antagonists\traitor\objectives\final_objective\supermatter_cascade.dm"
#include "code\modules\antagonists\valentines\heartbreaker.dm"
#include "code\modules\antagonists\valentines\valentine.dm"
+#include "code\modules\antagonists\venus_human_trap\venus_human_trap.dm"
#include "code\modules\antagonists\wishgranter\wishgranter.dm"
+#include "code\modules\antagonists\wizard\imp_antag.dm"
+#include "code\modules\antagonists\wizard\slaughter_antag.dm"
#include "code\modules\antagonists\wizard\wizard.dm"
#include "code\modules\antagonists\wizard\equipment\artefact.dm"
#include "code\modules\antagonists\wizard\equipment\soulstone.dm"
@@ -3865,10 +3866,9 @@
#include "code\modules\mob\living\silicon\robot\robot_movement.dm"
#include "code\modules\mob\living\silicon\robot\robot_say.dm"
#include "code\modules\mob\living\simple_animal\animal_defense.dm"
-#include "code\modules\mob\living\simple_animal\constructs.dm"
#include "code\modules\mob\living\simple_animal\damage_procs.dm"
-#include "code\modules\mob\living\simple_animal\heretic_monsters.dm"
#include "code\modules\mob\living\simple_animal\parrot.dm"
+#include "code\modules\mob\living\simple_animal\revenant.dm"
#include "code\modules\mob\living\simple_animal\shade.dm"
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\bot\bot.dm"
@@ -3923,18 +3923,23 @@
#include "code\modules\mob\living\simple_animal\hostile\ant.dm"
#include "code\modules\mob\living\simple_animal\hostile\bear.dm"
#include "code\modules\mob\living\simple_animal\hostile\bees.dm"
+#include "code\modules\mob\living\simple_animal\hostile\blob.dm"
+#include "code\modules\mob\living\simple_animal\hostile\blobbernaut.dm"
+#include "code\modules\mob\living\simple_animal\hostile\blobspore.dm"
#include "code\modules\mob\living\simple_animal\hostile\cat_butcher.dm"
#include "code\modules\mob\living\simple_animal\hostile\dark_wizard.dm"
#include "code\modules\mob\living\simple_animal\hostile\eyeballs.dm"
#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm"
#include "code\modules\mob\living\simple_animal\hostile\goose.dm"
#include "code\modules\mob\living\simple_animal\hostile\headcrab.dm"
+#include "code\modules\mob\living\simple_animal\hostile\heretic_monsters.dm"
#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm"
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm"
#include "code\modules\mob\living\simple_animal\hostile\lightgeist.dm"
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
+#include "code\modules\mob\living\simple_animal\hostile\morph.dm"
#include "code\modules\mob\living\simple_animal\hostile\mushroom.dm"
#include "code\modules\mob\living\simple_animal\hostile\nanotrasen.dm"
#include "code\modules\mob\living\simple_animal\hostile\netherworld.dm"
@@ -3943,6 +3948,7 @@
#include "code\modules\mob\living\simple_animal\hostile\regalrat.dm"
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm"
+#include "code\modules\mob\living\simple_animal\hostile\slaughter_demon.dm"
#include "code\modules\mob\living\simple_animal\hostile\smspider.dm"
#include "code\modules\mob\living\simple_animal\hostile\space_dragon.dm"
#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
@@ -3954,6 +3960,11 @@
#include "code\modules\mob\living\simple_animal\hostile\zombie.dm"
#include "code\modules\mob\living\simple_animal\hostile\bosses\boss.dm"
#include "code\modules\mob\living\simple_animal\hostile\bosses\paperwizard.dm"
+#include "code\modules\mob\living\simple_animal\hostile\constructs\artificer.dm"
+#include "code\modules\mob\living\simple_animal\hostile\constructs\constructs.dm"
+#include "code\modules\mob\living\simple_animal\hostile\constructs\harvester.dm"
+#include "code\modules\mob\living\simple_animal\hostile\constructs\juggernaut.dm"
+#include "code\modules\mob\living\simple_animal\hostile\constructs\wraith.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
#include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
diff --git a/tgui/packages/tgui/interfaces/JobSelection.tsx b/tgui/packages/tgui/interfaces/JobSelection.tsx
index f46e84747db..7dd1a8225ab 100644
--- a/tgui/packages/tgui/interfaces/JobSelection.tsx
+++ b/tgui/packages/tgui/interfaces/JobSelection.tsx
@@ -3,7 +3,7 @@ import { Box, Button, StyleableSection, Icon, Stack, NoticeBox } from '../compon
import { Window } from '../layouts';
import { Color } from 'common/color';
import { SFC } from 'inferno';
-import { JobToIcon } from './common/JobToIcon';
+import { JOB2ICON } from './common/JobToIcon';
import { deepMerge } from 'common/collections';
import { BooleanLike } from 'common/react';
@@ -42,7 +42,7 @@ export const JobEntry: SFC<{
const jobName = data.jobName;
const job = data.job;
const department = data.department;
- const jobIcon = job.icon || JobToIcon[jobName] || null;
+ const jobIcon = job.icon || JOB2ICON[jobName] || null;
return (