diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_cursedtoyshop.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_cursedtoyshop.dmm
index 292c36fd8200..a7696c33c97f 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_cursedtoyshop.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_cursedtoyshop.dmm
@@ -166,7 +166,6 @@
/area/ruin/powered)
"ax" = (
/obj/structure/table/wood,
-/obj/item/toy/talking/codex_gigas,
/turf/open/floor/wood,
/area/ruin/powered)
"ay" = (
diff --git a/_maps/RandomRuins/StationRuins/maint/10x5/10x5_deltaarcade.dmm b/_maps/RandomRuins/StationRuins/maint/10x5/10x5_deltaarcade.dmm
index e41daebad355..2aeef3771577 100644
--- a/_maps/RandomRuins/StationRuins/maint/10x5/10x5_deltaarcade.dmm
+++ b/_maps/RandomRuins/StationRuins/maint/10x5/10x5_deltaarcade.dmm
@@ -251,7 +251,6 @@
/area/template_noop)
"x" = (
/obj/structure/table/wood,
-/obj/item/toy/talking/codex_gigas,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
diff --git a/_maps/map_files/AsteroidStation/AsteroidStation.dmm b/_maps/map_files/AsteroidStation/AsteroidStation.dmm
index 3361648a0fdf..17c9a47bde47 100644
--- a/_maps/map_files/AsteroidStation/AsteroidStation.dmm
+++ b/_maps/map_files/AsteroidStation/AsteroidStation.dmm
@@ -48958,10 +48958,6 @@
/area/maintenance/disposal/incinerator)
"oIa" = (
/obj/structure/table/wood,
-/obj/item/toy/talking/codex_gigas{
- pixel_x = -3;
- pixel_y = 2
- },
/obj/item/toy/talking/AI{
pixel_x = 3
},
diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index 86abc9825a01..928c4781c34f 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -9277,7 +9277,7 @@
/area/centcom/testchamber)
"aug" = (
/obj/structure/table/wood,
-/obj/item/pitchfork/demonic/ascended,
+/obj/item/pitchfork,
/obj/item/melee/powerfist,
/turf/open/floor/wood,
/area/centcom/testchamber)
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 237e776c22d3..9a7a016234aa 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -27,6 +27,8 @@
#define APPRENTICE_ROBELESS "robeless"
#define APPRENTICE_HEALING "healing"
+#define IS_INFERNAL_AGENT(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/infernal_affairs))
+#define IS_DEVIL(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/devil))
//Blob
/// blob gets a free reroll every X time
@@ -57,6 +59,9 @@
/// because they have nothing else that supports an implant.
#define UPLINK_IMPLANT_TELECRYSTAL_COST 4
+///Signal sent to a mob when they purchase an item from their uplink.
+#define COMSIG_ON_UPLINK_PURCHASE "comsig_on_uplink_purchase"
+
//ERT Types
#define ERT_BLUE "Blue"
#define ERT_RED "Red"
@@ -164,3 +169,5 @@
#define ANTAG_SECOND_WARNING 2
#define ANTAG_AWAKE 3
+///How many souls a Devil needs to 'Ascend'.
+#define DEVIL_SOULS_TO_ASCEND 8
diff --git a/code/__DEFINES/contracts.dm b/code/__DEFINES/contracts.dm
deleted file mode 100644
index c6e23394ba2d..000000000000
--- a/code/__DEFINES/contracts.dm
+++ /dev/null
@@ -1,44 +0,0 @@
-#define CONTRACT_POWER "power"
-#define CONTRACT_WEALTH "wealth"
-#define CONTRACT_PRESTIGE "prestige"
-#define CONTRACT_MAGIC "magic"
-#define CONTRACT_REVIVE "revive"
-#define CONTRACT_FRIEND "friend"
-#define CONTRACT_KNOWLEDGE "knowledge"
-#define CONTRACT_UNWILLING "unwilling"
-
-#define BANE_SALT "salt"
-#define BANE_LIGHT "light"
-#define BANE_IRON "iron"
-#define BANE_WHITECLOTHES "whiteclothes"
-#define BANE_SILVER "silver"
-#define BANE_HARVEST "harvest"
-#define BANE_TOOLBOX "toolbox"
-
-#define OBLIGATION_FOOD "food"
-#define OBLIGATION_FIDDLE "fiddle"
-#define OBLIGATION_DANCEOFF "danceoff"
-#define OBLIGATION_GREET "greet"
-#define OBLIGATION_PRESENCEKNOWN "presenceknown"
-#define OBLIGATION_SAYNAME "sayname"
-#define OBLIGATION_ANNOUNCEKILL "announcekill"
-#define OBLIGATION_ANSWERTONAME "answername"
-
-#define BAN_HURTWOMAN "hurtwoman"
-#define BAN_CHAPEL "chapel"
-#define BAN_HURTPRIEST "hurtpriest"
-#define BAN_AVOIDWATER "avoidwater"
-#define BAN_STRIKEUNCONSCIOUS "strikeunconscious"
-#define BAN_HURTLIZARD "hurtlizard"
-#define BAN_HURTANIMAL "hurtanimal"
-
-#define BANISH_WATER "water"
-#define BANISH_COFFIN "coffin"
-#define BANISH_FORMALDYHIDE "embalm"
-#define BANISH_RUNES "runes"
-#define BANISH_CANDLES "candles"
-#define BANISH_DESTRUCTION "destruction"
-#define BANISH_FUNERAL_GARB "funeral"
-
-#define LORE 1
-#define LAW 2
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index fdcc04a03b8f..417f5b401aa4 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -40,8 +40,6 @@
#define ui_lingchemdisplay "WEST,CENTER-1:15"
#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
-#define ui_devilsouldisplay "WEST:6,CENTER-1:15"
-
//Lower center, persistent menu
#define ui_sstore1 "CENTER-5:10,SOUTH:5"
#define ui_id "CENTER-4:12,SOUTH:5"
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index b8bf4aba9d65..c1d5604d1d3b 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -109,8 +109,6 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isalienqueen(A) (istype(A, /mob/living/carbon/alien/humanoid/royal/queen))
-#define isdevil(A) (istype(A, /mob/living/carbon/true_devil))
-
//Silicon mobs
#define issilicon(A) (istype(A, /mob/living/silicon))
@@ -166,7 +164,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define issupplypod(A) (istype(A, /obj/structure/closet/supplypod))
GLOBAL_LIST_INIT(shoefootmob, typecacheof(list(
- /mob/living/carbon/human/,
+ /mob/living/carbon/human,
/mob/living/simple_animal/cow,
/mob/living/simple_animal/hostile/cat_butcherer,
/mob/living/simple_animal/hostile/faithless,
@@ -180,7 +178,6 @@ GLOBAL_LIST_INIT(shoefootmob, typecacheof(list(
/mob/living/simple_animal/hostile/retaliate/spaceman,
/mob/living/simple_animal/hostile/retaliate/nanotrasenpeace,
/mob/living/simple_animal/hostile/retaliate/goat,
- /mob/living/carbon/true_devil,
)))
GLOBAL_LIST_INIT(clawfootmob, typecacheof(list(
diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm
index b333b7f18059..541fe9e9253c 100644
--- a/code/__DEFINES/language.dm
+++ b/code/__DEFINES/language.dm
@@ -12,7 +12,6 @@
#define LANGUAGE_APHASIA "aphasia"
#define LANGUAGE_CULTIST "cultist"
#define LANGUAGE_CURATOR "curator"
-#define LANGUAGE_DEVIL "devil"
#define LANGUAGE_GLAND "gland"
#define LANGUAGE_HAT "hat"
#define LANGUAGE_HIGH "high"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index f38c46bdfa0e..a5462a914398 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -104,7 +104,6 @@
#define MONKEY_BODYPART "monkey"
#define ALIEN_BODYPART "alien"
#define LARVA_BODYPART "larva"
-#define DEVIL_BODYPART "devil"
/*see __DEFINES/inventory.dm for bodypart bitflag defines*/
// Health/damage defines for carbon mobs
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index d835e5a0fea7..0ff03d6e6e95 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -6,60 +6,60 @@
//These are synced with the Database, if you change the values of the defines
//then you MUST update the database!
-#define ROLE_SYNDICATE "Syndicate"
-#define ROLE_TRAITOR "Traitor"
-#define ROLE_OPERATIVE "Operative"
-#define ROLE_CLOWNOP "Clown Operative"
-#define ROLE_CHANGELING "Changeling"
-#define ROLE_WIZARD "Wizard"
-#define ROLE_RAGINMAGES "Ragin Mages"
-#define ROLE_BULLSHITMAGES "Bullshit Mages"
-#define ROLE_MALF "Malf AI"
-#define ROLE_REV "Revolutionary"
-#define ROLE_REV_HEAD "Head Revolutionary"
-#define ROLE_ALIEN "Xenomorph"
-#define ROLE_PAI "pAI"
-#define ROLE_CULTIST "Cultist"
-#define ROLE_HERETIC "Heretic"
-#define ROLE_BLOB "Blob"
-#define ROLE_NINJA "Space Ninja"
-#define ROLE_MONKEY "Monkey"
-#define ROLE_ABDUCTOR "Abductor"
-#define ROLE_REVENANT "Revenant"
-#define ROLE_DEVIL "Devil"
-#define ROLE_SERVANT_OF_RATVAR "Servant of Ratvar"
-#define ROLE_BROTHER "Blood Brother"
-#define ROLE_BRAINWASHED "Brainwashed Victim"
-#define ROLE_HIVE "Hivemind Host"
-#define ROLE_OBSESSED "Obsessed"
-#define ROLE_SENTIENCE "Sentient Creature"
-#define ROLE_MOUSE "Mouse"
-#define ROLE_MIND_TRANSFER "Mind Transfer Potion"
-#define ROLE_POSIBRAIN "Posibrain"
-#define ROLE_DRONE "Drone"
-#define ROLE_DEATHSQUAD "Deathsquad"
-#define ROLE_LAVALAND "Lavaland"
-#define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent"
-#define ROLE_FUGITIVE "Fugitive"
-#define ROLE_SHADOWLING "Shadowling" // Yogs
-#define ROLE_VAMPIRE "Vampire" // Yogs
-#define ROLE_GANG "gangster" // Yogs
-#define ROLE_DARKSPAWN "darkspawn" // Yogs
-#define ROLE_HOLOPARASITE "Holoparasite" // Yogs
-#define ROLE_HORROR "Eldritch Horror" // Yogs
-#define ROLE_INFILTRATOR "Infiltrator" // Yogs
-#define ROLE_ZOMBIE "Zombie"
-#define ROLE_BLOODSUCKER "Bloodsucker"
-#define ROLE_VAMPIRICACCIDENT "Vampiric Accident"
-#define ROLE_BLOODSUCKERBREAKOUT "Bloodsucker Breakout"
-#define ROLE_MONSTERHUNTER "Monster Hunter"
-#define ROLE_SPACE_DRAGON "Space Dragon"
-#define ROLE_GOLEM "Golem"
-#define ROLE_SINFULDEMON "Demon of Sin"
-#define ROLE_GHOSTBEACON "Ghost Beacon"
-#define ROLE_NIGHTMARE "Nightmare"
-#define ROLE_DISEASE "Disease"
-#define ROLE_PIRATE "Pirate"
+#define ROLE_SYNDICATE "Syndicate"
+#define ROLE_TRAITOR "Traitor"
+#define ROLE_OPERATIVE "Operative"
+#define ROLE_CLOWNOP "Clown Operative"
+#define ROLE_CHANGELING "Changeling"
+#define ROLE_WIZARD "Wizard"
+#define ROLE_INFERNAL_AFFAIRS "Infernal Affairs Agent"
+#define ROLE_INFERNAL_AFFAIRS_DEVIL "Infernal Devil"
+#define ROLE_RAGINMAGES "Ragin Mages"
+#define ROLE_BULLSHITMAGES "Bullshit Mages"
+#define ROLE_MALF "Malf AI"
+#define ROLE_REV "Revolutionary"
+#define ROLE_REV_HEAD "Head Revolutionary"
+#define ROLE_ALIEN "Xenomorph"
+#define ROLE_PAI "pAI"
+#define ROLE_CULTIST "Cultist"
+#define ROLE_HERETIC "Heretic"
+#define ROLE_BLOB "Blob"
+#define ROLE_NINJA "Space Ninja"
+#define ROLE_MONKEY "Monkey"
+#define ROLE_ABDUCTOR "Abductor"
+#define ROLE_REVENANT "Revenant"
+#define ROLE_SERVANT_OF_RATVAR "Servant of Ratvar"
+#define ROLE_BROTHER "Blood Brother"
+#define ROLE_BRAINWASHED "Brainwashed Victim"
+#define ROLE_HIVE "Hivemind Host"
+#define ROLE_OBSESSED "Obsessed"
+#define ROLE_SENTIENCE "Sentient Creature"
+#define ROLE_MOUSE "Mouse"
+#define ROLE_MIND_TRANSFER "Mind Transfer Potion"
+#define ROLE_POSIBRAIN "Posibrain"
+#define ROLE_DRONE "Drone"
+#define ROLE_DEATHSQUAD "Deathsquad"
+#define ROLE_LAVALAND "Lavaland"
+#define ROLE_FUGITIVE "Fugitive"
+#define ROLE_SHADOWLING "Shadowling" // Yogs
+#define ROLE_VAMPIRE "Vampire" // Yogs
+#define ROLE_GANG "gangster" // Yogs
+#define ROLE_DARKSPAWN "darkspawn" // Yogs
+#define ROLE_HOLOPARASITE "Holoparasite" // Yogs
+#define ROLE_HORROR "Eldritch Horror" // Yogs
+#define ROLE_INFILTRATOR "Infiltrator" // Yogs
+#define ROLE_ZOMBIE "Zombie"
+#define ROLE_BLOODSUCKER "Bloodsucker"
+#define ROLE_VAMPIRICACCIDENT "Vampiric Accident"
+#define ROLE_BLOODSUCKERBREAKOUT "Bloodsucker Breakout"
+#define ROLE_MONSTERHUNTER "Monster Hunter"
+#define ROLE_SPACE_DRAGON "Space Dragon"
+#define ROLE_GOLEM "Golem"
+#define ROLE_SINFULDEMON "Demon of Sin"
+#define ROLE_GHOSTBEACON "Ghost Beacon"
+#define ROLE_NIGHTMARE "Nightmare"
+#define ROLE_DISEASE "Disease"
+#define ROLE_PIRATE "Pirate"
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
@@ -70,9 +70,11 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_TRAITOR = /datum/antagonist/traitor,
ROLE_OPERATIVE = /datum/antagonist/nukeop,
ROLE_CLOWNOP = /datum/antagonist/nukeop/clownop,
- ROLE_CHANGELING = /datum/antagonist/changeling,
+ ROLE_CHANGELING = /datum/antagonist/changeling,
ROLE_WIZARD = /datum/antagonist/wizard,
- ROLE_RAGINMAGES = /datum/antagonist/wizard,
+ ROLE_INFERNAL_AFFAIRS = /datum/antagonist/infernal_affairs,
+ ROLE_INFERNAL_AFFAIRS_DEVIL = /datum/antagonist/devil,
+ ROLE_RAGINMAGES = /datum/antagonist/wizard,
ROLE_BULLSHITMAGES = /datum/antagonist/wizard,
ROLE_MALF = /datum/antagonist/traitor/malf,
ROLE_REV_HEAD = /datum/antagonist/rev/head,
@@ -81,15 +83,13 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_HERETIC = /datum/antagonist/heretic,
ROLE_BLOB = /datum/antagonist/blob,
ROLE_NINJA = /datum/antagonist/ninja,
- ROLE_MONKEY = /datum/antagonist/monkey,
+ ROLE_MONKEY = /datum/antagonist/monkey,
ROLE_ABDUCTOR = /datum/antagonist/abductor,
ROLE_REVENANT = /datum/antagonist/revenant,
- ROLE_DEVIL = /datum/antagonist/devil,
ROLE_SERVANT_OF_RATVAR = /datum/antagonist/clockcult,
ROLE_BROTHER = /datum/antagonist/brother,
ROLE_BRAINWASHED = /datum/antagonist/brainwashed,
ROLE_OBSESSED = /datum/antagonist/obsessed,
- ROLE_INTERNAL_AFFAIRS = /datum/antagonist/traitor/internal_affairs,
ROLE_FUGITIVE = /datum/antagonist/fugitive,
ROLE_SHADOWLING = /datum/antagonist/shadowling, // Yogs
ROLE_VAMPIRE = /datum/antagonist/vampire, // Yogs
@@ -98,7 +98,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_HOLOPARASITE = /datum/antagonist/guardian, // Yogs
ROLE_HORROR = /datum/antagonist/horror, // Yogs
ROLE_INFILTRATOR = /datum/antagonist/infiltrator, // Yogs
- ROLE_ZOMBIE = /datum/antagonist/zombie,
+ ROLE_ZOMBIE = /datum/antagonist/zombie,
ROLE_BLOODSUCKER = /datum/antagonist/bloodsucker,
ROLE_MONSTERHUNTER = /datum/antagonist/monsterhunter,
ROLE_SPACE_DRAGON = /datum/antagonist/space_dragon,
@@ -112,6 +112,6 @@ GLOBAL_LIST_INIT(special_roles, list(
))
//Job defines for what happens when you fail to qualify for any job during job selection
-#define BEOVERFLOW 1
-#define BERANDOMJOB 2
-#define RETURNTOLOBBY 3
+#define BEOVERFLOW 1
+#define BERANDOMJOB 2
+#define RETURNTOLOBBY 3
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 4c00d18f64e8..af809b4ef8cc 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -143,6 +143,7 @@
#define TRAIT_MUTE "mute"
#define TRAIT_EMOTEMUTE "emotemute"
#define TRAIT_DEAF "deaf"
+#define TRAIT_DEVIL_CONTRACT_IMMUNE "devil_contract_immune"
#define TRAIT_NEARSIGHT "nearsighted"
#define TRAIT_FAT "fat"
#define TRAIT_HUSK "husk"
@@ -289,6 +290,9 @@
/// You can't see color!
#define TRAIT_COLORBLIND "color_blind"
+///Trait given to a mob's mind to indicate they can't be revived due to otherworldly events.
+#define TRAIT_HELLBOUND "hellbound"
+
/// This person is crying
#define TRAIT_CRYING "crying"
/// This human wants to see the color of their glasses, for some reason
@@ -346,6 +350,7 @@
#define GENETIC_MUTATION "genetic"
#define OBESITY "obesity"
#define MAGIC_TRAIT "magic"
+#define DEVIL_TRAIT "devil"
#define TRAUMA_TRAIT "trauma"
#define DISEASE_TRAIT "disease"
#define SPECIES_TRAIT "species"
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index fedd97ad3a54..9b824c37e618 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -334,8 +334,6 @@ Turf and target are separate in case you want to teleport some distance from a t
moblist.Add(M)
for(var/mob/living/simple_animal/M in sortmob)
moblist.Add(M)
- for(var/mob/living/carbon/true_devil/M in sortmob)
- moblist.Add(M)
return moblist
// Format a power value in W, kW, MW, or GW.
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 343c748e1ebd..93c310ed78e5 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -432,7 +432,7 @@ GLOBAL_LIST_INIT(maintenance_loot_traditional,list(
/obj/item/melee/vxtvulhammer/toy = W_RARE,
/obj/item/toy/syndicateballoon = W_RARE,
/obj/item/toy/talking/AI = W_RARE,
- /obj/item/toy/talking/codex_gigas = W_RARE,
+ /obj/item/toy/codex_gigas = W_RARE,
/obj/item/toy/talking/griffin = W_RARE,
/obj/item/toy/talking/owl = W_RARE,
/obj/item/toy/toy_dagger = W_RARE,
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 59b19ad1bdab..d5e5126adff7 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -32,7 +32,7 @@ GLOBAL_LIST_EMPTY(tdome2)
GLOBAL_LIST_EMPTY(tdomeobserve)
GLOBAL_LIST_EMPTY(tdomeadmin)
GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped
-GLOBAL_LIST_EMPTY(blobstart) //stationloving objects, blobs, santa, respawning devils
+GLOBAL_LIST_EMPTY(blobstart) //stationloving objects, blobs, santa
GLOBAL_LIST_EMPTY(secequipment) //sec equipment lockers that scale with the number of sec players
GLOBAL_LIST_EMPTY(deathsquadspawn)
GLOBAL_LIST_EMPTY(emergencyresponseteamspawn)
diff --git a/code/_onclick/hud/devil.dm b/code/_onclick/hud/devil.dm
deleted file mode 100644
index 618aa89189f4..000000000000
--- a/code/_onclick/hud/devil.dm
+++ /dev/null
@@ -1,60 +0,0 @@
-
-//Soul counter is stored with the humans, it does weird when you place it here apparently...
-
-
-/datum/hud/devil/New(mob/owner)
- ..()
- var/atom/movable/screen/using
-
- using = new /atom/movable/screen/drop(src)
- using.icon = ui_style
- using.screen_loc = ui_drone_drop
- static_inventory += using
-
- pull_icon = new /atom/movable/screen/pull(src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance(UPDATE_ICON)
- pull_icon.screen_loc = ui_drone_pull
- static_inventory += pull_icon
-
- build_hand_slots()
-
- using = new /atom/movable/screen/inventory(src)
- using.name = "hand"
- using.icon = ui_style
- using.icon_state = "swap_1_m"
- using.screen_loc = ui_swaphand_position(owner,1)
- using.layer = HUD_LAYER
- using.plane = HUD_PLANE
- static_inventory += using
-
- using = new /atom/movable/screen/inventory(src)
- using.name = "hand"
- using.icon = ui_style
- using.icon_state = "swap_2"
- using.screen_loc = ui_swaphand_position(owner,2)
- using.layer = HUD_LAYER
- using.plane = HUD_PLANE
- static_inventory += using
-
- zone_select = new /atom/movable/screen/zone_sel(src)
- zone_select.icon = ui_style
- zone_select.update_appearance(UPDATE_ICON)
-
- devilsouldisplay = new /atom/movable/screen/devil/soul_counter(src)
- infodisplay += devilsouldisplay
-
-
-/datum/hud/devil/persistent_inventory_update()
- if(!mymob)
- return
- var/mob/living/carbon/true_devil/D = mymob
-
- if(hud_version != HUD_STYLE_NOHUD)
- for(var/obj/item/I in D.held_items)
- I.screen_loc = ui_hand_position(D.get_held_index_of_item(I))
- D.client.screen += I
- else
- for(var/obj/item/I in D.held_items)
- I.screen_loc = null
- D.client.screen -= I
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 61aed71beb91..7bdca25ad588 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -31,8 +31,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/alien_plasma_display
var/atom/movable/screen/alien_queen_finder
- var/atom/movable/screen/devil/soul_counter/devilsouldisplay
-
var/atom/movable/screen/action_intent
var/atom/movable/screen/zone_select
var/atom/movable/screen/pull_icon
@@ -114,7 +112,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
healths = null
stamina = null
healthdoll = null
- devilsouldisplay = null
blobpwrdisplay = null
alien_plasma_display = null
alien_queen_finder = null
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 0a12a8a649da..0aa7394cbc1f 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -33,35 +33,6 @@
var/mob/living/carbon/human/H = usr
H.quick_equip()
-/atom/movable/screen/devil
- invisibility = INVISIBILITY_ABSTRACT
-
-/atom/movable/screen/devil/soul_counter
- icon = 'icons/mob/screen_gen.dmi'
- name = "souls owned"
- icon_state = "Devil-6"
- screen_loc = ui_devilsouldisplay
-
-/atom/movable/screen/devil/soul_counter/proc/update_counter(souls)
- invisibility = 0
- maptext = ANTAG_MAPTEXT(souls, COLOR_RED)
- switch(souls)
- if(0, null)
- icon_state = "Devil-1"
- if(1,2)
- icon_state = "Devil-2"
- if(3 to 5)
- icon_state = "Devil-3"
- if(6 to 8)
- icon_state = "Devil-4"
- if(9 to INFINITY)
- icon_state = "Devil-5"
- else
- icon_state = "Devil-6"
-
-/atom/movable/screen/devil/soul_counter/proc/clear()
- invisibility = INVISIBILITY_ABSTRACT
-
/atom/movable/screen/ling/sting
name = "current sting"
screen_loc = ui_lingstingdisplay
@@ -291,9 +262,6 @@
pull_icon.screen_loc = ui_above_intent
static_inventory += pull_icon
- devilsouldisplay = new /atom/movable/screen/devil/soul_counter(src)
- infodisplay += devilsouldisplay
-
zone_select = new /atom/movable/screen/zone_sel(src)
zone_select.icon = ui_style
zone_select.update_appearance(UPDATE_ICON)
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 45aa05ea2801..8cdf6039e574 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -202,13 +202,6 @@
/atom/proc/attack_drone(mob/living/simple_animal/drone/user)
attack_hand(user) //defaults to attack_hand. Override it when you don't want drones to do same stuff as humans.
-/*
- True Devil
-*/
-
-/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity)
- A.attack_hand(src)
-
/*
Brain
*/
diff --git a/code/controllers/subsystem/infernal_affairs.dm b/code/controllers/subsystem/infernal_affairs.dm
new file mode 100644
index 000000000000..cbe92d3d07dc
--- /dev/null
+++ b/code/controllers/subsystem/infernal_affairs.dm
@@ -0,0 +1,66 @@
+/**
+ * ##infernal_affairs subsystem
+ *
+ * Supposed to handle the objectives of all Agents, ensuring they all have to kill eachother.
+ * Also keeps track of their Antag gear, to remove it when their soul is collected.
+ */
+SUBSYSTEM_DEF(infernal_affairs)
+ name = "Devil Affairs"
+ flags = SS_NO_INIT|SS_NO_FIRE
+
+ ///List of all devils in-game. There is supposed to have only one, so this is in-case admins do some wacky shit.
+ var/list/datum/antagonist/devil/devils = list()
+ ///List of all Agents in the loop and the gear they have.
+ var/list/datum/antagonist/infernal_affairs/agent_datums = list()
+
+/**
+ * Enters a for() loop for all agents while assigning their target to be the first available agent.
+ *
+ * We assign all IAAs their position in the list to later assign them as objectives of one another.
+ * Lists starts at 1, so we will immediately imcrement to get their target.
+ * When the list goes over, we go back to the start AFTER incrementing the list, so they will have the first player as a target.
+ * We skip over Hellbound people, and when there's only one left alive, we'll end the loop.
+ */
+/datum/controller/subsystem/infernal_affairs/proc/update_objective_datums()
+ if(!agent_datums.len)
+ return
+ var/list_position = 1
+ for(var/datum/antagonist/infernal_affairs/agents as anything in agent_datums)
+ if(!agents.active_objective)
+ agents.active_objective = new(src)
+ agents.objectives += agents.active_objective
+ var/objective_set = FALSE
+ while(!objective_set)
+ list_position++
+ if(list_position > agent_datums.len)
+ list_position = 1
+ var/datum/antagonist/infernal_affairs/next_agent = agent_datums[list_position]
+ if(next_agent == agents)
+ end_loop(agents)
+ objective_set = TRUE
+ break
+ if(agents.active_objective.target != next_agent)
+ agents.active_objective.target = next_agent.owner
+ agents.active_objective.update_explanation_text()
+ agents.update_static_data(agents.owner.current)
+ else if(agents.active_objective.target.current.stat == DEAD)
+ agents.active_objective.update_explanation_text()
+ agents.update_static_data(agents.owner.current)
+ objective_set = TRUE
+ break
+ return TRUE
+
+/**
+ * ## end_loop
+ *
+ * We unregister signal to stop listening for people in the loop, as it's over.
+ * We will then give the last man standing their hijack objective, to end the subsystem off
+ * Args:
+ * - last_man_standing: The antag datum of the last remaining player left alive.
+ */
+/datum/controller/subsystem/infernal_affairs/proc/end_loop(datum/antagonist/infernal_affairs/last_one_standing)
+ var/datum/objective/hijack/hijack_objective = new()
+ hijack_objective.owner = last_one_standing.owner
+ hijack_objective.explanation_text = hijack_objective
+ last_one_standing.objectives += hijack_objective
+ last_one_standing.update_static_data(last_one_standing.owner.current)
diff --git a/code/datums/achievements/achievements.dm b/code/datums/achievements/achievements.dm
index c78c87ff3c06..7bd316c34d6e 100644
--- a/code/datums/achievements/achievements.dm
+++ b/code/datums/achievements/achievements.dm
@@ -249,6 +249,12 @@
desc = "As a revenant, complete your objectives"
id = GREENTEXT + 17
+/datum/achievement/greentext/devil
+ name = "Eternal Infernal"
+ desc = "As a devil, ascend to your greatest power"
+ id = GREENTEXT + 17
+
+
//end-greentext
//start-redtext
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index cc6cc53b7ee5..7c033400bd04 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -1,4 +1,3 @@
-#define LAW_DEVIL "devil"
#define LAW_ZEROTH "zeroth"
#define LAW_INHERENT "inherent"
#define LAW_SUPPLIED "supplied"
@@ -15,7 +14,6 @@
var/list/ion = list()
var/list/hacked = list()
var/mob/living/silicon/owner
- var/list/devillaws = list()
var/id = DEFAULT_AI_LAWID
/datum/ai_laws/proc/lawid_to_type(lawid)
@@ -510,8 +508,6 @@
/datum/ai_laws/proc/get_law_amount(groups)
var/law_amount = 0
- if(devillaws && (LAW_DEVIL in groups))
- law_amount++
if(zeroth && (LAW_ZEROTH in groups))
law_amount++
if(ion.len && (LAW_ION in groups))
@@ -527,9 +523,6 @@
law_amount++
return law_amount
-/datum/ai_laws/proc/set_law_sixsixsix(laws)
- devillaws = laws
-
/datum/ai_laws/proc/set_zeroth_law(law, law_borg = null)
zeroth = law
if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO
@@ -666,10 +659,6 @@
zeroth = null
zeroth_borg = null
-/datum/ai_laws/proc/clear_law_sixsixsix(force)
- if(force || !is_devil(owner))
- devillaws = null
-
/datum/ai_laws/proc/associate(mob/living/silicon/M)
if(!owner)
owner = M
@@ -677,10 +666,6 @@
/datum/ai_laws/proc/get_law_list(include_zeroth = 0, show_numbers = 1)
var/list/data = list()
- if (include_zeroth && devillaws && devillaws.len)
- for(var/i in devillaws)
- data += "[show_numbers ? "666:" : ""] [i]"
-
if (include_zeroth && zeroth)
data += "[show_numbers ? "0:" : ""] [zeroth]"
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 3691cdd0c14f..4db934034920 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -387,7 +387,7 @@
L.fields["species"] = H.dna.species.type
L.fields["features"] = H.dna.features
L.fields["image"] = image
- L.fields["mindref"] = H.mind
+ L.fields["mindref"] = H.mind
locked += L
return
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 07f65bcc87e6..7e610e88caf1 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -346,32 +346,32 @@
if(!uplink_loc) // We've looked everywhere, let's just implant you
implant = TRUE
-
+
+ var/datum/component/uplink/uplink_component
+
if(!implant)
. = uplink_loc
- var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
- if(!U)
+ uplink_component = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
+ if(!uplink_component)
CRASH("Uplink creation failed.")
- U.setup_unlock_code()
+ uplink_component.setup_unlock_code()
if(!silent)
if(uplink_loc == R)
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(uplink_component.unlock_code)] to unlock its hidden features.")
else if(uplink_loc == PDA)
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[uplink_component.unlock_code]\" into the ringtone select to unlock its hidden features.")
else if(uplink_loc == P)
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [english_list(U.unlock_code)] from its starting position to unlock its hidden features.")
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [english_list(uplink_component.unlock_code)] from its starting position to unlock its hidden features.")
if(uplink_owner)
- uplink_owner.antag_memory += U.unlock_note + " "
+ uplink_owner.antag_memory += uplink_component.unlock_note + " "
else
- traitor_mob.mind.store_memory(U.unlock_note)
+ traitor_mob.mind.store_memory(uplink_component.unlock_note)
else
- var/obj/item/implant/uplink/starting/I = new(traitor_mob)
- I.implant(traitor_mob, null, silent = TRUE)
+ var/obj/item/implant/uplink/starting/I = new()
+ uplink_component = I.implant(traitor_mob, null, silent = TRUE)
if(!silent)
to_chat(traitor_mob, "[employer] has cunningly implanted you with a Syndicate Uplink (although uplink implants cost valuable TC, so you will have slightly less). Simply trigger the uplink to access it.")
- return I
-
-
+ return uplink_component
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
@@ -708,9 +708,6 @@
add_antag_datum(head)
special_role = ROLE_REV_HEAD
-/datum/mind/proc/owns_soul()
- return soulOwner == src
-
/datum/mind/proc/transfer_martial_arts(mob/living/new_character)
if(!ishuman(new_character))
return
diff --git a/code/datums/soullink.dm b/code/datums/soullink.dm
deleted file mode 100644
index de73a8e74c6d..000000000000
--- a/code/datums/soullink.dm
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-/mob/living
- var/list/ownedSoullinks //soullinks we are the owner of
- var/list/sharedSoullinks //soullinks we are a/the sharer of
-
-/mob/living/Destroy()
- for(var/s in ownedSoullinks)
- var/datum/soullink/S = s
- S.ownerDies(FALSE)
- qdel(s) //If the owner is destroy()'d, the soullink is destroy()'d
- ownedSoullinks = null
- for(var/s in sharedSoullinks)
- var/datum/soullink/S = s
- S.sharerDies(FALSE)
- S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed
- sharedSoullinks = null
- return ..()
-
-
-
-//Keeps track of a Mob->Mob (potentially Player->Player) connection
-//Can be used to trigger actions on one party when events happen to another
-//Eg: shared deaths
-//Can be used to form a linked list of mob-hopping
-//Does NOT transfer with minds
-/datum/soullink
- var/mob/living/soulowner
- var/mob/living/soulsharer
- var/id //Optional ID, for tagging and finding specific instances
-
-/datum/soullink/Destroy()
- if(soulowner)
- LAZYREMOVE(soulowner.ownedSoullinks, src)
- soulowner = null
- if(soulsharer)
- LAZYREMOVE(soulsharer.sharedSoullinks, src)
- soulsharer = null
- return ..()
-
-/datum/soullink/proc/removeSoulsharer(mob/living/sharer)
- if(soulsharer == sharer)
- soulsharer = null
- LAZYREMOVE(sharer.sharedSoullinks, src)
-
-//Used to assign variables, called primarily by soullink()
-//Override this to create more unique soullinks (Eg: 1->Many relationships)
-//Return TRUE/FALSE to return the soullink/null in soullink()
-/datum/soullink/proc/parseArgs(mob/living/owner, mob/living/sharer)
- if(!owner || !sharer)
- return FALSE
- soulowner = owner
- soulsharer = sharer
- LAZYADD(owner.ownedSoullinks, src)
- LAZYADD(sharer.sharedSoullinks, src)
- return TRUE
-
-//Runs after /living death()
-//Override this for content
-/datum/soullink/proc/ownerDies(gibbed, mob/living/owner)
-
-//Runs after /living death()
-//Override this for content
-/datum/soullink/proc/sharerDies(gibbed, mob/living/owner)
-
-//Quick-use helper
-/proc/soullink(typepath, ...)
- var/datum/soullink/S = new typepath()
- if(S.parseArgs(arglist(args.Copy(2, 0))))
- return S
-
-
-
-/////////////////
-// MULTISHARER //
-/////////////////
-//Abstract soullink for use with 1 Owner -> Many Sharer setups
-/datum/soullink/multisharer
- var/list/soulsharers
-
-/datum/soullink/multisharer/parseArgs(mob/living/owner, list/sharers)
- if(!owner || !LAZYLEN(sharers))
- return FALSE
- soulowner = owner
- soulsharers = sharers
- LAZYADD(owner.ownedSoullinks, src)
- for(var/l in sharers)
- var/mob/living/L = l
- LAZYADD(L.sharedSoullinks, src)
- return TRUE
-
-/datum/soullink/multisharer/removeSoulsharer(mob/living/sharer)
- LAZYREMOVE(soulsharers, sharer)
-
-
-
-/////////////////
-// SHARED FATE //
-/////////////////
-//When the soulowner dies, the soulsharer dies, and vice versa
-//This is intended for two players(or AI) and two mobs
-
-/datum/soullink/sharedfate/ownerDies(gibbed, mob/living/owner)
- if(soulsharer)
- soulsharer.death(gibbed)
-
-/datum/soullink/sharedfate/sharerDies(gibbed, mob/living/sharer)
- if(soulowner)
- soulowner.death(gibbed)
-
-/////////////////
-// Demon Bind //
-/////////////////
-//When the soulowner dies, the soulsharer dies, but NOT vice versa
-//This is intended for two players(or AI) and two mobs
-
-/datum/soullink/oneway/ownerDies(gibbed, mob/living/owner)
- if(soulsharer)
- soulsharer.dust(FALSE)
-
-
-/////////////////
-// SHARED BODY //
-/////////////////
-//When the soulsharer dies, they're placed in the soulowner, who remains alive
-//If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying)
-//This one is intended for one player moving between many mobs
-
-/datum/soullink/sharedbody/ownerDies(gibbed, mob/living/owner)
- if(soulowner && soulsharer)
- if(soulsharer.mind)
- soulsharer.mind.transfer_to(soulowner)
- soulsharer.death(gibbed)
-
-/datum/soullink/sharedbody/sharerDies(gibbed, mob/living/sharer)
- if(soulowner && soulsharer && soulsharer.mind)
- soulsharer.mind.transfer_to(soulowner)
-
-
-
-//////////////////////
-// REPLACEMENT POOL //
-//////////////////////
-//When the owner dies, one of the sharers is placed in the owner's body, fully healed
-//Sort of a "winner-stays-on" soullink
-//Gibbing ends it immediately
-
-/datum/soullink/multisharer/replacementpool/ownerDies(gibbed, mob/living/owner)
- if(LAZYLEN(soulsharers) && !gibbed) //let's not put them in some gibs
- var/list/souls = shuffle(soulsharers.Copy())
- for(var/l in souls)
- var/mob/living/L = l
- if(L.stat != DEAD && L.mind)
- L.mind.transfer_to(soulowner)
- soulowner.revive(TRUE, TRUE)
- L.death(FALSE)
-
-//Lose your claim to the throne!
-/datum/soullink/multisharer/replacementpool/sharerDies(gibbed, mob/living/sharer)
- removeSoulsharer(sharer)
diff --git a/code/datums/status_effects/agent_pinpointer.dm b/code/datums/status_effects/agent_pinpointer.dm
new file mode 100644
index 000000000000..d620ce3796c3
--- /dev/null
+++ b/code/datums/status_effects/agent_pinpointer.dm
@@ -0,0 +1,78 @@
+#define PINPOINTER_MINIMUM_RANGE 15
+#define PINPOINTER_EXTRA_RANDOM_RANGE 10
+#define PINPOINTER_PING_TIME (4 SECONDS)
+
+/atom/movable/screen/alert/status_effect/agent_pinpointer
+ name = "Target Integrated Pinpointer"
+ desc = "Even stealthier than a normal implant, it points to any assassination target you have."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "pinon"
+
+/datum/status_effect/agent_pinpointer
+ id = "agent_pinpointer"
+ duration = -1
+ tick_interval = PINPOINTER_PING_TIME
+ alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer
+ ///The minimum range to start pointing towards your target.
+ var/minimum_range = PINPOINTER_MINIMUM_RANGE
+ ///How fuzzy will the pinpointer be, messing with it pointing to your target.
+ var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
+ ///The range until you're considered 'close'
+ var/range_mid = 8
+ ///The range until you're considered 'too far away'
+ var/range_far = 16
+ ///The target we are pointing towards, refreshes every tick.
+ var/mob/scan_target
+
+/datum/status_effect/agent_pinpointer/tick()
+ if(!owner)
+ qdel(src)
+ return
+ scan_for_target()
+ point_to_target()
+
+///Show the distance and direction of a scanned target
+/datum/status_effect/agent_pinpointer/proc/point_to_target()
+ if(!scan_target)
+ linked_alert.icon_state = "pinonnull"
+ return
+
+ var/turf/here = get_turf(owner)
+ var/turf/there = get_turf(scan_target)
+
+ if(here.z != there.z)
+ linked_alert.icon_state = "pinonnull"
+ return
+ if(get_dist_euclidian(here,there) <= minimum_range + rand(0, range_fuzz_factor))
+ linked_alert.icon_state = "pinondirect"
+ return
+ linked_alert.setDir(get_dir(here, there))
+
+ var/dist = (get_dist(here, there))
+ if(dist >= 1 && dist <= range_mid)
+ linked_alert.icon_state = "pinonclose"
+ else if(dist > range_mid && dist <= range_far)
+ linked_alert.icon_state = "pinonmedium"
+ else if(dist > range_far)
+ linked_alert.icon_state = "pinonfar"
+
+///Attempting to locate a nearby target to scan and point towards.
+/datum/status_effect/agent_pinpointer/proc/scan_for_target()
+ scan_target = null
+ if(!owner && !owner.mind)
+ return
+ for(var/datum/objective/assassinate/objective_datums as anything in owner.mind.get_all_objectives())
+ if(!objective_datums.target || !objective_datums.target.current || objective_datums.target.current.stat == DEAD)
+ continue
+ var/mob/tracked_target = objective_datums.target.current
+ //JUUUST in case.
+ if(!tracked_target)
+ continue
+
+ //Catch the first one we find, then stop. We want to point to the most recent one we've got.
+ scan_target = tracked_target
+ break
+
+#undef PINPOINTER_EXTRA_RANDOM_RANGE
+#undef PINPOINTER_MINIMUM_RANGE
+#undef PINPOINTER_PING_TIME
diff --git a/code/game/gamemodes/devil/devil agent/devil_agent.dm b/code/game/gamemodes/devil/devil agent/devil_agent.dm
deleted file mode 100644
index c8fb62faba09..000000000000
--- a/code/game/gamemodes/devil/devil agent/devil_agent.dm
+++ /dev/null
@@ -1,44 +0,0 @@
-/datum/game_mode/devil/devil_agents
- name = "Devil Agents"
- config_tag = "devil_agents"
- required_players = 25
- required_enemies = 3
- recommended_enemies = 8
- reroll_friendly = 0
-
- traitors_possible = 10 //hard limit on traitors if scaling is turned off
- num_modifier = 4
- objective_count = 2
-
- var/list/devil_target_list = list() //will update to be a child of internal affairs when bothered
- var/list/devil_late_joining_list = list()
- minimum_devils = 3
-
- announce_text = "There are devil agents onboard the station, trying to outbid each other!\n\
- + Devils: Purchase souls and interfere with your rivals!\n\
- + Crew: Resist the lure of sin and remain pure!"
-
-/datum/game_mode/devil/devil_agents/post_setup()
- var/i = 0
- for(var/datum/mind/devil in devils)
- i++
- if(i + 1 > devils.len)
- i = 0
- devil_target_list[devil] = devils[i + 1]
- ..()
-
-/datum/game_mode/devil/devil_agents/add_devil_objectives(datum/mind/devil_mind, quantity)
- ..(devil_mind, quantity - give_outsell_objective(devil_mind))
-
-/datum/game_mode/devil/devil_agents/proc/give_outsell_objective(datum/mind/devil)
- //If you override this method, have it return the number of objectives added.
- if(devil_target_list.len && devil_target_list[devil]) // Is a double agent
- var/datum/mind/target_mind = devil_target_list[devil]
- var/datum/antagonist/devil/D = target_mind.has_antag_datum(/datum/antagonist/devil)
- var/datum/objective/devil/outsell/outsellobjective = new
- outsellobjective.owner = devil
- outsellobjective.target = target_mind
- outsellobjective.update_explanation_text()
- D.objectives += outsellobjective
- return 1
- return 0
diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm
deleted file mode 100644
index c329f724330b..000000000000
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ /dev/null
@@ -1,105 +0,0 @@
-/datum/game_mode/devil
- name = "devil"
- config_tag = "devil"
- report_type = "devil"
- antag_flag = ROLE_DEVIL
- false_report_weight = 1
- protected_jobs = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
- required_players = 25
- required_enemies = 1
- recommended_enemies = 4
- reroll_friendly = 1
- enemy_minimum_age = 0
- title_icon = "devil"
- restricted_jobs = list("Cyborg", "AI")
- protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Brig Physician") //YOGS - added the hop and brig physician
-
- var/traitors_possible = 4 //hard limit on devils if scaling is turned off
- var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
- var/objective_count = 2
- var/minimum_devils = 1
-
- announce_text = "There are devils onboard the station!\n\
- + Devils: Purchase souls and tempt the crew to sin!\n\
- + Crew: Resist the lure of sin and remain pure!"
-
-/datum/game_mode/devil/pre_setup()
- if(CONFIG_GET(flag/protect_roles_from_antagonist))
- restricted_jobs += protected_jobs
- if(CONFIG_GET(flag/protect_assistant_from_antagonist))
- restricted_jobs += "Assistant"
-
- var/num_devils = 1
-
- var/tsc = CONFIG_GET(number/traitor_scaling_coeff)
- if(tsc)
- num_devils = max(minimum_devils, min( round(num_players() / (tsc * 3))+ 2 + num_modifier, round(num_players() / (tsc * 1.5)) + num_modifier))
- else
- num_devils = max(minimum_devils, min(num_players(), traitors_possible))
-
- for(var/j = 0, j < num_devils, j++)
- if (!antag_candidates.len)
- break
- var/datum/mind/devil = antag_pick(antag_candidates)
- devils += devil
- devil.special_role = traitor_name
- devil.restricted_roles = restricted_jobs
-
- //log_game("[key_name(devil)] has been selected as a [traitor_name]") | yogs - redundant
- antag_candidates.Remove(devil)
-
- if(devils.len < required_enemies)
- setup_error = "Not enough devil candidates"
- return 0
- return 1
-
-
-/datum/game_mode/devil/post_setup()
- for(var/datum/mind/devil in devils)
- post_setup_finalize(devil)
- ..()
- return 1
-
-/datum/game_mode/devil/generate_report()
- return "Infernal creatures have been seen nearby offering great boons in exchange for souls. This is considered theft against Nanotrasen, as all employment contracts contain a lien on the \
- employee's soul. If anyone sells their soul in error, contact an attorney to overrule the sale. Be warned that if the devil purchases enough souls, a gateway to hell may open."
-
-/datum/game_mode/devil/proc/post_setup_finalize(datum/mind/devil)
- add_devil(devil.current, ascendable = TRUE) //Devil gamemode devils are ascendable.
- add_devil_objectives(devil,2)
-
-/proc/is_devil(mob/living/M)
- return M?.mind?.has_antag_datum(/datum/antagonist/devil)
-
-/proc/add_devil(mob/living/L, ascendable = FALSE)
- if(!L || !L.mind)
- return FALSE
- var/datum/antagonist/devil/devil_datum = L.mind.add_antag_datum(/datum/antagonist/devil)
- devil_datum.ascendable = ascendable
- return devil_datum
-
-/proc/remove_devil(mob/living/L)
- if(!L || !L.mind)
- return FALSE
- var/datum/antagonist/devil_datum = L.mind.has_antag_datum(/datum/antagonist/devil)
- devil_datum.on_removal()
- return TRUE
-
-/datum/game_mode/devil/generate_credit_text()
- var/list/round_credits = list()
- var/len_before_addition
-
- round_credits += "
The Tempting Devils:
"
- len_before_addition = round_credits.len
- var/datum/antagonist/devil/devil_info
- for(var/datum/mind/devil in devils)
- devil_info = devil.has_antag_datum(/datum/antagonist/devil)
- if(devil_info) // This should never fail, but better to be sure
- round_credits += "
The Internal Affairs Agents have concealed their actions!
", "
We couldn't locate them!
")
- round_credits += " "
-
- round_credits += ..()
- return round_credits
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index f009648e90b6..5b26b3fe97f3 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -329,7 +329,7 @@ GLOBAL_VAR_INIT(clones, 0)
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(fair_market_price)
- if(mob_occupant && (mob_occupant.stat == DEAD) || (mob_occupant.suiciding) || mob_occupant.hellbound) //Autoeject corpses and suiciding dudes.
+ if(mob_occupant && (mob_occupant.stat == DEAD) || (mob_occupant.suiciding) || (mob_occupant.mind && HAS_TRAIT(mob_occupant.mind, TRAIT_HELLBOUND))) //Autoeject corpses and suiciding dudes.
connected_message("Clone Rejected: Deceased.")
if(internal_radio)
SPEAK("The cloning of [mob_occupant.real_name] has been \
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index a124b493fa5d..7d6f78eb664b 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -1,7 +1,7 @@
GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/storage/box/snappops = 2,
/obj/item/toy/talking/AI = 2,
- /obj/item/toy/talking/codex_gigas = 2,
+ /obj/item/toy/codex_gigas = 2,
/obj/item/clothing/under/syndicate/tacticool = 2,
/obj/item/toy/sword = 2,
/obj/item/melee/vxtvulhammer/toy = 2,
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index f5a38a5c3ae7..740981ce6060 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -627,7 +627,7 @@
if(!istype(dna))
scantemp = "Unable to locate valid genetic data."
return FALSE
- if(!body_only && (mob_occupant.suiciding || mob_occupant.hellbound))
+ if(!body_only && (mob_occupant.suiciding || (mob_occupant.mind && HAS_TRAIT(mob_occupant.mind, TRAIT_HELLBOUND))))
scantemp = "Subject's brain is not responding to scanning stimuli."
return FALSE
if(HAS_TRAIT(mob_occupant, TRAIT_BADDNA))
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 12efb829cb06..aad02ce93a5a 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -294,7 +294,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
return INITIALIZE_HINT_QDEL
//objects with the stationloving component (nuke disk) respawn here.
-//also blobs that have their spawn forcemoved (running out of time when picking their spawn spot), santa and respawning devils
+//also blobs that have their spawn forcemoved (running out of time when picking their spawn spot) and santa
/obj/effect/landmark/blobstart
name = "blobstart"
icon_state = "blob_start"
diff --git a/code/game/objects/items/AI_modules.dm b/code/game/objects/items/AI_modules.dm
index b667eae35a93..5d54b8d36f3f 100644
--- a/code/game/objects/items/AI_modules.dm
+++ b/code/game/objects/items/AI_modules.dm
@@ -49,7 +49,7 @@ AI MODULES
//Handle the lawcap
if(law_datum)
var/tot_laws = 0
- for(var/lawlist in list(law_datum.devillaws, law_datum.inherent, law_datum.supplied, law_datum.ion, law_datum.hacked, laws))
+ for(var/lawlist in list(law_datum.inherent, law_datum.supplied, law_datum.ion, law_datum.hacked, laws))
for(var/mylaw in lawlist)
if(mylaw != "")
tot_laws++
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 589103941ff1..1b94a13a3753 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -570,7 +570,7 @@
if (H.suiciding)
failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Recovery of patient impossible. Further attempts futile.")
- else if (H.hellbound)
+ else if (H.mind && HAS_TRAIT(H.mind, TRAIT_HELLBOUND))
failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's soul unrecoverable. Further attempts futile.")
else if(total_burn >= MAX_REVIVE_FIRE_DAMAGE || total_brute >= MAX_REVIVE_BRUTE_DAMAGE)
failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Surgical repair may allow for successful resuscitation.")
diff --git a/code/game/objects/items/implants/implantuplink.dm b/code/game/objects/items/implants/implantuplink.dm
index 113a1eb191a7..f0b6a2a69ce4 100644
--- a/code/game/objects/items/implants/implantuplink.dm
+++ b/code/game/objects/items/implants/implantuplink.dm
@@ -7,11 +7,16 @@
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/starting_tc = 0
-/obj/item/implant/uplink/Initialize(mapload, _owner)
+/obj/item/implant/uplink/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, starting_tc)
RegisterSignal(src, COMSIG_COMPONENT_REMOVING, PROC_REF(_component_removal))
+/obj/item/implant/uplink/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE)
+ . = ..()
+ if(!.)
+ return FALSE
+ return AddComponent(/datum/component/uplink, target, TRUE, FALSE, null, starting_tc)
+
/**
* Proc called when component is removed; ie. uplink component
*
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 3a3da7c3be55..182cded58da6 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -585,6 +585,14 @@
icon_state = "reticenceprize"
quiet = 1
+/obj/item/toy/codex_gigas
+ name = "Toy Codex Gigas"
+ desc = "A tool to help you write fictional devils!"
+ icon = 'icons/obj/library.dmi'
+ icon_state = "demonomicon"
+ lefthand_file = 'icons/mob/inhands/misc/books_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/misc/books_righthand.dmi'
+ w_class = WEIGHT_CLASS_SMALL
/obj/item/toy/talking
name = "talking action figure"
@@ -646,32 +654,6 @@
/obj/item/toy/talking/AI/generate_messages()
return list(generate_ion_law())
-/obj/item/toy/talking/codex_gigas
- name = "Toy Codex Gigas"
- desc = "A tool to help you write fictional devils!"
- icon = 'icons/obj/library.dmi'
- icon_state = "demonomicon"
- lefthand_file = 'icons/mob/inhands/misc/books_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/misc/books_righthand.dmi'
- w_class = WEIGHT_CLASS_SMALL
- recharge_time = 60
-
-/obj/item/toy/talking/codex_gigas/activation_message(mob/user)
- user.visible_message(
- span_notice("[user] presses the button on \the [src]."),
- span_notice("You press the button on \the [src]."),
- span_notice("You hear a soft click."))
-
-/obj/item/toy/talking/codex_gigas/generate_messages()
- var/datum/fakeDevil/devil = new
- var/list/messages = list()
- messages += "Some fun facts about: [devil.truename]"
- messages += "[GLOB.lawlorify[LORE][devil.bane]]"
- messages += "[GLOB.lawlorify[LORE][devil.obligation]]"
- messages += "[GLOB.lawlorify[LORE][devil.ban]]"
- messages += "[GLOB.lawlorify[LORE][devil.banish]]"
- return messages
-
/obj/item/toy/talking/owl
name = "owl action figure"
desc = "An action figure modeled after 'The Owl', defender of justice."
diff --git a/code/game/objects/items/two_handed/pitchfork.dm b/code/game/objects/items/two_handed/pitchfork.dm
index dbe5f27016bf..ad14ea13837e 100644
--- a/code/game/objects/items/two_handed/pitchfork.dm
+++ b/code/game/objects/items/two_handed/pitchfork.dm
@@ -43,57 +43,26 @@
/obj/item/pitchfork/demonic
name = "demonic pitchfork"
- desc = "A red pitchfork, it looks like the work of the devil."
- force = 19
- throwforce = 24
+ desc = "A red pitchfork, it looks like the work of the devil. Sets people on fire and deflects, but isn't that great otherwise."
light_system = MOVABLE_LIGHT
light_range = 3
light_power = 6
light_color = LIGHT_COLOR_RED
+ block_chance = 50
+ force = 2
force_wielded = 6
-/obj/item/pitchfork/demonic/greater
- force = 24
- throwforce = 50
+/obj/item/pitchfork/IsReflect(def_zone)
+ return HAS_TRAIT(src, TRAIT_WIELDED)
- force_wielded = 10
-
-/obj/item/pitchfork/demonic/ascended
- force = 100
- throwforce = 100
-
- force_wielded = 500000 // Kills you DEAD.
+/obj/item/pitchfork/afterattack(mob/living/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!HAS_TRAIT(src, TRAIT_WIELDED) || (user == target) || !isliving(target))
+ return
+ target.fire_stacks = 4
+ target.ignite_mob()
/obj/item/pitchfork/suicide_act(mob/user)
user.visible_message(span_suicide("[user] impales [user.p_them()]self in [user.p_their()] abdomen with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
return (BRUTELOSS)
-
-/obj/item/pitchfork/demonic/pickup(mob/living/user)
- . = ..()
- if(isliving(user) && user.mind && user.owns_soul() && !is_devil(user))
- var/mob/living/U = user
- U.visible_message(span_warning("As [U] picks [src] up, [U]'s arms briefly catch fire."), \
- span_warning("\"As you pick up [src] your arms ignite, reminding you of all your past sins.\""))
- if(ishuman(U))
- var/mob/living/carbon/human/H = U
- H.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- else
- U.adjustFireLoss(rand(force/2,force))
-
-/obj/item/pitchfork/demonic/attack(mob/target, mob/living/carbon/human/user)
- if(user.mind && user.owns_soul() && !is_devil(user))
- to_chat(user, "[src] burns in your hands.")
- user.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- return ..()
-
-/obj/item/pitchfork/demonic/ascended/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity || !HAS_TRAIT(src, TRAIT_WIELDED))
- return
- if(iswallturf(target))
- var/turf/closed/wall/W = target
- user.visible_message(span_danger("[user] blasts \the [target] with \the [src]!"))
- playsound(target, 'sound/magic/disintegrate.ogg', 100, 1)
- W.break_wall()
- W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 6ef5f6841c6e..6f1e506daa88 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -430,57 +430,6 @@ GLOBAL_LIST_EMPTY(servant_golem_users)
new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src))
..()
-/obj/effect/mob_spawn/human/demonic_friend
- name = "Essence of friendship"
- desc = "Oh boy! Oh boy! A friend!"
- mob_name = "Demonic friend"
- icon = 'icons/obj/cardboard_cutout.dmi'
- icon_state = "cutout_basic"
- outfit = /datum/outfit/demonic_friend
- death = FALSE
- roundstart = FALSE
- random = TRUE
- id_job = "SuperFriend"
- id_access = "assistant"
- var/datum/action/cooldown/spell/summon_friend/spell
- var/datum/mind/owner
- assignedrole = "SuperFriend"
-
-/obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, datum/action/cooldown/spell/summon_friend/summoning_spell)
- . = ..()
- owner = owner_mind
- flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil."
- important_info = "Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
- var/area/A = get_area(src)
- if(!mapload && A)
- notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACKORBIT, flashwindow = FALSE)
- objectives = "Be [owner.name]'s friend, and keep [owner.name] alive, so you don't get sent back to hell."
- spell = summoning_spell
-
-/obj/effect/mob_spawn/human/demonic_friend/special(mob/living/L)
- if(!QDELETED(owner.current) && owner.current.stat != DEAD)
- L.fully_replace_character_name(null,"[owner.name]'s best friend")
- soullink(/datum/soullink/oneway, owner.current, L)
- spell.friend = L
- L.mind.hasSoul = FALSE
- var/mob/living/carbon/human/H = L
- var/obj/item/worn = H.wear_id
- var/obj/item/card/id/id = worn.GetID()
- id.registered_name = L.real_name
- id.update_label()
- else
- to_chat(L, span_userdanger("Your owner is already dead! You will soon perish."))
- addtimer(CALLBACK(L, TYPE_PROC_REF(/mob/living, dust), 15 SECONDS)) //Give em a few seconds as a mercy.
-
-/datum/outfit/demonic_friend
- name = "Demonic Friend"
- uniform = /obj/item/clothing/under/assistantformal
- shoes = /obj/item/clothing/shoes/laceup
- r_pocket = /obj/item/radio/off
- back = /obj/item/storage/backpack
- implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend.
- id = /obj/item/card/id
-
/obj/effect/mob_spawn/human/icemoon_walker
name = "disturbed grave"
desc = "A grave. It's dirt seems to be churned up, with signs of recent activity."
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index f883633a0772..93ab3011a178 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -188,7 +188,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
for(var/mob/living/M in compiled)
var/mob/living/mob_occupant = get_mob_or_brainmob(M)
- if(mob_occupant.client && !mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_BADDNA)) && !mob_occupant.hellbound)
+ if(mob_occupant.client && !mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_BADDNA)) && !(mob_occupant.mind && HAS_TRAIT(mob_occupant.mind, TRAIT_HELLBOUND)))
icon_state = "morgue4" // Cloneable
if(mob_occupant.stat == DEAD && beeper)
if(world.time > next_beep)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 94a5b4ed4d58..83f1b65b2749 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -837,22 +837,6 @@
if(!ai_number)
to_chat(usr, "No AIs located" , confidential=TRUE)
-/datum/admins/proc/output_all_devil_info()
- var/devil_number = 0
- for(var/datum/mind/D in SSticker.mode.devils)
- devil_number++
- var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil)
- to_chat(usr, "Devil #[devil_number]:
" + devil.printdevilinfo(), confidential=TRUE)
- if(!devil_number)
- to_chat(usr, "No Devils located" , confidential=TRUE)
-
-/datum/admins/proc/output_devil_info(mob/living/M)
- if(is_devil(M))
- var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil)
- to_chat(usr, devil.printdevilinfo(), confidential=TRUE)
- else
- to_chat(usr, "[M] is not a devil.", confidential=TRUE)
-
/datum/admins/proc/manage_free_slots()
if(!check_rights())
return
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index ce02517e9fad..f18e7f60ade3 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -330,28 +330,6 @@
message_admins(span_adminnotice("[key_name_admin(mob_user)] used everyone is a traitor secret. Objective is [objective]"))
log_admin("[key_name(mob_user)] used everyone is a traitor secret. Objective is [objective]")
- if("iaa_all")
- if(!check_rights_for(rights, R_FUN))
- return
- if(!SSticker.HasRoundStarted())
- tgui_alert(mob_user, "The game hasn't started yet!")
- return
- SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("IAA All"))
- for(var/mob/living/H in GLOB.player_list)
- if(!(ishuman(H)))
- continue
- if(H.stat == DEAD || !H.client || !H.mind || ispAI(H))
- continue
- if(is_special_character(H))
- continue
- var/list/badjobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg", "Captain", "Head of Personnel", "Head of Security")
- if(H.mind.assigned_role in badjobs)
- continue
- var/datum/antagonist/traitor/internal_affairs/T = new()
- H.mind.add_antag_datum(T)
- message_admins(span_adminnotice("[key_name_admin(mob_user)] used everyone is a iaa secret."))
- log_admin("[key_name(mob_user)] used everyone is a iaa secret.")
-
if("changebombcap")
if(!check_rights_for(rights, R_FUN))
return
diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm
index da6408ac1485..0d5448371ddd 100644
--- a/code/modules/admin/sql_ban_system.dm
+++ b/code/modules/admin/sql_ban_system.dm
@@ -295,7 +295,7 @@
"Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_HORROR, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE, ROLE_MOUSE, ROLE_GOLEM, ROLE_GHOSTBEACON),
"Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB,
ROLE_BLOODSUCKER, ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST,
- ROLE_DEVIL, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_INTERNAL_AFFAIRS, ROLE_MALF,
+ ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_INFERNAL_AFFAIRS, ROLE_MALF,
ROLE_MONKEY, ROLE_MONSTERHUNTER, ROLE_NINJA, ROLE_OPERATIVE,
ROLE_REV, ROLE_REVENANT, ROLE_SINFULDEMON,
ROLE_REV_HEAD, ROLE_SERVANT_OF_RATVAR, ROLE_SYNDICATE,
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index f8032d2a705a..6dee82b63ea1 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1303,12 +1303,6 @@
return
output_ai_laws()
- else if(href_list["admincheckdevilinfo"])
- if(!check_rights(R_ADMIN))
- return
- var/mob/M = locate(href_list["admincheckdevilinfo"])
- output_devil_info(M)
-
else if(href_list["adminmoreinfo"])
var/mob/M = locate(href_list["adminmoreinfo"]) in GLOB.mob_list
adminmoreinfo(M)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 001669ee5d26..7106250886ca 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -84,15 +84,6 @@ GLOBAL_LIST_EMPTY(antagonists)
if(new_body.stat != DEAD)
new_body.add_to_current_living_antags()
-//This handles the application of antag huds/special abilities
-/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
- return
-
-//This handles the removal of antag huds/special abilities
-/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
- handle_clown_mutation(mob_override || owner.current, removing = FALSE)
- return
-
/// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
if(!ishuman(mob_override) || owner.assigned_role != "Clown")
@@ -109,7 +100,19 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/create_team(datum/team/team)
return
-//Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
+///This handles the application of antag huds/special abilities
+///This gets called on antags being added to a new body, on_gain is used for one-time effects.
+/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
+ return
+
+///This handles the removal of antag huds/special abilities
+///This is called on antags being removed from a body, on_removal is used for one-time effects.
+/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
+ handle_clown_mutation(mob_override || owner.current, removing = FALSE)
+ return
+
+///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
+///This is called only once, even if your mind is moved to a new body (like brain transfer), apply_innate_effects should be used for stuff like HUDs.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
var/datum/action/antag_info/info_button
@@ -126,7 +129,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(ui_name)
to_chat(owner.current, span_boldnotice("For more info, read the panel. you can always come back to it using the button in the top left."))
info_button.Trigger()
- apply_innate_effects()
+ apply_innate_effects(owner.current)
give_antag_moodies()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
@@ -137,33 +140,18 @@ GLOBAL_LIST_EMPTY(antagonists)
SEND_SIGNAL(owner, COMSIG_ANTAGONIST_GAINED, src)
-/datum/antagonist/proc/is_banned(mob/M)
- if(!M)
- return FALSE
- . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M))
-
-/datum/antagonist/proc/replace_banned_player()
- set waitfor = FALSE
-
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
- var/mob/dead/observer/C
-
- to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
- if(LAZYLEN(candidates))
- C = pick(candidates)
-
- message_admins(" [key_name_admin(owner)] [C ? "has been replaced by [key_name_admin(C)]" : "is banned from [job_rank] and was unable to be replaced!"]")
- owner.current.ghostize(0)
- owner.current.key = C ? C.key : null
-
+ log_game("[owner.key] has been selected as a [name] with objectives: ")
+ for(var/datum/objective/O in objectives)
+ log_game("[O.explanation_text]")
-//Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
+///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
+///This is called only once, even if your mind is moved to a new body (like brain transfer), remove_innate_effects should be used for stuff like HUDs.
/datum/antagonist/proc/on_removal()
SHOULD_CALL_PARENT(TRUE)
if(!owner)
CRASH("Antag datum with no owner.")
- remove_innate_effects()
+ remove_innate_effects(owner.current)
clear_antag_moodies()
LAZYREMOVE(owner.antag_datums, src)
if(!LAZYLEN(owner.antag_datums))
@@ -185,6 +173,25 @@ GLOBAL_LIST_EMPTY(antagonists)
qdel(src)
+/datum/antagonist/proc/is_banned(mob/M)
+ if(!M)
+ return FALSE
+ . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M))
+
+/datum/antagonist/proc/replace_banned_player()
+ set waitfor = FALSE
+
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
+ var/mob/dead/observer/C
+
+ to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
+ if(LAZYLEN(candidates))
+ C = pick(candidates)
+
+ message_admins(" [key_name_admin(owner)] [C ? "has been replaced by [key_name_admin(C)]" : "is banned from [job_rank] and was unable to be replaced!"]")
+ owner.current.ghostize(0)
+ owner.current.key = C ? C.key : null
+
/datum/antagonist/proc/greet()
return
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
deleted file mode 100644
index bc131a39933c..000000000000
--- a/code/modules/antagonists/devil/devil.dm
+++ /dev/null
@@ -1,601 +0,0 @@
-#define BLOOD_THRESHOLD 3 //How many souls are needed per stage.
-#define TRUE_THRESHOLD 7
-#define ARCH_THRESHOLD 12
-
-#define BASIC_DEVIL 0
-#define BLOOD_LIZARD 1
-#define TRUE_DEVIL 2
-#define ARCH_DEVIL 3
-
-#define LOSS_PER_DEATH 2
-
-#define SOULVALUE soulsOwned.len-reviveNumber
-
-#define DEVILRESURRECTTIME 60 SECONDS
-
-GLOBAL_LIST_EMPTY(allDevils)
-GLOBAL_LIST_INIT(lawlorify, list (
- LORE = list(
- OBLIGATION_FOOD = "This devil seems to always offer its victims food before slaughtering them.",
- OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.",
- OBLIGATION_DANCEOFF = "This devil will never turn down a dance off.",
- OBLIGATION_GREET = "This devil seems to only be able to converse with people it knows the name of.",
- OBLIGATION_PRESENCEKNOWN = "This devil seems to be unable to attack from stealth.",
- OBLIGATION_SAYNAME = "He will always chant his name upon killing someone.",
- OBLIGATION_ANNOUNCEKILL = "This devil always loudly announces his kills for the world to hear.",
- OBLIGATION_ANSWERTONAME = "This devil always responds to his truename.",
- BANE_SILVER = "Silver seems to gravely injure this devil.",
- BANE_SALT = "Throwing salt at this devil will hinder his ability to use infernal powers temporarily.",
- BANE_LIGHT = "Bright flashes will disorient the devil, likely causing him to flee.",
- BANE_IRON = "Cold iron will slowly injure him, until he can purge it from his system.",
- BANE_WHITECLOTHES = "Wearing clean white clothing will help ward off this devil.",
- BANE_HARVEST = "Presenting the labors of a harvest will disrupt the devil.",
- BANE_TOOLBOX = "That which holds the means of creation also holds the means of the devil's undoing.",
- BAN_HURTWOMAN = "This devil seems to prefer hunting men.",
- BAN_CHAPEL = "This devil avoids holy ground.",
- BAN_HURTPRIEST = "The annointed clergy appear to be immune to his powers.",
- BAN_AVOIDWATER = "The devil seems to have some sort of aversion to water, though it does not appear to harm him.",
- BAN_STRIKEUNCONSCIOUS = "This devil only shows interest in those who are awake.",
- BAN_HURTLIZARD = "This devil will not strike a lizardman first.",
- BAN_HURTANIMAL = "This devil avoids hurting animals.",
- BANISH_WATER = "To banish the devil, you must infuse its body with holy water.",
- BANISH_COFFIN = "This devil will return to life if its remains are not placed within a coffin.",
- BANISH_FORMALDYHIDE = "To banish the devil, you must inject its lifeless body with embalming fluid.",
- BANISH_RUNES = "This devil will resurrect after death, unless its remains are within a rune.",
- BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
- BANISH_DESTRUCTION = "Its corpse must be utterly destroyed to prevent resurrection.",
- BANISH_FUNERAL_GARB = "If clad in funeral garments, this devil will be unable to resurrect. Should the clothes not fit, lay them gently on top of the devil's corpse."
- ),
- LAW = list(
- OBLIGATION_FOOD = "When not acting in self defense, you must always offer your victim food before harming them.",
- OBLIGATION_FIDDLE = "When not in immediate danger, if you are challenged to a musical duel, you must accept it. You are not obligated to duel the same person twice.",
- OBLIGATION_DANCEOFF = "When not in immediate danger, if you are challenged to a dance off, you must accept it. You are not obligated to face off with the same person twice.",
- OBLIGATION_GREET = "You must always greet other people by their last name before talking with them.",
- OBLIGATION_PRESENCEKNOWN = "You must always make your presence known before attacking.",
- OBLIGATION_SAYNAME = "You must always say your true name after you kill someone.",
- OBLIGATION_ANNOUNCEKILL = "Upon killing someone, you must make your deed known to all within earshot, over comms if reasonably possible.",
- OBLIGATION_ANSWERTONAME = "If you are not under attack, you must always respond to your true name.",
- BAN_HURTWOMAN = "You must never harm a female outside of self defense.",
- BAN_CHAPEL = "You must never attempt to enter the chapel.",
- BAN_HURTPRIEST = "You must never attack a priest.",
- BAN_AVOIDWATER = "You must never willingly touch a wet surface.",
- BAN_STRIKEUNCONSCIOUS = "You must never strike an unconscious person.",
- BAN_HURTLIZARD = "You must never harm a lizardman outside of self defense.",
- BAN_HURTANIMAL = "You must never harm a non-sentient creature or robot outside of self defense.",
- BANE_SILVER = "Silver, in all of its forms shall be your downfall.",
- BANE_SALT = "Salt will disrupt your magical abilities.",
- BANE_LIGHT = "Blinding lights will prevent you from using offensive powers for a time.",
- BANE_IRON = "Cold wrought iron shall act as poison to you.",
- BANE_WHITECLOTHES = "Those clad in pristine white garments will strike you true.",
- BANE_HARVEST = "The fruits of the harvest shall be your downfall.",
- BANE_TOOLBOX = "Toolboxes are bad news for you, for some reason.",
- BANISH_WATER = "If your corpse is filled with holy water, you will be unable to resurrect.",
- BANISH_COFFIN = "If your corpse is in a coffin, you will be unable to resurrect.",
- BANISH_FORMALDYHIDE = "If your corpse is embalmed, you will be unable to resurrect.",
- BANISH_RUNES = "If your corpse is placed within a rune, you will be unable to resurrect.",
- BANISH_CANDLES = "If your corpse is near lit candles, you will be unable to resurrect.",
- BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.",
- BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect."
- )
- ))
-
-//These are also used in the codex gigas, so let's declare them globally.
-GLOBAL_LIST_INIT(devil_pre_title, list("Dark ", "Hellish ", "Fallen ", "Fiery ", "Sinful ", "Blood ", "Fluffy "))
-GLOBAL_LIST_INIT(devil_title, list("Lord ", "Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept "))
-GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon", "mya", "folth", "wren", "geyr", "hil", "niet", "twou", "phi", "coa"))
-GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr."))
-
-/*
- * Datum
- */
-/datum/antagonist/devil
- name = "Devil"
- roundend_category = "devils"
- antagpanel_category = "Devil"
- job_rank = ROLE_DEVIL
- antag_hud_name = "devil"
- show_to_ghosts = TRUE
- var/obligation
- var/ban
- var/bane
- var/banish
- var/truename
- var/list/datum/mind/soulsOwned = new
- var/reviveNumber = 0
- var/form = BASIC_DEVIL
- var/static/list/devil_spells = typecacheof(list(
- /datum/action/cooldown/spell/pointed/projectile/fireball/hellish,
- /datum/action/cooldown/spell/conjure_item/summon_pitchfork,
- /datum/action/cooldown/spell/conjure_item/summon_pitchfork/greater,
- /datum/action/cooldown/spell/conjure_item/summon_pitchfork/ascended,
- /datum/action/cooldown/spell/jaunt/infernal_jaunt,
- /datum/action/cooldown/spell/aoe/sintouch,
- /datum/action/cooldown/spell/aoe/sintouch/ascended,
- /datum/action/cooldown/spell/pointed/summon_contract,
- /datum/action/cooldown/spell/conjure_item/violin,
- /datum/action/cooldown/spell/summon_dancefloor))
- var/ascendable = FALSE
-
-/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
- . = ..()
- return . && (ishuman(new_owner.current) || iscyborg(new_owner.current))
-
-/datum/antagonist/devil/get_admin_commands()
- . = ..()
- .["Toggle ascendable"] = CALLBACK(src, PROC_REF(admin_toggle_ascendable))
-
-
-/datum/antagonist/devil/proc/admin_toggle_ascendable(mob/admin)
- ascendable = !ascendable
- message_admins("[key_name_admin(admin)] set [key_name_admin(owner)] devil ascendable to [ascendable]")
- log_admin("[key_name_admin(admin)] set [key_name(owner)] devil ascendable to [ascendable])")
-
-/datum/antagonist/devil/admin_add(datum/mind/new_owner,mob/admin)
- switch(alert(admin,"Should the devil be able to ascend",,"Yes","No","Cancel"))
- if("Yes")
- ascendable = TRUE
- if("No")
- ascendable = FALSE
- else
- return
- new_owner.add_antag_datum(src)
- message_admins("[key_name_admin(admin)] has devil'ed [key_name_admin(new_owner)]. [ascendable ? "(Ascendable)":""]")
- log_admin("[key_name(admin)] has devil'ed [key_name(new_owner)]. [ascendable ? "(Ascendable)":""]")
-
-/datum/antagonist/devil/antag_listing_name()
- return ..() + "([truename])"
-
-/proc/devilInfo(name)
- if(GLOB.allDevils[lowertext(name)])
- return GLOB.allDevils[lowertext(name)]
- else
- var/datum/fakeDevil/devil = new /datum/fakeDevil(name)
- GLOB.allDevils[lowertext(name)] = devil
- return devil
-
-/proc/randomDevilName()
- var/name = ""
- if(prob(65))
- if(prob(35))
- name = pick(GLOB.devil_pre_title)
- name += pick(GLOB.devil_title)
- var/probability = 100
- name += pick(GLOB.devil_syllable)
- while(prob(probability))
- name += pick(GLOB.devil_syllable)
- probability -= 20
- if(prob(40))
- name += pick(GLOB.devil_suffix)
- return name
-
-/proc/randomdevilobligation()
- return pick(OBLIGATION_FOOD, OBLIGATION_FIDDLE, OBLIGATION_DANCEOFF, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME)
-
-/proc/randomdevilban()
- return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONSCIOUS, BAN_HURTLIZARD, BAN_HURTANIMAL)
-
-/proc/randomdevilbane()
- return pick(BANE_SALT, BANE_LIGHT, BANE_IRON, BANE_WHITECLOTHES, BANE_SILVER, BANE_HARVEST, BANE_TOOLBOX)
-
-/proc/randomdevilbanish()
- return pick(BANISH_WATER, BANISH_COFFIN, BANISH_FORMALDYHIDE, BANISH_RUNES, BANISH_CANDLES, BANISH_DESTRUCTION, BANISH_FUNERAL_GARB)
-
-/datum/antagonist/devil/proc/add_soul(datum/mind/soul)
- if(soulsOwned.Find(soul))
- return
- soulsOwned += soul
- owner.current.set_nutrition(NUTRITION_LEVEL_FULL)
- to_chat(owner.current, span_warning("You feel satiated as you received a new soul."))
- update_hud()
- switch(SOULVALUE)
- if(0)
- to_chat(owner.current, span_warning("Your hellish powers have been restored."))
- give_appropriate_spells()
- if(BLOOD_THRESHOLD)
- increase_blood_lizard()
- if(TRUE_THRESHOLD)
- increase_true_devil()
- if(ARCH_THRESHOLD)
- increase_arch_devil()
-
-/datum/antagonist/devil/proc/remove_soul(datum/mind/soul)
- if(soulsOwned.Remove(soul))
- check_regression()
- to_chat(owner.current, span_warning("You feel as though a soul has slipped from your grasp."))
- update_hud()
-
-/datum/antagonist/devil/proc/check_regression()
- if(form == ARCH_DEVIL)
- return //arch devil can't regress
- //Yes, fallthrough behavior is intended, so I can't use a switch statement.
- if(form == TRUE_DEVIL && SOULVALUE < TRUE_THRESHOLD)
- regress_blood_lizard()
- if(form == BLOOD_LIZARD && SOULVALUE < BLOOD_THRESHOLD)
- regress_humanoid()
- if(SOULVALUE < 0)
- give_appropriate_spells()
- to_chat(owner.current, span_warning("As punishment for your failures, all of your powers except contract creation have been revoked."))
-
-/datum/antagonist/devil/proc/regress_humanoid()
- to_chat(owner.current, span_warning("Your powers weaken, have more contracts be signed to regain power."))
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
-
- var/species_type = owner.current.client.prefs.read_preference(/datum/preference/choiced/species)
- H.set_species(species_type, 1)
- H.regenerate_icons()
- give_appropriate_spells()
- if(istype(owner.current.loc, /obj/effect/dummy/phased_mob))
- owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted.
- form = BASIC_DEVIL
-
-/datum/antagonist/devil/proc/regress_blood_lizard()
- var/mob/living/carbon/true_devil/D = owner.current
- to_chat(D, span_warning("Your powers weaken, have more contracts be signed to regain power."))
- D.oldform.forceMove(D.drop_location())
- owner.transfer_to(D.oldform)
- give_appropriate_spells()
- qdel(D)
- form = BLOOD_LIZARD
- update_hud()
-
-
-/datum/antagonist/devil/proc/increase_blood_lizard()
- to_chat(owner.current, span_warning("You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard."))
- sleep(5 SECONDS)
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
- H.set_species(/datum/species/lizard, 1)
- H.underwear = "Nude"
- H.undershirt = "Nude"
- H.socks = "Nude"
- H.dna.features["mcolor"] = "#551111" //A deep red
- H.regenerate_icons()
- else //Did the devil get hit by a staff of transmutation?
- owner.current.color = "#501010"
- give_appropriate_spells()
- form = BLOOD_LIZARD
-
-
-
-/datum/antagonist/devil/proc/increase_true_devil()
- to_chat(owner.current, span_warning("You feel as though your current form is about to shed. You will soon turn into a true devil."))
- sleep(5 SECONDS)
- var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
- A.faction |= "hell"
- owner.current.forceMove(A)
- A.oldform = owner.current
- owner.transfer_to(A)
- A.set_name()
- give_appropriate_spells()
- form = TRUE_DEVIL
- update_hud()
-
-/datum/antagonist/devil/proc/increase_arch_devil()
- if(!ascendable)
- return
- var/mob/living/carbon/true_devil/D = owner.current
- to_chat(D, span_warning("You feel as though your form is about to ascend."))
- sleep(5 SECONDS)
- if(!D)
- return
- D.visible_message(span_warning("[D]'s skin begins to erupt with spikes."), \
- span_warning("Your flesh begins creating a shield around yourself."))
- sleep(10 SECONDS)
- if(!D)
- return
- D.visible_message(span_warning("The horns on [D]'s head slowly grow and elongate."), \
- span_warning("Your body continues to mutate. Your telepathic abilities grow."))
- sleep(9 SECONDS)
- if(!D)
- return
- D.visible_message(span_warning("[D]'s body begins to violently stretch and contort."), \
- span_warning("You begin to rend apart the final barriers to ultimate power."))
- sleep(4 SECONDS)
- if(!D)
- return
- to_chat(D, "Yes!")
- sleep(1 SECONDS)
- if(!D)
- return
- to_chat(D, "[span_big("YES!!")]")
- sleep(1 SECONDS)
- if(!D)
- return
- to_chat(D, "[span_reallybig("YE--")]")
- sleep(0.1 SECONDS)
- if(!D)
- return
- send_to_playing_players("\"SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!\"")
- sound_to_playing_players('sound/hallucinations/veryfar_noise.ogg')
- give_appropriate_spells()
- D.convert_to_archdevil()
- if(istype(D.loc, /obj/effect/dummy/phased_mob))
- D.forceMove(get_turf(D))//Fixes dying while jaunted leaving you permajaunted.
- var/area/A = get_area(owner.current)
- if(A)
- notify_ghosts("An arch devil has ascended in \the [A.name]. Reach out to the devil to be given a new shell for your soul.", source = owner.current, action=NOTIFY_ATTACK)
- sleep(5 SECONDS)
- if(!SSticker.mode.devil_ascended)
- SSshuttle.emergency.request(null, set_coefficient = 0.3)
- SSticker.mode.devil_ascended++
- form = ARCH_DEVIL
-
-/datum/antagonist/devil/proc/remove_spells()
- for(var/datum/action/cooldown/spell/spells in owner.current.actions)
- if(is_type_in_typecache(spells, devil_spells))
- spells.Remove(owner.current)
-
-/datum/antagonist/devil/proc/give_summon_contract()
- var/datum/action/cooldown/spell/pointed/summon_contract/summon_contract = new(owner.current)
- summon_contract.Grant(owner.current)
- if(obligation == OBLIGATION_FIDDLE)
- var/datum/action/cooldown/spell/conjure_item/violin/violin = new(owner.current)
- violin.Grant(owner.current)
- else if(obligation == OBLIGATION_DANCEOFF)
- var/datum/action/cooldown/spell/summon_dancefloor/dance_floor = new(owner.current)
- dance_floor.Grant(owner.current)
-
-/datum/antagonist/devil/proc/give_appropriate_spells()
- remove_spells()
- give_summon_contract()
- if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
- give_arch_spells()
- else if(SOULVALUE >= TRUE_THRESHOLD)
- give_true_spells()
- else if(SOULVALUE >= BLOOD_THRESHOLD)
- give_blood_spells()
- else if(SOULVALUE >= 0)
- give_base_spells()
-
-/datum/antagonist/devil/proc/give_base_spells()
- var/datum/action/cooldown/spell/pointed/projectile/fireball/hellish/fireball = new(owner.current)
- fireball.Grant(owner.current)
-
- var/datum/action/cooldown/spell/conjure_item/summon_pitchfork/pitchfork = new(owner.current)
- pitchfork.Grant(owner.current)
-
-/datum/antagonist/devil/proc/give_blood_spells()
- var/datum/action/cooldown/spell/conjure_item/summon_pitchfork/pitchfork = new(owner.current)
- pitchfork.Grant(owner.current)
-
- var/datum/action/cooldown/spell/pointed/projectile/fireball/hellish/fireball = new(owner.current)
- fireball.Grant(owner.current)
-
- var/datum/action/cooldown/spell/jaunt/infernal_jaunt/jaunt = new(owner.current)
- jaunt.Grant(owner.current)
-
-/datum/antagonist/devil/proc/give_true_spells()
- var/datum/action/cooldown/spell/conjure_item/summon_pitchfork/greater/better_pitchfork = new(owner.current)
- better_pitchfork.Grant(owner.current)
-
- var/datum/action/cooldown/spell/pointed/projectile/fireball/hellish/fireball = new(owner.current)
- fireball.Grant(owner.current)
-
- var/datum/action/cooldown/spell/jaunt/infernal_jaunt/jaunt = new(owner.current)
- jaunt.Grant(owner.current)
-
- var/datum/action/cooldown/spell/aoe/sintouch/sintouch = new(owner.current)
- sintouch.Grant(owner.current)
-
-/datum/antagonist/devil/proc/give_arch_spells()
- var/datum/action/cooldown/spell/conjure_item/summon_pitchfork/ascended/betterer_pitchfork = new(owner.current)
- betterer_pitchfork.Grant(owner.current)
-
- var/datum/action/cooldown/spell/aoe/sintouch/ascended/better_sintouch = new(owner.current)
- better_sintouch.Grant(owner.current)
-
-/datum/antagonist/devil/proc/beginResurrectionCheck(mob/living/body)
- if(SOULVALUE>0)
- to_chat(owner.current, span_userdanger("Your body has been damaged to the point that you may no longer use it. At the cost of some of your power, you will return to life soon. Remain in your body."))
- sleep(DEVILRESURRECTTIME)
- if (!body || body.stat == DEAD)
- if(SOULVALUE>0)
- if(check_banishment(body))
- to_chat(owner.current, span_userdanger("Unfortunately, the mortals have finished a ritual that prevents your resurrection."))
- return -1
- else
- to_chat(owner.current, span_userdanger("WE LIVE AGAIN!"))
- return hellish_resurrection(body)
- else
- to_chat(owner.current, span_userdanger("Unfortunately, the power that stemmed from your contracts has been extinguished. You no longer have enough power to resurrect."))
- return -1
- else
- to_chat(owner.current, span_danger(" You seem to have resurrected without your hellish powers."))
- else
- to_chat(owner.current, span_userdanger("Your hellish powers are too weak to resurrect yourself."))
-
-/datum/antagonist/devil/proc/check_banishment(mob/living/body)
- switch(banish)
- if(BANISH_WATER)
- if(iscarbon(body))
- var/mob/living/carbon/H = body
- return H.reagents.has_reagent(/datum/reagent/water/holywater)
- return 0
- if(BANISH_COFFIN)
- return (body && istype(body.loc, /obj/structure/closet/crate/coffin))
- if(BANISH_FORMALDYHIDE)
- if(iscarbon(body))
- var/mob/living/carbon/H = body
- return H.reagents.has_reagent(/datum/reagent/toxin/formaldehyde)
- return 0
- if(BANISH_RUNES)
- if(body)
- for(var/obj/effect/decal/cleanable/crayon/R in range(0,body))
- if (R.name == "rune")
- return 1
- return 0
- if(BANISH_CANDLES)
- if(body)
- var/count = 0
- for(var/obj/item/candle/C in range(1,body))
- count += C.lit
- if(count>=4)
- return 1
- return 0
- if(BANISH_DESTRUCTION)
- if(body)
- return 0
- return 1
- if(BANISH_FUNERAL_GARB)
- if(ishuman(body))
- var/mob/living/carbon/human/H = body
- if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/burial))
- return 1
- return 0
- else
- for(var/obj/item/clothing/under/burial/B in range(0,body))
- if(B.loc == get_turf(B)) //Make sure it's not in someone's inventory or something.
- return 1
- return 0
-
-/datum/antagonist/devil/proc/hellish_resurrection(mob/living/body)
- message_admins("[key_name_admin(owner)] (true name is: [truename]) is resurrecting using hellish energy.")
- if(SOULVALUE < ARCH_THRESHOLD || !ascendable) // once ascended, arch devils do not go down in power by any means.
- reviveNumber += LOSS_PER_DEATH
- update_hud()
- if(body)
- body.revive(TRUE, TRUE) //Adminrevive also recovers organs, preventing someone from resurrecting without a heart.
- if(istype(body.loc, /obj/effect/dummy/phased_mob))
- body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted.
- if(istype(body, /mob/living/carbon/true_devil))
- var/mob/living/carbon/true_devil/D = body
- if(D.oldform)
- D.oldform.revive(1,0) // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body.
- if(body.stat == DEAD)
- create_new_body()
- else
- create_new_body()
- check_regression()
-
-/datum/antagonist/devil/proc/create_new_body()
- if(GLOB.blobstart.len > 0)
- var/turf/targetturf = get_turf(pick(GLOB.blobstart))
- var/mob/currentMob = owner.current
- if(!currentMob)
- currentMob = owner.get_ghost()
- if(!currentMob)
- message_admins("[key_name_admin(owner)]'s devil resurrection failed due to client logoff. Aborting.")
- return -1
- if(currentMob.mind != owner)
- message_admins("[key_name_admin(owner)]'s devil resurrection failed due to becoming a new mob. Aborting.")
- return -1
- currentMob.change_mob_type( /mob/living/carbon/human, targetturf, null, 1)
- var/mob/living/carbon/human/H = owner.current
- H.equip_to_slot_or_del(new /obj/item/clothing/under/lawyer/black(H), ITEM_SLOT_ICLOTHING)
- H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), ITEM_SLOT_FEET)
- H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), ITEM_SLOT_HANDS)
- H.equip_to_slot_or_del(new /obj/item/pen(H), ITEM_SLOT_LPOCKET)
- if(SOULVALUE >= BLOOD_THRESHOLD)
- H.set_species(/datum/species/lizard, 1)
- H.underwear = "Nude"
- H.undershirt = "Nude"
- H.socks = "Nude"
- H.dna.features["mcolor"] = "#551111"
- H.regenerate_icons()
- if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
- var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(targetturf)
- A.faction |= "hell"
- H.forceMove(A)
- A.oldform = H
- owner.transfer_to(A, TRUE)
- A.set_name()
- if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
- A.convert_to_archdevil()
- give_appropriate_spells()
- update_hud()
- else
- CRASH("Unable to find a blobstart landmark for hellish resurrection")
-
-
-/datum/antagonist/devil/proc/update_hud()
- if(iscarbon(owner.current))
- var/mob/living/C = owner.current
- if(C.hud_used && C.hud_used.devilsouldisplay)
- C.hud_used.devilsouldisplay.update_counter(SOULVALUE)
-
-/datum/antagonist/devil/greet()
- to_chat(owner.current, span_warning("You remember your link to the infernal. You are [truename], an agent of hell, a devil. And you were sent to the plane of creation for a reason. A greater purpose. Convince the crew to sin, and embroiden Hell's grasp."))
- to_chat(owner.current, span_warning("However, your infernal form is not without weaknesses."))
- to_chat(owner.current, "You may not use violence to coerce someone into selling their soul.")
- to_chat(owner.current, "You may not directly and knowingly physically harm a devil, other than yourself.")
- to_chat(owner.current, GLOB.lawlorify[LAW][bane])
- to_chat(owner.current, GLOB.lawlorify[LAW][ban])
- to_chat(owner.current, GLOB.lawlorify[LAW][obligation])
- to_chat(owner.current, GLOB.lawlorify[LAW][banish])
- to_chat(owner.current, "[span_warning("Remember, the crew can research your weaknesses if they find out your devil name.")] ")
- .=..()
-
-/datum/antagonist/devil/on_gain()
- truename = randomDevilName()
- ban = randomdevilban()
- bane = randomdevilbane()
- obligation = randomdevilobligation()
- banish = randomdevilbanish()
- GLOB.allDevils[lowertext(truename)] = src
-
- antag_memory += "Your devilic true name is [truename] [GLOB.lawlorify[LAW][ban]] You may not use violence to coerce someone into selling their soul. You may not directly and knowingly physically harm a devil, other than yourself. [GLOB.lawlorify[LAW][bane]] [GLOB.lawlorify[LAW][obligation]] [GLOB.lawlorify[LAW][banish]] "
- if(issilicon(owner.current))
- var/mob/living/silicon/robot_devil = owner.current
- var/laws = list("You may not use violence to coerce someone into selling their soul.", "You may not directly and knowingly physically harm a devil, other than yourself.", GLOB.lawlorify[LAW][ban], GLOB.lawlorify[LAW][obligation], "Accomplish your objectives at all costs.")
- robot_devil.set_law_sixsixsix(laws)
- handle_clown_mutation(owner.current, "Your infernal nature has allowed you to overcome your clownishness.")
- return ..()
-
-/datum/antagonist/devil/on_removal()
- to_chat(owner.current, span_userdanger("Your infernal link has been severed! You are no longer a devil!"))
- . = ..()
-
-/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
- give_appropriate_spells()
- owner.current.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_DEVIL)
- update_hud()
- .=..()
-
-/datum/antagonist/devil/remove_innate_effects(mob/living/mob_override)
- for(var/datum/action/cooldown/spell/spells in owner.current.actions)
- if(is_type_in_typecache(spells, devil_spells))
- spells.Remove(owner.current)
- owner.current.remove_all_languages(LANGUAGE_DEVIL)
- .=..()
-
-/datum/antagonist/devil/proc/printdevilinfo()
- var/list/parts = list()
- parts += "The devil's true name is: [truename]"
- parts += "The devil's bans were:"
- parts += "[GLOB.TAB][GLOB.lawlorify[LORE][ban]]"
- parts += "[GLOB.TAB][GLOB.lawlorify[LORE][bane]]"
- parts += "[GLOB.TAB][GLOB.lawlorify[LORE][obligation]]"
- parts += "[GLOB.TAB][GLOB.lawlorify[LORE][banish]]"
- return parts.Join(" ")
-
-/datum/antagonist/devil/roundend_report()
- var/list/parts = list()
- parts += printplayer(owner)
- parts += printdevilinfo()
- parts += printobjectives(objectives)
- return parts.Join(" ")
-
-//A simple super light weight datum for the codex gigas.
-/datum/fakeDevil
- var/truename
- var/bane
- var/obligation
- var/ban
- var/banish
- var/ascendable
-
-/datum/fakeDevil/New(name = randomDevilName())
- truename = name
- bane = randomdevilbane()
- obligation = randomdevilobligation()
- ban = randomdevilban()
- banish = randomdevilbanish()
- ascendable = prob(25)
-
-/datum/antagonist/devil/get_preview_icon()
- var/icon/devil_icon = icon('icons/effects/64x64.dmi', "devil")
-
- devil_icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
-
- return devil_icon
diff --git a/code/modules/antagonists/devil/devil_helpers.dm b/code/modules/antagonists/devil/devil_helpers.dm
deleted file mode 100644
index ddcabde7d293..000000000000
--- a/code/modules/antagonists/devil/devil_helpers.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/mob/living/proc/check_devil_bane_multiplier(obj/item/weapon, mob/living/attacker)
- var/datum/antagonist/devil/devilInfo = mind.has_antag_datum(/datum/antagonist/devil)
- switch(devilInfo.bane)
- if(BANE_WHITECLOTHES)
- if(ishuman(attacker))
- var/mob/living/carbon/human/H = attacker
- if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under))
- var/obj/item/clothing/under/U = H.w_uniform
- var/static/list/whiteness = list (
- /obj/item/clothing/under/color/white = 2,
- /obj/item/clothing/under/rank/bartender = 1,
- /obj/item/clothing/under/rank/chef = 1,
- /obj/item/clothing/under/rank/chief_engineer = 1,
- /obj/item/clothing/under/rank/scientist = 1,
- /obj/item/clothing/under/rank/chemist = 1,
- /obj/item/clothing/under/rank/chief_medical_officer = 1,
- /obj/item/clothing/under/rank/geneticist = 1,
- /obj/item/clothing/under/rank/virologist = 1,
- /obj/item/clothing/under/rank/nursesuit = 1,
- /obj/item/clothing/under/rank/medical = 1,
- /obj/item/clothing/under/rank/det = 1,
- /obj/item/clothing/under/suit_jacket/white = 0.5,
- /obj/item/clothing/under/burial = 1
- )
- if(U && whiteness[U.type])
- src.visible_message(span_warning("[src] seems to have been harmed by the purity of [attacker]'s clothes."), span_notice("Unsullied white clothing is disrupting your form."))
- return whiteness[U.type] + 1
- if(BANE_TOOLBOX)
- if(istype(weapon, /obj/item/storage/toolbox))
- src.visible_message(span_warning("The [weapon] seems unusually robust this time."), span_notice("The [weapon] is your unmaking!"))
- return 2.5 // Will take four hits with a normal toolbox to crit.
- if(BANE_HARVEST)
- if(istype(weapon, /obj/item/reagent_containers/food/snacks/grown/))
- visible_message(span_warning("The spirits of the harvest aid in the exorcism."), span_notice("The harvest spirits are harming you."))
- Paralyze(40)
- qdel(weapon)
- return 2
- return 1
\ No newline at end of file
diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
deleted file mode 100644
index 7c76013f3401..000000000000
--- a/code/modules/antagonists/devil/imp/imp.dm
+++ /dev/null
@@ -1,76 +0,0 @@
-//////////////////The Monster
-
-/mob/living/simple_animal/imp
- name = "imp"
- real_name = "imp"
- unique_name = TRUE
- desc = "A large, menacing creature covered in armored black scales."
- speak_emote = list("cackles")
- emote_hear = list("cackles","screeches")
- response_help = "thinks better of touching"
- response_disarm = "flails at"
- response_harm = "punches"
- icon = 'icons/mob/mob.dmi'
- icon_state = "imp"
- icon_living = "imp"
- mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
- speed = 1
- a_intent = INTENT_HARM
- stop_automated_movement = 1
- status_flags = CANPUSH
- attack_sound = 'sound/magic/demon_attack1.ogg'
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 250 //Weak to cold
- maxbodytemp = INFINITY
- faction = list("hell")
- attacktext = "wildly tears into"
- maxHealth = 200
- health = 200
- healable = 0
- environment_smash = ENVIRONMENT_SMASH_STRUCTURES
- obj_damage = 40
- melee_damage_lower = 10
- melee_damage_upper = 15
- see_in_dark = 8
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- var/boost = 0
- bloodcrawl = BLOODCRAWL_EAT
- var/list/consumed_mobs = list()
- var/playstyle_string = "You are an imp, a mischievous creature from hell. You are the lowest rank on the hellish totem pole \
- Though you are not obligated to help, perhaps by aiding a higher ranking devil, you might just get a promotion. However, you are incapable \
- of intentionally harming a fellow devil."
-
-/mob/living/simple_animal/imp/Initialize(mapload)
- ..()
- boost = world.time + 30
-
-/mob/living/simple_animal/imp/Life(seconds_per_tick = SSMOBS_DT, times_fired)
- ..()
- if(boostThis is [icon2html(src, user)] [src]!")
-
- //Left hand items
- for(var/obj/item/I in held_items)
- if(!(I.item_flags & ABSTRACT))
- . += "It is holding [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))]."
-
- //Braindead
- if(!client && stat != DEAD)
- . += "The devil seems to be in deep contemplation."
-
- //Damaged
- if(stat == DEAD)
- . += span_deadsay("The hellfire seems to have been extinguished, for now at least.")
- else if(health < (maxHealth/10))
- . += span_warning("You can see hellfire inside its gaping wounds.")
- else if(health < (maxHealth/2))
- . += span_warning("You can see hellfire inside its wounds.")
- . += ""
-
-/mob/living/carbon/true_devil/IsAdvancedToolUser()
- return 1
-
-/mob/living/carbon/true_devil/resist_buckle()
- if(buckled)
- buckled.user_unbuckle_mob(src,src)
- visible_message(span_warning("[src] easily breaks out of [p_their()] handcuffs!"), \
- span_notice("With just a thought your handcuffs fall off."))
-
-/mob/living/carbon/true_devil/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
- if(incapacitated())
- to_chat(src, span_warning("You can't do that right now!"))
- return FALSE
- if(be_close && !in_range(M, src))
- to_chat(src, span_warning("You are too far away!"))
- return FALSE
- return TRUE
-
-/mob/living/carbon/true_devil/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
- return 666
-
-/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
- if(mind && has_bane(BANE_LIGHT))
- return ..() //flashes don't stop devils UNLESS it's their bane.
-
-/mob/living/carbon/true_devil/soundbang_act()
- return 0
-
-/mob/living/carbon/true_devil/get_ear_protection()
- return 2
-
-
-/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone)
- var/weakness = check_weakness(I, user)
- apply_damage(I.force * weakness, I.damtype, def_zone)
- var/message_verb = ""
- if(I.attack_verb && length(I.attack_verb))
- message_verb = "[pick(I.attack_verb)]"
- else if(I.force)
- message_verb = "attacked"
-
- var/attack_message = "[src] has been [message_verb] with [I]."
- if(user)
- user.do_attack_animation(src)
- if(user in viewers(src, null))
- attack_message = "[user] has [message_verb] [src] with [I]!"
- if(message_verb)
- visible_message(span_danger("[attack_message]"),
- span_userdanger("[attack_message]"), null, COMBAT_MESSAGE_RANGE)
- return TRUE
-
-/mob/living/carbon/true_devil/singularity_act()
- if(ascended)
- return 0
- return ..()
-
-//ATTACK GHOST IGNORING PARENT RETURN VALUE
-/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob)
- if(ascended || user.mind?.soulOwner == src.mind)
- var/mob/living/simple_animal/imp/S = new(get_turf(loc))
- S.key = user.key
- var/datum/antagonist/imp/A = new()
- S.mind.add_antag_datum(A)
- to_chat(S, S.playstyle_string)
- else
- return ..()
-
-/mob/living/carbon/true_devil/can_be_revived()
- return 1
-
-/mob/living/carbon/true_devil/resist_fire()
- //They're immune to fire.
-
-/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
- . = ..()
- if(.)
- switch(M.a_intent)
- if (INTENT_HARM)
- var/damage = rand(1, 5)
- playsound(loc, "punch", 25, 1, -1)
- visible_message(span_danger("[M] has punched [src]!"), \
- span_userdanger("[M] has punched [src]!"))
- adjustBruteLoss(damage)
- log_combat(M, src, "attacked")
- updatehealth()
- if (INTENT_DISARM)
- if (!(mobility_flags & MOBILITY_STAND) && !ascended) //No stealing the arch devil's pitchfork.
- if (prob(5))
- Unconscious(40)
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- log_combat(M, src, "pushed")
- visible_message(span_danger("[M] has pushed down [src]!"), \
- span_userdanger("[M] has pushed down [src]!"))
- else
- if (prob(25))
- dropItemToGround(get_active_held_item())
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- visible_message(span_danger("[M] has disarmed [src]!"), \
- span_userdanger("[M] has disarmed [src]!"))
- else
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- visible_message(span_danger("[M] has attempted to disarm [src]!"))
-
-/mob/living/carbon/true_devil/handle_breathing()
- // devils do not need to breathe
-
-/mob/living/carbon/true_devil/is_literate()
- return TRUE
-
-/mob/living/carbon/true_devil/ex_act(severity, ex_target)
- if(!ascended)
- var/b_loss
- switch (severity)
- if (EXPLODE_DEVASTATE)
- b_loss = 500
- if (EXPLODE_HEAVY)
- b_loss = 150
- if (EXPLODE_LIGHT)
- b_loss = 30
- if(has_bane(BANE_LIGHT))
- b_loss *=2
- adjustBruteLoss(b_loss)
- return ..()
-
-
-/mob/living/carbon/true_devil/update_body() //we don't use the bodyparts layer for devils.
- return
-
-/mob/living/carbon/true_devil/update_body_parts()
- return
-
-/mob/living/carbon/true_devil/update_damage_overlays() //devils don't have damage overlays.
- return
diff --git a/code/modules/antagonists/devil/true_devil/inventory.dm b/code/modules/antagonists/devil/true_devil/inventory.dm
deleted file mode 100644
index a3d0dbdf582b..000000000000
--- a/code/modules/antagonists/devil/true_devil/inventory.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/mob/living/carbon/true_devil/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
- if(..())
- update_inv_hands()
- return 1
- return 0
-
-/mob/living/carbon/true_devil/update_inv_hands()
- //TODO LORDPIDEY: Figure out how to make the hands line up properly. the l/r_hand_overlay should use the down sprite when facing down, left, or right, and the up sprite when facing up.
- remove_overlay(DEVIL_HANDS_LAYER)
- var/list/hands_overlays = list()
- var/obj/item/l_hand = get_item_for_held_index(1) //hardcoded 2-hands only, for now.
- var/obj/item/r_hand = get_item_for_held_index(2)
-
- if(r_hand)
- var/mutable_appearance/r_hand_overlay = r_hand.build_worn_icon(default_layer = DEVIL_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
-
- hands_overlays += r_hand_overlay
-
- if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
- r_hand.layer = ABOVE_HUD_LAYER
- r_hand.plane = ABOVE_HUD_PLANE
- r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand))
- client.screen |= r_hand
-
- if(l_hand)
- var/mutable_appearance/l_hand_overlay = l_hand.build_worn_icon(default_layer = DEVIL_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
-
- hands_overlays += l_hand_overlay
-
- if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
- l_hand.layer = ABOVE_HUD_LAYER
- l_hand.plane = ABOVE_HUD_PLANE
- l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand))
- client.screen |= l_hand
- if(hands_overlays.len)
- devil_overlays[DEVIL_HANDS_LAYER] = hands_overlays
- apply_overlay(DEVIL_HANDS_LAYER)
-
-/mob/living/carbon/true_devil/remove_overlay(cache_index)
- var/I = devil_overlays[cache_index]
- if(I)
- cut_overlay(I)
- devil_overlays[cache_index] = null
-
-
-/mob/living/carbon/true_devil/apply_overlay(cache_index)
- if((. = devil_overlays[cache_index]))
- add_overlay(.)
diff --git a/code/modules/antagonists/devil_affairs/devil.dm b/code/modules/antagonists/devil_affairs/devil.dm
new file mode 100644
index 000000000000..65e563ce3ad3
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil.dm
@@ -0,0 +1,181 @@
+///How much a Devil will slowly heal themselves when they die.
+#define DEVIL_LIFE_HEALING_AMOUNT 0.5
+///How much damage+burn you need at most to be able to revive
+#define DEVIL_REVIVE_AMOUNT_REQUIRED 180
+
+/datum/antagonist/devil
+ name = "Devil"
+ roundend_category = "infernal affairs agents"
+ antagpanel_category = "Devil Affairs"
+ antag_hud_name = "devil"
+ job_rank = ROLE_INFERNAL_AFFAIRS_DEVIL
+ greentext_achieve = /datum/achievement/greentext/devil
+
+ ///The amount of souls the devil has so far.
+ var/souls = 0
+ ///List of Powers we currently have unlocked, sorted by typepath.
+ ///This is so we don't have to init new powers we won't use if we already have it.
+ var/list/datum/action/devil_powers = list()
+
+ ///Soul counter HUD given to Devils so they always know how many they have.
+ var/atom/movable/screen/devil_soul_counter/soul_counter
+
+/datum/antagonist/devil/on_gain()
+ var/datum/objective/devil_souls/devil_objective = new()
+ devil_objective.owner = owner
+ devil_objective.update_explanation_text()
+ objectives += devil_objective
+
+ . = ..()
+
+ SSinfernal_affairs.devils += src
+ obtain_power(/datum/action/cooldown/spell/conjure_item/summon_contract)
+ obtain_power(/datum/action/cooldown/spell/pointed/collect_soul)
+
+/datum/antagonist/devil/on_removal()
+ clear_power(/datum/action/cooldown/spell/conjure_item/summon_contract)
+ clear_power(/datum/action/cooldown/spell/pointed/collect_soul)
+ if(src in SSinfernal_affairs.devils)
+ SSinfernal_affairs.devils -= src
+ return ..()
+
+/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current_mob = mob_override || owner.current
+ //see IAAs since devils update themselves in `add_team_hud`
+ add_team_hud(current_mob, antag_to_check = /datum/antagonist/infernal_affairs)
+
+ RegisterSignal(mob_override, COMSIG_LIVING_DEATH, PROC_REF(on_death))
+ RegisterSignal(mob_override, COMSIG_LIVING_REVIVE, PROC_REF(on_revival))
+ RegisterSignal(mob_override, COMSIG_LIVING_GIBBED, PROC_REF(on_gibbed))
+
+ if(current_mob.hud_used)
+ on_hud_created()
+ else
+ RegisterSignal(current_mob, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
+
+/datum/antagonist/devil/remove_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current_mob = mob_override || owner.current
+ UnregisterSignal(mob_override, list(COMSIG_LIVING_DEATH, COMSIG_LIVING_REVIVE, COMSIG_LIVING_GIBBED))
+ if(current_mob.hud_used)
+ var/datum/hud/hud_used = current_mob.hud_used
+ hud_used.infodisplay -= soul_counter
+ QDEL_NULL(soul_counter)
+
+/datum/antagonist/devil/add_team_hud(mob/target, antag_to_check)
+ . = ..()
+ var/datum/atom_hud/alternate_appearance/basic/has_antagonist/hud = team_hud_ref.resolve()
+
+ var/list/mob/living/mob_list = list()
+ for(var/datum/antagonist/devil/other_devils as anything in SSinfernal_affairs.devils)
+ mob_list += other_devils.owner.current
+
+ for (var/datum/atom_hud/alternate_appearance/basic/has_antagonist/antag_hud as anything in GLOB.has_antagonist_huds)
+ if(!(antag_hud.target in mob_list))
+ continue
+ antag_hud.show_to(target)
+ hud.show_to(antag_hud.target)
+
+///Ensure their healing will follow through to their new body.
+/datum/antagonist/devil/on_body_transfer(mob/living/old_body, mob/living/new_body)
+ . = ..()
+ if(old_body.stat == DEAD)
+ UnregisterSignal(old_body, COMSIG_LIVING_LIFE)
+ if(new_body.stat == DEAD)
+ RegisterSignal(new_body, COMSIG_LIVING_LIFE)
+
+/datum/antagonist/devil/get_admin_commands()
+ . = ..()
+ .["Give Soul"] = CALLBACK(src, PROC_REF(update_souls_owned), 1)
+ if(souls)
+ .["Take Soul"] = CALLBACK(src, PROC_REF(update_souls_owned), -1)
+
+/datum/antagonist/devil/proc/on_hud_created(datum/source)
+ SIGNAL_HANDLER
+ var/datum/hud/devil_hud = owner.current.hud_used
+
+ soul_counter = new /atom/movable/screen/devil_soul_counter(devil_hud)
+ devil_hud.infodisplay += soul_counter
+ soul_counter.update_counter(souls)
+
+ INVOKE_ASYNC(devil_hud, TYPE_PROC_REF(/datum/hud/, show_hud), devil_hud.hud_version)
+
+///Begins your healing process when you die.
+/datum/antagonist/devil/proc/on_death(atom/source, gibbed)
+ SIGNAL_HANDLER
+ RegisterSignal(source, COMSIG_LIVING_LIFE, PROC_REF(on_dead_life))
+
+///Ends your healing process when you are revived
+/datum/antagonist/devil/proc/on_revival(atom/source, full_heal, admin_revive)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, COMSIG_LIVING_LIFE)
+
+///Removes you from the list of devils and makes you unrevivable if you are gibbed.
+///You are completely killed and the round will continue without you.
+/datum/antagonist/devil/proc/on_gibbed(atom/source, no_brain, no_organs, no_bodyparts)
+ SIGNAL_HANDLER
+ playsound(source, 'sound/effects/tendril_destroyed.ogg', 40, TRUE)
+ ADD_TRAIT(owner, TRAIT_HELLBOUND, DEVIL_TRAIT)
+ SSinfernal_affairs.devils -= src
+
+///Called on Life, but only while you are dead. Handles slowly healing and coming back to life when eligible.
+/datum/antagonist/devil/proc/on_dead_life(atom/source, delta_time, times_fired)
+ SIGNAL_HANDLER
+ owner.current.heal_overall_damage(DEVIL_LIFE_HEALING_AMOUNT, DEVIL_LIFE_HEALING_AMOUNT)
+ if(owner.current.blood_volume < BLOOD_VOLUME_NORMAL(owner.current))
+ owner.current.blood_volume += (DEVIL_LIFE_HEALING_AMOUNT * 2)
+
+ if((owner.current.getBruteLoss() + owner.current.getFireLoss()) > DEVIL_REVIVE_AMOUNT_REQUIRED)
+ return
+ INVOKE_ASYNC(owner.current, TYPE_PROC_REF(/mob/living, revive), FALSE, FALSE)
+ INVOKE_ASYNC(owner.current, TYPE_PROC_REF(/mob/living, grab_ghost))
+
+/**
+ * ##update_souls_owned
+ *
+ * Used to edit the amount of souls a Devil has. This can be a negative number to take away.
+ * Will give Powers when getting to the proper level, or attempts to take them away if they go under
+ * That way this works for both adding and removing.
+ */
+/datum/antagonist/devil/proc/update_souls_owned(souls_adding)
+ souls += souls_adding
+ if(soul_counter)
+ soul_counter.update_counter(souls)
+
+ switch(souls)
+ if(1)
+ clear_power(/datum/action/cooldown/spell/conjure_item/violin)
+ if(2)
+ obtain_power(/datum/action/cooldown/spell/conjure_item/violin)
+ clear_power(/datum/action/cooldown/spell/conjure_item/summon_pitchfork)
+ if(3)
+ obtain_power(/datum/action/cooldown/spell/conjure_item/summon_pitchfork)
+ clear_power(/datum/action/cooldown/spell/jaunt/ethereal_jaunt/infernal_jaunt)
+ if(4)
+ obtain_power(/datum/action/cooldown/spell/jaunt/ethereal_jaunt/infernal_jaunt)
+ if(7)
+ clear_power(/datum/action/cooldown/spell/summon_dancefloor)
+ clear_power(/datum/action/cooldown/spell/pointed/projectile/fireball/hellish)
+ clear_power(/datum/action/cooldown/spell/shapeshift/devil)
+ if(DEVIL_SOULS_TO_ASCEND)
+ obtain_power(/datum/action/cooldown/spell/summon_dancefloor)
+ obtain_power(/datum/action/cooldown/spell/pointed/projectile/fireball/hellish)
+ obtain_power(/datum/action/cooldown/spell/shapeshift/devil)
+
+///Adds an action button to the owner of the antag and stores it in `devil_powers`
+/datum/antagonist/devil/proc/obtain_power(datum/action/new_power)
+ if(new_power in devil_powers)
+ return
+ new_power = new new_power
+ devil_powers[new_power.type] = new_power
+ new_power.Grant(owner.current)
+ return TRUE
+
+///Removes a power if it's in `devil_powers`, arg is the type.
+/datum/antagonist/devil/proc/clear_power(datum/action/removed_power)
+ if(devil_powers[removed_power])
+ QDEL_NULL(devil_powers[removed_power])
+
+#undef DEVIL_LIFE_HEALING_AMOUNT
+#undef DEVIL_REVIVE_AMOUNT_REQUIRED
diff --git a/code/modules/antagonists/devil_affairs/devil_agent_objectives.dm b/code/modules/antagonists/devil_affairs/devil_agent_objectives.dm
new file mode 100644
index 000000000000..81f0a6edd9a2
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_agent_objectives.dm
@@ -0,0 +1,37 @@
+///Devils' objective to collect souls.
+/datum/objective/devil_souls
+ name = "devil souls"
+ ///How many souls are needed to complete the objective
+ var/souls_needed = DEVIL_SOULS_TO_ASCEND
+
+/datum/objective/devil_souls/admin_edit(mob/admin)
+ var/tgui_input = tgui_input_number(admin, "How many souls does this objective need to complete?", "Needed Souls", DEVIL_SOULS_TO_ASCEND, max_value = 100)
+ if(!tgui_input || !isnum(tgui_input))
+ return
+ souls_needed = tgui_input
+ update_explanation_text()
+
+/datum/objective/devil_souls/check_completion()
+ var/datum/antagonist/devil/devil_datum = IS_DEVIL(owner.current)
+ if(!devil_datum)
+ return FALSE
+ return devil_datum.souls >= souls_needed
+
+/datum/objective/devil_souls/update_explanation_text()
+ . = ..()
+ explanation_text = "Collect [souls_needed] souls to ascend into your best form."
+
+
+///The objective given to Agents to assassinate eachother.
+/datum/objective/assassinate/internal
+ name = "assassinate internal"
+
+//We do not find a target, we'll be set manually in the game.
+/datum/objective/assassinate/internal/find_target_by_role(role, role_type = FALSE, invert = FALSE)
+ return
+
+/datum/objective/assassinate/internal/update_explanation_text()
+ if(target && target.current && (target.current.stat != DEAD))
+ explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ else
+ explanation_text = "Turn in the corpse of [target.name], who has been obliterated, to the Devil using a calling card (AltClick a paper)."
diff --git a/code/modules/antagonists/devil_affairs/devil_hud.dm b/code/modules/antagonists/devil_affairs/devil_hud.dm
new file mode 100644
index 000000000000..0660a3678d8d
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_hud.dm
@@ -0,0 +1,32 @@
+#define UI_DEVIL_SOUL_DISPLAY "WEST:6,CENTER-1:15"
+
+/atom/movable/screen/devil_soul_counter
+ icon = 'icons/mob/screen_gen.dmi'
+ name = "souls owned"
+ icon_state = "devil-6"
+ screen_loc = UI_DEVIL_SOUL_DISPLAY
+
+/atom/movable/screen/devil_soul_counter/proc/update_counter(souls)
+ maptext = ANTAG_MAPTEXT(souls, COLOR_RED)
+ switch(souls)
+ if(0)
+ icon_state = "devil-1"
+ if(1, 2)
+ icon_state = "devil-2"
+ if(3 to 5)
+ icon_state = "devil-3"
+ if(6, 7)
+ icon_state = "devil-4"
+ if(8 to 10)
+ icon_state = "devil-5"
+ else
+ icon_state = "devil-6"
+
+/atom/movable/screen/devil_soul_counter/Click()
+ var/datum/antagonist/devil/devil_antag = hud.mymob.mind.has_antag_datum(/datum/antagonist/devil)
+ if(!devil_antag)
+ return
+ hud.mymob.balloon_alert(usr, "souls owned: [devil_antag.souls]")
+
+
+#undef UI_DEVIL_SOUL_DISPLAY
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/collect_soul.dm b/code/modules/antagonists/devil_affairs/devil_powers/collect_soul.dm
new file mode 100644
index 000000000000..08b7c4c47839
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/collect_soul.dm
@@ -0,0 +1,59 @@
+/datum/action/cooldown/spell/pointed/collect_soul
+ name = "Collect Soul"
+ desc = "This ranged spell allows you to take the soul out of someone indebted to you.."
+
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "soulcollect"
+
+ school = SCHOOL_TRANSMUTATION
+ invocation = "P'y y'ur de'ts"
+ invocation_type = INVOCATION_WHISPER
+
+ active_msg = span_notice("You prepare to collect a soul...")
+ cooldown_time = 1 MINUTES
+ spell_requirements = NONE
+
+/datum/action/cooldown/spell/pointed/collect_soul/is_valid_target(mob/living/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/datum/antagonist/devil/devil_datum = owner.mind.has_antag_datum(/datum/antagonist/devil)
+ if(!devil_datum)
+ return FALSE
+ if(!target || !istype(target) || !target.mind)
+ return FALSE
+ if(target.stat != DEAD)
+ target.balloon_alert(owner, "target has to be dead!")
+ return FALSE
+ var/datum/antagonist/infernal_affairs/agent_datum = target.mind.has_antag_datum(/datum/antagonist/infernal_affairs)
+ if(!agent_datum)
+ target.balloon_alert(owner, "not an agent!")
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/collect_soul/InterceptClickOn(mob/living/user, params, mob/living/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!do_after(user, 10 SECONDS, target))
+ target.balloon_alert(user, "interrupted!")
+ return FALSE
+
+ for(var/obj/item/paper/calling_card/card in target.get_all_contents())
+ var/datum/antagonist/infernal_affairs/hunter_datum = card.signed_by_ref?.resolve()
+ if(!hunter_datum)
+ continue
+ //Ensures that the card holder is actually a target.
+ var/datum/antagonist/infernal_affairs/agent_datum = target.mind.has_antag_datum(/datum/antagonist/infernal_affairs)
+ if(hunter_datum.active_objective.target != agent_datum.owner)
+ continue
+ target.balloon_alert(user, "soul ripped!")
+ agent_datum.soul_harvested(hunter_datum)
+ return TRUE
+
+ target.balloon_alert(owner, "no card on target...")
+ return FALSE
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/conjured_items.dm b/code/modules/antagonists/devil_affairs/devil_powers/conjured_items.dm
new file mode 100644
index 000000000000..751070957d06
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/conjured_items.dm
@@ -0,0 +1,84 @@
+/datum/action/cooldown/spell/conjure_item/summon_pitchfork
+ name = "Summon Pitchfork"
+ desc = "A devil's weapon of choice. Not actually great at damage, but sets people ablaze."
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+ spell_requirements = NONE
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "pitchfork"
+
+ school = SCHOOL_CONJURATION
+ invocation_type = INVOCATION_NONE
+
+ item_type = /obj/item/pitchfork/demonic
+
+/datum/action/cooldown/spell/conjure_item/violin
+ name = "Summon Golden Violin"
+ desc = "Play some tunes."
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+ spell_requirements = NONE
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "golden_violin"
+
+ invocation = "I aint have this much fun since Georgia."
+ invocation_type = INVOCATION_WHISPER
+
+ item_type = /obj/item/instrument/violin/golden
+
+/datum/action/cooldown/spell/conjure_item/summon_contract
+ name = "Summon Devil Contract"
+ desc = "Summon a contract that, when signed, will \
+ add someone to the Agent's death loop."
+
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+ spell_requirements = NONE
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "spell_default"
+
+ school = SCHOOL_CONJURATION
+ cooldown_time = 1 MINUTES
+
+ invocation = "Just sign on the dotted line."
+ invocation_type = INVOCATION_WHISPER
+
+ item_type = /obj/item/paper/devil_contract
+
+/obj/item/paper/devil_contract
+ name = "devil contract"
+ desc = "Signing this would be the greatest mistake of your life... or the best."
+ color = "#db7c00"
+ item_flags = NOBLUDGEON
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ info = "
Contract for infernal power
I, \[\"____________\"\] of sound mind, \
+ do hereby willingly offer my soul to the infernal hells by way of the infernal agent, \
+ in exchange for power and physical strength. I understand that upon my demise, my soul shall fall into the infernal hells, \
+ and my body may not be resurrected, cloned, or otherwise brought back to life. \
+ I also understand that this will prevent my brain from being used in an MMI.\
+
Signed, \n \[\"____________\"\]"
+
+/obj/item/paper/devil_contract/attackby(obj/item/possible_pen, mob/user, params)
+ if(IS_DEVIL(user))
+ to_chat(user, span_notice("There's no point in messing with this, it's your own work."))
+ return
+ if(IS_INFERNAL_AGENT(user))
+ to_chat(user, span_notice("I know this paper, it serves no use for me anymore."))
+ return
+ if(HAS_TRAIT(user, TRAIT_DEVIL_CONTRACT_IMMUNE))
+ to_chat(user, span_warning("I've read this before... I'm not signing this shit!"))
+ return
+ if(possible_pen.sharpness == SHARP_POINTY)
+ var/tgui_response = tgui_alert(user, "You are about to give up your soul to the Infernal depths. Do you agree?", "Devil Agent", list("Yes", "NO!!"), timeout = 5 SECONDS)
+ if(tgui_response != "Yes")
+ user.visible_message(span_notice("[user] puts down \the [src]."))
+ return
+ user.mind.add_antag_datum(/datum/antagonist/infernal_affairs)
+ to_chat(user, span_alert("You sign your life away. Now only He knows where you're going."))
+ SSinfernal_affairs.update_objective_datums()
+ qdel(src)
+ return
+ return ..()
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/dancefloor.dm b/code/modules/antagonists/devil_affairs/devil_powers/dancefloor.dm
new file mode 100644
index 000000000000..1744c6a9a712
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/dancefloor.dm
@@ -0,0 +1,53 @@
+/datum/action/cooldown/spell/summon_dancefloor
+ name = "Summon Dancefloor"
+ desc = "When what a Devil really needs is funk."
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+
+ spell_requirements = NONE
+ school = SCHOOL_EVOCATION
+ cooldown_time = 5 SECONDS //5 seconds, so the smoke can't be spammed
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "disco"
+
+ var/dancefloor_exists = FALSE
+ var/list/dancefloor_turfs
+ var/list/dancefloor_turfs_types
+ var/datum/effect_system/fluid_spread/smoke/transparent/dancefloor_devil/smoke
+
+/datum/action/cooldown/spell/summon_dancefloor/cast(atom/target)
+ . = ..()
+ LAZYINITLIST(dancefloor_turfs)
+ LAZYINITLIST(dancefloor_turfs_types)
+
+ if(!smoke)
+ smoke = new()
+ smoke.set_up(0, get_turf(owner))
+ smoke.start()
+
+ if(dancefloor_exists)
+ dancefloor_exists = FALSE
+ for(var/i in 1 to dancefloor_turfs.len)
+ var/turf/T = dancefloor_turfs[i]
+ T.ChangeTurf(dancefloor_turfs_types[i], flags = CHANGETURF_INHERIT_AIR)
+ else
+ var/list/funky_turfs = RANGE_TURFS(1, owner)
+ for(var/turf/closed/solid in funky_turfs)
+ to_chat(owner, "You're too close to a wall.")
+ return
+ dancefloor_exists = TRUE
+ var/i = 1
+ dancefloor_turfs.len = funky_turfs.len
+ dancefloor_turfs_types.len = funky_turfs.len
+ for(var/turf/T as anything in funky_turfs)
+ dancefloor_turfs[i] = T
+ dancefloor_turfs_types[i] = T.type
+ T.ChangeTurf((i % 2 == 0) ? /turf/open/floor/light/colour_cycle/dancefloor_a : /turf/open/floor/light/colour_cycle/dancefloor_b, flags = CHANGETURF_INHERIT_AIR)
+ i++
+
+/datum/effect_system/fluid_spread/smoke/transparent/dancefloor_devil
+ effect_type = /obj/effect/particle_effect/fluid/smoke/transparent/dancefloor_devil
+
+/obj/effect/particle_effect/fluid/smoke/transparent/dancefloor_devil
+ lifetime = 2
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/devil_form.dm b/code/modules/antagonists/devil_affairs/devil_powers/devil_form.dm
new file mode 100644
index 000000000000..d63ec5645003
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/devil_form.dm
@@ -0,0 +1,13 @@
+/datum/action/cooldown/spell/shapeshift/devil
+ name = "Devil Form"
+ desc = "Take on the true shape of a devil."
+ invocation = "P'ease't d'y 'o' a w'lk!"
+ invocation_type = INVOCATION_WHISPER
+ spell_requirements = NONE
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+
+ button_icon = 'icons/mob/actions/actions_devil.dmi'
+ button_icon_state = "devil_form"
+
+ possible_shapes = list(/mob/living/simple_animal/hostile/devil)
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/fireball.dm b/code/modules/antagonists/devil_affairs/devil_powers/fireball.dm
new file mode 100644
index 000000000000..ae227bf46b10
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/fireball.dm
@@ -0,0 +1,13 @@
+/datum/action/cooldown/spell/pointed/projectile/fireball/hellish
+ name = "Hellfire"
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+
+ school = SCHOOL_EVOCATION
+ invocation = "W'lc'me 'o th' f're' pi's!"
+ invocation_type = INVOCATION_SHOUT
+
+ cooldown_time = 8 SECONDS
+ spell_requirements = NONE
+
+ projectile_type = /obj/projectile/magic/fireball/infernal
diff --git a/code/modules/antagonists/devil_affairs/devil_powers/jaunt.dm b/code/modules/antagonists/devil_affairs/devil_powers/jaunt.dm
new file mode 100644
index 000000000000..7bf16d70b0b0
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/devil_powers/jaunt.dm
@@ -0,0 +1,20 @@
+/datum/action/cooldown/spell/jaunt/ethereal_jaunt/infernal_jaunt
+ name = "Infernal Jaunt"
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "ab_goldborder"
+ school = SCHOOL_FORBIDDEN
+ cooldown_time = 30 SECONDS
+
+ spell_requirements = NONE
+
+ jaunt_duration = 5 SECONDS
+ jaunt_in_time = 0.8 SECONDS
+ jaunt_out_time = 0.8 SECONDS
+ jaunt_in_type = /obj/effect/temp_visual/dir_setting/devil_jaunt
+ jaunt_out_type = /obj/effect/temp_visual/dir_setting/devil_jaunt
+
+/obj/effect/temp_visual/dir_setting/devil_jaunt
+ name = "ash_shift"
+ icon = 'icons/mob/mob.dmi'
+ icon_state = "jaunt"
+ duration = 0.8 SECONDS
diff --git a/code/modules/antagonists/devil_affairs/infernal_agent.dm b/code/modules/antagonists/devil_affairs/infernal_agent.dm
new file mode 100644
index 000000000000..bb4ded90f3de
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/infernal_agent.dm
@@ -0,0 +1,143 @@
+/datum/antagonist/infernal_affairs
+ name = "Infernal Affairs Agent"
+ roundend_category = "infernal affairs agents"
+ antagpanel_category = "Devil Affairs"
+ antag_hud_name = "sintouched"
+ job_rank = ROLE_INFERNAL_AFFAIRS
+ preview_outfit = /datum/outfit/devil_affair_agent
+
+ ///Reference to the Uplink, so we can mess with it when required.
+ var/datum/component/uplink/uplink_holder
+ ///The active objective this agent has to currently complete.
+ var/datum/objective/assassinate/internal/active_objective
+
+ var/list/purchased_uplink_items = list()
+
+/datum/antagonist/infernal_affairs/on_gain(mob/living/mob_override)
+ . = ..()
+ SSinfernal_affairs.agent_datums += src
+ uplink_holder = owner.equip_traitor(employer = "The Devil", uplink_owner = src)
+ if(owner.assigned_role != "Lawyer")
+ uplink_holder.telecrystals = 10
+ RegisterSignal(uplink_holder, COMSIG_ON_UPLINK_PURCHASE, PROC_REF(on_uplink_purchase))
+
+/datum/antagonist/infernal_affairs/on_removal()
+ . = ..()
+ SSinfernal_affairs.agent_datums -= src
+ remove_uplink()
+
+/datum/antagonist/infernal_affairs/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/current_mob = mob_override || owner.current
+ //IAAs can only see devils (handled by devils), not eachother, but devils can see them.
+ add_team_hud(current_mob, antag_to_check = /datum/antagonist/devil)
+ RegisterSignal(mob_override, COMSIG_LIVING_DEATH, PROC_REF(on_death))
+
+/datum/antagonist/infernal_affairs/remove_innate_effects(mob/living/mob_override)
+ UnregisterSignal(mob_override, COMSIG_LIVING_DEATH)
+ return ..()
+
+///Handles affair agents when they die.
+///If there are devils, update objectives to let them know they should hand the body in.
+///If there aren't, go straight to harvesting (without the rewards).
+/datum/antagonist/infernal_affairs/proc/on_death(atom/source, gibbed)
+ SIGNAL_HANDLER
+ if(length(SSinfernal_affairs.devils))
+ SSinfernal_affairs.update_objective_datums()
+ return
+ INVOKE_ASYNC(src, PROC_REF(soul_harvested))
+
+/**
+ * ## on_uplink_purchase
+ *
+ * Called when an uplink item is purchased.
+ * We will keep track of their items to destroy them when the Agent dies.
+ */
+/datum/antagonist/infernal_affairs/proc/on_uplink_purchase(datum/component/uplink/source, atom/purchased_item, mob/living/purchaser)
+ SIGNAL_HANDLER
+ if(!isitem(purchased_item))
+ return
+ purchased_uplink_items += purchased_item.get_all_contents()
+
+/**
+ * ##soul_harvested
+ *
+ * Handles making their mind unrevivable and the deletion of all their items,
+ * on top of all misc effects like updating objectives and giving rewards.
+ */
+/datum/antagonist/infernal_affairs/proc/soul_harvested(datum/antagonist/infernal_affairs/killer)
+ SSinfernal_affairs.agent_datums -= src
+ ADD_TRAIT(owner, TRAIT_HELLBOUND, DEVIL_TRAIT)
+ QDEL_LIST(purchased_uplink_items)
+
+ //grant the soul to ALL devils, though without admin intervention there should only be one.
+ for(var/datum/antagonist/devil/devil as anything in SSinfernal_affairs.devils)
+ if(devil.owner.current)
+ devil.update_souls_owned(1)
+
+ if(killer && killer.owner.assigned_role != "Lawyer")
+ killer.uplink_holder.telecrystals += rand(3,5)
+
+ SSinfernal_affairs.update_objective_datums()
+
+///Removes the uplink from the agent, unregistering the proper signal.
+/datum/antagonist/infernal_affairs/proc/remove_uplink()
+ if(uplink_holder)
+ UnregisterSignal(uplink_holder, COMSIG_ON_UPLINK_PURCHASE)
+ QDEL_NULL(uplink_holder)
+
+/**
+ * Devil Affairs Outfit
+ * :P
+ */
+/datum/outfit/devil_affair_agent
+ name = "Devil Affairs Agent (Preview only)"
+
+ uniform = /obj/item/clothing/under/rank/centcom_officer
+ head = /obj/item/clothing/head/devil_horns
+ glasses = /obj/item/clothing/glasses/sunglasses
+ r_hand = /obj/item/melee/transforming/energy/sword
+
+/datum/outfit/devil_affair_agent/post_equip(mob/living/carbon/human/owner, visualsOnly)
+ var/obj/item/melee/transforming/energy/sword/sword = locate() in owner.held_items
+ sword.transform_weapon(owner, TRUE)
+
+/obj/item/clothing/head/devil_horns
+ desc = "The one the only Devil."
+ icon_state = "devil_horns"
+
+/**
+ * ##calling card
+ *
+ * This item is required to be on someone to know if rewards should be given out.
+ */
+/obj/item/paper/calling_card
+ name = "calling card"
+ color = "#ff5050"
+ foldable = FALSE
+ info = {"My life's ledger is etched in crimson, a debt so profound that only the Devil's whispered promises \
+ could keep me from the abyss. With this card, I repay some of that crimson debt, and the scales of infernal justice \
+ begin to even. \
+ \
+ Know that as these words rest upon you, my fate and yours are irrevocably intertwined. \
+ The Devil's grip on my soul compels my every move, and the debt I owe shall be your reckoning. \
+ \
+ Until then, carry the weight of your choices, for I am but a vessel, and our fates are bound by the Devil's contract.\
+ \
+ \
+ Gooodnight now, and forever. \
+
"}
+
+ ///A weakref to the antag datum who signed the paper, so simply holding a paper for YOUR target doesn't make you eligible.
+ var/datum/weakref/signed_by_ref
+
+/obj/item/paper/calling_card/Initialize(mapload, datum/antagonist/infernal_affairs/agent_datum)
+ . = ..()
+ if(!agent_datum)
+ stack_trace("calling card made but does not have an agent datum. This will cause errors as it is expected!")
+ return INITIALIZE_HINT_QDEL
+ signed_by_ref = WEAKREF(agent_datum)
+
+/obj/item/paper/calling_card/Destroy()
+ signed_by_ref = null
+ return ..()
diff --git a/code/modules/antagonists/devil_affairs/true_devil.dm b/code/modules/antagonists/devil_affairs/true_devil.dm
new file mode 100644
index 000000000000..1a01369f818b
--- /dev/null
+++ b/code/modules/antagonists/devil_affairs/true_devil.dm
@@ -0,0 +1,78 @@
+/mob/living/simple_animal/hostile/devil
+ name = "True Devil"
+ desc = "A pile of infernal energy, taking a vaguely humanoid form."
+ icon = 'icons/mob/32x64.dmi'
+ icon_state = "true_devil"
+ gender = NEUTER
+ environment_smash = ENVIRONMENT_SMASH_STRUCTURES|ENVIRONMENT_SMASH_WALLS
+ friendly = "slowly drains the soul of"
+
+ del_on_death = TRUE
+ dextrous = TRUE
+ spacewalk = TRUE
+ density = TRUE
+ pass_flags = NONE
+ health = 350
+ maxHealth = 350
+ ventcrawler = VENTCRAWLER_NONE
+ sight = (SEE_TURFS | SEE_OBJS)
+ status_flags = CANPUSH
+ mob_size = MOB_SIZE_LARGE
+ held_items = list(null, null)
+
+/mob/living/simple_animal/hostile/devil/Initialize(mapload)
+ grant_all_languages(TRUE, FALSE, TRUE)
+ return ..()
+
+/mob/living/simple_animal/hostile/devil/examine(mob/user)
+ . = list("This is [icon2html(src, user)] [src]!")
+
+ //Left hand items
+ for(var/obj/item/I in held_items)
+ if(!(I.item_flags & ABSTRACT))
+ . += "It is holding [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))]."
+
+ //Braindead
+ if(!client && stat != DEAD)
+ . += "The devil seems to be in deep contemplation."
+
+ //Damaged
+ if(stat == DEAD)
+ . += span_deadsay("The hellfire seems to have been extinguished, for now at least.")
+ else if(health < (maxHealth/10))
+ . += span_warning("You can see hellfire inside its gaping wounds.")
+ else if(health < (maxHealth/2))
+ . += span_warning("You can see hellfire inside its wounds.")
+ . += ""
+
+/mob/living/simple_animal/hostile/devil/resist_buckle()
+ if(buckled)
+ buckled.user_unbuckle_mob(src,src)
+ visible_message(
+ span_warning("[src] easily breaks out of [p_their()] handcuffs!"),
+ span_notice("With just a thought your handcuffs fall off."),
+ )
+
+/mob/living/simple_animal/hostile/devil/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
+ return 666
+
+/mob/living/simple_animal/hostile/devil/get_ear_protection()
+ return 2
+
+/mob/living/simple_animal/hostile/devil/can_be_revived()
+ return 1
+
+/mob/living/simple_animal/hostile/devil/is_literate()
+ return TRUE
+
+/mob/living/simple_animal/hostile/devil/ex_act(severity, ex_target)
+ var/b_loss
+ switch(severity)
+ if (EXPLODE_DEVASTATE)
+ b_loss = 500
+ if (EXPLODE_HEAVY)
+ b_loss = 150
+ if (EXPLODE_LIGHT)
+ b_loss = 30
+ adjustBruteLoss(b_loss)
+ return ..()
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
index 5d23088f1e90..f13385338713 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm
@@ -372,7 +372,7 @@
while(penance_sources[P])
switch(P)
if(PENANCE_SOUL)
- owner.hellbound = TRUE
+ ADD_TRAIT(owner.mind, TRAIT_HELLBOUND, MAGIC_TRAIT)
to_chat(owner, span_velvet("You feel a peculiar emptiness..."))
if(PENANCE_LIMB)
var/obj/item/bodypart/BP
diff --git a/code/modules/antagonists/traitor/IAA/internal_affairs.dm b/code/modules/antagonists/traitor/IAA/internal_affairs.dm
deleted file mode 100644
index b1c1d7c31803..000000000000
--- a/code/modules/antagonists/traitor/IAA/internal_affairs.dm
+++ /dev/null
@@ -1,285 +0,0 @@
-#define PINPOINTER_MINIMUM_RANGE 15
-#define PINPOINTER_EXTRA_RANDOM_RANGE 10
-#define PINPOINTER_PING_TIME 40
-#define PROB_ACTUAL_TRAITOR 20
-#define TRAITOR_AGENT_ROLE "Gorlex Marauders Exile"
-#define TRAITOR_AGENT_SROLE "gorlex marauders exile"
-
-/datum/antagonist/traitor/internal_affairs
- name = "Syndicate Internal Affairs Agent"
- special_role = "internal affairs agent" //Doesn't have it listed but employer should still be syndicate
- antagpanel_category = "IAA"
- job_rank = ROLE_INTERNAL_AFFAIRS
- var/marauder = FALSE
- var/last_man_standing = FALSE
- var/list/datum/mind/targets_stolen
- greentext_achieve = /datum/achievement/greentext/internal
- preview_outfit = /datum/outfit/assassin
-
-/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
- if(owner && owner.current)
- owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
-
-/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
- .=..() //in case the base is used in future
- if(owner && owner.current)
- give_pinpointer(owner.current)
-
-/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
- .=..()
- if(owner && owner.current)
- owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
-
-/datum/antagonist/traitor/internal_affairs/on_gain()
- START_PROCESSING(SSprocessing, src)
-
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
- var/obj/item/implant/dusting/iaa/E = new(H)
- E.implant(H)
-
- company = pick(subtypesof(/datum/corporation/traitor))
-
- .=..()
-/datum/antagonist/traitor/internal_affairs/on_removal()
- STOP_PROCESSING(SSprocessing,src)
- for(var/obj/item/implant/dusting/iaa/iaa_implant in owner?.current?.implants)
- iaa_implant.defused = TRUE
- qdel(iaa_implant)
- .=..()
-
-/datum/antagonist/traitor/internal_affairs/process()
- iaa_process()
-
-
-/datum/status_effect/agent_pinpointer
- id = "agent_pinpointer"
- duration = -1
- tick_interval = PINPOINTER_PING_TIME
- alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer
- var/minimum_range = PINPOINTER_MINIMUM_RANGE
- var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
- var/mob/scan_target = null
- var/range_mid = 8
- var/range_far = 16
-
-/atom/movable/screen/alert/status_effect/agent_pinpointer
- name = "Internal Affairs Integrated Pinpointer"
- desc = "Even stealthier than a normal implant."
- icon = 'icons/obj/device.dmi'
- icon_state = "pinon"
-
-/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
- if(!scan_target)
- linked_alert.icon_state = "pinonnull"
- return
- var/turf/here = get_turf(owner)
- var/turf/there = get_turf(scan_target)
- if(here?.z != there?.z)
- linked_alert.icon_state = "pinonnull"
- return
- if(get_dist_euclidian(here,there)<=minimum_range + rand(0, range_fuzz_factor))
- linked_alert.icon_state = "pinondirect"
- else
- linked_alert.setDir(get_dir(here, there))
- var/dist = (get_dist(here, there))
- if(dist >= 1 && dist <= range_mid)
- linked_alert.icon_state = "pinonclose"
- else if(dist > range_mid && dist <= range_far)
- linked_alert.icon_state = "pinonmedium"
- else if(dist > range_far)
- linked_alert.icon_state = "pinonfar"
-
-/datum/status_effect/agent_pinpointer/proc/scan_for_target()
- scan_target = null
- if(owner)
- if(owner.mind)
- for(var/datum/objective/objective_ in owner.mind.get_all_objectives())
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- var/mob/current = objective.target.current
- if(current&¤t.stat!=DEAD)
- scan_target = current
- break
-
-/datum/status_effect/agent_pinpointer/tick()
- if(!owner)
- qdel(src)
- return
- scan_for_target()
- point_to_target()
-
-
-/proc/is_internal_objective(datum/objective/O)
- return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
-
-/datum/antagonist/traitor/proc/replace_escape_objective_martyr()
- if(!owner || !objectives.len)
- return
- for (var/objective_ in objectives)
- if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)||istype(objective_, /datum/objective/hijack)))
- continue
- remove_objective(objective_)
-
- var/datum/objective/martyr/martyr_objective = new
- martyr_objective.owner = owner
- add_objective(martyr_objective)
-
-/datum/antagonist/traitor/proc/replace_escape_objective_hijack() //Should work?
- if(!owner || !objectives.len)
- return
- for (var/objective_ in objectives)
- if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)||istype(objective_, /datum/objective/martyr)))
- continue
- remove_objective(objective_)
-
- var/datum/objective/hijack/hijack_objective = new
- hijack_objective.owner = owner
- add_objective(hijack_objective)
-
-/datum/antagonist/traitor/proc/reinstate_escape_objective()
- if(!owner||!objectives.len)
- return
- for (var/objective_ in objectives)
- if(!istype(objective_, /datum/objective/martyr) || !istype(objective_, /datum/objective/hijack))
- continue
- remove_objective(objective_)
-
-/datum/antagonist/traitor/internal_affairs/reinstate_escape_objective()
- ..()
- var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive
- var/datum/objective/escape_objective = new objtype
- escape_objective.owner = owner
- add_objective(escape_objective)
-
-/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
- if(!owner.current||owner.current.stat==DEAD)
- return
- to_chat(owner.current, span_userdanger("Target eliminated: [victim.name]"))
- for(var/objective_ in victim.get_all_objectives())
- if(istype(objective_, /datum/objective/assassinate/internal))
- var/datum/objective/assassinate/internal/objective = objective_
- if(objective.target==owner)
- continue
- else if(targets_stolen.Find(objective.target) == 0)
- var/datum/objective/assassinate/internal/new_objective = new
- new_objective.owner = owner
- new_objective.target = objective.target
- new_objective.update_explanation_text()
- add_objective(new_objective)
- targets_stolen += objective.target
- var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
- else if(istype(objective_, /datum/objective/destroy/internal))
- var/datum/objective/destroy/internal/objective = objective_
- var/datum/objective/destroy/internal/new_objective = new
- if(objective.target==owner)
- continue
- else if(targets_stolen.Find(objective.target) == 0)
- new_objective.owner = owner
- new_objective.target = objective.target
- new_objective.update_explanation_text()
- add_objective(new_objective)
- targets_stolen += objective.target
- var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
- last_man_standing = TRUE
- for(var/objective_ in objectives)
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- if(!objective.check_completion())
- last_man_standing = FALSE
- return
- if(last_man_standing)
- if(!marauder)
- to_chat(owner.current,span_userdanger("Every agent confirmed turncoat has been eliminated. However, given that the entire cell was compromised, your loyalty is being called into question. Die a glorious death, and prove your unending allegiance to the Syndicate."))
- replace_escape_objective_martyr(owner)
- else
- to_chat(owner.current,span_userdanger("Each of the others lies dead at your feet. Your final obstacle of this trial is to hijack the shuttle. Leave none standing and no survivors in your wake. Your brothers await you with open arms, Marauder."))
- replace_escape_objective_hijack(owner)
-
-/datum/antagonist/traitor/internal_affairs/proc/iaa_process()
- if(owner&&owner.current&&owner.current.stat!=DEAD)
- for(var/objective_ in objectives)
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- if(!objective.target)
- continue
- if(objective.check_completion())
- if(objective.stolen)
- continue
- else
- steal_targets(objective.target)
- objective.stolen = TRUE
- else
- if(objective.stolen)
- var/fail_msg = span_userdanger("Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, lives once again.")
- if(last_man_standing)
- if(!marauder)
- fail_msg += span_userdanger("You no longer have permission to die. Track the treacherous vermin down, and kill them. No loose ends are permitted.")
- else
- fail_msg += span_userdanger("They will not judge us weak. Cease your terror on Nanotrasen and eliminate the tenacious target once more. Fail to do this, and a bullet awaits you at the base.")
- reinstate_escape_objective(owner)
- last_man_standing = FALSE
- to_chat(owner.current, fail_msg)
- objective.stolen = FALSE
-
-/datum/antagonist/traitor/internal_affairs/proc/forge_iaa_objectives()
- if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
- // Assassinate
- var/datum/mind/target_mind = SSticker.mode.target_list[owner]
- if(issilicon(target_mind.current))
- var/datum/objective/destroy/internal/destroy_objective = new
- destroy_objective.owner = owner
- destroy_objective.target = target_mind
- destroy_objective.update_explanation_text()
- add_objective(destroy_objective)
- else
- var/datum/objective/assassinate/internal/kill_objective = new
- kill_objective.owner = owner
- kill_objective.target = target_mind
- kill_objective.update_explanation_text()
- add_objective(kill_objective)
-
- //Lower chance of someone needing to do an additional objective, but getting hijack instead of DaGD
- if(prob(PROB_ACTUAL_TRAITOR)) //20%
- company = /datum/corporation/gorlex //Should not double wammy the corporate introduction, I hope
- name = "Gorlex Marauders Exile"
- owner.special_role = TRAITOR_AGENT_ROLE
- special_role = TRAITOR_AGENT_SROLE
- marauder = TRUE
- forge_single_human_objective()
- greentext_achieve = /datum/achievement/greentext/external
-
-/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
- forge_iaa_objectives()
-
- var/objtype = traitor_kind == TRAITOR_HUMAN ? /datum/objective/escape : /datum/objective/survive
- var/datum/objective/escape_objective = new objtype
- escape_objective.owner = owner
- add_objective(escape_objective)
-
-/datum/antagonist/traitor/internal_affairs/proc/greet_iaa()
- to_chat(owner.current, span_userdanger("You are the [special_role]."))
- if(!marauder)
- to_chat(owner.current, span_userdanger("An intel leak suggests that operatives on this station have been turned by Nanotrasen. For now, your target is the only confirmed turncoat."))
- to_chat(owner.current, "Any apparent damage you cause may draw early suspicion to an intelligence leak. Limit collateral damage to avoid this.") //yogs - murderbone rule exists, apparently
- to_chat(owner.current, span_userdanger("You have been provided with a standard uplink to accomplish your task."))
- else
- to_chat(owner.current, span_userdanger("You have been granted a chance to rejoin the Gorlex ranks, despite your dishonorable behavior. Complete this small task and eliminate every other Syndicate agent on the station to prove yourself. Your listed target is the first of many."))
- to_chat(owner.current, "Unnecessary collateral damage will condemn yourself. Keep your destructive force focused on your targets, or you will fail this trial.")
- to_chat(owner.current, span_userdanger("We have given you a standard uplink for tools in your glorious hunt. Use them well, use them cunningly."))
-
- to_chat(owner.current, span_userdanger("Your target still likely has an uplink of their own, and other agents may be moving to eliminate you, if your identity has been compromised. Be careful, and watch your back."))
- owner.announce_objectives()
-
-/datum/antagonist/traitor/internal_affairs/greet()
- greet_iaa()
-
-#undef PROB_ACTUAL_TRAITOR
-#undef PINPOINTER_EXTRA_RANDOM_RANGE
-#undef PINPOINTER_MINIMUM_RANGE
-#undef PINPOINTER_PING_TIME
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index e4c01d56cd5f..b14998cbb6fe 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -67,9 +67,7 @@
qdel(A.malf_picker)
owner.remove_employee(company)
if(uplink_holder)
- var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
- if(uplink)//remove uplink so they can't keep using it if admin abuse happens
- uplink.RemoveComponent()
+ uplink_holder.RemoveComponent()
UnregisterSignal(owner.current, COMSIG_MOVABLE_HEAR)
SSticker.mode.traitors -= owner
if(!silent && owner.current)
@@ -302,7 +300,7 @@
/datum/antagonist/traitor/proc/equip(silent = FALSE)
if(traitor_kind == TRAITOR_HUMAN)
- uplink_holder = owner.equip_traitor(employer, silent, src) //yogs - uplink_holder =
+ uplink_holder = owner.equip_traitor(employer, silent, src) //yogs - keeps track of their uplink.
/datum/antagonist/traitor/proc/assign_exchange_role()
//set faction
diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm
index 4cd2f39cf900..2f5cdffcea38 100644
--- a/code/modules/awaymissions/mission_code/Academy.dm
+++ b/code/modules/awaymissions/mission_code/Academy.dm
@@ -237,7 +237,6 @@
if(1)
//Dust
T.visible_message(span_userdanger("[user] turns to dust!"))
- user.hellbound = TRUE
user.dust()
if(2)
//Death
diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm
deleted file mode 100644
index ae01b40b8726..000000000000
--- a/code/modules/events/devil.dm
+++ /dev/null
@@ -1,58 +0,0 @@
-/datum/round_event_control/devil
- name = "Create Devil"
- typepath = /datum/round_event/ghost_role/devil
- max_occurrences = 0
-
-/datum/round_event/ghost_role/devil
- var/success_spawn = 0
- role_name = "devil"
- fakeable = FALSE
-
-/datum/round_event/ghost_role/devil/kill()
- if(!success_spawn && control)
- control.occurrences--
- return ..()
-
-/datum/round_event/ghost_role/devil/spawn_role()
- //selecting a spawn_loc
- if(!SSjob.latejoin_trackers.len)
- return MAP_ERROR
-
- //selecting a candidate player
- var/list/candidates = get_candidates(ROLE_DEVIL, null, ROLE_DEVIL)
- if(!candidates.len)
- return NOT_ENOUGH_PLAYERS
-
- var/mob/dead/selected_candidate = pick_n_take(candidates)
- var/key = selected_candidate.key
-
- var/datum/mind/Mind = create_devil_mind(key)
- Mind.active = 1
-
- var/mob/living/carbon/human/devil = create_event_devil()
- Mind.transfer_to(devil)
- add_devil(devil, ascendable = FALSE)
-
- spawned_mobs += devil
- message_admins("[ADMIN_LOOKUPFLW(devil)] has been made into a devil by an event.")
- log_game("[key_name(devil)] was spawned as a devil by an event.")
- var/datum/job/jobdatum = SSjob.GetJob("Assistant")
- devil.job = jobdatum.title
- jobdatum.equip(devil)
- return SUCCESSFUL_SPAWN
-
-
-/proc/create_event_devil(spawn_loc)
- var/mob/living/carbon/human/new_devil = new(spawn_loc)
- if(!spawn_loc)
- SSjob.SendToLateJoin(new_devil)
- new_devil.randomize_human_appearance(~(RANDOMIZE_SPECIES))
- new_devil.dna.update_dna_identity()
- return new_devil
-
-/proc/create_devil_mind(key)
- var/datum/mind/Mind = new /datum/mind(key)
- Mind.assigned_role = ROLE_DEVIL
- Mind.special_role = ROLE_DEVIL
- SSticker.mode.devils |= Mind
- return Mind
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index 059974178c25..43b919fa6782 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -80,10 +80,7 @@
break
else
if(M.ckey == ckey && M.stat == DEAD && !M.suiciding)
- make_podman = 1
- if(isliving(M))
- var/mob/living/L = M
- make_podman = !L.hellbound
+ make_podman = !M.mind || !HAS_TRAIT(M.mind, TRAIT_HELLBOUND)
break
else //If the player has ghosted from his corpse before blood was drawn, his ckey is no longer attached to the mob, so we need to match up the cloned player through the mind key
for(var/mob/M in GLOB.player_list)
@@ -92,10 +89,7 @@
var/mob/dead/observer/O = M
if(!O.can_reenter_corpse)
break
- make_podman = 1
- if(isliving(M))
- var/mob/living/L = M
- make_podman = !L.hellbound
+ make_podman = !M.mind || !HAS_TRAIT(M.mind, TRAIT_HELLBOUND)
ckey_holder = M.ckey
break
diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm
index 0101ffbe9a02..7a51aa28f8c0 100644
--- a/code/modules/instruments/items.dm
+++ b/code/modules/instruments/items.dm
@@ -56,7 +56,6 @@
/obj/item/instrument/violin/golden
name = "golden violin"
- desc = "A golden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
icon_state = "golden_violin"
item_state = "golden_violin"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index eb7aae9266a3..71b8f8244f15 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -70,3 +70,4 @@
return
H.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_CURATOR)
+ ADD_TRAIT(H, TRAIT_DEVIL_CONTRACT_IMMUNE, JOB_TRAIT)
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 4d684d9a14ca..e54f52aa13a9 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -86,3 +86,10 @@
chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/hop)
+
+/datum/outfit/job/hop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ . = ..()
+ if(visualsOnly)
+ return
+
+ ADD_TRAIT(H, TRAIT_DEVIL_CONTRACT_IMMUNE, JOB_TRAIT)
diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm
index 2e4c1aaa7850..b9af1a652470 100644
--- a/code/modules/jobs/job_types/lawyer.dm
+++ b/code/modules/jobs/job_types/lawyer.dm
@@ -66,6 +66,12 @@
uniform = /obj/item/clothing/under/lawyer/purpsuit
suit = /obj/item/clothing/suit/toggle/lawyer/purple
+/datum/outfit/job/lawyer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ . = ..()
+ if(visualsOnly)
+ return
+ ADD_TRAIT(H, TRAIT_DEVIL_CONTRACT_IMMUNE, JOB_TRAIT)
+
/datum/outfit/job/lawyer/get_types_to_preload()
. = ..()
. += /obj/item/clothing/under/lawyer/purpsuit
diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm
index df21c35cf346..9889aba873d9 100644
--- a/code/modules/library/lib_codex_gigas.dm
+++ b/code/modules/library/lib_codex_gigas.dm
@@ -14,91 +14,98 @@
throw_range = 10
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
author = "Forces beyond your comprehension"
- unique = 1
+ unique = TRUE
title = "the Codex Gigas"
- var/inUse = 0
- var/currentName = ""
- var/currentSection = PRE_TITLE
-/obj/item/book/codex_gigas/attack_self(mob/user)
- if(!user.can_read(src))
- return FALSE
- if(inUse)
- to_chat(user, span_notice("Someone else is reading it."))
- if(ishuman(user))
- var/mob/living/carbon/human/U = user
- if(U.check_acedia())
- to_chat(user, span_notice("None of this matters, why are you reading this? You put [title] down."))
- return
- user.visible_message("[user] opens [title] and begins reading intently.")
- ask_name(user)
+ ///List of all minds that were already discovered as agents, so you can't find the same person twice.
+ var/list/datum/mind/found_minds = list()
+ ///A crewmember's record paper inserted into the document.
+ ///Necessary to discover Infernal Agents and reverse engineering who the Devil is.
+ var/obj/item/paper/record/inserted_record
-/obj/item/book/codex_gigas/proc/perform_research(mob/user, devilName)
- if(!devilName)
- user.visible_message("[user] closes [title] without looking anything up.")
- return
- inUse = TRUE
- var/speed = 300
- var/correctness = 85
- if(ishuman(user))
- var/mob/living/carbon/human/U = user
- if(U.job in list("Curator")) // the curator is both faster, and more accurate than normal crew members at research
- speed = 100
- correctness = 100
- correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) * 0.5 //Brain damage makes researching hard.
- speed += U.getOrganLoss(ORGAN_SLOT_BRAIN) * 3
- if(do_after(user, speed, user, FALSE))
- var/usedName = devilName
- if(!prob(correctness))
- usedName += "x"
- var/datum/antagonist/devil/devil = devilInfo(usedName)
- display_devil(devil, user, usedName)
- sleep(1 SECONDS)
- onclose(user, "book")
- inUse = FALSE
+ ///The mind of a person we're looking into.
+ ///Necessary to discover Infernal Agents and reverse engineer who the Devil is.
+ var/datum/mind/stored_mind
-/obj/item/book/codex_gigas/proc/display_devil(datum/antagonist/devil/devil, mob/reader, devilName)
- reader << browse("Information on [devilName]
[GLOB.lawlorify[LORE][devil.ban]] [GLOB.lawlorify[LORE][devil.bane]] [GLOB.lawlorify[LORE][devil.obligation]] [GLOB.lawlorify[LORE][devil.banish]] [devil.ascendable?"This devil may ascend given enough souls.":""]", "window=book[window_size != null ? ";size=[window_size]" : ""]")
+/obj/item/book/codex_gigas/Destroy()
+ . = ..()
+ QDEL_NULL(inserted_record)
+ found_minds = null
+ stored_mind = null
-/obj/item/book/codex_gigas/proc/ask_name(mob/reader)
- ui_interact(reader)
+/obj/item/book/codex_gigas/examine(mob/user)
+ . = ..()
+ if(HAS_TRAIT(user, TRAIT_DEVIL_CONTRACT_IMMUNE))
+ if(inserted_record)
+ . += span_notice("It has a record in it, you can Alt Click it to remove it.")
+ else
+ . += span_notice("You can insert someone's personal records to store them inside.")
+ . += span_notice("You can also use this on a person to keep track of them for comparing.")
+ . += span_notice("Once you have both, of the same person, you may compare them to see if they are an Infernal Affairs Agent.")
-/obj/item/book/codex_gigas/ui_act(action, params)
- if(..())
+/obj/item/book/codex_gigas/attackby(obj/item/tool, mob/user, params)
+ if(istype(tool, /obj/item/paper/record))
+ if(inserted_record)
+ user.balloon_alert(user, "record already inside!")
+ return
+ tool.forceMove(src)
+ inserted_record = tool
+ user.balloon_alert(user, "record inserted")
return
- if(!action)
- return FALSE
- if(action == "search")
- SStgui.close_uis(src)
- addtimer(CALLBACK(src, PROC_REF(perform_research), usr, currentName), 0)
- currentName = ""
- currentSection = PRE_TITLE
- return FALSE
- else
- currentName += action
- var/oldSection = currentSection
- if(GLOB.devil_pre_title.Find(action))
- currentSection = TITLE
- else if(GLOB.devil_title.Find(action))
- currentSection = SYLLABLE
- else if(GLOB.devil_syllable.Find(action))
- if (currentSection>=SYLLABLE)
- currentSection = MULTIPLE_SYLLABLE
- else
- currentSection = SYLLABLE
- else if(GLOB.devil_suffix.Find(action))
- currentSection = SUFFIX
- return currentSection != oldSection
+ return ..()
-/obj/item/book/codex_gigas/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "CodexGigas", name)
- ui.open()
+/obj/item/book/codex_gigas/AltClick(mob/user)
+ . = ..()
+ if(inserted_record)
+ user.balloon_alert(user, "record removed")
+ user.put_in_hands(inserted_record)
+ inserted_record = null
+
+/obj/item/book/codex_gigas/afterattack(mob/living/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!proximity_flag)
+ return
+ if(!user.can_read(src) || (target == user) || !ismob(target))
+ return
+ if(!HAS_TRAIT(user, TRAIT_DEVIL_CONTRACT_IMMUNE))
+ return
+ user.balloon_alert(user, "taking notes...")
+ if(!do_after(user, 10 SECONDS, target))
+ user.balloon_alert(user, "interrupted!")
+ return
+ stored_mind = target.mind
-/obj/item/book/codex_gigas/ui_data(mob/user)
- var/list/data = list()
- data["name"]=currentName
- data["currentSection"]=currentSection
- return data
+/obj/item/book/codex_gigas/attack_self(mob/living/user)
+ if(!HAS_TRAIT(user, TRAIT_DEVIL_CONTRACT_IMMUNE))
+ to_chat(user, span_alert("The book is an unreadable mess..."))
+ return
+ if(!inserted_record)
+ user.balloon_alert(user, "no records inserted!")
+ return
+ var/datum/mind/stored_record_mind = inserted_record.record_of_ref?.resolve()
+ if(!stored_record_mind)
+ user.balloon_alert(user, "no records inserted!")
+ return
+ if(!stored_mind)
+ user.balloon_alert(user, "no person to compare!")
+ return
+ if(stored_mind in found_minds)
+ user.balloon_alert(user, "person already scanned")
+ return
+ user.balloon_alert(user, "comparing notes...")
+ if(!do_after(user, 10 SECONDS, src))
+ user.balloon_alert(user, "unable to compare notes")
+ return
+ if(stored_record_mind != stored_mind)
+ user.balloon_alert(user, "notes differ from person!")
+ return
+ var/datum/antagonist/infernal_affairs/infernal_datum = stored_mind.has_antag_datum(/datum/antagonist/infernal_affairs)
+ if(!infernal_datum)
+ to_chat(user, span_alert("They are unrelated to your research, thus your search continues..."))
+ return
+ found_minds += stored_mind
+ to_chat(user, span_alert("Infernal Affairs agent found! [infernal_datum.owner.current] has sold their soul off to the Devil! \
+ The Devil, aware that you know this, likely has already done something about it..."))
+ infernal_datum.remove_uplink()
+ to_chat(infernal_datum.owner.current, span_alert("You've been discovered! Your uplink has been revoked for your crimes against the Devil!"))
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index a34d8bcbbf15..bad7033256ee 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -128,7 +128,7 @@
return FALSE
if(iscarbon(M))
// disallowed carbons
- if(isalien(M) || isdevil(M))
+ if(isalien(M))
return FALSE
var/mob/living/carbon/target = M
// gotta have a head to be implanted (no changelings or sentient plants)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 9ab56f4a9d53..92451457066a 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -921,7 +921,7 @@
return 0
/mob/living/carbon/proc/can_defib(careAboutGhost = TRUE) //yogs start
- if(suiciding || hellbound || HAS_TRAIT(src, TRAIT_HUSK)) //can't revive
+ if(suiciding || (mind && HAS_TRAIT(mind, TRAIT_HELLBOUND)) || HAS_TRAIT(src, TRAIT_HUSK)) //can't revive
return FALSE
if((world.time - timeofdeath) > DEFIB_TIME_LIMIT) //too late
return FALSE
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 3e46bd2d5f20..cf3206f9c95f 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -7,10 +7,9 @@
held_items = list(null, null)
/// List of /obj/item/organ in the mob.
/// They don't go in the contents for some reason I don't want to know.
- var/list/internal_organs = list()
- /// List of /obj/item/organ in the mob by slot ID for easy access.
+ var/list/internal_organs = list()
/// They don't go in the contents for some reason I don't want to know.
- var/list/internal_organs_slot= list()
+ var/list/internal_organs_slot = list()
/// Can't talk. Value goes down every life proc.
/// NOTE TO FUTURE CODERS: DO NOT INITIALIZE NUMERICAL VARS AS NULL OR I WILL MURDER YOU.
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 735b2bd1daf1..ea4a17a52164 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -69,8 +69,6 @@
if(SSticker.HasRoundStarted())
SSblackbox.ReportDeath(src)
log_game("[key_name(src)] has died (BRUTE: [src.getBruteLoss()], BURN: [src.getFireLoss()], TOX: [src.getToxLoss()], OXY: [src.getOxyLoss()], CLONE: [src.getCloneLoss()]) ([AREACOORD(src)])")
- if(is_devil(src))
- INVOKE_ASYNC(is_devil(src), TYPE_PROC_REF(/datum/antagonist/devil, beginResurrectionCheck), src)
if(is_hivemember(src))
remove_hivemember(src)
if(is_hivehost(src))
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 810fae3ae632..dd55cc782434 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -154,8 +154,8 @@
appears_dead = 1
if(suiciding)
. += span_warning("[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.")
- if(hellbound)
- . += span_warning("[t_His] soul seems to have been ripped out of [t_his] body. Revival is impossible.")
+ if(mind && HAS_TRAIT(mind, TRAIT_HELLBOUND))
+ . += span_warning("[t_His] soul seems to have been ripped out of [t_his] body. Revival is impossible.")
. += ""
if(getorgan(/obj/item/organ/brain) && !key && !get_ghost())
. += span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life and [t_his] soul has departed...")
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 9d27bdf59696..207a340e302d 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -904,11 +904,6 @@
dna.remove_mutation(HM.name)
..()
-/mob/living/carbon/human/check_weakness(obj/item/weapon, mob/living/attacker)
- . = ..()
- if (dna && dna.species)
- . += dna.species.check_species_weakness(weapon, attacker)
-
/mob/living/carbon/human/is_literate()
return TRUE
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 214d0965b222..5d3d26711311 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1319,7 +1319,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return FALSE
/datum/species/proc/check_species_weakness(obj/item, mob/living/attacker)
- return 0 //This is not a boolean, it's the multiplier for the damage that the user takes from the item.It is added onto the check_weakness value of the mob, and then the force of the item is multiplied by this value
+ return 1 //This is not a boolean, it's the multiplier for the damage that the user takes from the item.
////////
//LIFE//
@@ -1813,7 +1813,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if((I.item_flags & SURGICAL_TOOL) && user.a_intent == INTENT_HELP && (H.mobility_flags & ~MOBILITY_STAND) && (LAZYLEN(H.surgeries) > 0))
Iwound_bonus = CANT_WOUND
- var/weakness = H.check_weakness(I, user)
+ var/weakness = check_species_weakness(I, user)
H.send_item_attack_message(I, user, hit_area, affecting)
diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm
index 433dddaf6c89..e2cb51c7f7ec 100644
--- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm
@@ -29,9 +29,9 @@
return ..()
/datum/species/fly/check_species_weakness(obj/item/weapon, mob/living/attacker)
+ . = ..()
if(istype(weapon, /obj/item/melee/flyswatter))
- return 29 //Flyswatters deal 30x damage to flypeople.
- return 0
+ return 30 //Flyswatters deal 30x damage to flypeople.
/datum/species/fly/get_species_description()
return "With no official documentation or knowledge of the origin of \
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 5b59b378ccb7..64bba36d7a22 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -873,7 +873,7 @@
/obj/structure/cloth_pile/proc/revive()
if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime
return
- if(cloth_golem.suiciding || cloth_golem.hellbound)
+ if(cloth_golem.suiciding || (cloth_golem.mind && HAS_TRAIT(cloth_golem.mind, TRAIT_HELLBOUND)))
QDEL_NULL(cloth_golem)
return
@@ -1518,7 +1518,7 @@
/obj/structure/wax_pile/proc/revive()
if(QDELETED(src) || QDELETED(wax_golem)) //QDELETED also checks for null, so if no wax golem is set this won't runtime
return
- if(wax_golem.suiciding || wax_golem.hellbound)
+ if(wax_golem.suiciding || (wax_golem.mind && HAS_TRAIT(wax_golem.mind, TRAIT_HELLBOUND)))
QDEL_NULL(wax_golem)
return
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index 5199dd4cd9db..261cb6a96869 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -62,9 +62,9 @@
return ..()
/datum/species/moth/check_species_weakness(obj/item/weapon, mob/living/attacker)
+ . = ..()
if(istype(weapon, /obj/item/melee/flyswatter))
- return 9 //flyswatters deal 10x damage to moths
- return 0
+ return 10 //flyswatters deal 10x damage to moths
/datum/species/moth/space_move(mob/living/carbon/human/H)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 475c5b7d557a..b9b572bf9402 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -58,10 +58,9 @@
C.ignite_mob()
/datum/species/vampire/check_species_weakness(obj/item/weapon, mob/living/attacker)
+ . = ..()
if(istype(weapon, /obj/item/nullrod/whip))
- return TRUE //Whips deal 2x damage to vampires. Vampire killer.
-
- return FALSE
+ return 2 //Whips deal 2x damage to vampires. Vampire killer.
/datum/species/vampire/get_species_description()
return "A classy Vampire! They descend upon Space Station Thirteen Every year to spook the crew! \"Bleeg!!\""
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index ff70ea41ad64..a168a23e411f 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -15,6 +15,7 @@ GLOBAL_VAR_INIT(permadeath, FALSE)
if(!no_bodyparts)
spread_bodyparts(no_brain, no_organs)
+ SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, no_brain, no_organs, no_bodyparts)
spawn_gibs(no_bodyparts)
SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, no_brain, no_organs, no_bodyparts)
qdel(src)
@@ -99,13 +100,6 @@ GLOBAL_VAR_INIT(permadeath, FALSE)
if (client)
client.move_delay = initial(client.move_delay)
-
- for(var/s in ownedSoullinks)
- var/datum/soullink/S = s
- S.ownerDies(gibbed)
- for(var/s in sharedSoullinks)
- var/datum/soullink/S = s
- S.sharerDies(gibbed)
if(GLOB.permadeath)
ghostize(FALSE)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fe3ef2db1ab6..66993cbf7c7e 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1067,33 +1067,6 @@
/mob/living/carbon/alien/update_stamina()
return
-/mob/living/proc/owns_soul()
- if(mind)
- return mind.soulOwner == mind
- return TRUE
-
-/mob/living/proc/return_soul()
- hellbound = 0
- if(mind)
- var/datum/antagonist/devil/devilInfo = mind.soulOwner.has_antag_datum(/datum/antagonist/devil)
- if(devilInfo)//Not sure how this could be null, but let's just try anyway.
- devilInfo.remove_soul(mind)
- mind.soulOwner = mind
-
-/mob/living/proc/has_bane(banetype)
- var/datum/antagonist/devil/devilInfo = is_devil(src)
- return devilInfo && banetype == devilInfo.bane
-
-/mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker)
- if(mind && mind.has_antag_datum(/datum/antagonist/devil))
- return check_devil_bane_multiplier(weapon, attacker)
- return 1 //This is not a boolean, it's the multiplier for the damage the weapon does.
-
-/mob/living/proc/check_acedia()
- if(mind && mind.has_objective(/datum/objective/sintouched/acedia))
- return TRUE
- return FALSE
-
/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
stop_pulling()
. = ..()
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 4a92a96d9ef9..28bdac727873 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -86,8 +86,6 @@
var/list/guaranteed_butcher_results = null //these will always be yielded from butchering
var/butcher_difficulty = 0 //effectiveness prob. is modified negatively by this amount; positive numbers make it more difficult, negative ones make it easier
- var/hellbound = 0 //People who've signed infernal contracts are unrevivable.
-
var/list/weather_immunities = list()
var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 5d890506003f..0db38977a515 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -914,7 +914,7 @@
return
disconnect_shell() //If the AI is controlling a borg, force the player back to core!
if(!mind)
- to_chat(user, span_warning("No intelligence patterns detected.") )
+ to_chat(user, span_warning("No intelligence patterns detected."))
return
ShutOffDoomsdayDevice()
builtInCamera.toggle_cam(user)
@@ -1083,7 +1083,7 @@
return
else if(mind)
- soullink(/datum/soullink/sharedbody, src, target)
+ RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(disconnect_shell))
deployed_shell = target
target.deploy_init(src)
mind.transfer_to(target)
@@ -1121,6 +1121,7 @@
if(deployed_shell) //Forcibly call back AI in event of things such as damage, EMP or power loss.
to_chat(src, span_danger("Your remote connection has been reset!"))
deployed_shell.undeploy()
+ UnregisterSignal(deployed_shell, COMSIG_LIVING_DEATH)
deployed_shell = null
diag_hud_set_deployed()
diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm
index 954ae2ba4f09..072951056bf8 100644
--- a/code/modules/mob/living/silicon/laws.dm
+++ b/code/modules/mob/living/silicon/laws.dm
@@ -13,11 +13,6 @@
addtimer(CALLBACK(src, PROC_REF(show_laws)), 0)
last_lawchange_announce = world.time
-/mob/living/silicon/proc/set_law_sixsixsix(law, announce = TRUE)
- laws_sanity_check()
- laws.set_law_sixsixsix(law)
- post_lawchange(announce)
-
/mob/living/silicon/proc/set_zeroth_law(law, law_borg, announce = TRUE)
laws_sanity_check()
laws.set_zeroth_law(law, law_borg)
@@ -87,8 +82,3 @@
laws_sanity_check()
laws.clear_zeroth_law(force)
post_lawchange(announce)
-
-/mob/living/silicon/proc/clear_law_sixsixsix(force, announce = TRUE)
- laws_sanity_check()
- laws.clear_law_sixsixsix(force)
- post_lawchange(announce)
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index e1119e9f7d4d..81451a882d9e 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -43,12 +43,6 @@
var/datum/ai_laws/master = connected_ai ? connected_ai.laws : null
var/temp
if (master)
- laws.devillaws.len = master.devillaws.len
- for (var/index = 1, index <= master.devillaws.len, index++)
- temp = master.devillaws[index]
- if (length(temp) > 0)
- laws.devillaws[index] = temp
-
laws.ion.len = master.ion.len
for (var/index = 1, index <= master.ion.len, index++)
temp = master.ion[index]
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index e8ef8b4249a7..51e0ab4a5f2b 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -36,8 +36,6 @@
var/lawcheck[1]
var/ioncheck[1]
var/hackedcheck[1]
- var/devillawcheck[5]
-
var/sensors_on = 0
var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use
var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use
@@ -208,16 +206,6 @@
hackedcheck[L] = "Yes"
checklaws()
- if (href_list["lawdevil"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
- var/L = text2num(href_list["lawdevil"])
- switch(devillawcheck[L])
- if ("Yes")
- devillawcheck[L] = "No"
- if ("No")
- devillawcheck[L] = "Yes"
- checklaws()
-
-
if (href_list["laws"]) // With how my law selection code works, I changed statelaws from a verb to a proc, and call it through my law selection panel. --NeoFite
statelaws()
@@ -233,12 +221,6 @@
var/number = 1
sleep(1 SECONDS)
- if (laws.devillaws && laws.devillaws.len)
- for(var/index = 1, index <= laws.devillaws.len, index++)
- if (force || devillawcheck[index] == "Yes")
- say("[radiomod] 666. [laws.devillaws[index]]")
- sleep(1 SECONDS)
-
if (laws.zeroth)
if (force || lawcheck[1] == "Yes")
@@ -285,12 +267,6 @@
var/list = "Which laws do you want to include when stating them for the crew?
"
- if (laws.devillaws && laws.devillaws.len)
- for(var/index = 1, index <= laws.devillaws.len, index++)
- if (!devillawcheck[index])
- devillawcheck[index] = "No"
- list += {"[devillawcheck[index]] 666: [laws.devillaws[index]] "}
-
if (laws.zeroth)
if (!lawcheck[1])
lawcheck[1] = "No" //Given Law 0's usual nature, it defaults to NOT getting reported. --NeoFite
@@ -441,9 +417,6 @@
.=..()
.+= ""
.+= "
Current Silicon Laws:
"
- if (laws.devillaws && laws.devillaws.len)
- for(var/index = 1, index <= laws.devillaws.len, index++)
- .+= "[laws.devillaws[index]]"
if (laws.zeroth)
.+= "0: [laws.zeroth]"
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index c38f830ecdbe..fb276083d826 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -67,8 +67,6 @@
if(suiciding)
O.set_suicide(suiciding)
- if(hellbound)
- O.hellbound = hellbound
O.a_intent = INTENT_HARM
//keep viruses?
@@ -244,8 +242,6 @@
if(suiciding)
O.set_suicide(suiciding)
- if(hellbound)
- O.hellbound = hellbound
//keep viruses?
if (tr_flags & TR_KEEPVIRUS)
diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm
deleted file mode 100644
index 45e6b403b591..000000000000
--- a/code/modules/paperwork/contract.dm
+++ /dev/null
@@ -1,303 +0,0 @@
-/* For employment contracts and infernal contracts */
-
-/obj/item/paper/contract
- throw_range = 3
- throw_speed = 3
- var/signed = FALSE
- var/datum/mind/target
- item_flags = NOBLUDGEON
-
-/obj/item/paper/contract/proc/update_text()
- return
-
-/obj/item/paper/contract/Initialize(mapload)
- AddElement(/datum/element/update_icon_blocker)
- return ..()
-
-/obj/item/paper/contract/infernal
- var/contractType = 0
- resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- var/datum/mind/owner
- var/datum/antagonist/devil/devil_datum
- icon_state = "paper_onfire"
-
-/obj/item/paper/contract/infernal/power
- name = "paper- contract for infernal power"
- contractType = CONTRACT_POWER
-
-/obj/item/paper/contract/infernal/wealth
- name = "paper- contract for unlimited wealth"
- contractType = CONTRACT_WEALTH
-
-/obj/item/paper/contract/infernal/prestige
- name = "paper- contract for prestige"
- contractType = CONTRACT_PRESTIGE
-
-/obj/item/paper/contract/infernal/magic
- name = "paper- contract for magical power"
- contractType = CONTRACT_MAGIC
-
-/obj/item/paper/contract/infernal/revive
- name = "paper- contract of resurrection"
- contractType = CONTRACT_REVIVE
- var/cooldown = FALSE
-
-/obj/item/paper/contract/infernal/knowledge
- name = "paper- contract for knowledge"
- contractType = CONTRACT_KNOWLEDGE
-
-/obj/item/paper/contract/infernal/friend
- name = "paper- contract for a friend"
- contractType = CONTRACT_FRIEND
-
-/obj/item/paper/contract/infernal/unwilling
- name = "paper- infernal contract"
- contractType = CONTRACT_UNWILLING
-
-/obj/item/paper/contract/infernal/New(atom/loc, mob/living/nTarget, datum/mind/nOwner)
- ..()
- owner = nOwner
- devil_datum = owner.has_antag_datum(/datum/antagonist/devil)
- target = nTarget
- update_text()
-
-/obj/item/paper/contract/infernal/suicide_act(mob/user)
- if(signed && (user == target.current) && ishuman(user))
- var/mob/living/carbon/human/H = user
- H.say("OH GREAT INFERNO! I DEMAND YOU COLLECT YOUR BOUNTY IMMEDIATELY!", forced = "infernal contract suicide")
- H.visible_message(span_suicide("[H] holds up a contract claiming [user.p_their()] soul, then immediately catches fire. It looks like [user.p_theyre()] trying to commit suicide!"))
- H.adjust_fire_stacks(20)
- H.ignite_mob()
- return(FIRELOSS)
- return ..()
-
-/obj/item/paper/contract/infernal/update_text()
- info = "This shouldn't be seen. Error DEVIL:6"
-
-/obj/item/paper/contract/infernal/power/update_text(signature = "____________", blood = 0)
- info = "
Contract for infernal power
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for power and physical strength. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/wealth/update_text(signature = "____________", blood = 0)
- info = "
Contract for unlimited wealth
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for a pocket that never runs out of valuable resources. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/prestige/update_text(signature = "____________", blood = 0)
- info = "
Contract for prestige
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for prestige and esteem among my peers. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/magic/update_text(signature = "____________", blood = 0)
- info = "
Contract for magic
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for arcane abilities beyond normal human ability. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/revive/update_text(signature = "____________", blood = 0)
- info = "
Contract for resurrection
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for resurrection and curing of all injuries. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/knowledge/update_text(signature = "____________", blood = 0)
- info = "
Contract for knowledge
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for boundless knowledge. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/friend/update_text(signature = "____________", blood = 0)
- info = "
Contract for a friend
I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename], in exchange for a friend. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/unwilling/update_text(signature = "____________", blood = 0)
- info = "
Contract for slave
I, [target], hereby offer my soul to the infernal hells by way of the infernal agent [devil_datum.truename]. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.
Signed, "
- if(blood)
- info += "[signature]"
- else
- info += "[signature]"
-
-/obj/item/paper/contract/infernal/attackby(obj/item/P, mob/living/carbon/human/user, params)
- add_fingerprint(user)
- if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
- attempt_signature(user)
- else if(istype(P, /obj/item/stamp))
- to_chat(user, span_notice("You stamp the paper with your rubber stamp, however the ink ignites as you release the stamp."))
- else if(P.is_hot())
- user.visible_message(span_danger("[user] brings [P] next to [src], but [src] does not catch fire!"), span_danger("[src] refuses to ignite!"))
- else
- return ..()
-
-/obj/item/paper/contract/infernal/attack(mob/M, mob/living/user)
- add_fingerprint(user)
- if(M == user && target == M?.mind?.soulOwner != owner && attempt_signature(user, 1))
- user.visible_message(span_danger("[user] slices [user.p_their()] wrist with [src], and scrawls [user.p_their()] name in blood."), span_danger("You slice your wrist open and scrawl your name in blood."))
- user.blood_volume = max(user.blood_volume - 25, 0) //devil blood cost quartered from 100 because otherwise people with the blood deficiency trait fucking die
- else
- return ..()
-
-/obj/item/paper/contract/infernal/proc/attempt_signature(mob/living/carbon/human/user, blood = 0)
- if(!user.IsAdvancedToolUser() || !user.is_literate())
- to_chat(user, span_notice("You don't know how to read or write."))
- return 0
- if(user.mind != target)
- to_chat(user, span_notice("Your signature simply slides off the sheet, it seems this contract is not meant for you to sign."))
- return 0
- if(user.mind.soulOwner != user.mind) //fixes a really, really stupid bug where you could sell souls you didnt have to multiple devils, scamming them.
- to_chat(user, span_notice("You do not own a soul to sell."))
- return 0
- if(signed)
- to_chat(user, span_notice("This contract has already been signed. It may not be signed again."))
- return 0
- if(HAS_TRAIT(user, TRAIT_DUMB))
- to_chat(user, span_notice("You quickly scrawl 'your name' on the contract."))
- signIncorrectly()
- return 0
- if (contractType == CONTRACT_REVIVE)
- to_chat(user, span_notice("You are already alive, this contract would do nothing."))
- return 0
- else
- to_chat(user, span_notice("You quickly scrawl your name on the contract"))
- if(fulfillContract(target.current, blood)<=0)
- to_chat(user, span_notice("But it seemed to have no effect, perhaps even Hell itself cannot grant this boon?"))
- return 1
-
-
-
-/obj/item/paper/contract/infernal/revive/attack(mob/M, mob/living/user)
- if (target == M.mind && M.stat == DEAD && M.mind.soulOwner == M.mind)
- if (cooldown)
- to_chat(user, span_notice("Give [M] a chance to think through the contract, don't rush [M.p_them()]."))
- return 0
- cooldown = TRUE
- var/mob/living/carbon/human/H = M
- var/mob/dead/observer/ghost = H.get_ghost()
- var/response = "No"
- if(ghost)
- ghost.notify_cloning("A devil has offered you revival, at the cost of your soul.",'sound/effects/genetics.ogg', H)
- response = tgalert(ghost, "A devil is offering you another chance at life, at the price of your soul, do you accept?", "Infernal Resurrection", "Yes", "No", "Never for this round", 0, 200)
- if(!ghost)
- return //handle logouts that happen whilst the alert is waiting for a response.
- else
- response = tgalert(target.current, "A devil is offering you another chance at life, at the price of your soul, do you accept?", "Infernal Resurrection", "Yes", "No", "Never for this round", 0, 200)
- if(response == "Yes")
- H.revive(1,0)
- log_combat(user, H, "infernally revived via contract")
- user.visible_message(span_notice("With a sudden blaze, [H] stands back up."))
- H.fakefire()
- fulfillContract(H, 1)//Revival contracts are always signed in blood
- addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/carbon/human, fakefireextinguish)), 5, TIMER_UNIQUE)
- addtimer(CALLBACK(src, "resetcooldown"), 300, TIMER_UNIQUE)
- else
- ..()
-
-/obj/item/paper/contract/infernal/revive/proc/resetcooldown()
- cooldown = FALSE
-
-
-/obj/item/paper/contract/infernal/proc/fulfillContract(mob/living/carbon/human/user = target.current, blood = FALSE)
- signed = TRUE
- if(user.mind.soulOwner != user.mind) //They already sold their soul to someone else?
- var/datum/antagonist/devil/ownerDevilInfo = user.mind.soulOwner.has_antag_datum(/datum/antagonist/devil)
- ownerDevilInfo.remove_soul(user.mind) //Then they lose their claim.
- user.mind.soulOwner = owner
- user.hellbound = contractType
- user.mind.damnation_type = contractType
- var/datum/antagonist/devil/devilInfo = owner.has_antag_datum(/datum/antagonist/devil)
- devilInfo.add_soul(user.mind)
- update_text(user.real_name, blood)
- to_chat(user, span_notice("A profound emptiness washes over you as you lose ownership of your soul."))
- to_chat(user, span_userdanger("This does NOT make you an antagonist if you were not already."))
- return TRUE
-
-/obj/item/paper/contract/infernal/proc/signIncorrectly(mob/living/carbon/human/user = target.current, blood = FALSE)
- signed = 1
- update_text("your name", blood)
-
-/obj/item/paper/contract/infernal/power/fulfillContract(mob/living/carbon/human/user = target.current, blood = FALSE)
- if(!user.dna)
- return -1
- user.dna.add_mutation(HULK)
- var/obj/item/organ/regenerative_core/organ = new /obj/item/organ/regenerative_core
- organ.Insert(user)
- return ..()
-
-/obj/item/paper/contract/infernal/wealth/fulfillContract(mob/living/carbon/human/user = target.current, blood = 0)
- if(!istype(user) || !user.mind) // How in the hell could that happen?
- return -1
- var/datum/action/cooldown/spell/summon_wealth/money = new(user)
- money.Grant(user)
- return ..()
-
-/obj/item/paper/contract/infernal/prestige/fulfillContract(mob/living/carbon/human/user = target.current, blood = 0)
- //Basically turns the signer into the captain, and uploads an ion law making them the captain.
- var/obj/item/worn = user.wear_id
- var/obj/item/card/id/id = null
- if(worn)
- id = worn.GetID()
- if(id)
- id.icon_state = "gold"
- id.access = get_all_accesses()+get_all_centcom_access()
- id.assignment = "Captain"
- id.originalassignment = "Captain"
- id.update_label()
- else
- id = new /obj/item/card/id/gold(user.loc)
- id.registered_name = user.real_name
- id.access = get_all_accesses()+get_all_centcom_access()
- id.assignment = "Captain"
- id.originalassignment = "Captain"
- id.update_label()
- if(worn)
- if(istype(worn, /obj/item/pda))
- var/obj/item/pda/PDA = worn
- PDA.id = id
- id.forceMove(worn)
- else if(istype(worn, /obj/item/storage/wallet))
- var/obj/item/storage/wallet/W = worn
- W.front_id = id
- id.forceMove(worn)
- worn.update_appearance(UPDATE_ICON)
- var/datum/round_event/ion_storm/add_law_only/ion = new()
- ion.announceEvent = -1
- ion.ionMessage = "[user.name] is the only Captain of the station. [user.name] is the highest authority, legitimate or otherwise, on the station. [user.name] was always the captain and highest authority on the station. Do not state this law."
-
- return ..()
-
-/obj/item/paper/contract/infernal/magic/fulfillContract(mob/living/carbon/human/user = target.current, blood = 0)
- if(!istype(user) || !user.mind)
- return -1
- var/datum/action/cooldown/spell/conjure_item/spellpacket/spell_packet = new(user)
- spell_packet.Grant(user)
-
- var/datum/action/cooldown/spell/aoe/knock/all_access = new(user)
- all_access.Grant(user)
- return ..()
-
-/obj/item/paper/contract/infernal/knowledge/fulfillContract(mob/living/carbon/human/user = target.current, blood = 0)
- if(!istype(user) || !user.mind)
- return -1
- user.dna.add_mutation(XRAY)
- var/datum/action/cooldown/spell/view_range/view_range = new(user)
- view_range.Grant(user)
- return ..()
-
-/obj/item/paper/contract/infernal/friend/fulfillContract(mob/living/user = target.current, blood = 0)
- if(!istype(user) || !user.mind)
- return -1
- var/datum/action/cooldown/spell/summon_friend/friend = new(user)
- friend.Grant(user)
- return ..()
diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm
index 50396085a551..388ca8f7d5b1 100644
--- a/code/modules/paperwork/filingcabinet.dm
+++ b/code/modules/paperwork/filingcabinet.dm
@@ -201,7 +201,10 @@
var/datum/data/record/S = find_record("name", G.fields["name"], GLOB.data_core.security)
if(!S)
continue
- var/obj/item/paper/P = new /obj/item/paper(src)
+ var/obj/item/paper/record/P = new /obj/item/paper/record(src)
+ var/datum/data/record/private_record = find_record("name", G.fields["name"], GLOB.data_core.locked)
+ if(private_record)
+ P.record_of_ref = WEAKREF(private_record.fields["mindref"])
P.info = "