[MANUAL MIRROR] Better groups for antagonists (#18839)

* Better groups for antagonists (#72576)

Better categorizes antagonists into groups for the orbit UI, admin
traitor panel.
Permanently hostile antags were intermixed in the "Alive" section, and
it was sometimes tough to see what's going on.
To do this, many minor antags needed new antag datums which were added
with fluff objectives as a bonus.

Group Name | Antags
--- | ---
Syndicate | Nukies, battlecruiser crew, lone op
Extradimensional Abominations | Nightmare, morph, pyro
slime
Spaceborne Leviathans | Space dragon, (spawned) carps
Invasive Overgrowth | Venus human traps
Arachnid Infestation | Spiders
Eldritch Horrors | Cult constructs, revenant

( = New antag datum, proper orbit category, new objective text)

<details>
<summary>Screenshots (outdated)</summary>

Antags get more tooltip info
![Screenshot 2023-01-08
173344](https://user-images.githubusercontent.com/42397676/211233179-d0e9229c-0f2e-4cd3-838b-fc0c5255ec9c.png)

Populating space dragon + carps (now leviathans)
![Screenshot 2023-01-08
235526](https://user-images.githubusercontent.com/42397676/211267252-2a86f8bc-4b4a-4ad6-81ed-f6b2e72d8fc0.png)

Detail to distinguish job & rank
![Screenshot 2023-01-08
233608](https://user-images.githubusercontent.com/42397676/211267334-60cabefc-b768-4a8e-8d4b-74b3d4fd45b1.png)

New categories in action
![Screenshot 2023-01-09
131003](https://user-images.githubusercontent.com/42397676/211408962-2e3c3cb9-f08e-4e27-a765-6485fe5064b0.png)

Organized Traitor Panel

![Screenshot_20230109_113453](https://user-images.githubusercontent.com/42397676/211392793-a9e6c170-60de-46bc-b0b6-02d74d45e42d.png)

</details>

Better UI, observer experience, admin panel.
It's now very obvious when something is hostile from the orbit panel.

🆑
qol: The orbit menu now sorts antagonists into groups more effectively.
Some get new named groups.
admin: Traitor panel should have slightly more sane categories.
add: Many minor antagonists now have "objectives".
refactor: Refactors code in several antag datums.
/🆑

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>

* Fixes the compile issues

* Also puts the drifting contractor in the syndicate group in the antag panel

---------

Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
This commit is contained in:
lessthanthree
2023-02-03 19:57:20 -05:00
committed by GitHub
co-authored by LemonInTheDark Jeremiah GoldenAlpharex
parent 50f6fb6716
commit 4999bdfd19
70 changed files with 1130 additions and 918 deletions
+90 -90
View File
@@ -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.
@@ -336,9 +414,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
@@ -383,21 +461,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))
@@ -435,37 +498,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
@@ -481,39 +513,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
@@ -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
@@ -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
@@ -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
@@ -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 += "<B>[nuke] Code</B>: [nuke.r_code]<br>"
owner.add_memory(/datum/memory/key/nuke_code, nuclear_code = nuke.r_code)
to_chat(owner, "The nuclear authorization code is: <B>[nuke.r_code]</B>")
return ..()
@@ -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()
@@ -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"
@@ -667,9 +667,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
@@ -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!!"
@@ -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
@@ -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 = ""
+2 -1
View File
@@ -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
@@ -223,7 +224,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
@@ -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."
+1 -1
View File
@@ -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."
@@ -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
@@ -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"
@@ -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)
@@ -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"
@@ -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
+2 -2
View File
@@ -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
@@ -122,7 +122,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
@@ -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
@@ -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!!"
@@ -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
@@ -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
@@ -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."
@@ -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
@@ -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--
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -516,7 +516,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, "<b>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.</b>")
else if(stoner)
@@ -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()
. = ..()
@@ -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
+2 -2
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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
@@ -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 = "<span class='big bold'>You are a generic construct!</span><b> Your job is to not exist, and you should probably adminhelp this.</b>"
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("<span class='[text_span]'>This is [icon2html(src, user)] \a <b>[src]</b>!\n[desc]", EXAMINE_SECTION_BREAK) //SKYRAT EDIT CHANGE
if(health < maxHealth)
if(health >= maxHealth/2)
. += span_warning("[t_He] look[t_s] slightly dented.")
else
. += span_warning("<b>[t_He] look[t_s] severely dented!</b>")
. += "</span>"
/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 <b>[src]'s</b> dents."), \
span_cult("You repair some of <b>[src]'s</b> dents, leaving <b>[src]</b> at <b>[health]/[maxHealth]</b> 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 <b>[user.health]/[user.maxHealth]</b> health."))
else
if(src != user)
to_chat(user, span_cult("You cannot repair <b>[src]'s</b> 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 = "<b>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.</b>"
/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 = "<b>You are a Wraith. Though relatively fragile, you are fast, deadly, \
can phase through walls, and your attacks will lower the cooldown on phasing.</b>"
// 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 = "<b>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, <i>and most important of all,</i> you can create new constructs \
by producing soulstones to capture souls, and shells to place those soulstones into.</b>"
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 = "<B>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.</B>"
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, "<span class='cult italic'>You have no master to seek!</span>")
the_construct.seeking = FALSE
return
if(tracking)
tracking = FALSE
the_construct.seeking = FALSE
to_chat(the_construct, "<span class='cult italic'>You are no longer tracking your master.</span>")
return
else
tracking = TRUE
the_construct.seeking = TRUE
to_chat(the_construct, "<span class='cult italic'>You are now tracking your master.</span>")
/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 class='cult italic'>You are now tracking Nar'Sie, return to reap the harvest!</span>")
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, "<span class='cult italic'>You are now tracking your prey, [real_target.real_name] - harvest [real_target.p_them()]!</span>")
else
to_chat(the_construct, "<span class='cult italic'>Nar'Sie has completed her harvest!</span>")
return
desc = "Activate to track Nar'Sie!"
button_icon_state = "sintouch"
the_construct.seeking = TRUE
@@ -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
@@ -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)
@@ -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))
@@ -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 = "<b>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, <i>and most important of all,</i> you can create new constructs \
by producing soulstones to capture souls, and shells to place those soulstones into.</b>"
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,
)
@@ -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 = "<span class='big bold'>You are a generic construct!</span><b> Your job is to not exist, and you should probably adminhelp this.</b>"
/// 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("<span class='[text_span]'>This is [icon2html(src, user)] \a <b>[src]</b>!\n[desc]")
if(health < maxHealth)
if(health >= maxHealth/2)
. += span_warning("[pronoun] look[plural] slightly dented.")
else
. += span_warning("<b>[pronoun] look[plural] severely dented!</b>")
. += "</span>"
/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 <b>[src]'s</b> 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 <b>[user.health]/[user.maxHealth]</b> health."))
return
Beam(user, icon_state="sendbeam", time = 4)
user.visible_message(span_danger("[user] repairs some of \the <b>[src]'s</b> dents."), \
span_cult("You repair some of <b>[src]'s</b> dents, leaving <b>[src]</b> at <b>[health]/[maxHealth]</b> 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
@@ -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 = "<B>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.</B>"
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
@@ -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 = "<b>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.</b>"
/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
@@ -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 = "<b>You are a Wraith. Though relatively fragile, you are fast, deadly, \
can phase through walls, and your attacks will lower the cooldown on phasing.</b>"
// 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
@@ -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("<b>[directive]</b>"))
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()
@@ -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
@@ -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 += "<B>[nuke] Code</B>: [nuke.r_code]<br>"
owner.add_memory(/datum/memory/key/nuke_code, nuclear_code = nuke.r_code)
to_chat(owner, "The nuclear authorization code is: <B>[nuke.r_code]</B>")
return ..()
/datum/outfit/syndicate_empty/battlecruiser
name = "Syndicate Battlecruiser Ship Operative"
belt = /obj/item/storage/belt/military/assault