From e29c2bd2ca0b4d6d5ea639b370e5ec50eb9dfa26 Mon Sep 17 00:00:00 2001
From: Antoonij <42318445+Antoonij@users.noreply.github.com>
Date: Mon, 6 Jan 2025 20:15:30 +0100
Subject: [PATCH] add: Updated Devil gamemode (#5944)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* let's do first step to rework that piece of ..
* step 2 - remove fucken devilinfo from mind
* step 3 - eat zuzya (clean mind and living procs)
* toy gigax, fake devil datum
* UPD
* UPD
* some fixes
* UPD
* попытка зарезолвить конфликт дубль 1
* дубль 2
* fastfix
* double 3
* Delete code/game/gamemodes/devil/devilinfo.dm
* CLRF to LF
* Resurrection rework
* catsmile
* fix inconsistent
* fix.. again
* improve
* resurrection bugfixes
* полностью модульные бейны
* фикс пробелов
* fixes. again
* fix nr2
* again
* omagad is THAT objectivos?????
* fix
* UPD
* forgotten moments fixed
* identitation fix
* fix again
* ranks are now separated datum
* fix
* fix again
* forgotten things
* some fixes
* NO_DEATH TRAIT
* bugfixes + rewrite toy gigas
* whoops.
* UPD + bugfix
* fastfix
* fastfix and trailing
* metabolism UPD
* the last fix, i hope
* попытка исправить cast
* resurrection rework
* fix
* fix again...
* UPD
* модульная информация о димоне
* fixes
* UPD
* fix inconsistent
* the last fixes
* UPD
* fix inconsistent
* cleanup
* runtime fixes
* Global list liquidation - full modular devil bans, devil obligations
* inconsistent fix
* fix
* UPD
* fastfix
* UPD
* UPD
* fix inconsistent
* fix again
* rewrite codex gigas + resolve merge conflict
* rewrite sintouched to antagonist datum
* dme
* inconsistent
* last fixes
* rewrite sintouched give objectives
* les go final
* fix
* fix inconsistent
* fix
* i love inconsistent
* finallyyyyyyyy
* тепеь финал
* forgotten moments and ama ready
* clean unused
* forgotten moments
* forgotten moments fix
* improve
* improve x2
* fix
* wtf
* Rewrite add reagent signal
* early*
* improve
* first fixes
* fix vocal cords
* more fixes
* упс
* optimize
* fiix
* mor fix
* get_all_objectives
* OnUnarmedAttack
* 1
* 2
* 3
* conflicts
* fix
* fix x2
* dme
* smol fix
*
* improve
* fix inconsistent
* fix x2
* trailing....
---
code/__DEFINES/contracts.dm | 37 --
code/__DEFINES/dcs/signals.dm | 10 +
code/__DEFINES/devil.dm | 87 +++
code/__DEFINES/gamemode.dm | 94 +--
code/__DEFINES/role_preferences.dm | 2 +-
code/__DEFINES/traits/declarations.dm | 1 +
code/_globalvars/traits.dm | 1 +
code/datums/components/ritual_object.dm | 5 +-
code/datums/diseases/ectoplasmic.dm | 2 +-
code/datums/elements/devil_banishment.dm | 51 ++
code/datums/elements/devil_regen.dm | 113 ++++
code/datums/mind.dm | 76 +--
code/datums/rituals.dm | 2 +-
.../spell_handler/devil_spell_handler.dm | 10 +
code/datums/spells/devil.dm | 357 ++++++++---
code/game/gamemodes/devil/devil.dm | 48 --
.../devil/devil_agent/devil_agent.dm | 38 --
code/game/gamemodes/devil/devil_game_mode.dm | 66 ---
code/game/gamemodes/devil/devilinfo.dm | 560 ------------------
code/game/gamemodes/devil/game_mode.dm | 103 ----
code/game/gamemodes/devil/objectives.dm | 149 ++---
.../gamemodes/devil/true_devil/inventory.dm | 2 -
.../miniantags/sintouched/objectives.dm | 11 -
.../objects/effects/decals/Cleanable/misc.dm | 18 +
code/game/objects/items/toys.dm | 45 +-
code/game/objects/items/weapons/twohanded.dm | 8 +-
.../antagonists}/devil/contracts/friend.dm | 0
code/modules/antagonists/devil/devil.dm | 205 +++++++
code/modules/antagonists/devil/devil_ban.dm | 71 +++
code/modules/antagonists/devil/devil_bane.dm | 191 ++++++
.../modules/antagonists/devil/devil_banish.dm | 90 +++
code/modules/antagonists/devil/devil_info.dm | 66 +++
.../antagonists/devil/devil_obligation.dm | 94 +++
.../modules/antagonists/devil/devil_outfit.dm | 30 +
code/modules/antagonists/devil/devil_pawn.dm | 7 +
code/modules/antagonists/devil/devil_rank.dm | 123 ++++
.../modules/antagonists/devil/devil_ritual.dm | 88 +++
.../modules/antagonists/devil/helper_procs.dm | 14 +
.../antagonists}/devil/imp/imp.dm | 11 +-
code/modules/antagonists/devil/sintouched.dm | 43 ++
.../devil/true_devil/_true_devil.dm | 81 +--
code/modules/library/codex_gigas.dm | 97 +--
code/modules/mob/living/carbon/carbon.dm | 5 -
code/modules/mob/living/carbon/human/human.dm | 40 --
.../mob/living/carbon/human/human_defense.dm | 17 +-
code/modules/mob/living/death.dm | 8 +-
code/modules/mob/living/living.dm | 33 +-
code/modules/paperwork/contract.dm | 63 +-
code/modules/reagents/chemistry/holder.dm | 22 +-
code/modules/reagents/chemistry/reagents.dm | 7 +-
.../reagents/chemistry/reagents/misc.dm | 10 -
code/modules/surgery/organs/vocal_cords.dm | 15 +-
paradise.dme | 31 +-
53 files changed, 1918 insertions(+), 1440 deletions(-)
create mode 100644 code/__DEFINES/devil.dm
create mode 100644 code/datums/elements/devil_banishment.dm
create mode 100644 code/datums/elements/devil_regen.dm
create mode 100644 code/datums/spell_handler/devil_spell_handler.dm
delete mode 100644 code/game/gamemodes/devil/devil.dm
delete mode 100644 code/game/gamemodes/devil/devil_agent/devil_agent.dm
delete mode 100644 code/game/gamemodes/devil/devil_game_mode.dm
delete mode 100644 code/game/gamemodes/devil/devilinfo.dm
delete mode 100644 code/game/gamemodes/devil/true_devil/inventory.dm
rename code/{game/gamemodes => modules/antagonists}/devil/contracts/friend.dm (100%)
create mode 100644 code/modules/antagonists/devil/devil.dm
create mode 100644 code/modules/antagonists/devil/devil_ban.dm
create mode 100644 code/modules/antagonists/devil/devil_bane.dm
create mode 100644 code/modules/antagonists/devil/devil_banish.dm
create mode 100644 code/modules/antagonists/devil/devil_info.dm
create mode 100644 code/modules/antagonists/devil/devil_obligation.dm
create mode 100644 code/modules/antagonists/devil/devil_outfit.dm
create mode 100644 code/modules/antagonists/devil/devil_pawn.dm
create mode 100644 code/modules/antagonists/devil/devil_rank.dm
create mode 100644 code/modules/antagonists/devil/devil_ritual.dm
create mode 100644 code/modules/antagonists/devil/helper_procs.dm
rename code/{game/gamemodes => modules/antagonists}/devil/imp/imp.dm (74%)
create mode 100644 code/modules/antagonists/devil/sintouched.dm
rename code/{game/gamemodes => modules/antagonists}/devil/true_devil/_true_devil.dm (72%)
diff --git a/code/__DEFINES/contracts.dm b/code/__DEFINES/contracts.dm
index 26351cfbac2..b333b769108 100644
--- a/code/__DEFINES/contracts.dm
+++ b/code/__DEFINES/contracts.dm
@@ -19,40 +19,3 @@
#define CONTRACT_FOOD "Food"
#define CONTRACT_SPACE "Space Gear"
#define CONTRACT_CALAMITY "Calamity"
-
-#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_HURTMAN "hurtman"
-#define BAN_CHAPEL "chapel"
-#define BAN_HURTPRIEST "hurtpriest"
-#define BAN_AVOIDWATER "avoidwater"
-#define BAN_STRIKEUNCONCIOUS "strikeunconcious"
-#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/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index f2c8df77b11..008f501e8dd 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -553,6 +553,9 @@
#define COMSIG_LIVING_SHOCK_PREVENTED "living_shock_prevented"
///sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock"
+/// Source: /mob/living/proc/flash_eyes(intensity, override_blindness_check, affect_silicon, visual, type)
+#define COMSIG_LIVING_EARLY_FLASH_EYES "living_flash_eyes"
+ #define STOP_FLASHING_EYES (1<<0)
///from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_LIVING_REVIVE "living_revive"
///from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
@@ -582,6 +585,8 @@
///From living/Life(). (deltatime, times_fired)
#define COMSIG_LIVING_LIFE "living_life"
///from base of mob/living/death(): (gibbed)
+#define COMSIG_LIVING_EARLY_DEATH "living_early_death"
+///from base of mob/living/death(): (gibbed)
#define COMSIG_LIVING_DEATH "living_death"
//sent from mobs when they exit their body as a ghost
#define COMSIG_LIVING_GHOSTIZED "ghostized"
@@ -1008,6 +1013,11 @@
///from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder)
#define COMSIG_FOOD_EATEN "food_eaten"
+/// Reagents
+/// Source: /datum/reagents/proc/add_reagent (datum/reagents, reagent_id, amount, data, reagtemp, no_react, chem_temp)
+#define COMSIG_EARLY_REAGENT_ADDED "reagent_early_added"
+ #define COMPONENT_PREVENT_ADD_REAGENT (1<<0)
+
//Gibs
///from base of /obj/effect/decal/cleanable/blood/gibs/streak(): (list/directions, list/diseases)
diff --git a/code/__DEFINES/devil.dm b/code/__DEFINES/devil.dm
new file mode 100644
index 00000000000..f58afda46b9
--- /dev/null
+++ b/code/__DEFINES/devil.dm
@@ -0,0 +1,87 @@
+GLOBAL_LIST_INIT(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/psych = 1,
+ /obj/item/clothing/under/rank/orderly = 1,
+ /obj/item/clothing/under/rank/security/brigphys = 1,
+ /obj/item/clothing/under/rank/internalaffairs = 1,
+ /obj/item/clothing/under/rank/ntrep = 1,
+ /obj/item/clothing/under/det = 1,
+ /obj/item/clothing/under/wedding/bride_white = 1,
+ /obj/item/clothing/under/mafia/white = 1,
+ /obj/item/clothing/under/noble_clothes = 1,
+ /obj/item/clothing/under/sl_suit = 1,
+ /obj/item/clothing/under/burial = 1
+))
+
+#define ENRAGED_THRESHOLD 4
+#define BLOOD_THRESHOLD 7
+#define TRUE_THRESHOLD 10
+
+#define BASIC_DEVIL_REGEN_THRESHOLD 10 SECONDS
+#define ENRAGED_DEVIL_REGEN_THRESHOLD 10 SECONDS
+#define BLOOD_LIZARD_REGEN_THRESHOLD 5 SECONDS
+#define TRUE_DEVIL_REGEN_THRESHOLD 3 SECONDS
+
+#define BASIC_DEVIL_REGEN_AMOUNT 20
+#define ENRAGED_DEVIL_REGEN_AMOUNT 40
+#define BLOOD_LIZARD_REGEN_AMOUNT 60
+#define TRUE_DEVIL_REGEN_AMOUNT 80
+
+#define BASIC_DEVIL_RANK /datum/devil_rank/basic_devil
+#define ENRAGED_DEVIL_RANK /datum/devil_rank/enraged_devil
+#define BLOOD_LIZARD_RANK /datum/devil_rank/blood_lizard
+#define TRUE_DEVIL_RANK /datum/devil_rank/true_devil
+
+#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_HURTMAN "hurtman"
+#define BAN_CHAPEL "chapel"
+#define BAN_HURTPRIEST "hurtpriest"
+#define BAN_AVOIDWATER "avoidwater"
+#define BAN_STRIKEUNCONCIOUS "strikeunconcious"
+#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 BANE_TOOLBOX_DAMAGE_MODIFIER 2.5
+#define BANE_HARVEST_DAMAGE_MULTIPLIER 2
+
+GLOBAL_LIST_EMPTY(allDevils)
+//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."))
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 1457c5bc039..121121aa158 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -25,50 +25,52 @@
#define GAMEMODE_IS_WIZARD (SSticker && istype(SSticker.mode, /datum/game_mode/wizard))
#define GAMEMODE_IS_RAGIN_MAGES (SSticker && istype(SSticker.mode, /datum/game_mode/wizard/raginmages))
-//special roles
+// special roles
// Distinct from the ROLE_X defines because some antags have multiple special roles but only one ban type
-#define SPECIAL_ROLE_ABDUCTOR_AGENT "Abductor Agent"
-#define SPECIAL_ROLE_ABDUCTOR_SCIENTIST "Abductor Scientist"
-#define SPECIAL_ROLE_BLOB "Blob"
-#define SPECIAL_ROLE_BLOB_OVERMIND "Blob Overmind"
-#define SPECIAL_ROLE_BLOB_MINION "Blob Minion"
-#define SPECIAL_ROLE_BORER "Borer"
-#define SPECIAL_ROLE_CARP "Space Carp"
-#define SPECIAL_ROLE_CHANGELING "Changeling"
-#define SPECIAL_ROLE_CULTIST "Cultist"
-#define SPECIAL_ROLE_CLOCKER "Clockwork cultist"
-#define SPECIAL_ROLE_DEATHSQUAD "Death Commando"
-#define SPECIAL_ROLE_ERT "Response Team"
-#define SPECIAL_ROLE_FREE_GOLEM "Free Golem"
-#define SPECIAL_ROLE_GOLEM "Golem"
-#define SPECIAL_ROLE_HEAD_REV "Head Revolutionary"
-#define SPECIAL_ROLE_HEADSLUG "HeadSlug"
-#define SPECIAL_ROLE_HONKSQUAD "Honksquad"
-#define SPECIAL_ROLE_REV "Revolutionary"
-#define SPECIAL_ROLE_MORPH "Morph"
-#define SPECIAL_ROLE_MULTIVERSE "Multiverse Traveller"
-#define SPECIAL_ROLE_NUKEOPS "Syndicate"
-#define SPECIAL_ROLE_PYROCLASTIC_SLIME "Pyroclastic Anomaly Slime"
-#define SPECIAL_ROLE_RAIDER "Vox Raider"
-#define SPECIAL_ROLE_REVENANT "Revenant"
-#define SPECIAL_ROLE_SHADOWLING "Shadowling"
-#define SPECIAL_ROLE_SHADOWLING_THRALL "Shadowling Thrall"
-#define SPECIAL_ROLE_DEMON "Demon"
-#define SPECIAL_ROLE_SUPER "Super"
-#define SPECIAL_ROLE_SYNDICATE_DEATHSQUAD "Syndicate Commando"
-#define SPECIAL_ROLE_TRAITOR "Traitor"
-#define SPECIAL_ROLE_VAMPIRE "Vampire"
-#define SPECIAL_ROLE_VAMPIRE_THRALL "Vampire Thrall"
-#define SPECIAL_ROLE_WIZARD "Wizard"
-#define SPECIAL_ROLE_WIZARD_APPRENTICE "Wizard Apprentice"
-#define SPECIAL_ROLE_XENOMORPH "Xenomorph"
-#define SPECIAL_ROLE_XENOMORPH_QUEEN "Xenomorph Queen"
-#define SPECIAL_ROLE_XENOMORPH_HUNTER "Xenomorph Hunter"
-#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone"
-#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel"
-#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva"
-#define SPECIAL_ROLE_SPACE_NINJA "Space Ninja"
-#define SPECIAL_ROLE_THIEF "Thief"
-#define SPECIAL_ROLE_SPACE_DRAGON "Space Dragon"
-#define SPECIAL_ROLE_EVENTMISC "Event Role"
-#define SPECIAL_ROLE_MALFAI "Malfunctioning AI"
+#define SPECIAL_ROLE_ABDUCTOR_AGENT "Abductor Agent"
+#define SPECIAL_ROLE_ABDUCTOR_SCIENTIST "Abductor Scientist"
+#define SPECIAL_ROLE_BLOB "Blob"
+#define SPECIAL_ROLE_BLOB_OVERMIND "Blob Overmind"
+#define SPECIAL_ROLE_BLOB_MINION "Blob Minion"
+#define SPECIAL_ROLE_BORER "Borer"
+#define SPECIAL_ROLE_CARP "Space Carp"
+#define SPECIAL_ROLE_CHANGELING "Changeling"
+#define SPECIAL_ROLE_CULTIST "Cultist"
+#define SPECIAL_ROLE_CLOCKER "Clockwork cultist"
+#define SPECIAL_ROLE_DEATHSQUAD "Death Commando"
+#define SPECIAL_ROLE_ERT "Response Team"
+#define SPECIAL_ROLE_FREE_GOLEM "Free Golem"
+#define SPECIAL_ROLE_GOLEM "Golem"
+#define SPECIAL_ROLE_HEAD_REV "Head Revolutionary"
+#define SPECIAL_ROLE_HEADSLUG "HeadSlug"
+#define SPECIAL_ROLE_HONKSQUAD "Honksquad"
+#define SPECIAL_ROLE_REV "Revolutionary"
+#define SPECIAL_ROLE_MORPH "Morph"
+#define SPECIAL_ROLE_MULTIVERSE "Multiverse Traveller"
+#define SPECIAL_ROLE_NUKEOPS "Syndicate"
+#define SPECIAL_ROLE_PYROCLASTIC_SLIME "Pyroclastic Anomaly Slime"
+#define SPECIAL_ROLE_RAIDER "Vox Raider"
+#define SPECIAL_ROLE_REVENANT "Revenant"
+#define SPECIAL_ROLE_SHADOWLING "Shadowling"
+#define SPECIAL_ROLE_SHADOWLING_THRALL "Shadowling Thrall"
+#define SPECIAL_ROLE_DEMON "Demon"
+#define SPECIAL_ROLE_SUPER "Super"
+#define SPECIAL_ROLE_SYNDICATE_DEATHSQUAD "Syndicate Commando"
+#define SPECIAL_ROLE_TRAITOR "Traitor"
+#define SPECIAL_ROLE_VAMPIRE "Vampire"
+#define SPECIAL_ROLE_VAMPIRE_THRALL "Vampire Thrall"
+#define SPECIAL_ROLE_WIZARD "Wizard"
+#define SPECIAL_ROLE_WIZARD_APPRENTICE "Wizard Apprentice"
+#define SPECIAL_ROLE_XENOMORPH "Xenomorph"
+#define SPECIAL_ROLE_XENOMORPH_QUEEN "Xenomorph Queen"
+#define SPECIAL_ROLE_XENOMORPH_HUNTER "Xenomorph Hunter"
+#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone"
+#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel"
+#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva"
+#define SPECIAL_ROLE_SPACE_NINJA "Space Ninja"
+#define SPECIAL_ROLE_THIEF "Thief"
+#define SPECIAL_ROLE_SPACE_DRAGON "Space Dragon"
+#define SPECIAL_ROLE_EVENTMISC "Event Role"
+#define SPECIAL_ROLE_MALFAI "Malfunctioning AI"
+#define SPECIAL_ROLE_SINTOUCHED "Sintouched"
+#define SPECIAL_ROLE_DEVIL_PAWN "Devil's pawn"
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 0285fecd7bf..a0429a5cb97 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_CULTIST = /datum/game_mode/cult, // Cultist
ROLE_CLOCKER = /datum/game_mode/clockwork, // Clockwork Cultist
ROLE_DEMON, // Demons (Slaughter/Laughter/Shadow)
- ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil
+ ROLE_DEVIL, // Devil
ROLE_GSPIDER, // Giant spider
ROLE_GUARDIAN, // Guardian
ROLE_ELITE, // Lavaland Elite
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 718a34a9308..2f24d20b028 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -48,6 +48,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
//mob traits
#define TRAIT_GODMODE "godmode"
#define TRAIT_PACIFISM "pacifism"
+#define TRAIT_NO_DEATH "nodeath"
#define TRAIT_WATERBREATH "waterbreathing"
#define TRAIT_BLOODCRAWL "bloodcrawl"
#define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 94b555a43c1..f61699a0c5e 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -91,6 +91,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NEARSIGHTED" = TRAIT_NEARSIGHTED,
"TRAIT_NEGATES_GRAVITY" = TRAIT_NEGATES_GRAVITY,
"TRAIT_NO_BIOCHIPS" = TRAIT_NO_BIOCHIPS,
+ "TRAIT_NO_DEATH" = TRAIT_NO_DEATH,
"TRAIT_NO_BLOOD" = TRAIT_NO_BLOOD,
"TRAIT_NO_BLOOD_RESTORE" = TRAIT_NO_BLOOD_RESTORE,
"TRAIT_NO_BREATH" = TRAIT_NO_BREATH,
diff --git a/code/datums/components/ritual_object.dm b/code/datums/components/ritual_object.dm
index 31064e438db..f67fbd8b9aa 100644
--- a/code/datums/components/ritual_object.dm
+++ b/code/datums/components/ritual_object.dm
@@ -22,6 +22,7 @@
src.allowed_categories = allowed_categories
src.allowed_species = allowed_species
src.allowed_special_role = allowed_special_role
+
get_rituals()
/datum/component/ritual_object/RegisterWithParent()
@@ -62,7 +63,7 @@
if(allowed_species && !is_type_in_list(human.dna.species, allowed_species))
return
- if(allowed_special_role && !is_type_in_list(human.mind?.special_role, allowed_special_role))
+ if(allowed_special_role && !LAZYIN(allowed_special_role, human.mind?.special_role))
return
active_ui = TRUE
@@ -111,7 +112,7 @@
if(ritual.allowed_species && !is_type_in_list(human.dna.species, ritual.allowed_species))
continue
- if(ritual.allowed_special_role && !is_type_in_list(human.mind?.special_role, ritual.allowed_special_role))
+ if(ritual.allowed_special_role && !LAZYIN(ritual.allowed_special_role, human.mind?.special_role))
continue
LAZYADD(rituals_list, ritual.name)
diff --git a/code/datums/diseases/ectoplasmic.dm b/code/datums/diseases/ectoplasmic.dm
index 06b17032931..8d6ecf3b649 100644
--- a/code/datums/diseases/ectoplasmic.dm
+++ b/code/datums/diseases/ectoplasmic.dm
@@ -47,7 +47,7 @@
create_effect = TRUE
if(5)
if(prob(SYMPTOM_ACTIVATION_PROB * 10))
- human.influenceSin()
+ human.mind?.add_antag_datum(/datum/antagonist/sintouched)
to_chat(human, span_revenbignotice("You suddenly feel your soul become corrupted."))
else
human.apply_damage(80, STAMINA)
diff --git a/code/datums/elements/devil_banishment.dm b/code/datums/elements/devil_banishment.dm
new file mode 100644
index 00000000000..4fefe4d79d9
--- /dev/null
+++ b/code/datums/elements/devil_banishment.dm
@@ -0,0 +1,51 @@
+/datum/element/devil_banishment
+ element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
+ id_arg_index = 2
+
+ var/linked_timer
+
+/datum/element/devil_banishment/Attach(datum/target)
+ . = ..()
+ var/mob/living/carbon/human = target
+
+ if(!istype(human) || !human.mind?.has_antag_datum(/datum/antagonist/devil))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(human, COMSIG_LIVING_EARLY_DEATH, PROC_REF(pre_death))
+
+/datum/element/devil_banishment/Detach(datum/target)
+ . = ..()
+
+ UnregisterSignal(target, COMSIG_LIVING_EARLY_DEATH)
+
+/datum/element/devil_banishment/proc/pre_death(datum/source, gibbed)
+ SIGNAL_HANDLER
+
+ if(gibbed || linked_timer)
+ return
+
+ var/mob/living/carbon/human = source
+ var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(!devil?.info)
+ return
+
+ playsound(get_turf(human), 'sound/magic/vampire_anabiosis.ogg', 50, 0, TRUE)
+ linked_timer = addtimer(CALLBACK(src, PROC_REF(try_banishment), human, devil), devil.rank.regen_threshold / 2, TIMER_LOOP | TIMER_STOPPABLE | TIMER_DELETE_ME)
+
+/datum/element/devil_banishment/proc/try_banishment(mob/living/carbon/human, datum/antagonist/devil/devil)
+ if(human.health >= human.maxHealth)
+ stop_banishment_check()
+ return
+
+ if(!devil.info.banish.check_banishment())
+ return
+
+ human.dust()
+
+/datum/element/devil_banishment/proc/stop_banishment_check()
+ if(!linked_timer)
+ return
+
+ deltimer(linked_timer)
+ linked_timer = null
diff --git a/code/datums/elements/devil_regen.dm b/code/datums/elements/devil_regen.dm
new file mode 100644
index 00000000000..1d5de2d95bb
--- /dev/null
+++ b/code/datums/elements/devil_regen.dm
@@ -0,0 +1,113 @@
+/datum/element/devil_regeneration
+ element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
+ id_arg_index = 2
+
+ var/linked_timer
+ var/list/sounds = list('sound/magic/demon_consume.ogg', 'sound/effects/attackblob.ogg')
+
+/datum/element/devil_regeneration/Attach(datum/target)
+ . = ..()
+ var/mob/living/carbon/human = target
+
+ if(!istype(human) || !human.mind?.has_antag_datum(/datum/antagonist/devil))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(human, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(start_regen_bodypart))
+ RegisterSignal(human, COMSIG_LIVING_EARLY_DEATH, PROC_REF(pre_death))
+
+ var/obj/item/organ/internal/brain/brain = human.get_organ_slot(INTERNAL_ORGAN_BRAIN)
+ brain?.decoy_brain = TRUE
+
+/datum/element/devil_regeneration/Detach(datum/target)
+ . = ..()
+
+ UnregisterSignal(target, COMSIG_CARBON_LOSE_ORGAN)
+ UnregisterSignal(target, COMSIG_LIVING_EARLY_DEATH)
+
+ if(!iscarbon(target))
+ return
+
+ var/mob/living/carbon/carbon = target
+ var/obj/item/organ/internal/brain/brain = carbon.get_organ_slot(INTERNAL_ORGAN_BRAIN)
+
+ brain?.decoy_brain = FALSE
+
+/datum/element/devil_regeneration/proc/start_regen_bodypart(datum/source, obj/item/organ/organ)
+ SIGNAL_HANDLER
+
+ var/obj/item/organ/external/external = organ
+ if(!istype(external))
+ return
+
+ var/mob/living/carbon/human = source
+ var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(!devil)
+ return
+
+ addtimer(CALLBACK(src, PROC_REF(regen_bodypart), human, external, devil), devil.rank.regen_threshold)
+
+/datum/element/devil_regeneration/proc/regen_bodypart(
+ mob/living/carbon/human,
+ obj/item/organ/external/external,
+ datum/antagonist/devil/devil
+ )
+ external = new external(human)
+ human.heal_overall_damage(devil.rank.regen_amount, devil.rank.regen_amount)
+
+ playsound(get_turf(human), pick(sounds), 50, 0, TRUE)
+ update_status(human)
+
+/datum/element/devil_regeneration/proc/pre_death(datum/source, gibbed)
+ SIGNAL_HANDLER
+
+ if(gibbed || linked_timer)
+ return
+
+ var/mob/living/carbon/human = source
+ var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(!devil)
+ return
+
+ to_chat(human, span_revenbignotice("Сверхъестественные силы предотвращают вашу смерть."))
+ playsound(get_turf(human), 'sound/magic/vampire_anabiosis.ogg', 50, 0, TRUE)
+
+ linked_timer = addtimer(CALLBACK(src, PROC_REF(apply_regeneration), human, devil), devil.rank.regen_threshold, TIMER_LOOP | TIMER_STOPPABLE | TIMER_DELETE_ME)
+
+/datum/element/devil_regeneration/proc/on_revive()
+ if(!linked_timer)
+ return
+
+ deltimer(linked_timer)
+ linked_timer = null
+
+/datum/element/devil_regeneration/proc/apply_regeneration(mob/living/carbon/human, datum/antagonist/devil/devil)
+ if(human.health >= human.maxHealth)
+ on_revive()
+
+ human.setOxyLoss(0)
+ human.heal_damages(
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ devil.rank.regen_amount,
+ TRUE,
+ TRUE
+ )
+
+ if(ishuman(human))
+ var/mob/living/carbon/human/mob = human
+ mob.check_and_regenerate_organs()
+
+ playsound(get_turf(human), pick(sounds), 50, 0, TRUE)
+ update_status(human)
+
+/datum/element/devil_regeneration/proc/update_status(mob/living/carbon/human)
+ human.updatehealth()
+ human.UpdateDamageIcon()
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 40dd4d2881b..2b24f9b19df 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -62,7 +62,6 @@
var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state
var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD
var/datum/mindslaves/som //stands for slave or master...hush..
- var/datum/devilinfo/devilinfo //Information about the devil, if any.
var/damnation_type = 0
var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src
var/hasSoul = TRUE
@@ -504,16 +503,15 @@
/datum/mind/proc/memory_edit_devil(mob/living/H)
. = _memory_edit_header("devil", list("devilagents"))
if(src in SSticker.mode.devils)
+ var/datum/antagonist/devil/devilinfo = has_antag_datum(/datum/antagonist/devil)
if(!devilinfo)
. += "No devilinfo found! Yell at a coder!"
- else if(!devilinfo.ascendable)
- . += "DEVIL|Ascendable Devil|sintouched|no"
else
- . += "DEVIL|ASCENDABLE DEVIL|sintouched|no"
+ . += "DEVIL|sintouched|no"
else if(src in SSticker.mode.sintouched)
- . += "devil|Ascendable Devil|SINTOUCHED|no"
+ . += "devil|SINTOUCHED|no"
else
- . += "devil|Ascendable Devil|sintouched|NO"
+ . += "devil|sintouched|NO"
. += _memory_edit_role_enabled(ROLE_DEVIL)
@@ -1906,47 +1904,24 @@
if("clear")
if(src in SSticker.mode.devils)
log_admin("[key_name(usr)] has de-devil'ed [current].")
+
else if(src in SSticker.mode.sintouched)
message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].")
log_admin("[key_name(usr)] has de-sintouch'ed [current].")
+
remove_devil_role()
if("devil")
- if(devilinfo)
- devilinfo.ascendable = FALSE
- message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.")
- log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.")
- return
- if(!ishuman(current) && !isrobot(current))
- to_chat(usr, "This only works on humans and cyborgs!")
- return
- SSticker.mode.devils += src
- special_role = "devil"
- SSticker.mode.update_devil_icons_added(src)
- SSticker.mode.finalize_devil(src, FALSE)
- SSticker.mode.forge_devil_objectives(src, 2)
- SSticker.mode.greet_devil(src)
+ if(has_antag_datum(/datum/antagonist/devil))
+ return
+
+ add_antag_datum(/datum/antagonist/devil)
message_admins("[key_name_admin(usr)] has devil'ed [current].")
log_admin("[key_name(usr)] has devil'ed [current].")
- if("ascendable_devil")
- if(devilinfo)
- devilinfo.ascendable = TRUE
- message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.")
- log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.")
- return
- if(!ishuman(current) && !isrobot(current))
- to_chat(usr, "This only works on humans and cyborgs!")
- return
- SSticker.mode.devils += src
- special_role = "devil"
- SSticker.mode.update_devil_icons_added(src)
- SSticker.mode.finalize_devil(src, TRUE)
- SSticker.mode.forge_devil_objectives(src, 2)
- SSticker.mode.greet_devil(src)
- message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.")
- log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.")
if("sintouched")
- var/mob/living/carbon/human/H = current
- H.influenceSin()
+ if(has_antag_datum(/datum/antagonist/sintouched))
+ return
+
+ add_antag_datum(/datum/antagonist/sintouched)
message_admins("[key_name_admin(usr)] has sintouch'ed [current].")
log_admin("[key_name(usr)] has sintouch'ed [current].")
@@ -2703,27 +2678,10 @@
/datum/mind/proc/remove_devil_role()
if(src in SSticker.mode.devils)
- if(istype(current,/mob/living/carbon/true_devil/))
- else
- SSticker.mode.devils -= src
- SSticker.mode.update_devil_icons_removed(src)
- special_role = null
- RemoveSpell(/obj/effect/proc_holder/spell/infernal_jaunt)
- RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish)
- RemoveSpell(/obj/effect/proc_holder/spell/summon_contract)
- RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork)
- RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork/greater)
- RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork/ascended)
- RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/violin)
- RemoveSpell(/obj/effect/proc_holder/spell/summon_dancefloor)
- RemoveSpell(/obj/effect/proc_holder/spell/sintouch)
- RemoveSpell(/obj/effect/proc_holder/spell/sintouch/ascended)
- if(issilicon(current))
- var/mob/living/silicon/S = current
- S.laws.clear_sixsixsix_laws()
- devilinfo = null
+ remove_antag_datum(/datum/antagonist/devil)
+
else if(src in SSticker.mode.sintouched)
- SSticker.mode.sintouched -= src
+ remove_antag_datum(/datum/antagonist/sintouched)
/datum/mind/proc/remove_contractor_role()
diff --git a/code/datums/rituals.dm b/code/datums/rituals.dm
index 592bfdde4a9..954fdda83e5 100644
--- a/code/datums/rituals.dm
+++ b/code/datums/rituals.dm
@@ -75,7 +75,7 @@
if(NONE)
failed = TRUE
- if(start_cooldown)
+ if(start_cooldown && cooldown_after_cast)
COOLDOWN_START(src, ritual_cooldown, cooldown_after_cast)
if(cause_disaster && prob(disaster_prob))
diff --git a/code/datums/spell_handler/devil_spell_handler.dm b/code/datums/spell_handler/devil_spell_handler.dm
new file mode 100644
index 00000000000..303d661e959
--- /dev/null
+++ b/code/datums/spell_handler/devil_spell_handler.dm
@@ -0,0 +1,10 @@
+/datum/spell_handler/devil
+
+/datum/spell_handler/devil/can_cast(mob/living/carbon/user, charge_check, show_message, obj/effect/proc_holder/spell/spell)
+ if(!istype(user))
+ return FALSE
+
+ if(!user.mind?.has_antag_datum(/datum/antagonist/devil))
+ return FALSE
+
+ return TRUE
diff --git a/code/datums/spells/devil.dm b/code/datums/spells/devil.dm
index d2a513dd5a7..1df5e792bf1 100644
--- a/code/datums/spells/devil.dm
+++ b/code/datums/spells/devil.dm
@@ -1,9 +1,12 @@
/obj/effect/proc_holder/spell/conjure_item/pitchfork
name = "Summon Pitchfork"
desc = "A devil's weapon of choice. Use this to summon/unsummon your pitchfork."
+
item_type = /obj/item/twohanded/pitchfork/demonic
+
action_icon_state = "pitchfork"
action_background_icon_state = "bg_demon"
+
human_req = FALSE
/obj/effect/proc_holder/spell/conjure_item/pitchfork/greater
@@ -15,27 +18,35 @@
/obj/effect/proc_holder/spell/conjure_item/violin
- item_type = /obj/item/instrument/violin/golden
+ name = "Summon golden violin"
desc = "A devil's instrument of choice. Use this to summon/unsummon your golden violin."
+
+ item_type = /obj/item/instrument/violin/golden
+
invocation_type = "whisper"
invocation = "I ain't have this much fun since Georgia."
+
action_icon_state = "golden_violin"
- name = "Summon golden violin"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/summon_contract
name = "Summon infernal contract"
desc = "Skip making a contract by hand, just do it by magic."
+
invocation_type = "whisper"
invocation = "Just sign on the dotted line."
- selection_activated_message = "You prepare a detailed contract. Click on a target to summon the contract in his hands."
- selection_deactivated_message = "You archive the contract for later use."
+
+ selection_activated_message = span_notice("You prepare a detailed contract. Click on a target to summon the contract in his hands.")
+ selection_deactivated_message = span_notice("You archive the contract for later use.")
+
clothes_req = FALSE
human_req = FALSE
+
school = "conjuration"
base_cooldown = 15 SECONDS
cooldown_min = 1 SECONDS
+
action_icon_state = "spell_default"
action_background_icon_state = "bg_demon"
need_active_overlay = TRUE
@@ -57,41 +68,50 @@
/obj/effect/proc_holder/spell/summon_contract/cast(list/targets, mob/user = usr)
for(var/target in targets)
var/mob/living/carbon/C = target
- if(C.mind && user.mind)
- if(C.stat == DEAD)
- if(user.drop_from_active_hand())
- var/obj/item/paper/contract/infernal/revive/contract = new(user.loc, C.mind, user.mind)
- user.put_in_hands(contract)
- else
- var/obj/item/paper/contract/infernal/contract
- var/contractTypeName = input(user, "What type of contract?") in list (CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_KNOWLEDGE, CONTRACT_FRIENDSHIP) //TODO: Refactor this to be less boilerplate-y
- switch(contractTypeName)
- if(CONTRACT_POWER)
- contract = new /obj/item/paper/contract/infernal/power(C.loc, C.mind, user.mind)
- if(CONTRACT_WEALTH)
- contract = new /obj/item/paper/contract/infernal/wealth(C.loc, C.mind, user.mind)
- if(CONTRACT_PRESTIGE)
- contract = new /obj/item/paper/contract/infernal/prestige(C.loc, C.mind, user.mind)
- if(CONTRACT_MAGIC)
- contract = new /obj/item/paper/contract/infernal/magic(C.loc, C.mind, user.mind)
- if(CONTRACT_KNOWLEDGE)
- contract = new /obj/item/paper/contract/infernal/knowledge(C.loc, C.mind, user.mind)
- if(CONTRACT_FRIENDSHIP)
- contract = new /obj/item/paper/contract/infernal/friendship(C.loc, C.mind, user.mind)
- C.put_in_hands(contract)
+ if(!C.mind || !user.mind)
+ to_chat(user, span_notice("[C] seems to not be sentient. You are unable to summon a contract for them."))
+ continue
+
+ if(C.stat == DEAD)
+ if(!user.drop_from_active_hand())
+ continue
+
+ var/obj/item/paper/contract/infernal/revive/contract = new(user.loc, C.mind, user.mind)
+ user.put_in_hands(contract)
else
- to_chat(user,"[C] seems to not be sentient. You are unable to summon a contract for them.")
+ var/obj/item/paper/contract/infernal/contract
+ var/contractTypeName = input(user, "What type of contract?") in list (CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_KNOWLEDGE, CONTRACT_FRIENDSHIP) // no todo: contracts are deprecated and soon will be deleted
+
+ switch(contractTypeName)
+ if(CONTRACT_POWER)
+ contract = new /obj/item/paper/contract/infernal/power(C.loc, C.mind, user.mind)
+ if(CONTRACT_WEALTH)
+ contract = new /obj/item/paper/contract/infernal/wealth(C.loc, C.mind, user.mind)
+ if(CONTRACT_PRESTIGE)
+ contract = new /obj/item/paper/contract/infernal/prestige(C.loc, C.mind, user.mind)
+ if(CONTRACT_MAGIC)
+ contract = new /obj/item/paper/contract/infernal/magic(C.loc, C.mind, user.mind)
+ if(CONTRACT_KNOWLEDGE)
+ contract = new /obj/item/paper/contract/infernal/knowledge(C.loc, C.mind, user.mind)
+ if(CONTRACT_FRIENDSHIP)
+ contract = new /obj/item/paper/contract/infernal/friendship(C.loc, C.mind, user.mind)
+
+ C.put_in_hands(contract)
/obj/effect/proc_holder/spell/fireball/hellish
name = "Hellfire"
desc = "This spell launches hellfire at the target."
+
school = "evocation"
base_cooldown = 8 SECONDS
+
clothes_req = FALSE
human_req = FALSE
+
invocation = "Your very soul will catch fire!"
invocation_type = "shout"
+
fireball_type = /obj/item/projectile/magic/fireball/infernal
action_background_icon_state = "bg_demon"
@@ -99,88 +119,111 @@
/obj/effect/proc_holder/spell/infernal_jaunt
name = "Infernal Jaunt"
desc = "Use hellfire to phase out of existence."
+
base_cooldown = 20 SECONDS
cooldown_min = 0
+
overlay = null
+
action_icon_state = "jaunt"
action_background_icon_state = "bg_demon"
+
phase_allowed = TRUE
+
clothes_req = FALSE
human_req = FALSE
/obj/effect/proc_holder/spell/infernal_jaunt/create_new_targeting()
return new /datum/spell_targeting/self
+/obj/effect/proc_holder/spell/infernal_jaunt/can_cast(mob/living/user, charge_check, show_message)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!istype(user))
+ return FALSE
/obj/effect/proc_holder/spell/infernal_jaunt/cast(list/targets, mob/living/user = usr)
- if(istype(user))
- if(istype(user.loc, /obj/effect/dummy/slaughter))
- var/continuing = 0
- if(istype(get_area(user), /area/shuttle)) // Can always phase in in a shuttle.
- continuing = TRUE
- else
- for(var/mob/living/C in orange(2, get_turf(user.loc))) //Can also phase in when nearby a potential buyer.
- if (C.mind && C.mind.soulOwner == C.mind)
- continuing = TRUE
- break
- if(continuing)
- to_chat(user,"You are now phasing in.")
- if(do_after(user, 15 SECONDS, user, NONE))
- user.infernalphasein(src)
- else
- to_chat(user,"You can only re-appear near a potential signer or on a shuttle.")
- revert_cast()
- return ..()
+ if(istype(user.loc, /obj/effect/dummy/slaughter))
+ var/continuing = 0
+ if(istype(get_area(user), /area/shuttle)) // Can always phase in in a shuttle.
+ continuing = TRUE
else
- user.fakefire()
- to_chat(user,"You begin to phase back into sinful flames.")
+ for(var/mob/living/C in orange(2, get_turf(user.loc))) //Can also phase in when nearby a potential buyer.
+ if (C.mind && C.mind.soulOwner == C.mind)
+ continuing = TRUE
+ break
+ if(continuing)
+ to_chat(user, span_warning("You are now phasing in."))
if(do_after(user, 15 SECONDS, user, NONE))
- ADD_TRAIT(user, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
- user.infernalphaseout(src)
- else
- to_chat(user,"You must remain still while exiting.")
- user.ExtinguishMob()
- cooldown_handler.start_recharge()
- return
- revert_cast()
+ user.infernalphasein(src)
+ else
+ to_chat(user, span_warning("You can only re-appear near a potential signer or on a shuttle."))
+ revert_cast()
+ return ..()
+ else
+ user.fakefire()
+ to_chat(user, span_warning("You begin to phase back into sinful flames."))
+ if(do_after(user, 15 SECONDS, user, NONE))
+ ADD_TRAIT(user, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
+ user.infernalphaseout(src)
+ else
+ to_chat(user, span_warning("You must remain still while exiting."))
+ user.ExtinguishMob()
+
+ cooldown_handler.start_recharge()
+ return
/mob/living/proc/infernalphaseout(obj/effect/proc_holder/spell/infernal_jaunt/spell)
dust_animation()
- visible_message("[src] disappears in a flashfire!")
+
+ visible_message(span_warning("[src] disappears in a flashfire!"))
playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1)
+
var/obj/effect/dummy/slaughter/s_holder = new(loc)
+
ExtinguishMob()
forceMove(s_holder)
+
holder = s_holder
+
REMOVE_TRAIT(src, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(spell))
fakefireextinguish()
/mob/living/proc/infernalphasein(obj/effect/proc_holder/spell/infernal_jaunt/spell)
if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
- to_chat(src,"You're too busy to jaunt in.")
+ to_chat(src, span_warning("You're too busy to jaunt in."))
return FALSE
+
fakefire()
forceMove(get_turf(src))
- visible_message("[src] appears in a firey blaze!")
+
+ visible_message(span_warning("[src] appears in a firey blaze!"))
playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1)
- spawn(1.5 SECONDS)
- fakefireextinguish(TRUE)
+
+ addtimer(CALLBACK(src, PROC_REF(fakefireextinguish), TRUE), 1.5 SECONDS)
/obj/effect/proc_holder/spell/sintouch
name = "Sin Touch"
desc = "Subtly encourage someone to sin."
+
base_cooldown = 180 SECONDS
cooldown_min = 0
+
clothes_req = FALSE
human_req = FALSE
overlay = null
+
action_icon_state = "sintouch"
action_background_icon_state = "bg_demon"
+
invocation = "TASTE SIN AND INDULGE!!"
invocation_type = "shout"
+
var/max_targets = 3
@@ -191,25 +234,29 @@
/obj/effect/proc_holder/spell/sintouch/create_new_targeting()
- var/datum/spell_targeting/targeted/T = new()
- T.selection_type = SPELL_SELECTION_RANGE
- T.random_target = TRUE
- T.target_priority = SPELL_TARGET_RANDOM
- T.use_turf_of_user = TRUE
- T.range = 2
- T.max_targets = 3
- return T
+ var/datum/spell_targeting/targeted/targeting = new()
+
+ targeting.selection_type = SPELL_SELECTION_RANGE
+ targeting.random_target = TRUE
+ targeting.target_priority = SPELL_TARGET_RANDOM
+ targeting.use_turf_of_user = TRUE
+
+ targeting.range = 2
+ targeting.max_targets = 3
+
+ return targeting
/obj/effect/proc_holder/spell/sintouch/sintouch/cast(list/targets, mob/living/user = usr)
- for(var/mob/living/carbon/human/H in targets)
- if(!H.mind)
+ for(var/mob/living/carbon/human/human in targets)
+ if(!human.mind)
continue
- for(var/datum/objective/sintouched/A in H.mind.objectives)
+
+ if(human.mind.has_antag_datum(/datum/antagonist/sintouched))
continue
- H.influenceSin()
- H.Weaken(4 SECONDS)
+ human.mind.add_antag_datum(/datum/antagonist/sintouched)
+ human.Weaken(4 SECONDS)
/obj/effect/proc_holder/spell/summon_dancefloor
name = "Summon Dancefloor"
@@ -218,15 +265,13 @@
human_req = FALSE
school = "conjuration"
base_cooldown = 1 SECONDS
- cooldown_min = 5 SECONDS //5 seconds, so the smoke can't be spammed
+ cooldown_min = 5 SECONDS // 5 seconds, so the smoke can't be spammed
action_icon_state = "funk"
action_background_icon_state = "bg_demon"
var/list/dancefloor_turfs
var/list/dancefloor_turfs_types
var/dancefloor_exists = FALSE
-// var/datum/effect_system/smoke_spread/transparent/dancefloor_devil/smoke
-
/obj/effect/proc_holder/spell/summon_dancefloor/create_new_targeting()
return new /datum/spell_targeting/self
@@ -236,13 +281,6 @@
LAZYINITLIST(dancefloor_turfs)
LAZYINITLIST(dancefloor_turfs_types)
-/*
- if(!smoke)
- smoke = new()
- smoke.set_up(0, get_turf(user))
- smoke.start()
-*/
-
if(dancefloor_exists)
dancefloor_exists = FALSE
for(var/i in 1 to dancefloor_turfs.len)
@@ -252,12 +290,15 @@
var/list/funky_turfs = RANGE_TURFS(1, user)
for(var/turf/T in funky_turfs)
if(T.density)
- to_chat(user, "You're too close to a wall.")
+ to_chat(user, span_warning("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/t in funky_turfs)
var/turf/T = t
dancefloor_turfs[i] = T
@@ -265,12 +306,148 @@
T.ChangeTurf((i % 2 == 0) ? /turf/simulated/floor/light/colour_cycle/dancefloor_a : /turf/simulated/floor/light/colour_cycle/dancefloor_b)
i++
+/obj/effect/proc_holder/spell/aoe/devil_fire
+ name = "Devil fire"
+ desc = "Призывает огненные волны в радиусе заклинания."
+ action_icon_state = "explosion_old"
+
+ base_cooldown = 60 SECONDS
+ aoe_range = 10
+
+ clothes_req = FALSE
+ human_req = FALSE
+
+ var/fire_prob = 50
+ var/slow_time = 5 SECONDS
+
+/obj/effect/proc_holder/spell/aoe/devil_fire/create_new_targeting()
+ var/datum/spell_targeting/aoe/targeting = new()
+
+ targeting.range = aoe_range
+ targeting.allowed_type = /atom
+
+ return targeting
+
+/obj/effect/proc_holder/spell/aoe/devil_fire/cast(list/targets, mob/user = usr)
+ for(var/mob/living/living in targets)
+ living.Slowed(slow_time)
+
+ for(var/turf/turf in targets)
+ if(turf == get_turf(user))
+ continue
+
+ if(!prob(fire_prob))
+ continue
+
+ new /obj/effect/hotspot(turf)
+ turf.hotspot_expose(2000, 50, 1)
+
+ playsound(get_turf(user), 'sound/magic/blind.ogg', 50, TRUE)
+
+/obj/effect/proc_holder/spell/dark_conversion
+ name = "Dark conversion"
+ desc = "Превращает гуманоида в тенечеловека и искажает его восприятие реальности."
+
+ action_icon = 'icons/mob/actions/actions_cult.dmi'
+ action_icon_state = "horror"
+
+ base_cooldown = 300 SECONDS
+ var/cast_time = 5 SECONDS
+
+ clothes_req = FALSE
+ human_req = FALSE
+
+/obj/effect/proc_holder/spell/dark_conversion/create_new_targeting()
+ var/datum/spell_targeting/aoe/targeting = new()
+
+ targeting.range = 5
+ targeting.allowed_type = /mob/living/carbon/human
+
+ return targeting
+
+/obj/effect/proc_holder/spell/dark_conversion/create_new_handler()
+ var/datum/spell_handler/devil/devil = new
+ return devil
+
+/obj/effect/proc_holder/spell/dark_conversion/valid_target(mob/living/carbon/human/target, mob/user)
+ return target.mind && !isshadowperson(target)
+
+/obj/effect/proc_holder/spell/dark_conversion/cast(list/targets, mob/user = usr)
+ var/mob/living/carbon/human/human = targets[1]
+ var/mob/living/carbon/carbon = user
+ var/datum/antagonist/devil/devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ carbon.say("INF' [devil.info.truename] NO")
+ playsound(get_turf(carbon), 'sound/magic/narsie_attack.ogg', 100, TRUE)
+
+ human.Knockdown(0.1 SECONDS)
+
+ if(!do_after(user, cast_time, user, NONE))
+ revert_cast(user)
+ return
+
+ make_shadow(human)
+
+/obj/effect/proc_holder/spell/dark_conversion/proc/make_shadow(mob/living/carbon/human/human)
+ human.set_species(/datum/species/shadow)
+ human.store_memory("Вы - создание тьмы. Старайтесь сохранить свою истинную форму и выполнить свои задания.", TRUE)
+
+ var/datum/objective/assassinate/kill = new
+ kill.owner = human.mind
+ kill.find_target()
+
+ LAZYADD(human.mind.objectives, kill)
+ LAZYADD(human.faction, "hell")
+
+ human.mind.prepare_announce_objectives()
+ playsound(human, 'sound/magic/mutate.ogg', 100, TRUE)
+
+/obj/effect/proc_holder/spell/sacrifice_circle
+ name = "Create sacrifice circle"
+ desc = "Создает руну для жертвоприношений."
+
+ action_icon = 'icons/mob/actions/actions_cult.dmi'
+ action_icon_state = "sintouch"
+
+ base_cooldown = 900 SECONDS
+ var/cast_time = 5 SECONDS
+
+ clothes_req = FALSE
+ human_req = FALSE
+
+/obj/effect/proc_holder/spell/sacrifice_circle/create_new_targeting()
+ return new /datum/spell_targeting/self
+
+/obj/effect/proc_holder/spell/sacrifice_circle/create_new_handler()
+ var/datum/spell_handler/devil/devil = new
+ return devil
+
+/obj/effect/proc_holder/spell/sacrifice_circle/cast(list/targets, mob/user = usr)
+ playsound(get_turf(user), 'sound/magic/cult_spell.ogg', 100, TRUE)
+
+ if(!do_after(user, cast_time, user, NONE))
+ revert_cast(user)
+ return
+
+ create_rune(user)
+
+/obj/effect/proc_holder/spell/sacrifice_circle/proc/create_rune(mob/user)
+ var/mob/living/carbon/carbon = user
+ var/datum/antagonist/devil/devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(!devil)
+ return
+
+ var/obj/effect/decal/cleanable/devil/devil_rune = new(get_turf(carbon))
+ playsound(get_turf(carbon), 'sound/magic/invoke_general.ogg', 100, TRUE)
-/*
-/datum/effect_system/smoke_spread/transparent/dancefloor_devil
- effect_type = /obj/effect/particle_effect/smoke/transparent/dancefloor_devil
+ devil_rune.AddComponent( \
+ /datum/component/ritual_object, \
+ allowed_categories = /datum/ritual/devil, \
+ allowed_special_role = list(ROLE_DEVIL), \
+ )
+ devil_rune.devil = devil
+ devil_rune.update_appearance(UPDATE_DESC)
-/obj/effect/particle_effect/smoke/transparent/dancefloor_devil
- lifetime = 2
-*/
+ return
diff --git a/code/game/gamemodes/devil/devil.dm b/code/game/gamemodes/devil/devil.dm
deleted file mode 100644
index d6927753ed9..00000000000
--- a/code/game/gamemodes/devil/devil.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-GLOBAL_LIST_INIT(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/psych = 1,
- /obj/item/clothing/under/rank/orderly = 1,
- /obj/item/clothing/under/rank/security/brigphys = 1,
- /obj/item/clothing/under/rank/internalaffairs = 1,
- /obj/item/clothing/under/rank/ntrep = 1,
- /obj/item/clothing/under/det = 1,
- /obj/item/clothing/under/wedding/bride_white = 1,
- /obj/item/clothing/under/mafia/white = 1,
- /obj/item/clothing/under/noble_clothes = 1,
- /obj/item/clothing/under/sl_suit = 1,
- /obj/item/clothing/under/burial = 1
-))
-
-
-
-/mob/living/proc/check_devil_bane_multiplier(obj/item/weapon, mob/living/attacker)
- switch(mind.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
- if(GLOB.whiteness[U.type])
- src.visible_message("[src] seems to have been harmed by the purity of [attacker]'s clothes.", "Unsullied white clothing is disrupting your form.")
- return GLOB.whiteness[U.type] + 1
- if(BANE_TOOLBOX)
- if(istype(weapon,/obj/item/storage/toolbox))
- src.visible_message("The [weapon] seems unusually robust this time.", "The [weapon] is your unmaking!")
- return 2.5 // Will take four hits with a normal toolbox.
- if(BANE_HARVEST)
- if(istype(weapon,/obj/item/reagent_containers/food/snacks/grown/) || istype(weapon,/obj/item/grown))
- src.visible_message("The spirits of the harvest aid in the exorcism.", "The harvest spirits are harming you.")
- src.Weaken(4 SECONDS)
- qdel(weapon)
- return 2
- return 1
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 e9a1d998a7a..00000000000
--- a/code/game/gamemodes/devil/devil_agent/devil_agent.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/datum/game_mode/devil/devil_agents
- name = "Devil Agents"
- config_tag = "devilagents"
- required_players = 25
- required_enemies = 3
- recommended_enemies = 8
-
- traitors_possible = 10 //hard limit on traitors if scaling is turned off
- num_modifier = 4
- objective_count = 2
-
- var/list/target_list = list()
- var/list/late_joining_list = list()
- minimum_devils = 3
-
-/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
- target_list[devil] = devils[i + 1]
- ..()
-
-/datum/game_mode/devil/devil_agents/forge_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(target_list.len && target_list[devil]) // Is a double agent
- var/datum/mind/target_mind = target_list[devil]
- var/datum/objective/devil/outsell/outsellobjective = new
- outsellobjective.owner = devil
- outsellobjective.target = target_mind
- outsellobjective.update_explanation_text()
- devil.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 563bac3e3e0..00000000000
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ /dev/null
@@ -1,66 +0,0 @@
-/datum/game_mode/devil
- name = "devil"
- config_tag = "devil"
- protected_jobs = list(JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_PILOT, JOB_TITLE_JUDGE,
- JOB_TITLE_LAWYER, JOB_TITLE_LIBRARIAN, JOB_TITLE_CHAPLAIN, JOB_TITLE_HOS, JOB_TITLE_CAPTAIN, JOB_TITLE_BRIGDOC,
- JOB_TITLE_CCOFFICER, JOB_TITLE_CCSPECOPS, JOB_TITLE_AI, JOB_TITLE_CYBORG, JOB_TITLE_CCFIELD, JOB_TITLE_CCSUPREME)
- required_players = 2
- required_enemies = 1
- recommended_enemies = 4
-
- 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
-// var/devil_scale_coefficient = 10
-
-/datum/game_mode/devil/announce()
- to_chat(world, {"The current game mode is - Devil!
)
- Devils: Purchase souls and tempt the crew to sin!
- 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
-
- var/list/possible_devils = get_players_for_role(ROLE_DEVIL)
- var/num_devils = 0
- if(!possible_devils.len)
- return 0
- if(CONFIG_GET(number/traitor_scaling))
- num_devils = max(1, round((num_players())/(CONFIG_GET(number/traitor_scaling)))+1)
- else
- num_devils = max(1, min(num_players(), traitors_possible))
- add_game_logs("Number of devils chosen: [num_devils]")
-
- for(var/j = 0, j < num_devils, j++)
- if (!possible_devils.len)
- break
- var/datum/mind/devil = pick(possible_devils)
- devils += devil
- devil.special_role = ROLE_DEVIL
- devil.restricted_roles = restricted_jobs
-
- add_game_logs("[devil.key] has been selected as a [config_tag]", devil)
- possible_devils.Remove(devil)
-
- if(devils.len < required_enemies)
- return 0
- return 1
-
-
-/datum/game_mode/devil/post_setup()
- for(var/datum/mind/devil in devils)
- spawn(rand(10, 100))
- finalize_devil(devil, TRUE)
- spawn(100)
- forge_devil_objectives(devil, objective_count) //This has to be in a separate loop, as we need devil names to be generated before we give objectives in devil agent.
- devil.devilinfo.announce_laws(devil.current)
- var/obj_count = 1
- to_chat(devil.current, " Your current objectives:")
- for(var/datum/objective/objective in devil.objectives)
- to_chat(devil.current, "Objective #[obj_count]: [objective.explanation_text]")
- obj_count++
- ..()
- return 1
diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm
deleted file mode 100644
index 5c00d4b57a9..00000000000
--- a/code/game/gamemodes/devil/devilinfo.dm
+++ /dev/null
@@ -1,560 +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 600
-
-GLOBAL_LIST_EMPTY(allDevils)
-GLOBAL_LIST_INIT(lawlorify, list (
- LORE = list(
- OBLIGATION_FOOD = "This devil seems to always offer it's 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 anointed 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_STRIKEUNCONCIOUS = "This devil only shows interest in those who are awake.",
- BAN_HURTLIZARD = "This devil will not strike an Unathi first.",
- BAN_HURTANIMAL = "This devil avoids hurting animals.",
- BANISH_WATER = "To banish the devil, you must infuse it's body with holy water.",
- BANISH_COFFIN = "This devil will return to life if it's remains are not placed within a coffin.",
- BANISH_FORMALDYHIDE = "To banish the devil, you must inject it's lifeless body with embalming fluid.",
- BANISH_RUNES = "This devil will resurrect after death, unless it's remains are within a rune.",
- BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
- BANISH_DESTRUCTION = "It's 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_STRIKEUNCONCIOUS = "You must never strike an unconscious person.",
- BAN_HURTLIZARD = "You must never harm an Unathi 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 it's 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."
- )
- ))
-
-/datum/devilinfo
- var/datum/mind/owner = null
- var/obligation
- var/ban
- var/bane
- var/banish
- var/truename
- var/list/datum/mind/soulsOwned = new
- var/datum/dna/humanform = null
- var/reviveNumber = 0
- var/form = BASIC_DEVIL
- var/exists = 0
- var/static/list/dont_remove_spells = list(
- /obj/effect/proc_holder/spell/summon_contract,
- /obj/effect/proc_holder/spell/conjure_item/violin,
- /obj/effect/proc_holder/spell/summon_dancefloor)
- var/ascendable = FALSE
-
-/datum/devilinfo/New()
- ..()
- dont_remove_spells = typecacheof(dont_remove_spells)
-
-/proc/randomDevilInfo(name = randomDevilName())
- var/datum/devilinfo/devil = new
- devil.truename = name
- devil.bane = randomdevilbane()
- devil.obligation = randomdevilobligation()
- devil.ban = randomdevilban()
- devil.banish = randomdevilbanish()
- return devil
-
-/proc/devilInfo(name, saveDetails = 0)
- if(GLOB.allDevils[lowertext(name)])
- return GLOB.allDevils[lowertext(name)]
- else
- var/datum/devilinfo/devil = randomDevilInfo(name)
- GLOB.allDevils[lowertext(name)] = devil
- devil.exists = saveDetails
- return devil
-
-
-
-/proc/randomDevilName()
- var/preTitle = ""
- var/title = ""
- var/mainName = ""
- var/suffix = ""
- if(prob(65))
- if(prob(35))
- preTitle = pick("Dark ", "Hellish ", "Fiery ", "Sinful ", "Blood ")
- title = pick("Lord ", "Fallen Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept ")
- var/probability = 100
- mainName = pick("Hal", "Ve", "Odr", "Neit", "Ci", "Quon", "Mya", "Folth", "Wren", "Gyer", "Geyr", "Hil", "Niet", "Twou", "Hu", "Don")
- while(prob(probability))
- mainName += pick("hal", "ve", "odr", "neit", "ca", "quon", "mya", "folth", "wren", "gyer", "geyr", "hil", "niet", "twoe", "phi", "coa")
- probability -= 20
- if(prob(40))
- suffix = pick(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.")
- return preTitle + title + mainName + suffix
-
-/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_STRIKEUNCONCIOUS, 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/devilinfo/proc/link_with_mob(mob/living/L)
- if(ishuman(L))
- var/mob/living/carbon/human/H = L
- humanform = H.dna.Clone()
- owner = L.mind
- give_base_spells(1)
-
-/datum/devilinfo/proc/add_soul(datum/mind/soul)
- if(soulsOwned.Find(soul))
- return
- soulsOwned += soul
- owner.current.set_nutrition(NUTRITION_LEVEL_FULL)
- to_chat(owner.current, "You feel satiated as you received a new soul.")
- update_hud()
- switch(SOULVALUE)
- if(0)
- to_chat(owner.current, "Your hellish powers have been restored.")
- give_base_spells()
- if(BLOOD_THRESHOLD)
- to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.")
- sleep(50)
- increase_blood_lizard()
- if(TRUE_THRESHOLD)
- to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.")
- sleep(50)
- increase_true_devil()
- if(ARCH_THRESHOLD)
- arch_devil_prelude()
- increase_arch_devil()
-
-/datum/devilinfo/proc/remove_soul(datum/mind/soul)
- if(soulsOwned.Remove(soul))
- to_chat(owner.current, "You feel as though a soul has slipped from your grasp.")
- check_regression()
- update_hud()
-
-/datum/devilinfo/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)
- remove_spells()
- to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.")
-
-/datum/devilinfo/proc/regress_humanoid()
- to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.")
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
- if(humanform)
- H.set_species(humanform.species)
- H.dna = humanform.Clone()
- H.sync_organ_dna(assimilate = 0)
- else
- H.set_species(/datum/species/human)
- // TODO: Add some appearance randomization here or something
- humanform = H.dna.Clone()
- H.regenerate_icons()
- else
- owner.current.color = ""
- give_base_spells()
- if(istype(owner.current.loc, /obj/effect/dummy/slaughter))
- owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted.
- form = BASIC_DEVIL
-
-/datum/devilinfo/proc/regress_blood_lizard()
- var/mob/living/carbon/true_devil/devil = owner.current
- to_chat(devil, span_warning("Your powers weaken, have more contracts be signed to regain power."))
- devil.oldform.loc = devil.loc
- owner.transfer_to(devil.oldform)
- REMOVE_TRAIT(devil.oldform, TRAIT_GODMODE, UNIQUE_TRAIT_SOURCE(src))
- give_lizard_spells()
- qdel(devil)
- form = BLOOD_LIZARD
- update_hud()
-
-
-/datum/devilinfo/proc/increase_blood_lizard()
- if(ishuman(owner.current))
- var/mob/living/carbon/human/H = owner.current
- var/list/language_temp = LAZYLEN(H.languages) ? H.languages.Copy() : null
- H.set_species(/datum/species/unathi)
- if(language_temp)
- H.languages = language_temp
- H.underwear = "Nude"
- H.undershirt = "Nude"
- H.socks = "Nude"
- H.change_skin_color(80, 16, 16) //A deep red
- H.regenerate_icons()
- else //Did the devil get hit by a staff of transmutation?
- owner.current.color = "#501010"
- give_lizard_spells()
- form = BLOOD_LIZARD
-
-
-
-/datum/devilinfo/proc/increase_true_devil()
- var/mob/living/carbon/true_devil/ascended = new /mob/living/carbon/true_devil(owner.current.loc, owner.current)
- ascended.faction |= "hell"
- // Put the old body in stasis
- ADD_TRAIT(owner.current, TRAIT_GODMODE, UNIQUE_TRAIT_SOURCE(src))
- owner.current.loc = ascended
- ascended.oldform = owner.current
- owner.transfer_to(ascended)
- ascended.set_name()
- give_true_spells()
- form = TRUE_DEVIL
- update_hud()
-
-/datum/devilinfo/proc/arch_devil_prelude()
- if(!ascendable)
- return
- var/mob/living/carbon/true_devil/D = owner.current
- to_chat(D, "You feel as though your form is about to ascend.")
- sleep(50)
- if(!D)
- return
- D.visible_message("[D]'s skin begins to erupt with spikes.", \
- "Your flesh begins creating a shield around yourself.")
- sleep(100)
- if(!D)
- return
- D.visible_message("The horns on [D]'s head slowly grow and elongate.", \
- "Your body continues to mutate. Your telepathic abilities grow.")
- sleep(90)
- if(!D)
- return
- D.visible_message("[D]'s body begins to violently stretch and contort.", \
- "You begin to rend apart the final barriers to ultimate power.")
- sleep(40)
- if(!D)
- return
- to_chat(D, "Yes!")
- sleep(10)
- if(!D)
- return
- to_chat(D, "YES!!")
- sleep(10)
- if(!D)
- return
- to_chat(D, "YE--")
- sleep(1)
- if(!D)
- return
- to_chat(world, "SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!")
- world << 'sound/hallucinations/veryfar_noise.ogg'
- sleep(50)
- if(!SSticker.mode.devil_ascended)
- SSshuttle.emergency.request(null, 0.3)
- SSticker.mode.devil_ascended++
-
-/datum/devilinfo/proc/increase_arch_devil()
- if(!ascendable)
- return
- var/mob/living/carbon/true_devil/D = owner.current
- if(!istype(D))
- return
- give_arch_spells()
- D.convert_to_archdevil()
- if(istype(D.loc, /obj/effect/dummy/slaughter))
- D.forceMove(get_turf(D))
- var/area/A = get_area(owner.current)
- if(A)
- notify_ghosts("An arch devil has ascended in [A.name]. Reach out to the devil to start climbing the infernal corporate ladder.", title = "Arch Devil Ascended", source = owner.current, action = NOTIFY_ATTACK)
- form = ARCH_DEVIL
-
-/datum/devilinfo/proc/remove_spells()
- for(var/obj/effect/proc_holder/spell/spell as anything in owner.spell_list)
- if(!is_type_in_typecache(spell, dont_remove_spells))
- owner.RemoveSpell(spell)
-
-/datum/devilinfo/proc/give_summon_contract()
- owner.AddSpell(new /obj/effect/proc_holder/spell/summon_contract(null))
-
-
-/datum/devilinfo/proc/give_base_spells(give_summon_contract = 0)
- remove_spells()
- owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork(null))
- if(give_summon_contract)
- give_summon_contract()
- if(obligation == OBLIGATION_FIDDLE)
- owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/violin(null))
- if(obligation == OBLIGATION_DANCEOFF)
- owner.AddSpell(new /obj/effect/proc_holder/spell/summon_dancefloor(null))
-
-/datum/devilinfo/proc/give_lizard_spells()
- remove_spells()
- owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/infernal_jaunt(null))
-
-/datum/devilinfo/proc/give_true_spells()
- remove_spells()
- owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork/greater(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/infernal_jaunt(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/sintouch(null))
-
-/datum/devilinfo/proc/give_arch_spells()
- remove_spells()
- owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork/ascended(null))
- owner.AddSpell(new /obj/effect/proc_holder/spell/sintouch/ascended(null))
-
-/datum/devilinfo/proc/beginResurrectionCheck(mob/living/body)
- if(owner.current != body)
- body = owner.current
- if(SOULVALUE > 0)
- to_chat(owner.current, "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.")
- addtimer(CALLBACK(src, "activateResurrection", body), DEVILRESURRECTTIME)
- else
- to_chat(owner.messageable_mob(), "Your hellish powers are too weak to resurrect yourself.")
-
-/datum/devilinfo/proc/activateResurrection(mob/living/body)
- if(QDELETED(body) || body.stat == DEAD)
- if(SOULVALUE > 0)
- if(check_banishment(body))
- to_chat(owner.messageable_mob(), "Unfortunately, the mortals have finished a ritual that prevents your resurrection.")
- return -1
- else
- to_chat(owner.messageable_mob(), "WE LIVE AGAIN!")
- return hellish_resurrection(body)
- else
- to_chat(owner.messageable_mob(), "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, "You seem to have resurrected without your hellish powers.")
-
-/datum/devilinfo/proc/check_banishment(mob/living/body)
- switch(banish)
- if(BANISH_WATER)
- if(!QDELETED(body) && iscarbon(body))
- var/mob/living/carbon/H = body
- return H.reagents.has_reagent("holy water")
- return 0
- if(BANISH_COFFIN)
- return (!QDELETED(body) && istype(body.loc, /obj/structure/closet/coffin))
- if(BANISH_FORMALDYHIDE)
- if(!QDELETED(body) && iscarbon(body))
- var/mob/living/carbon/H = body
- return H.reagents.has_reagent("formaldehyde")
- return 0
- if(BANISH_RUNES)
- if(!QDELETED(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(!QDELETED(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(!QDELETED(body))
- return 0
- return 1
- if(BANISH_FUNERAL_GARB)
- if(!QDELETED(body) && iscarbon(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/devilinfo/proc/hellish_resurrection(mob/living/body)
- message_admins("[owner.name] (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(!QDELETED(body))
- body.revive()
- if(!body.client)
- var/mob/dead/observer/O = owner.get_ghost()
- O.reenter_corpse()
- if(istype(body.loc, /obj/effect/dummy/slaughter))
- 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() // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body.
- if(body.stat == DEAD) // Not sure why this would happen
- create_new_body()
- else if(GLOB.blobstart.len > 0)
- // teleport the body so repeated beatdowns aren't an option)
- body.forceMove(get_turf(pick(GLOB.blobstart)))
- // give them the devil lawyer outfit in case they got stripped
- if(ishuman(body))
- var/mob/living/carbon/human/H = body
- H.equipOutfit(/datum/outfit/devil_lawyer)
- else
- create_new_body()
- check_regression()
-
-/datum/devilinfo/proc/create_new_body()
- if(GLOB.blobstart.len > 0)
- var/turf/targetturf = get_turf(pick(GLOB.blobstart))
- var/mob/currentMob = owner.current
- if(QDELETED(currentMob))
- currentMob = owner.get_ghost()
- if(!currentMob)
- message_admins("[owner.name]'s devil resurrection failed due to client logoff. Aborting.")
- return -1
- if(currentMob.mind != owner)
- message_admins("[owner.name]'s devil resurrection failed due to becoming a new mob. Aborting.")
- return -1
- var/mob/living/carbon/human/H = new /mob/living/carbon/human(targetturf)
- owner.transfer_to(H)
- if(isobserver(currentMob))
- var/mob/dead/observer/O = currentMob
- O.reenter_corpse()
- if(humanform)
- H.set_species(humanform.species)
- H.dna = humanform.Clone()
-
- H.dna.UpdateSE()
- H.dna.UpdateUI()
-
- H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it
- H.UpdateAppearance()
- else
- // gibbed cyborg or similar - create a randomized "humanform" appearance
- H.scramble_appearance()
- humanform = H.dna.Clone()
-
-
- H.equipOutfit(/datum/outfit/devil_lawyer)
- give_base_spells(TRUE)
- if(SOULVALUE >= BLOOD_THRESHOLD)
- increase_blood_lizard()
- if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
- increase_true_devil()
- if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
- increase_arch_devil()
- else
- throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
-
-/datum/devilinfo/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)
-
-// SECTION: Messages and explanations
-
-/datum/devilinfo/proc/announce_laws()
- var/list/messages = list()
- messages.Add("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.")
- messages.Add("However, your infernal form is not without weaknesses.")
- messages.Add("You may not use violence to coerce someone into selling their soul.")
- messages.Add("You may not directly and knowingly physically harm a devil, other than yourself.")
- messages.Add(GLOB.lawlorify[LAW][bane])
- messages.Add(GLOB.lawlorify[LAW][ban])
- messages.Add(GLOB.lawlorify[LAW][obligation])
- messages.Add(GLOB.lawlorify[LAW][banish])
- messages.Add("
Remember, the crew can research your weaknesses if they find out your devil name.
")
- return messages
-
-#undef BLOOD_THRESHOLD
-#undef TRUE_THRESHOLD
-#undef ARCH_THRESHOLD
-#undef BASIC_DEVIL
-#undef BLOOD_LIZARD
-#undef TRUE_DEVIL
-#undef ARCH_DEVIL
-#undef LOSS_PER_DEATH
-#undef SOULVALUE
-#undef DEVILRESURRECTTIME
-
-/datum/outfit/devil_lawyer
- name = "Devil Lawyer"
- uniform = /obj/item/clothing/under/lawyer/black
- shoes = /obj/item/clothing/shoes/laceup
- back = /obj/item/storage/backpack
- l_hand = /obj/item/storage/briefcase
- l_pocket = /obj/item/pen
- l_ear = /obj/item/radio/headset
-
- id = /obj/item/card/id
-
-/datum/outfit/devil_lawyer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
- var/obj/item/card/id/W = H.wear_id
- if(!istype(W) || W.assignment) // either doesn't have a card, or the card is already written to
- return
- var/name_to_use = H.real_name
- if(H.mind && H.mind.devilinfo)
- // Having hell create an ID for you causes its risks
- name_to_use = H.mind.devilinfo.truename
-
- W.name = "[name_to_use]'s ID Card (Lawyer)"
- W.registered_name = name_to_use
- W.assignment = "Lawyer"
- W.rank = W.assignment
- W.age = H.age
- W.sex = capitalize(H.gender)
- W.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS)
- W.photo = get_id_photo(H)
diff --git a/code/game/gamemodes/devil/game_mode.dm b/code/game/gamemodes/devil/game_mode.dm
index a6c785fbd56..fc38bd056c9 100644
--- a/code/game/gamemodes/devil/game_mode.dm
+++ b/code/game/gamemodes/devil/game_mode.dm
@@ -1,106 +1,3 @@
/datum/game_mode
var/list/datum/mind/sintouched = list()
var/list/datum/mind/devils = list()
- var/devil_ascended = 0 // Number of arch devils on station
-
-/datum/game_mode/proc/auto_declare_completion_sintouched()
- var/text = ""
- if(!length(sintouched))
- return
-
- text += "
The sintouched were:"
- var/list/sintouchedUnique = uniqueList(sintouched)
- for(var/S in sintouchedUnique)
- var/datum/mind/sintouched_mind = S
- text += printplayer(sintouched_mind)
- text += printobjectives(sintouched_mind)
- text += "
"
-
- text += "
"
-
- to_chat(world,text)
-
-/datum/game_mode/proc/auto_declare_completion_devils()
- var/text = ""
- if(!length(devils))
- return
-
- text += "
The devils were:"
- for(var/D in devils)
- var/datum/mind/devil = D
- text += printplayer(devil)
- text += printdevilinfo(devil)
- text += printobjectives(devil)
- text += "
"
-
- text += "
"
-
- to_chat(world,text)
-
-
-/datum/game_mode/proc/finalize_devil(datum/mind/devil_mind, ascendable = FALSE)
- var/trueName= randomDevilName()
- devil_mind.devilinfo = devilInfo(trueName, 1)
- devil_mind.devilinfo.ascendable = ascendable
- devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.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][devil_mind.devilinfo.bane]]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.banish]]
")
- devil_mind.devilinfo.link_with_mob(devil_mind.current)
- if(devil_mind.assigned_role == JOB_TITLE_CLOWN)
- to_chat(devil_mind.current, "Your infernal nature allows you to wield weapons without harming yourself.")
- devil_mind.current.force_gene_block(GLOB.clumsyblock, FALSE)
- // Don't give them another action if they already have one.
- if(!(locate(/datum/action/innate/toggle_clumsy) in devil_mind.current.actions))
- var/datum/action/innate/toggle_clumsy/toggle_clumsy = new
- toggle_clumsy.Grant(devil_mind.current)
-
- spawn(10)
- devil_mind.devilinfo.update_hud()
- if(issilicon(devil_mind.current))
- var/mob/living/silicon/S = devil_mind.current
- S.laws.set_sixsixsix_law("You may not use violence to coerce someone into selling their soul.")
- S.laws.set_sixsixsix_law("You may not directly and knowingly physically harm a devil, other than yourself.")
- S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]")
- S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]")
- S.laws.set_sixsixsix_law("Accomplish your objectives at all costs.")
-
-// unsure about the second "quantity" arg and how it fits with the antag refactor
-/datum/game_mode/proc/forge_devil_objectives(datum/mind/devil_mind, quantity)
- var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target)
- for(var/i = 1 to quantity)
- var/type = pick(validtypes)
- var/datum/objective/devil/objective = new type(null)
- objective.owner = devil_mind
- devil_mind.objectives += objective
- if(!istype(objective, /datum/objective/devil/buy_target))
- validtypes -= type //prevent duplicate objectives, EXCEPT for buy_target.
- else
- objective.find_target()
-
-/datum/game_mode/proc/greet_devil(datum/mind/devil_mind)
- if(!devil_mind.devilinfo)
- return
- var/list/messages = list()
- messages.Add(devil_mind.devilinfo.announce_laws())
- messages.Add(devil_mind.prepare_announce_objectives())
- to_chat(devil_mind.current, chat_box_red(messages.Join("
")))
-
-
-/datum/game_mode/proc/printdevilinfo(datum/mind/ply)
- if(!ply.devilinfo)
- return "Target is not a devil."
- var/text = "The devil's true name is: [ply.devilinfo.truename]"
- text += "The devil's bans were:"
- text += " [GLOB.lawlorify[LORE][ply.devilinfo.ban]]"
- text += " [GLOB.lawlorify[LORE][ply.devilinfo.bane]]"
- text += " [GLOB.lawlorify[LORE][ply.devilinfo.obligation]]"
- text += " [GLOB.lawlorify[LORE][ply.devilinfo.banish]]"
- return text
-
-/datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind)
- var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL]
- hud.join_hud(devil_mind.current)
- set_antag_hud(devil_mind.current, "huddevil")
-
-/datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind)
- var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL]
- hud.leave_hud(devil_mind.current)
- set_antag_hud(devil_mind.current, null)
diff --git a/code/game/gamemodes/devil/objectives.dm b/code/game/gamemodes/devil/objectives.dm
index 835a16c4593..655920bb6de 100644
--- a/code/game/gamemodes/devil/objectives.dm
+++ b/code/game/gamemodes/devil/objectives.dm
@@ -1,112 +1,87 @@
/datum/objective/devil
-/datum/objective/devil/soulquantity
+/datum/objective/devil/sacrifice
+ var/list/target_minds = list()
needs_target = FALSE
- explanation_text = "You shouldn't see this text. Error:DEVIL1"
- target_amount = 4
+ check_cryo = FALSE
+ target_amount = 12
+ explanation_text = ""
-/datum/objective/devil/soulquantity/New()
- target_amount = pick(6, 7, 8)
- update_explanation_text()
+/datum/objective/devil/sacrifice/proc/forge()
+ if(!get_targets())
+ return FALSE
-/datum/objective/devil/proc/update_explanation_text()
- //Intentionally empty
+ for(var/datum/mind/mind in target_minds)
+ explanation_text += "Принесите в жертву [mind.name], [mind.assigned_role].\n
"
-/datum/objective/devil/soulquantity/update_explanation_text()
- explanation_text = "Purchase, and retain control over at least [target_amount] souls."
+ return TRUE
-/datum/objective/devil/soulquantity/check_completion()
- var/count = 0
- for(var/S in owner.devilinfo.soulsOwned)
- var/datum/mind/L = S
- if(L.soulOwner == owner)
- count++
- return count >= target_amount
+/datum/objective/devil/sacrifice/proc/get_targets()
+ var/list/command_minds = list()
+ var/list/security_minds = list()
+ var/list/other_minds = list()
+ for(var/datum/mind/mind in SSticker.minds)
+ if(mind == owner)
+ continue
+ if(!ishuman(mind.current) \
+ || mind.current.stat == DEAD \
+ || mind.offstation_role)
+ continue
-/datum/objective/devil/soulquality
- needs_target = FALSE
- explanation_text = "You shouldn't see this text. Error:DEVIL2"
- var/contractType
- var/contractName
-
-/datum/objective/devil/soulquality/New()
- contractType = pick(CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_REVIVE, CONTRACT_KNOWLEDGE)
- target_amount = pick(1, 2)
- switch(contractType)
- if(CONTRACT_POWER)
- contractName = "for power"
- if(CONTRACT_WEALTH)
- contractName = "for wealth"
- if(CONTRACT_PRESTIGE)
- contractName = "for prestige"
- if(CONTRACT_MAGIC)
- contractName = "for magic"
- if(CONTRACT_REVIVE)
- contractName = "of revival"
- if(CONTRACT_KNOWLEDGE)
- contractName = "for knowledge"
- update_explanation_text()
-
-/datum/objective/devil/soulquality/update_explanation_text()
- explanation_text = "Have mortals sign at least [target_amount] contracts [contractName]."
-
-/datum/objective/devil/soulquality/check_completion()
- var/count = 0
- for(var/S in owner.devilinfo.soulsOwned)
- var/datum/mind/L = S
- if(L.soulOwner != L && L.damnation_type == contractType)
- count++
- return count >= target_amount
+ if(LAZYIN(GLOB.command_positions, mind.assigned_role))
+ LAZYADD(command_minds, mind)
+ else if(LAZYIN(GLOB.security_positions, mind.assigned_role))
+ LAZYADD(security_minds, mind)
+ else
+ LAZYADD(other_minds, mind)
-/datum/objective/devil/sintouch
- needs_target = FALSE
- explanation_text = "You shouldn't see this text. Error:DEVIL3"
+ var/command_target_count = ceil(target_amount / 12)
+ var/security_target_count = floor(target_amount / 4)
+ var/other_target_count = target_amount - command_target_count - security_target_count
-/datum/objective/devil/sintouch/New()
- target_amount = pick(4, 5)
- explanation_text = "Ensure at least [target_amount] mortals are sintouched."
+ if(LAZYLEN(command_minds) < command_target_count || LAZYLEN(security_minds) < security_target_count || LAZYLEN(other_minds) < other_target_count)
+ return FALSE
-/datum/objective/devil/sintouch/check_completion()
- return target_amount <= SSticker.mode.sintouched.len
+ for(var/i in 1 to command_target_count)
+ LAZYADD(target_minds, pick_n_take(command_minds))
+
+ for(var/i in 1 to security_target_count)
+ LAZYADD(target_minds, pick_n_take(security_minds))
+ for(var/i in 1 to other_target_count)
+ LAZYADD(target_minds, pick_n_take(other_minds))
+ return TRUE
-/datum/objective/devil/buy_target
- explanation_text = "You shouldn't see this text. Error:DEVIL4"
+/datum/objective/devil/sacrifice/check_completion()
+ var/list/collected_minds = list()
-/datum/objective/devil/buy_target/New()
- find_target()
- update_explanation_text()
+ for(var/datum/mind/mind as anything in target_minds)
+ if(mind.hasSoul)
+ continue
-/datum/objective/devil/buy_target/update_explanation_text()
- if(target)
- explanation_text = "Purchase and retain the soul of [target.name], the [target.assigned_role]."
- else
- explanation_text = "Free objective."
+ LAZYADD(collected_minds, mind)
-/datum/objective/devil/buy_target/check_completion()
- return target.soulOwner == owner
+ return LAZYLEN(collected_minds) > target_amount
+/datum/objective/devil/sintouch
+ needs_target = FALSE
+ explanation_text = "You shouldn't see this text. Error:DEVIL3"
+
+/datum/objective/devil/sintouch/New()
+ target_amount = pick(4, 5)
+ explanation_text = "Ensure at least [target_amount] mortals are sintouched."
-/datum/objective/devil/outsell
- explanation_text = "You shouldn't see this text. Error:DEVIL5"
+/datum/objective/devil/sintouch/check_completion()
+ return target_amount <= SSticker.mode.sintouched.len
-/datum/objective/devil/outsell/update_explanation_text()
- explanation_text = "Purchase and retain control over more souls than [target.devilinfo.truename], known to mortals as [target.name], the [target.assigned_role]."
+/datum/objective/devil/ascend
+ explanation_text = "Ascend to your true form."
+ needs_target = FALSE
-/datum/objective/devil/outsell/check_completion()
- var/selfcount = 0
- for(var/S in owner.devilinfo.soulsOwned)
- var/datum/mind/L = S
- if(L.soulOwner == owner)
- selfcount++
- var/targetcount = 0
- for(var/S in target.devilinfo.soulsOwned)
- var/datum/mind/L = S
- if(L.soulOwner == target)
- targetcount++
- return selfcount > targetcount
+/datum/objective/devil/ascend/check_completion()
+ return isdevil(owner)
diff --git a/code/game/gamemodes/devil/true_devil/inventory.dm b/code/game/gamemodes/devil/true_devil/inventory.dm
deleted file mode 100644
index a1ab62fb623..00000000000
--- a/code/game/gamemodes/devil/true_devil/inventory.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-// empty now
-
diff --git a/code/game/gamemodes/miniantags/sintouched/objectives.dm b/code/game/gamemodes/miniantags/sintouched/objectives.dm
index be5ebe4b8cd..5c4288bacd8 100644
--- a/code/game/gamemodes/miniantags/sintouched/objectives.dm
+++ b/code/game/gamemodes/miniantags/sintouched/objectives.dm
@@ -3,17 +3,6 @@
needs_target = FALSE
var/mob/living/carbon/human/user
-/* NO ERP OBJECTIVE FOR YOU.
-/datum/objective/sintouched/lust
- dangerrating = 3 // it's not AS dangerous.
-
-/datum/objective/sintouched/lust/New()
- var/mob/dead/D = pick(dead_mob_list)
- if(prob(50) && D)
- explanation_text = "You know that [D] has perished.... and you think [D] is kinda cute. Make sure everyone knows how HOT [D]'s lifeless body is."
- else
- explanation_text = "Go get married, then immediately cheat on your new spouse." */
-
/datum/objective/sintouched/proc/on_apply()
return
diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm
index a288263f7a4..c8952e664e5 100644
--- a/code/game/objects/effects/decals/Cleanable/misc.dm
+++ b/code/game/objects/effects/decals/Cleanable/misc.dm
@@ -286,3 +286,21 @@
/obj/effect/decal/cleanable/ashrune/is_cleanable()
return FALSE
+
+/obj/effect/decal/cleanable/devil
+ name = "Sinister rune"
+ desc = "Безобразно выглядящая руна, писанная кровью."
+
+ icon = 'icons/effects/crayondecal.dmi'
+ icon_state = "rune6"
+ color = "#661b1b"
+
+ anchored = TRUE
+ mergeable_decal = FALSE
+ mouse_opacity = MOUSE_OPACITY_ICON
+
+ var/datum/antagonist/devil/devil
+
+/obj/effect/decal/cleanable/devil/update_desc()
+ . = ..()
+ desc = "[initial(desc)][devil ? " На руне видна подпись: [devil.info?.truename]." : null]"
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 70577d1b582..12bb19865ed 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1639,30 +1639,35 @@
icon = 'icons/obj/library.dmi'
icon_state = "demonomicon"
w_class = WEIGHT_CLASS_SMALL
- var/cooldown = FALSE
+ COOLDOWN_DECLARE(cooldown)
/obj/item/toy/codex_gigas/attack_self(mob/user)
- if(!cooldown)
- user.visible_message(
- "[user] presses the button on \the [src].",
- "You press the button on \the [src].",
- "You hear a soft click.")
- var/list/messages = list()
- var/datum/devilinfo/devil = randomDevilInfo()
- 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]]"
- playsound(loc, 'sound/machines/click.ogg', 20, 1)
- cooldown = TRUE
- for(var/message in messages)
- user.loc.visible_message("[bicon(src)] [message]")
- sleep(10)
- spawn(20)
- cooldown = FALSE
+ if(!COOLDOWN_FINISHED(src, cooldown))
return
+ user.visible_message(
+ span_notice("[user] presses the button on \the [src]."), \
+ span_notice("You press the button on \the [src]."), \
+ span_sinister("You hear a soft click."))
+
+ var/list/messages = list()
+ var/datum/devilinfo/devil = new
+
+ LAZYADD(messages, "Some fun facts about: [devil.truename]")
+ LAZYADD(messages, devil.bane.law)
+ LAZYADD(messages, devil.ban.law)
+ LAZYADD(messages, devil.obligation.law)
+ LAZYADD(messages, devil.banish.law)
+
+ playsound(loc, 'sound/machines/click.ogg', 20, 1)
+ COOLDOWN_START(src, cooldown, 2 SECONDS)
+
+ for(var/message in messages)
+ user.loc.visible_message(span_danger("[bicon(src)] [message]"))
+ sleep(1 SECONDS)
+
+ return
+
/obj/item/toy/owl
name = "owl action figure"
desc = "An action figure modeled after 'The Owl', defender of justice."
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index dd6bf4beb0f..b2be8e3a924 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -861,9 +861,9 @@
. = ..()
if(isliving(user))
var/mob/living/U = user
- if(U.mind && !U.mind.devilinfo && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul
- U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \
- "\"As you pick up the [src] your arms ignite, reminding you of all your past sins.\"")
+ if(!U.mind?.has_antag_datum(/datum/antagonist/devil) && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul
+ U.visible_message(span_warning("As [U] picks [src] up, [U]'s arms briefly catch fire."), \
+ span_warning("\"As you pick up the [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))
@@ -876,7 +876,7 @@
if(!ATTACK_CHAIN_SUCCESS_CHECK(.) || !HAS_TRAIT(src, TRAIT_WIELDED))
return .
- if(!user.mind || user.mind.devilinfo || (user.mind.soulOwner == user.mind))
+ if(user.mind?.has_antag_datum(/datum/antagonist/devil) || (user.mind.soulOwner == user.mind))
return .
to_chat(user, span_warning("The [name] burns in your hands!"))
diff --git a/code/game/gamemodes/devil/contracts/friend.dm b/code/modules/antagonists/devil/contracts/friend.dm
similarity index 100%
rename from code/game/gamemodes/devil/contracts/friend.dm
rename to code/modules/antagonists/devil/contracts/friend.dm
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
new file mode 100644
index 00000000000..1bf5fefadb5
--- /dev/null
+++ b/code/modules/antagonists/devil/devil.dm
@@ -0,0 +1,205 @@
+/datum/antagonist/devil
+ name = "Devil"
+ roundend_category = "devils"
+ job_rank = ROLE_DEVIL
+ special_role = ROLE_DEVIL
+ antag_hud_type = ANTAG_HUD_DEVIL
+
+ var/datum/devilinfo/info = new
+ var/list/soulsOwned
+ var/datum/devil_rank/rank
+
+/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/datum/mind/tested = new_owner || owner
+ if(!tested || !iscarbon(tested.current))
+ return FALSE
+
+ return TRUE
+
+/datum/antagonist/devil/Destroy(force)
+ QDEL_NULL(rank)
+ QDEL_NULL(info)
+ LAZYNULL(soulsOwned)
+
+ return ..()
+
+/datum/antagonist/devil/proc/add_soul(datum/mind/soul)
+ if((!istype(soul)) || (LAZYIN(soulsOwned, soul)))
+ return
+
+ LAZYADD(soulsOwned, soul)
+ to_chat(owner.current, span_warning("Вы поглощаете душу и насыщаетесь ею."))
+
+ owner.current.set_nutrition(NUTRITION_LEVEL_FULL)
+ soul.hasSoul = FALSE
+
+ try_update_rank()
+ update_hud()
+
+/datum/antagonist/devil/proc/remove_soul(datum/mind/soul)
+ LAZYREMOVE(soulsOwned, soul)
+ to_chat(owner.current, span_warning("Вы чувствуете, как часть ваших сил угасает"))
+ update_hud()
+
+/datum/antagonist/devil/proc/try_update_rank()
+ if(!rank.required_souls || !rank.next_rank_type)
+ return FALSE
+
+ if(LAZYLEN(soulsOwned) < rank.required_souls)
+ return FALSE
+
+ if(!init_new_rank(rank.next_rank_type, TRUE))
+ return FALSE
+
+ return TRUE // rank updated.
+
+/datum/antagonist/devil/proc/init_new_rank(typepath, remove_spells = FALSE)
+ if(rank && remove_spells)
+ rank.remove_spells()
+
+ if(typepath)
+ rank = new typepath()
+
+ if(!rank)
+ return FALSE // something bad occured, but we prevent runtimes
+
+ rank.link_rank(owner.current)
+ rank.apply_rank()
+ rank.give_spells()
+
+ return TRUE
+
+/datum/antagonist/devil/proc/remove_spells()
+ rank.remove_spells()
+ info.obligation.remove_spells()
+
+/datum/antagonist/devil/proc/update_hud()
+ var/mob/living/living = owner.current
+
+ if(!living.hud_used?.devilsouldisplay)
+ living.hud_used.devilsouldisplay = new /atom/movable/screen/devil/soul_counter(null, living.hud_used)
+ living.hud_used.infodisplay += living.hud_used.devilsouldisplay
+
+ living.hud_used?.devilsouldisplay.update_counter(LAZYLEN(soulsOwned))
+
+/datum/antagonist/devil/proc/remove_hud()
+ var/mob/living/living = owner.current
+
+ if(!living.hud_used?.devilsouldisplay)
+ return
+
+ living.hud_used.infodisplay -= living.hud_used.devilsouldisplay
+ qdel(living.hud_used.devilsouldisplay)
+
+/datum/antagonist/devil/greet()
+ var/list/messages = list()
+ LAZYADD(messages, span_warning("Вы - [info.truename], агент ада, дьявол.\n\
+ Вы прибыли сюда, преследуя важную цель.\n\
+ Склоните экипаж к грехопадению и укрепите влияние ада."))
+ LAZYADD(messages, "Вы никак не можете навредить другим дьяволам.")
+ LAZYADD(messages, info.bane.law)
+ LAZYADD(messages, info.ban.law)
+ LAZYADD(messages, info.obligation.law)
+ LAZYADD(messages, info.banish.law)
+ LAZYADD(messages, "[span_warning("Помните, экипаж может найти ваши слабости, если раскроет ваше истинное имя!")]
")
+ return messages
+
+/datum/antagonist/devil/on_gain()
+ init_devil()
+
+ . = ..()
+
+ if(!.)
+ return FALSE
+
+ var/mob/living/carbon/human/human = owner.current
+ human.store_memory("Your devilic true name is [info.truename]
[info.ban.law].
You may not directly and knowingly physically harm a devil, other than yourself.
[info.bane.law]
[info.obligation.law]
[info.banish.law]
")
+
+ update_hud()
+
+/datum/antagonist/devil/proc/init_devil()
+ GLOB.allDevils[lowertext(info.truename)] = src
+ rank = new BASIC_DEVIL_RANK()
+
+ return
+
+/datum/antagonist/devil/proc/init_bane()
+ info.bane.link_bane(owner.current)
+ info.bane.init_bane()
+
+ return
+
+/datum/antagonist/devil/proc/init_obligation()
+ info.obligation.link_obligation(owner.current)
+ info.obligation.apply_obligation_effect()
+ info.obligation.give_spells()
+
+ return
+
+/datum/antagonist/devil/proc/init_ban()
+ info.ban.link_ban(owner.current)
+ info.ban.apply_ban_effect()
+
+ return
+
+/datum/antagonist/devil/give_objectives()
+ add_objective(/datum/objective/devil/ascend)
+ add_objective(/datum/objective/devil/sintouch)
+ forge_sacrifice_objective()
+
+/datum/antagonist/devil/proc/forge_sacrifice_objective()
+ var/datum/objective/devil/sacrifice/sacrifice = new
+
+ if(!sacrifice.forge())
+ addtimer(CALLBACK(src, PROC_REF(forge_sacrifice_objective)), 1 MINUTES)
+ qdel(sacrifice)
+ return
+
+ add_objective(sacrifice)
+
+/datum/antagonist/devil/add_owner_to_gamemode()
+ LAZYADD(SSticker.mode.devils, owner)
+
+/datum/antagonist/devil/remove_owner_from_gamemode()
+ LAZYREMOVE(SSticker.mode.devils, owner)
+
+/datum/antagonist/devil/farewell()
+ to_chat(owner.current, span_userdanger("Ваша связь с адом пропадает. Вы более не дьявол!"))
+
+/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ owner.current.AddElement(/datum/element/devil_regeneration)
+ owner.current.AddElement(/datum/element/devil_banishment) // handles devil banishes
+
+ init_new_rank()
+ init_bane()
+
+ init_obligation()
+ init_ban()
+
+ update_hud()
+ info.banish.link_banish(owner.current)
+
+ LAZYADD(owner.current.faction, "hell")
+ ADD_TRAIT(owner.current, TRAIT_NO_DEATH, UNIQUE_TRAIT_SOURCE(src))
+
+/datum/antagonist/devil/remove_innate_effects()
+ . = ..()
+ owner.current.RemoveElement(/datum/element/devil_regeneration)
+ owner.current.RemoveElement(/datum/element/devil_banishment)
+
+ remove_spells()
+ remove_hud()
+
+ info.banish.remove_banish()
+ info.bane.remove_bane()
+
+ info.obligation.remove_obligation()
+ info.ban.remove_ban()
+
+ LAZYREMOVE(owner.current.faction, "hell")
+ REMOVE_TRAIT(owner.current, TRAIT_NO_DEATH, UNIQUE_TRAIT_SOURCE(src))
diff --git a/code/modules/antagonists/devil/devil_ban.dm b/code/modules/antagonists/devil/devil_ban.dm
new file mode 100644
index 00000000000..255503bb810
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_ban.dm
@@ -0,0 +1,71 @@
+/datum/devil_ban
+ var/name
+
+ var/desc
+ var/law
+
+ var/mob/living/carbon/owner
+ var/datum/antagonist/devil/devil
+
+/datum/devil_ban/proc/link_ban(mob/living/carbon/carbon)
+ owner = carbon
+ devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+/datum/devil_ban/proc/remove_ban()
+ remove_ban_effect()
+
+ owner = null
+ devil = null
+
+/datum/devil_ban/Destroy(force)
+ remove_ban()
+
+ return ..()
+
+/datum/devil_ban/proc/apply_ban_effect()
+ return
+
+/datum/devil_ban/proc/remove_ban_effect()
+ return
+
+/datum/devil_ban/hurtwoman
+ name = BAN_HURTWOMAN
+
+ desc = "This devil seems to prefer hunting men."
+ law = "You must never harm a female outside of self defense."
+
+/datum/devil_ban/chapel
+ name = BAN_CHAPEL
+
+ desc = "This devil avoids holy ground."
+ law = "You must never attempt to enter the chapel."
+
+/datum/devil_ban/hurtpriest
+ name = BAN_HURTPRIEST
+
+ desc = "The annointed clergy appear to be immune to his powers."
+ law = "You must never attack a priest."
+
+/datum/devil_ban/avoidwater
+ name = BAN_AVOIDWATER
+
+ desc = "The devil seems to have some sort of aversion to water, though it does not appear to harm him."
+ law = "You must never willingly touch a wet surface."
+
+/datum/devil_ban/strikeunconscious
+ name = BAN_STRIKEUNCONCIOUS
+
+ desc = "This devil only shows interest in those who are awake."
+ law = "You must never strike an unconscious person."
+
+/datum/devil_ban/hurtlizard
+ name = BAN_HURTLIZARD
+
+ desc = "This devil will not strike a lizardman first."
+ law = "You must never harm a lizardman outside of self defense."
+
+/datum/devil_ban/hurtanimal
+ name = BAN_HURTANIMAL
+
+ desc = "This devil avoids hurting animals."
+ law = "You must never harm a non-sentient creature or robot outside of self defense."
diff --git a/code/modules/antagonists/devil/devil_bane.dm b/code/modules/antagonists/devil/devil_bane.dm
new file mode 100644
index 00000000000..a0697128476
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_bane.dm
@@ -0,0 +1,191 @@
+/datum/devil_bane
+ var/name
+
+ var/desc
+ var/law
+
+ var/mob/living/carbon/owner
+ var/datum/antagonist/devil/devil
+
+ var/bonus_damage = 1
+
+/datum/devil_bane/Destroy(force)
+ remove_bane()
+
+ owner = null
+ devil = null
+
+ return ..()
+
+/datum/devil_bane/proc/remove_bane()
+ return
+
+/datum/devil_bane/proc/link_bane(mob/living/carbon/carbon)
+ owner = carbon
+ devil = owner.mind?.has_antag_datum(/datum/antagonist/devil)
+
+/datum/devil_bane/proc/init_bane()
+ return
+
+/datum/devil_bane/toolbox
+ name = BANE_TOOLBOX
+
+ law = "Toolboxes are bad news for you, for some reason."
+ desc = "That which holds the means of creation also holds the means of the devil's undoing."
+
+ bonus_damage = BANE_TOOLBOX_DAMAGE_MODIFIER
+
+/datum/devil_bane/toolbox/init_bane()
+ RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(toolbox_attack))
+
+/datum/devil_bane/toolbox/remove_bane()
+ UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY)
+
+/datum/devil_bane/toolbox/proc/toolbox_attack(datum/source, obj/item/item, mob/attacker, params)
+ SIGNAL_HANDLER
+
+ if(!istype(item, /obj/item/storage/toolbox))
+ return
+
+ owner.apply_damage(item.force * bonus_damage)
+ item.visible_message(
+ span_warning("The [item] seems unusually robust this time."),
+ span_notice("The [item] is [owner] unmaking!"))
+
+/datum/devil_bane/whiteclothes
+ name = BANE_WHITECLOTHES
+
+ desc = "Wearing clean white clothing will help ward off this devil."
+ law = "Those clad in pristine white garments will strike you true."
+
+/datum/devil_bane/whiteclothes/init_bane()
+ RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(whiteclothes_attack))
+
+/datum/devil_bane/whiteclothes/remove_bane()
+ UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY)
+
+/datum/devil_bane/whiteclothes/proc/whiteclothes_attack(datum/source, obj/item/item, mob/attacker, params)
+ SIGNAL_HANDLER
+
+ if(!ishuman(attacker))
+ return
+
+ var/mob/living/carbon/human/hunter = attacker
+ if(!istype(hunter.w_uniform, /obj/item/clothing/under))
+ return
+
+ var/obj/item/clothing/under/uniform = hunter.w_uniform
+ if(!GLOB.whiteness[uniform.type])
+ return
+
+ owner.apply_damage(bonus_damage * (item.force * (GLOB.whiteness[uniform.type] + 1)))
+ item.visible_message(span_warning("[owner] seems to have been harmed by the purity of [attacker]'s clothes."),
+ span_notice("Unsullied white clothing is disrupting [owner] form."))
+
+/datum/devil_bane/harvest
+ name = BANE_HARVEST
+
+ law = "The fruits of the harvest shall be your downfall."
+ desc = "Presenting the labors of a harvest will disrupt the devil."
+
+ bonus_damage = BANE_HARVEST_DAMAGE_MULTIPLIER
+
+/datum/devil_bane/harvest/init_bane()
+ RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(harvest_attack))
+
+/datum/devil_bane/harvest/remove_bane()
+ UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY)
+
+/datum/devil_bane/harvest/proc/harvest_attack(datum/source, obj/item/item, mob/attacker, params)
+ SIGNAL_HANDLER
+
+ if(!istype(item, /obj/item/reagent_containers/food/snacks/grown) || !istype(item, /obj/item/grown))
+ return
+
+ owner.apply_damage(item.force * bonus_damage)
+ item.visible_message(
+ span_warning("The spirits of the harvest aid in the exorcism."),
+ span_notice("The harvest spirits are harming [owner]."))
+
+ qdel(item)
+
+/datum/devil_bane/light
+ name = BANE_LIGHT
+
+ desc = "Bright flashes will disorient the devil, likely causing him to flee."
+ law = "Blinding lights will prevent you from using offensive powers for a time."
+
+/datum/devil_bane/light/init_bane()
+ RegisterSignal(owner, COMSIG_LIVING_EARLY_FLASH_EYES, PROC_REF(flash_eyes))
+
+/datum/devil_bane/light/remove_bane()
+ UnregisterSignal(owner, COMSIG_LIVING_EARLY_FLASH_EYES)
+
+/datum/devil_bane/light/proc/flash_eyes(datum/source, intensity, override_blindness_check, affect_silicon, visual, type)
+ SIGNAL_HANDLER
+
+ var/damage = intensity - owner.check_eye_prot()
+
+ if(!damage)
+ owner.mind?.disrupt_spells(0)
+ return
+
+ owner.mind?.disrupt_spells(-500)
+
+/datum/devil_bane/silver
+ name = BANE_SILVER
+
+ desc = "Silver seems to gravely injure this devil."
+ law = "Silver, in all of its forms shall be your downfall."
+
+/datum/devil_bane/silver/init_bane()
+ RegisterSignal(owner, COMSIG_EARLY_REAGENT_ADDED, PROC_REF(check_reagents))
+
+/datum/devil_bane/silver/remove_bane()
+ UnregisterSignal(owner, COMSIG_EARLY_REAGENT_ADDED)
+
+/datum/devil_bane/silver/proc/check_reagents(
+ datum/source,
+ reagent_id,
+ amount,
+ data,
+ reagtemp,
+ no_react,
+ chem_temp
+ )
+ SIGNAL_HANDLER
+
+ if(reagent_id != "silver")
+ return
+
+ owner.reagents?.add_reagent("toxin", amount * bonus_damage)
+
+/datum/devil_bane/iron
+ name = BANE_IRON
+
+ desc = "Cold iron will slowly injure him, until he can purge it from his system."
+ law = "Cold wrought iron shall act as poison to you."
+
+ bonus_damage = 1
+
+/datum/devil_bane/iron/init_bane()
+ RegisterSignal(owner.reagents, COMSIG_EARLY_REAGENT_ADDED, PROC_REF(check_reagents))
+
+/datum/devil_bane/iron/remove_bane()
+ UnregisterSignal(owner.reagents, COMSIG_EARLY_REAGENT_ADDED)
+
+/datum/devil_bane/iron/proc/check_reagents(
+ datum/source,
+ reagent_id,
+ amount,
+ data,
+ reagtemp,
+ no_react,
+ chem_temp
+ )
+ SIGNAL_HANDLER
+
+ if(reagent_id != "iron")
+ return
+
+ owner.reagents?.add_reagent("toxin", amount * bonus_damage)
diff --git a/code/modules/antagonists/devil/devil_banish.dm b/code/modules/antagonists/devil/devil_banish.dm
new file mode 100644
index 00000000000..daea633cb7b
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_banish.dm
@@ -0,0 +1,90 @@
+/datum/devil_banish
+ var/name
+
+ var/desc
+ var/law
+
+ var/mob/living/carbon/owner
+ var/datum/antagonist/devil/devil
+
+/datum/devil_banish/proc/link_banish(mob/living/carbon/carbon)
+ owner = carbon
+ devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+/datum/devil_banish/proc/remove_banish()
+ owner = null
+ devil = null
+
+/datum/devil_banish/Destroy(force)
+ remove_banish()
+
+ return ..()
+
+/datum/devil_banish/proc/check_banishment()
+ return
+
+/datum/devil_banish/water
+ name = BANISH_WATER
+
+ desc = "Чтобы изгнать дьявола, вы должны наполнить его тело святой водой."
+ law = "Если ваше тело наполнено святой водой, вы не сможете воскреснуть."
+
+/datum/devil_banish/water/check_banishment()
+ return owner.reagents?.has_reagent("holy water")
+
+/datum/devil_banish/coffin
+ name = BANISH_COFFIN
+
+ desc = "Этот дьявол вернётся к жизни, если его останки не будут помещены в гроб."
+ law = "Если ваше тело находится в гробу, вы не сможете воскреснуть."
+
+/datum/devil_banish/coffin/check_banishment()
+ return owner.loc && istype(owner.loc, /obj/structure/closet/coffin)
+
+/datum/devil_banish/formaldehyde
+ name = BANISH_FORMALDYHIDE
+
+ desc = "Чтобы изгнать дьявола, вы должны ввести в его безжизненное тело бальзамирующую жидкость."
+ law = "Если ваше тело забальзамировано, вы не сможете воскреснуть."
+
+/datum/devil_banish/formaldehyde/check_banishment()
+ return owner.reagents?.has_reagent("formaldehyde")
+
+/datum/devil_banish/rune
+ name = BANISH_RUNES
+
+ desc = "Этот дьявол воскреснет после смерти, если его рядом не будет руны."
+ law = "Если ваше тело находится возле руны, вы не сможете воскреснуть."
+
+/datum/devil_banish/rune/check_banishment()
+ return locate(/obj/effect/decal/cleanable/crayon) in range(1, owner)
+
+/datum/devil_banish/candle
+ name = BANISH_CANDLES
+
+ desc = "Большое количество зажжённых поблизости свечей помешает дьяволу воскреснуть."
+ law = "Если ваше тело находится рядом с зажжёнными свечами, вы не сможете воскреснуть."
+
+/datum/devil_banish/candle/check_banishment()
+ var/count = 0
+
+ for(var/obj/item/candle/candle in range(1, owner))
+ count += candle.lit
+
+ return count >= 4
+
+/datum/devil_banish/funeral
+ name = BANISH_FUNERAL_GARB
+
+ desc = "Если этот дьявол одет в траурные одежды, либо она лежит рядом с ним, то он не сможет воскреснуть."
+ law = "Если ваше тело облачено в траурные одежды, вы не сможете воскреснуть."
+
+/datum/devil_banish/funeral/check_banishment()
+ if(!ishuman(owner)) // can be true devil
+ return FALSE
+
+ var/mob/living/carbon/human/human = owner
+ if(human.w_uniform && istype(human.w_uniform, /obj/item/clothing/under/burial))
+ return TRUE
+
+ return locate(/obj/item/clothing/under/burial) in range(1, human)
diff --git a/code/modules/antagonists/devil/devil_info.dm b/code/modules/antagonists/devil/devil_info.dm
new file mode 100644
index 00000000000..6e3c52927af
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_info.dm
@@ -0,0 +1,66 @@
+/datum/devilinfo
+ /// Devil's truename
+ var/truename
+ /// Ban of our devil.
+ var/datum/devil_ban/ban
+ /// Obligation of our devil.
+ var/datum/devil_obligation/obligation
+ /// Banish of our devil. Used to dust him. Works only with devil_banishment element
+ var/datum/devil_banish/banish
+ /// Bane of our devil. Used to make devil weaker
+ var/datum/devil_bane/bane
+
+/datum/devilinfo/New(name = randomDevilName())
+ truename = name
+
+ randomdevilbane()
+ randomdevilobligation()
+
+ randomdevilban()
+ randomdevilbanish()
+
+/datum/devilinfo/Destroy(force)
+ QDEL_NULL(banish)
+ QDEL_NULL(bane)
+
+ QDEL_NULL(ban)
+ QDEL_NULL(obligation)
+
+ return ..()
+
+/datum/devilinfo/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
+
+/datum/devilinfo/proc/randomdevilobligation()
+ var/new_obligation = pick(subtypesof(/datum/devil_obligation))
+ obligation = new new_obligation()
+
+/datum/devilinfo/proc/randomdevilban()
+ var/new_ban = pick(subtypesof(/datum/devil_ban))
+ ban = new new_ban()
+
+/datum/devilinfo/proc/randomdevilbane()
+ var/new_bane = pick(subtypesof(/datum/devil_bane))
+ bane = new new_bane()
+
+/datum/devilinfo/proc/randomdevilbanish()
+ var/new_banish = pick(subtypesof(/datum/devil_banish))
+ banish = new new_banish()
diff --git a/code/modules/antagonists/devil/devil_obligation.dm b/code/modules/antagonists/devil/devil_obligation.dm
new file mode 100644
index 00000000000..d34d40c715e
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_obligation.dm
@@ -0,0 +1,94 @@
+/datum/devil_obligation
+ var/name
+
+ var/desc
+ var/law
+
+ var/mob/living/carbon/owner
+ var/datum/antagonist/devil/devil
+
+ var/list/obligation_spells
+
+/datum/devil_obligation/proc/link_obligation(mob/living/carbon/carbon)
+ owner = carbon
+ devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+/datum/devil_obligation/proc/remove_obligation()
+ remove_obligation_effect()
+
+ owner = null
+ devil = null
+
+/datum/devil_obligation/Destroy(force)
+ remove_obligation()
+
+ return ..()
+
+/datum/devil_obligation/proc/give_spells()
+ for(var/obj/effect/proc_holder/spell/spell as anything in obligation_spells)
+ owner.mind?.AddSpell(spell)
+
+/datum/devil_obligation/proc/remove_spells()
+ for(var/obj/effect/proc_holder/spell/spell as anything in owner.mind?.spell_list)
+ if(!is_type_in_list(spell, obligation_spells))
+ continue
+
+ owner.mind?.RemoveSpell(spell)
+
+/datum/devil_obligation/proc/apply_obligation_effect()
+ return
+
+/datum/devil_obligation/proc/remove_obligation_effect()
+ return
+
+/datum/devil_obligation/food
+ name = OBLIGATION_FOOD
+
+ desc = "Этот дьявол всегда предлагает его жертвам еду, прежде чем убивает их."
+ law = "Пока вы не самообороняетесь, вы должны предлагать вашим жертвам еду, прежде чем вредить им."
+
+ obligation_spells = list(/obj/effect/proc_holder/spell/conjure_item/violin)
+
+/datum/devil_obligation/fiddle
+ name = OBLIGATION_FIDDLE
+
+ desc = "Этот дьявол никогда не откажется от музыкального поединка"
+ law = "Пока вы не находитесь в опасности, при предложении музыкального поединка, то вы обязаны его принять."
+
+/datum/devil_obligation/danceoff
+ name = OBLIGATION_DANCEOFF
+
+ desc = "Этот дьявол никогда не откажется от танцевального поединка."
+ law = "Когда вам ничего не угрожает и вас вызвали на танцевальный поединок, то вы обязаны его принять."
+
+ obligation_spells = list(/obj/effect/proc_holder/spell/summon_dancefloor)
+
+/datum/devil_obligation/greet
+ name = OBLIGATION_GREET
+
+ desc = "Этот дьявол, похоже, может общаться только с теми, чьи имена он знает."
+ law = "Вы должны всегда здороваться с людьми, называя их по фамилии, прежде чем заговорить с ними."
+
+/datum/devil_obligation/presenceknown
+ name = OBLIGATION_PRESENCEKNOWN
+
+ desc = "Этот дьявол, похоже, не может нападать из-за укрытия."
+ law = "Вы должны всегда заявлять о своем присутствии перед атакой."
+
+/datum/devil_obligation/sayname
+ name = OBLIGATION_SAYNAME
+
+ desc = "Он всегда произносит свое имя, убивая кого-либо."
+ law = "Вы должны всегда произносить свое истинное имя после того, как убьете кого-либо."
+
+/datum/devil_obligation/announcekill
+ name = OBLIGATION_ANNOUNCEKILL
+
+ desc = "Этот дьявол всегда громко объявляет о своих убийствах, чтобы это услышал весь мир."
+ law = "Убив кого-либо, вы должны известить всех в пределах слышимости о своем поступке, через связь, если это возможно."
+
+/datum/devil_obligation/answertotruename
+ name = OBLIGATION_ANSWERTONAME
+
+ desc = "Этот дьявол всегда отвечает на свое истинное имя."
+ law = "Если на вас не нападают, вы должны всегда откликаться на свое истинное имя."
diff --git a/code/modules/antagonists/devil/devil_outfit.dm b/code/modules/antagonists/devil/devil_outfit.dm
new file mode 100644
index 00000000000..df8a40f9a32
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_outfit.dm
@@ -0,0 +1,30 @@
+/datum/outfit/devil_lawyer
+ name = "Devil Lawyer"
+ uniform = /obj/item/clothing/under/lawyer/black
+ shoes = /obj/item/clothing/shoes/laceup
+ back = /obj/item/storage/backpack
+ l_hand = /obj/item/storage/briefcase
+ l_pocket = /obj/item/pen
+ l_ear = /obj/item/radio/headset
+ id = /obj/item/card/id
+
+/datum/outfit/devil_lawyer/post_equip(mob/living/carbon/human/human, visualsOnly = FALSE)
+ var/obj/item/card/id/id = human.wear_id
+
+ if(!istype(id) || id.assignment) // either doesn't have a card, or the card is already written to
+ return
+
+ var/name_to_use = human.real_name
+ var/datum/antagonist/devil/devilinfo = human.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(devilinfo)
+ name_to_use = devilinfo.info.truename // Having hell create an ID for you causes its risks
+
+ id.name = "[name_to_use]'s ID Card (Lawyer)"
+ id.registered_name = name_to_use
+ id.assignment = "Lawyer"
+ id.rank = id.assignment
+ id.age = human.age
+ id.sex = capitalize(human.gender)
+ id.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS)
+ id.photo = get_id_photo(human)
diff --git a/code/modules/antagonists/devil/devil_pawn.dm b/code/modules/antagonists/devil/devil_pawn.dm
new file mode 100644
index 00000000000..40adf208303
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_pawn.dm
@@ -0,0 +1,7 @@
+/// Devil's allies/creations should be marked with this datum
+/datum/antagonist/devil_pawn
+ name = "Devil's pawn"
+ special_role = SPECIAL_ROLE_DEVIL_PAWN
+ give_objectives = FALSE
+ silent = TRUE
+ show_in_roundend = FALSE
diff --git a/code/modules/antagonists/devil/devil_rank.dm b/code/modules/antagonists/devil/devil_rank.dm
new file mode 100644
index 00000000000..86de4a02615
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_rank.dm
@@ -0,0 +1,123 @@
+/datum/devil_rank
+ /// Antagonist datum of our owner
+ var/datum/antagonist/devil/devil
+ /// Which spells we'll give to rank owner when rank is applied
+ var/list/rank_spells
+ /// Regeneration things for devil. Used in devil elements
+ var/regen_threshold
+ var/regen_amount
+ /// just OOP thing. Ranks without this designated as last rank.
+ var/next_rank_type
+ /// How many souls we need to ascend to next rank.
+ var/required_souls
+
+/datum/devil_rank/Destroy(force)
+ remove_spells()
+
+ devil = null
+
+ return ..()
+
+/datum/devil_rank/proc/link_rank(mob/living/carbon/carbon)
+ devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil)
+
+/datum/devil_rank/proc/remove_spells()
+ if(!devil.owner)
+ return
+
+ for(var/obj/effect/proc_holder/spell/spell as anything in devil.owner.spell_list)
+ if(!is_type_in_list(spell, rank_spells))
+ continue
+
+ devil.owner.RemoveSpell(spell)
+
+/datum/devil_rank/proc/apply_rank(mob/living/carbon/carbon)
+ return
+
+/datum/devil_rank/proc/give_spells()
+ if(!devil.owner)
+ return
+
+ for(var/obj/effect/proc_holder/spell/spell as anything in rank_spells)
+ devil.owner.AddSpell(new spell)
+
+/datum/devil_rank/basic_devil
+ regen_threshold = BASIC_DEVIL_REGEN_THRESHOLD
+ regen_amount = BASIC_DEVIL_REGEN_AMOUNT
+
+ next_rank_type = ENRAGED_DEVIL_RANK
+ required_souls = ENRAGED_THRESHOLD
+
+ rank_spells = list(/obj/effect/proc_holder/spell/sacrifice_circle)
+
+/datum/devil_rank/enraged_devil
+ regen_threshold = ENRAGED_DEVIL_REGEN_THRESHOLD
+ regen_amount = ENRAGED_DEVIL_REGEN_AMOUNT
+
+ next_rank_type = BLOOD_LIZARD_RANK
+ required_souls = BLOOD_THRESHOLD
+
+ rank_spells = list(
+ /obj/effect/proc_holder/spell/sacrifice_circle,
+ /obj/effect/proc_holder/spell/conjure_item/pitchfork,
+ /obj/effect/proc_holder/spell/aoe/devil_fire,
+ /obj/effect/proc_holder/spell/dark_conversion
+ )
+
+/datum/devil_rank/blood_lizard
+ regen_threshold = BLOOD_LIZARD_REGEN_THRESHOLD
+ regen_amount = BLOOD_LIZARD_REGEN_AMOUNT
+
+ next_rank_type = TRUE_DEVIL_RANK
+ required_souls = TRUE_THRESHOLD
+
+ rank_spells = list(
+ /obj/effect/proc_holder/spell/sacrifice_circle,
+ /obj/effect/proc_holder/spell/conjure_item/pitchfork,
+ /obj/effect/proc_holder/spell/fireball/hellish,
+ /obj/effect/proc_holder/spell/aoe/devil_fire,
+ /obj/effect/proc_holder/spell/infernal_jaunt,
+ /obj/effect/proc_holder/spell/dark_conversion,
+ /obj/effect/proc_holder/spell/aoe/devil_fire
+ )
+
+/datum/devil_rank/blood_lizard/apply_rank()
+ var/mob/living/carbon/human/human = devil.owner.current
+ var/list/language_temp = LAZYLEN(human.languages) ? human.languages.Copy() : null
+
+ human.set_species(/datum/species/unathi)
+ if(language_temp)
+ human.languages = language_temp
+
+ human.underwear = "Nude"
+ human.undershirt = "Nude"
+ human.socks = "Nude"
+ human.change_skin_color(80, 16, 16) //A deep red
+ human.regenerate_icons()
+
+ return
+
+/datum/devil_rank/true_devil
+ regen_threshold = TRUE_DEVIL_REGEN_THRESHOLD
+ regen_amount = TRUE_DEVIL_REGEN_AMOUNT
+
+ rank_spells = list(
+ /obj/effect/proc_holder/spell/conjure_item/pitchfork/greater,
+ /obj/effect/proc_holder/spell/fireball/hellish,
+ /obj/effect/proc_holder/spell/aoe/devil_fire,
+ /obj/effect/proc_holder/spell/infernal_jaunt,
+ /obj/effect/proc_holder/spell/sintouch,
+ /obj/effect/proc_holder/spell/dark_conversion,
+ /obj/effect/proc_holder/spell/aoe/devil_fire
+ )
+
+/datum/devil_rank/true_devil/apply_rank()
+ to_chat(devil.owner.current, span_revenbignotice("Вы чувствуете, как ваше тело меняется."))
+ var/mob/living/carbon/true_devil/true_devil = new /mob/living/carbon/true_devil(get_turf(devil.owner.current))
+
+ devil.owner.current.forceMove(true_devil)
+ true_devil.oldform = devil.owner.current
+ devil.owner.transfer_to(true_devil)
+ true_devil.set_name()
+
+ return
diff --git a/code/modules/antagonists/devil/devil_ritual.dm b/code/modules/antagonists/devil/devil_ritual.dm
new file mode 100644
index 00000000000..48171b71c07
--- /dev/null
+++ b/code/modules/antagonists/devil/devil_ritual.dm
@@ -0,0 +1,88 @@
+/datum/ritual/devil
+ allowed_special_role = list(ROLE_DEVIL)
+ cooldown_after_cast = null
+ disaster_prob = 0
+ fail_chance = 0
+
+/datum/ritual/devil/imp
+ name = "Imp summoning ritual"
+ required_things = list(
+ /obj/item/wirecutters = 3,
+ /obj/item/organ/internal/kidneys = 2,
+ /obj/item/organ/internal/heart = 1,
+ /obj/effect/decal/cleanable/vomit = 2
+ )
+
+/datum/ritual/devil/imp/del_things()
+ for(var/obj/obj in used_things) // no type ignore for future.
+ qdel(obj)
+
+ return
+
+/datum/ritual/devil/imp/do_ritual(mob/living/carbon/human/invoker)
+ var/list/candidates = SSghost_spawns.poll_candidates("Вы хотите сыграть за беса?", SPECIAL_ROLE_DEVIL_PAWN, TRUE)
+
+ if(!LAZYLEN(candidates))
+ return RITUAL_FAILED_ON_PROCEED
+
+ var/mob/mob = pick(candidates)
+ var/mob/living/simple_animal/imp/imp = new(get_turf(ritual_object))
+
+ imp.key = mob.key
+ imp.master_commander = invoker
+
+ improve_imp(imp, invoker)
+
+ return RITUAL_SUCCESSFUL
+
+/datum/ritual/devil/imp/proc/improve_imp(mob/living/simple_animal/imp/imp, mob/living/carbon/human/invoker)
+ var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ imp.universal_speak = TRUE
+ imp.sentience_act()
+
+ imp.mind.store_memory("Я подчиняюсь призывателю [imp.master_commander.name], также известному как [devil.info.truename].")
+ imp.mind.add_antag_datum(/datum/antagonist/devil_pawn)
+
+/datum/ritual/devil/sacrifice
+ name = "Sacrifice ritual"
+ ritual_should_del_things = FALSE
+ required_things = list(
+ /mob/living/carbon/human = 1
+ )
+
+/datum/ritual/devil/sacrifice/check_contents(mob/living/carbon/human/invoker)
+ . = ..()
+
+ if(!.)
+ return FALSE
+
+ var/mob/living/carbon/human/human = locate() in used_things
+
+ if(!human.mind || !human.mind.hasSoul)
+ ritual_object.balloon_alert("цель без души!")
+ return FALSE
+
+ var/datum/objective/devil/sacrifice/sacrifice = locate() in invoker.mind?.get_all_objectives()
+
+ if(sacrifice && !LAZYIN(sacrifice.target_minds, human.mind))
+ ritual_object.balloon_alert("не имеет ценности!")
+ return FALSE
+
+ var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(LAZYIN(devil.soulsOwned, human.mind))
+ return FALSE // Error occured / Admin changed hasSoul.
+
+ return TRUE
+
+/datum/ritual/devil/sacrifice/do_ritual(mob/living/carbon/human/invoker)
+ var/mob/living/carbon/human/human = locate() in used_things
+ var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil)
+
+ if(!devil || !human || !human.mind)
+ return RITUAL_FAILED_ON_PROCEED
+
+ devil.add_soul(human.mind)
+
+ return RITUAL_SUCCESSFUL
diff --git a/code/modules/antagonists/devil/helper_procs.dm b/code/modules/antagonists/devil/helper_procs.dm
new file mode 100644
index 00000000000..ec11f8b851f
--- /dev/null
+++ b/code/modules/antagonists/devil/helper_procs.dm
@@ -0,0 +1,14 @@
+/mob/living/proc/owns_soul()
+ if(!mind)
+ return FALSE
+
+ return mind.soulOwner == mind
+
+/proc/devilInfo(name)
+ if(GLOB.allDevils[lowertext(name)])
+ return GLOB.allDevils[lowertext(name)]
+
+ var/datum/devilinfo/devilinfo = new /datum/devilinfo(name)
+ GLOB.allDevils[lowertext(name)] = devilinfo
+
+ return devilinfo
diff --git a/code/game/gamemodes/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
similarity index 74%
rename from code/game/gamemodes/devil/imp/imp.dm
rename to code/modules/antagonists/devil/imp/imp.dm
index 3afb4e1c28c..b1281dafc01 100644
--- a/code/game/gamemodes/devil/imp/imp.dm
+++ b/code/modules/antagonists/devil/imp/imp.dm
@@ -28,10 +28,6 @@
melee_damage_upper = 15
nightvision = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- var/playstyle_string = "You are an imp, a mischevious 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/ComponentInitialize()
AddComponent( \
@@ -47,8 +43,9 @@
/mob/living/simple_animal/imp/death(gibbed)
- ..(1)
+ . = ..(TRUE)
+
playsound(get_turf(src),'sound/misc/demon_dies.ogg', 200, 1)
- visible_message("[src] screams in agony as it sublimates into a sulfurous smoke.")
- ghostize()
+ visible_message(span_danger("[src] screams in agony as it sublimates into a sulfurous smoke."))
+
qdel(src)
diff --git a/code/modules/antagonists/devil/sintouched.dm b/code/modules/antagonists/devil/sintouched.dm
new file mode 100644
index 00000000000..590991225a5
--- /dev/null
+++ b/code/modules/antagonists/devil/sintouched.dm
@@ -0,0 +1,43 @@
+/datum/antagonist/sintouched
+ name = "Sintouched"
+ special_role = SPECIAL_ROLE_SINTOUCHED
+
+/datum/antagonist/sintouched/can_be_owned(datum/mind/new_owner)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/datum/mind/tested = new_owner || owner
+
+ if(!tested || !ishuman(tested.current))
+ return FALSE
+
+ return TRUE
+
+/datum/antagonist/sintouched/give_objectives()
+ var/list/sins = list()
+
+ for(var/datum/objective/sintouched/sin as anything in subtypesof(/datum/objective/sintouched))
+ if(!sin.explanation_text)
+ continue
+
+ LAZYADD(sins, sin)
+
+ add_objective(pick(sins))
+
+/datum/antagonist/sintouched/add_owner_to_gamemode()
+ LAZYADD(SSticker.mode.sintouched, owner)
+
+/datum/antagonist/sintouched/remove_owner_from_gamemode()
+ LAZYREMOVE(SSticker.mode.sintouched, owner)
+
+/datum/antagonist/sintouched/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+
+ var/mob/living/carbon/human/human = mob_override || owner.current
+
+ for(var/datum/objective/sintouched/sin_objective in owner.objectives)
+ sin_objective.init_sin(human)
+
+/datum/antagonist/sintouched/on_body_transfer(mob/living/old_body, mob/living/new_body)
+ return // No.
diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm
similarity index 72%
rename from code/game/gamemodes/devil/true_devil/_true_devil.dm
rename to code/modules/antagonists/devil/true_devil/_true_devil.dm
index 6a02726016a..12b4942d6e8 100644
--- a/code/game/gamemodes/devil/true_devil/_true_devil.dm
+++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm
@@ -12,41 +12,37 @@
status_flags = CANPUSH
universal_understand = TRUE
universal_speak = TRUE //The devil speaks all languages meme
- hud_type = /datum/hud/devil
- var/ascended = FALSE
var/mob/living/oldform
+ var/datum/antagonist/devil/devilinfo
+ hud_type = /datum/hud/devil
-/mob/living/carbon/true_devil/New(loc, mob/living/carbon/dna_source)
+/mob/living/carbon/true_devil/Initialize(mapload, mob/living/carbon/dna_source)
if(dna_source)
dna = dna_source.dna.Clone()
else
dna = new
+ devilinfo = mind?.has_antag_datum(/datum/antagonist/devil)
new /obj/item/organ/internal/brain(src)
new /obj/item/organ/internal/ears(src)
- ..()
+
+ . = ..()
// Determines if mob has and can use his hands like a human
/mob/living/carbon/true_devil/real_human_being()
return TRUE
-
-/mob/living/carbon/true_devil/proc/convert_to_archdevil()
- maxHealth = 5000 // not an IMPOSSIBLE amount, but still near impossible.
- ascended = TRUE
- health = maxHealth
- icon_state = "arch_devil"
-
/mob/living/carbon/true_devil/set_name()
- name = mind.devilinfo.truename
+ name = devilinfo.info.truename
real_name = name
/mob/living/carbon/true_devil/Login()
..()
var/list/messages = list()
- if(mind.devilinfo)
- messages.Add(mind.devilinfo.announce_laws(src))
- messages.Add(mind.prepare_announce_objectives())
+
+ LAZYADD(messages, devilinfo?.greet())
+ LAZYADD(messages, mind.prepare_announce_objectives())
+
to_chat(mind.current, chat_box_red(messages.Join("
")))
@@ -94,12 +90,6 @@
/mob/living/carbon/true_devil/assess_threat()
return 666
-/mob/living/carbon/true_devil/flash_eyes(intensity = 1, override_blindness_check, affect_silicon, visual, type = /atom/movable/screen/fullscreen/flash)
- if(mind && has_bane(BANE_LIGHT))
- mind.disrupt_spells(-500)
- return ..() //flashes don't stop devils UNLESS it's their bane.
-
-
/mob/living/carbon/true_devil/proceed_attack_results(obj/item/I, mob/living/user, params, def_zone)
. = ATTACK_CHAIN_PROCEED_SUCCESS
@@ -107,7 +97,6 @@
if(!I.force)
return .
- apply_damage(I.force * check_weakness(I, user), I.damtype, def_zone, sharp = is_sharp(I), used_weapon = I)
if(QDELETED(src))
return ATTACK_CHAIN_BLOCKED_ALL
@@ -123,29 +112,8 @@
/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
-
-/mob/living/carbon/true_devil/singularity_act()
- if(ascended)
- return 0
- return ..()
-
-/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
- S.mind.assigned_role = "MODE"
- S.mind.special_role = "Imp"
- var/datum/objective/newobjective = new
- newobjective.explanation_text = "Try to get a promotion to a higher infernal rank."
- S.mind.objectives += newobjective
- to_chat(S,S.playstyle_string)
- to_chat(S,"Objective #1: [newobjective.explanation_text]")
- return
- else
- return ..()
-
/mob/living/carbon/true_devil/resist_fire()
- //They're immune to fire.
+ return
/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
if(..())
@@ -158,7 +126,7 @@
adjustBruteLoss(damage)
add_attack_logs(M, src, "attacked")
if(INTENT_DISARM)
- if(body_position == STANDING_UP && !ascended) //No stealing the arch devil's pitchfork.
+ if(body_position == STANDING_UP) //No stealing the arch devil's pitchfork.
if(prob(5))
// Weaken knocks people over
// Paralyse knocks people out
@@ -186,17 +154,14 @@
return TRUE
/mob/living/carbon/true_devil/ex_act(severity, ex_target)
- if(!ascended)
- var/b_loss
- switch (severity)
- if (1)
- b_loss = 500
- if (2)
- b_loss = 150
- if(3)
- b_loss = 30
- if(has_bane(BANE_LIGHT))
- b_loss *=2
- adjustBruteLoss(b_loss)
+ var/b_loss
+ switch(severity)
+ if(1)
+ b_loss = 500
+ if(2)
+ b_loss = 150
+ if(3)
+ b_loss = 30
+
+ adjustBruteLoss(b_loss)
return ..()
-
diff --git a/code/modules/library/codex_gigas.dm b/code/modules/library/codex_gigas.dm
index ef54b64f626..e97cd7646fc 100644
--- a/code/modules/library/codex_gigas.dm
+++ b/code/modules/library/codex_gigas.dm
@@ -7,50 +7,71 @@
author = "Forces beyond your comprehension"
unique = TRUE
title = "The codex gigas"
- var/inUse = 0
-
+ var/inUse = FALSE
/obj/item/book/codex_gigas/attack_self(mob/user)
if(!user.has_vision())
return
+
if(inUse)
to_chat(user,"Someone else is reading it.")
return
+
if(!user.is_literate())
- to_chat(user,"You don't know how to read.")
+ to_chat(user, span_notice("You don't know how to read."))
+ return
+
+ if(!ishuman(user))
+ return
+
+ var/mob/living/carbon/human/human = user
+
+ if(locate(/datum/objective/sintouched/acedia) in human.mind?.objectives)
+ to_chat(user, span_notice("None of this matters, why are you reading this? You put the [title] down."))
+ return
+
+ inUse = TRUE
+
+ var/devilName = copytext(sanitize(input(user, "What infernal being do you wish to research?", "Codex Gigas", null) as text), 1, MAX_MESSAGE_LEN)
+ var/speed = 30 SECONDS
+ var/correctness = 85
+ var/willpower = 95
+
+ if(human.job in list(JOB_TITLE_LIBRARIAN)) // the librarian is both faster, and more accurate than normal crew members at research
+ speed = 4.5 SECONDS
+ correctness = 100
+ willpower = 100
+
+ if(human.job in list(JOB_TITLE_CHAPLAIN)) // the librarian is both faster, and more accurate than normal crew members at research
+ speed = 30 SECONDS
+ correctness = 100
+
+ if(human.job in list(JOB_TITLE_CAPTAIN, JOB_TITLE_OFFICER, JOB_TITLE_HOS, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN))
+ willpower = 99
+
+ if(human.job in list(JOB_TITLE_CLOWN)) // WHO GAVE THE CLOWN A DEMONOMICON? BAD THINGS WILL HAPPEN!
+ willpower = 25
+
+ correctness -= human.getBrainLoss() *0.5 //Brain damage makes researching hard.
+ speed += human.getBrainLoss() * 0.3 SECONDS
+ user.visible_message("[user] opens [title] and begins reading intently.")
+
+ if(!do_after(human, speed, human, DEFAULT_DOAFTER_IGNORE | DA_IGNORE_HELD_ITEM))
return
- if(ishuman(user))
- var/mob/living/carbon/human/U = user
- if(U.check_acedia())
- to_chat(user,"None of this matters, why are you reading this? You put the [title] down.")
- return
- inUse = 1
- var/devilName = copytext(sanitize(input(user, "What infernal being do you wish to research?", "Codex Gigas", null) as text),1,MAX_MESSAGE_LEN)
- var/speed = 30 SECONDS
- var/correctness = 85
- var/willpower = 95
- if(U.job in list(JOB_TITLE_LIBRARIAN)) // the librarian is both faster, and more accurate than normal crew members at research
- speed = 4.5 SECONDS
- correctness = 100
- willpower = 100
- if(U.job in list(JOB_TITLE_CHAPLAIN)) // the librarian is both faster, and more accurate than normal crew members at research
- speed = 30 SECONDS
- correctness = 100
- if(U.job in list(JOB_TITLE_CAPTAIN, JOB_TITLE_OFFICER, JOB_TITLE_HOS, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN))
- willpower = 99
- if(U.job in list(JOB_TITLE_CLOWN)) // WHO GAVE THE CLOWN A DEMONOMICON? BAD THINGS WILL HAPPEN!
- willpower = 25
- correctness -= U.getBrainLoss() *0.5 //Brain damage makes researching hard.
- speed += U.getBrainLoss() * 0.3 SECONDS
- user.visible_message("[user] opens [title] and begins reading intently.")
- if(do_after(U, speed, U, DEFAULT_DOAFTER_IGNORE|DA_IGNORE_HELD_ITEM))
- var/usedName = devilName
- if(!prob(correctness))
- usedName += "x"
- var/datum/devilinfo/devil = devilInfo(usedName, 0)
- user << browse("Information on [devilName]
[GLOB.lawlorify[LORE][devil.ban]]
[GLOB.lawlorify[LORE][devil.bane]]
[GLOB.lawlorify[LORE][devil.obligation]]
[GLOB.lawlorify[LORE][devil.banish]]", "window=book")
- inUse = 0
- sleep(10)
- if(!prob(willpower))
- U.influenceSin()
- onclose(user, "book")
+
+ var/usedName = devilName
+
+ if(!prob(correctness))
+ usedName += "x"
+
+ var/datum/antagonist/devil/devil = devilInfo(usedName)
+ user << browse("Information on [devilName]
[devil.info.ban.desc]
[devil.info.bane.desc]
[devil.info.obligation.desc]
[devil.info.banish.desc]", "window=book")
+
+ inUse = FALSE
+ addtimer(CALLBACK(src, PROC_REF(close), human, willpower), 10 SECONDS)
+
+/obj/item/book/codex_gigas/proc/close(mob/living/carbon/human/human, willpower)
+ if(!prob(willpower))
+ human.mind?.add_antag_datum(/datum/antagonist/sintouched)
+
+ onclose(human, "book")
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a7047e55786..bbf221d5e01 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -396,16 +396,11 @@
else
to_chat(src, span_warning("Ваши глаза начинают изрядно болеть. Это определенно не очень хорошо!"))
- if(mind && has_bane(BANE_LIGHT))
- mind.disrupt_spells(-500)
return TRUE
else if(damage == 0) // just enough protection
if(prob(20))
to_chat(src, span_notice("Что-то яркое вспыхнуло на периферии вашего зрения!"))
- if(mind && has_bane(BANE_LIGHT))
- mind.disrupt_spells(0)
-
/mob/living/carbon/proc/create_dna()
if(!dna)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 8fe2746464a..79dec817b22 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1774,46 +1774,6 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
if(LAZYIN(mind.curses, "high_rp")) // Probably need to make a new proc to handle curses in case if there will be new ones
curse_high_rp()
-/mob/living/carbon/human/proc/influenceSin()
- if(!mind)
- return
-
- var/datum/objective/sintouched/sin_objective
-
- switch(rand(1,7))//traditional seven deadly sins... except lust.
- if(1) // acedia
- add_game_logs("[src] was influenced by the sin of Acedia.", src)
- sin_objective = new /datum/objective/sintouched/acedia
- if(2) // Gluttony
- add_game_logs("[src] was influenced by the sin of gluttony.", src)
- sin_objective = new /datum/objective/sintouched/gluttony
- if(3) // Greed
- add_game_logs("[src] was influenced by the sin of greed.", src)
- sin_objective = new /datum/objective/sintouched/greed
- if(4) // sloth
- add_game_logs("[src] was influenced by the sin of sloth.", src)
- sin_objective = new /datum/objective/sintouched/sloth
- if(5) // Wrath
- add_game_logs("[src] was influenced by the sin of wrath.", src)
- sin_objective = new /datum/objective/sintouched/wrath
- if(6) // Envy
- add_game_logs("[src] was influenced by the sin of envy.", src)
- sin_objective = new /datum/objective/sintouched/envy
- if(7) // Pride
- add_game_logs("[src] was influenced by the sin of pride.", src)
- sin_objective = new /datum/objective/sintouched/pride
-
- sin_objective.init_sin(src)
- LAZYADD(SSticker.mode.sintouched, mind)
- LAZYADD(mind.objectives, sin_objective)
-
- var/obj_count = 1
- to_chat(src, span_notice("Your current objectives:"))
-
- for(var/datum/objective/objective in mind.objectives)
- to_chat(src, "Objective #[obj_count]: [objective.explanation_text]")
- obj_count++
-
/mob/living/carbon/human/is_literate()
return getBrainLoss() < 100
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 7f463abfa0c..24a0cf757e6 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -510,24 +510,23 @@ emp_act
if(weapon_sharp && prob(getarmor(user.zone_selected, MELEE)))
weapon_sharp = FALSE
- var/cached_force = I.force * check_weakness(I, user)
// this can destroy some species (damn nucleo-bombers), so from now on we cannot count on its existance
- var/apply_damage_result = apply_damage(cached_force, I.damtype, affecting, armor, weapon_sharp, I)
+ var/apply_damage_result = apply_damage(I.force, I.damtype, affecting, armor, weapon_sharp, I)
var/IM_ALIVE = !QDELETED(src)
var/list/all_objectives = user.mind?.get_all_objectives()
if(all_objectives)
for(var/datum/objective/pain_hunter/objective in all_objectives)
if(mind == objective.target)
- objective.take_damage(cached_force, I.damtype)
+ objective.take_damage(I.force, I.damtype)
if(!IM_ALIVE)
return .
var/bloody = FALSE
- if(apply_damage_result && I.damtype == BRUTE && prob(25 + cached_force * 2))
+ if(apply_damage_result && I.damtype == BRUTE && prob(25 + I.force * 2))
I.add_mob_blood(src) //Make the weapon bloody, not the person.
- if(prob(cached_force * 2)) //blood spatter!
+ if(prob(I.force * 2)) //blood spatter!
bloody = TRUE
add_splatter_floor()
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
@@ -536,14 +535,14 @@ emp_act
switch(hit_area)
if(BODY_ZONE_HEAD)//Harder to score a stun but if you do it lasts a bit longer
if(apply_damage_result && stat == CONSCIOUS && armor < 50)
- if(prob(cached_force))
+ if(prob(I.force))
visible_message(
span_combatdanger("[src] has been knocked down!"),
span_combatuserdanger("[src] has been knocked down!"),
)
apply_effect(4 SECONDS, KNOCKDOWN, armor)
AdjustConfused(30 SECONDS)
- if(mind?.special_role == SPECIAL_ROLE_REV && prob(cached_force + ((100 - health)/2)) && src != user && I.damtype == BRUTE)
+ if(mind?.special_role == SPECIAL_ROLE_REV && prob(I.force + ((100 - health)/2)) && src != user && I.damtype == BRUTE)
SSticker.mode.remove_revolutionary(mind)
if(bloody)//Apply blood
@@ -558,7 +557,7 @@ emp_act
update_inv_glasses()
if(BODY_ZONE_CHEST)//Easier to score a stun but lasts less time
- if(apply_damage_result && stat == CONSCIOUS && prob(cached_force + 10))
+ if(apply_damage_result && stat == CONSCIOUS && prob(I.force + 10))
visible_message(
span_combatdanger("[src] has been knocked down!"),
span_combatuserdanger("[src] has been knocked down!"),
@@ -573,7 +572,7 @@ emp_act
w_uniform.add_mob_blood(src)
update_inv_w_uniform()
- if(apply_damage_result && (cached_force > 10 || (cached_force >= 5 && prob(33))))
+ if(apply_damage_result && (I.force > 10 || (I.force >= 5 && prob(33))))
forcesay(GLOB.hit_appends) //forcesay checks stat already
. |= dna.species.spec_proceed_attack_results(I, src, user, affecting)
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index dc484ceedcb..275345c0147 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -40,12 +40,14 @@
return TRUE
/mob/living/proc/can_die()
- return !(stat == DEAD || HAS_TRAIT(src, TRAIT_GODMODE))
+ return !(stat == DEAD || HAS_TRAIT(src, TRAIT_GODMODE) || HAS_TRAIT(src, TRAIT_NO_DEATH))
// Returns true if mob transitioned from live to dead
// Do a check with `can_die` beforehand if you need to do any
// handling before `stat` is set
/mob/living/death(gibbed)
+ SEND_SIGNAL(src, COMSIG_LIVING_EARLY_DEATH, gibbed)
+
if(stat == DEAD || !can_die())
// Whew! Good thing I'm indestructible! (or already dead)
return FALSE
@@ -103,10 +105,6 @@
SSticker.mode.check_win()
clear_alert("succumb")
-
- if(mind && mind.devilinfo) // Expand this into a general-purpose death-response system when appropriate
- mind.devilinfo.beginResurrectionCheck(src)
-
SEND_SIGNAL(src, COMSIG_LIVING_DEATH, gibbed)
// u no we dead
return TRUE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 0618bc649d7..50d50476a21 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1266,6 +1266,10 @@
/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check, affect_silicon, visual, type = /atom/movable/screen/fullscreen/flash)
if(HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
+
+ if(SEND_SIGNAL(src, COMSIG_LIVING_EARLY_FLASH_EYES, intensity, override_blindness_check, affect_silicon, visual, type) & STOP_FLASHING_EYES)
+ return FALSE
+
if(check_eye_prot() < intensity && (override_blindness_check || !HAS_TRAIT(src, TRAIT_BLIND)))
overlay_fullscreen("flash", type)
addtimer(CALLBACK(src, PROC_REF(clear_fullscreen), "flash", 25), 25)
@@ -1647,35 +1651,6 @@
..()
update_z(new_turf?.z)
-/mob/living/proc/owns_soul()
- if(mind)
- return mind.soulOwner == mind
- return 1
-
-/mob/living/proc/return_soul()
- if(mind)
- if(mind.soulOwner.devilinfo)//Not sure how this could happen, but whatever.
- mind.soulOwner.devilinfo.remove_soul(mind)
- mind.soulOwner = mind
- mind.damnation_type = 0
-
-/mob/living/proc/has_bane(banetype)
- if(mind)
- if(mind.devilinfo)
- return mind.devilinfo.bane == banetype
- return 0
-
-/mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker)
- if(mind && mind.devilinfo)
- return check_devil_bane_multiplier(weapon, attacker)
- return 1
-
-/mob/living/proc/check_acedia()
- if(src.mind && src.mind.objectives)
- for(var/datum/objective/sintouched/acedia/A in src.mind.objectives)
- return 1
- return 0
-
/mob/living/proc/fakefireextinguish()
return
diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm
index 2a8eaffcab1..d1b51b7a0c3 100644
--- a/code/modules/paperwork/contract.dm
+++ b/code/modules/paperwork/contract.dm
@@ -38,35 +38,11 @@
Кроме того, Раб соглашается передать право на владение своей душой отделу лояльности Вездесущего и полезного наблюдателя за человечеством.\
В случае, если передача души Раба невозможна, Раб вносит вместо неё залог.
Подписано,
[target]"
-
-/obj/item/paper/contract/employment/attack(mob/living/victim, mob/living/user, params, def_zone, skip_attack_anim = FALSE)
- . = ..()
- if(!ATTACK_CHAIN_SUCCESS_CHECK(.))
- return .
-
- var/deconvert = 0
- if(victim.mind == target && target.soulOwner != target)
- if(user.mind && (user.mind.assigned_role == JOB_TITLE_LAWYER))
- deconvert = 60
- else if (user.mind && (user.mind.assigned_role == JOB_TITLE_HOP) || (user.mind.assigned_role == "Centcom Commander") || (user.mind.assigned_role == JOB_TITLE_JUDGE))
- deconvert = 40
- else if(user.mind && (user.mind.assigned_role == JOB_TITLE_CAPTAIN))
- deconvert = 25
- else
- deconvert = 0.0001
-
- if(prob(deconvert))
- victim.visible_message(
- span_notice("Благодаря [user] [victim] вспоминает, что душа [victim] уже приобретена НаноТрейзен!"),
- span_boldnotice("Вы чувствуете, как Ваша душа возвращается к её правомочному владельцу — НаноТрейзен."),
- )
- victim.return_soul()
-
-
/obj/item/paper/contract/infernal
var/contractType = 0
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- var/datum/mind/owner
+ var/datum/antagonist/devil/devilinfo
+ var/mob/living/carbon/human/owner
icon_state = "evil_contract"
/obj/item/paper/contract/infernal/power
@@ -103,6 +79,7 @@
/obj/item/paper/contract/infernal/New(atom/loc, mob/living/nTarget, datum/mind/nOwner)
..()
+ devilinfo = nOwner.has_antag_datum(/datum/antagonist/devil)
owner = nOwner
target = nTarget
update_text()
@@ -122,56 +99,56 @@
info = "This shouldn't be seen. Error DEVIL:6"
/obj/item/paper/contract/infernal/power/update_text(var/signature = "____________", blood = 0)
- info = "