diff --git a/code/__DEFINES/spell_defines.dm b/code/__DEFINES/spell_defines.dm
index db62b5c83f0..8b496e3e7af 100644
--- a/code/__DEFINES/spell_defines.dm
+++ b/code/__DEFINES/spell_defines.dm
@@ -22,6 +22,13 @@
#define GLOW_INTENSITY_HIGH 3 // Large AOE
#define GLOW_INTENSITY_VERY_HIGH 4 // Greater Fireball or Massive AOE / T4 spells
+//Gods - Ascendant
+#define GLOW_COLOR_GRAGGAR "#19345E" //Graggar Dark Blue
+#define GLOW_COLOR_ZIZO "#b76bff" //Graggar Dark Purple
+#define GLOW_COLOR_BAOTHA "#ff008c" //Baotha Violent Rose
+#define GLOW_COLOR_MATTHIOS "#ffd900" //Matthios Golden
+
+
// Constants for enchantment effects (used by fit_clothing, gems, etc.)
#define FORCE_BLADE_ENCHANT 2
#define DURABILITY_ENCHANT 3
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ce3e5dc15cf..9a8b45bc949 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -421,7 +421,7 @@ GLOBAL_LIST_INIT(roguetraits, list(
TRAIT_LIMPDICK = span_crit("My soldier refuses to rise to attention! Curses!"),
TRAIT_SEEDKNOW = span_info("I know which seeds grow which crops."),
TRAIT_PERFECT_TRACKER = span_info("I am a master at pursuing those I hunt. I can discern every last detail within a spotted track, and any attempts to hide said-tracks will fail to deceive me."),//Hearthstone port.
- TRAIT_ZIZOSIGHT = span_info("Zizo blesses my eyes to be unburdened by the night."), //Hearthstone change.
+ TRAIT_ZIZOSIGHT = span_info("Zizo blesses my eyes to be unburdened by the night. I can also somewhat judge if a corpse can be reanimated or not."), //Hearthstone change.
TRAIT_CIVILIZEDBARBARIAN = span_info("My rigorous training in the martial arts has turned me into a living weapon. No limb is out of reach for my fists and feet, and my unarmed strikes are now stronger (+4 Unarmed Damage). My parrying with bracers, knuckles, or bandages is significantly more effective."),
TRAIT_COMICSANS = span_sans("I am cursed with a odd voice."),
TRAIT_SQUIRE_REPAIR = span_info("Trained at my Master's side, I can restore any kind of gears with time and polish them until they gleam like new."),
diff --git a/code/controllers/subsystem/rogue/spawned_mobs.dm b/code/controllers/subsystem/rogue/spawned_mobs.dm
new file mode 100644
index 00000000000..f8e6e17d77e
--- /dev/null
+++ b/code/controllers/subsystem/rogue/spawned_mobs.dm
@@ -0,0 +1,124 @@
+#define SPAWNED_MOBS_DEFAULT_LIFESPAN 10 MINUTES
+
+GLOBAL_LIST_INIT(spawnmob_lifespan, list(
+ "Necromancer" = 15 MINUTES,
+ "Lich" = 60 MINUTES
+))
+
+GLOBAL_LIST_INIT(spawnmob_lifespan_override, list())
+
+SUBSYSTEM_DEF(spawned_mobs)
+ name = "Spawned Mob Cull"
+ wait = 30 SECONDS
+ flags = SS_BACKGROUND | SS_NO_INIT
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ var/list/tracked_mobs = list()
+ var/list/currentrun = list()
+
+/datum/controller/subsystem/spawned_mobs/stat_entry()
+ ..("T:[length(tracked_mobs)]")
+
+/datum/controller/subsystem/spawned_mobs/fire(resumed = FALSE)
+ if(!resumed)
+ currentrun = list()
+ for(var/datum/weakref/mobref as anything in tracked_mobs)
+ currentrun += mobref
+
+ while(currentrun.len)
+ var/datum/weakref/mobref = currentrun[currentrun.len]
+ currentrun.len--
+
+ var/expires = tracked_mobs[mobref]
+ if(!expires)
+ if(MC_TICK_CHECK)
+ return
+ continue
+
+ var/mob/living/target = mobref.resolve()
+ if(!target || QDELETED(target) || target.stat == DEAD || target.client)
+ unregister_mob(mobref, target)
+ if(MC_TICK_CHECK)
+ return
+ continue
+
+ if(expires <= world.time)
+ unregister_mob(mobref, target)
+ target.visible_message(span_warning("[target] unravels as the power binding it expires!"))
+ target.death(FALSE)
+
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/spawned_mobs/proc/get_role_lifespan(role)
+ if(role && GLOB.spawnmob_lifespan[role])
+ return GLOB.spawnmob_lifespan[role]
+
+/datum/controller/subsystem/spawned_mobs/proc/get_type_lifespan(mob/living/target)
+ var/current_type = target?.type
+ while(current_type)
+ if(GLOB.spawnmob_lifespan_override[current_type])
+ return GLOB.spawnmob_lifespan_override[current_type]
+ current_type = type2parent(current_type)
+
+/datum/controller/subsystem/spawned_mobs/proc/get_lifespan(mob/living/target, mob/living/summoner, lifespan)
+ if(isnum(lifespan))
+ return lifespan
+
+ var/type_lifespan = get_type_lifespan(target)
+ if(type_lifespan)
+ return type_lifespan
+
+ var/role_lifespan = get_role_lifespan(summoner?.advjob)
+ if(role_lifespan)
+ return role_lifespan
+
+ role_lifespan = get_role_lifespan(summoner?.job)
+ if(role_lifespan)
+ return role_lifespan
+
+ role_lifespan = get_role_lifespan(summoner?.mind?.assigned_role)
+ if(role_lifespan)
+ return role_lifespan
+
+ return SPAWNED_MOBS_DEFAULT_LIFESPAN
+
+/datum/controller/subsystem/spawned_mobs/proc/register_mob(mob/living/target, mob/living/summoner, lifespan)
+ if(!target || target.client)
+ return FALSE
+
+ var/timer = get_lifespan(target, summoner, lifespan)
+ if(!isnum(timer) || timer <= 0 || timer == INFINITY)
+ return FALSE
+
+ var/datum/weakref/mobref = WEAKREF(target)
+ if(!tracked_mobs[mobref])
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(on_mob_examine))
+ tracked_mobs[mobref] = world.time + timer
+ return TRUE
+
+/datum/controller/subsystem/spawned_mobs/proc/unregister_mob(datum/weakref/mobref, mob/living/target)
+ tracked_mobs -= mobref
+ if(target)
+ UnregisterSignal(target, COMSIG_PARENT_EXAMINE)
+
+/datum/controller/subsystem/spawned_mobs/proc/get_remaining_lifespan(mob/living/target)
+ if(!target || target.client || target.stat == DEAD)
+ return 0
+
+ var/expires = tracked_mobs[WEAKREF(target)]
+ if(!expires)
+ return 0
+
+ return max(0, expires - world.time)
+
+/datum/controller/subsystem/spawned_mobs/proc/on_mob_examine(mob/living/source, mob/user, list/examine_list)
+ var/remaining_lifespan = get_remaining_lifespan(source)
+ if(!remaining_lifespan)
+ return
+
+ examine_list += span_notice("A fading force binds [source] together. It will last for [DisplayTimeText(remaining_lifespan)].")
+
+/proc/apply_mob_lifespan(mob/living/target, mob/living/summoner, lifespan)
+ return SSspawned_mobs.register_mob(target, summoner, lifespan)
+
+#undef SPAWNED_MOBS_DEFAULT_LIFESPAN
diff --git a/code/datums/actions/action_cooldown.dm b/code/datums/actions/action_cooldown.dm
index faf125e3ffb..1bc3719367e 100644
--- a/code/datums/actions/action_cooldown.dm
+++ b/code/datums/actions/action_cooldown.dm
@@ -41,6 +41,8 @@
var/active_icon_state
/// Timer for retriggering the spell
var/retrigger_timer
+ /// This will make it so spells that are of the "same type" cannot be obtained. This is mostly for "Lesser" variants to not stack with the normal ones.
+ var/exclusive_group = null
/datum/action/cooldown/New(Target)
. = ..()
diff --git a/code/datums/components/summoning.dm b/code/datums/components/summoning.dm
index f973097beb9..c4549d501a1 100644
--- a/code/datums/components/summoning.dm
+++ b/code/datums/components/summoning.dm
@@ -6,6 +6,7 @@
var/spawn_text
var/spawn_sound
var/list/faction
+ var/spawn_lifespan
var/last_spawned_time = 0
var/list/spawned_mobs = list()
@@ -21,6 +22,7 @@
src.spawn_text = spawn_text
src.spawn_sound = spawn_sound
src.faction = faction
+ src.spawn_lifespan = spawn_lifespan
/datum/component/summoning/RegisterWithParent()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
@@ -60,6 +62,8 @@
spawned_mobs += L
if(faction != null)
L.faction = faction
+ if(spawn_lifespan)
+ apply_mob_lifespan(L, summoner, spawn_lifespan)
RegisterSignal(L, COMSIG_MOB_DEATH, PROC_REF(on_spawned_death)) // so we can remove them from the list, etc (for mobs with corpses)
playsound(spawn_location,spawn_sound, 50, TRUE)
spawn_location.visible_message("[L] [spawn_text].")
diff --git a/code/datums/gods/patrons/inhumen/resurrect_inhumen.dm b/code/datums/gods/patrons/inhumen/resurrect_inhumen.dm
index 9e4f6b7e2b7..3c01811c21e 100644
--- a/code/datums/gods/patrons/inhumen/resurrect_inhumen.dm
+++ b/code/datums/gods/patrons/inhumen/resurrect_inhumen.dm
@@ -8,7 +8,7 @@
alt_required_items = list()
required_items = list()
sound = 'sound/magic/slimesquish.ogg'
- chargedloop = /datum/looping_sound/invokelightning
+ chargedloop = /datum/looping_sound/invokeascendant
harms_undead = FALSE
recharge_time = 2 MINUTES //Anastasis Equivalent
overlay_icon = 'icons/mob/actions/matthiosmiracles.dmi'
@@ -26,7 +26,7 @@
alt_required_items = list(/obj/item/organ/heart = 1)
required_items = list(/obj/item/organ/heart = 1)
sound = 'sound/magic/slimesquish.ogg'
- chargedloop = /datum/looping_sound/invokelightning
+ chargedloop = /datum/looping_sound/invokeascendant
harms_undead = FALSE
overlay_icon = 'icons/mob/actions/graggarmiracles.dmi'
overlay_state = "revival"
@@ -41,7 +41,7 @@
alt_required_items = list(/obj/item/natural/thorn = 3)
required_items = list(/obj/item/natural/thorn = 7)
sound = 'sound/magic/slimesquish.ogg'
- chargedloop = /datum/looping_sound/invokelightning
+ chargedloop = /datum/looping_sound/invokeascendant
harms_undead = FALSE
overlay_icon = 'icons/mob/actions/baothamiracles.dmi'
overlay_state = "revival"
@@ -51,19 +51,19 @@
req_items = list() // temp. baothans dont have a holy symbol. apparently one is being commed so this is just the stopgap.
/obj/effect/proc_holder/spell/invoked/resurrect/zizo
- name = "Zizoid Rebirth"
- desc = "Revive a fallen ally by siphoning their potential. You gain their strength, whilst they gain a second chance.\
+ name = "Hollow Rebirth"
+ desc = "Revive a fallen subject while siphoning their potential and destroying some of their Lux as a toll. You gain their strength, whilst they gain a second chance.\
If they die, you will lose their stolen strength."
sound = 'sound/magic/slimesquish.ogg'
- chargedloop = /datum/looping_sound/invokelightning
+ chargedloop = /datum/looping_sound/invokeascendant
harms_undead = FALSE
overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
overlay_state = "revival"
action_icon_state = "revival"
+ recharge_time = 5 MINUTES // halved compared to others
action_icon = 'icons/mob/actions/zizomiracles.dmi'
- required_items = list(/obj/item/heart_blood_vial/filled = 3)
- alt_required_items = list(/obj/item/heart_blood_vial/filled = 1)
- // We apply zizo's debuff differently
+ // We apply zizo's revival differently from this point onward
+ zizo = TRUE
debuff_type = null
required_structure = /obj/structure/fluff/psycross/zizocross
@@ -483,19 +483,104 @@
// check if parent returns TRUE
if(.)
var/mob/living/carbon/human/target = targets[1]
+
user.apply_status_effect(/datum/status_effect/buff/zizo_tithe, tithe_distribution, target)
target.apply_status_effect(/datum/status_effect/debuff/zizo_drain, tithe_distribution)
- to_chat(user, span_nicegreen("The victim's essence flows into you as they gasp for air."))
- to_chat(target, span_userdanger("You are alive, but Zizo has taken his tithe from your soul."))
+ var/found_zizo_cross = FALSE
+
+ for(var/atom/A in oview(1, target))
+ if(istype(A, /obj/structure/fluff/psycross/zizocross))
+ found_zizo_cross = TRUE
+ break
+
+ if(istype(A, /turf))
+ var/turf/T = A
+ for(var/obj/O in T.contents)
+ if(istype(O, /obj/structure/fluff/psycross/zizocross))
+ found_zizo_cross = TRUE
+ break
+
+ if(found_zizo_cross)
+ break
+
+ // A proper Zizo cross stabilizes the rite and prevents undeath complications
+ if(found_zizo_cross)
+ to_chat(target, span_warning("Your stolen Lux writhes violently, but the unholy cross steadies your Lux before undeath can fully take hold."))
+ else
+ if(!target.has_status_effect(/datum/status_effect/debuff/zizo_temp_undeath))
+ target.apply_status_effect(/datum/status_effect/debuff/zizo_temp_undeath)
+ to_chat(target, span_userdanger("You feel your rekindled Lux torn from within, leaving you hollowed as undeath threatens to gnaw at your fading soul."))
+ else
+ playsound(user.loc, 'sound/misc/smelter_sound.ogg', 50, FALSE)
+ to_chat(target, span_userdanger("Your fading Lux collapses inward far too soon. Flesh sloughs from bone as undeath tightens its grip upon you."))
+ target.apply_status_effect(/datum/status_effect/debuff/devitalised)
+
+ to_chat(user, span_nicegreen("You wrench the victim's rekindled Lux into yourself, leaving them hollowed and starving for life."))
+
+/atom/movable/screen/alert/status_effect/debuff/zizo_temp_undeath
+ name = "Embrace of Zizo"
+ desc = "You feel your very essence struggling against the hold of Undeath... Your mind is beseethed with dark, evil thoughts, and all you feel is hunger..."
+
+/datum/status_effect/debuff/zizo_temp_undeath
+ id = "zizo_temp_undeath"
+ duration = 15 MINUTES
+ tick_interval = 1 MINUTES // every minute, starve, if you manage to fill your belly, duration is reduced by 5 minutes
+ alert_type = /atom/movable/screen/alert/status_effect/debuff/zizo_temp_undeath
+
+/datum/status_effect/debuff/zizo_temp_undeath/on_creation()
+ . = ..()
+ to_chat(owner, span_warning("Hungry... Hungry... HUNGRY. I AM STARVING. I NEED TO EAT. I NEED TO EAT!"))
+ ADD_TRAIT(owner, TRAIT_ROTMAN, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_NASTY_EATER, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_STRONGBITE, "zizo_temp_undeath")
+ to_chat(owner, span_necrosis("My limbs... I am rotten under my skin. Anything can remove them-- Anything can reattach them...?"))
+ ADD_TRAIT(owner, TRAIT_EASYDISMEMBER, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_LIMBATTACHMENT, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_SILVER_WEAK, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_DEATHLESS, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_ZOMBIE_IMMUNE, "zizo_temp_undeath")
+ to_chat(owner, span_boldred("THIS WORLD IS WRONG. EVERYTHING IS WRONG. WE LIVE IN A CORPSE. THE DECAYING CORPSE OF A DEAD GOD!"))
+ ADD_TRAIT(owner, TRAIT_PSYCHOSIS, "zizo_temp_undeath")
+ ADD_TRAIT(owner, TRAIT_NOMOOD, "zizo_temp_undeath")
+
+/datum/status_effect/debuff/zizo_temp_undeath/tick()
+ . = ..()
+ var/mob/living/carbon/human/H = owner
+ if(!istype(H))
+ return
+ if(H.stat == DEAD)
+ return
+ var/very_hongry = pick("HUNGRY...", "Hungry...", "Hungry! Hungry!", "I NEED TO EAT!", "I'm STARVING!!", "Need to eat... anything... I'll eat anything. I'm so hungry.")
+ if(H.nutrition >= NUTRITION_LEVEL_FED)
+ duration -= 5 MINUTES
+ to_chat(owner, span_warning("You feel some of your Lux react to being full... your reserves drain rapidly and your stomach quickly empties."))
+ to_chat(owner, span_green("I am recovering faster..."))
+
+ to_chat(owner, span_warning(very_hongry))
+ H.nutrition = 0
+
+/datum/status_effect/debuff/zizo_temp_undeath/on_remove()
+ . = ..()
+ to_chat(owner, span_boldgreen("...You feel the corroded part of your Lux finally recover, giving you some well-deserved clarity back. What the hell was that?"))
+ REMOVE_TRAIT(owner, TRAIT_ROTMAN, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_NASTY_EATER, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_STRONGBITE, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_EASYDISMEMBER, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_LIMBATTACHMENT, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_SILVER_WEAK, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_DEATHLESS, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_ZOMBIE_IMMUNE, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_PSYCHOSIS, "zizo_temp_undeath")
+ REMOVE_TRAIT(owner, TRAIT_NOMOOD, "zizo_temp_undeath")
/atom/movable/screen/alert/status_effect/debuff/zizo_drain
- name = "Zizo's drain"
- desc = "Zizo has deemed my return worthy, but at a dear expense."
+ name = "Syphoned Lux"
+ desc = "Half of your very rekindled Lux has been syphoned away and the leftovers profaned..."
/atom/movable/screen/alert/status_effect/buff/zizo_tithe
- name = "Zizo's tithe"
- desc = "Zizo has boosted my capabilities with their vitality."
+ name = "Lux Syphon"
+ desc = "You are invigorated with the rekindled Lux of another. A thousand more, and perhaps you will reach Her first step to Ascension."
// THE BOON - Caster
/datum/status_effect/buff/zizo_tithe
@@ -512,14 +597,23 @@
return ..()
/datum/status_effect/buff/zizo_tithe/on_remove()
- UnregisterSignal(victim, COMSIG_LIVING_DEATH)
+
+ if(victim)
+ UnregisterSignal(victim, COMSIG_LIVING_DEATH)
. = ..()
/datum/status_effect/buff/zizo_tithe/proc/cancel_early()
SIGNAL_HANDLER
- var/mob/living/carbon/human/H = owner
- H.remove_status_effect(/datum/status_effect/buff/zizo_tithe)
+ var/mob/living/carbon/human/caster = owner
+ var/mob/living/carbon/human/target = victim
+
+ if(caster)
+ caster.remove_status_effect(/datum/status_effect/buff/zizo_tithe)
+
+ if(target)
+ target.remove_status_effect(/datum/status_effect/debuff/zizo_drain)
+ target.remove_status_effect(/datum/status_effect/debuff/zizo_temp_undeath)
// THE DRAIN - Victim
/datum/status_effect/debuff/zizo_drain
diff --git a/code/datums/gods/patrons/inhumen/zizo.dm b/code/datums/gods/patrons/inhumen/zizo.dm
index 25133f85236..81025282cd5 100644
--- a/code/datums/gods/patrons/inhumen/zizo.dm
+++ b/code/datums/gods/patrons/inhumen/zizo.dm
@@ -4,16 +4,18 @@
desc = "A once-mortal snow elf turned god. Her hubris in thinking she could harvest lux from the planet itself led to the elimination of her entire race. Her works are still used to this dae in some cases."
worshippers = "Necromancers, Researchers, Warlocks, and the Undead"
mob_traits = list(TRAIT_CABAL, TRAIT_ZIZOSIGHT)
- miracles = list(/obj/effect/proc_holder/spell/targeted/touch/orison = CLERIC_ORI,
- /obj/effect/proc_holder/spell/self/zizo_snuff = CLERIC_T0,
- /obj/effect/proc_holder/spell/invoked/lesser_heal = CLERIC_T1,
+ miracles = list(/obj/effect/proc_holder/spell/targeted/touch/orison = CLERIC_ORI,
+ /datum/action/cooldown/spell/zizo/snuff_lights = CLERIC_T0,
+ /obj/effect/proc_holder/spell/invoked/lesser_heal = CLERIC_T1,
/obj/effect/proc_holder/spell/invoked/blood_heal = CLERIC_T1,
- /obj/effect/proc_holder/spell/invoked/projectile/profane/miracle = CLERIC_T1,
- /obj/effect/proc_holder/spell/invoked/raise_undead_formation/miracle= CLERIC_T2,
- /obj/effect/proc_holder/spell/invoked/raise_undead_guard/miracle = CLERIC_T2,
- /obj/effect/proc_holder/spell/invoked/tame_undead/miracle = CLERIC_T3,
- /obj/effect/proc_holder/spell/invoked/rituos/miracle = CLERIC_T3,
- /obj/effect/proc_holder/spell/invoked/resurrect/zizo = CLERIC_T4
+ /datum/action/cooldown/spell/projectile/zizo/profane = CLERIC_T1,
+ /datum/action/cooldown/spell/raise_undead_formation/zizo = CLERIC_T2,
+ /datum/action/cooldown/spell/zizo/bone_cataclysm = CLERIC_T2,
+ /datum/action/cooldown/spell/tame_undead/zizo = CLERIC_T3,
+ /datum/action/cooldown/spell/zizo/rituos = CLERIC_T3,
+ /obj/effect/proc_holder/spell/invoked/resurrect/zizo = CLERIC_T3,
+ /datum/action/cooldown/spell/convert_heretic = CLERIC_T4,
+ /datum/action/cooldown/spell/lacrima/zizo = CLERIC_T4,
)
confess_lines = list(
"PRAISE ZIZO!",
@@ -39,7 +41,7 @@
// Allows prayer near EEEVIL psycross
for(var/obj/structure/fluff/psycross/zizocross/cross in view(4, get_turf(follower)))
if(cross.divine == TRUE)
- to_chat(follower, span_danger("That acсursed cross interupts my prayers!"))
+ to_chat(follower, span_danger("That accursed cross interrupts my prayers!"))
return FALSE
return TRUE
// Allows prayer near a grave.
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 4ab23e1629b..9ee8e95f09a 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -911,6 +911,12 @@ GLOBAL_LIST_EMPTY(personal_objective_minds)
// New action-based spell system
if(istype(spell_or_action, /datum/action/cooldown/spell))
var/datum/action/cooldown/spell/new_spell = spell_or_action
+
+ // check exclusivity
+ for(var/datum/action/cooldown/spell/S in spell_list)
+ if(S.exclusive_group && S.exclusive_group == new_spell.exclusive_group)
+ return // already have one of this group
+
for(var/datum/action/cooldown/spell/present in spell_list)
if(present.name == new_spell.name && present.type == new_spell.type)
return
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index cab8177089c..b59a4ae8b79 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -221,7 +221,7 @@ UNDER NO CIRCUMSTANCE SHOULD ANY OF THE BOOKS BE GIVEN OUT INTO SPAWNERS OR TO B
/obj/item/book/granter/spell/bonechill
name = "Scroll of Bone Chill"
- spell = /obj/effect/proc_holder/spell/invoked/bonechill
+ spell = /datum/action/cooldown/spell/bonechill
spellname = "Bone Chill"
icon = 'icons/roguetown/items/misc.dmi'
icon_state = "scrolldarkred"
diff --git a/code/game/objects/items/ritualcircles.dm b/code/game/objects/items/ritualcircles.dm
index 271ccc017b7..ac9044234fa 100644
--- a/code/game/objects/items/ritualcircles.dm
+++ b/code/game/objects/items/ritualcircles.dm
@@ -1176,7 +1176,15 @@
//target.visible_message(span_danger("[target] is unmade by divine magic! The Toll is accepted, and [target] is dragged to ever-death!"), span_userdanger("I'm unmade by divine magic!"))
//target.gib()
//Caustic Edit End
- return
+ //return
+ if(alert(user, "[target]'s body rattles and seizes under the divine force. This will likely unmake them permanently. Continue?", "Divine Revival", "PURGE THE UNCLEAN!", "Stop") != "PURGE THE UNCLEAN!")
+ to_chat(user, span_notice("You halt the rite before the divine force can fully take hold."))
+ return FALSE
+ target.visible_message(span_danger("[target] is unmade by divine magic!"), span_userdanger("Holy power tears my undead form apart!"))
+ playsound(target.loc, 'sound/magic/churn.ogg', 100, TRUE)
+ target.dust()
+ return TRUE
+
if(alert(target, "A Toll is being offered for your soul, BREAK FREE?", "Revival", "I need to wake up", "Don't let me go") != "I need to wake up")
target.visible_message(span_notice("Nothing happens. They are not being let go."))
return
diff --git a/code/modules/antagonists/roguetown/villain/lich.dm b/code/modules/antagonists/roguetown/villain/lich.dm
index a5b9dfe86a8..6091700ead1 100644
--- a/code/modules/antagonists/roguetown/villain/lich.dm
+++ b/code/modules/antagonists/roguetown/villain/lich.dm
@@ -149,18 +149,19 @@
if(H.mind)
// Lich-specific spells (not from aspects)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonechill)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonechill)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/raise_undead)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/raise_undead_formation)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/raise_undead_formation)
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/blood_bolt())
H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/diagnose/secular)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/minion_order)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/gravemark)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/suicidebomb)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/remotebomb)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/lich_announce)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/tame_undead)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/convert_heretic)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/tame_undead)
H.mind.AddSpell(new /datum/action/cooldown/spell/raise_deadite)
H.ambushable = FALSE
H.dna.species.soundpack_m = new /datum/voicepack/other/lich()
diff --git a/code/modules/antagonists/roguetown/villain/unbound_deathknight.dm b/code/modules/antagonists/roguetown/villain/unbound_deathknight.dm
index 897f7f48e5d..9efc5192aff 100644
--- a/code/modules/antagonists/roguetown/villain/unbound_deathknight.dm
+++ b/code/modules/antagonists/roguetown/villain/unbound_deathknight.dm
@@ -114,7 +114,7 @@
H.adjust_skillrank(/datum/skill/misc/climbing, 3, TRUE)
H.adjust_skillrank(/datum/skill/combat/shields, 4, TRUE)
H.adjust_skillrank(/datum/skill/misc/reading, 4, TRUE)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonemend)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
beltl = /obj/item/rogueweapon/scabbard/sword
belt = /obj/item/storage/belt/rogue/leather
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_shaman.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_shaman.dm
index 9f62d9fa480..108f80bd646 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_shaman.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_shaman.dm
@@ -45,7 +45,7 @@
don_pelt(H)
var/datum/devotion/C = new /datum/devotion(H, H.patron)
C.grant_miracles(H, cleric_tier = CLERIC_T4, passive_gain = CLERIC_REGEN_MINOR, start_maxed = TRUE)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)//Caustic Edit start.
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/convert_heretic/free)//Caustic Edit start.
H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/extract_heart)//Caustic Edit end.
/obj/item/clothing/suit/roguetown/armor/regenerating/skin/gnoll_armor/shaman
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_templar.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_templar.dm
index 8df87b23cde..e228dc28907 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_templar.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/antag/gnoll/gnoll_templar.dm
@@ -37,7 +37,7 @@
don_pelt(H)
var/datum/devotion/C = new /datum/devotion(H, H.patron)
C.grant_miracles(H, cleric_tier = CLERIC_T2, passive_gain = CLERIC_REGEN_MINOR, start_maxed = FALSE)
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/convert_heretic/free)
/obj/item/clothing/suit/roguetown/armor/regenerating/skin/gnoll_armor/templar
icon_state = "templar"
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/cleric.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/cleric.dm
index 466df63f6ca..27d66e9c95a 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/cleric.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/cleric.dm
@@ -691,8 +691,8 @@
if(/datum/patron/inhumen/zizo)
cloak = /obj/item/clothing/suit/roguetown/shirt/robe
head = /obj/item/clothing/head/roguetown/roguehood
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/minion_order)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/gravemark)
else
cloak = /obj/item/clothing/suit/roguetown/shirt/robe //placeholder, anyone who doesn't have cool patron drip sprites just gets generic robes
head = /obj/item/clothing/head/roguetown/roguehood
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mystic.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mystic.dm
index 59fa0e971f1..8b8209f7ec7 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mystic.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mystic.dm
@@ -154,7 +154,7 @@
H.mind.AddSpell(new /datum/action/cooldown/spell/stoneskin)
H.mind.AddSpell(new /datum/action/cooldown/spell/bestow_ward)
- var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast")
+ var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast", "Lesser Soulshot")
var/poke_choice = input(H, "Choose your offensive cantrip.", "Arcyne Training") as anything in poke_options
switch(poke_choice)
if("Spitfire")
@@ -171,6 +171,8 @@
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/arcyne_lance)
if("Lesser Gravel Blast")
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/gravel_blast/lesser)
+ if("Lesser Soulshot")
+ H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/soulshot/lesser)
var/datum/devotion/C = new /datum/devotion(H, H.patron)
C.grant_miracles(H, cleric_tier = CLERIC_T1, passive_gain = CLERIC_REGEN_MINOR, devotion_limit = CLERIC_REQ_1)
@@ -297,7 +299,7 @@
/obj/item/book/spellbook = 1,
/obj/item/chalk = 1,
)
- var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast")
+ var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast", "Lesser Soulshot")
var/poke_choice = input(H, "Choose your offensive cantrip.", "Arcyne Training") as anything in poke_options
switch(poke_choice)
if("Spitfire")
@@ -314,6 +316,8 @@
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/arcyne_lance)
if("Lesser Gravel Blast")
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/gravel_blast/lesser)
+ if("Lesser Soulshot")
+ H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/soulshot/lesser)
var/datum/devotion/C = new /datum/devotion(H, H.patron)
C.grant_miracles(H, cleric_tier = CLERIC_T1, passive_gain = CLERIC_REGEN_WITCH, devotion_limit = CLERIC_REQ_1)
if(H.mind)
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/pilgrim/witch.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/pilgrim/witch.dm
index 255569d11b5..239d81abc98 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/pilgrim/witch.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/pilgrim/witch.dm
@@ -106,7 +106,7 @@
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shapeshift/witch/cabbit)
switch (classchoice)
if("Mystagogue")
- var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast")
+ var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast", "Lesser Soulshot")
var/poke_choice = input(H, "Choose your offensive cantrip.", "Arcyne Training") as anything in poke_options
switch(poke_choice)
if("Spitfire")
@@ -123,6 +123,8 @@
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/arcyne_lance)
if("Lesser Gravel Blast")
H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/gravel_blast/lesser)
+ if("Lesser Soulshot")
+ H.mind.AddSpell(new /datum/action/cooldown/spell/projectile/soulshot/lesser)
if(H.gender == FEMALE)
armor = /obj/item/clothing/suit/roguetown/armor/corset
shirt = /obj/item/clothing/suit/roguetown/shirt/undershirt/lowcut
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_deathknight.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_deathknight.dm
index db109926d0d..31c225692b9 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_deathknight.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_deathknight.dm
@@ -88,7 +88,7 @@
if(H.mind)
H.mind.AddSpell(new /datum/action/cooldown/spell/mending)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/suicidebomb/lesser)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonemend)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
H.set_blindness(0)
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_spellblade.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_spellblade.dm
index b741083d4aa..21aefa6ec9d 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_spellblade.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/ancient_spellblade.dm
@@ -111,7 +111,7 @@
H.mind.AddSpell(new /datum/action/cooldown/spell/bind_weapon)
H.mind.AddSpell(new /datum/action/cooldown/spell/mending)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/suicidebomb/lesser)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonemend)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
H.adjust_blindness(-3)
var/helmets = list(
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/defiler.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/defiler.dm
index 618cb1814bc..a9fce30676a 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/defiler.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/defiler.dm
@@ -42,5 +42,5 @@
belt = /obj/item/storage/belt/rogue/leather/knifebelt/black/kazengun
backl = /obj/item/storage/backpack/rogue/satchel
H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/gravemark)
H.set_blindness(0)
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/heretic.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/heretic.dm
index 42546ab483f..699335198cd 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/heretic.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/heretic.dm
@@ -85,13 +85,13 @@
wretch_select_bounty(H)
// You can convert those the church has shunned.
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/convert_heretic)
H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/wound_heal)
if (istype (H.patron, /datum/patron/inhumen/zizo))
if(H.mind)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/minion_order)
H.verbs |= /mob/living/carbon/human/proc/revelations
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/gravemark)
H.mind?.current.faction += "[H.name]_faction"
ADD_TRAIT(H, TRAIT_GRAVEROBBER, TRAIT_GENERIC)
mask = /obj/item/clothing/mask/rogue/facemask/steel
@@ -389,11 +389,11 @@
if (istype (H.patron, /datum/patron/inhumen/zizo))
if(H.mind)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/minion_order)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/gravemark)
H.mind?.current.faction += "[H.name]_faction"
ADD_TRAIT(H, TRAIT_GRAVEROBBER, TRAIT_GENERIC)
- H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)
+ H.mind?.AddSpell(new /datum/action/cooldown/spell/convert_heretic)
H.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/wound_heal)
/datum/outfit/job/roguetown/wretch/hereticspy/choose_loadout(mob/living/carbon/human/H)
@@ -476,110 +476,6 @@
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/roguetown/boots/leather/reinforced, SLOT_SHOES, TRUE)
H.equip_to_slot_or_del(new /obj/item/clothing/wrists/roguetown/bracers/leather/heavy, SLOT_WRISTS, TRUE)
-/obj/effect/proc_holder/spell/invoked/convert_heretic
- name = "Convert The Downtrodden"
- desc = "Convert an soul excommunicated, cursed, or forced onto apotasy to your cause. Requires a willing participant, and takes a long time to cast."
- invocations = list("Show this lost sheep the righteous path.")
- invocation_type = "whisper"
- sound = 'sound/magic/bless.ogg'
- devotion_cost = 100
- recharge_time = 20 MINUTES
- // Long to prevent combat casting and forcing popups.
- chargetime = 10 SECONDS
- associated_skill = /datum/skill/magic/holy
- overlay_state = "convert_heretic"
-
-/obj/effect/proc_holder/spell/invoked/convert_heretic/cast(list/targets, mob/living/carbon/human/user)
- if(!HAS_TRAIT(user, TRAIT_HERESIARCH))
- to_chat(user, span_warning("You lack the knowledge for this ritual."))
- return FALSE
-
- var/mob/living/carbon/human/target = targets[1]
-
- if(!ishuman(target))
- revert_cast()
- return FALSE
-
- if(target.cmode)
- revert_cast()
- return FALSE
-
- //This SHOULD stop most heretics from being convertible and self-curing should they somehow get cursed in the future.
- if(HAS_TRAIT(target, TRAIT_HERESIARCH))
- to_chat(user, span_warning("[target] is already serving the greater good."))
- revert_cast()
- return FALSE
-
- if(alert(target, "[user.real_name] is trying to convert you to their patron, [user.patron.name]. Do you accept?", "Conversion Request", "Yes", "No") != "Yes")
- to_chat(user, span_warning("[target] refused your offer of conversion."))
- revert_cast()
- return FALSE
-
- var/absolvable = FALSE
- // Check if target qualifies for absolving
- if(HAS_TRAIT(target, TRAIT_EXCOMMUNICATED))
- absolvable = TRUE
-
- if(target.has_status_effect(/datum/status_effect/debuff/apostasy))
- target.remove_status_effect(/datum/status_effect/debuff/apostasy)
- absolvable = TRUE
-
- // Remove from global lists
- if(target.real_name in GLOB.apostasy_players)
- GLOB.apostasy_players -= target.real_name
- absolvable = TRUE
- if(target.real_name in GLOB.excommunicated_players)
- GLOB.excommunicated_players -= target.real_name
- absolvable = TRUE
-
- if(!absolvable)
- to_chat(user, span_warning("[target] doesn't bear the church's marks of shame!"))
- return
-
- // Remove divine punishments
- target.remove_status_effect(/datum/status_effect/debuff/apostasy)
- target.remove_status_effect(/datum/status_effect/debuff/excomm)
- target.remove_stress(/datum/stressevent/apostasy)
- target.remove_stress(/datum/stressevent/excommunicated)
-
- // Remove divine curses
- for(var/datum/curse/C in target.curses)
- target.remove_curse(C)
-
- // Save devotion state if exists
- var/saved_level = CLERIC_T0
- var/saved_max_progression = CLERIC_T1
- var/saved_devotion_gain = CLERIC_REGEN_MINOR
-
- if(target.devotion)
- saved_level = target.devotion.level
- saved_devotion_gain = target.devotion.passive_devotion_gain
- saved_max_progression = target.devotion.max_progression
-
- // Remove all granted spells
- if(target.patron != user.patron)
- for(var/obj/effect/proc_holder/spell/S in target.devotion.granted_spells)
- target.mind.RemoveSpell(S)
-
- target.devotion.Destroy()
-
- // Change patron
- target.patron = new user.patron.type()
- to_chat(target, span_userdanger("Your soul now belongs to [user.patron.name]!"))
-
- // Grant new devotion
- var/datum/devotion/new_devotion = new /datum/devotion(target, target.patron)
- target.devotion = new_devotion
- new_devotion.grant_miracles(target, saved_level, saved_devotion_gain, saved_max_progression)
-
- // Final conversion
- ADD_TRAIT(target, TRAIT_HERESIARCH, TRAIT_GENERIC)
- ADD_TRAIT(target, TRAIT_EXCOMMUNICATED, TRAIT_GENERIC)
- ADD_TRAIT(target, TRAIT_ZURCH, TRAIT_GENERIC)
- to_chat(user, span_danger("You've converted [target.name] to [user.patron.name]!"))
- to_chat(target, span_danger("You feel ancient powers lifting divine burdens from your soul..."))
-
- return TRUE
/mob/living/carbon/human/proc/revelations()
set name = "Revelations"
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/necromancer.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/necromancer.dm
index d7772e0fce1..e13ad8ade9c 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/necromancer.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/wretch/necromancer.dm
@@ -60,14 +60,16 @@
backr = choose_implement(H, "greater")
H.mind?.current.faction += "[H.name]_faction"
H.set_patron(/datum/patron/inhumen/zizo)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/eyebite)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonechill)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/raise_undead_formation/necromancer)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/raise_undead_guard)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/convert_heretic)
- H.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/tame_undead)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/eyebite)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonechill)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/minion_order)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/gravemark)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/raise_undead_formation/necromancer)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/raise_undead_guard/necromancer)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/convert_heretic/free)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/lacrima)
+ H.mind.AddSpell(new /datum/action/cooldown/spell/tame_undead)
H.mind.AddSpell(new /datum/action/cooldown/spell/raise_deadite)
wretch_select_bounty(H)
H.grant_language(/datum/language/undead)
diff --git a/code/modules/mob/living/carbon/stress.dm b/code/modules/mob/living/carbon/stress.dm
index df731610ff7..384d333d831 100644
--- a/code/modules/mob/living/carbon/stress.dm
+++ b/code/modules/mob/living/carbon/stress.dm
@@ -223,6 +223,13 @@
stress_freakout()*/
/mob/living/carbon/proc/stress_freakout()
+ var/determination = src.STAWIL * 4
+ if(HAS_TRAIT(src, TRAIT_STEELHEARTED) || HAS_TRAIT(src, TRAIT_PSYDONIAN_GRIT) && prob(determination))
+ if(HAS_TRAIT(src, TRAIT_PSYDONIAN_GRIT))
+ to_chat(src, span_boldred("--PRAY!! WEEP!! ENDURE!!"))
+ else
+ to_chat(src, span_boldred("Deep breaths, deep breaths... I can handle this."))
+ return
to_chat(src, span_boldred("I PANIC!!!"))
Stun(2 SECONDS)
blur_eyes(2)
diff --git a/code/modules/mob/living/living_topic.dm b/code/modules/mob/living/living_topic.dm
index b48fdcef0ec..2d17009d61f 100644
--- a/code/modules/mob/living/living_topic.dm
+++ b/code/modules/mob/living/living_topic.dm
@@ -3,7 +3,7 @@
if(href_list["check_hb"] && (observer_privilege || usr.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)))
if(!observer_privilege)
usr.visible_message(span_info("[usr] tries to hear [src]'s heartbeat."))
- if(!do_after(usr, 30, needhand = TRUE, target = src))
+ if(!do_after(usr, 2 SECONDS, needhand = TRUE, target = src))
return
var/list/following_my_heart = check_heartbeat(usr)
if(following_my_heart)
@@ -35,7 +35,7 @@
message += "[p_they(TRUE)] commited suicide... Nothing can be done..."
if(HAS_TRAIT(src, TRAIT_DNR))
message += "[p_their(TRUE)] heart will never beat again..."
- if(isobserver(user) || HAS_TRAIT(user, TRAIT_SOUL_EXAMINE) || (user.get_skill_level(/datum/skill/misc/medicine) >= SKILL_LEVEL_MASTER))
+ if(isobserver(user) || HAS_TRAIT(user, TRAIT_SOUL_EXAMINE) || HAS_TRAIT(user, TRAIT_ZIZOSIGHT) || (user.get_skill_level(/datum/skill/misc/medicine) >= SKILL_LEVEL_MASTER))
if(!key && !get_ghost(FALSE, TRUE))
message += span_deadsay("[p_their(TRUE)] soul has departed...")
else
diff --git a/code/modules/mob/living/simple_animal/hostile/simple_skeleton.dm b/code/modules/mob/living/simple_animal/hostile/simple_skeleton.dm
index 27ac233f3aa..b5f1065d84e 100644
--- a/code/modules/mob/living/simple_animal/hostile/simple_skeleton.dm
+++ b/code/modules/mob/living/simple_animal/hostile/simple_skeleton.dm
@@ -118,25 +118,29 @@
if("idle")
return pick('sound/vo/mobs/skel/skeleton_idle (1).ogg','sound/vo/mobs/skel/skeleton_idle (2).ogg','sound/vo/mobs/skel/skeleton_idle (3).ogg')
-
/mob/living/simple_animal/hostile/rogue/skeleton/Initialize(mapload, mob/user, cabal_affine = FALSE, is_summoned = FALSE)
. = ..()
+
if(user)
if(user.mind && user.mind.current)
summoner = user.mind.current.real_name
else
summoner = user.name
- if (is_summoned || cabal_affine)
- faction = list(FACTION_CABAL) //No mix undead faction and cabal, summoned skeletons can attack any undead, mark your friends
- // adds the name of the summoner to the faction, to avoid the hooded "Unknown" bug with Skeleton IDs
+
+ if(is_summoned || cabal_affine)
+ faction = list(FACTION_CABAL)
+
if(user && user.mind && user.mind.current)
- faction = list("[user.mind.current.real_name]_faction") //if you summon this, he not affected on cabal. This skeletons can attack any undead and other zizo affected characters
- // lich also gets to have friendlies, as a treat
+ faction = user.mind.current.faction.Copy()
+ faction += "[user.mind.current.real_name]_faction"
var/datum/antagonist/lich/lich_antag = user.mind.has_antag_datum(/datum/antagonist/lich)
if(lich_antag && user.real_name)
- faction = list(FACTION_UNDEAD, "[user.mind.current.real_name]_faction", "[user.real_name]_faction") //no changes. Undead faction + lich_name faction
+ faction += FACTION_UNDEAD
+ faction += "[user.real_name]_faction"
+
damage_check = world.time
- if(is_summoned) //check, if it NOT summoned skeleton, he lifetime - infinity. For mapping-spawned skeltons
+
+ if(is_summoned)
addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_animal/hostile/rogue/skeleton, deathtime), TRUE), 1 MINUTES)
/mob/living/simple_animal/hostile/rogue/skeleton/proc/deathtime()
@@ -235,6 +239,24 @@
/mob/living/simple_animal/hostile/rogue/skeleton/bow/Initialize(mapload, mob/user, cabal_affine = FALSE, is_summoned = FALSE)
. = ..(mapload, user, cabal_affine, is_summoned)
+
+/mob/living/simple_animal/hostile/rogue/skeleton/Destroy()
+ for(var/mob/living/M in viewers(10, src))
+ var/datum/ai_controller/controller = M.ai_controller
+ if(!controller)
+ continue
+ var/needs_cleanup = FALSE
+ if(controller.blackboard[BB_HIGHEST_THREAT_MOB] == src)
+ controller.clear_blackboard_key(BB_HIGHEST_THREAT_MOB)
+ needs_cleanup = TRUE
+ if(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] == src)
+ controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
+ needs_cleanup = TRUE
+ if(needs_cleanup)
+ controller.CancelActions()
+
+ return ..()
+
/mob/living/simple_animal/hostile/rogue/skeleton/ravox_ghost
name = "Ravoxian Soul"
desc = "A portion of a Ravoxian's soul. Kill it to damage and stun them. Metal."
diff --git a/code/modules/spells/minion_order.dm b/code/modules/spells/minion_order.dm
index 5f3a178691e..87498c55b1d 100644
--- a/code/modules/spells/minion_order.dm
+++ b/code/modules/spells/minion_order.dm
@@ -1,93 +1,95 @@
-/obj/effect/proc_holder/spell/invoked/minion_order
+/datum/action/cooldown/spell/minion_order
name = "Order Minions"
- desc = "Cast on turf to head in that direction ignoring all else. \
- Cast on a minion to set to aggressive, cast on self to passive and follow, cast on target to focus them. \
+ desc = "Issues commands to your summoned minions within 12 tiles. \
+ Cast on a turf to send them marching there, ignoring all else along the way. \
+ Cast on yourself to recall them - they will turn passive and follow you. \
+ Cast on an enemy to focus them; minions will pursue and attack that target. \
+ Cast on one of your own minions to toggle its stance: a passive minion becomes hostile and will hunt strangers on its own; a hostile minion calms down and reverts to follow-and-defend. \
Does not work on greater skeletons."
- range = 12
+ button_icon = 'icons/mob/actions/roguespells.dmi'
+ button_icon_state = "raiseskele"
+ cast_range = 12
associated_skill = /datum/skill/misc/athletics
- chargedrain = 1
- chargetime = 0 SECONDS
- releasedrain = 0
- recharge_time = 3 SECONDS
+ charge_required = FALSE
+ primary_resource_type = SPELL_COST_NONE
+ cooldown_time = 2 SECONDS
+ spell_requirements = SPELL_REQUIRES_SAME_Z
+ zizo_spell = TRUE
+ has_visual_effects = FALSE
+ sound = null
var/order_range = 12
- var/faction_ordering = FALSE ///this sets whether it orders mobs the user is aligned with in range or just mobs who are the character's 'friends' (ie, their summons)
+ var/faction_ordering = FALSE
-/obj/effect/proc_holder/spell/invoked/minion_order/lich //as an example, this should allow the lich to command the entire undead faction
+/datum/action/cooldown/spell/minion_order/lich
faction_ordering = TRUE
-/obj/effect/proc_holder/spell/invoked/minion_order/cast(list/targets, mob/user)
- var/mob/caster = user
- var/target = targets[1]
- var/faction_tag = "[caster.mind.current.real_name]_faction"
+/datum/action/cooldown/spell/minion_order/cast(atom/cast_on)
+ . = ..()
+ var/faction_tag = "[owner.mind.current.real_name]_faction"
- // Target is one of our own minions
- if(ismob(target) && istype(target, /mob/living/simple_animal))
- var/mob/living/simple_animal/minion = target
+ if(ismob(cast_on) && istype(cast_on, /mob/living/simple_animal))
+ var/mob/living/simple_animal/minion = cast_on
if(faction_tag in minion.faction)
- src.process_minions(order_type = "toggle_stance", target = minion, faction_tag = faction_tag)
- return
+ process_minions(order_type = "toggle_stance", target = minion, faction_tag = faction_tag)
+ return TRUE
- // Minions goto turf
- if(isturf(target))
- src.process_minions(order_type = "goto", target_location = target, faction_tag = faction_tag)
- return
+ if(isturf(cast_on))
+ process_minions(order_type = "goto", target_location = cast_on, faction_tag = faction_tag)
+ return TRUE
- // Target is the caster (set minions to passive and follow)
- else if(target == caster)
- src.process_minions(order_type = "follow", target = caster, faction_tag = faction_tag)
- return
+ if(cast_on == owner)
+ process_minions(order_type = "follow", target = owner, faction_tag = faction_tag)
+ return TRUE
- // Target is another mob
- else if(ismob(target))
- var/mob/living/mob_target = target
- if(faction_tag in mob_target.faction)//We're only checking for faction tagged individuals. Potential issue may arise with commanded mobs attacking mobs with same faction leading to cheese circumstances, but most mobs are retaliatory.
- src.process_minions(order_type = "aggressive", target = target, faction_tag = faction_tag)
- return
+ if(ismob(cast_on))
+ var/mob/living/mob_target = cast_on
+ if(faction_tag in mob_target.faction)
+ process_minions(order_type = "aggressive", target = mob_target, faction_tag = faction_tag)
else
- // Set all minions to focus on the enemy target
- src.process_minions(order_type = "attack", target = target, faction_tag = faction_tag)
- return
- else
- revert_cast()
- return
+ process_minions(order_type = "attack", target = mob_target, faction_tag = faction_tag)
+ return TRUE
+
+ reset_spell_cooldown()
+ return FALSE
-/obj/effect/proc_holder/spell/invoked/minion_order/proc/process_minions(var/order_type, turf/target_location = null, mob/living/target = null, var/faction_tag = null)
- var/mob/caster = usr
+/datum/action/cooldown/spell/minion_order/proc/process_minions(order_type, turf/target_location, mob/living/target, faction_tag)
var/count = 0
var/msg = ""
- for (var/mob/other_mob in oview(src.order_range, caster))
- if (istype(other_mob, /mob/living/simple_animal) && !other_mob.client) // Only simple_mobs for now
+ for(var/mob/other_mob in oview(order_range, owner))
+ if(istype(other_mob, /mob/living/simple_animal) && !other_mob.client)
var/mob/living/simple_animal/minion = other_mob
- if ((faction_ordering && caster.faction_check_mob(minion)) || (!faction_ordering && faction_tag && (faction_tag in minion.faction)))
- minion.ai_controller.CancelActions() //this should immediately halt present actions/orders given.
+ if((faction_ordering && owner.faction_check_mob(minion)) || (!faction_ordering && faction_tag && (faction_tag in minion.faction)))
+ minion.ai_controller.CancelActions()
minion.ai_controller.clear_blackboard_key(BB_FOLLOW_TARGET)
minion.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
minion.ai_controller.clear_blackboard_key(BB_TRAVEL_DESTINATION)
minion.ai_controller.clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST)
count += 1
- switch (order_type)
- if ("goto")
+ switch(order_type)
+ if("goto")
minion.ai_controller.set_blackboard_key(BB_TRAVEL_DESTINATION, target_location)
msg = "go to [target_location]"
- if ("follow")
+ if("follow")
minion.ai_controller.set_blackboard_key(BB_FOLLOW_TARGET, target)
msg = "follow you."
- if ("aggressive")
+ if("aggressive")
msg = "roam free."
- if ("attack")
+ if("attack")
minion.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
msg = "attack [target.name]"
if("toggle_stance")
- if(minion == target) // single minion clicked
- if("neutral" in minion.faction) // currently passive → switch to aggressive
+ if(minion == target)
+ if("neutral" in minion.faction)
minion.faction -= "neutral"
+ minion.pet_passive = FALSE
msg = "[minion.name] becomes hostile to nearby strangers."
else
minion.faction += "neutral"
+ minion.pet_passive = TRUE
msg = "[minion.name] calms down."
- if(count>0)
- to_chat(caster, "Ordered [count] minions to " + msg)
+ if(count > 0)
+ to_chat(owner, "Ordered [count] minions to [msg]")
else
- to_chat(caster, "We weren't able to order anyone.")
+ to_chat(owner, "We weren't able to order anyone.")
diff --git a/code/modules/spells/pantheon/inhumen/zizo.dm b/code/modules/spells/pantheon/inhumen/zizo.dm
index bbc7af108fc..c9ceffe3331 100644
--- a/code/modules/spells/pantheon/inhumen/zizo.dm
+++ b/code/modules/spells/pantheon/inhumen/zizo.dm
@@ -1,260 +1,551 @@
-// T0: Snuffs out fires/lights around area of the caster, greater range with higher HOLY skill
-/obj/effect/proc_holder/spell/self/zizo_snuff
+/datum/action/cooldown/spell/projectile/zizo
+ background_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ spell_color = GLOW_COLOR_ZIZO
+ ignore_armor_penalty = TRUE
+ attunement_school = null
+ primary_resource_type = SPELL_COST_DEVOTION
+ secondary_resource_type = SPELL_COST_STAMINA
+ has_visual_effects = FALSE
+ spell_impact_intensity = SPELL_IMPACT_NONE
+ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
+ associated_stat = null
+ associated_skill = /datum/skill/magic/holy
+ zizo_spell = TRUE
+ spell_tier = 0
+ point_cost = 0
+ required_items = list(/obj/item/clothing/neck/roguetown/psicross)
+
+/datum/action/cooldown/spell/zizo
+ background_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ spell_color = GLOW_COLOR_ZIZO
+ ignore_armor_penalty = TRUE
+ attunement_school = null
+ primary_resource_type = SPELL_COST_DEVOTION
+ secondary_resource_type = SPELL_COST_STAMINA
+ has_visual_effects = FALSE
+ spell_impact_intensity = SPELL_IMPACT_NONE
+ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
+ associated_stat = null
+ associated_skill = /datum/skill/magic/holy
+ zizo_spell = TRUE
+ spell_tier = 0
+ point_cost = 0
+ required_items = list(/obj/item/clothing/neck/roguetown/psicross)
+
+// SNUFF LIGHTS (T0) - Extinguishes most light sources, and grants you a temporary Dark Vision steroid that scales from your Holy skill.
+/datum/action/cooldown/spell/zizo/snuff_lights
name = "Snuff Lights"
- desc = "Extinguish all lights in range, with your Miracles skill increasing range."
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "snufflight"
- releasedrain = 10
- chargedrain = 0
- chargetime = 0
- chargedloop = /datum/looping_sound/invokeholy
- invocations = list("exhales a dark grey smog, choking any lights nearby.")
- invocation_type = "emote"
+ desc = "Extinguish most light sources within 2 range. For 5 seconds, you will also hone your Darksight. Both effects scale up from Miracle skill."
+ fluff_desc = "Flame, light, purity... all arrogant lies of the living. Wretched falsehoods peddled by the Ten to keep mortals fearful of the dark. They are intrusions; frail comforts that convince men they are safe from what waits beyond their sight. Zizo's first revelation was simple: light is not needed to see. Truth does not shine. It festers in the dark, waiting for those willing to behold it."
+ button_icon_state = "snufflight"
+ associated_stat = null
+ charge_required = FALSE
+ click_to_activate = FALSE
+ cooldown_time = 40 SECONDS
+ primary_resource_cost = 30
+ secondary_resource_cost = 10
sound = 'sound/magic/zizo_snuff.ogg'
- associated_skill = /datum/skill/magic/holy
- antimagic_allowed = FALSE
- recharge_time = 20 SECONDS
- miracle = TRUE
- devotion_cost = 30
- range = 2
+ var/snuff_range = 2
-/obj/effect/proc_holder/spell/self/zizo_snuff/cast(list/targets, mob/user = usr)
+/datum/action/cooldown/spell/zizo/snuff_lights/cast(atom/cast_on)
. = ..()
- if(!ishuman(user))
- revert_cast()
+
+ if(!ishuman(owner))
return FALSE
- var/checkrange = (range + user.get_skill_level(/datum/skill/magic/holy)) //+1 range per holy skill up to a potential of 8.
- for(var/obj/O in range(checkrange, user))
+
+ var/mob/living/L = owner
+ var/skill_level = owner.get_skill_level(/datum/skill/magic/holy)
+ var/checkrange = snuff_range + skill_level
+
+ for(var/obj/O in range(checkrange, owner))
O.extinguish()
- for(var/mob/M in range(checkrange, user))
+
+ for(var/mob/M in range(checkrange, owner))
for(var/obj/O in M.contents)
O.extinguish()
+
+ var/bonus_duration = 10 SECONDS + ((max(skill_level - 1, 0)) * 30 SECONDS)
+ L.apply_status_effect(/datum/status_effect/buff/snuff_lights, bonus_duration)
+ owner.visible_message(span_purple("[owner] exhales a cold fog that smothers nearby lights."))
return TRUE
-// T1: (fires a bone splinter at a target for brute and bleeding if you're not holding bones in your other hand, fires a significantly stronger bone lance if you are)
+/atom/movable/screen/alert/status_effect/buff/snuff_lights
+ name = "Embracing Darkness"
+ desc = "My eyes can see clearly in darkness. No secrets can hide from my prying gaze."
+ icon_state = "darkvision"
+
+/datum/status_effect/buff/snuff_lights
+ id = "snuff_lights"
+ duration = 5 SECONDS
+ status_type = STATUS_EFFECT_REPLACE
+ alert_type = /atom/movable/screen/alert/status_effect/buff/snuff_lights
+
+/datum/status_effect/buff/snuff_lights/on_creation(mob/living/new_owner, bonus_duration)
+ if(bonus_duration)
+ duration = bonus_duration
+ return ..()
+
+/datum/status_effect/buff/snuff_lights/on_apply()
+ . = ..()
+ ADD_TRAIT(owner, TRAIT_NITEVISION, "snuff_lights")
+ owner.update_sight()
+
+/datum/status_effect/buff/snuff_lights/on_remove()
+ . = ..()
+ REMOVE_TRAIT(owner, TRAIT_NITEVISION, "snuff_lights")
+ owner.update_sight()
-/obj/effect/proc_holder/spell/invoked/projectile/profane
+////////////////
+//T1 - PROFANE//
+////////////////
+/datum/action/cooldown/spell/projectile/zizo/profane
name = "Profane"
- desc = "Fire forth a splinter of unholy bone, tearing flesh and causing bleeding. If you hold pieces of bone in your other hand, you will coax a much stronger lance of bone into being."
- clothes_req = FALSE
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "profane"
- range = 8
- associated_skill = /datum/skill/magic/arcane
+ desc = "Launch a cursed bone shard that can lodge into victims, slowly poisoning them while embedded. More embedded shards increase the damage (max. 7 DMG over time, 2x vs NPCs). Four bones in your hand (or around) may be consumed to empower the projectile, causing it to fracture into nearby non-Gravemarked enemies and embed regardless."
+ fluff_desc = "An early Cabal sacrament: bone, profaned through Zizo's teachings, proved a willing conduit for Avantyne's anti-life qualities. Splinters touched by Her grace 'bless' the living with lingering agony. Fed exactly 'four' fresh bones, the rite grows unstable, scattering its sacred cruelty to ones who do not bear your mark. Why this occurs is still never fully understood."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "profane"
projectile_type = /obj/projectile/magic/profane
- chargedloop = /datum/looping_sound/invokeholy
- invocation_type = "none"
- releasedrain = 30
- chargedrain = 0
- chargetime = 15
- recharge_time = 10 SECONDS
- hide_charge_effect = TRUE // Left handed magick babe
-
-/obj/effect/proc_holder/spell/invoked/projectile/profane/miracle
- miracle = TRUE
- devotion_cost = 15
- associated_skill = /datum/skill/magic/holy
+ cast_range = SPELL_RANGE_PROJECTILE
+ primary_resource_cost = 15
+ secondary_resource_cost = 15
+ charge_required = FALSE
+ cooldown_time = 30 SECONDS
+
+/datum/action/cooldown/spell/projectile/zizo/profane/cast(atom/cast_on)
+ var/mob/living/user = owner
+ var/original_primary = primary_resource_cost
+ var/original_secondary = secondary_resource_cost
+ var/original_projectile = projectile_type
+
+ if(consume_bones_for_profane(user, 4))
+ primary_resource_cost = 0
+ secondary_resource_cost = 0
+ projectile_type = /obj/projectile/magic/profane/enhanced
+ user.visible_message(span_purple("Lingering bones crumble around [user]'s hand..."), span_purple("Lingering bones enhance your Divine evocation. Blessed four!"))
+
+ . = ..()
+ projectile_type = original_projectile
+ primary_resource_cost = original_primary
+ secondary_resource_cost = original_secondary
+
+/proc/consume_bones_for_profane(mob/living/user, amount = 4)
+ var/remaining = amount
+
+ for(var/turf/T in range(1, user))
+ if(remaining <= 0)
+ break
+ for(var/obj/item/natural/bone/B in T.contents)
+ if(remaining <= 0)
+ break
+ new /obj/item/ash(T)
+ qdel(B)
+ remaining--
+
+ for(var/obj/item/natural/bundle/bone/BB in T.contents)
+ if(remaining <= 0)
+ break
+ if(QDELETED(BB) || BB.amount <= 0)
+ continue
+ var/take = min(BB.amount, remaining)
+ BB.amount -= take
+ remaining -= take
+ new /obj/item/ash(T)
+ if(BB.amount <= 0)
+ qdel(BB)
+ else if(BB.amount == 1)
+ new /obj/item/natural/bone(get_turf(BB))
+ qdel(BB)
+
+ if(remaining > 0)
+ for(var/obj/item/natural/bone/B in user.contents)
+ if(remaining <= 0)
+ break
+ qdel(B)
+ remaining--
+
+ for(var/obj/item/natural/bundle/bone/BB in user.contents)
+ if(remaining <= 0)
+ break
+ if(QDELETED(BB) || BB.amount <= 0)
+ continue
+ var/take = min(BB.amount, remaining)
+ BB.amount -= take
+ remaining -= take
+ if(BB.amount <= 0)
+ qdel(BB)
+ else if(BB.amount == 1)
+ new /obj/item/natural/bone(user.loc)
+ qdel(BB)
+
+ return remaining <= 0
+
+/obj/item/bone/profane_splinter
+ name = "profaned splinter"
+ desc = "A jagged shard of bone pulsing with malignant energy."
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "chronobolt"
+ embedding = list("embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE)
-/obj/effect/proc_holder/spell/invoked/projectile/profane/fire_projectile(mob/living/user, atom/target)
- current_amount--
-
- var/obj/item/held_item = user.get_active_held_item()
- var/big_cast = FALSE
- if (istype(held_item, /obj/item/natural/bundle/bone))
- var/obj/item/natural/bundle/bone/bonez = held_item
- if (bonez.use(1))
- projectile_type = /obj/projectile/magic/profane/major
- big_cast = TRUE
- else if (istype(held_item, /obj/item/natural/bone))
- qdel(held_item)
- projectile_type = /obj/projectile/magic/profane/major
- big_cast = TRUE
- else if (istype(held_item, /obj/item/natural/bundle/bone))
- var/obj/item/natural/bundle/bone/boney_bundle = held_item
- if (boney_bundle.use(1))
- projectile_type = /obj/projectile/magic/profane/major
- big_cast = TRUE
-
- var/obj/projectile/P = new projectile_type(user.loc)
- P.firer = user
- P.preparePixelProjectile(target, user)
- P.fire()
-
- if (big_cast)
- user.visible_message(span_danger("[user] conjures and hurls a vicious lance of bone towards [target]!"), span_notice("I hurl a vicious lance of bone at [target]!")) //hehe. vicious lance of bone
- else
- user.visible_message(span_danger("[user] swings their arm in a wide arc, hurling a splinter of bone towards [target]!"), span_notice("I fling a shard of profaned bone at [target]!"))
+/obj/item/bone/profane_splinter/Initialize()
+ . = ..()
+ spawn(1)
+ if(QDELETED(src))
+ return
+ if(!is_embedded)
+ crumble()
+
+/obj/item/bone/profane_splinter/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(!is_embedded)
+ crumble()
- projectile_type = initial(projectile_type)
+/obj/item/bone/profane_splinter/dropped(mob/user)
+ . = ..()
+ crumble()
+
+/obj/item/bone/profane_splinter/Moved()
+ . = ..()
+ if(QDELETED(src))
+ return
+ if(!is_embedded)
+ crumble()
+
+/obj/item/bone/profane_splinter/proc/crumble()
+ if(QDELETED(src))
+ return
+ visible_message(span_purple("[src] crumbles into dust..."), span_purple("[src] crumbles into dust..."))
+ new /obj/item/ash(get_turf(src))
+ qdel(src)
+
+/obj/item/bone/profane_splinter/on_embed(obj/item/bodypart/bp)
+ . = ..()
+ if(bp?.owner)
+ var/mob/living/L = bp.owner
+ L.apply_status_effect(/datum/status_effect/debuff/profane_poison)
+ L.visible_message(span_purple("A cursed splinter buries itself deeper into [L]'s flesh!"), span_purple("The shard buries itself deep inside me!"))
+
+/datum/status_effect/debuff/profane_poison
+ id = "profane_poison"
+ status_type = STATUS_EFFECT_UNIQUE
+ duration = INFINITY
+ tick_interval = 3 SECONDS
+ var/poison_hardcap = 7
+
+/datum/status_effect/debuff/profane_poison/tick()
+ if(!owner)
+ qdel(src)
+ return
+
+ if(owner.stat == DEAD)
+ qdel(src)
+ return
+
+ if(!iscarbon(owner))
+ if(owner.stat == CONSCIOUS)
+ owner.adjustToxLoss(7)
+ return
+
+ var/mob/living/carbon/C = owner
+ var/splinter_count = 0
+
+ for(var/obj/item/bodypart/BP in C.bodyparts)
+ if(!BP.embedded_objects)
+ continue
+
+ for(var/obj/item/I in BP.embedded_objects)
+ if(istype(I, /obj/item/bone/profane_splinter))
+ splinter_count++
+
+ if(splinter_count <= 0)
+ C.visible_message(span_notice("The profane corruption fades from [C] as the final splinter is removed."), span_notice("The profane corruption fades as the final splinter is removed."))
+ qdel(src)
+ return
+
+ if(C.stat != CONSCIOUS)
+ return
+
+ var/tox_damage = min(1 + splinter_count, poison_hardcap)
+ C.adjustToxLoss(tox_damage)
+ if(!C.mind && prob(50))
+ C.adjustToxLoss(tox_damage)
+
+ if(prob(min(splinter_count * 2, 50)))
+ C.emote("pain")
+ C.Immobilize(15)
/obj/projectile/magic/profane
- name = "profaned bone splinter"
+ name = "profaned bone shard"
+ icon = 'icons/obj/projectiles.dmi'
icon_state = "chronobolt"
- damage = 20
+ damage = 15
damage_type = BRUTE
nodamage = FALSE
- var/embed_prob = 10
-
-/obj/projectile/magic/profane/major
- name = "profaned bone lance"
- damage = 35
- embed_prob = 30
+ range = SPELL_RANGE_PROJECTILE
+ speed = MAGE_PROJ_FAST
+ accuracy = 40
+ var/embed_chance = 35
/obj/projectile/magic/profane/on_hit(atom/target, blocked)
. = ..()
- if (iscarbon(target) && prob(embed_prob))
- var/mob/living/carbon/carbon_target = target
- var/obj/item/bodypart/victim_limb = pick(carbon_target.bodyparts)
- var/obj/item/bone/splinter/our_splinter = new
- victim_limb.add_embedded_object(our_splinter, FALSE, TRUE)
-/obj/item/bone/splinter
- name = "bone splinter"
- embedding = list(
- "embed_chance" = 100,
- "embedded_pain_chance" = 25,
- "embedded_fall_chance" = 5,
- )
+ if(!isliving(target))
+ qdel(src)
+ return
-/obj/item/bone/splinter/dropped(mob/user, silent)
- . = ..()
- to_chat(user, span_danger("[src] crumbles into dust..."))
+ var/mob/living/L = target
+
+ if(L.anti_magic_check())
+ visible_message(span_warning("[src] shatters harmlessly against [target]!"))
+ playsound(get_turf(target), 'sound/magic/magic_nulled.ogg', 100)
+ qdel(src)
+ return BULLET_ACT_BLOCK
+
+ try_embed_target(L)
qdel(src)
-// T2: just use lesser animate undead for now
+/obj/projectile/magic/profane/proc/try_embed_target(mob/living/L)
+ if(!prob(embed_chance))
+ return
-/obj/effect/proc_holder/spell/invoked/raise_undead_formation/miracle
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "skeleton_formation"
- miracle = TRUE
- devotion_cost = 75
- cabal_affine = TRUE
- to_spawn = 1
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
-// T2: carbon spawn
+ if(!length(C.bodyparts))
+ return
-/obj/effect/proc_holder/spell/invoked/raise_undead_guard/miracle
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "skeleton"
- name = "Raise Deadite"
- desc = "Raises a singular, weak deadite."
- chargetime = 3 SECONDS
- miracle = TRUE
- devotion_cost = 75
+ var/obj/item/bodypart/limb = pick(C.bodyparts)
+ if(!limb)
+ return
+
+ var/obj/item/bone/profane_splinter/S = new
+ limb.add_embedded_object(S, FALSE, TRUE, TRUE)
+ if(!L.has_status_effect(/datum/status_effect/debuff/profane_poison))
+ L.apply_status_effect(/datum/status_effect/debuff/profane_poison)
+ playsound(get_turf(L),pick('sound/combat/fracture/fracturedry (1).ogg','sound/combat/fracture/fracturedry (2).ogg','sound/combat/fracture/fracturedry (3).ogg'),80,TRUE)
+ return
+
+ if(istype(L, /mob/living/simple_animal))
+ if(!L.has_status_effect(/datum/status_effect/debuff/profane_poison))
+ L.apply_status_effect(/datum/status_effect/debuff/profane_poison)
+ playsound(get_turf(L),pick('sound/combat/fracture/fracturedry (1).ogg','sound/combat/fracture/fracturedry (2).ogg','sound/combat/fracture/fracturedry (3).ogg'),80,TRUE)
-// T3: tames bio_type = undead mobs
+/obj/projectile/magic/profane/enhanced
+ name = "empowered profane shard"
+ damage = 20
+ embed_chance = 100
+
+/obj/projectile/magic/profane/enhanced/on_hit(atom/target, blocked)
+ if(!isliving(target))
+ qdel(src)
+ return
+
+ var/mob/living/main_target = target
+
+ if(main_target.anti_magic_check())
+ visible_message(span_warning("[src] shatters harmlessly against [target]!"))
+ playsound(get_turf(target), 'sound/magic/magic_nulled.ogg', 100)
+ qdel(src)
+ return BULLET_ACT_BLOCK
+
+ try_embed_target(main_target)
+
+ main_target.visible_message(span_purple("[main_target] is struck as the shard fractures outward violently!"),span_purple("The shard explodes into a storm of splinters!"))
+
+ var/mob/living/caster = firer
+ var/faction_tag
+
+ if(caster)
+ faction_tag = "[caster.real_name]_faction"
+
+ if(!main_target || QDELETED(main_target))
+ qdel(src)
+ return
+
+ for(var/mob/living/L in view(5, main_target))
+ if(QDELETED(L))
+ continue
+
+ if(L.stat == DEAD)
+ continue
+
+ if(L.resting)
+ continue
+
+ if(L == main_target)
+ continue
+
+ if(L == caster)
+ continue
-/obj/effect/proc_holder/spell/invoked/tame_undead/miracle
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "deadite_tame"
+ if(faction_tag)
+ if(L.mind?.current)
+ if(faction_tag in L.mind.current.faction)
+ continue
+ else
+ if(faction_tag in L.faction)
+ continue
+
+ main_target.Beam(L, icon_state = "chronobolt", icon = 'icons/obj/projectiles.dmi', time = 5, maxdistance = 20)
+ playsound(get_turf(L),pick('sound/combat/fracture/fracturedry (1).ogg','sound/combat/fracture/fracturedry (2).ogg','sound/combat/fracture/fracturedry (3).ogg'),80,TRUE)
+ playsound(get_turf(L),'sound/combat/hits/bladed/genstab (1).ogg',50,TRUE)
+ try_embed_target(L)
+
+ qdel(src)
+
+// RAISE LESSER SKELETON (T2) - The new 'main' Zizo undeath-raising skill. Summon's durability scales from Miracle skill.
+/datum/action/cooldown/spell/raise_undead_formation/zizo
+ button_icon_state = "skeleton"
+ name = "Raise Lesser Skeleton"
+ desc = "Invoke raw Enochian magicka to bind loose bones into a simple skeletal thrall. Its crude physiology is held together purely by magic; unable to be incapacitated, it shall stand until it crumbles into spare bones. It is also simpler to control, so you can order it to move, guard or attack manually."
+ fluff_desc = "The faithful of Zizo do not raise the dead, they mock life by proving how little of it is truly required. Flesh decays, thought falters, and souls flee screaming into the arms of Necra, yet bone remains obedient. Through the language of ancient Enochian words of power, scattered remains are lashed together into a parody of mortal form, animated not by purpose or memory, but by the simple joy of defying the natural order."
+ spell_color = GLOW_COLOR_ZIZO
+ primary_resource_cost = 60
+ secondary_resource_cost = 40
+ charge_required = TRUE
+ weapon_cast_penalized = TRUE
+ charge_time = 2 SECONDS
+ charge_drain = 1
+ charge_slowdown = CHARGING_SLOWDOWN_SMALL
+ charge_sound = 'sound/magic/chargingold.ogg'
+ cooldown_time = 30 SECONDS
+ cabal_affine = TRUE
miracle = TRUE
- devotion_cost = 100
+ to_spawn = 1
+ invocation_type = null
+ invocations = null
+ associated_skill = /datum/skill/magic/holy
+
+// TAME UNDEAD (T3) - I don't know why this is a T3, being just a forced Gravemark on a hostile NPC undead.
+/datum/action/cooldown/spell/tame_undead/zizo
+ associated_skill = /datum/skill/magic/holy
+ primary_resource_cost = 100
// T3: Rituos - Zizo's Lesser Work. A single painful ritual that grants the caster a choice:
-// Progress: Arcyne knowledge (2 minor aspects, 4 utilities). No skeletonization.
-// Unlife: Full skeletonization + MOB_UNDEAD, grants bonechill and raise_deadite directly.
+// Progress: Arcyne knowledge (2 minor aspects, 4 utilities). No skeletonization. -- Kunai: I made this more distinctive from Undeath, now it also gives you some traits to give a better progress vibe.
+// Unlife: Full skeletonization + MOB_UNDEAD, grants bonechill and raise_deadite directly. -- Kunai: We already have raise_deadite, so it's a moot point to give them the Necromancer version of it. Just gave them bonemend and a few more traits to give the vibe of a 'half-lich'.
// Both paths grant undead language and TRAIT_ARCYNE. One-time use - cannot be cast again after completion.
-/obj/effect/proc_holder/spell/invoked/rituos
+/datum/action/cooldown/spell/zizo/rituos
name = "Rituos"
desc = "Enact one of the Lesser Work of Zizo - a single, agonizing ritual that tears open a path to power. Choose Progress to gain arcyne knowledge, or Unlife to embrace undeath."
- clothes_req = FALSE
- action_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_icon = 'icons/mob/actions/zizomiracles.dmi'
- overlay_state = "rituos"
- associated_skill = /datum/skill/magic/arcane
- chargedloop = /datum/looping_sound/invokeholy
- chargedrain = 0
- chargetime = 50
- releasedrain = 90
- no_early_release = TRUE
- movement_interrupt = TRUE
- recharge_time = 5 MINUTES
- hide_charge_effect = TRUE
-
-/obj/effect/proc_holder/spell/invoked/rituos/miracle
- miracle = TRUE
- devotion_cost = 120
- associated_skill = /datum/skill/magic/holy
+ fluff_desc = "The holiest of Zizo's Lesser Works among the Cabal. A rite of surrendering weakness and mortality to embrace your purpose in Her design. Through agony, the faithful offer either mind or flesh, allowing Zizo to strip away mortal frailty and shape them into reflections of her ascension. Some surrender thought for forbidden understanding. Others surrender flesh for the stillness of unlife. Few endure enough to become what She envisioned. When the gifts fade, the faithful are taught only one truth: they have not sacrificed enough."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "rituos"
+ charge_sound = 'sound/magic/chargingold.ogg'
+ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC | SPELL_REQUIRES_NO_MOVE
+ click_to_activate = FALSE
+ self_cast_possible = TRUE
+ charge_message = "ZIZO! ZIZO! ZIZO!"
+ charge_required = TRUE
+ charge_time = 10 SECONDS
+ charge_slowdown = CHARGING_SLOWDOWN_HEAVY
+ cooldown_time = 3 MINUTES
+ primary_resource_cost = 100
+ secondary_resource_cost = 100
+ sound = 'sound/magic/swap.ogg'
+
+
+/datum/action/cooldown/spell/zizo/rituos/cast(atom/cast_on)
+ . = ..()
+ if(!ishuman(owner))
+ return FALSE
-/obj/effect/proc_holder/spell/invoked/rituos/cast(list/targets, mob/living/carbon/human/user)
+ var/mob/living/carbon/human/user = owner
var/path_choice = tgui_alert(user, "What path of the Lesser Work do you seek?", "THE LESSER WORK", list("Progress", "Unlife", "Cancel"))
if(!path_choice || path_choice == "Cancel")
+ reset_spell_cooldown()
return FALSE
- // The chant - path-specific invocations
user.visible_message(span_boldwarning("[user] throws back [user.p_their()] head, arcyne energy crackling across [user.p_their()] body!"))
+ user.grant_language(/datum/language/undead)
+
var/list/chant_lines
switch(path_choice)
if("Progress")
chant_lines = list(
- "ZIZO! ZIZO! ZIZO! GRANT ME INSIGHT UNSHACKLED!",
- "STRIP ME OF STAGNATION AND IGNORANCE!",
- "I OFFER THIS MIND TO COMPLETE THY WORK!",
+ ",w ZIZO! ZIZO! ZIZO! GRANT ME INSIGHT UNSHACKLED!",
+ ",w STRIP ME OF STAGNATION AND IGNORANCE!",
+ ",w BREAK THE CHAINS OF FALSE UNDERSTANDING!",
+ ",w LET REVELATION FLOOD THIS FRAIL MIND!",
+ ",w I OFFER THIS MIND TO COMPLETE THY WORK!",
)
if("Unlife")
chant_lines = list(
- "ZIZO! ZIZO! ZIZO! FLENSE FLESH FROM MY BONE!",
- "STRIP ME OF MORTALITY'S SHACKLE!",
- "I OFFER THIS VESSEL TO THY LESSER WORK!",
+ ",w ZIZO! ZIZO! ZIZO! FLENSE FLESH FROM MY BONE!",
+ ",w STRIP ME OF MORTALITY'S SHACKLE!",
+ ",w LET THIS FRAIL MORTALITY FALL AWAY FROM PURPOSE!",
+ ",w REMAKE ME IN DEATH'S ENDURING IMAGE!",
+ ",w I OFFER THIS VESSEL TO COMPLETE THY WORK!",
)
for(var/i in 1 to length(chant_lines))
user.say(chant_lines[i], forced = "spell", language = /datum/language/common)
user.adjustBruteLoss(15)
if(path_choice == "Progress")
- user.emote(pick("whimper", "gasp"))
- user.emote("painscream")
+ user.emote(pick("whimper", "painmoan", "gag", "choke"))
else
- user.emote("painscream")
+ user.emote(pick("painscream", "agony", "paincrit", "choke"))
if(i > 1)
shake_camera(user, i * 2, i)
if(!do_after(user, 3 SECONDS, target = user))
to_chat(user, span_warning("The ritual collapses. Zizo's gaze turns away."))
return FALSE
- user.grant_language(/datum/language/undead)
ADD_TRAIT(user, TRAIT_ARCYNE, "[type]")
switch(path_choice)
- if("Progress")
+ if("Progress") // support path, your mind is twisted in Her design
user.adjust_skillrank(/datum/skill/magic/arcane, 3, TRUE)
if(user.mind)
- user.mind.setup_mage_aspects(list("mastery" = FALSE, "major" = 0, "minor" = 2, "utilities" = 4))
+ user.mind.setup_mage_aspects(list("mastery" = FALSE, "major" = 0, "minor" = 2, "utilities" = 6))
+ ADD_TRAIT(user, TRAIT_STEELHEARTED, "[type]") // so you can commit atrocities with a smile
+ ADD_TRAIT(user, TRAIT_JACKOFALLTRADES, "[type]") // the progress palooza to let you grind more efficiently
+ ADD_TRAIT(user, TRAIT_SELF_SUSTENANCE, "[type]") // also fitting for the progress vibe, way more balanced than the specialist traits IMO
grant_poke_spell(user)
user.visible_message(span_boldwarning("Arcyne runes sear themselves across [user]'s skin, glowing with a sickly light before fading beneath the flesh!"), span_notice("THE LESSER WORK IS DONE! Arcyne knowledge floods my mind - I can see the threads of magic itself!"))
- if("Unlife")
+ if("Unlife") // combat path, your body is now carries undeath resilience
user.mob_biotypes |= MOB_UNDEAD
- ADD_TRAIT(user, TRAIT_NOHUNGER, "[type]")
- ADD_TRAIT(user, TRAIT_NOBREATH, "[type]")
+ ADD_TRAIT(user, TRAIT_NOMOOD, "[type]") // undead apathy
+ ADD_TRAIT(user, TRAIT_NOPAIN, "[type]") // you have no flesh
+ ADD_TRAIT(user, TRAIT_NOHUNGER, "[type]") // you have no stomach
+ ADD_TRAIT(user, TRAIT_NOBREATH, "[type]") // you have no lungs
+ ADD_TRAIT(user, TRAIT_TOXIMMUNE, "[type]") // just in case NOBLOOD is not enough
+ ADD_TRAIT(user, TRAIT_BLOODLOSS_IMMUNE, "[type]") // just in case NOBLOOD is not enough
+ ADD_TRAIT(user, TRAIT_LIMBATTACHMENT, "[type]") // cause old Rituos let you recreate your skeleton limbs, but since this one deletes the spell after use, this is the best way to make it level
+ ADD_TRAIT(user, TRAIT_ZOMBIE_IMMUNE, "[type]") // cause it makes no sense
+ ADD_TRAIT(user, TRAIT_SILVER_WEAK, "[type]") // must have
for(var/obj/item/bodypart/part as anything in user.bodyparts)
if(istype(part, /obj/item/bodypart/head))
continue
part.skeletonize(FALSE)
+ user.update_body_parts()
+ playsound(user.loc, 'sound/misc/smelter_sound.ogg', 50, FALSE)
+ sleep(15)
var/obj/item/bodypart/torso = user.get_bodypart(BODY_ZONE_CHEST)
+ playsound(user.loc, 'sound/misc/lava_death.ogg', 100, FALSE)
torso?.skeletonize(FALSE)
user.update_body_parts()
user.adjust_skillrank(/datum/skill/magic/arcane, 3, TRUE)
if(user.mind)
user.mind.setup_mage_aspects(list("mastery" = FALSE, "major" = 0, "minor" = 2, "utilities" = 4))
- user.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/bonechill)
+ user.mind.AddSpell(new /datum/action/cooldown/spell/bonechill)
+ user.mind.AddSpell(new /datum/action/cooldown/spell/bonemend)
grant_poke_spell(user)
- user.visible_message(span_boldwarning("[user]'s flesh sloughs away in sheets, revealing bare bone beneath as [user.p_they()] [user.p_are()] consumed by the Lesser Work!"), span_notice("THE LESSER WORK IS DONE! My flesh is forfeit - but death itself answers my call!"))
- to_chat(user, span_small("...what have I done?"))
+ user.visible_message(span_boldwarning("[user]'s skin and flesh burns away in necrotic flames, revealing bare bone beneath as [user.p_they()] [user.p_are()] consumed by the Lesser Work!"), span_notice("THE LESSER WORK IS DONE! My flesh is forfeit - and death itself answers my call!"))
+ to_chat(user, span_purple("You finished Rituos to perfection, you should be a full-fledged Lich now, but..."))
+ sleep(30)
+ to_chat(user, "...Vestiges of mortality still cling to me...? Why?")
- // The Lesser Work is done - remove the spell
user.mind?.RemoveSpell(src)
qdel(src)
+ return TRUE
-/obj/effect/proc_holder/spell/invoked/rituos/proc/grant_poke_spell(mob/living/carbon/human/user)
- var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast")
+/datum/action/cooldown/spell/zizo/rituos/proc/grant_poke_spell(mob/living/carbon/human/user)
+ var/list/poke_options = list("Spitfire", "Frost Bolt", "Arc Bolt", "Greater Arcyne Bolt", "Stygian Efflorescence", "Arcyne Lance", "Lesser Gravel Blast", "Lesser Soulshot")
var/poke_choice = tgui_input_list(user, "Choose your offensive cantrip.", "Arcyne Awakening", poke_options)
if(!poke_choice || !user.mind)
return
@@ -273,3 +564,190 @@
user.mind.AddSpell(new /datum/action/cooldown/spell/projectile/arcyne_lance)
if("Lesser Gravel Blast")
user.mind.AddSpell(new /datum/action/cooldown/spell/projectile/gravel_blast/lesser)
+ if("Lesser Soulshot")
+ user.mind.AddSpell(new /datum/action/cooldown/spell/projectile/soulshot/lesser)
+
+/// T3: Bone Cataclysm - Pretty much pops your summons into sad remains of their former selves. Shouldn't do a lot of damage, but it frags someone with bone splinters if they're close enough.
+/datum/action/cooldown/spell/zizo/bone_cataclysm
+ name = "Bone Cataclysm"
+ desc = "Detonate all of your nearby skeletons in a wave of profane bone shrapnel. You and Gravemarked allies will not be harmed by it.
If used outside Combat Mode, you will disintegrate them and restore your energy."
+ fluff_desc = "Zizo taught her faithful that the dead must always serve twice: once in unlife, and once more when their bones are shattered in her name."
+ button_icon = 'icons/mob/actions/actions_clockcult.dmi'
+ button_icon_state = "Kindle"
+ click_to_activate = FALSE
+ self_cast_possible = TRUE
+ charge_required = TRUE
+ charge_time = 3 SECONDS
+ charge_slowdown = CHARGING_SLOWDOWN_HEAVY
+ charge_message = "I begin unraveling my undead servants..."
+ cooldown_time = 1.5 MINUTES
+ primary_resource_cost = 50
+ secondary_resource_cost = 50
+ invocations = list("Solve ossa, redite ad pulverem!")
+ invocation_type = INVOCATION_SHOUT
+ sound = 'sound/magic/swap.ogg'
+
+/datum/action/cooldown/spell/zizo/bone_cataclysm/cast(atom/cast_on)
+ . = ..()
+ var/list/valid_skeletons = list()
+ var/faction_tag = "[REF(owner)]_faction"
+ var/mob/living/caster = owner
+ for(var/mob/living/L in view(9, owner))
+ if(QDELETED(L))
+ continue
+ if(L.stat == DEAD)
+ continue
+ if(istype(L, /mob/living/simple_animal/hostile/rogue/skeleton))
+ var/mob/living/simple_animal/hostile/rogue/skeleton/S = L
+ if(S.summoner != owner.real_name)
+ continue
+ valid_skeletons += S
+ continue
+
+ if(istype(L, /mob/living/carbon/human/species/skeleton))
+ if(L.mind?.current)
+ if(!(faction_tag in L.mind.current.faction))
+ continue
+ else
+ if(!(faction_tag in L.faction))
+ continue
+ valid_skeletons += L
+
+ if(!valid_skeletons.len)
+ owner.balloon_alert(owner, "No bound skeletons nearby!")
+ return FALSE
+
+ if(owner.cmode)
+ owner.visible_message(span_danger("[owner] raises their hand as nearby skeletons begin violently rattling apart!"), span_userdanger("I prime my undead servants to violently explode."))
+ for(var/mob/living/S in valid_skeletons)
+ S.Jitter(100)
+ var/datum/beam/B = caster.Beam(S, icon_state = "necra_beam", time = 50, maxdistance = 20)
+ addtimer(CALLBACK(src, PROC_REF(explode_skeleton), S, caster, B), rand(3 SECONDS, 6 SECONDS))
+
+ return TRUE
+
+ else
+ owner.visible_message(span_danger("[owner] raises their hand as nearby skeletons begin calmly rattling apart!"), span_userdanger("I sacrifice my undead servants, and sap their energy."))
+ for(var/mob/living/S in valid_skeletons)
+ S.Jitter(100)
+ var/datum/beam/B = caster.Beam(S,icon_state = "necra_beam", time = 30, maxdistance = 20)
+ addtimer(CALLBACK(src, PROC_REF(despawn_skeleton), S, caster, B), rand(2 SECONDS, 3 SECONDS))
+
+ return TRUE
+
+/obj/item/bone/splinter
+ name = "bone splinter"
+ desc = "A jagged shard of shattered bone."
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "chronobolt"
+ embedding = list("embed_chance" = 100, "embedded_pain_chance" = 45, "embedded_fall_chance" = 0, "embedded_bloodloss" = 0, "embedded_ignore_throwspeed_threshold" = TRUE)
+
+/datum/action/cooldown/spell/zizo/bone_cataclysm/proc/explode_skeleton(mob/living/S, mob/living/caster, datum/beam/B)
+ if(B)
+ B.End()
+ if(!S || QDELETED(S))
+ return
+ if(!caster || QDELETED(caster))
+ return
+
+ var/turf/T = get_turf(S)
+ if(!T)
+ return
+
+ var/faction_tag = "[caster.real_name]_faction"
+
+ S.visible_message(span_danger("[S] erupts into a storm of bone fragments!"))
+ new /obj/effect/temp_visual/explosion(T)
+ playsound(T, 'sound/misc/explode/explosion.ogg', 50)
+
+// Repulse copypasta for more chupatz, will affect you too, just not do damage.
+ var/list/thrownatoms = list()
+ for(var/turf/nearby in get_hear(1, T))
+ for(var/atom/movable/AM in nearby)
+ thrownatoms += AM
+ for(var/atom/movable/AM in thrownatoms)
+ if(QDELETED(AM))
+ continue
+ if(AM == S)
+ continue
+ if(AM.anchored)
+ continue
+ if(isliving(AM))
+ var/mob/living/M = AM
+ if(M == owner)
+ continue
+ if(M.mind?.current)
+ if(faction_tag in M.mind.current.faction)
+ continue
+ else
+ if(faction_tag in M.faction)
+ continue
+ if(!M.mind && M.resting && M.stat != CONSCIOUS) // to finish off NPCs in a cooler way
+ M.gib(TRUE, TRUE, TRUE, FALSE)
+ if(!M.mind)
+ M.Stun(50)
+ M.set_resting(TRUE, TRUE)
+ to_chat(M, span_danger("The blast hurls you backwards!"))
+ var/atom/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(AM, T)))
+ AM.safe_throw_at(throwtarget, 2, 1, owner, force = MOVE_FORCE_EXTREMELY_STRONG)
+
+ for(var/mob/living/carbon/C in view(4, T))
+ if(C.stat == DEAD && C.mind)
+ continue
+ if(C == owner)
+ continue
+ if(C.mind?.current)
+ if(faction_tag in C.mind.current.faction)
+ continue
+ else
+ if(faction_tag in C.faction)
+ continue
+
+ var/dist = get_dist(C, T)
+ var/min_splinters
+ var/max_splinters
+
+ switch(dist)
+ if(0,1)
+ min_splinters = 3
+ max_splinters = 4
+ if(2)
+ min_splinters = 1
+ max_splinters = 3
+ if(3)
+ min_splinters = 1
+ max_splinters = 2
+ else
+ continue
+ var/splinter_count = rand(min_splinters, max_splinters)
+ C.adjustBruteLoss(rand(10,20))
+
+ for(var/i in 1 to splinter_count)
+ if(!length(C.bodyparts))
+ break
+ var/obj/item/bodypart/limb = pick(C.bodyparts)
+ var/obj/item/bone/splinter/P = new
+ limb.add_embedded_object(P, FALSE, TRUE)
+ C.apply_status_effect(/datum/status_effect/debuff/clickcd, 8 SECONDS)
+ C.apply_status_effect(/datum/status_effect/debuff/exposed, 10 SECONDS)
+ to_chat(C, span_userdanger("Bone splinters bury themselves deep into your flesh!"))
+ new /obj/effect/decal/remains/human(T)
+ qdel(S)
+
+/datum/action/cooldown/spell/zizo/bone_cataclysm/proc/despawn_skeleton(mob/living/S, mob/living/caster, datum/beam/B)
+ if(B)
+ B.End()
+ if(!S || QDELETED(S))
+ return
+ if(!caster || QDELETED(caster))
+ return
+ var/turf/T = get_turf(S)
+ if(!T)
+ return
+ S.visible_message(span_warning("[S] crumbles apart into pale dust as its essence is siphoned away!"), span_warning("Ashes to ashes, dust to dust..."))
+ playsound(T, 'sound/magic/swap.ogg', 50, TRUE)
+ caster.energy_add(100)
+ caster.stamina_add(-50)
+ new /obj/item/ash(T)
+ new /obj/item/ash(T)
+ qdel(S)
diff --git a/code/modules/spells/roguetown/acolyte/abyssor.dm b/code/modules/spells/roguetown/acolyte/abyssor.dm
index 35557e0ea88..b2e6d7d938e 100644
--- a/code/modules/spells/roguetown/acolyte/abyssor.dm
+++ b/code/modules/spells/roguetown/acolyte/abyssor.dm
@@ -470,8 +470,8 @@
. = ..()
var/turf/T = get_turf(targets[1])
if(isopenturf(T))
- if(!user.mind.has_spell(/obj/effect/proc_holder/spell/invoked/minion_order))
- user.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
+ if(!user.mind.has_spell(/datum/action/cooldown/spell/minion_order))
+ user.mind.AddSpell(new /datum/action/cooldown/spell/minion_order)
QDEL_NULL(summoned)
summoned = new /mob/living/simple_animal/hostile/retaliate/rogue/mossback(T, user, townercrab)
return TRUE
diff --git a/code/modules/spells/roguetown/acolyte/astrata.dm b/code/modules/spells/roguetown/acolyte/astrata.dm
index 431b6c91f25..866e17782f6 100644
--- a/code/modules/spells/roguetown/acolyte/astrata.dm
+++ b/code/modules/spells/roguetown/acolyte/astrata.dm
@@ -186,11 +186,13 @@
for(var/obj/structure/fluff/psycross/S in oview(5, user))
S.AOE_flash(user, range = 8)
if(target.mob_biotypes & MOB_UNDEAD) //positive energy harms the undead
- target.visible_message(
- span_danger("[target] is unmade by holy light!"),
- span_userdanger("I'm unmade by holy light!")
- )
- target.gib()
+ if(alert(user, "[target]'s body rattles and seizes under the divine force. This will likely unmake them permanently. Continue?", "Divine Revival", "PURGE THE UNCLEAN!", "Stop") != "PURGE THE UNCLEAN!")
+ to_chat(user, span_notice("You halt the rite before the divine force can fully take hold."))
+ revert_cast()
+ return FALSE
+ target.visible_message(span_danger("[target] is unmade by divine magic!"), span_userdanger("Holy power tears my undead form apart!"))
+ playsound(target.loc, 'sound/magic/churn.ogg', 100, TRUE)
+ target.dust()
return TRUE
if(alert(target, "They are calling for you. Are you ready?", "Revival", "I need to wake up", "Don't let me go") != "I need to wake up")
target.visible_message(span_notice("Nothing happens. They are not being let go."))
diff --git a/code/modules/spells/roguetown/acolyte/ravox.dm b/code/modules/spells/roguetown/acolyte/ravox.dm
index be6488815e1..d341f4b92f7 100644
--- a/code/modules/spells/roguetown/acolyte/ravox.dm
+++ b/code/modules/spells/roguetown/acolyte/ravox.dm
@@ -583,11 +583,11 @@ GLOBAL_LIST_EMPTY(arenafolks) // we're just going to use a list and add to it. S
if(!("[user.mind.current.real_name]_faction" in user.faction)) //FUCK VVV
user.faction |= "[user.mind.current.real_name]_faction"
- if(!locate(/obj/effect/proc_holder/spell/invoked/gravemark) in user.mind?.spell_list) //OFF VVV
- user.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark/no_sprite)
+ if(!locate(/datum/action/cooldown/spell/gravemark) in user.mind?.spell_list) //OFF VVV
+ user.mind?.AddSpell(new /datum/action/cooldown/spell/gravemark/no_sprite)
- if(!locate(/obj/effect/proc_holder/spell/invoked/minion_order) in user.mind?.spell_list) //SPELLGRANT IN CLASS FILE
- user.mind?.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
+ if(!locate(/datum/action/cooldown/spell/minion_order) in user.mind?.spell_list) //SPELLGRANT IN CLASS FILE
+ user.mind?.AddSpell(new /datum/action/cooldown/spell/minion_order)
var/skill = user.get_skill_level(/datum/skill/magic/holy)
var/time = 1 MINUTES
diff --git a/code/modules/spells/roguetown/acolyte/resurrect.dm b/code/modules/spells/roguetown/acolyte/resurrect.dm
index c68faded86d..7d23eb4377d 100644
--- a/code/modules/spells/roguetown/acolyte/resurrect.dm
+++ b/code/modules/spells/roguetown/acolyte/resurrect.dm
@@ -28,6 +28,7 @@
var/debuff_type = /datum/status_effect/debuff/revived
var/structure_range = 1
var/harms_undead = TRUE
+ var/zizo = FALSE
priest_excluded = TRUE
/obj/effect/proc_holder/spell/invoked/resurrect/start_recharge()
@@ -54,29 +55,30 @@
revert_cast()
return FALSE
- var/found_structure = FALSE
- var/list/search_area = oview(structure_range, target)
-
- for(var/atom/A in search_area)
- // Check if the atom itself is the required structure type
- if(istype(A, required_structure))
- found_structure = TRUE
- break
-
- if(istype(A, /turf))
- var/turf/T = A
- for(var/obj/O in T.contents)
- if(istype(O, required_structure))
- found_structure = TRUE
- break // Found it in the turf, no need to check further
- if(found_structure)
- break
-
- if(!found_structure)
- var/atom/temp_structure = required_structure
- to_chat(user, span_warning("I need a holy [initial(temp_structure.name)] near [target]."))
- revert_cast()
- return FALSE
+ if(!zizo)
+ var/found_structure = FALSE
+ var/list/search_area = oview(structure_range, target)
+
+ for(var/atom/A in search_area)
+ // Check if the atom itself is the required structure type
+ if(istype(A, required_structure))
+ found_structure = TRUE
+ break
+
+ if(istype(A, /turf))
+ var/turf/T = A
+ for(var/obj/O in T.contents)
+ if(istype(O, required_structure))
+ found_structure = TRUE
+ break // Found it in the turf, no need to check further
+ if(found_structure)
+ break
+
+ if(!found_structure)
+ var/atom/temp_structure = required_structure
+ to_chat(user, span_warning("I need a holy [initial(temp_structure.name)] near [target]."))
+ revert_cast()
+ return FALSE
//OV edit
if(istype(target, /mob/living/simple_animal/hostile/retaliate/rogue/ooze_blob/suffering))
target.revive()
@@ -85,13 +87,17 @@
if(!target.check_revive(user))
revert_cast()
return FALSE
+
if(target.mob_biotypes & MOB_UNDEAD && harms_undead) //positive energy harms the undead
- target.visible_message(
- span_danger("[target] is unmade by divine magic!"),
- span_userdanger("I'm unmade by divine magic!")
- )
- target.gib()
+ if(alert(user, "[target]'s body rattles and seizes under the divine force. This will likely unmake them permanently. Continue?", "Divine Revival", "PURGE THE UNCLEAN!", "Stop") != "PURGE THE UNCLEAN!")
+ to_chat(user, span_notice("You halt the rite before the divine force can fully take hold."))
+ revert_cast()
+ return FALSE
+ target.visible_message(span_danger("[target] is unmade by divine magic!"), span_userdanger("Holy power tears my undead form apart!"))
+ playsound(target.loc, 'sound/magic/churn.ogg', 100, TRUE)
+ target.dust()
return TRUE
+
to_chat(user, span_notice("You feel the energies of life flow through you, and into [target.name]... All that's left is hope for the best...")) //CC Edit: Feedback
if(alert(target, "They are calling for you. Are you ready?", "Revival", "I need to wake up", "Don't let me go") != "I need to wake up")
target.visible_message(span_notice("Nothing happens. They are not being let go."))
@@ -133,6 +139,21 @@
return TRUE
/obj/effect/proc_holder/spell/invoked/resurrect/proc/validate_items(atom/center)
+ // Zizo revivals require nearby bleeding sacrifice instead of items
+ if(zizo)
+ for(var/mob/living/L in range(1, center))
+ if(L == center)
+ continue
+ if(QDELETED(L))
+ continue
+ if(L.stat == DEAD)
+ continue
+
+ if(L.get_bleed_rate() > 0)
+ return ""
+
+ return "A living, bleeding victim"
+
var/list/current_required_items = get_current_required_items()
var/list/available_items = list()
var/list/missing_items = list()
diff --git a/code/modules/spells/roguetown/necromancer/bone_chill.dm b/code/modules/spells/roguetown/necromancer/bone_chill.dm
index 6d85509fd5e..952e5f62290 100644
--- a/code/modules/spells/roguetown/necromancer/bone_chill.dm
+++ b/code/modules/spells/roguetown/necromancer/bone_chill.dm
@@ -1,32 +1,29 @@
-/obj/effect/proc_holder/spell/invoked/bonechill
+/datum/action/cooldown/spell/bonechill
name = "Bone Chill"
- desc = "Chill the chosen target with a burst of necrotic magicka. Applies a strong slowdown effect to the chosen target, alongside further reducing their Strength and Speed."
- cost = 3
- overlay_state = "profane"
- releasedrain = 30
- chargetime = 5
- range = 7
- warnie = "sydwarning"
- movement_interrupt = FALSE
- chargedloop = null
+ desc = "Chill the chosen target with a burst of necrotic magicka. Applies a strong slowdown effect to the chosen target, alongside further reducing their Strength and Speed."
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "bonechill"
+ cast_range = 7
sound = 'sound/magic/whiteflame.ogg'
spell_tier = 2
- chargedloop = /datum/looping_sound/invokegen
+ primary_resource_cost = SPELLCOST_MAJOR_PROJECTILE
+ primary_resource_type = SPELL_COST_STAMINA
+ charge_required = TRUE
+ charge_time = 5
associated_skill = /datum/skill/magic/arcane
- gesture_required = TRUE // Potential offensive use, need a target
- antimagic_allowed = TRUE
- recharge_time = 15 SECONDS
- miracle = FALSE
+ cooldown_time = 15 SECONDS
+ spell_requirements = SPELL_REQUIRES_SAME_Z
+ self_cast_possible = FALSE
zizo_spell = TRUE
-/obj/effect/proc_holder/spell/invoked/bonechill/cast(list/targets, mob/living/user)
- ..()
- if(!isliving(targets[1]))
- return FALSE
+/datum/action/cooldown/spell/bonechill/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
- var/mob/living/target = targets[1]
- if(target.mob_biotypes & MOB_UNDEAD) //positive energy harms the undead
- var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(user.zone_selected))
+/datum/action/cooldown/spell/bonechill/cast(atom/cast_on)
+ . = ..()
+ var/mob/living/target = cast_on
+ if(target.mob_biotypes & MOB_UNDEAD)
+ var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(owner.zone_selected))
if(affecting && (affecting.heal_damage(50, 50) || affecting.heal_wounds(50)))
target.update_damage_overlays()
target.visible_message(span_danger("[target] reforms under the vile energy!"), span_notice("I'm remade by dark magic!"))
@@ -37,5 +34,4 @@
target.apply_status_effect(/datum/status_effect/debuff/chilled)
else
target.adjustBruteLoss(20)
-
return TRUE
diff --git a/code/modules/spells/roguetown/necromancer/bone_mend.dm b/code/modules/spells/roguetown/necromancer/bone_mend.dm
index 698ca4002f7..fcdbe22ffbb 100644
--- a/code/modules/spells/roguetown/necromancer/bone_mend.dm
+++ b/code/modules/spells/roguetown/necromancer/bone_mend.dm
@@ -1,35 +1,28 @@
-// Copy paste of Bone Chill but healing only with a doafter
-/obj/effect/proc_holder/spell/invoked/bonemend
+/datum/action/cooldown/spell/bonemend
name = "Bone Mend"
- desc = "Mend the chosen target's bones with a burst of necrotic magick. Requires standing still for a few seconds"
- cost = 3
- overlay_state = "profane"
- releasedrain = 50
- chargetime = 5 SECONDS // Make in combat usage harder
- range = 2
- warnie = "sydwarning"
- movement_interrupt = FALSE
- chargedloop = null
+ desc = "Mend the chosen target's bones with a burst of necrotic magick. Requires standing still for a few seconds."
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "skeleton"
+ cast_range = 2
sound = 'sound/magic/whiteflame.ogg'
- spell_tier = 2
- chargedloop = /datum/looping_sound/invokegen
+ charge_required = TRUE
+ charge_time = 5 SECONDS
+ primary_resource_cost = 50
+ primary_resource_type = SPELL_COST_STAMINA
+ cooldown_time = 30 SECONDS
associated_skill = /datum/skill/magic/arcane
- gesture_required = TRUE
- antimagic_allowed = TRUE
- recharge_time = 30 SECONDS
- miracle = FALSE
zizo_spell = TRUE
+ spell_requirements = SPELL_REQUIRES_SAME_Z
-/obj/effect/proc_holder/spell/invoked/bonemend/cast(list/targets, mob/living/user)
- ..()
- if(!isliving(targets[1]))
- return FALSE
+/datum/action/cooldown/spell/bonemend/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
- var/mob/living/target = targets[1]
- if(target.mob_biotypes & MOB_UNDEAD) //positive energy harms the undead
- var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(user.zone_selected))
+/datum/action/cooldown/spell/bonemend/cast(atom/cast_on)
+ . = ..()
+ var/mob/living/target = cast_on
+ if(target.mob_biotypes & MOB_UNDEAD)
+ var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(owner.zone_selected))
if(affecting && (affecting.heal_damage(50, 50) || affecting.heal_wounds(50)))
target.update_damage_overlays()
target.visible_message(span_danger("[target] reforms under the vile energy!"), span_notice("I'm remade by dark magic!"))
- return TRUE
return TRUE
diff --git a/code/modules/spells/roguetown/necromancer/covert_the_downtrodden.dm b/code/modules/spells/roguetown/necromancer/covert_the_downtrodden.dm
new file mode 100644
index 00000000000..d9f907867d0
--- /dev/null
+++ b/code/modules/spells/roguetown/necromancer/covert_the_downtrodden.dm
@@ -0,0 +1,116 @@
+/datum/action/cooldown/spell/convert_heretic
+ name = "Convert to Ecclesiarchy"
+ desc = "Initiate a lengthy ritual to convert a willing excommunicate, apostate, or cursed soul into your faith."
+ fluff_desc = "In the end, this was always a matter of faith. Not all Ecclesiarchs are thieves, madmen, cannibals, or tyrants; they are simply those who learned too early that humanity must shape the future of this dying world. Divinity was never meant to remain outside mortal hands."
+ button_icon = 'icons/mob/actions/roguespells.dmi'
+ button_icon_state = "convert_heretic"
+ invocations = list("Close your eyes and open your mind, this world is decaying but you shall be its last hope.")
+ invocation_type = INVOCATION_WHISPER
+ sound = 'sound/magic/bless.ogg'
+ charge_sound = 'sound/magic/chargingold.ogg'
+ primary_resource_type = SPELL_COST_DEVOTION
+ primary_resource_cost = 100
+ secondary_resource_type = SPELL_COST_DEVOTION
+ secondary_resource_cost = 100
+ cooldown_time = 20 MINUTES
+ charge_required = TRUE
+ charge_time = 10 SECONDS
+ associated_skill = /datum/skill/magic/holy
+ associated_stat = null
+ self_cast_possible = FALSE
+
+/datum/action/cooldown/spell/convert_heretic/arcyne
+ primary_resource_type = SPELL_COST_ENERGY
+ primary_resource_cost = 125
+ invocations = list("Claude oculos, aperi mentem. Ex ruina spes surgi, mundus cadit, tu spes renova.")
+
+/datum/action/cooldown/spell/convert_heretic/free
+ primary_resource_type = SPELL_COST_ENERGY
+ primary_resource_cost = 125
+ invocations = list("Welcome to the righteous path. The future belongs to us.")
+
+/datum/action/cooldown/spell/convert_heretic/is_valid_target(atom/cast_on)
+ return ishuman(cast_on)
+
+/datum/action/cooldown/spell/convert_heretic/cast(atom/cast_on)
+ . = ..()
+ var/mob/living/carbon/human/user = owner
+ var/mob/living/carbon/human/target = cast_on
+
+ if(!HAS_TRAIT(user, TRAIT_HERESIARCH))
+ to_chat(user, span_warning("You lack the knowledge for this ritual."))
+ reset_spell_cooldown()
+ return FALSE
+
+ if(target.cmode)
+ reset_spell_cooldown()
+ return FALSE
+
+ if(HAS_TRAIT(target, TRAIT_HERESIARCH))
+ to_chat(user, span_warning("[target] is already serving the greater good."))
+ reset_spell_cooldown()
+ return FALSE
+
+ if(alert(target, "[user.real_name] is trying to convert you to their patron, [user.patron.name]. Do you accept?", "Conversion Request", "Yes", "No") != "Yes")
+ to_chat(user, span_warning("[target] refused your offer of conversion."))
+ reset_spell_cooldown()
+ return FALSE
+
+ var/absolvable = FALSE
+ if(HAS_TRAIT(target, TRAIT_EXCOMMUNICATED))
+ absolvable = TRUE
+
+ if(target.has_status_effect(/datum/status_effect/debuff/apostasy))
+ target.remove_status_effect(/datum/status_effect/debuff/apostasy)
+ absolvable = TRUE
+
+ if(target.real_name in GLOB.apostasy_players)
+ GLOB.apostasy_players -= target.real_name
+ absolvable = TRUE
+ if(target.real_name in GLOB.excommunicated_players)
+ GLOB.excommunicated_players -= target.real_name
+ absolvable = TRUE
+
+ if(!absolvable)
+ to_chat(user, span_warning("[target] doesn't bear the church's marks of shame!"))
+ return FALSE
+
+ target.remove_status_effect(/datum/status_effect/debuff/apostasy)
+ target.remove_status_effect(/datum/status_effect/debuff/excomm)
+ target.remove_stress(/datum/stressevent/apostasy)
+ target.remove_stress(/datum/stressevent/excommunicated)
+
+ for(var/datum/curse/C in target.curses)
+ target.remove_curse(C)
+
+ var/saved_level = CLERIC_T0
+ var/saved_max_progression = CLERIC_T1
+ var/saved_devotion_gain = CLERIC_REGEN_MINOR
+
+ if(target.devotion)
+ saved_level = target.devotion.level
+ saved_devotion_gain = target.devotion.passive_devotion_gain
+ saved_max_progression = target.devotion.max_progression
+
+ if(target.patron != user.patron)
+ for(var/datum/action/cooldown/spell/S in target.devotion.granted_spells)
+ target.mind.RemoveSpell(S)
+
+ target.devotion.Destroy()
+
+ target.patron = new user.patron.type()
+ to_chat(target, span_userdanger("Your soul now belongs to [user.patron.name]!"))
+
+ var/datum/devotion/new_devotion = new /datum/devotion(target, target.patron)
+ target.devotion = new_devotion
+ new_devotion.grant_miracles(target, saved_level, saved_devotion_gain, saved_max_progression)
+
+ ADD_TRAIT(target, TRAIT_HERESIARCH, TRAIT_GENERIC)
+ ADD_TRAIT(target, TRAIT_EXCOMMUNICATED, TRAIT_GENERIC)
+ ADD_TRAIT(target, TRAIT_ZURCH, TRAIT_GENERIC)
+ to_chat(user, span_danger("You've converted [target.name] to [user.patron.name]!"))
+ to_chat(target, span_danger("You feel ancient powers lifting divine burdens from your soul..."))
+
+ return TRUE
+
+
diff --git a/code/modules/spells/roguetown/necromancer/eyebite.dm b/code/modules/spells/roguetown/necromancer/eyebite.dm
index b024963944d..60fca029a9c 100644
--- a/code/modules/spells/roguetown/necromancer/eyebite.dm
+++ b/code/modules/spells/roguetown/necromancer/eyebite.dm
@@ -1,26 +1,26 @@
-/obj/effect/proc_holder/spell/invoked/eyebite
+/datum/action/cooldown/spell/eyebite
name = "Eyebite"
- desc = "Manipulate the shadows within a chosen target's eye into jagged, gnashing teeth. Temporarily blinds the chosen target, while moderately damaging them."
- overlay_state = "raiseskele"
- releasedrain = 30
- chargetime = 15
- range = 7
- warnie = "sydwarning"
- movement_interrupt = FALSE
- chargedloop = null
+ desc = "Manipulate the shadows within a chosen target's eye into jagged, gnashing teeth. Temporarily blinds the chosen target, while moderately damaging them."
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "blind"
+ cast_range = 7
sound = 'sound/items/beartrap.ogg'
+ primary_resource_cost = 30
+ primary_resource_type = SPELL_COST_STAMINA
+ charge_required = TRUE
+ charge_time = 15
associated_skill = /datum/skill/magic/arcane
- gesture_required = TRUE // Offensive spell
- antimagic_allowed = TRUE
- recharge_time = 15 SECONDS
- miracle = FALSE
- hide_charge_effect = TRUE
+ cooldown_time = 15 SECONDS
+ spell_requirements = SPELL_REQUIRES_SAME_Z
+ self_cast_possible = FALSE
+ zizo_spell = TRUE
-/obj/effect/proc_holder/spell/invoked/eyebite/cast(list/targets, mob/living/user)
- ..()
- if(!isliving(targets[1]))
- return FALSE
- var/mob/living/carbon/target = targets[1]
+/datum/action/cooldown/spell/eyebite/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
+
+/datum/action/cooldown/spell/eyebite/cast(atom/cast_on)
+ . = ..()
+ var/mob/living/carbon/target = cast_on
target.visible_message(span_info("A loud crunching sound has come from [target]!"), span_userdanger("I feel arcane teeth biting into my eyes!"))
target.adjustBruteLoss(30)
target.blind_eyes(2)
diff --git a/code/modules/spells/roguetown/necromancer/gravemark.dm b/code/modules/spells/roguetown/necromancer/gravemark.dm
index 20a19fe50af..f2ac1065d3e 100644
--- a/code/modules/spells/roguetown/necromancer/gravemark.dm
+++ b/code/modules/spells/roguetown/necromancer/gravemark.dm
@@ -1,44 +1,73 @@
-
-/obj/effect/proc_holder/spell/invoked/gravemark
+/datum/action/cooldown/spell/gravemark
name = "Gravemark"
desc = "Adjusts a chosen target's status, allowing you to denote them as an ally to the undead creechers under your command. Marked allies \
will not be targeted nor attacked by any undead creechers under your command. Casting the 'Gravemark' spell on them again will mark them as \
an enemy, causing all undead creechers under your command to become hostile against them."
- overlay_state = "raiseskele"
- range = 7
- warnie = "sydwarning"
- movement_interrupt = FALSE
- chargedloop = null
- antimagic_allowed = TRUE
- recharge_time = 15 SECONDS
- hide_charge_effect = TRUE
-
-/obj/effect/proc_holder/spell/invoked/gravemark/cast(list/targets, mob/living/user)
- . = ..()
- if(isliving(targets[1]))
- var/mob/living/target = targets[1]
- var/faction_tag = "[user.mind.current.real_name]_faction"
- if (target == user)
- to_chat(user, span_warning("It would be unwise to make an enemy of your own skeletons."))
- return FALSE
- if(target.mind && target.mind.current)
- if (faction_tag in target.mind?.current.faction)
- target.mind?.current.faction -= faction_tag
- user.say("Hostis declaratus es.", language = /datum/language/common)
- else
- target.mind?.current.faction += faction_tag
- user.say("Amicus declaratus es.", language = /datum/language/common)
- target.notify_faction_change()
- else if(istype(target, /mob/living/simple_animal))
- if (faction_tag in target.faction)
- target.faction -= faction_tag
- user.say("Hostis declaratus es.", language = /datum/language/common)
- else
- target.faction |= faction_tag
- user.say("Amicus declaratus es.", language = /datum/language/common)
- target.notify_faction_change()
+ button_icon = 'icons/mob/actions/actions_clockcult.dmi'
+ button_icon_state = "Judicial Marker"
+ cast_range = 8
+ charge_required = FALSE
+ cooldown_time = 3 SECONDS
+ spell_requirements = SPELL_REQUIRES_SAME_Z
+ primary_resource_type = SPELL_COST_NONE
+ self_cast_possible = TRUE
+ has_visual_effects = FALSE
+ has_visual_effects = FALSE
+ sound = null
+ zizo_spell = TRUE
+
+/datum/action/cooldown/spell/gravemark/cast(atom/cast_on)
+ if(!owner)
+ return FALSE
+
+ var/mob/living/target = cast_on
+ if(!isliving(target))
+ return FALSE
+
+ var/faction_tag = "[owner.real_name]_faction"
+
+ // Self-cast = list current allies
+ if(target == owner)
+ var/list/allies = list()
+
+ for(var/mob/living/M in world)
+ if(M == owner)
+ continue
+
+ if(M.mind?.current)
+ if(faction_tag in M.mind.current.faction)
+ allies += M.real_name
+ else if(istype(M, /mob/living/simple_animal))
+ if(faction_tag in M.faction)
+ allies += M.name
+
+ if(!length(allies))
+ to_chat(owner, span_notice("You have declared no allies among the living or dead."))
+ else
+ to_chat(owner, span_notice("Those bearing your Gravemark: [english_list(allies)]."))
+
return TRUE
- return FALSE
-/obj/effect/proc_holder/spell/invoked/gravemark/no_sprite
- overlay_state = ""
+ var/list/faction_list
+
+ if(target.mind?.current)
+ faction_list = target.mind.current.faction
+ else if(istype(target, /mob/living/simple_animal))
+ faction_list = target.faction
+ else
+ return FALSE
+
+ . = ..()
+
+ if(faction_tag in faction_list)
+ faction_list -= faction_tag
+ owner.say("Hostis declaratus es!", language = /datum/language/common)
+ else
+ faction_list += faction_tag
+ owner.say("Amicus declaratus es.", language = /datum/language/common)
+
+ target.notify_faction_change()
+ return TRUE
+
+/datum/action/cooldown/spell/gravemark/no_sprite
+ button_icon_state = ""
diff --git a/code/modules/spells/roguetown/necromancer/lacrima.dm b/code/modules/spells/roguetown/necromancer/lacrima.dm
new file mode 100644
index 00000000000..c72ef729879
--- /dev/null
+++ b/code/modules/spells/roguetown/necromancer/lacrima.dm
@@ -0,0 +1,117 @@
+// Plunge your hand into someone's ribs to rip out their impure lux for your diabolical uses
+
+/datum/action/cooldown/spell/lacrima
+ name = "Lacrima"
+ desc = "Requires an aggressive grab on a prone and living target. Begin a dark ritual that fractures their ribcage and, directly but violently, extracts their Lux."
+ fluff_desc = "A method devised by the Cabal to require minimal ritual and effort. A method of extraction that is brutish, inelegant, yet undeniably effective. Zizo does not scorn efficiency, though resorting to something so lacking in flair can feel embarrassingly unceremonious. It may score a giggle or two from Her, especially against the ones who deserve it."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "zizograsp"
+ charge_required = FALSE
+ click_to_activate = FALSE
+ primary_resource_type = SPELL_COST_ENERGY
+ primary_resource_cost = 100
+ secondary_resource_type = SPELL_COST_STAMINA
+ secondary_resource_cost = 100
+ cooldown_time = 5 MINUTES
+ invocations = "Cede, et pars Magni Operis Eius eris."
+ associated_skill = /datum/skill/magic/arcane
+ zizo_spell = TRUE
+
+/datum/action/cooldown/spell/lacrima/zizo
+ primary_resource_type = SPELL_COST_DEVOTION
+ primary_resource_cost = 100
+ secondary_resource_type = SPELL_COST_ENERGY
+ secondary_resource_cost = 100
+ associated_skill = /datum/skill/magic/holy
+ cooldown_time = 1 MINUTES
+ spell_requirements = SPELL_REQUIRES_SAME_Z
+
+/datum/action/cooldown/spell/lacrima/cast(atom/cast_on)
+ . = ..()
+ if(!ishuman(owner))
+ return FALSE
+
+ if(owner.pulling && ishuman(owner.pulling) && owner.grab_state >= GRAB_AGGRESSIVE)
+ lux_rip(owner.pulling, owner)
+ return TRUE
+
+ to_chat(owner, span_warning("I need an aggressive grab on a floored victim to use Lacrima!"))
+ reset_spell_cooldown()
+ return FALSE
+
+/datum/action/cooldown/spell/lacrima/proc/lux_rip(mob/living/carbon/human/target, mob/living/carbon/human/user)
+ var/break_time = 100
+ var/tear_time = 100
+
+ if(target == user)
+ return
+ if(!iscarbon(target))
+ to_chat(user, span_info("Their Lux is insufficient or plain worthless for this ritual."))
+ return
+ if(!target.Adjacent(user))
+ to_chat(user, span_info("I need to be next to [target] to excise their Lux."))
+ return
+ if((target.mobility_flags & MOBILITY_STAND))
+ to_chat(user, span_info("My victim must be lying down."))
+ return
+ if(target.has_status_effect(/datum/status_effect/debuff/devitalised) || target.mob_biotypes & MOB_UNDEAD)
+ to_chat(user, span_notice("This victim's Lux is corroded. There is little I can make use of."))
+ return
+ else
+ user.visible_message(span_alert("[user] reaches towards [target]'s chest, necrotic flames wreathing [user.p_their()] hand..."))
+ var/obj/item/bodypart/chest = target.get_bodypart(BODY_ZONE_CHEST)
+ if(!chest.has_wound(/datum/wound/fracture/chest))
+ if(!do_after(user, break_time, target = target))
+ return
+ if(chest)
+ if(!HAS_TRAIT(target, TRAIT_NOPAIN))
+ target.emote("agony")
+ chest.add_wound(/datum/wound/fracture/chest)
+ target.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
+ user.visible_message(span_alert("[user] plunges their fist into [target]'s ribcage, shattering it spectacularly!"))
+ if(!do_after(user, tear_time, target = target) && chest.has_wound(/datum/wound/fracture/chest))
+ return
+ if(!HAS_TRAIT(target, TRAIT_NOPAIN))
+ target.emote("agony")
+ playsound(user, 'sound/items/blackmirror_needle.ogg', 60, FALSE, 3)
+ user.visible_message(span_alert("[user] tears a glob of pulsating Lux from [target]'s heart!"))
+
+ if(HAS_TRAIT(target, TRAIT_PSYDONITE) || HAS_TRAIT(target, TRAIT_INQUISITION))
+ to_chat(target, span_purple("You hear a vicious giggle echoing through your mind. The Dame of Progress is pleased."))
+ target.add_stress(/datum/stressevent/torn_lux_psydonite)
+ owner.add_stress(/datum/stressevent/dame_favor)
+
+ else if(HAS_TRAIT(target, TRAIT_NOBLE) || HAS_TRAIT(target, TRAIT_CLERGY))
+ to_chat(target, span_purple("You hear a vicious giggle echoing through your mind. The Dame of Progress is pleased."))
+ target.add_stress(/datum/stressevent/torn_lux_devout)
+ owner.add_stress(/datum/stressevent/dame_favor)
+
+ else
+ target.add_stress(/datum/stressevent/torn_lux)
+
+ new /obj/item/reagent_containers/lux_impure(target.loc)
+ SEND_SIGNAL(user, COMSIG_LUX_EXTRACTED, target)
+ record_featured_stat(FEATURED_STATS_CRIMINALS, user)
+ record_round_statistic(STATS_LUX_HARVESTED)
+ record_round_statistic(STATS_TORTURES)
+ target.apply_status_effect(/datum/status_effect/debuff/devitalised)
+
+/datum/stressevent/torn_lux
+ desc = span_boldred("THE ESSENCE OF MY LYFE HAS BEEN RIPPED FROM ME!!")
+ stressadd = 30
+ timer = 5 MINUTES
+
+/datum/stressevent/torn_lux_psydonite
+ desc = span_boldred("PSYDON... forgive me... I feel impure. Defiled. Hollow. How could I allow this sacrilege upon your most precious gift?!")
+ stressadd = 30
+ timer = 5 MINUTES
+
+/datum/stressevent/torn_lux_devout
+ desc = span_boldred("SOMETHING IS TERRIBLY WRONG WITH ME!! It writhes beneath my skin! It claws through my thoughts! I feel my patron's fury upon me... what have they done to my Lux?!")
+ stressadd = 30
+ timer = 5 MINUTES
+
+/datum/stressevent/dame_favor
+ desc = span_purple("That laugh... this cold warmth in my hollow heart. Her voice graces me at last. She is pleased. She sees me. Ahh... such bliss. Watch me, my Dame. Watch what I become.")
+ stressadd = -10
+ timer = 5 MINUTES
diff --git a/code/modules/spells/roguetown/necromancer/raise_undead_formation.dm b/code/modules/spells/roguetown/necromancer/raise_undead_formation.dm
index 41263fb5c14..d39da5b78be 100644
--- a/code/modules/spells/roguetown/necromancer/raise_undead_formation.dm
+++ b/code/modules/spells/roguetown/necromancer/raise_undead_formation.dm
@@ -1,86 +1,113 @@
-
-/obj/effect/proc_holder/spell/invoked/raise_undead_formation
+/datum/action/cooldown/spell/raise_undead_formation
name = "Raise Undead Formation"
- desc = "Invoke forbidden magicka to summon a cohort of mindless, shambling skeletons. Mindless skeletons can be given orders to guard, patrol, and attack by their \
- summoner. These skeletons are weaker than their more complex-jointed counterparts, but are harder to incapacitate."
- clothes_req = FALSE
- overlay_state = "animate"
- range = 7
- sound = list('sound/magic/magnet.ogg')
- releasedrain = 40
- chargetime = 6 SECONDS
- warnie = "spellwarning"
- no_early_release = TRUE
- charging_slowdown = 1
- chargedloop = /datum/looping_sound/invokegen
- gesture_required = TRUE // Summon spell
+ desc = "Invoke forbidden magicka to summon a cohort of mindless, shambling skeletons.\nMindless skeletons can be given orders to guard, patrol, and attack by their summoner.\nThese skeletons are weaker than their more complex-jointed counterparts, but are harder to incapacitate."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "skeleton_formation"
+ cast_range = 7
+ sound = 'sound/magic/magnet.ogg'
+ primary_resource_cost = 40
+ primary_resource_type = SPELL_COST_STAMINA
+ charge_required = TRUE
+ charge_time = 6 SECONDS
+ charge_slowdown = 1
associated_skill = /datum/skill/magic/arcane
- recharge_time = 20 SECONDS
+ cooldown_time = 20 SECONDS
+ zizo_spell = TRUE
+ invocation_type = INVOCATION_SHOUT
+ invocations = list("Evoca skeletos!")
+ var/miracle = FALSE
var/cabal_affine = FALSE
var/is_summoned = FALSE
var/to_spawn = 4
- hide_charge_effect = TRUE
-
-/obj/effect/proc_holder/spell/invoked/raise_undead_formation/cast(list/targets, mob/living/user)
- ..()
- // Caustic Edit Start
- // Just in case the user doesn't have the spells to manage their minions
- if(!user.mind.has_spell(/obj/effect/proc_holder/spell/invoked/minion_order))
- user.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/minion_order)
- if(!user.mind.has_spell(/obj/effect/proc_holder/spell/invoked/gravemark))
- user.mind.AddSpell(new /obj/effect/proc_holder/spell/invoked/gravemark)
- // Caustic Edit End
-
- if(istype(get_area(user), /area/rogue/indoors/ravoxarena))
- to_chat(user, span_userdanger("I reach for outer help, but something rebukes me! This challenge is only for me to overcome!"))
- revert_cast()
+ var/spawn_lifespan
+
+/datum/action/cooldown/spell/raise_undead_formation/cast(atom/cast_on)
+ . = ..()
+
+ if(!owner)
return FALSE
-
- var/turf/T = get_turf(targets[1])
- if(!isopenturf(T))
- to_chat(user, span_warning("The targeted location is blocked. My summon fails to come forth."))
+
+ if(istype(get_area(owner), /area/rogue/indoors/ravoxarena))
+ to_chat(owner, span_userdanger("I reach for outer help, but something rebukes me! This challenge is only for me to overcome!"))
+ reset_spell_cooldown()
return FALSE
- var/skeleton_roll
+ var/turf/T = get_turf(cast_on)
+ if(!isopenturf(T))
+ to_chat(owner, span_warning("The targeted location is blocked. My summon fails to come forth."))
+ return FALSE
for(var/i = 1 to to_spawn)
- if(i > to_spawn)
- i = 1
+ var/turf/spawn_turf = T
if(i > 1)
- if(user.dir == NORTH || user.dir == SOUTH)
- if(prob(50))
- T = get_step(T, EAST)
- else
- T = get_step(T, WEST)
+ if(owner.dir == NORTH || owner.dir == SOUTH)
+ spawn_turf = get_step(T, prob(50) ? EAST : WEST)
else
- if(prob(50))
- T = get_step(T, NORTH)
- else
- T = get_step(T, SOUTH)
+ spawn_turf = get_step(T, prob(50) ? NORTH : SOUTH)
- if(!isopenturf(T))
+ if(!isopenturf(spawn_turf))
continue
- new /obj/effect/temp_visual/bluespace_fissure(T)
- skeleton_roll = rand(1,100)
+ new /obj/effect/temp_visual/bluespace_fissure(spawn_turf)
+
+ var/skeleton_roll = rand(1,100)
+ var/skeleton_type
+
switch(skeleton_roll)
if(1 to 20)
- new /mob/living/simple_animal/hostile/rogue/skeleton/axe(T, user, cabal_affine)
- if(21 to 40)
- new /mob/living/simple_animal/hostile/rogue/skeleton/spear(T, user, cabal_affine)
- if(41 to 60)
- new /mob/living/simple_animal/hostile/rogue/skeleton/guard(T, user, cabal_affine)
- if(61 to 80)
- new /mob/living/simple_animal/hostile/rogue/skeleton/bow(T, user, cabal_affine)
- if(81 to 100)
- new /mob/living/simple_animal/hostile/rogue/skeleton(T, user, cabal_affine)
+ skeleton_type = /mob/living/simple_animal/hostile/rogue/skeleton/axe
+ if(21 to 30)
+ skeleton_type = /mob/living/simple_animal/hostile/rogue/skeleton/spear
+ if(31 to 60)
+ skeleton_type = /mob/living/simple_animal/hostile/rogue/skeleton/guard
+ if(61 to 70)
+ skeleton_type = /mob/living/simple_animal/hostile/rogue/skeleton/axe
+ if(71 to 100)
+ skeleton_type = /mob/living/simple_animal/hostile/rogue/skeleton/guard
+
+ var/mob/living/simple_animal/hostile/rogue/skeleton/S = new skeleton_type(spawn_turf, owner, cabal_affine)
+
+ if(!S)
+ continue
+
+ if(miracle)
+ var/holyLV = owner.get_skill_level(/datum/skill/magic/holy)
+ var/bonus = max(0, holyLV - 1) * 2
+
+ S.STASTR += bonus
+ S.STASPD += round(bonus / 2)
+ S.maxHealth += bonus * 50
+ S.health = S.maxHealth
+
+ var/aggro_range = 8
+
+ for(var/mob/living/M in view(aggro_range, S))
+ if(M == S)
+ continue
+ if(M.stat == DEAD)
+ continue
+ if(M.mind)
+ continue
+ if(M.faction_check_mob(S))
+ continue
+ if(M.faction_check_mob(owner))
+ continue
+
+ M.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, S)
+ M.ai_controller.set_blackboard_key(BB_HIGHEST_THREAT_MOB, S)
+
+ var/datum/component/ai_aggro_system/aggro = M.GetComponent(/datum/component/ai_aggro_system)
+
+ if(aggro)
+ aggro.add_threat_to_mob(S, 100)
+
+ apply_mob_lifespan(S, owner, spawn_lifespan)
+
return TRUE
-/obj/effect/proc_holder/spell/invoked/raise_undead_formation/necromancer
+/datum/action/cooldown/spell/raise_undead_formation/necromancer
cabal_affine = TRUE
is_summoned = TRUE
- recharge_time = 35 SECONDS
+ cooldown_time = 35 SECONDS
to_spawn = 3
-
-
diff --git a/code/modules/spells/roguetown/necromancer/raise_undead_guard.dm b/code/modules/spells/roguetown/necromancer/raise_undead_guard.dm
index 96b38a47d41..85c63c0acf0 100644
--- a/code/modules/spells/roguetown/necromancer/raise_undead_guard.dm
+++ b/code/modules/spells/roguetown/necromancer/raise_undead_guard.dm
@@ -1,38 +1,47 @@
-/obj/effect/proc_holder/spell/invoked/raise_undead_guard
+/datum/action/cooldown/spell/raise_undead_guard
name = "Conjure Undead"
- desc = "Invoke forbidden magicka to summon a mindless, shambling skeleton. Mindless skeletons can be given orders to guard, patrol, and attack by their \
- summoner. These skeletons are weaker than their more complex-jointed counterparts, but are harder to incapacitate."
- clothes_req = FALSE
- overlay_state = "animate"
- range = 7
- sound = list('sound/magic/magnet.ogg')
- releasedrain = 40
- chargetime = 3 SECONDS
- warnie = "spellwarning"
- no_early_release = TRUE
- charging_slowdown = 1
- chargedloop = /datum/looping_sound/invokegen
- gesture_required = TRUE // Summon spell
+ desc = "Invoke forbidden magicka to summon a mindless, shambling skeleton.\nMindless skeletons can be given orders to guard, patrol, and attack by their summoner.\nThese skeletons are weaker than their more complex-jointed counterparts, but are harder to incapacitate."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "skeleton"
+ cast_range = 7
+ sound = 'sound/magic/magnet.ogg'
+ primary_resource_cost = 40
+ primary_resource_type = SPELL_COST_STAMINA
+ charge_required = TRUE
+ charge_time = 3 SECONDS
+ charge_slowdown = 1
associated_skill = /datum/skill/magic/arcane
- recharge_time = 30 SECONDS
- hide_charge_effect = TRUE
+ cooldown_time = 30 SECONDS
+ zizo_spell = TRUE
+ invocations = list("Convoca spectres custodes!")
+ invocation_type = INVOCATION_SHOUT
+ var/spawn_lifespan
-/obj/effect/proc_holder/spell/invoked/raise_undead_guard/cast(list/targets, mob/living/user)
- ..()
+/datum/action/cooldown/spell/raise_undead_guard/cast(atom/cast_on)
+ . = ..()
- if(istype(get_area(user), /area/rogue/indoors/ravoxarena))
- to_chat(user, span_userdanger("I reach for outer help, but something rebukes me! This challenge is only for me to overcome!"))
- revert_cast()
+ if(istype(get_area(owner), /area/rogue/indoors/ravoxarena))
+ to_chat(owner, span_userdanger("I reach for outer help, but something rebukes me! This challenge is only for me to overcome!"))
+ reset_spell_cooldown()
return FALSE
-
- var/turf/T = get_turf(targets[1])
+
+ var/turf/T = get_turf(cast_on)
if(!isopenturf(T))
- to_chat(user, span_warning("The targeted location is blocked. My summon fails to come forth."))
+ to_chat(owner, span_warning("The targeted location is blocked. My summon fails to come forth."))
return FALSE
new /obj/effect/temp_visual/gib_animation(T, "gibbed-h")
- var/mob/living/skeleton_new = new /mob/living/carbon/human/species/skeleton/npc/bogguard(T, user)
- spawn(11) //Ashamed of this but I hate how after_creation() uses spawn too and I'm not making a timer for this. Proc needs a look-over. - Ryan
- skeleton_new.faction |= list("cabal", "[user.mind.current.real_name]_faction")
+ var/mob/living/skeleton_new = new /mob/living/carbon/human/species/skeleton/npc/bogguard(T, owner)
+ apply_mob_lifespan(skeleton_new, owner, spawn_lifespan)
+ var/caster_name = owner.mind?.current?.real_name
+ if(caster_name)
+ addtimer(CALLBACK(src, PROC_REF(add_skeleton_faction), skeleton_new, caster_name), 1.1 SECONDS)
return TRUE
+
+/datum/action/cooldown/spell/raise_undead_guard/proc/add_skeleton_faction(mob/living/skeleton, caster_name)
+ if(!QDELETED(skeleton))
+ skeleton.faction |= list("cabal", "[caster_name]_faction")
+
+/datum/action/cooldown/spell/raise_undead_guard/necromancer
+ spawn_lifespan = 30 MINUTES
diff --git a/code/modules/spells/roguetown/necromancer/tame_undead.dm b/code/modules/spells/roguetown/necromancer/tame_undead.dm
index 65bd407ae84..ea49e54c2c7 100644
--- a/code/modules/spells/roguetown/necromancer/tame_undead.dm
+++ b/code/modules/spells/roguetown/necromancer/tame_undead.dm
@@ -1,40 +1,40 @@
-/obj/effect/proc_holder/spell/invoked/tame_undead
+/datum/action/cooldown/spell/tame_undead
name = "Tame Undead"
- desc = "Oftentymes, husks and shamblers walk aimlessly - uncertain of their future. They need not look further, any longer. \
- Requires the target to be within four tiles. Works on undead animals, too."
- overlay_state = "raiseskele"
- range = 4
- warnie = "sydwarning"
- recharge_time = 60 SECONDS
- releasedrain = 40
- chargetime = 5 SECONDS
- charging_slowdown = 1
- gesture_required = TRUE
- chargedloop = /datum/looping_sound/invokegen
- no_early_release = TRUE
-
-/obj/effect/proc_holder/spell/invoked/tame_undead/cast(list/targets, mob/living/user)
- ..()
-
- if(!isliving(targets[1]))
- revert_cast()
- return FALSE
+ desc = "Oftentymes, husks and shamblers walk aimlessly - uncertain of their future. They need not look further, any longer.\nRequires the target to be within four tiles. Works on undead animals, too."
+ button_icon = 'icons/mob/actions/zizomiracles.dmi'
+ button_icon_state = "deadite_tame"
+ cast_range = 4
+ primary_resource_cost = 40
+ primary_resource_type = SPELL_COST_STAMINA
+ cooldown_time = 60 SECONDS
+ charge_required = TRUE
+ charge_time = 5 SECONDS
+ charge_slowdown = 1
+ associated_skill = /datum/skill/magic/arcane
+ self_cast_possible = FALSE
+ zizo_spell = TRUE
+
+/datum/action/cooldown/spell/tame_undead/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
- var/mob/living/target = targets[1]
+/datum/action/cooldown/spell/tame_undead/cast(atom/cast_on)
+ . = ..()
+
+ var/mob/living/target = cast_on
if(!(target.mob_biotypes & MOB_UNDEAD))
- to_chat(user, span_warning("[target]'s soul is not Hers, yet. I cannot do anything."))
- revert_cast()
+ to_chat(owner, span_warning("[target]'s soul is not Hers, yet. I cannot do anything."))
+ reset_spell_cooldown()
return FALSE
-
+
if(target.mind)
- to_chat(user, span_warning("[target]'s mind resists your goadings. It will not do."))
- revert_cast()
+ to_chat(owner, span_warning("[target]'s mind resists your goadings. It will not do."))
+ reset_spell_cooldown()
return FALSE
- target.faction |= list("cabal", "[user.mind.current.real_name]_faction")
- target.visible_message(span_notice("[target] turns its head to pay heed to [user]!"))
+ target.faction |= list("cabal", "[owner.mind.current.real_name]_faction")
+ target.visible_message(span_notice("[target] turns its head to pay heed to [owner]!"))
if(!target.ai_controller)
target.ai_controller = /datum/ai_controller/undead
target.InitializeAIController()
diff --git a/code/modules/spells/spell_types/bardic/vicious_mockery.dm b/code/modules/spells/spell_types/bardic/vicious_mockery.dm
index 19965bd6207..9fa2a6255a6 100644
--- a/code/modules/spells/spell_types/bardic/vicious_mockery.dm
+++ b/code/modules/spells/spell_types/bardic/vicious_mockery.dm
@@ -37,6 +37,7 @@ GLOBAL_LIST_INIT(mockery_insults, list(
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_slowdown = CHARGING_SLOWDOWN_NONE
cooldown_time = MOCKERY_COOLDOWN
diff --git a/code/modules/spells/spell_types/undead/summon/raise_undead.dm b/code/modules/spells/spell_types/undead/summon/raise_undead.dm
index cfe32e58da1..7313e6436e5 100644
--- a/code/modules/spells/spell_types/undead/summon/raise_undead.dm
+++ b/code/modules/spells/spell_types/undead/summon/raise_undead.dm
@@ -36,7 +36,7 @@
var/message = "The depths are hollow."
if(user.cmode)
message += " A decrepit skeleton rises instead."
- backup_summon(T)
+ backup_summon(T, user)
to_chat(user, span_warning(message))
return TRUE
@@ -59,12 +59,14 @@
target.mind.AddSpell(new /obj/effect/proc_holder/spell/self/suicidebomb/lesser)
return TRUE
-/obj/effect/proc_holder/spell/invoked/raise_undead/proc/backup_summon(var/turf/T)
+/obj/effect/proc_holder/spell/invoked/raise_undead/proc/backup_summon(var/turf/T, mob/living/user)
var/skeleton_roll = rand(1, 3)
+ var/mob/living/skeletonnew
// 66% chance of medium 33% of heavy
switch(skeleton_roll)
if(1 to 2) // 66% chance
- new /mob/living/carbon/human/species/skeleton/npc/medium(T)
+ skeletonnew = new /mob/living/carbon/human/species/skeleton/npc/medium(T)
if(3) // 33% chance
- new /mob/living/carbon/human/species/skeleton/npc/hard(T)
+ skeletonnew = new /mob/living/carbon/human/species/skeleton/npc/hard(T)
+ apply_mob_lifespan(skeletonnew, user)
return TRUE
diff --git a/code/modules/spells/spell_types/wizard/battlewardry/arrow_ward.dm b/code/modules/spells/spell_types/wizard/battlewardry/arrow_ward.dm
index 9f3c90d0eca..144145558d2 100644
--- a/code/modules/spells/spell_types/wizard/battlewardry/arrow_ward.dm
+++ b/code/modules/spells/spell_types/wizard/battlewardry/arrow_ward.dm
@@ -19,6 +19,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_SMALL
diff --git a/code/modules/spells/spell_types/wizard/cryomancy/chill_food.dm b/code/modules/spells/spell_types/wizard/cryomancy/chill_food.dm
index 3090aa03d55..db53522a15c 100644
--- a/code/modules/spells/spell_types/wizard/cryomancy/chill_food.dm
+++ b/code/modules/spells/spell_types/wizard/cryomancy/chill_food.dm
@@ -25,6 +25,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
cooldown_time = 30 SECONDS
diff --git a/code/modules/spells/spell_types/wizard/cryomancy/frost_blast.dm b/code/modules/spells/spell_types/wizard/cryomancy/frost_blast.dm
index 3e3f61b8edd..e15461ebd0c 100644
--- a/code/modules/spells/spell_types/wizard/cryomancy/frost_blast.dm
+++ b/code/modules/spells/spell_types/wizard/cryomancy/frost_blast.dm
@@ -19,7 +19,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = 1 SECONDS
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_SMALL
diff --git a/code/modules/spells/spell_types/wizard/ferramancy/arcyne_lance.dm b/code/modules/spells/spell_types/wizard/ferramancy/arcyne_lance.dm
index 43f594d154e..b70934398df 100644
--- a/code/modules/spells/spell_types/wizard/ferramancy/arcyne_lance.dm
+++ b/code/modules/spells/spell_types/wizard/ferramancy/arcyne_lance.dm
@@ -21,7 +21,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/ferramancy/stygian_efflorescence.dm b/code/modules/spells/spell_types/wizard/ferramancy/stygian_efflorescence.dm
index a83bf2ef1b7..c69c92a2f90 100644
--- a/code/modules/spells/spell_types/wizard/ferramancy/stygian_efflorescence.dm
+++ b/code/modules/spells/spell_types/wizard/ferramancy/stygian_efflorescence.dm
@@ -28,7 +28,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/fulgurmancy/arc_bolt.dm b/code/modules/spells/spell_types/wizard/fulgurmancy/arc_bolt.dm
index 6300a560fb2..8012924c78b 100644
--- a/code/modules/spells/spell_types/wizard/fulgurmancy/arc_bolt.dm
+++ b/code/modules/spells/spell_types/wizard/fulgurmancy/arc_bolt.dm
@@ -20,7 +20,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/geomancy/emergence.dm b/code/modules/spells/spell_types/wizard/geomancy/emergence.dm
index 180f3ed4ab7..4bf536f86e5 100644
--- a/code/modules/spells/spell_types/wizard/geomancy/emergence.dm
+++ b/code/modules/spells/spell_types/wizard/geomancy/emergence.dm
@@ -21,7 +21,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_SMALL
diff --git a/code/modules/spells/spell_types/wizard/geomancy/gravel_blast.dm b/code/modules/spells/spell_types/wizard/geomancy/gravel_blast.dm
index 634078b1177..326a40a6ef4 100644
--- a/code/modules/spells/spell_types/wizard/geomancy/gravel_blast.dm
+++ b/code/modules/spells/spell_types/wizard/geomancy/gravel_blast.dm
@@ -24,7 +24,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/invoked_single_target/raise_deadite.dm b/code/modules/spells/spell_types/wizard/invoked_single_target/raise_deadite.dm
index bae02e0a66b..7beed984977 100644
--- a/code/modules/spells/spell_types/wizard/invoked_single_target/raise_deadite.dm
+++ b/code/modules/spells/spell_types/wizard/invoked_single_target/raise_deadite.dm
@@ -2,7 +2,7 @@
name = "Raise Deadite"
desc = "Infuse the target with quick acting Rot, raising them as a deadite. They will not be friendly to you."
button_icon = 'icons/mob/actions/roguespells.dmi'
- button_icon_state = "raisedead"
+ button_icon_state = "blesscrop"
sound = 'sound/magic/whiteflame.ogg'
click_to_activate = TRUE
diff --git a/code/modules/spells/spell_types/wizard/kinesis/greater_cleaning.dm b/code/modules/spells/spell_types/wizard/kinesis/greater_cleaning.dm
index 24252d9136b..52342e8ce5e 100644
--- a/code/modules/spells/spell_types/wizard/kinesis/greater_cleaning.dm
+++ b/code/modules/spells/spell_types/wizard/kinesis/greater_cleaning.dm
@@ -23,6 +23,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
cooldown_time = 10 SECONDS
diff --git a/code/modules/spells/spell_types/wizard/kinesis/soulshot.dm b/code/modules/spells/spell_types/wizard/kinesis/soulshot.dm
index f3b5e62abc8..8bd0fbb41e3 100644
--- a/code/modules/spells/spell_types/wizard/kinesis/soulshot.dm
+++ b/code/modules/spells/spell_types/wizard/kinesis/soulshot.dm
@@ -79,3 +79,18 @@
qdel(src)
return . || BULLET_ACT_HIT
return BULLET_ACT_FORCE_PIERCE
+
+/datum/action/cooldown/spell/projectile/soulshot/lesser
+ name = "Lesser Soulshot"
+ desc = "Fire a devastating beam of kinetic force that pierces through up to 2 targets. Stopped by solid objects. \
+ Damage is halved after the first target. \
+ Deals 50% increased damage to simple-minded creechurs. \
+ Basic offensive magic, refined for over a millenium since the first Magi expelled mana from their body with pure malice and determination to destroy another."
+ invocations = list("Animus Ictus!")
+ projectile_type = /obj/projectile/magic/soulshot/lesser
+ attunement_school = null
+ spell_tier = 0
+ point_cost = 0
+
+/obj/projectile/magic/soulshot/lesser
+ max_hits = 2
diff --git a/code/modules/spells/spell_types/wizard/misc/blink.dm b/code/modules/spells/spell_types/wizard/misc/blink.dm
index 93e356e256f..ca5bb03a299 100644
--- a/code/modules/spells/spell_types/wizard/misc/blink.dm
+++ b/code/modules/spells/spell_types/wizard/misc/blink.dm
@@ -17,6 +17,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_SMALL
diff --git a/code/modules/spells/spell_types/wizard/projectiles_single/greater_arcyne_bolt.dm b/code/modules/spells/spell_types/wizard/projectiles_single/greater_arcyne_bolt.dm
index 293e29e8eec..0df3262e85f 100644
--- a/code/modules/spells/spell_types/wizard/projectiles_single/greater_arcyne_bolt.dm
+++ b/code/modules/spells/spell_types/wizard/projectiles_single/greater_arcyne_bolt.dm
@@ -22,7 +22,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/pyromancy/spitfire.dm b/code/modules/spells/spell_types/wizard/pyromancy/spitfire.dm
index 99d85f4ebfe..0d3fbcc3d95 100644
--- a/code/modules/spells/spell_types/wizard/pyromancy/spitfire.dm
+++ b/code/modules/spells/spell_types/wizard/pyromancy/spitfire.dm
@@ -20,7 +20,7 @@
invocation_type = INVOCATION_SHOUT
charge_required = TRUE
- weapon_cast_penalized = TRUE
+ weapon_cast_penalized = FALSE
charge_time = CHARGETIME_POKE
charge_drain = 1
charge_slowdown = CHARGING_SLOWDOWN_NONE
diff --git a/code/modules/spells/spell_types/wizard/utility/mending.dm b/code/modules/spells/spell_types/wizard/utility/mending.dm
index 73b96178735..a4c64be8da8 100644
--- a/code/modules/spells/spell_types/wizard/utility/mending.dm
+++ b/code/modules/spells/spell_types/wizard/utility/mending.dm
@@ -187,3 +187,10 @@
repair_percent = 0.10
cooldown_time = 30 SECONDS
point_cost = 1
+
+
+/datum/action/cooldown/spell/mending
+ exclusive_group = "mending"
+
+/datum/action/cooldown/spell/mending/lesser
+ exclusive_group = "mending"
diff --git a/icons/mob/actions/zizomiracles.dmi b/icons/mob/actions/zizomiracles.dmi
index 8e98d191445..eee86842d8a 100644
Binary files a/icons/mob/actions/zizomiracles.dmi and b/icons/mob/actions/zizomiracles.dmi differ
diff --git a/modular_ratwood/modules/spells/scrolls/spell_granters.dm b/modular_ratwood/modules/spells/scrolls/spell_granters.dm
index 88753eb4070..2e54cad1e8f 100644
--- a/modular_ratwood/modules/spells/scrolls/spell_granters.dm
+++ b/modular_ratwood/modules/spells/scrolls/spell_granters.dm
@@ -1,6 +1,6 @@
/obj/item/book/granter/spell/skeleton // Mirror Transform Spell
name = "Scroll of Raise Undead Formation"
- spell = /obj/effect/proc_holder/spell/invoked/raise_undead_formation
+ spell = /datum/action/cooldown/spell/raise_undead_formation/necromancer
spellname = "Raise Undead Formation"
icon = 'icons/roguetown/items/misc.dmi'
icon_state = "scrollred"
diff --git a/roguetown.dme b/roguetown.dme
index 04fe89223ce..3a1ac272997 100644
--- a/roguetown.dme
+++ b/roguetown.dme
@@ -496,6 +496,7 @@
#include "code\controllers\subsystem\rogue\roguerot.dm"
#include "code\controllers\subsystem\rogue\scheduled_event_subsytem.dm"
#include "code\controllers\subsystem\rogue\soundloopers.dm"
+#include "code\controllers\subsystem\rogue\spawned_mobs.dm"
#include "code\controllers\subsystem\rogue\treasury.dm"
#include "code\controllers\subsystem\rogue\treasury_snapshot.dm"
#include "code\controllers\subsystem\rogue\water_level.dm"
@@ -2986,8 +2987,10 @@
#include "code\modules\spells\roguetown\acolyte\pestra\pestra_status_effects.dm"
#include "code\modules\spells\roguetown\necromancer\bone_chill.dm"
#include "code\modules\spells\roguetown\necromancer\bone_mend.dm"
+#include "code\modules\spells\roguetown\necromancer\covert_the_downtrodden.dm"
#include "code\modules\spells\roguetown\necromancer\eyebite.dm"
#include "code\modules\spells\roguetown\necromancer\gravemark.dm"
+#include "code\modules\spells\roguetown\necromancer\lacrima.dm"
#include "code\modules\spells\roguetown\necromancer\raise_undead_formation.dm"
#include "code\modules\spells\roguetown\necromancer\raise_undead_guard.dm"
#include "code\modules\spells\roguetown\necromancer\ray_of_sickness.dm"