Cult Vs. Heretic: 7 Months Later Edition (#82877)
## About The Pull Request This PR was originally meant as a replacement for the Bloody Bastard blade, but then I stopped existing for 7 months. Now that I'm here again, I'm finishing the job once and for all. ### **HERETICS VERSUS CULTISTS** ### Heretics Heretics can now sacrifice cultists, which will give them one of three gifts: The Cursed Blade, the Crimson Focus, and the Rusted Harvester. The gifts given are weighted to be spread out equally with each type. They will also gain one knowledge point. - The Cursed Blade is a free heretic blade that is more powerful than the normal heretic blade, including a small block chance. It can also be used to draw heretic runes off combat mode. - The Crimson Focus is a necklace that grants focusing and a minor regeneration effect which also affects nearby heretics, at the cost of gaining the BLOODY_MESS trait while wearing it. Additionally, it can be squeezed to heal 50 points of brute/burn damage, injecting yourself with three to six (separately) units of Eldritch ~~Water~~ Essence and Unholy Water. Yes, this isn't good. - The Rusted Harvester is a heretic 'monster' summon. It's a normal Harvester, but instead of Area Conversion and Forcewall, it has Aggressive Spread and Rust Construction (Raise Wall). It can delimb, but only cultists, with a delay. It has an aura of decay, corroding the environment and withering enemies near it, but it's VERY fragile. Rusting cultist item dispensers will now cause them to turn into a Heretic object. Altars turn into small heretic runes, Archives turn into Codex Cicatrixi, Forges turn into Mawed Crucibles. Ideally, Heretics would be able to gain an amount of these new powers and use them to turn the tide against the cultists, amassing their power and almost forming a sect of their own in turn which sweeps over and converts the cult. ### Cultists When a Cultist sacrifices a heretic, two things will happen: - A new item will be available for creation at one of the dispensers. - The Heretic will be trapped inside a powerful Haunted Blade. `/obj/item/melee/cultblade/haunted` ` name = "haunted longsword"` ` desc = "An eerie sword with a blade that is less 'black' than it is 'absolute nothingness'. It glows with furious, restrained green energy."` This blade will be stronger across-the-board than a normal cult sword, and will even allow those who wield it to cast one heretic spell from their previous path. The only downside? The heretic can also cast one spell. It's up to the trapped spirit if it wants to help you, or be a nuisance. The unlocked items are: - The Cursed Blade, again. For cultists, it can be used to draw runes twice as fast as usual, and they can even right-click it to teleport to safety, just like a heretic! - The Crimson Focus, again. Cultists are twice as fast at carving spells into their body, and they gain a 5th spellslot as long as they wear the amulet. It still causes hemophilia and grants weak regeneration. - The Proteon Orb. This orb will create a gateway to Nar'sie's own realm, spawning one Proteon every 15 seconds, which ghosts can possess. The gateways cannot be placed close to one another. Originally, they were going to be able to create a Harvester Shell, but there were some concerns of it being too OP. The true Bastard sword has been fully deleted. The null rod conversion has been changed to a Bloody Halberd instead. I'm considering re-enabling Stun Hand on Heretics, with Mansus Grasp stats. ### Other All the items above can be used by both Heretics and Cultists, no matter how they were first created. Hell, even normal crew can use them! This is probably not the best idea a lot of the time, though. There are a lot of other changes in this PR. A loooooot. I will likely miss some in the changelog, but I'll try to be as thorough as possible. There's probably also some leftover garbo that I didn't find and clear out yet. ## Why It's Good For The Game Cult and Heretics, despite being mortally opposed, have very few interactions with eachother, especially now that the Blade's gone. The only thing of note is just the Heretic's unfair complete resistance to stun hand, which is only marginally better than the alternative. This PR will reintroduce their animosity, and give both sides a very, very good reason to fight eachother. The Cult will gain a sick sword that keeps the heretic in the game, and unlike with the original implementation, will recieve a cult-wide bonus in the form of a powerful, well deserved, and fun new item to summon. The Heretic will gain powerful trinkets and knowledge from the sacrifices, incentivizing them to become a terrifying cult-hunter. And if they do succeed in wiping out the cult, they will have quite the rewards to help with their ascension. The crew, while mostly unaffected, will have a damn good reason to not just Side with the heretic, out of fear of what they may become after the cult is stomped down. They can also use a few of the items here in an attempt to get one up on either side, as long as they manage to stay clear of the side-effects. Let the heretics eradicate the apostates. Let the cultists root out the heathens.  The haunted longsword creates an aura of darkness (disabled for the cultist for the image) Sprites... are not great. Hopefully someone comes by and improves them. code: Added get_inactive_hand() as an easy shortcut for carbons code: Wall walker element can now accept a trait for wall-checking fix: Fixed soulsword component being unable to invoke the post summon callback refactor: Turned Heretic rust turf healing into an element, given to Rust Walkers and Rusted Harvesters refactor: Converted Limb Amputation from an element to a component Blade and Sword sprites by meyhaza!!! I did the inhands though. Cuz im cool
@@ -113,6 +113,7 @@
|
||||
#define CONSTRUCT_JUGGERNAUT "Juggernaut"
|
||||
#define CONSTRUCT_WRAITH "Wraith"
|
||||
#define CONSTRUCT_ARTIFICER "Artificer"
|
||||
#define CONSTRUCT_HARVESTER "Harvester"
|
||||
|
||||
/// The Classic Wizard wizard loadout.
|
||||
#define WIZARD_LOADOUT_CLASSIC "loadout_classic"
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#define COLOR_LIME "#32CD32"
|
||||
#define COLOR_DARK_LIME "#00aa00"
|
||||
#define COLOR_VERY_PALE_LIME_GREEN "#DDFFD3"
|
||||
#define COLOR_HERETIC_GREEN COLOR_VERY_PALE_LIME_GREEN // i am co-opting this as heretic glow.
|
||||
#define COLOR_VERY_DARK_LIME_GREEN "#003300"
|
||||
#define COLOR_GREEN "#008000"
|
||||
#define COLOR_CHRISTMAS_GREEN "#00873E"
|
||||
@@ -130,6 +131,7 @@
|
||||
#define COLOR_DARK_ORANGE "#C3630C"
|
||||
#define COLOR_PRISONER_ORANGE "#A54900"
|
||||
#define COLOR_DARK_MODERATE_ORANGE "#8B633B"
|
||||
#define COLOR_RUSTED_GLASS "#917c65"
|
||||
|
||||
#define COLOR_BROWN "#BA9F6D"
|
||||
#define COLOR_DARK_BROWN "#997C4F"
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#define RUNE_COLOR_SUMMON COLOR_VIBRANT_LIME
|
||||
|
||||
//blood magic
|
||||
/// The maximum number of cult spell slots each cultist is allowed to scribe at once.
|
||||
#define ENHANCED_BLOODCHARGE 5
|
||||
#define MAX_BLOODCHARGE 4
|
||||
#define RUNELESS_MAX_BLOODCHARGE 1
|
||||
/// percent before rise
|
||||
@@ -28,6 +30,8 @@
|
||||
#define THEME_CULT "cult"
|
||||
#define THEME_WIZARD "wizard"
|
||||
#define THEME_HOLY "holy"
|
||||
/// Only used for heretic Harvesters, obtained from sacrificing cultists
|
||||
#define THEME_HERETIC "heretic"
|
||||
|
||||
/// Defines for cult item_dispensers.
|
||||
#define PREVIEW_IMAGE "preview"
|
||||
@@ -51,3 +55,8 @@ GLOBAL_LIST(sacrificed)
|
||||
#define CULT_VICTORY 1
|
||||
#define CULT_LOSS 0
|
||||
#define CULT_NARSIE_KILLED -1
|
||||
|
||||
// Used to keep track of items rewarded after a heretic is sacked.
|
||||
#define CURSED_BLADE_UNLOCKED "Cursed Blade"
|
||||
#define CRIMSON_FOCUS_UNLOCKED "Crimson Focus"
|
||||
#define PROTEON_ORB_UNLOCKED "Proteon Orb"
|
||||
|
||||
@@ -134,6 +134,9 @@
|
||||
///cancel clean
|
||||
#define COMSIG_ATOM_CANCEL_CLEAN (1<<0)
|
||||
|
||||
/// From /obj/item/stack/make_item()
|
||||
#define COMSIG_ATOM_CONSTRUCTED "atom_constructed"
|
||||
|
||||
/// From /obj/effect/particle_effect/sparks/proc/sparks_touched(datum/source, atom/movable/singed)
|
||||
#define COMSIG_ATOM_TOUCHED_SPARKS "atom_touched_sparks"
|
||||
#define COMSIG_ATOM_TOUCHED_HAZARDOUS_SPARKS "atom_touched_hazardous_sparks"
|
||||
|
||||
@@ -204,6 +204,10 @@
|
||||
#define STOP_SACRIFICE (1<<0)
|
||||
/// Don't send a message for sacrificing this thing, we have our own
|
||||
#define SILENCE_SACRIFICE_MESSAGE (1<<1)
|
||||
/// Don't send a message for sacrificing this thing UNLESS it's the cult target
|
||||
#define SILENCE_NONTARGET_SACRIFICE_MESSAGE (1<<2)
|
||||
/// Dusts the target instead of gibbing them (no soulstone)
|
||||
#define DUST_SACRIFICE (1<<3)
|
||||
|
||||
/// From /mob/living/befriend() : (mob/living/new_friend)
|
||||
#define COMSIG_LIVING_BEFRIENDED "living_befriended"
|
||||
|
||||
@@ -231,6 +231,8 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
|
||||
|
||||
#define islandmine(A) (istype(A, /obj/effect/mine))
|
||||
|
||||
#define iscloset(A) (istype(A, /obj/structure/closet))
|
||||
|
||||
#define issupplypod(A) (istype(A, /obj/structure/closet/supplypod))
|
||||
|
||||
#define isammocasing(A) (istype(A, /obj/item/ammo_casing))
|
||||
|
||||
@@ -98,6 +98,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_IWASBATONED "iwasbatoned"
|
||||
#define TRAIT_SLEEPIMMUNE "sleep_immunity"
|
||||
#define TRAIT_PUSHIMMUNE "push_immunity"
|
||||
/// can't be kicked to the side
|
||||
#define TRAIT_NO_SIDE_KICK "no_side_kick"
|
||||
/// Are we immune to shocks?
|
||||
#define TRAIT_SHOCKIMMUNE "shock_immunity"
|
||||
/// Are we immune to specifically tesla / SM shocks?
|
||||
@@ -1089,6 +1091,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
/// Note this doesn't mean all spells are guaranteed to work or the mob is guaranteed to cast
|
||||
#define TRAIT_CASTABLE_LOC "castable_loc"
|
||||
|
||||
/// Needs above trait to work.
|
||||
/// This trait makes it so that any cast spells will attempt to transfer to the location's location.
|
||||
/// For example, a heretic inside the haunted blade's spells would emanate from the mob wielding the sword.
|
||||
#define TRAIT_SPELLS_TRANSFER_TO_LOC "spells_transfer_to_loc"
|
||||
|
||||
///Trait given by /datum/element/relay_attacker
|
||||
#define TRAIT_RELAYING_ATTACKER "relaying_attacker"
|
||||
|
||||
@@ -1151,6 +1158,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
/// Trait applied to objects and mobs that can attack a boulder and break it down. (See /obj/item/boulder/manual_process())
|
||||
#define TRAIT_BOULDER_BREAKER "boulder_breaker"
|
||||
|
||||
/// Trait given to anything linked to, not necessarily allied to, the mansus
|
||||
#define TRAIT_MANSUS_TOUCHED "mansus_touched"
|
||||
|
||||
/// Trait given to mobs wearing the clown mask
|
||||
#define TRAIT_PERCEIVED_AS_CLOWN "perceived_as_clown"
|
||||
/// Does this item bypass ranged armor checks?
|
||||
|
||||
@@ -400,8 +400,8 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
/**
|
||||
* Checks whether or not a particular typepath or subtype of it is present on a turf
|
||||
*
|
||||
* Returns TRUE if an instance of the desired type or a subtype of it is found
|
||||
* Returns FALSE if the type is not found, or if no turf is supplied
|
||||
* Returns the first instance located if an instance of the desired type or a subtype of it is found
|
||||
* Returns null if the type is not found, or if no turf is supplied
|
||||
*
|
||||
* Arguments:
|
||||
* * location - The turf to be checked for the desired type
|
||||
@@ -409,10 +409,9 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
*/
|
||||
/proc/is_type_on_turf(turf/location, type_to_find)
|
||||
if(!location)
|
||||
return FALSE
|
||||
if(locate(type_to_find) in location)
|
||||
return TRUE
|
||||
return FALSE
|
||||
return
|
||||
var/found_type = locate(type_to_find) in location
|
||||
return found_type
|
||||
|
||||
/**
|
||||
* get_blueprint_data
|
||||
|
||||
@@ -492,7 +492,6 @@ GLOBAL_LIST_INIT(phobia_objs, list(
|
||||
/obj/item/clothing/suit/wizrobe,
|
||||
/obj/item/clothing/under/rank/civilian/chaplain,
|
||||
/obj/item/codex_cicatrix,
|
||||
/obj/item/cult_bastard,
|
||||
/obj/item/gun/magic,
|
||||
/obj/item/melee/cultblade,
|
||||
/obj/item/melee/rune_carver,
|
||||
|
||||
@@ -56,6 +56,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR,
|
||||
"TRAIT_NO_THROW_HITPUSH" = TRAIT_NO_THROW_HITPUSH,
|
||||
"TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
|
||||
"TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
|
||||
"TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT,
|
||||
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
|
||||
"TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION,
|
||||
@@ -149,6 +150,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_BOMBIMMUNE" = TRAIT_BOMBIMMUNE,
|
||||
"TRAIT_BONSAI" = TRAIT_BONSAI,
|
||||
"TRAIT_BOOZE_SLIDER" = TRAIT_BOOZE_SLIDER,
|
||||
"TRAIT_BOXING_READY" = TRAIT_BOXING_READY,
|
||||
"TRAIT_BORN_MONKEY" = TRAIT_BORN_MONKEY,
|
||||
"TRAIT_BOXING_READY" = TRAIT_BOXING_READY,
|
||||
"TRAIT_BRAINWASHING" = TRAIT_BRAINWASHING,
|
||||
@@ -296,6 +298,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_MAGICALLY_GIFTED" = TRAIT_MAGICALLY_GIFTED,
|
||||
"TRAIT_MAGICALLY_PHASED" = TRAIT_MAGICALLY_PHASED,
|
||||
"TRAIT_MARTIAL_ARTS_IMMUNE" = TRAIT_MARTIAL_ARTS_IMMUNE,
|
||||
"TRAIT_MANSUS_TOUCHED" = TRAIT_MANSUS_TOUCHED,
|
||||
"TRAIT_MEDIBOTCOMINGTHROUGH" = TRAIT_MEDIBOTCOMINGTHROUGH,
|
||||
"TRAIT_MEDICAL_HUD" = TRAIT_MEDICAL_HUD,
|
||||
"TRAIT_MESON_VISION" = TRAIT_MESON_VISION,
|
||||
@@ -535,6 +538,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_NO_BARCODES" = TRAIT_NO_BARCODES,
|
||||
"TRAIT_NO_STORAGE_INSERT" = TRAIT_NO_STORAGE_INSERT,
|
||||
"TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT,
|
||||
"TRAIT_NO_SIDE_KICK" = TRAIT_NO_SIDE_KICK,
|
||||
"TRAIT_NODROP" = TRAIT_NODROP,
|
||||
"TRAIT_OMNI_BAIT" = TRAIT_OMNI_BAIT,
|
||||
"TRAIT_PLANT_WILDMUTATE" = TRAIT_PLANT_WILDMUTATE,
|
||||
|
||||
@@ -525,7 +525,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
alerttooltipstyle = "cult"
|
||||
var/static/image/narnar
|
||||
var/angle = 0
|
||||
var/mob/living/basic/construct/Cviewer
|
||||
var/mob/living/basic/construct/construct_owner
|
||||
|
||||
/atom/movable/screen/alert/bloodsense/Initialize(mapload, datum/hud/hud_owner)
|
||||
. = ..()
|
||||
@@ -533,7 +533,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
/atom/movable/screen/alert/bloodsense/Destroy()
|
||||
Cviewer = null
|
||||
construct_owner = null
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
return ..()
|
||||
|
||||
@@ -543,45 +543,53 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
if(!owner.mind)
|
||||
return
|
||||
|
||||
var/datum/antagonist/cult/antag = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if(!antag)
|
||||
return
|
||||
var/datum/objective/sacrifice/sac_objective = locate() in antag.cult_team.objectives
|
||||
if(isconstruct(owner))
|
||||
construct_owner = owner
|
||||
else
|
||||
construct_owner = null
|
||||
|
||||
if(antag.cult_team.blood_target)
|
||||
if(!get_turf(antag.cult_team.blood_target))
|
||||
antag.cult_team.unset_blood_target()
|
||||
else
|
||||
blood_target = antag.cult_team.blood_target
|
||||
if(Cviewer?.seeking && Cviewer.master)
|
||||
blood_target = Cviewer.master
|
||||
desc = "Your blood sense is leading you to [Cviewer.master]"
|
||||
if(!blood_target)
|
||||
if(sac_objective && !sac_objective.check_completion())
|
||||
if(icon_state == "runed_sense0")
|
||||
return
|
||||
animate(src, transform = null, time = 1, loop = 0)
|
||||
angle = 0
|
||||
cut_overlays()
|
||||
icon_state = "runed_sense0"
|
||||
desc = "Nar'Sie demands that [sac_objective.target] be sacrificed before the summoning ritual can begin."
|
||||
add_overlay(sac_objective.sac_image)
|
||||
else
|
||||
var/datum/objective/eldergod/summon_objective = locate() in antag.cult_team.objectives
|
||||
if(!summon_objective)
|
||||
return
|
||||
var/list/location_list = list()
|
||||
for(var/area/area_to_check in summon_objective.summon_spots)
|
||||
location_list += area_to_check.get_original_area_name()
|
||||
desc = "The sacrifice is complete, summon Nar'Sie! The summoning can only take place in [english_list(location_list)]!"
|
||||
if(icon_state == "runed_sense1")
|
||||
return
|
||||
animate(src, transform = null, time = 1, loop = 0)
|
||||
angle = 0
|
||||
cut_overlays()
|
||||
icon_state = "runed_sense1"
|
||||
add_overlay(narnar)
|
||||
return
|
||||
// construct track
|
||||
if(construct_owner?.seeking && construct_owner.master)
|
||||
blood_target = construct_owner.master
|
||||
desc = "Your blood sense is leading you to [construct_owner.master]"
|
||||
|
||||
// cult track
|
||||
var/datum/antagonist/cult/antag = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if(antag)
|
||||
var/datum/objective/sacrifice/sac_objective = locate() in antag.cult_team.objectives
|
||||
if(antag.cult_team.blood_target)
|
||||
if(!get_turf(antag.cult_team.blood_target))
|
||||
antag.cult_team.unset_blood_target()
|
||||
else
|
||||
blood_target = antag.cult_team.blood_target
|
||||
if(!blood_target)
|
||||
if(sac_objective && !sac_objective.check_completion())
|
||||
if(icon_state == "runed_sense0")
|
||||
return
|
||||
animate(src, transform = null, time = 1, loop = 0)
|
||||
angle = 0
|
||||
cut_overlays()
|
||||
icon_state = "runed_sense0"
|
||||
desc = "Nar'Sie demands that [sac_objective.target] be sacrificed before the summoning ritual can begin."
|
||||
add_overlay(sac_objective.sac_image)
|
||||
else
|
||||
var/datum/objective/eldergod/summon_objective = locate() in antag.cult_team.objectives
|
||||
if(!summon_objective)
|
||||
return
|
||||
var/list/location_list = list()
|
||||
for(var/area/area_to_check in summon_objective.summon_spots)
|
||||
location_list += area_to_check.get_original_area_name()
|
||||
desc = "The sacrifice is complete, summon Nar'Sie! The summoning can only take place in [english_list(location_list)]!"
|
||||
if(icon_state == "runed_sense1")
|
||||
return
|
||||
animate(src, transform = null, time = 1, loop = 0)
|
||||
angle = 0
|
||||
cut_overlays()
|
||||
icon_state = "runed_sense1"
|
||||
add_overlay(narnar)
|
||||
return
|
||||
|
||||
// actual tracking
|
||||
var/turf/P = get_turf(blood_target)
|
||||
var/turf/Q = get_turf(owner)
|
||||
if(!P || !Q || (P.z != Q.z)) //The target is on a different Z level, we cannot sense that far.
|
||||
@@ -593,6 +601,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
desc = "You are currently tracking [real_target.real_name] in [get_area_name(blood_target)]."
|
||||
else
|
||||
desc = "You are currently tracking [blood_target] in [get_area_name(blood_target)]."
|
||||
|
||||
var/target_angle = get_angle(Q, P)
|
||||
var/target_dist = get_dist(P, Q)
|
||||
cut_overlays()
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/cult_dagger/Trigger(trigger_flags)
|
||||
for(var/obj/item/held_item as anything in owner.held_items) // In case we were already holding a dagger
|
||||
if(istype(held_item, /obj/item/melee/cultblade/dagger))
|
||||
held_item.attack_self(owner)
|
||||
return
|
||||
if(target in owner.held_items)
|
||||
var/obj/item/target_item = target
|
||||
target_item.attack_self(owner)
|
||||
return
|
||||
var/obj/item/target_item = target
|
||||
if(owner.can_equip(target_item, ITEM_SLOT_HANDS))
|
||||
owner.temporarilyRemoveItemFromInventory(target_item)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/// This component will intercept bare-handed attacks by the owner on sufficiently injured carbons and amputate random limbs instead
|
||||
/datum/element/amputating_limbs
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
argument_hash_start_idx = 2
|
||||
/datum/component/amputating_limbs
|
||||
/// How long does it take?
|
||||
var/surgery_time
|
||||
/// What is the means by which we describe the act of amputation?
|
||||
@@ -12,34 +10,38 @@
|
||||
var/snip_chance
|
||||
/// The types of limb we can remove
|
||||
var/list/target_zones
|
||||
/// Callback for a proc right before confirming the attack. If it returns FALSE, cancel
|
||||
var/datum/callback/pre_hit_callback
|
||||
|
||||
/datum/element/amputating_limbs/Attach(
|
||||
datum/target,
|
||||
/datum/component/amputating_limbs/Initialize(
|
||||
surgery_time = 5 SECONDS,
|
||||
surgery_verb = "prying",
|
||||
minimum_stat = SOFT_CRIT,
|
||||
snip_chance = 100,
|
||||
list/target_zones = list(BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG),
|
||||
datum/callback/pre_hit_callback,
|
||||
)
|
||||
. = ..()
|
||||
if (!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
if (!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if (!length(target_zones))
|
||||
CRASH("[src] for [target] was not provided a valid list of body zones to target.")
|
||||
CRASH("[src] for [parent] was not provided a valid list of body zones to target.")
|
||||
|
||||
src.surgery_time = surgery_time
|
||||
src.surgery_verb = surgery_verb
|
||||
src.minimum_stat = minimum_stat
|
||||
src.snip_chance = snip_chance
|
||||
src.target_zones = target_zones
|
||||
RegisterSignals(target, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET), PROC_REF(try_amputate))
|
||||
src.pre_hit_callback = pre_hit_callback
|
||||
|
||||
/datum/element/amputating_limbs/Detach(datum/source)
|
||||
UnregisterSignal(source, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET))
|
||||
return ..()
|
||||
/datum/component/amputating_limbs/RegisterWithParent()
|
||||
RegisterSignals(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET), PROC_REF(try_amputate))
|
||||
|
||||
/datum/component/amputating_limbs/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET))
|
||||
|
||||
/// Called when you click on literally anything with your hands, see if it is an injured carbon and then try to cut it up
|
||||
/datum/element/amputating_limbs/proc/try_amputate(mob/living/surgeon, atom/victim, proximity, modifiers)
|
||||
/datum/component/amputating_limbs/proc/try_amputate(mob/living/surgeon, atom/victim, proximity, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
if (!proximity || !iscarbon(victim) || HAS_TRAIT(victim, TRAIT_NODISMEMBER) || !prob(snip_chance))
|
||||
return
|
||||
@@ -52,6 +54,9 @@
|
||||
surgeon.balloon_alert(surgeon, "already busy!")
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
if(pre_hit_callback && !pre_hit_callback.Invoke(victim))
|
||||
return
|
||||
|
||||
var/list/valid_targets = list()
|
||||
for (var/obj/item/bodypart/possible_target as anything in limbed_victim.bodyparts)
|
||||
if (possible_target.bodypart_flags & BODYPART_UNREMOVABLE)
|
||||
@@ -67,8 +72,9 @@
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/// Chop one off
|
||||
/datum/element/amputating_limbs/proc/amputate(mob/living/surgeon, mob/living/carbon/victim, obj/item/bodypart/to_remove)
|
||||
surgeon.visible_message(span_warning("[surgeon] [surgery_verb] [to_remove] off of [victim]!"))
|
||||
/datum/component/amputating_limbs/proc/amputate(mob/living/surgeon, mob/living/carbon/victim, obj/item/bodypart/to_remove)
|
||||
if(surgery_time > 0 SECONDS)
|
||||
surgeon.visible_message(span_warning("[surgeon] is [surgery_verb] the [to_remove] off of [victim]!"))
|
||||
if (surgery_time > 0 && !do_after(surgeon, delay = surgery_time, target = victim))
|
||||
return
|
||||
to_remove.dismember()
|
||||
@@ -128,6 +128,7 @@
|
||||
INVOKE_ASYNC(src, PROC_REF(do_destroy_girder), target, cultist)
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
|
||||
|
||||
if(istype(target, /obj/structure/destructible/cult))
|
||||
INVOKE_ASYNC(src, PROC_REF(do_unanchor_structure), target, cultist)
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
/// Which factions are immune to the damage aura
|
||||
var/list/immune_factions = null
|
||||
|
||||
/// If set, gives a message when damaged
|
||||
var/damage_message = null
|
||||
|
||||
/// Probability for above.
|
||||
var/message_probability = 0
|
||||
|
||||
/// Sets a special set of conditions for the owner
|
||||
var/datum/weakref/current_owner = null
|
||||
|
||||
@@ -54,6 +60,8 @@
|
||||
organ_damage = null,
|
||||
simple_damage = 0,
|
||||
immune_factions = null,
|
||||
damage_message = null,
|
||||
message_probability = 0,
|
||||
mob/living/current_owner = null,
|
||||
)
|
||||
if (!isatom(parent))
|
||||
@@ -72,6 +80,8 @@
|
||||
src.organ_damage = organ_damage
|
||||
src.simple_damage = simple_damage
|
||||
src.immune_factions = immune_factions
|
||||
src.damage_message = damage_message
|
||||
src.message_probability = message_probability
|
||||
src.current_owner = WEAKREF(current_owner)
|
||||
|
||||
/datum/component/damage_aura/Destroy(force)
|
||||
@@ -120,6 +130,9 @@
|
||||
if (candidate.health < candidate.maxHealth)
|
||||
new /obj/effect/temp_visual/cosmic_gem(get_turf(candidate))
|
||||
|
||||
if(damage_message && prob(message_probability))
|
||||
to_chat(candidate, damage_message)
|
||||
|
||||
if (iscarbon(candidate) || issilicon(candidate) || isbasicmob(candidate))
|
||||
candidate.adjustBruteLoss(brute_damage * seconds_per_tick, updating_health = FALSE)
|
||||
candidate.adjustFireLoss(burn_damage * seconds_per_tick, updating_health = FALSE)
|
||||
|
||||
@@ -142,13 +142,20 @@
|
||||
return
|
||||
if (extra_control_checks && !extra_control_checks.Invoke(harbinger))
|
||||
return
|
||||
|
||||
harbinger.log_message("took control of [new_body].", LOG_GAME)
|
||||
// doesn't transfer mind because that transfers antag datum as well
|
||||
new_body.key = harbinger.key
|
||||
to_chat(new_body, span_boldnotice(assumed_control_message))
|
||||
after_assumed_control?.Invoke(harbinger)
|
||||
|
||||
// Already qdels due to below proc but just in case
|
||||
qdel(src)
|
||||
|
||||
/// When someone else assumes control via some other means, get rid of our component
|
||||
/datum/component/ghost_direct_control/proc/on_login()
|
||||
/// When someone assumes control, get rid of our component
|
||||
/datum/component/ghost_direct_control/proc/on_login(mob/harbinger)
|
||||
SIGNAL_HANDLER
|
||||
// This proc is called the very moment .key is set, so we need to force mind to initialize here if we want the invoke to affect the mind of the mob
|
||||
if(isnull(harbinger.mind))
|
||||
harbinger.mind_initialize()
|
||||
to_chat(harbinger, span_boldnotice(assumed_control_message))
|
||||
after_assumed_control?.Invoke(harbinger)
|
||||
qdel(src)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
var/list/faction
|
||||
/// List of weak references to things we have already created
|
||||
var/list/spawned_things = list()
|
||||
/// Callback to a proc that is called when a mob is spawned. Primarily used for sentient spawners.
|
||||
var/datum/callback/spawn_callback
|
||||
/// How many mobs can we spawn maximum each time we try to spawn? (1 - max)
|
||||
var/max_spawn_per_attempt
|
||||
/// Distance from the spawner to spawn mobs
|
||||
@@ -19,7 +21,7 @@
|
||||
var/spawn_distance_exclude
|
||||
COOLDOWN_DECLARE(spawn_delay)
|
||||
|
||||
/datum/component/spawner/Initialize(spawn_types = list(), spawn_time = 30 SECONDS, max_spawned = 5, max_spawn_per_attempt = 1 , faction = list(FACTION_MINING), spawn_text = null, spawn_distance = 1, spawn_distance_exclude = 0)
|
||||
/datum/component/spawner/Initialize(spawn_types = list(), spawn_time = 30 SECONDS, max_spawned = 5, max_spawn_per_attempt = 1 , faction = list(FACTION_MINING), spawn_text = null, datum/callback/spawn_callback = null, spawn_distance = 1, spawn_distance_exclude = 0, initial_spawn_delay = 0 SECONDS)
|
||||
if (!islist(spawn_types))
|
||||
CRASH("invalid spawn_types to spawn specified for spawner component!")
|
||||
src.spawn_time = spawn_time
|
||||
@@ -27,9 +29,13 @@
|
||||
src.faction = faction
|
||||
src.spawn_text = spawn_text
|
||||
src.max_spawned = max_spawned
|
||||
src.spawn_callback = spawn_callback
|
||||
src.max_spawn_per_attempt = max_spawn_per_attempt
|
||||
src.spawn_distance = spawn_distance
|
||||
src.spawn_distance_exclude = spawn_distance_exclude
|
||||
// If set, doesn't instantly spawn a creature when the spawner component is applied.
|
||||
if(initial_spawn_delay)
|
||||
COOLDOWN_START(src, spawn_delay, spawn_time)
|
||||
|
||||
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(stop_spawning))
|
||||
RegisterSignal(parent, COMSIG_VENT_WAVE_CONCLUDED, PROC_REF(stop_spawning))
|
||||
@@ -89,6 +95,7 @@
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_SPAWNER_SPAWNED, created)
|
||||
RegisterSignal(created, COMSIG_QDELETING, PROC_REF(on_deleted))
|
||||
spawn_callback?.Invoke(created)
|
||||
|
||||
|
||||
if (spawn_text)
|
||||
|
||||
@@ -6,12 +6,20 @@
|
||||
/datum/component/spirit_holding
|
||||
///bool on if this component is currently polling for observers to inhabit the item
|
||||
var/attempting_awakening = FALSE
|
||||
/// Allows renaming the bound item
|
||||
var/allow_renaming
|
||||
/// Allows channeling
|
||||
var/allow_channeling
|
||||
///mob contained in the item.
|
||||
var/mob/living/basic/shade/bound_spirit
|
||||
|
||||
/datum/component/spirit_holding/Initialize()
|
||||
/datum/component/spirit_holding/Initialize(datum/mind/soul_to_bind, mob/awakener, allow_renaming = TRUE, allow_channeling = TRUE)
|
||||
if(!ismovable(parent)) //you may apply this to mobs, i take no responsibility for how that works out
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
src.allow_renaming = allow_renaming
|
||||
src.allow_channeling = allow_channeling
|
||||
if(soul_to_bind)
|
||||
bind_the_soule(soul_to_bind, awakener, soul_to_bind.name)
|
||||
|
||||
/datum/component/spirit_holding/Destroy(force)
|
||||
. = ..()
|
||||
@@ -30,7 +38,7 @@
|
||||
/datum/component/spirit_holding/proc/on_examine(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
if(!bound_spirit)
|
||||
examine_list += span_notice("[parent] sleeps. Use [parent] in your hands to attempt to awaken it.")
|
||||
examine_list += span_notice("[parent] sleeps.[allow_channeling ? " Use [parent] in your hands to attempt to awaken it." : ""]")
|
||||
return
|
||||
examine_list += span_notice("[parent] is alive.")
|
||||
|
||||
@@ -48,6 +56,9 @@
|
||||
thing.balloon_alert(user, "spirits are unwilling!")
|
||||
to_chat(user, span_warning("Anomalous otherworldly energies block you from awakening [parent]!"))
|
||||
return
|
||||
if(!allow_channeling && bound_spirit)
|
||||
to_chat(user, span_warning("Try as you might, the spirit within slumbers."))
|
||||
return
|
||||
attempting_awakening = TRUE
|
||||
thing.balloon_alert(user, "channeling...")
|
||||
var/mob/chosen_one = SSpolling.poll_ghosts_for_target(
|
||||
@@ -74,28 +85,29 @@
|
||||
|
||||
// Immediately unregister to prevent making a new spirit
|
||||
UnregisterSignal(parent, COMSIG_ITEM_ATTACK_SELF)
|
||||
|
||||
if(QDELETED(parent)) //if the thing that we're conjuring a spirit in has been destroyed, don't create a spirit
|
||||
to_chat(ghost, span_userdanger("The new vessel for your spirit has been destroyed! You remain an unbound ghost."))
|
||||
return
|
||||
|
||||
bound_spirit = new(parent)
|
||||
bound_spirit.ckey = ghost.ckey
|
||||
bound_spirit.fully_replace_character_name(null, "The spirit of [parent]")
|
||||
bound_spirit.status_flags |= GODMODE
|
||||
bound_spirit.copy_languages(awakener, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the awakener.
|
||||
bound_spirit.get_language_holder().omnitongue = TRUE //Grants omnitongue
|
||||
bind_the_soule(ghost, awakener)
|
||||
|
||||
//Add new signals for parent and stop attempting to awaken
|
||||
RegisterSignal(parent, COMSIG_ATOM_RELAYMOVE, PROC_REF(block_buckle_message))
|
||||
RegisterSignal(parent, COMSIG_BIBLE_SMACKED, PROC_REF(on_bible_smacked))
|
||||
attempting_awakening = FALSE
|
||||
|
||||
if(!allow_renaming)
|
||||
return
|
||||
// Now that all of the important things are in place for our spirit, it's time for them to choose their name.
|
||||
var/valid_input_name = custom_name(awakener)
|
||||
if(valid_input_name)
|
||||
bound_spirit.fully_replace_character_name(null, "The spirit of [valid_input_name]")
|
||||
|
||||
attempting_awakening = FALSE
|
||||
/datum/component/spirit_holding/proc/bind_the_soule(datum/mind/chosen_spirit, mob/awakener, name_override)
|
||||
bound_spirit = new(parent)
|
||||
chosen_spirit.transfer_to(bound_spirit)
|
||||
bound_spirit.fully_replace_character_name(null, "The spirit of [name_override ? name_override : parent]")
|
||||
bound_spirit.get_language_holder().omnitongue = TRUE //Grants omnitongue
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_RELAYMOVE, PROC_REF(block_buckle_message))
|
||||
RegisterSignal(parent, COMSIG_BIBLE_SMACKED, PROC_REF(on_bible_smacked))
|
||||
|
||||
/**
|
||||
* custom_name : Simply sends a tgui input text box to the blade asking what name they want to be called, and retries it if the input is invalid.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/***
|
||||
* # Cult eyes element
|
||||
*
|
||||
* Applies and removes the glowing cult eyes
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
/**
|
||||
* Cult eye setter proc
|
||||
*
|
||||
* Changes the eye color, and adds the glowing eye trait to the mob.
|
||||
* * Changes the eye color, and adds the glowing eye trait to the mob.
|
||||
*/
|
||||
/datum/element/cult_eyes/proc/set_eyes(mob/living/target)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
@@ -94,8 +94,9 @@
|
||||
eyes.set_organ_damage(eyes.low_threshold)
|
||||
|
||||
// At over 10 damage, there is a 50% chance they drop all their items
|
||||
if (prob(50))
|
||||
if (target.stat != DEAD && target.drop_all_held_items())
|
||||
if (prob(50) && target.stat != DEAD)
|
||||
var/list/dropped = target.drop_all_held_items()
|
||||
if(length(dropped))
|
||||
to_chat(target, span_danger("You drop what you're holding and clutch at your eyes!"))
|
||||
target.adjust_eye_blur_up_to(20 SECONDS, EYESTAB_MAX_BLUR)
|
||||
target.Unconscious(2 SECONDS)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/// Buffs and heals the target while standing on rust.
|
||||
/datum/element/leeching_walk
|
||||
|
||||
/datum/element/leeching_walk/Attach(datum/target)
|
||||
. = ..()
|
||||
if (!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
||||
RegisterSignal(target, COMSIG_LIVING_LIFE, PROC_REF(on_life))
|
||||
|
||||
/datum/element/leeching_walk/Detach(datum/source)
|
||||
. = ..()
|
||||
UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_LIFE))
|
||||
|
||||
/*
|
||||
* Signal proc for [COMSIG_MOVABLE_MOVED].
|
||||
*
|
||||
* Checks if we should have baton resistance on the new turf.
|
||||
*/
|
||||
/datum/element/leeching_walk/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/mover_turf = get_turf(source)
|
||||
if(HAS_TRAIT(mover_turf, TRAIT_RUSTY))
|
||||
ADD_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
|
||||
return
|
||||
|
||||
REMOVE_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
|
||||
|
||||
/**
|
||||
* Signal proc for [COMSIG_LIVING_LIFE].
|
||||
*
|
||||
* Gradually heals the heretic ([source]) on rust,
|
||||
* including baton knockdown and stamina damage.
|
||||
*/
|
||||
/datum/element/leeching_walk/proc/on_life(mob/living/source, seconds_per_tick, times_fired)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/our_turf = get_turf(source)
|
||||
if(!HAS_TRAIT(our_turf, TRAIT_RUSTY))
|
||||
return
|
||||
|
||||
// Heals all damage + Stamina
|
||||
var/need_mob_update = FALSE
|
||||
need_mob_update += source.adjustBruteLoss(-3, updating_health = FALSE)
|
||||
need_mob_update += source.adjustFireLoss(-3, updating_health = FALSE)
|
||||
need_mob_update += source.adjustToxLoss(-3, updating_health = FALSE, forced = TRUE) // Slimes are people to
|
||||
need_mob_update += source.adjustOxyLoss(-1.5, updating_health = FALSE)
|
||||
need_mob_update += source.adjustStaminaLoss(-10, updating_stamina = FALSE)
|
||||
if(need_mob_update)
|
||||
source.updatehealth()
|
||||
// Reduces duration of stuns/etc
|
||||
source.AdjustAllImmobility(-0.5 SECONDS)
|
||||
// Heals blood loss
|
||||
if(source.blood_volume < BLOOD_VOLUME_NORMAL)
|
||||
source.blood_volume += 2.5 * seconds_per_tick
|
||||
@@ -4,16 +4,20 @@
|
||||
argument_hash_start_idx = 2
|
||||
/// What kind of walls can we pass through?
|
||||
var/wall_type
|
||||
/// What trait on turfs allows us to pass through? Can be used as OR if wall_type is null, or AND if it's set.
|
||||
var/or_trait
|
||||
|
||||
/datum/element/wall_walker/Attach(
|
||||
datum/target,
|
||||
wall_type = /turf/closed/wall,
|
||||
or_trait,
|
||||
)
|
||||
. = ..()
|
||||
if (!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
src.wall_type = wall_type
|
||||
src.or_trait = or_trait
|
||||
RegisterSignal(target, COMSIG_LIVING_WALL_BUMP, PROC_REF(try_pass_wall))
|
||||
RegisterSignal(target, COMSIG_LIVING_WALL_EXITED, PROC_REF(exit_wall))
|
||||
|
||||
@@ -23,7 +27,10 @@
|
||||
|
||||
/// If the wall is of the proper type, pass into it and keep hold on whatever you're pulling
|
||||
/datum/element/wall_walker/proc/try_pass_wall(mob/living/passing_mob, turf/closed/bumped_wall)
|
||||
if(!istype(bumped_wall, wall_type))
|
||||
if(wall_type && !istype(bumped_wall, wall_type))
|
||||
return
|
||||
|
||||
if(or_trait && !HAS_TRAIT(bumped_wall, or_trait))
|
||||
return
|
||||
|
||||
var/atom/movable/stored_pulling = passing_mob.pulling
|
||||
|
||||
@@ -317,6 +317,13 @@
|
||||
/obj/machinery/door/window/narsie_act()
|
||||
add_atom_colour(NARSIE_WINDOW_COLOUR, FIXED_COLOUR_PRIORITY)
|
||||
|
||||
/obj/machinery/door/window/rust_heretic_act()
|
||||
add_atom_colour(COLOR_RUSTED_GLASS, FIXED_COLOUR_PRIORITY)
|
||||
AddElement(/datum/element/rust)
|
||||
set_armor(/datum/armor/none)
|
||||
take_damage(get_integrity() * 0.5)
|
||||
modify_max_integrity(max_integrity * 0.5)
|
||||
|
||||
/obj/machinery/door/window/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
|
||||
return (exposed_temperature > T0C + (reinf ? 1600 : 800))
|
||||
|
||||
|
||||
@@ -97,6 +97,15 @@
|
||||
/obj/effect/decal/cleanable/trail_holder/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
// normal version of the above trail holder object for use in less convoluted things
|
||||
/obj/effect/decal/cleanable/blood/trails
|
||||
desc = "Looks like a corpse was smeared all over the floor like ketchup. Kinda makes you hungry."
|
||||
random_icon_states = list("trails_1", "trails_2")
|
||||
icon_state = "trails_1"
|
||||
beauty = -50
|
||||
dryname = "dried tracks"
|
||||
drydesc = "Looks like a corpse was smeared all over the floor like ketchup, but it's all dried up and nasty now, ew. You lose some of your appetite."
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs
|
||||
name = "gibs"
|
||||
desc = "They look bloody and gruesome."
|
||||
|
||||
@@ -426,6 +426,7 @@
|
||||
|
||||
if(created)
|
||||
created.setDir(builder.dir)
|
||||
SEND_SIGNAL(created, COMSIG_ATOM_CONSTRUCTED, builder)
|
||||
on_item_crafted(builder, created)
|
||||
|
||||
// Use up the material
|
||||
|
||||
@@ -1013,7 +1013,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/closet/container_resist_act(mob/living/user)
|
||||
/obj/structure/closet/container_resist_act(mob/living/user, loc_required = TRUE)
|
||||
if(isstructure(loc))
|
||||
relay_container_resist_act(user, loc)
|
||||
if(opened)
|
||||
@@ -1038,7 +1038,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
|
||||
addtimer(CALLBACK(src, PROC_REF(check_if_shake)), 1 SECONDS)
|
||||
|
||||
if(do_after(user,(breakout_time), target = src))
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) )
|
||||
if(!user || user.stat != CONSCIOUS || (loc_required && (user.loc != src)) || opened || (!locked && !welded) )
|
||||
return
|
||||
//we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting
|
||||
user.visible_message(span_danger("[user] successfully broke out of [src]!"),
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/obj/structure/closet/body_bag/environmental/prisoner/container_resist_act(mob/living/user)
|
||||
/obj/structure/closet/body_bag/environmental/prisoner/container_resist_act(mob/living/user, loc_required = TRUE)
|
||||
/// copy-pasted with changes because flavor text as well as some other misc stuff
|
||||
if(opened)
|
||||
return
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
spawn_time = spawn_time, \
|
||||
max_spawned = max_mobs, \
|
||||
faction = faction, \
|
||||
spawn_text = spawn_text, \
|
||||
spawn_text = spawn_text,\
|
||||
spawn_callback = CALLBACK(src, PROC_REF(on_mob_spawn)), \
|
||||
initial_spawn_delay = !mapload, \
|
||||
)
|
||||
|
||||
/obj/structure/spawner/attack_animal(mob/living/simple_animal/user, list/modifiers)
|
||||
@@ -78,6 +80,8 @@
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/spawner/proc/on_mob_spawn(atom/created_atom)
|
||||
return
|
||||
|
||||
/obj/structure/spawner/syndicate
|
||||
name = "warp beacon"
|
||||
@@ -226,3 +230,74 @@
|
||||
newmob.desc = "It's [living_mob], but [living_mob.p_their()] flesh has an ashy texture, and [living_mob.p_their()] face is featureless save an eerie smile."
|
||||
src.visible_message(span_warning("[living_mob] reemerges from the link!"))
|
||||
qdel(living_mob)
|
||||
|
||||
/obj/structure/spawner/sentient
|
||||
var/role_name = "A sentient mob"
|
||||
var/assumed_control_message = "You are a sentient mob from a badly coded spawner"
|
||||
|
||||
/obj/structure/spawner/sentient/Initialize(mapload)
|
||||
. = ..()
|
||||
notify_ghosts(
|
||||
"A [name] has been created in \the [get_area(src)]!",
|
||||
source = src,
|
||||
header = "Sentient Spawner Created",
|
||||
notify_flags = NOTIFY_CATEGORY_NOFLASH,
|
||||
)
|
||||
|
||||
/obj/structure/spawner/sentient/on_mob_spawn(atom/created_atom)
|
||||
created_atom.AddComponent(\
|
||||
/datum/component/ghost_direct_control,\
|
||||
role_name = src.role_name,\
|
||||
assumed_control_message = src.assumed_control_message,\
|
||||
after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\
|
||||
)
|
||||
|
||||
/obj/structure/spawner/sentient/proc/became_player_controlled(mob/proteon)
|
||||
return
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner
|
||||
name = "eldritch gateway"
|
||||
desc = "A dizzying structure that somehow links into Nar'Sie's own domain. The screams of the damned echo continously."
|
||||
icon = 'icons/obj/antags/cult/structures.dmi'
|
||||
icon_state = "hole"
|
||||
light_power = 2
|
||||
light_color = COLOR_CULT_RED
|
||||
max_integrity = 50
|
||||
density = FALSE
|
||||
max_mobs = 2
|
||||
spawn_time = 15 SECONDS
|
||||
mob_types = list(/mob/living/basic/construct/proteon/hostile)
|
||||
spawn_text = "arises from"
|
||||
faction = list(FACTION_CULT)
|
||||
role_name = "A proteon cult construct"
|
||||
assumed_control_message = null
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner/examine_status(mob/user)
|
||||
if(IS_CULTIST(user) || !isliving(user))
|
||||
return span_cult("It's at <b>[round(atom_integrity * 100 / max_integrity)]%</b> stability.")
|
||||
return ..()
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_CULTIST(user) && isliving(user))
|
||||
var/mob/living/living_user = user
|
||||
living_user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15)
|
||||
. += span_danger("The voices of the damned echo relentlessly in your mind, continously rebounding on the walls of your self the more you focus on [src]. Your head pounds, better keep away...")
|
||||
else
|
||||
. += span_cult("The gateway will create one weak proteon construct every [spawn_time * 0.1] seconds, up to a total of [max_mobs], that may be controlled by the spirits of the dead.")
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner/became_player_controlled(mob/living/basic/construct/proteon/proteon)
|
||||
proteon.mind.add_antag_datum(/datum/antagonist/cult)
|
||||
proteon.add_filter("awoken_proteon", 3, list("type" = "outline", "color" = COLOR_CULT_RED, "size" = 2))
|
||||
visible_message(span_cult_bold("[proteon] awakens, glowing an eerie red as it stirs from its stupor!"))
|
||||
playsound(proteon, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE)
|
||||
proteon.balloon_alert_to_viewers("awoken!")
|
||||
addtimer(CALLBACK(src, PROC_REF(remove_wake_outline), proteon), 8 SECONDS)
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner/proc/remove_wake_outline(mob/proteon)
|
||||
proteon.remove_filter("awoken_proteon")
|
||||
proteon.add_filter("sentient_proteon", 3, list("type" = "outline", "color" = COLOR_CULT_RED, "size" = 2, "alpha" = 40))
|
||||
|
||||
/obj/structure/spawner/sentient/proteon_spawner/handle_deconstruct(disassembled)
|
||||
playsound('sound/hallucinations/veryfar_noise.ogg', 125)
|
||||
visible_message(span_cult_bold("[src] completely falls apart, the screams of the damned reaching a feverous pitch before slowly fading away into nothing."))
|
||||
|
||||
@@ -568,6 +568,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/spawner, 0)
|
||||
|
||||
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/unanchored/spawner, 0)
|
||||
|
||||
// You can't rust glass! So only reinforced glass can be impacted.
|
||||
/obj/structure/window/reinforced/rust_heretic_act()
|
||||
add_atom_colour(COLOR_RUSTED_GLASS, FIXED_COLOUR_PRIORITY)
|
||||
AddElement(/datum/element/rust)
|
||||
set_armor(/datum/armor/none)
|
||||
take_damage(get_integrity() * 0.5)
|
||||
modify_max_integrity(max_integrity * 0.5)
|
||||
|
||||
/obj/structure/window/plasma
|
||||
name = "plasma window"
|
||||
desc = "A window made out of a plasma-silicate alloy. It looks insanely tough to break and burn through."
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
default_button_position = DEFAULT_BLOODSPELLS
|
||||
var/list/spells = list()
|
||||
var/channeling = FALSE
|
||||
/// If the magic has been enhanced somehow, likely due to a crimson focus.
|
||||
var/magic_enhanced = FALSE
|
||||
|
||||
/datum/action/innate/cult/blood_magic/Remove()
|
||||
for(var/X in spells)
|
||||
@@ -29,7 +31,7 @@
|
||||
var/atom/movable/screen/movable/action_button/button = viewers[hud]
|
||||
var/position = screen_loc_to_offset(button.screen_loc)
|
||||
var/list/position_list = list()
|
||||
for(var/possible_position in 1 to MAX_BLOODCHARGE)
|
||||
for(var/possible_position in 1 to magic_enhanced ? ENHANCED_BLOODCHARGE : MAX_BLOODCHARGE)
|
||||
position_list += possible_position
|
||||
for(var/datum/action/innate/cult/blood_spell/blood_spell in spells)
|
||||
if(blood_spell.positioned)
|
||||
@@ -50,10 +52,10 @@
|
||||
rune = TRUE
|
||||
break
|
||||
if(rune)
|
||||
limit = MAX_BLOODCHARGE
|
||||
limit = magic_enhanced ? ENHANCED_BLOODCHARGE : MAX_BLOODCHARGE
|
||||
if(length(spells) >= limit)
|
||||
if(rune)
|
||||
to_chat(owner, span_cult_italic("You cannot store more than [MAX_BLOODCHARGE] spells. <b>Pick a spell to remove.</b>"))
|
||||
to_chat(owner, span_cult_italic("You cannot store more than [limit] spells. <b>Pick a spell to remove.</b>"))
|
||||
else
|
||||
to_chat(owner, span_cult_bold_italic("<u>You cannot store more than [RUNELESS_MAX_BLOODCHARGE] spells without an empowering rune! Pick a spell to remove.</u>"))
|
||||
var/nullify_spell = tgui_input_list(owner, "Spell to remove", "Current Spells", spells)
|
||||
@@ -86,10 +88,15 @@
|
||||
else
|
||||
to_chat(owner, span_cult_italic("You are already invoking blood magic!"))
|
||||
return
|
||||
if(do_after(owner, 100 - rune*60, target = owner))
|
||||
var/spell_carving_timer = 10 SECONDS
|
||||
if(rune)
|
||||
spell_carving_timer = 4 SECONDS
|
||||
if(magic_enhanced)
|
||||
spell_carving_timer *= 0.5
|
||||
if(do_after(owner, spell_carving_timer, target = owner))
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/human_owner = owner
|
||||
human_owner.bleed(40 - rune*32)
|
||||
human_owner.bleed(rune ? 8 : 40)
|
||||
var/datum/action/innate/cult/blood_spell/new_spell = new BS(owner.mind)
|
||||
new_spell.Grant(owner, src)
|
||||
spells += new_spell
|
||||
@@ -428,19 +435,35 @@
|
||||
return
|
||||
var/datum/antagonist/cult/cultist = IS_CULTIST(user)
|
||||
var/datum/team/cult/cult_team = cultist.get_team()
|
||||
var/effect_coef = 1 - (cult_team.cult_risen ? 0.4 : 0) - (cult_team.cult_ascendent ? 0.5 : 0)
|
||||
var/effect_coef = 1
|
||||
if(cult_team.cult_ascendent)
|
||||
effect_coef = 0.1
|
||||
else if(cult_team.cult_risen)
|
||||
effect_coef = 0.4
|
||||
user.visible_message(
|
||||
span_warning("[user] holds up [user.p_their()] hand, which explodes in a flash of red light!"),
|
||||
span_cult_italic("You attempt to stun [target] with the spell!"),
|
||||
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
||||
)
|
||||
user.mob_light(range = 1.1, power = 2, color = LIGHT_COLOR_BLOOD_MAGIC, duration = 0.2 SECONDS)
|
||||
// Heretics are momentarily disoriented by the stunning aura. Enough for both parties to go 'oh shit' but only a mild combat ability.
|
||||
// Heretics have an identical effect on their grasp. The cultist's worse spell preparation is offset by their extra gear and teammates.
|
||||
if(IS_HERETIC(target))
|
||||
to_chat(user, span_warning("Some force greater than you intervenes! [target] is protected by the Forgotten Gods!"))
|
||||
to_chat(target, span_warning("You are protected by your faith to the Forgotten Gods."))
|
||||
target.AdjustKnockdown(0.5 SECONDS)
|
||||
target.adjust_confusion_up_to(1.5 SECONDS, 3 SECONDS)
|
||||
target.adjust_dizzy_up_to(1.5 SECONDS, 3 SECONDS)
|
||||
ADD_TRAIT(target, TRAIT_NO_SIDE_KICK, REF(src)) // We don't want this to be a good stunning tool, just minor disorientation
|
||||
addtimer(TRAIT_CALLBACK_REMOVE(target, TRAIT_NO_SIDE_KICK, REF(src)), 1 SECONDS)
|
||||
|
||||
var/old_color = target.color
|
||||
target.color = rgb(0, 128, 0)
|
||||
animate(target, color = old_color, time = 1 SECONDS, easing = EASE_IN)
|
||||
target.color = COLOR_HERETIC_GREEN
|
||||
animate(target, color = old_color, time = 4 SECONDS, easing = EASE_IN)
|
||||
target.mob_light(range = 1.5, power = 2.5, color = COLOR_HERETIC_GREEN, duration = 0.5 SECONDS)
|
||||
playsound(target, 'sound/magic/magic_block_mind.ogg', 150, TRUE) // insanely quiet
|
||||
|
||||
to_chat(user, span_warning("An eldritch force intervenes as you touch [target], absorbing most of the effects!"))
|
||||
to_chat(target, span_warning("As [user] touches you with vile magicks, the Mansus absorbs most of the effects!"))
|
||||
target.balloon_alert_to_viewers("absorbed!")
|
||||
else if(target.can_block_magic())
|
||||
to_chat(user, span_warning("The spell had no effect!"))
|
||||
else
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
|
||||
/// Cult Bastard Sword, earned by cultists when they manage to sacrifice a heretic.
|
||||
/obj/item/cult_bastard
|
||||
name = "bloody bastard sword"
|
||||
desc = "An enormous sword used by Nar'Sien cultists to rapidly harvest the souls of non-believers."
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
block_chance = 50
|
||||
block_sound = 'sound/weapons/parry.ogg'
|
||||
throwforce = 20
|
||||
force = 35
|
||||
armour_penetration = 45
|
||||
throw_speed = 1
|
||||
throw_range = 3
|
||||
sharpness = SHARP_EDGED
|
||||
light_color = "#ff0000"
|
||||
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
|
||||
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
|
||||
icon = 'icons/obj/weapons/sword.dmi'
|
||||
icon_state = "cultbastard"
|
||||
inhand_icon_state = "cultbastard"
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
inhand_x_dimension = 64
|
||||
inhand_y_dimension = 64
|
||||
actions_types = list()
|
||||
item_flags = SLOWS_WHILE_IN_HAND
|
||||
///if we are using our attack_self ability
|
||||
var/spinning = FALSE
|
||||
|
||||
/obj/item/cult_bastard/Initialize(mapload)
|
||||
. = ..()
|
||||
set_light(4)
|
||||
AddComponent(/datum/component/butchering, 50, 80)
|
||||
AddComponent(/datum/component/two_handed, require_twohands = TRUE)
|
||||
AddComponent(/datum/component/soul_stealer, soulstone_type = /obj/item/soulstone)
|
||||
AddComponent( \
|
||||
/datum/component/spin2win, \
|
||||
spin_cooldown_time = 25 SECONDS, \
|
||||
on_spin_callback = CALLBACK(src, PROC_REF(on_spin)), \
|
||||
on_unspin_callback = CALLBACK(src, PROC_REF(on_unspin)), \
|
||||
start_spin_message = span_danger("%USER begins swinging the sword around with inhuman strength!"), \
|
||||
end_spin_message = span_warning("%USER's inhuman strength dissipates and the sword's runes grow cold!") \
|
||||
)
|
||||
|
||||
/obj/item/cult_bastard/proc/on_spin(mob/living/user, duration)
|
||||
var/oldcolor = user.color
|
||||
user.color = "#ff0000"
|
||||
user.add_stun_absorption(
|
||||
source = name,
|
||||
duration = duration,
|
||||
priority = 2,
|
||||
message = span_warning("%EFFECT_OWNER doesn't even flinch as the sword's power courses through [user.p_them()]!"),
|
||||
self_message = span_boldwarning("You shrug off the stun!"),
|
||||
examine_message = span_warning("%EFFECT_OWNER_THEYRE glowing with a blazing red aura!"),
|
||||
)
|
||||
user.spin(duration, 1)
|
||||
animate(user, color = oldcolor, time = duration, easing = EASE_IN)
|
||||
addtimer(CALLBACK(user, TYPE_PROC_REF(/atom, update_atom_colour)), duration)
|
||||
block_chance = 100
|
||||
slowdown += 1.5
|
||||
spinning = TRUE
|
||||
|
||||
/obj/item/cult_bastard/proc/on_unspin(mob/living/user)
|
||||
block_chance = 50
|
||||
slowdown -= 1.5
|
||||
spinning = FALSE
|
||||
|
||||
/obj/item/cult_bastard/can_be_pulled(user)
|
||||
return FALSE
|
||||
|
||||
/obj/item/cult_bastard/pickup(mob/living/user)
|
||||
. = ..()
|
||||
if(!IS_CULTIST(user))
|
||||
if(!IS_HERETIC(user))
|
||||
to_chat(user, "<span class='cultlarge'>\"I wouldn't advise that.\"</span>")
|
||||
force = 5
|
||||
return
|
||||
else
|
||||
to_chat(user, span_cult_large("\"You cling to the Forgotten Gods, as if you're more than their pawn.\""))
|
||||
to_chat(user, span_userdanger("A horrible force yanks at your arm!"))
|
||||
user.emote("scream")
|
||||
user.apply_damage(30, BRUTE, pick(GLOB.arm_zones))
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.Paralyze(50)
|
||||
return
|
||||
force = initial(force)
|
||||
|
||||
/obj/item/cult_bastard/IsReflect(def_zone)
|
||||
if(!spinning)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/cult_bastard/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
|
||||
if(!prob(final_block_chance))
|
||||
return FALSE
|
||||
owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!"))
|
||||
return TRUE
|
||||
@@ -76,6 +76,8 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
block_sound = 'sound/weapons/parry.ogg'
|
||||
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
|
||||
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
|
||||
/// If TRUE, it can be used at will by anyone, non-cultists included
|
||||
var/free_use = FALSE
|
||||
|
||||
/obj/item/melee/cultblade/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -93,7 +95,7 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
return FALSE
|
||||
|
||||
/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
if(!IS_CULTIST(user))
|
||||
if(!IS_CULTIST(user) && !free_use)
|
||||
user.Paralyze(100)
|
||||
user.dropItemToGround(src, TRUE)
|
||||
user.visible_message(span_warning("A powerful force shoves [user] away from [target]!"), \
|
||||
@@ -106,6 +108,174 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
return
|
||||
..()
|
||||
|
||||
#define WIELDER_SPELLS "wielder_spell"
|
||||
#define SWORD_SPELLS "sword_spell"
|
||||
#define SWORD_PREFIX "sword_prefix"
|
||||
|
||||
/obj/item/melee/cultblade/haunted
|
||||
name = "haunted longsword"
|
||||
desc = "An eerie sword with a blade that is less 'black' than it is 'absolute nothingness'. It glows with furious, restrained green energy."
|
||||
icon_state = "hauntedblade"
|
||||
inhand_icon_state = "hauntedblade"
|
||||
worn_icon_state = "hauntedblade"
|
||||
force = 35
|
||||
throwforce = 35
|
||||
block_chance = 55
|
||||
wound_bonus = -25
|
||||
bare_wound_bonus = 30
|
||||
free_use = TRUE
|
||||
light_color = COLOR_HERETIC_GREEN
|
||||
light_range = 4
|
||||
/// holder for the actual action when created.
|
||||
var/datum/action/cooldown/spell/path_wielder_action
|
||||
var/mob/living/trapped_entity
|
||||
/// The heretic path that the variable below uses to index abilities. Assigned when the heretic is ensouled.
|
||||
var/heretic_path
|
||||
/// Nested static list used to index abilities and names.
|
||||
var/static/list/heretic_paths_to_haunted_sword_abilities = list(
|
||||
// Ash
|
||||
PATH_ASH = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/ash_beams),
|
||||
SWORD_PREFIX = "ashen",
|
||||
),
|
||||
// Flesh
|
||||
PATH_FLESH = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/blood_siphon),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/cleave),
|
||||
SWORD_PREFIX = "sanguine",
|
||||
),
|
||||
// Void
|
||||
PATH_VOID = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/void_phase),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/cone/staggered/cone_of_cold/void),
|
||||
SWORD_PREFIX = "tenebrous",
|
||||
),
|
||||
// Blade
|
||||
PATH_BLADE = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/furious_steel/haunted),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/furious_steel/solo),
|
||||
SWORD_PREFIX = "keen",
|
||||
),
|
||||
// Rust
|
||||
PATH_RUST = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/cone/staggered/entropic_plume),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/aoe/rust_conversion, /datum/action/cooldown/spell/pointed/rust_construction),
|
||||
SWORD_PREFIX = "rusted",
|
||||
),
|
||||
// Cosmic
|
||||
PATH_COSMIC = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/conjure/cosmic_expansion),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/star_blast),
|
||||
SWORD_PREFIX = "astral",
|
||||
),
|
||||
// Lock
|
||||
PATH_LOCK = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/burglar_finesse),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/apetra_vulnera),
|
||||
SWORD_PREFIX = "incisive",
|
||||
),
|
||||
// Moon
|
||||
PATH_MOON = list(
|
||||
WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/moon_parade),
|
||||
SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/moon_smile),
|
||||
SWORD_PREFIX = "shimmering",
|
||||
),
|
||||
// Starter
|
||||
PATH_START = list(
|
||||
WIELDER_SPELLS = null,
|
||||
SWORD_SPELLS = null,
|
||||
SWORD_PREFIX = "stillborn", // lol loser
|
||||
) ,
|
||||
)
|
||||
|
||||
/obj/item/melee/cultblade/haunted/Initialize(mapload, mob/soul_to_bind, mob/awakener, do_bind = TRUE)
|
||||
. = ..()
|
||||
|
||||
AddElement(/datum/element/heretic_focus)
|
||||
add_traits(list(TRAIT_CASTABLE_LOC, TRAIT_SPELLS_TRANSFER_TO_LOC), INNATE_TRAIT)
|
||||
if(do_bind && !mapload)
|
||||
bind_soul(soul_to_bind, awakener)
|
||||
|
||||
/obj/item/melee/cultblade/haunted/proc/bind_soul(mob/soul_to_bind, mob/awakener)
|
||||
|
||||
var/datum/mind/trapped_mind = soul_to_bind?.mind
|
||||
|
||||
if(!trapped_mind)
|
||||
return // Can't do anything further down the list
|
||||
|
||||
if(trapped_mind)
|
||||
AddComponent(/datum/component/spirit_holding,\
|
||||
soul_to_bind = trapped_mind,\
|
||||
awakener = awakener,\
|
||||
allow_renaming = FALSE,\
|
||||
allow_channeling = FALSE,\
|
||||
)
|
||||
|
||||
// Get the heretic's new body and antag datum.
|
||||
trapped_entity = trapped_mind?.current
|
||||
trapped_entity.key = trapped_mind?.key
|
||||
var/datum/antagonist/heretic/heretic_holder = IS_HERETIC(trapped_entity)
|
||||
if(!heretic_holder)
|
||||
stack_trace("[soul_to_bind] in but not a heretic on the heretic soul blade.")
|
||||
|
||||
// Give the spirit a spell that lets them try to fly around.
|
||||
var/datum/action/cooldown/spell/pointed/sword_fling/fling_act = \
|
||||
new /datum/action/cooldown/spell/pointed/sword_fling(trapped_mind, to_fling = src)
|
||||
fling_act.Grant(trapped_entity)
|
||||
|
||||
// Set the sword's path for spell selection.
|
||||
heretic_path = heretic_holder.heretic_path
|
||||
|
||||
// Copy the objectives to keep for roundend, remove the datum as neither us nor the heretic need it anymore
|
||||
var/list/copied_objectives = heretic_holder.objectives.Copy()
|
||||
trapped_entity.mind.remove_antag_datum(/datum/antagonist/heretic)
|
||||
|
||||
// Add the fallen antag datum, give them a heads-up of what's happening.
|
||||
var/datum/antagonist/soultrapped_heretic/bozo = new()
|
||||
bozo.objectives |= copied_objectives
|
||||
trapped_entity.mind.add_antag_datum(bozo)
|
||||
to_chat(trapped_entity, span_userdanger("You've been sacrificed to the Enemy, and trapped inside a haunted blade! While you cannot escape, you may help the Cult, your current wielder, or even pester everyone with what few abilities you kept."))
|
||||
|
||||
// Assigning the spells to give to the wielder and spirit.
|
||||
// Let them cast the given spell.
|
||||
ADD_TRAIT(trapped_entity, TRAIT_ALLOW_HERETIC_CASTING, INNATE_TRAIT)
|
||||
|
||||
var/list/path_spells = heretic_paths_to_haunted_sword_abilities[heretic_path]
|
||||
|
||||
var/list/wielder_spells = path_spells[WIELDER_SPELLS]
|
||||
var/list/sword_spells = path_spells[SWORD_SPELLS]
|
||||
|
||||
name = "[path_spells[SWORD_PREFIX]] [name]"
|
||||
|
||||
|
||||
// Granting the path spells. The sword spirit gains it outright, while it's just instanced for wielders to be added on pickup.
|
||||
|
||||
if(sword_spells)
|
||||
for(var/datum/action/cooldown/spell/sword_spell as anything in sword_spells)
|
||||
sword_spell = new sword_spell(trapped_entity)
|
||||
sword_spell?.Grant(trapped_entity)
|
||||
sword_spell?.overlay_icon_state = "bg_cult_border" // for flavor, and also helps distinguish
|
||||
|
||||
|
||||
if(wielder_spells)
|
||||
for(var/datum/action/cooldown/spell/wielder_spell as anything in wielder_spells)
|
||||
path_wielder_action = new wielder_spell(src)
|
||||
wielder_spell?.overlay_icon_state = "bg_cult_border"
|
||||
|
||||
/obj/item/melee/cultblade/haunted/equipped(mob/user, slot, initial)
|
||||
. = ..()
|
||||
if(slot & ITEM_SLOT_HANDS)
|
||||
path_wielder_action?.Grant(user)
|
||||
|
||||
/obj/item/melee/cultblade/haunted/dropped(mob/user, silent)
|
||||
. = ..()
|
||||
path_wielder_action?.Remove(user)
|
||||
|
||||
#undef WIELDER_SPELLS
|
||||
#undef SWORD_SPELLS
|
||||
#undef SWORD_PREFIX
|
||||
|
||||
/obj/item/melee/cultblade/ghost
|
||||
name = "eldritch sword"
|
||||
force = 19 //can't break normal airlocks
|
||||
@@ -120,7 +290,7 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
|
||||
/obj/item/melee/cultblade/pickup(mob/living/user)
|
||||
..()
|
||||
if(!IS_CULTIST(user))
|
||||
if(!IS_CULTIST(user) && !free_use)
|
||||
to_chat(user, span_cult_large("\"I wouldn't advise that.\""))
|
||||
|
||||
/datum/action/innate/dash/cult
|
||||
@@ -563,6 +733,78 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
|
||||
#undef MAX_SHUTTLE_CURSES
|
||||
|
||||
#define GATEWAY_TURF_SCAN_RANGE 40
|
||||
|
||||
/obj/item/proteon_orb
|
||||
name = "summoning orb"
|
||||
desc = "An eerie translucent orb that feels impossibly light. Legends say summoning orbs are created from corrupted scrying orbs. If you hold it close to your ears, you can hear the screams of the damned."
|
||||
icon = 'icons/obj/antags/cult/items.dmi'
|
||||
icon_state = "summoning_orb"
|
||||
light_range = 3
|
||||
light_color = COLOR_CULT_RED
|
||||
|
||||
/obj/item/proteon_orb/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_CULTIST(user) && isliving(user))
|
||||
var/mob/living/living_user = user
|
||||
living_user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5)
|
||||
. += span_danger("It hurts just to look at it. Better keep away.")
|
||||
else
|
||||
. += span_cult("It can be used to create a gateway to Nar'Sie's domain, which will summon weak, sentient constructs over time.")
|
||||
|
||||
/obj/item/proteon_orb/attack_self(mob/living/user)
|
||||
|
||||
var/list/turfs_to_scan = detect_room(get_turf(user), max_size = GATEWAY_TURF_SCAN_RANGE)
|
||||
|
||||
if(!IS_CULTIST(user))
|
||||
to_chat(user, span_cult_large("\"You want to enter my domain? Go ahead.\""))
|
||||
turfs_to_scan = null // narsie wants to have some fun and the veil wont stop her
|
||||
|
||||
for(var/turf/hole_candidate as anything in turfs_to_scan)
|
||||
if(locate(/obj/structure/spawner/sentient/proteon_spawner) in hole_candidate)
|
||||
to_chat(user, span_cult_bold("There's a gateway too close nearby. The veil is not yet weak enough to allow such close rips in its fabric."))
|
||||
return
|
||||
to_chat(user, span_cult_bold_italic("You focus on [src] and direct it into the ground. It rumbles..."))
|
||||
|
||||
var/turf/open/hole_spot = get_turf(user)
|
||||
if(!istype(hole_spot) || isgroundlessturf(hole_spot))
|
||||
to_chat(user, span_notice("This is not a suitable spot."))
|
||||
return
|
||||
|
||||
INVOKE_ASYNC(hole_spot, TYPE_PROC_REF(/turf/open, quake_gateway), user)
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* Bespoke proc that happens when a proteon orb is activated, creating a gateway.
|
||||
* If activated by a non-cultist, they get an unusual game over.
|
||||
*/
|
||||
/turf/open/proc/quake_gateway(mob/living/user)
|
||||
Shake(2, 2, 5 SECONDS)
|
||||
narsie_act(TRUE, TRUE, 100)
|
||||
var/fucked = FALSE
|
||||
if(!IS_CULTIST(user))
|
||||
fucked = TRUE
|
||||
ADD_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src)) // keep em in place
|
||||
user.add_atom_colour(COLOR_CULT_RED, TEMPORARY_COLOUR_PRIORITY)
|
||||
user.visible_message(span_cult_bold("Dark tendrils appear from the ground and root [user] in place!"))
|
||||
sleep(5 SECONDS) // can we still use these or. i mean its async
|
||||
new /obj/structure/spawner/sentient/proteon_spawner(src)
|
||||
visible_message(span_cult_bold("A mysterious hole appears out of nowhere!"))
|
||||
if(!fucked || QDELETED(user))
|
||||
return
|
||||
if(get_turf(user) != src) // they get away. for now
|
||||
REMOVE_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src))
|
||||
return
|
||||
user.visible_message(span_cult_bold("[user] is pulled into the portal through an infinitesmally minuscule hole, shredding [user.p_their()] body!"))
|
||||
sleep(5 SECONDS)
|
||||
user.visible_message(span_cult_italic("An unusually large construct appears through the portal!"))
|
||||
user.gib() // total destruction
|
||||
var/mob/living/basic/construct/proteon/hostile/remnant = new(get_step_rand(src))
|
||||
remnant.name = "[user]" // no, they do not become it
|
||||
remnant.transform *= 1.5
|
||||
|
||||
#undef GATEWAY_TURF_SCAN_RANGE
|
||||
|
||||
/obj/item/cult_shift
|
||||
name = "veil shifter"
|
||||
desc = "This relic instantly teleports you, and anything you're pulling, forward by a moderate distance."
|
||||
|
||||
@@ -16,8 +16,15 @@
|
||||
/proc/is_convertable_to_cult(mob/living/target, datum/team/cult/specific_cult)
|
||||
if(!istype(target))
|
||||
return FALSE
|
||||
if(isnull(target.mind) || !GET_CLIENT(target))
|
||||
if(isnull(target.mind))
|
||||
return FALSE
|
||||
|
||||
// disables client checks if testing for easier debugging
|
||||
#ifndef TESTING
|
||||
if(!GET_CLIENT(target))
|
||||
return FALSE
|
||||
#endif
|
||||
|
||||
if(target.mind.unconvertable)
|
||||
return FALSE
|
||||
if(ishuman(target) && target.mind.holy_role)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ELDRITCH_WHETSTONE "Eldritch Whetstone"
|
||||
#define CONSTRUCT_SHELL "Construct Shell"
|
||||
#define UNHOLY_WATER "Flask of Unholy Water"
|
||||
#define PROTEON_ORB "Portal Summoning Orb"
|
||||
|
||||
// Cult altar. Gives out consumable items.
|
||||
/obj/structure/destructible/cult/item_dispenser/altar
|
||||
@@ -10,6 +11,7 @@
|
||||
cult_examine_tip = "Can be used to create eldritch whetstones, construct shells, and flasks of unholy water."
|
||||
icon_state = "talismanaltar"
|
||||
break_message = "<span class='warning'>The altar shatters, leaving only the wailing of the damned!</span>"
|
||||
mansus_conversion_path = /obj/effect/heretic_rune
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/altar/setup_options()
|
||||
var/static/list/altar_items = list(
|
||||
@@ -27,7 +29,20 @@
|
||||
),
|
||||
)
|
||||
|
||||
var/extra_item = extra_options()
|
||||
|
||||
options = altar_items
|
||||
if(!isnull(extra_item))
|
||||
options += extra_item
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/altar/extra_options()
|
||||
if(!cult_team?.unlocked_heretic_items[PROTEON_ORB_UNLOCKED])
|
||||
return
|
||||
return list(PROTEON_ORB = list(
|
||||
PREVIEW_IMAGE = image(icon = 'icons/obj/antags/cult/items.dmi', icon_state = "summoning_orb"),
|
||||
OUTPUT_ITEMS = list(/obj/item/proteon_orb),
|
||||
),
|
||||
)
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/altar/succcess_message(mob/living/user, obj/item/spawned_item)
|
||||
to_chat(user, span_cult_italic("You kneel before [src] and your faith is rewarded with [spawned_item]!"))
|
||||
@@ -35,3 +50,4 @@
|
||||
#undef ELDRITCH_WHETSTONE
|
||||
#undef CONSTRUCT_SHELL
|
||||
#undef UNHOLY_WATER
|
||||
#undef PROTEON_ORB
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define CULT_BLINDFOLD "Zealot's Blindfold"
|
||||
#define CURSE_ORB "Shuttle Curse"
|
||||
#define VEIL_WALKER "Veil Walker Set"
|
||||
#define CRIMSON_FOCUS "Crimson Focus"
|
||||
|
||||
// Cult archives. Gives out utility items.
|
||||
/obj/structure/destructible/cult/item_dispenser/archives
|
||||
@@ -12,6 +13,7 @@
|
||||
light_range = 1.5
|
||||
light_color = LIGHT_COLOR_FIRE
|
||||
break_message = "<span class='warning'>The books and tomes of the archives burn into ash as the desk shatters!</span>"
|
||||
mansus_conversion_path = /obj/item/codex_cicatrix
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/archives/setup_options()
|
||||
var/static/list/archive_items = list(
|
||||
@@ -29,7 +31,20 @@
|
||||
),
|
||||
)
|
||||
|
||||
var/extra_item = extra_options()
|
||||
|
||||
options = archive_items
|
||||
if(!isnull(extra_item))
|
||||
options += extra_item
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/archives/extra_options()
|
||||
if(!cult_team?.unlocked_heretic_items[CRIMSON_FOCUS_UNLOCKED])
|
||||
return
|
||||
return list(CRIMSON_FOCUS = list(
|
||||
PREVIEW_IMAGE = image(icon = 'icons/obj/clothing/neck.dmi', icon_state = "crimson_focus"),
|
||||
OUTPUT_ITEMS = list(/obj/item/clothing/neck/heretic_focus/crimson_focus),
|
||||
),
|
||||
)
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/archives/succcess_message(mob/living/user, obj/item/spawned_item)
|
||||
to_chat(user, span_cult_italic("You summon [spawned_item] from [src]!"))
|
||||
@@ -41,3 +56,4 @@
|
||||
#undef CULT_BLINDFOLD
|
||||
#undef CURSE_ORB
|
||||
#undef VEIL_WALKER
|
||||
#undef CRIMSON_FOCUS
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define NARSIE_ARMOR "Nar'Sien Hardened Armor"
|
||||
#define FLAGELLANT_ARMOR "Flagellant's Robe"
|
||||
#define ELDRITCH_SWORD "Eldritch Longsword"
|
||||
#define CURSED_BLADE "Cursed Ritual Blade"
|
||||
|
||||
// Cult forge. Gives out combat weapons.
|
||||
/obj/structure/destructible/cult/item_dispenser/forge
|
||||
@@ -12,6 +13,7 @@
|
||||
light_range = 2
|
||||
light_color = LIGHT_COLOR_LAVA
|
||||
break_message = "<span class='warning'>The forge breaks apart into shards with a howling scream!</span>"
|
||||
mansus_conversion_path = /obj/structure/destructible/eldritch_crucible
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/forge/setup_options()
|
||||
var/static/list/forge_items = list(
|
||||
@@ -29,7 +31,21 @@
|
||||
),
|
||||
)
|
||||
|
||||
var/extra_item = extra_options()
|
||||
|
||||
options = forge_items
|
||||
if(!isnull(extra_item))
|
||||
options += extra_item
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/forge/extra_options()
|
||||
if(!cult_team?.unlocked_heretic_items[CURSED_BLADE_UNLOCKED])
|
||||
return
|
||||
return list(CURSED_BLADE = list(
|
||||
PREVIEW_IMAGE = image(icon = 'icons/obj/weapons/khopesh.dmi', icon_state = "cursed_blade"),
|
||||
OUTPUT_ITEMS = list(/obj/item/melee/sickly_blade/cursed),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/forge/succcess_message(mob/living/user, obj/item/spawned_item)
|
||||
to_chat(user, span_cult_italic("You work [src] as dark knowledge guides your hands, creating [spawned_item]!"))
|
||||
@@ -42,3 +58,4 @@
|
||||
#undef NARSIE_ARMOR
|
||||
#undef FLAGELLANT_ARMOR
|
||||
#undef ELDRITCH_SWORD
|
||||
#undef CURSED_BLADE
|
||||
|
||||
@@ -12,16 +12,43 @@
|
||||
var/cult_examine_tip
|
||||
/// The cooldown for when items can be dispensed.
|
||||
COOLDOWN_DECLARE(use_cooldown)
|
||||
/// Assigned cult team, set when cultistism is checked.
|
||||
var/datum/team/cult/cult_team
|
||||
|
||||
/obj/structure/destructible/cult/Destroy()
|
||||
cult_team = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/destructible/cult/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_ATOM_CONSTRUCTED, PROC_REF(on_constructed))
|
||||
|
||||
/obj/structure/destructible/cult/proc/on_constructed(datum/source, mob/builder)
|
||||
SIGNAL_HANDLER
|
||||
var/datum/antagonist/cult/cultist = builder.mind?.has_antag_datum(/datum/antagonist/cult, TRUE)
|
||||
cult_team = cultist?.get_team()
|
||||
|
||||
/// Tries to find a cultist. If it succeeds, it also takes advantage of the moment to define the structure's cult team if it's not set yet.
|
||||
/obj/structure/destructible/cult/proc/is_cultist_check(mob/fool)
|
||||
|
||||
if(!IS_CULTIST(fool))
|
||||
return FALSE
|
||||
|
||||
if(isnull(cult_team))
|
||||
var/datum/antagonist/cult/cultist = fool.mind?.has_antag_datum(/datum/antagonist/cult, TRUE)
|
||||
cult_team = cultist?.get_team()
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/structure/destructible/cult/examine_status(mob/user)
|
||||
if(IS_CULTIST(user) || isobserver(user))
|
||||
if(is_cultist_check(user) || isobserver(user))
|
||||
return span_cult("It's at <b>[round(atom_integrity * 100 / max_integrity)]%</b> stability.")
|
||||
return ..()
|
||||
|
||||
/obj/structure/destructible/cult/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("[src] is [anchored ? "secured to":"unsecured from"] the floor.")
|
||||
if(IS_CULTIST(user) || isobserver(user))
|
||||
if(is_cultist_check(user) || isobserver(user))
|
||||
if(cult_examine_tip)
|
||||
. += span_cult(cult_examine_tip)
|
||||
if(!COOLDOWN_FINISHED(src, use_cooldown_duration))
|
||||
@@ -65,16 +92,25 @@
|
||||
/obj/structure/destructible/cult/item_dispenser
|
||||
/// An associated list of options this structure can make. See setup_options() for format.
|
||||
var/list/options
|
||||
/// The dispenser will create this item and then delete itself if it is rust converted.
|
||||
var/obj/mansus_conversion_path = /obj/item/skub
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/Initialize(mapload)
|
||||
. = ..()
|
||||
setup_options()
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/rust_heretic_act()
|
||||
visible_message(span_notice("[src] crumbles to dust. In its midst, you spot \a [initial(mansus_conversion_path.name)]."))
|
||||
var/turf/turfy = get_turf(src)
|
||||
new mansus_conversion_path(turfy)
|
||||
turfy.rust_heretic_act()
|
||||
return ..()
|
||||
|
||||
/obj/structure/destructible/cult/item_dispenser/attack_hand(mob/living/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(!isliving(user) || !IS_CULTIST(user))
|
||||
if(!isliving(user) || !is_cultist_check(user))
|
||||
to_chat(user, span_warning("You're pretty sure you know exactly what this is used for and you can't seem to touch it."))
|
||||
return
|
||||
if(!anchored)
|
||||
@@ -84,6 +120,8 @@
|
||||
to_chat(user, span_cult_italic("The magic in [src] is too weak, it will be ready to use again in <b>[DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]</b>."))
|
||||
return
|
||||
|
||||
setup_options()
|
||||
|
||||
var/list/spawned_items = get_items_to_spawn(user)
|
||||
if(!length(spawned_items))
|
||||
return
|
||||
@@ -109,6 +147,18 @@
|
||||
/obj/structure/destructible/cult/item_dispenser/proc/setup_options()
|
||||
return
|
||||
|
||||
/*
|
||||
* Extra options, currently used for items unlocked after sacrificing a heretic.
|
||||
*
|
||||
* The list of options is a associated list of format:
|
||||
* item_name = list(
|
||||
* preview = image(),
|
||||
* output = list(paths),
|
||||
* )
|
||||
*/
|
||||
/obj/structure/destructible/cult/item_dispenser/proc/extra_options()
|
||||
return
|
||||
|
||||
/*
|
||||
* Get all items that this cult building will spawn when interacted with.
|
||||
* Opens a radial menu for the user and shows them the list of options, which they can choose from.
|
||||
@@ -150,7 +200,7 @@
|
||||
* Returns TRUE if the user is a living mob that is a cultist and is not incapacitated.
|
||||
*/
|
||||
/obj/structure/destructible/cult/item_dispenser/proc/check_menu(mob/user)
|
||||
return isliving(user) && IS_CULTIST(user) && !user.incapacitated()
|
||||
return isliving(user) && is_cultist_check(user) && !user.incapacitated()
|
||||
|
||||
// Spooky looking door used in gateways. Or something.
|
||||
/obj/effect/gateway
|
||||
|
||||
@@ -16,9 +16,16 @@
|
||||
var/reckoning_complete = FALSE
|
||||
///Has the cult risen, and gotten red eyes?
|
||||
var/cult_risen = FALSE
|
||||
///Has the cult asceneded, and gotten halos?
|
||||
///Has the cult ascended, and gotten halos?
|
||||
var/cult_ascendent = FALSE
|
||||
|
||||
/// List that keeps track of which items have been unlocked after a heretic was sacked.
|
||||
var/list/unlocked_heretic_items = list(
|
||||
CURSED_BLADE_UNLOCKED = FALSE,
|
||||
CRIMSON_FOCUS_UNLOCKED = FALSE,
|
||||
PROTEON_ORB_UNLOCKED = FALSE,
|
||||
)
|
||||
|
||||
///Has narsie been summoned yet?
|
||||
var/narsie_summoned = FALSE
|
||||
///How large were we at max size.
|
||||
|
||||
@@ -326,16 +326,12 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
return TRUE
|
||||
|
||||
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, datum/team/cult/cult_team)
|
||||
var/big_sac = FALSE
|
||||
var/target_sac = FALSE
|
||||
if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3)
|
||||
for(var/invoker in invokers)
|
||||
to_chat(invoker, span_cult_italic("[sacrificial] is too greatly linked to the world! You need three acolytes!"))
|
||||
return FALSE
|
||||
|
||||
var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers, cult_team)
|
||||
if(signal_result & STOP_SACRIFICE)
|
||||
return FALSE
|
||||
|
||||
if(sacrificial.mind)
|
||||
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial.mind))
|
||||
for(var/datum/objective/sacrifice/sac_objective in cult_team.objectives)
|
||||
@@ -343,15 +339,23 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
sac_objective.sacced = TRUE
|
||||
sac_objective.clear_sacrifice()
|
||||
sac_objective.update_explanation_text()
|
||||
big_sac = TRUE
|
||||
target_sac = TRUE
|
||||
else
|
||||
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial))
|
||||
|
||||
new /obj/effect/temp_visual/cult/sac(loc)
|
||||
|
||||
if(!(signal_result & SILENCE_SACRIFICE_MESSAGE))
|
||||
var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers, cult_team)
|
||||
|
||||
var/do_message = TRUE
|
||||
if(signal_result & SILENCE_SACRIFICE_MESSAGE)
|
||||
do_message = FALSE
|
||||
if((signal_result & SILENCE_NONTARGET_SACRIFICE_MESSAGE) && !(target_sac))
|
||||
do_message = FALSE
|
||||
|
||||
if(do_message)
|
||||
for(var/invoker in invokers)
|
||||
if(big_sac)
|
||||
if(target_sac)
|
||||
to_chat(invoker, span_cult_large("\"Yes! This is the one I desire! You have done well.\""))
|
||||
continue
|
||||
if(ishuman(sacrificial) || iscyborg(sacrificial))
|
||||
@@ -359,6 +363,10 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
else
|
||||
to_chat(invoker, span_cult_large("\"I accept this meager sacrifice.\""))
|
||||
|
||||
// post-message
|
||||
if(signal_result & STOP_SACRIFICE)
|
||||
return FALSE
|
||||
|
||||
if(iscyborg(sacrificial))
|
||||
var/construct_class = show_radial_menu(invokers[1], sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE)
|
||||
if(QDELETED(sacrificial) || !construct_class)
|
||||
@@ -370,17 +378,20 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
sacriborg.mmi = null
|
||||
qdel(sacrificial)
|
||||
return TRUE
|
||||
|
||||
var/obj/item/soulstone/stone = new(loc)
|
||||
if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
|
||||
stone.capture_soul(sacrificial, invokers[1], forced = TRUE)
|
||||
|
||||
if(sacrificial)
|
||||
if(sacrificial && (signal_result & DUST_SACRIFICE)) // No soulstone when dusted
|
||||
playsound(sacrificial, 'sound/magic/teleport_diss.ogg', 100, TRUE)
|
||||
sacrificial.investigate_log("has been sacrificially dusted by the cult.", INVESTIGATE_DEATHS)
|
||||
sacrificial.dust(TRUE, FALSE, TRUE)
|
||||
else if (sacrificial)
|
||||
var/obj/item/soulstone/stone = new(loc)
|
||||
if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
|
||||
stone.capture_soul(sacrificial, invokers[1], forced = TRUE)
|
||||
playsound(sacrificial, 'sound/magic/disintegrate.ogg', 100, TRUE)
|
||||
sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS)
|
||||
sacrificial.gib(DROP_ALL_REMAINS)
|
||||
|
||||
try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Tries to convert a null rod over the rune to a cult sword
|
||||
@@ -396,12 +407,12 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
|
||||
rod.visible_message(span_cult_italic(displayed_message))
|
||||
switch(num_slain)
|
||||
if(0, 1)
|
||||
if(0)
|
||||
animate_spawn_sword(rod, /obj/item/melee/cultblade/dagger)
|
||||
if(2)
|
||||
if(1)
|
||||
animate_spawn_sword(rod, /obj/item/melee/cultblade)
|
||||
else
|
||||
animate_spawn_sword(rod, /obj/item/cult_bastard)
|
||||
animate_spawn_sword(rod, /obj/item/melee/cultblade/halberd)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
/datum/action/cooldown/spell/pointed/sword_fling
|
||||
name = "Sword Fling"
|
||||
desc = "Try to fling yourself around."
|
||||
ranged_mousepointer = 'icons/effects/mouse_pointers/cult_target.dmi'
|
||||
background_icon_state = "bg_heretic"
|
||||
overlay_icon_state = "bg_cult_border"
|
||||
|
||||
button_icon = 'icons/mob/actions/actions_cult.dmi'
|
||||
button_icon_state = "sword_fling"
|
||||
|
||||
school = SCHOOL_EVOCATION
|
||||
cooldown_time = 4 SECONDS
|
||||
invocation_type = INVOCATION_NONE
|
||||
spell_requirements = NONE
|
||||
|
||||
cast_range = 6
|
||||
active_msg = "You ready yourself to attempt to leap!"
|
||||
var/obj/item/flinged_sword
|
||||
|
||||
/datum/action/cooldown/spell/pointed/sword_fling/New(Target, to_fling)
|
||||
. = ..()
|
||||
flinged_sword = to_fling
|
||||
|
||||
/datum/action/cooldown/spell/pointed/sword_fling/Destroy()
|
||||
flinged_sword = null
|
||||
. = ..()
|
||||
|
||||
/datum/action/cooldown/spell/pointed/sword_fling/is_valid_target(atom/cast_on)
|
||||
return isatom(cast_on)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/sword_fling/cast(turf/cast_on)
|
||||
. = ..()
|
||||
var/atom/sword_loc = flinged_sword.loc
|
||||
if(ismob(sword_loc))
|
||||
var/mob/loccer = sword_loc
|
||||
var/resist_chance = 25
|
||||
var/fail_text = "You struggle, but [loccer] keeps [loccer.p_their()] grip on you!"
|
||||
var/particle_to_spawn = null
|
||||
if(IS_CULTIST_OR_CULTIST_MOB(loccer))
|
||||
resist_chance = 10 // your mastahs
|
||||
fail_text = "You struggle, but [loccer]'s grip is unnaturally hard to resist!"
|
||||
particle_to_spawn = /obj/effect/temp_visual/cult/sparks
|
||||
if(IS_HERETIC_OR_MONSTER(loccer) || IS_LUNATIC(loccer))
|
||||
resist_chance = 15
|
||||
fail_text = "You struggle, but [loccer] deftly handles the grip movement."
|
||||
particle_to_spawn = /obj/effect/temp_visual/eldritch_sparks
|
||||
if(loccer.mind?.holy_role) // IS_PRIEST()
|
||||
resist_chance = 12
|
||||
fail_text = "You struggle, but [loccer]'s holy grip holds tight against your thrashing."
|
||||
particle_to_spawn = /obj/effect/temp_visual/blessed
|
||||
if(IS_WIZARD(loccer))
|
||||
resist_chance = 5 // magic master
|
||||
fail_text = "You struggle, but [loccer]'s handle on magic easily neutralizes your movement."
|
||||
particle_to_spawn = /obj/effect/particle_effect/sparks/electricity
|
||||
|
||||
new particle_to_spawn(get_turf(loccer))
|
||||
|
||||
if(prob(resist_chance))
|
||||
flinged_sword.forceMove(get_turf(loccer))
|
||||
flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [loccer]'s grip!"))
|
||||
// flung by later code
|
||||
else
|
||||
to_chat(owner, span_warning(fail_text))
|
||||
return
|
||||
|
||||
if(isitem(sword_loc))
|
||||
flinged_sword.forceMove(get_turf(sword_loc))
|
||||
flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [sword_loc]!"))
|
||||
// flung by later code
|
||||
|
||||
if(iscloset(sword_loc))
|
||||
var/obj/structure/closet/sword_closet = sword_loc
|
||||
if(!(sword_closet.open(owner, force = prob(5), special_effects = TRUE)))
|
||||
sword_closet.container_resist_act(owner, loc_required = FALSE)
|
||||
flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [sword_closet]!"))
|
||||
|
||||
// no general struct/machinery check. imagine if someone put the sword in a vendor
|
||||
|
||||
if(isturf(sword_loc))
|
||||
new /obj/effect/temp_visual/sword_sparks(sword_loc)
|
||||
flinged_sword.throw_at(cast_on, cast_range, flinged_sword.throw_speed, owner)
|
||||
flinged_sword.visible_message(\
|
||||
span_warning("\the [flinged_sword] lunges at \the [cast_on]!"))
|
||||
|
||||
/obj/effect/temp_visual/eldritch_sparks
|
||||
icon_state = "purplesparkles"
|
||||
|
||||
/obj/effect/temp_visual/sword_sparks
|
||||
icon_state = "mech_toxin" // only used in one place and it looks kinda good
|
||||
|
||||
/obj/effect/temp_visual/blessed
|
||||
icon_state = "blessed"
|
||||
@@ -85,6 +85,15 @@
|
||||
PATH_MOON = COLOR_BLUE_LIGHT,
|
||||
)
|
||||
|
||||
/// List that keeps track of which items have been gifted to the heretic after a cultist was sacrificed. Used to alter drop chances to reduce dupes.
|
||||
var/list/unlocked_heretic_items = list(
|
||||
/obj/item/melee/sickly_blade/cursed = 0,
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus = 0,
|
||||
/mob/living/basic/construct/harvester/heretic = 0,
|
||||
)
|
||||
/// Simpler version of above used to limit amount of loot that can be hoarded
|
||||
var/rewards_given = 0
|
||||
|
||||
/datum/antagonist/heretic/Destroy()
|
||||
LAZYNULL(sac_targets)
|
||||
return ..()
|
||||
@@ -230,6 +239,8 @@
|
||||
if (!issilicon(our_mob))
|
||||
GLOB.reality_smash_track.add_tracked_mind(owner)
|
||||
|
||||
ADD_TRAIT(our_mob, TRAIT_MANSUS_TOUCHED, REF(src))
|
||||
RegisterSignal(our_mob, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed))
|
||||
RegisterSignals(our_mob, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED), PROC_REF(on_spell_cast))
|
||||
RegisterSignal(our_mob, COMSIG_USER_ITEM_INTERACTION, PROC_REF(on_item_use))
|
||||
RegisterSignal(our_mob, COMSIG_MOB_LOGIN, PROC_REF(fix_influence_network))
|
||||
@@ -243,12 +254,14 @@
|
||||
if (owner in GLOB.reality_smash_track.tracked_heretics)
|
||||
GLOB.reality_smash_track.remove_tracked_mind(owner)
|
||||
|
||||
REMOVE_TRAIT(our_mob, TRAIT_MANSUS_TOUCHED, REF(src))
|
||||
UnregisterSignal(our_mob, list(
|
||||
COMSIG_MOB_BEFORE_SPELL_CAST,
|
||||
COMSIG_MOB_SPELL_ACTIVATED,
|
||||
COMSIG_USER_ITEM_INTERACTION,
|
||||
COMSIG_MOB_LOGIN,
|
||||
COMSIG_LIVING_POST_FULLY_HEAL,
|
||||
COMSIG_LIVING_CULT_SACRIFICED,
|
||||
))
|
||||
|
||||
/datum/antagonist/heretic/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
@@ -395,6 +408,118 @@
|
||||
var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
|
||||
heart_knowledge.on_research(source, src)
|
||||
|
||||
/// Signal proc for [COMSIG_LIVING_CULT_SACRIFICED] to reward cultists for sacrificing a heretic
|
||||
/datum/antagonist/heretic/proc/on_cult_sacrificed(mob/living/source, list/invokers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) // uhh let's find the guy to shove him back in
|
||||
if((ghost.mind?.current == source) && ghost.client) // is it the same guy and do they have the same client
|
||||
ghost.reenter_corpse() // shove them in! it doesnt do it automatically
|
||||
|
||||
// Drop all items and splatter them around messily.
|
||||
var/list/dustee_items = source.unequip_everything()
|
||||
for(var/obj/item/loot as anything in dustee_items)
|
||||
loot.throw_at(get_step_rand(source), 2, 4, pick(invokers), TRUE)
|
||||
|
||||
// Create the blade, give it the heretic and a randomly-chosen master for the soul sword component
|
||||
var/obj/item/melee/cultblade/haunted/haunted_blade = new(get_turf(source), source, pick(invokers))
|
||||
|
||||
// Cool effect for the rune as well as the item
|
||||
var/obj/effect/rune/convert/conversion_rune = locate() in get_turf(source)
|
||||
if(conversion_rune)
|
||||
conversion_rune.gender_reveal(
|
||||
outline_color = COLOR_HERETIC_GREEN,
|
||||
ray_color = null,
|
||||
do_float = FALSE,
|
||||
do_layer = FALSE,
|
||||
)
|
||||
|
||||
haunted_blade.gender_reveal(outline_color = null, ray_color = COLOR_HERETIC_GREEN)
|
||||
|
||||
for(var/mob/living/culto as anything in invokers)
|
||||
to_chat(culto, span_cult_large("\"A follower of the forgotten gods! You must be rewarded for such a valuable sacrifice.\""))
|
||||
|
||||
// Locate a cultist team (Is there a better way??)
|
||||
var/mob/living/random_cultist = pick(invokers)
|
||||
var/datum/antagonist/cult/antag = random_cultist.mind.has_antag_datum(/datum/antagonist/cult)
|
||||
ASSERT(antag)
|
||||
var/datum/team/cult/cult_team = antag.get_team()
|
||||
|
||||
// Unlock one of 3 special items!
|
||||
var/list/possible_unlocks
|
||||
for(var/i in cult_team.unlocked_heretic_items)
|
||||
if(cult_team.unlocked_heretic_items[i])
|
||||
continue
|
||||
LAZYADD(possible_unlocks, i)
|
||||
if(length(possible_unlocks))
|
||||
var/result = pick(possible_unlocks)
|
||||
cult_team.unlocked_heretic_items[result] = TRUE
|
||||
|
||||
for(var/datum/mind/mind as anything in cult_team.members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/magic/clockwork/narsie_attack.ogg')
|
||||
to_chat(mind.current, span_cult_large(span_warning("Arcane and forbidden knowledge floods your forges and archives. The cult has learned how to create the ")) + span_cult_large(span_hypnophrase("[result]!")))
|
||||
|
||||
return SILENCE_SACRIFICE_MESSAGE|DUST_SACRIFICE
|
||||
|
||||
/**
|
||||
* Creates an animation of the item slowly lifting up from the floor with a colored outline, then slowly drifting back down.
|
||||
* Arguments:
|
||||
* * outline_color: Default is between pink and light blue, is the color of the outline filter.
|
||||
* * ray_color: Null by default. If not set, just copies outline. Used for the ray filter.
|
||||
* * anim_time: Total time of the animation. Split into two different calls.
|
||||
* * do_float: Lets you disable the sprite floating up and down.
|
||||
* * do_layer: Lets you disable the layering increase.
|
||||
*/
|
||||
/obj/proc/gender_reveal(
|
||||
outline_color = null,
|
||||
ray_color = null,
|
||||
anim_time = 10 SECONDS,
|
||||
do_float = TRUE,
|
||||
do_layer = TRUE,
|
||||
)
|
||||
|
||||
var/og_layer
|
||||
if(do_layer)
|
||||
// Layering above to stand out!
|
||||
og_layer = layer
|
||||
layer = ABOVE_MOB_LAYER
|
||||
|
||||
// Slowly floats up, then slowly goes down.
|
||||
if(do_float)
|
||||
animate(src, pixel_y = 12, time = anim_time * 0.5, easing = QUAD_EASING | EASE_OUT)
|
||||
animate(pixel_y = 0, time = anim_time * 0.5, easing = QUAD_EASING | EASE_IN)
|
||||
|
||||
// Adding a cool outline effect
|
||||
if(outline_color)
|
||||
add_filter("gender_reveal_outline", 3, list("type" = "outline", "color" = outline_color, "size" = 0.5))
|
||||
// Animating it!
|
||||
var/gay_filter = get_filter("gender_reveal_outline")
|
||||
animate(gay_filter, alpha = 110, time = 1.5 SECONDS, loop = -1)
|
||||
animate(alpha = 40, time = 2.5 SECONDS)
|
||||
|
||||
// Adding a cool ray effect
|
||||
if(ray_color)
|
||||
add_filter(name = "gender_reveal_ray", priority = 1, params = list(
|
||||
type = "rays",
|
||||
size = 45,
|
||||
color = ray_color,
|
||||
density = 6
|
||||
))
|
||||
// Animating it!
|
||||
var/ray_filter = get_filter("gender_reveal_ray")
|
||||
// I understand nothing but copypaste saves lives
|
||||
animate(ray_filter, offset = 100, time = 30 SECONDS, loop = -1, flags = ANIMATION_PARALLEL)
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(remove_gender_reveal_fx), og_layer), anim_time)
|
||||
|
||||
/**
|
||||
* Removes the non-animate effects from above proc
|
||||
*/
|
||||
/obj/proc/remove_gender_reveal_fx(og_layer)
|
||||
remove_filter(list("gender_reveal_outline", "gender_reveal_ray"))
|
||||
layer = og_layer
|
||||
|
||||
/**
|
||||
* Create our objectives for our heretic.
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
var/priority = 0
|
||||
/// What path is this on. If set to "null", assumed to be unreachable (or abstract).
|
||||
var/route
|
||||
///Determines what kind of monster ghosts will ignore from here on out. Defaults to POLL_IGNORE_HERETIC_MONSTER, but we define other types of monsters for more granularity.
|
||||
var/poll_ignore_define = POLL_IGNORE_HERETIC_MONSTER
|
||||
|
||||
/datum/heretic_knowledge/New()
|
||||
if(!mutually_exclusive)
|
||||
@@ -523,11 +525,23 @@
|
||||
abstract_parent_type = /datum/heretic_knowledge/summon
|
||||
/// Typepath of a mob to summon when we finish the recipe.
|
||||
var/mob/living/mob_to_summon
|
||||
///Determines what kind of monster ghosts will ignore from here on out. Defaults to POLL_IGNORE_HERETIC_MONSTER, but we define other types of monsters for more granularity.
|
||||
var/poll_ignore_define = POLL_IGNORE_HERETIC_MONSTER
|
||||
|
||||
/datum/heretic_knowledge/summon/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
|
||||
var/mob/living/summoned = new mob_to_summon(loc)
|
||||
summon_ritual_mob(user, loc, mob_to_summon)
|
||||
|
||||
/**
|
||||
* Creates the ritual mob and grabs a ghost for it
|
||||
*
|
||||
* * user - the mob doing the summoning
|
||||
* * loc - where the summon is happening
|
||||
* * mob_to_summon - either a mob instance or a mob typepath
|
||||
*/
|
||||
/datum/heretic_knowledge/proc/summon_ritual_mob(mob/living/user, turf/loc, mob/living/mob_to_summon)
|
||||
var/mob/living/summoned
|
||||
if(isliving(mob_to_summon))
|
||||
summoned = mob_to_summon
|
||||
else
|
||||
summoned = new mob_to_summon(loc)
|
||||
summoned.ai_controller?.set_ai_status(AI_STATUS_OFF)
|
||||
// Fade in the summon while the ghost poll is ongoing.
|
||||
// Also don't let them mess with the summon while waiting
|
||||
@@ -557,6 +571,7 @@
|
||||
|
||||
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
heretic_monster.set_owner(user.mind)
|
||||
summoned.RegisterSignal(user, COMSIG_LIVING_DEATH, TYPE_PROC_REF(/mob/living/, on_master_death))
|
||||
|
||||
var/datum/objective/heretic_summon/summon_objective = locate() in user.mind.get_all_objectives()
|
||||
summon_objective?.num_summoned++
|
||||
|
||||
@@ -38,3 +38,7 @@
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, span_boldnotice("You are a [ishuman(owner.current) ? "shambling corpse returned":"horrible creation brought"] to this plane through the Gates of the Mansus."))
|
||||
to_chat(owner, span_notice("Your master is [master]. Assist them to all ends."))
|
||||
|
||||
if(istype(owner.current, /mob/living/basic/construct/harvester/heretic))
|
||||
var/mob/living/basic/construct/harvester/heretic/shitcode = owner.current
|
||||
shitcode.master = master
|
||||
|
||||
@@ -24,23 +24,37 @@
|
||||
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
|
||||
var/after_use_message = ""
|
||||
|
||||
/obj/item/melee/sickly_blade/examine(mob/user)
|
||||
. = ..()
|
||||
if(!check_usability(user))
|
||||
return
|
||||
|
||||
. += span_notice("You can shatter the blade to teleport to a random, (mostly) safe location by <b>activating it in-hand</b>.")
|
||||
|
||||
/// Checks if the passed mob can use this blade without being stunned
|
||||
/obj/item/melee/sickly_blade/proc/check_usability(mob/living/user)
|
||||
return IS_HERETIC_OR_MONSTER(user)
|
||||
|
||||
/obj/item/melee/sickly_blade/pre_attack(atom/A, mob/living/user, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return .
|
||||
if(!IS_HERETIC_OR_MONSTER(user))
|
||||
if(!check_usability(user))
|
||||
to_chat(user, span_danger("You feel a pulse of alien intellect lash out at your mind!"))
|
||||
user.AdjustParalyzed(5 SECONDS)
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
human_user.AdjustParalyzed(5 SECONDS)
|
||||
return TRUE
|
||||
|
||||
return .
|
||||
|
||||
/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, click_parameters)
|
||||
if(isliving(target))
|
||||
SEND_SIGNAL(user, COMSIG_HERETIC_BLADE_ATTACK, target, src)
|
||||
|
||||
/obj/item/melee/sickly_blade/attack_self(mob/user)
|
||||
var/turf/safe_turf = find_safe_turf(zlevel = z, extended_safety_checks = TRUE)
|
||||
if(IS_HERETIC_OR_MONSTER(user))
|
||||
seek_safety(user)
|
||||
return ..()
|
||||
|
||||
/// Attempts to teleport the passed mob to somewhere safe on the station, if they can use the blade.
|
||||
/obj/item/melee/sickly_blade/proc/seek_safety(mob/user)
|
||||
var/turf/safe_turf = find_safe_turf(zlevels = z, extended_safety_checks = TRUE)
|
||||
if(check_usability(user))
|
||||
if(do_teleport(user, safe_turf, channel = TELEPORT_CHANNEL_MAGIC))
|
||||
to_chat(user, span_warning("As you shatter [src], you feel a gust of energy flow through your body. [after_use_message]"))
|
||||
else
|
||||
@@ -50,18 +64,15 @@
|
||||
playsound(src, SFX_SHATTER, 70, TRUE) //copied from the code for smashing a glass sheet onto the ground to turn it into a shard
|
||||
qdel(src)
|
||||
|
||||
/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, click_parameters)
|
||||
if(isliving(target))
|
||||
SEND_SIGNAL(user, COMSIG_HERETIC_BLADE_ATTACK, target, src)
|
||||
|
||||
/obj/item/melee/sickly_blade/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
||||
if(isliving(interacting_with))
|
||||
SEND_SIGNAL(user, COMSIG_HERETIC_RANGED_BLADE_ATTACK, interacting_with, src)
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
|
||||
/obj/item/melee/sickly_blade/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_HERETIC_OR_MONSTER(user))
|
||||
return
|
||||
|
||||
. += span_notice("You can shatter the blade to teleport to a random, (mostly) safe location by <b>activating it in-hand</b>.")
|
||||
|
||||
// Path of Rust's blade
|
||||
/obj/item/melee/sickly_blade/rust
|
||||
name = "\improper rusted blade"
|
||||
@@ -154,3 +165,76 @@
|
||||
icon_state = "moon_blade"
|
||||
inhand_icon_state = "moon_blade"
|
||||
after_use_message = "The Moon hears your call..."
|
||||
|
||||
// Path of Nar'Sie's blade
|
||||
// What!? This blade is given to cultists as an altar item when they sacrifice a heretic.
|
||||
// It is also given to the heretic themself if they sacrifice a cultist.
|
||||
/obj/item/melee/sickly_blade/cursed
|
||||
name = "\improper cursed blade"
|
||||
desc = "A dark blade, cursed to bleed forever. In constant struggle between the eldritch and the dark, it is forced to accept any wielder as its master. \
|
||||
Its eye's cornea drips blood endlessly into the ground, yet its piercing gaze remains on you."
|
||||
force = 25
|
||||
throwforce = 15
|
||||
block_chance = 35
|
||||
wound_bonus = 25
|
||||
bare_wound_bonus = 15
|
||||
armour_penetration = 35
|
||||
icon_state = "cursed_blade"
|
||||
inhand_icon_state = "cursed_blade"
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
var/examine_text = {"Allows the scribing of blood runes of the cult of Nar'Sie.
|
||||
The combination of eldritch power and Nar'Sie's might allows for vastly increased rune drawing speed,
|
||||
alongside the vicious strength of the blade being more powerful than usual.\n
|
||||
<b>It can also be shattered in-hand by cultists (via right-click), teleporting them to relative safety.<b>"}
|
||||
|
||||
AddComponent(/datum/component/cult_ritual_item, span_cult(examine_text), turfs_that_boost_us = /turf) // Always fast to draw!
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/attack_self_secondary(mob/user)
|
||||
seek_safety(user, TRUE)
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/seek_safety(mob/user, secondary_attack = FALSE)
|
||||
if(IS_CULTIST(user) && !secondary_attack)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/check_usability(mob/living/user)
|
||||
if(IS_HERETIC_OR_MONSTER(user) || IS_CULTIST(user))
|
||||
return TRUE
|
||||
if(prob(15))
|
||||
to_chat(user, span_cult_large(pick("\"An untouched mind? Amusing.\"", "\" I suppose it isn't worth the effort to stop you.\"", "\"Go ahead. I don't care.\"", "\"You'll be mine soon enough.\"")))
|
||||
var/obj/item/bodypart/affecting = user.get_active_hand()
|
||||
if(!affecting)
|
||||
return
|
||||
affecting.receive_damage(burn = 5)
|
||||
playsound(src, SFX_SEAR, 25, TRUE)
|
||||
to_chat(user, span_danger("Your hand sizzles.")) // Nar nar might not care but their essence still doesn't like you
|
||||
else if(prob(15))
|
||||
to_chat(user, span_big(span_hypnophrase("LW'NAFH'NAHOR UH'ENAH'YMG EPGOKA AH NAFL MGEMPGAH'EHYE")))
|
||||
to_chat(user, span_danger("Horrible, unintelligible utterances flood your mind!"))
|
||||
user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15) // This can kill you if you ignore it
|
||||
return TRUE
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(IS_HERETIC_OR_MONSTER(user))
|
||||
after_use_message = "The Mansus hears your call..."
|
||||
else if(IS_CULTIST(user))
|
||||
after_use_message = "Nar'Sie hears your call..."
|
||||
else
|
||||
after_use_message = null
|
||||
|
||||
/obj/item/melee/sickly_blade/cursed/interact_with_atom(atom/target, mob/living/user, list/modifiers)
|
||||
. = ..()
|
||||
|
||||
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
|
||||
if(!heretic_datum)
|
||||
return NONE
|
||||
|
||||
// Can only carve runes with it if off combat mode.
|
||||
if(isopenturf(target) && !user.combat_mode)
|
||||
heretic_datum.try_draw_rune(user, target, drawing_time = 14 SECONDS) // Faster than pen, slower than cicatrix
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
return NONE
|
||||
|
||||
@@ -9,6 +9,101 @@
|
||||
. = ..()
|
||||
AddElement(/datum/element/heretic_focus)
|
||||
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus
|
||||
name = "Crimson Focus"
|
||||
desc = "A blood-red focusing glass that provides a link to the world beyond, and worse. Its eye is constantly twitching and gazing in all directions. It almost seems to be silently screaming..."
|
||||
icon_state = "crimson_focus"
|
||||
/// The aura healing component. Used to delete it when taken off.
|
||||
var/datum/component/component
|
||||
/// If active or not, used to add and remove its cult and heretic buffs.
|
||||
var/active = FALSE
|
||||
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus/equipped(mob/living/user, slot)
|
||||
. = ..()
|
||||
if(!(slot & ITEM_SLOT_NECK))
|
||||
return
|
||||
|
||||
var/team_color = COLOR_ADMIN_PINK
|
||||
if(IS_CULTIST(user))
|
||||
var/datum/action/innate/cult/blood_magic/magic_holder = locate() in user.actions
|
||||
team_color = COLOR_CULT_RED
|
||||
magic_holder.magic_enhanced = TRUE
|
||||
else if(IS_HERETIC_OR_MONSTER(user) && !active)
|
||||
for(var/datum/action/cooldown/spell/spell_action in user.actions)
|
||||
spell_action.cooldown_time *= 0.5
|
||||
active = TRUE
|
||||
team_color = COLOR_GREEN
|
||||
else
|
||||
team_color = pick(COLOR_CULT_RED, COLOR_GREEN)
|
||||
|
||||
user.add_traits(list(TRAIT_MANSUS_TOUCHED, TRAIT_BLOODY_MESS), REF(src))
|
||||
to_chat(user, span_alert("Your heart takes on a strange yet soothing irregular rhythm, and your blood feels significantly less viscous than it used to be. You're not sure if that's a good thing."))
|
||||
component = user.AddComponent( \
|
||||
/datum/component/aura_healing, \
|
||||
range = 3, \
|
||||
brute_heal = 1, \
|
||||
burn_heal = 1, \
|
||||
blood_heal = 2, \
|
||||
suffocation_heal = 5, \
|
||||
simple_heal = 0.6, \
|
||||
requires_visibility = FALSE, \
|
||||
limit_to_trait = TRAIT_MANSUS_TOUCHED, \
|
||||
healing_color = team_color, \
|
||||
)
|
||||
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus/dropped(mob/living/user)
|
||||
. = ..()
|
||||
|
||||
if(!istype(user))
|
||||
return
|
||||
|
||||
if(HAS_TRAIT_FROM(user, TRAIT_MANSUS_TOUCHED, REF(src)))
|
||||
to_chat(user, span_notice("Your heart and blood return to their regular old rhythm and flow."))
|
||||
|
||||
if(IS_HERETIC_OR_MONSTER(user) && active)
|
||||
for(var/datum/action/cooldown/spell/spell_action in user.actions)
|
||||
spell_action.cooldown_time *= 2
|
||||
active = FALSE
|
||||
QDEL_NULL(component)
|
||||
user.remove_traits(list(TRAIT_MANSUS_TOUCHED, TRAIT_BLOODY_MESS), REF(src))
|
||||
|
||||
// If boosted enable is set, to prevent false dropped() calls from repeatedly nuking the max spells.
|
||||
var/datum/action/innate/cult/blood_magic/magic_holder = locate() in user.actions
|
||||
// Remove the last spell if over new limit, as we will reduce our max spell amount. Done beforehand as it causes a index out of bounds runtime otherwise.
|
||||
if(magic_holder?.magic_enhanced)
|
||||
QDEL_NULL(magic_holder.spells[ENHANCED_BLOODCHARGE])
|
||||
magic_holder?.magic_enhanced = FALSE
|
||||
|
||||
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus/attack_self(mob/living/user, modifiers)
|
||||
. = ..()
|
||||
to_chat(user, span_danger("You start tightly squeezing [src]..."))
|
||||
if(!do_after(user, 1.25 SECONDS, src))
|
||||
return
|
||||
to_chat(user, span_danger("[src] explodes into a shower of gore and blood, drenching your arm. You can feel the blood seeping into your skin. You inmediately feel better, but soon, the feeling turns hollow as your veins itch."))
|
||||
new /obj/effect/gibspawner/generic(get_turf(src))
|
||||
var/heal_amt = user.adjustBruteLoss(-50)
|
||||
user.adjustFireLoss( -(50 - abs(heal_amt)) ) // no double dipping
|
||||
|
||||
// I want it to poison the user but I also think it'd be neat if they got their juice as well. But that cancels most of the damage out. So I dunno.
|
||||
user.reagents?.add_reagent(/datum/reagent/fuel/unholywater, rand(6, 10))
|
||||
user.reagents?.add_reagent(/datum/reagent/eldritch, rand(6, 10))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/clothing/neck/heretic_focus/crimson_focus/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
var/magic_dude
|
||||
if(IS_CULTIST(user))
|
||||
. += span_cult_bold("This focus will allow you to store one extra spell and halve the empowering time, alongside providing a small regenerative effect.")
|
||||
magic_dude = TRUE
|
||||
if(IS_HERETIC_OR_MONSTER(user))
|
||||
. += span_notice("This focus will halve your spell cooldowns, alongside granting a small regenerative effect to any nearby heretics or monsters, including you.")
|
||||
magic_dude = TRUE
|
||||
|
||||
if(magic_dude)
|
||||
. += span_red("You can also squeeze it to recover a large amount of health quickly, at a cost...")
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet
|
||||
name = "Warm Eldritch Medallion"
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulsing of a thousand others."
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// Rusting an airlock causes it to lose power, mostly to prevent the airlock from shocking you.
|
||||
// This is a bit of a hack, but fixing this would require the enture wire cut/pulse system to be reworked.
|
||||
// This is a bit of a hack, but fixing this would require the entire wire cut/pulse system to be reworked.
|
||||
if(istype(target, /obj/machinery/door/airlock))
|
||||
var/obj/machinery/door/airlock/airlock = target
|
||||
airlock.loseMainPower()
|
||||
@@ -95,56 +95,10 @@
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/heretic_knowledge/rust_regen/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
|
||||
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
||||
RegisterSignal(user, COMSIG_LIVING_LIFE, PROC_REF(on_life))
|
||||
user.AddElement(/datum/element/leeching_walk)
|
||||
|
||||
/datum/heretic_knowledge/rust_regen/on_lose(mob/user, datum/antagonist/heretic/our_heretic)
|
||||
UnregisterSignal(user, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_LIFE))
|
||||
|
||||
/*
|
||||
* Signal proc for [COMSIG_MOVABLE_MOVED].
|
||||
*
|
||||
* Checks if we should have baton resistance on the new turf.
|
||||
*/
|
||||
/datum/heretic_knowledge/rust_regen/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/mover_turf = get_turf(source)
|
||||
if(HAS_TRAIT(mover_turf, TRAIT_RUSTY))
|
||||
ADD_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
|
||||
return
|
||||
|
||||
REMOVE_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
|
||||
|
||||
/**
|
||||
* Signal proc for [COMSIG_LIVING_LIFE].
|
||||
*
|
||||
* Gradually heals the heretic ([source]) on rust,
|
||||
* including baton knockdown and stamina damage.
|
||||
*/
|
||||
/datum/heretic_knowledge/rust_regen/proc/on_life(mob/living/source, seconds_per_tick, times_fired)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/our_turf = get_turf(source)
|
||||
if(!HAS_TRAIT(our_turf, TRAIT_RUSTY))
|
||||
return
|
||||
|
||||
// Heals all damage + Stamina
|
||||
var/need_mob_update = FALSE
|
||||
need_mob_update += source.adjustBruteLoss(-3, updating_health = FALSE)
|
||||
need_mob_update += source.adjustFireLoss(-3, updating_health = FALSE)
|
||||
need_mob_update += source.adjustToxLoss(-3, updating_health = FALSE, forced = TRUE) // Slimes are people too
|
||||
need_mob_update += source.adjustOxyLoss(-1.5, updating_health = FALSE)
|
||||
need_mob_update += source.adjustStaminaLoss(-10, updating_stamina = FALSE)
|
||||
if(need_mob_update)
|
||||
source.updatehealth()
|
||||
// Reduces duration of stuns/etc
|
||||
source.AdjustAllImmobility(-0.5 SECONDS)
|
||||
// Heals blood loss
|
||||
if(source.blood_volume < BLOOD_VOLUME_NORMAL)
|
||||
source.blood_volume += 2.5 * seconds_per_tick
|
||||
// Slowly regulates your body temp
|
||||
source.adjust_bodytemperature((source.get_body_temp_normal() - source.bodytemperature)/5)
|
||||
user.RemoveElement(/datum/element/leeching_walk)
|
||||
|
||||
/datum/heretic_knowledge/mark/rust_mark
|
||||
name = "Mark of Rust"
|
||||
|
||||
@@ -80,8 +80,11 @@
|
||||
// If we have targets, we can check to see if we can do a sacrifice
|
||||
// Let's remove any humans in our atoms list that aren't a sac target
|
||||
for(var/mob/living/carbon/human/sacrifice in atoms)
|
||||
// If the mob's not in soft crit or worse, or isn't one of the sacrifices, remove it from the list
|
||||
if(sacrifice.stat < SOFT_CRIT || !(sacrifice in heretic_datum.sac_targets))
|
||||
// If the mob's not in soft crit or worse, remove from list
|
||||
if(sacrifice.stat < SOFT_CRIT)
|
||||
atoms -= sacrifice
|
||||
// Otherwise if it's neither a target nor a cultist, remove it
|
||||
else if(!(sacrifice in heretic_datum.sac_targets) && !IS_CULTIST(sacrifice))
|
||||
atoms -= sacrifice
|
||||
|
||||
// Finally, return TRUE if we have a target in the list
|
||||
@@ -94,7 +97,9 @@
|
||||
|
||||
/datum/heretic_knowledge/hunt_and_sacrifice/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
|
||||
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
|
||||
if(!LAZYLEN(heretic_datum.sac_targets))
|
||||
// Force it to work if the sacrifice is a cultist, even if there's no targets.
|
||||
var/mob/living/carbon/human/sac = selected_atoms[1]
|
||||
if(!LAZYLEN(heretic_datum.sac_targets) && !IS_CULTIST(sac))
|
||||
if(obtain_targets(user, heretic_datum = heretic_datum))
|
||||
return TRUE
|
||||
else
|
||||
@@ -183,34 +188,128 @@
|
||||
* * selected_atoms - a list of all atoms chosen. Should be (at least) one human.
|
||||
* * loc - the turf the sacrifice is occuring on
|
||||
*/
|
||||
/datum/heretic_knowledge/hunt_and_sacrifice/proc/sacrifice_process(mob/living/user, list/selected_atoms)
|
||||
/datum/heretic_knowledge/hunt_and_sacrifice/proc/sacrifice_process(mob/living/user, list/selected_atoms, turf/loc)
|
||||
|
||||
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
|
||||
var/mob/living/carbon/human/sacrifice = locate() in selected_atoms
|
||||
if(!sacrifice)
|
||||
CRASH("[type] sacrifice_process didn't have a human in the atoms list. How'd it make it so far?")
|
||||
if(!(sacrifice in heretic_datum.sac_targets))
|
||||
CRASH("[type] sacrifice_process managed to get a non-target human. This is incorrect.")
|
||||
if(!(sacrifice in heretic_datum.sac_targets) && !IS_CULTIST(sacrifice))
|
||||
CRASH("[type] sacrifice_process managed to get a non-target, non-cult human. This is incorrect.")
|
||||
|
||||
if(sacrifice.mind)
|
||||
LAZYADD(target_blacklist, sacrifice.mind)
|
||||
heretic_datum.remove_sacrifice_target(sacrifice)
|
||||
|
||||
|
||||
var/feedback = "Your patrons accept your offer"
|
||||
var/sac_job_flag = sacrifice.mind?.assigned_role?.job_flags | sacrifice.last_mind?.assigned_role?.job_flags
|
||||
if(sac_job_flag & JOB_HEAD_OF_STAFF)
|
||||
heretic_datum.knowledge_points++
|
||||
var/datum/antagonist/cult/cultist_datum = IS_CULTIST(sacrifice)
|
||||
// Heads give 3 points, cultists give 1 point (and a special reward), normal sacrifices give 2 points.
|
||||
heretic_datum.total_sacrifices++
|
||||
if((sac_job_flag & JOB_HEAD_OF_STAFF))
|
||||
heretic_datum.knowledge_points += 3
|
||||
heretic_datum.high_value_sacrifices++
|
||||
feedback += " <i>graciously</i>"
|
||||
else if(cultist_datum)
|
||||
heretic_datum.knowledge_points += 1
|
||||
grant_reward(user, sacrifice, loc)
|
||||
// easier to read
|
||||
var/rewards_given = heretic_datum.rewards_given
|
||||
// Chance for it to send a warning to cultists, higher with each reward. Stops after 5 because they probably got the hint by then.
|
||||
if(prob(min(15 * rewards_given)) && (rewards_given <= 5))
|
||||
for(var/datum/mind/mind as anything in cultist_datum.cult_team.members)
|
||||
if(mind.current)
|
||||
SEND_SOUND(mind.current, 'sound/magic/clockwork/narsie_attack.ogg')
|
||||
var/message = span_narsie("A vile heretic has ") + \
|
||||
span_cult_large(span_hypnophrase("sacrificed")) + \
|
||||
span_narsie(" one of our own. Destroy and sacrifice the infidel before it claims more!")
|
||||
to_chat(mind.current, message)
|
||||
// he(retic) gets a warn too
|
||||
var/message = span_cult_bold("You feel that your action has attracted") + \
|
||||
span_cult_bold_italic(" attention.")
|
||||
to_chat(user, message)
|
||||
return
|
||||
else
|
||||
heretic_datum.knowledge_points += 2
|
||||
|
||||
to_chat(user, span_hypnophrase("[feedback]."))
|
||||
heretic_datum.total_sacrifices++
|
||||
heretic_datum.knowledge_points += 2
|
||||
if(!begin_sacrifice(sacrifice))
|
||||
disembowel_target(sacrifice)
|
||||
return
|
||||
|
||||
sacrifice.apply_status_effect(/datum/status_effect/heretic_curse, user)
|
||||
|
||||
if(!begin_sacrifice(sacrifice))
|
||||
disembowel_target(sacrifice)
|
||||
|
||||
/datum/heretic_knowledge/hunt_and_sacrifice/proc/grant_reward(mob/living/user, mob/living/sacrifice, turf/loc)
|
||||
|
||||
// Visible and audible encouragement!
|
||||
to_chat(user, span_big(span_hypnophrase("A servant of the Sanguine Apostate!")))
|
||||
to_chat(user, span_hierophant("Your patrons are rapturous!"))
|
||||
playsound(sacrifice, 'sound/magic/disintegrate.ogg', 75, TRUE)
|
||||
|
||||
// Drop all items and splatter them around messily.
|
||||
var/list/dustee_items = sacrifice.unequip_everything()
|
||||
for(var/obj/item/loot as anything in dustee_items)
|
||||
loot.throw_at(get_step_rand(sacrifice), 2, 4, user, TRUE)
|
||||
|
||||
// The loser is DUSTED.
|
||||
sacrifice.dust(TRUE, TRUE)
|
||||
|
||||
// Increase reward counter
|
||||
var/datum/antagonist/heretic/antag = IS_HERETIC(user)
|
||||
antag.rewards_given++
|
||||
|
||||
// We limit the amount so the heretic doesn't just turn into a frickin' god (early)
|
||||
to_chat(user, span_hierophant("You feel the rotten energies of the infidel warp and twist, mixing with that of your own..."))
|
||||
if(prob(8 * antag.rewards_given))
|
||||
to_chat(user, span_hierophant("Faint, dark red sparks flit around the dust, then fade. It looks like your patrons weren't able to fashion something out of it."))
|
||||
return
|
||||
|
||||
// Cool effect for the rune as well as the item
|
||||
var/obj/effect/heretic_rune/rune = locate() in range(2, user)
|
||||
if(rune)
|
||||
rune.gender_reveal(
|
||||
outline_color = COLOR_CULT_RED,
|
||||
ray_color = null,
|
||||
do_float = FALSE,
|
||||
do_layer = FALSE,
|
||||
)
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(deposit_reward), user, loc, null, rune), 5 SECONDS)
|
||||
|
||||
|
||||
/datum/heretic_knowledge/hunt_and_sacrifice/proc/deposit_reward(mob/user, turf/loc, loop = 0, obj/rune)
|
||||
if(loop > 5) // Max limit for retrying a reward
|
||||
return
|
||||
// Remove the rays, we don't need them anymore.
|
||||
rune?.remove_filter("reward_outline")
|
||||
playsound(loc, 'sound/magic/repulse.ogg', 75, TRUE)
|
||||
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
|
||||
ASSERT(heretic_datum)
|
||||
// This list will be almost identical to unlocked_heretic_items, with the same keys, the difference being the values will be 1 to 5.
|
||||
var/list/rewards = heretic_datum.unlocked_heretic_items.Copy()
|
||||
// We will make it increasingly less likely to get a reward if you've already got it
|
||||
for(var/possible_reward in heretic_datum.unlocked_heretic_items)
|
||||
var/amount_already_awarded = heretic_datum.unlocked_heretic_items[possible_reward]
|
||||
rewards[possible_reward] = min(5 - (amount_already_awarded * 2), 1)
|
||||
|
||||
var/atom/reward = pick_weight(rewards)
|
||||
reward = new reward(loc)
|
||||
|
||||
if(isliving(reward))
|
||||
if(summon_ritual_mob(user, loc, reward) == FALSE)
|
||||
qdel(reward)
|
||||
deposit_reward(user, loc, loop++, rune) // If no ghosts, try again until limit is hit
|
||||
return
|
||||
|
||||
else if(isitem(reward))
|
||||
var/obj/item/item_reward = reward
|
||||
item_reward.gender_reveal(outline_color = null, ray_color = COLOR_CULT_RED)
|
||||
|
||||
ASSERT(reward)
|
||||
|
||||
return reward
|
||||
|
||||
/**
|
||||
* This proc is called from [proc/sacrifice_process] after the heretic successfully sacrifices [sac_target].)
|
||||
|
||||
@@ -17,7 +17,16 @@
|
||||
aoe_radius = 2
|
||||
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion/get_things_to_cast_on(atom/center)
|
||||
return RANGE_TURFS(aoe_radius, center)
|
||||
|
||||
var/list/things_to_convert = RANGE_TURFS(aoe_radius, center)
|
||||
|
||||
// Also converts things right next to you.
|
||||
for(var/atom/movable/nearby_movable in view(1, center))
|
||||
if(nearby_movable == owner || !isstructure(nearby_movable) )
|
||||
continue
|
||||
things_to_convert += nearby_movable
|
||||
|
||||
return things_to_convert
|
||||
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion/cast_on_thing_in_aoe(turf/victim, mob/living/caster)
|
||||
// We have less chance of rusting stuff that's further
|
||||
@@ -27,9 +36,11 @@
|
||||
if(prob(chance_of_not_rusting))
|
||||
return
|
||||
|
||||
caster.do_rust_heretic_act(victim)
|
||||
if(ismob(caster))
|
||||
caster.do_rust_heretic_act(victim)
|
||||
else
|
||||
victim.rust_heretic_act()
|
||||
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion/small
|
||||
name = "Rust Conversion"
|
||||
desc = "Spreads rust onto nearby surfaces."
|
||||
aoe_radius = 2
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion/construct
|
||||
name = "Construct Spread"
|
||||
cooldown_time = 15 SECONDS
|
||||
|
||||
@@ -129,16 +129,17 @@
|
||||
INVOKE_ASYNC(src, PROC_REF(fire_line), owner, line_target(offset, flame_line_length, target, owner))
|
||||
|
||||
/datum/action/cooldown/spell/pointed/ash_beams/proc/line_target(offset, range, atom/at, atom/user)
|
||||
var/turf/user_loc = get_turf(user)
|
||||
if(!at)
|
||||
return
|
||||
var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
|
||||
var/angle = ATAN2(at.x - user_loc.x, at.y - user_loc.y) + offset
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/i in 1 to range)
|
||||
var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
|
||||
var/turf/check = locate(user_loc.x + cos(angle) * i, user_loc.y + sin(angle) * i, user_loc.z)
|
||||
if(!check)
|
||||
break
|
||||
T = check
|
||||
return (get_line(user, T) - get_turf(user))
|
||||
return (get_line(user_loc, T) - user_loc)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/ash_beams/proc/fire_line(atom/source, list/turfs)
|
||||
var/list/hit_list = list()
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
return FALSE
|
||||
|
||||
var/obj/storage_item = locate(/obj/item/storage/backpack) in cast_on.contents
|
||||
|
||||
|
||||
if(isnull(storage_item))
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
var/beam_duration = 2 SECONDS
|
||||
|
||||
/datum/action/cooldown/spell/charged/beam/fire_blast/cast(atom/cast_on)
|
||||
if(isliving(cast_on))
|
||||
var/mob/living/caster = cast_on
|
||||
var/mob/living/caster = get_caster_from_target(cast_on)
|
||||
if(istype(caster))
|
||||
// Caster becomes fireblasted, but in a good way - heals damage over time
|
||||
caster.apply_status_effect(/datum/status_effect/fire_blasted, beam_duration, -2)
|
||||
return ..()
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
QDEL_NULL(blade_effect)
|
||||
|
||||
var/mob/living/living_user = on_who
|
||||
blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS)
|
||||
blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS, projectile_type)
|
||||
RegisterSignal(blade_effect, COMSIG_QDELETING, PROC_REF(on_status_effect_deleted))
|
||||
|
||||
/datum/action/cooldown/spell/pointed/projectile/furious_steel/on_deactivation(mob/on_who, refund_cooldown = TRUE)
|
||||
@@ -106,10 +106,12 @@
|
||||
sharpness = SHARP_EDGED
|
||||
wound_bonus = 15
|
||||
pass_flags = PASSTABLE | PASSFLAPS
|
||||
/// Color applied as an outline filter on init
|
||||
var/outline_color = "#f8f8ff"
|
||||
|
||||
/obj/projectile/floating_blade/Initialize(mapload)
|
||||
. = ..()
|
||||
add_filter("dio_knife", 2, list("type" = "outline", "color" = "#f8f8ff", "size" = 1))
|
||||
add_filter("dio_knife", 2, list("type" = "outline", "color" = outline_color, "size" = 1))
|
||||
|
||||
/obj/projectile/floating_blade/prehit_pierce(atom/hit)
|
||||
if(isliving(hit) && isliving(firer))
|
||||
@@ -128,3 +130,40 @@
|
||||
return PROJECTILE_DELETE_WITHOUT_HITTING
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/projectile/floating_blade/haunted
|
||||
name = "ritual blade"
|
||||
icon = 'icons/obj/weapons/khopesh.dmi'
|
||||
icon_state = "render"
|
||||
damage = 35
|
||||
wound_bonus = 25
|
||||
outline_color = "#D7CBCA"
|
||||
|
||||
/datum/action/cooldown/spell/pointed/projectile/furious_steel/solo
|
||||
name = "Lesser Furious Steel"
|
||||
cooldown_time = 20 SECONDS
|
||||
projectile_amount = 1
|
||||
active_msg = "You summon forth a blade of furious silver."
|
||||
deactive_msg = "You conceal the blade of furious silver."
|
||||
|
||||
/datum/action/cooldown/spell/pointed/projectile/furious_steel/haunted
|
||||
name = "Cursed Steel"
|
||||
desc = "Summon two cursed blades which orbit you. \
|
||||
While orbiting you, these blades will protect you from from attacks, but will be consumed on use. \
|
||||
Additionally, you can click to fire the blades at a target, dealing damage and causing bleeding."
|
||||
background_icon_state = "bg_heretic" // kept intentionally
|
||||
overlay_icon_state = "bg_cult_border"
|
||||
button_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
button_icon_state = "cursed_steel"
|
||||
sound = 'sound/weapons/guillotine.ogg'
|
||||
|
||||
cooldown_time = 40 SECONDS
|
||||
invocation = "IA!"
|
||||
invocation_type = INVOCATION_SHOUT
|
||||
|
||||
spell_requirements = NONE
|
||||
|
||||
active_msg = "You summon forth two cursed blades."
|
||||
deactive_msg = "You conceal the cursed blades."
|
||||
projectile_amount = 2
|
||||
projectile_type = /obj/projectile/floating_blade/haunted
|
||||
|
||||
@@ -38,11 +38,34 @@
|
||||
|
||||
var/mob/living/living_hit = victim
|
||||
living_hit.apply_damage(10, BRUTE, wound_bonus = CANT_WOUND)
|
||||
if(iscarbon(victim))
|
||||
var/mob/living/carbon/carbon_hit = victim
|
||||
carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic)
|
||||
carbon_hit.AdjustKnockdown(5 SECONDS)
|
||||
carbon_hit.adjustStaminaLoss(80)
|
||||
if(!iscarbon(victim))
|
||||
return TRUE
|
||||
|
||||
var/mob/living/carbon/carbon_hit = victim
|
||||
|
||||
// Cultists are momentarily disoriented by the stunning aura. Enough for both parties to go 'oh shit' but only a mild combat ability.
|
||||
// Cultists have an identical effect on their stun hand. The heretic's faster spell charge time is made up for by their lack of teammates.
|
||||
if(IS_CULTIST(carbon_hit))
|
||||
carbon_hit.AdjustKnockdown(0.5 SECONDS)
|
||||
carbon_hit.adjust_confusion_up_to(1.5 SECONDS, 3 SECONDS)
|
||||
carbon_hit.adjust_dizzy_up_to(1.5 SECONDS, 3 SECONDS)
|
||||
ADD_TRAIT(carbon_hit, TRAIT_NO_SIDE_KICK, REF(src)) // We don't want this to be a good stunning tool, just minor disorientation
|
||||
addtimer(TRAIT_CALLBACK_REMOVE(carbon_hit, TRAIT_NO_SIDE_KICK, REF(src)), 1 SECONDS)
|
||||
|
||||
var/old_color = carbon_hit.color
|
||||
carbon_hit.color = COLOR_CULT_RED
|
||||
animate(carbon_hit, color = old_color, time = 4 SECONDS, easing = EASE_IN)
|
||||
carbon_hit.mob_light(range = 1.5, power = 2.5, color = COLOR_CULT_RED, duration = 0.5 SECONDS)
|
||||
playsound(carbon_hit, 'sound/magic/curse.ogg', 50, TRUE)
|
||||
|
||||
to_chat(caster, span_warning("An unholy force intervenes as you grasp [carbon_hit], absorbing most of the effects!"))
|
||||
to_chat(carbon_hit, span_warning("As [caster] grasps you with eldritch forces, your blood magic absorbs most of the effects!"))
|
||||
carbon_hit.balloon_alert_to_viewers("absorbed!")
|
||||
return TRUE
|
||||
|
||||
carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic)
|
||||
carbon_hit.AdjustKnockdown(5 SECONDS)
|
||||
carbon_hit.adjustStaminaLoss(80)
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
|
||||
|
||||
school = SCHOOL_FORBIDDEN
|
||||
cooldown_time = 5 SECONDS
|
||||
cooldown_time = 8 SECONDS
|
||||
|
||||
invocation = "Someone raises a wall of rust."
|
||||
invocation_self_message = "You raise a wall of rust."
|
||||
@@ -16,15 +16,19 @@
|
||||
spell_requirements = NONE
|
||||
|
||||
cast_range = 4
|
||||
aim_assist = FALSE
|
||||
|
||||
/// How long does the filter last on walls we make?
|
||||
var/filter_duration = 2 MINUTES
|
||||
|
||||
/**
|
||||
* Overrides 'aim assist' because we always want to hit just the turf we clicked on.
|
||||
*/
|
||||
/datum/action/cooldown/spell/pointed/rust_construction/aim_assist(mob/living/caller, atom/target)
|
||||
return get_turf(target)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/rust_construction/is_valid_target(atom/cast_on)
|
||||
if(!isfloorturf(cast_on))
|
||||
if(isturf(cast_on) && owner)
|
||||
cast_on.balloon_alert(owner, "not a floor!")
|
||||
if(!isturf(cast_on))
|
||||
cast_on.balloon_alert(owner, "not a wall or floor!")
|
||||
return FALSE
|
||||
|
||||
if(!HAS_TRAIT(cast_on, TRAIT_RUSTY))
|
||||
@@ -43,9 +47,22 @@
|
||||
invocation = span_danger("<b>[owner]</b> drags [owner.p_their()] hand[living_owner.usable_hands == 1 ? "":"s"] upwards as a wall of rust rises out of [cast_on]!")
|
||||
invocation_self_message = span_notice("You drag [living_owner.usable_hands == 1 ? "a hand":"your hands"] upwards as a wall of rust rises out of [cast_on].")
|
||||
|
||||
/datum/action/cooldown/spell/pointed/rust_construction/cast(turf/open/cast_on)
|
||||
/datum/action/cooldown/spell/pointed/rust_construction/cast(turf/cast_on)
|
||||
. = ..()
|
||||
var/rises_message = "rises out of [cast_on]"
|
||||
|
||||
// If we casted at a wall we'll try to rust it. In the case of an enchanted wall it'll deconstruct it
|
||||
if(isclosedturf(cast_on))
|
||||
cast_on.visible_message(span_warning("\The [cast_on] quakes as the rust causes it to crumble!"))
|
||||
var/mob/living/living_owner = owner
|
||||
living_owner?.do_rust_heretic_act(cast_on)
|
||||
// ref transfers to floor
|
||||
cast_on.Shake(shake_interval = 0.1 SECONDS, duration = 0.5 SECONDS)
|
||||
// which we need to re-rust
|
||||
living_owner?.do_rust_heretic_act(cast_on)
|
||||
playsound(cast_on, 'sound/effects/bang.ogg', 50, vary = TRUE)
|
||||
return
|
||||
|
||||
var/turf/closed/wall/new_wall = cast_on.place_on_top(/turf/closed/wall)
|
||||
if(!istype(new_wall))
|
||||
return
|
||||
@@ -53,7 +70,8 @@
|
||||
playsound(new_wall, 'sound/effects/constructform.ogg', 50, TRUE)
|
||||
new_wall.rust_heretic_act()
|
||||
new_wall.name = "\improper enchanted [new_wall.name]"
|
||||
new_wall.hardness = 10
|
||||
new_wall.AddComponent(/datum/component/torn_wall)
|
||||
new_wall.hardness = 60
|
||||
new_wall.sheet_amount = 0
|
||||
new_wall.girder_type = null
|
||||
|
||||
@@ -61,8 +79,8 @@
|
||||
// but I guess a fading filter will have to do for now as walls have 0 depth (currently)
|
||||
// damn though with 3/4ths walls this'll look sick just imagine it
|
||||
new_wall.add_filter("rust_wall", 2, list("type" = "outline", "color" = "#85be299c", "size" = 2))
|
||||
addtimer(CALLBACK(src, PROC_REF(fade_wall_filter), new_wall), filter_duration * (1/20))
|
||||
addtimer(CALLBACK(src,PROC_REF(remove_wall_filter), new_wall), filter_duration)
|
||||
addtimer(CALLBACK(src, PROC_REF(fade_wall_filter), new_wall), filter_duration * 0.5)
|
||||
addtimer(CALLBACK(src, PROC_REF(remove_wall_filter), new_wall), filter_duration)
|
||||
|
||||
var/message_shown = FALSE
|
||||
for(var/mob/living/living_mob in cast_on)
|
||||
@@ -108,7 +126,7 @@
|
||||
if(!rust_filter)
|
||||
return
|
||||
|
||||
animate(rust_filter, alpha = 0, time = filter_duration * (19/20))
|
||||
animate(rust_filter, alpha = 0, time = filter_duration * (9/20))
|
||||
|
||||
/datum/action/cooldown/spell/pointed/rust_construction/proc/remove_wall_filter(turf/closed/wall)
|
||||
if(QDELETED(wall))
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
new /obj/effect/temp_visual/dir_setting/entropic(get_step(cast_on, cast_on.dir), cast_on.dir)
|
||||
|
||||
/datum/action/cooldown/spell/cone/staggered/entropic_plume/do_turf_cone_effect(turf/target_turf, mob/living/caster, level)
|
||||
caster.do_rust_heretic_act(target_turf)
|
||||
if(ismob(caster))
|
||||
caster.do_rust_heretic_act(target_turf)
|
||||
else
|
||||
target_turf.rust_heretic_act()
|
||||
|
||||
/datum/action/cooldown/spell/cone/staggered/entropic_plume/do_mob_cone_effect(mob/living/victim, atom/caster, level)
|
||||
if(victim.can_block_magic(antimagic_flags) || IS_HERETIC_OR_MONSTER(victim) || victim == caster)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
///a heretic that got soultrapped by cultists. does nothing, other than signify they suck
|
||||
/datum/antagonist/soultrapped_heretic
|
||||
name = "\improper Soultrapped Heretic"
|
||||
roundend_category = "Heretics"
|
||||
antagpanel_category = "Heretic"
|
||||
job_rank = ROLE_HERETIC
|
||||
antag_moodlet = /datum/mood_event/soultrapped_heretic
|
||||
antag_hud_name = "heretic"
|
||||
|
||||
// Will never show up because they're shades inside a sword
|
||||
/datum/mood_event/soultrapped_heretic
|
||||
description = "They trapped me! I can't escape!"
|
||||
mood_change = -20
|
||||
|
||||
// always failure obj
|
||||
/datum/objective/heretic_trapped
|
||||
name = "soultrapped failure"
|
||||
explanation_text = "Help the cult. Kill the cult. Help the crew. Kill the crew. Help your wielder. Kill your wielder. Kill everyone. Rattle your chains."
|
||||
|
||||
/datum/antagonist/soultrapped_heretic/on_gain()
|
||||
..()
|
||||
var/datum/objective/epic_fail = new /datum/objective/heretic_trapped()
|
||||
epic_fail.completed = FALSE
|
||||
objectives += epic_fail
|
||||
@@ -118,6 +118,8 @@
|
||||
var/time_between_initial_blades = 0.25 SECONDS
|
||||
/// If TRUE, we self-delete our status effect after all the blades are deleted.
|
||||
var/delete_on_blades_gone = TRUE
|
||||
/// What blade type to create
|
||||
var/blade_type = /obj/effect/floating_blade
|
||||
/// A list of blade effects orbiting / protecting our owner
|
||||
var/list/obj/effect/floating_blade/blades = list()
|
||||
|
||||
@@ -127,12 +129,14 @@
|
||||
max_num_blades = 4,
|
||||
blade_orbit_radius = 20,
|
||||
time_between_initial_blades = 0.25 SECONDS,
|
||||
blade_type = /obj/effect/floating_blade,
|
||||
)
|
||||
|
||||
src.duration = new_duration
|
||||
src.max_num_blades = max_num_blades
|
||||
src.blade_orbit_radius = blade_orbit_radius
|
||||
src.time_between_initial_blades = time_between_initial_blades
|
||||
src.blade_type = blade_type
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/protective_blades/on_apply()
|
||||
@@ -157,7 +161,7 @@
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
var/obj/effect/floating_blade/blade = new(get_turf(owner))
|
||||
var/obj/effect/floating_blade/blade = new blade_type(get_turf(owner))
|
||||
blades += blade
|
||||
blade.orbit(owner, blade_orbit_radius)
|
||||
RegisterSignal(blade, COMSIG_QDELETING, PROC_REF(remove_blade))
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
return span_notice("It's at <b>[round(atom_integrity * 100 / max_integrity)]%</b> stability.")
|
||||
return ..()
|
||||
|
||||
// no breaky herety thingy
|
||||
/obj/structure/destructible/eldritch_crucible/rust_heretic_act()
|
||||
return
|
||||
|
||||
/obj/structure/destructible/eldritch_crucible/attacked_by(obj/item/weapon, mob/living/user)
|
||||
if(!iscarbon(user))
|
||||
return ..()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/// The heretic's rune, which they use to complete transmutation rituals.
|
||||
/obj/effect/heretic_rune
|
||||
name = "transmutation rune"
|
||||
desc = "A flowing circle of shapes and runes is etched into the floor, filled with a thick black tar-like fluid."
|
||||
icon_state = ""
|
||||
desc = "A flowing circle of shapes and runes is etched into the floor, filled with a thick black tar-like fluid. This one looks pretty small."
|
||||
icon = 'icons/obj/antags/cult/rune.dmi'
|
||||
icon_state = "main1"
|
||||
anchored = TRUE
|
||||
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
@@ -260,16 +260,17 @@
|
||||
icon = 'icons/mob/shells.dmi'
|
||||
icon_state = "construct_cult"
|
||||
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
|
||||
|
||||
/obj/structure/constructshell/examine(mob/user)
|
||||
. = ..()
|
||||
if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD)
|
||||
. += {"<span class='cult'>A construct shell, used to house bound souls from a soulstone.\n
|
||||
var/extra_desc = {"<span class='cult'>A construct shell, used to house bound souls from a soulstone.\n
|
||||
Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n
|
||||
An <b>Artificer</b>, which can produce <b>more shells and soulstones</b>, as well as fortifications.\n
|
||||
A <b>Wraith</b>, which does high damage and can jaunt through walls, though it is quite fragile.\n
|
||||
A <b>Juggernaut</b>, which is very hard to kill and can produce temporary walls, but is slow.</span>"}
|
||||
|
||||
/obj/structure/constructshell/examine(mob/user)
|
||||
. = ..()
|
||||
if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD)
|
||||
. += extra_desc
|
||||
|
||||
/obj/structure/constructshell/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, /obj/item/soulstone))
|
||||
var/obj/item/soulstone/SS = O
|
||||
@@ -494,6 +495,11 @@
|
||||
make_new_construct(/mob/living/basic/construct/artificer/angelic, target, creator, cultoverride, loc_override)
|
||||
if(THEME_CULT)
|
||||
make_new_construct(/mob/living/basic/construct/artificer/noncult, target, creator, cultoverride, loc_override)
|
||||
if(CONSTRUCT_HARVESTER)
|
||||
if(IS_HERETIC_OR_MONSTER(creator))
|
||||
make_new_construct(/mob/living/basic/construct/harvester/heretic, target, creator, cultoverride, loc_override)
|
||||
else
|
||||
make_new_construct(/mob/living/basic/construct/harvester, target, creator, cultoverride, loc_override)
|
||||
|
||||
/proc/make_new_construct(mob/living/basic/construct/ctype, mob/target, mob/stoner = null, cultoverride = FALSE, loc_override = null)
|
||||
if(QDELETED(target))
|
||||
@@ -523,7 +529,7 @@
|
||||
newstruct.clear_alert("bloodsense")
|
||||
sense_alert = newstruct.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
|
||||
if(sense_alert)
|
||||
sense_alert.Cviewer = newstruct
|
||||
sense_alert.construct_owner = newstruct
|
||||
newstruct.cancel_camera()
|
||||
|
||||
/obj/item/soulstone/anybody
|
||||
|
||||
@@ -310,25 +310,12 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
|
||||
if(.)
|
||||
return .
|
||||
|
||||
if(istype(bible_smacked, /obj/item/cult_bastard) && !IS_CULTIST(user))
|
||||
var/obj/item/cult_bastard/sword = bible_smacked
|
||||
bible_smacked.balloon_alert(user, "exorcising...")
|
||||
if(istype(bible_smacked, /obj/item/melee/cultblade/haunted) && !IS_CULTIST(user))
|
||||
var/obj/item/melee/cultblade/haunted/sword = bible_smacked
|
||||
sword.balloon_alert(user, "exorcising...")
|
||||
playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,TRUE)
|
||||
if(do_after(user, 4 SECONDS, target = sword))
|
||||
playsound(src,'sound/effects/pray_chaplain.ogg',60,TRUE)
|
||||
for(var/obj/item/soulstone/stone in sword.contents)
|
||||
stone.required_role = null
|
||||
for(var/mob/living/basic/shade/shade in stone)
|
||||
var/datum/antagonist/cult/cultist = shade.mind.has_antag_datum(/datum/antagonist/cult)
|
||||
if(cultist)
|
||||
cultist.silent = TRUE
|
||||
cultist.on_removal()
|
||||
SSblackbox.record_feedback("tally", "cult_shade_purified", 1)
|
||||
shade.theme = THEME_HOLY
|
||||
shade.name = "Purified [shade.real_name]"
|
||||
shade.update_appearance(UPDATE_ICON_STATE)
|
||||
stone.release_shades(user)
|
||||
qdel(stone)
|
||||
new /obj/item/nullrod/claymore(get_turf(sword))
|
||||
user.visible_message(span_notice("[user] exorcises [sword]!"))
|
||||
qdel(sword)
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
deconstruct(TRUE)
|
||||
return TRUE
|
||||
|
||||
/obj/structure/closet/crate/grave/container_resist_act(mob/living/user)
|
||||
/obj/structure/closet/crate/grave/container_resist_act(mob/living/user, loc_required = TRUE)
|
||||
if(opened)
|
||||
return
|
||||
// The player is trying to dig themselves out of an early grave
|
||||
|
||||
@@ -290,8 +290,12 @@
|
||||
/mob/proc/is_holding_items()
|
||||
return !!locate(/obj/item) in held_items
|
||||
|
||||
/**
|
||||
* Returns a list of all dropped held items.
|
||||
* If none were dropped, returns an empty list.
|
||||
*/
|
||||
/mob/proc/drop_all_held_items()
|
||||
. = FALSE
|
||||
. = list()
|
||||
for(var/obj/item/I in held_items)
|
||||
. |= dropItemToGround(I)
|
||||
|
||||
@@ -319,24 +323,25 @@
|
||||
|
||||
/**
|
||||
* Used to drop an item (if it exists) to the ground.
|
||||
* * Will pass as TRUE is successfully dropped, or if there is no item to drop.
|
||||
* * Will pass FALSE if the item can not be dropped due to TRAIT_NODROP via doUnEquip()
|
||||
* * Will return null if the item wasn't dropped.
|
||||
* * If it was, returns the item.
|
||||
* If the item can be dropped, it will be forceMove()'d to the ground and the turf's Entered() will be called.
|
||||
*/
|
||||
/mob/proc/dropItemToGround(obj/item/I, force = FALSE, silent = FALSE, invdrop = TRUE)
|
||||
if (isnull(I))
|
||||
return TRUE
|
||||
return
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MOB_DROPPING_ITEM)
|
||||
. = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
|
||||
var/try_uneqip = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
|
||||
|
||||
if(!. || !I) //ensure the item exists and that it was dropped properly.
|
||||
if(!try_uneqip || !I) //ensure the item exists and that it was dropped properly.
|
||||
return
|
||||
|
||||
if(!(I.item_flags & NO_PIXEL_RANDOM_DROP))
|
||||
I.pixel_x = I.base_pixel_x + rand(-6, 6)
|
||||
I.pixel_y = I.base_pixel_y + rand(-6, 6)
|
||||
I.do_drop_animation(src)
|
||||
return I
|
||||
|
||||
//for when the item will be immediately placed in a loc other than the ground
|
||||
/mob/proc/transferItemToLoc(obj/item/I, newloc = null, force = FALSE, silent = TRUE)
|
||||
@@ -416,13 +421,23 @@
|
||||
items += worn_under.attached_accessories
|
||||
return items
|
||||
|
||||
/**
|
||||
* Returns the items that were succesfully unequipped.
|
||||
*/
|
||||
/mob/living/proc/unequip_everything()
|
||||
var/list/items = list()
|
||||
items |= get_equipped_items(INCLUDE_POCKETS)
|
||||
// In case something isn't actually unequipped somehow
|
||||
var/list/dropped_items = list()
|
||||
for(var/I in items)
|
||||
dropItemToGround(I)
|
||||
drop_all_held_items()
|
||||
|
||||
var/return_val = dropItemToGround(I)
|
||||
if(!isitem(return_val))
|
||||
continue
|
||||
dropped_items |= return_val
|
||||
var/return_val = drop_all_held_items()
|
||||
if(islist(return_val))
|
||||
dropped_items |= return_val
|
||||
return dropped_items
|
||||
|
||||
/mob/living/carbon/proc/check_obscured_slots(transparent_protection)
|
||||
var/obscured = NONE
|
||||
|
||||
@@ -51,10 +51,12 @@
|
||||
THEME_CULT = list(/obj/item/ectoplasm/construct),
|
||||
THEME_HOLY = list(/obj/item/ectoplasm/angelic),
|
||||
THEME_WIZARD = list(/obj/item/ectoplasm/mystic),
|
||||
THEME_HERETIC = list(/obj/item/ectoplasm/construct),
|
||||
)
|
||||
|
||||
/mob/living/basic/construct/Initialize(mapload)
|
||||
. = ..()
|
||||
throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
|
||||
AddElement(/datum/element/simple_flying)
|
||||
var/list/remains = string_list(remains_by_theme[theme])
|
||||
if(length(remains))
|
||||
|
||||
@@ -26,13 +26,16 @@
|
||||
|
||||
/mob/living/basic/construct/harvester/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(\
|
||||
/datum/element/amputating_limbs,\
|
||||
grant_abilities()
|
||||
|
||||
/mob/living/basic/construct/harvester/proc/grant_abilities()
|
||||
AddElement(/datum/element/wall_walker, /turf/closed/wall/mineral/cult)
|
||||
AddComponent(\
|
||||
/datum/component/amputating_limbs,\
|
||||
surgery_time = 0,\
|
||||
surgery_verb = "slicing",\
|
||||
minimum_stat = CONSCIOUS,\
|
||||
)
|
||||
AddElement(/datum/element/wall_walker, /turf/closed/wall/mineral/cult)
|
||||
var/datum/action/innate/seek_prey/seek = new(src)
|
||||
seek.Grant(src)
|
||||
seek.Activate()
|
||||
@@ -59,7 +62,7 @@
|
||||
overlay_icon_state = "bg_demon_border"
|
||||
|
||||
buttontooltipstyle = "cult"
|
||||
button_icon = "icons/mob/actions/actions_cult.dmi"
|
||||
button_icon = 'icons/mob/actions/actions_cult.dmi'
|
||||
button_icon_state = "cult_mark"
|
||||
/// Where is nar nar? Are we even looking?
|
||||
var/tracking = FALSE
|
||||
@@ -93,7 +96,6 @@
|
||||
the_construct.seeking = TRUE
|
||||
to_chat(the_construct, span_cult_italic("You are now tracking your master."))
|
||||
|
||||
|
||||
/datum/action/innate/seek_prey
|
||||
name = "Seek the Harvest"
|
||||
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
|
||||
@@ -126,3 +128,115 @@
|
||||
desc = "Activate to track Nar'Sie!"
|
||||
button_icon_state = "sintouch"
|
||||
the_construct.seeking = TRUE
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic
|
||||
name = "Rusted Harvester"
|
||||
real_name = "Rusted Harvester"
|
||||
desc = "A long, thin, decrepit construct originally built to herald Nar'Sie's rise, corrupted and rusted by the forces of the Mansus to spread its will instead."
|
||||
icon_state = "harvester"
|
||||
icon_living = "harvester"
|
||||
construct_spells = list(
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion,
|
||||
/datum/action/cooldown/spell/pointed/rust_construction,
|
||||
)
|
||||
can_repair = FALSE
|
||||
slowed_by_drag = FALSE
|
||||
faction = list(FACTION_HERETIC)
|
||||
maxHealth = 35
|
||||
health = 35
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
// Dim green
|
||||
lighting_cutoff_red = 10
|
||||
lighting_cutoff_green = 20
|
||||
lighting_cutoff_blue = 5
|
||||
playstyle_string = "<B>You are a Rusted Harvester, built to serve the Sanguine Apostate, twisted to work the will of the Mansus. You are fragile and weak, but you rend cultists (only) apart on each attack. Follow your Master's orders!<B>"
|
||||
theme = THEME_HERETIC
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_MANSUS_TOUCHED, REF(src))
|
||||
add_filter("rusted_harvester", 3, list("type" = "outline", "color" = COLOR_GREEN, "size" = 2, "alpha" = 40))
|
||||
|
||||
|
||||
/**
|
||||
* Somewhat janky proc called when a heretic monster's master dies
|
||||
* Used to kill any living Rusted Harvester
|
||||
*/
|
||||
/mob/living/proc/on_master_death()
|
||||
return
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic/attack_animal(mob/living/simple_animal/user, list/modifiers)
|
||||
// They're pretty fragile so this is probably necessary to prevent bullshit deaths.
|
||||
if(user == src)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic/on_master_death()
|
||||
to_chat(src, span_userdanger("Your link to the mansus suddenly snaps as your master perishes! Without its support, your body crumbles..."))
|
||||
visible_message(span_alert("[src] suddenly crumbles to dust!"))
|
||||
death()
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic/grant_abilities()
|
||||
AddElement(/datum/element/wall_walker, or_trait = TRAIT_RUSTY)
|
||||
AddElement(/datum/element/leeching_walk)
|
||||
AddComponent(\
|
||||
/datum/component/amputating_limbs,\
|
||||
surgery_time = 1.5 SECONDS,\
|
||||
surgery_verb = "slicing",\
|
||||
minimum_stat = CONSCIOUS,\
|
||||
pre_hit_callback = CALLBACK(src, PROC_REF(is_cultist_handler)),\
|
||||
)
|
||||
AddComponent(/datum/component/damage_aura,\
|
||||
range = 3,\
|
||||
brute_damage = 0.5,\
|
||||
burn_damage = 0.5,\
|
||||
toxin_damage = 0.5,\
|
||||
stamina_damage = 4,\
|
||||
simple_damage = 1.5,\
|
||||
immune_factions = list(FACTION_HERETIC),\
|
||||
damage_message = span_boldwarning("Your body wilts and withers as it comes near [src]'s aura."),\
|
||||
message_probability = 7,\
|
||||
current_owner = src,\
|
||||
)
|
||||
var/datum/action/innate/seek_master/heretic/seek = new(src)
|
||||
seek.Grant(src)
|
||||
seek.Activate()
|
||||
|
||||
// These aren't friends they're assholes
|
||||
// Don't let them be near you!
|
||||
/mob/living/basic/construct/harvester/heretic/Life(seconds_per_tick, times_fired)
|
||||
. = ..()
|
||||
if(!SPT_PROB(7, seconds_per_tick))
|
||||
return
|
||||
|
||||
var/turf/adjacent = get_step(src, pick(GLOB.alldirs))
|
||||
// 90% chance to be directional, otherwise what we're on top of
|
||||
var/turf/open/land = (isopenturf(adjacent) && prob(90)) ? adjacent : get_turf(src)
|
||||
do_rust_heretic_act(land)
|
||||
|
||||
if(prob(7))
|
||||
to_chat(src, span_notice("Eldritch energies emanate from your body."))
|
||||
|
||||
/mob/living/basic/construct/harvester/heretic/proc/is_cultist_handler(mob/victim)
|
||||
return IS_CULTIST(victim)
|
||||
|
||||
/datum/action/innate/seek_master/heretic
|
||||
name = "Seek your Master"
|
||||
desc = "Use your direct link to the Mansus to sense where your master is located via the arrow on the top-right of your HUD."
|
||||
button_icon = 'icons/mob/actions/actions_cult.dmi'
|
||||
background_icon_state = "bg_heretic"
|
||||
overlay_icon_state = "bg_heretic_border"
|
||||
tracking = TRUE
|
||||
|
||||
/datum/action/innate/seek_master/heretic/New(Target)
|
||||
. = ..()
|
||||
the_construct = Target
|
||||
the_construct.seeking = TRUE
|
||||
var/datum/antagonist/heretic_monster/antag = IS_HERETIC_MONSTER(the_construct)
|
||||
if(antag)
|
||||
the_construct.master = antag.master
|
||||
|
||||
// no real reason for most of this weird oldcode
|
||||
/datum/action/innate/seek_master/Activate()
|
||||
return
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
AddElement(/datum/element/dextrous)
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT)
|
||||
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = gorilla_food)
|
||||
AddElement(
|
||||
/datum/element/amputating_limbs, \
|
||||
AddComponent(
|
||||
/datum/component/amputating_limbs, \
|
||||
surgery_time = 0 SECONDS, \
|
||||
surgery_verb = "punches",\
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
if(cult_converted)
|
||||
for(var/mob/living/cultist as anything in invokers)
|
||||
to_chat(cultist, span_cult_italic("[src] has already been sacrificed!"))
|
||||
return STOP_SACRIFICE
|
||||
return STOP_SACRIFICE|SILENCE_SACRIFICE_MESSAGE
|
||||
|
||||
for(var/mob/living/cultist as anything in invokers)
|
||||
to_chat(cultist, span_cult_italic("This feels a bit too cliché, don't you think?"))
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
/mob/living/basic/heretic_summon/armsy/Initialize(mapload, spawn_bodyparts = TRUE, worm_length = 6)
|
||||
. = ..()
|
||||
AddElement(/datum/element/wall_smasher, ENVIRONMENT_SMASH_RWALLS)
|
||||
AddElement(\
|
||||
/datum/element/amputating_limbs,\
|
||||
AddComponent(\
|
||||
/datum/component/amputating_limbs,\
|
||||
surgery_time = 0 SECONDS,\
|
||||
surgery_verb = "tears",\
|
||||
minimum_stat = CONSCIOUS,\
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_RUST)
|
||||
|
||||
var/static/list/grantable_spells = list(
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion/small = BB_GENERIC_ACTION,
|
||||
/datum/action/cooldown/spell/aoe/rust_conversion = BB_GENERIC_ACTION,
|
||||
/datum/action/cooldown/spell/basic_projectile/rust_wave/short = BB_TARGETED_ACTION,
|
||||
)
|
||||
grant_actions_by_list(grantable_spells)
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
AddElement(/datum/element/mob_grabber)
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
|
||||
AddElement(/datum/element/basic_eating, food_types = target_foods)
|
||||
AddElement(\
|
||||
/datum/element/amputating_limbs,\
|
||||
AddComponent(\
|
||||
/datum/component/amputating_limbs,\
|
||||
surgery_verb = "begins snipping",\
|
||||
target_zones = GLOB.arm_zones,\
|
||||
)
|
||||
|
||||
@@ -698,5 +698,7 @@
|
||||
. |= SHOVE_CAN_STAGGER
|
||||
if(IsKnockdown() && !IsParalyzed())
|
||||
. |= SHOVE_CAN_KICK_SIDE
|
||||
if(HAS_TRAIT(src, TRAIT_NO_SIDE_KICK)) // added as an extra check, just in case
|
||||
. &= ~SHOVE_CAN_KICK_SIDE
|
||||
|
||||
#undef SHAKE_ANIMATION_OFFSET
|
||||
|
||||
@@ -89,8 +89,7 @@
|
||||
. = ..()
|
||||
var/obj/item/storage/backpack/bag = new_snailperson.get_item_by_slot(ITEM_SLOT_BACK)
|
||||
if(!istype(bag, /obj/item/storage/backpack/snail))
|
||||
if(new_snailperson.dropItemToGround(bag)) //returns TRUE even if its null
|
||||
new_snailperson.equip_to_slot_or_del(new /obj/item/storage/backpack/snail(new_snailperson), ITEM_SLOT_BACK)
|
||||
new_snailperson.equip_to_slot_or_del(new /obj/item/storage/backpack/snail(new_snailperson), ITEM_SLOT_BACK)
|
||||
new_snailperson.AddElement(/datum/element/lube_walking, require_resting = TRUE)
|
||||
new_snailperson.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/snail, multiplicative_slowdown = snail_speed_mod)
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@
|
||||
|
||||
for(var/mob/living/cultist as anything in invokers)
|
||||
to_chat(cultist, span_cult_italic("You don't think this is what Nar'Sie had in mind when She asked for blood sacrifices..."))
|
||||
return STOP_SACRIFICE
|
||||
return STOP_SACRIFICE|SILENCE_SACRIFICE_MESSAGE
|
||||
|
||||
/// Updates the distance we can be from our pai card
|
||||
/mob/living/silicon/pai/proc/increment_range(increment_amount)
|
||||
|
||||
@@ -277,3 +277,78 @@
|
||||
charges--
|
||||
user.AddComponent(/datum/component/shrink, -1) // small forever
|
||||
return ..()
|
||||
|
||||
// Wand of debugging
|
||||
|
||||
#ifdef TESTING
|
||||
|
||||
/obj/item/gun/magic/wand/antag
|
||||
name = "wand of antag"
|
||||
desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag"
|
||||
school = SCHOOL_FORBIDDEN
|
||||
ammo_type = /obj/item/ammo_casing/magic/antag
|
||||
icon_state = "revivewand"
|
||||
base_icon_state = "revivewand"
|
||||
color = COLOR_ADMIN_PINK
|
||||
max_charges = 99999
|
||||
|
||||
/obj/item/gun/magic/wand/antag/zap_self(mob/living/user)
|
||||
. = ..()
|
||||
var/obj/item/ammo_casing/magic/antag/casing = new ammo_type()
|
||||
var/obj/projectile/magic/magic_proj = casing.projectile_type
|
||||
magic_proj = new magic_proj(src)
|
||||
magic_proj.on_hit(user)
|
||||
QDEL_NULL(casing)
|
||||
|
||||
/obj/item/ammo_casing/magic/antag
|
||||
projectile_type = /obj/projectile/magic/antag
|
||||
harmful = FALSE
|
||||
|
||||
/obj/projectile/magic/antag
|
||||
name = "bolt of antag"
|
||||
icon_state = "ion"
|
||||
var/antag = /datum/antagonist/traitor
|
||||
|
||||
/obj/projectile/magic/antag/on_hit(atom/target, blocked, pierce_hit)
|
||||
. = ..()
|
||||
|
||||
if(isliving(target))
|
||||
var/mob/living/victim = target
|
||||
if(isnull(victim.mind))
|
||||
victim.mind_initialize()
|
||||
if(victim.mind.has_antag_datum(antag))
|
||||
victim.mind.remove_antag_datum(antag)
|
||||
to_chat(world, "removed")
|
||||
else
|
||||
victim.mind.add_antag_datum(antag)
|
||||
to_chat(world, "added")
|
||||
|
||||
/obj/item/gun/magic/wand/antag/heretic
|
||||
name = "wand of antag heretic"
|
||||
desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag heretic"
|
||||
color = COLOR_GREEN
|
||||
ammo_type = /obj/item/ammo_casing/magic/antag/heretic
|
||||
|
||||
/obj/item/ammo_casing/magic/antag/heretic
|
||||
projectile_type = /obj/projectile/magic/antag/heretic
|
||||
|
||||
/obj/projectile/magic/antag/heretic
|
||||
name = "bolt of antag heretic"
|
||||
icon_state = "ion"
|
||||
antag = /datum/antagonist/heretic
|
||||
|
||||
/obj/item/gun/magic/wand/antag/cult
|
||||
name = "wand of antag cultist"
|
||||
desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag cultist"
|
||||
color = COLOR_CULT_RED
|
||||
ammo_type = /obj/item/ammo_casing/magic/antag/cult
|
||||
|
||||
/obj/item/ammo_casing/magic/antag/cult
|
||||
projectile_type = /obj/projectile/magic/antag/cult
|
||||
|
||||
/obj/projectile/magic/antag/cult
|
||||
name = "bolt of antag cult"
|
||||
icon_state = "ion"
|
||||
antag = /datum/antagonist/cult
|
||||
|
||||
#endif
|
||||
|
||||
@@ -246,7 +246,11 @@
|
||||
return target // They're just standing around, proceed as normal
|
||||
|
||||
if(HAS_TRAIT(cast_loc, TRAIT_CASTABLE_LOC))
|
||||
return cast_loc // They're in an atom which allows casting, so redirect the caster to loc
|
||||
if(HAS_TRAIT(cast_loc, TRAIT_SPELLS_TRANSFER_TO_LOC) && ismob(cast_loc.loc))
|
||||
return cast_loc.loc
|
||||
else
|
||||
return cast_loc
|
||||
// They're in an atom which allows casting, so redirect the caster to loc
|
||||
|
||||
return null
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
var/deactive_msg
|
||||
/// The casting range of our spell
|
||||
var/cast_range = 7
|
||||
/// Variable dictating if the spell will use turf based aim assist
|
||||
/// If aim asisst is used. Disable to disable
|
||||
var/aim_assist = TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/New(Target)
|
||||
@@ -65,17 +65,18 @@
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target)
|
||||
|
||||
var/atom/aim_assist_target
|
||||
if(aim_assist && isturf(target))
|
||||
// Find any human in the list. We aren't picky, it's aim assist after all
|
||||
aim_assist_target = locate(/mob/living/carbon/human) in target
|
||||
if(!aim_assist_target)
|
||||
// If we didn't find a human, we settle for any living at all
|
||||
aim_assist_target = locate(/mob/living) in target
|
||||
|
||||
if(aim_assist)
|
||||
aim_assist_target = aim_assist(caller, target)
|
||||
return ..(caller, params, aim_assist_target || target)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/proc/aim_assist(mob/living/caller, atom/target)
|
||||
if(!isturf(target))
|
||||
return
|
||||
|
||||
// Find any human, or if that fails, any living target
|
||||
return locate(/mob/living/carbon/human) in target || locate(/mob/living) in target
|
||||
|
||||
/datum/action/cooldown/spell/pointed/is_valid_target(atom/cast_on)
|
||||
if(cast_on == owner)
|
||||
to_chat(owner, span_warning("You cannot cast [src] on yourself!"))
|
||||
|
||||
@@ -48,6 +48,15 @@
|
||||
which_hand = BODY_ZONE_PRECISE_R_HAND
|
||||
return get_bodypart(check_zone(which_hand))
|
||||
|
||||
/// Gets the inactive hand of the mob. Returns FALSE on non-carbons, otherwise returns the /obj/item/bodypart.
|
||||
/mob/proc/get_inactive_hand()
|
||||
return null
|
||||
|
||||
/mob/living/carbon/get_inactive_hand()
|
||||
var/which_hand = BODY_ZONE_PRECISE_R_HAND
|
||||
if(!(active_hand_index % RIGHT_HANDS))
|
||||
which_hand = BODY_ZONE_PRECISE_L_HAND
|
||||
return get_bodypart(check_zone(which_hand))
|
||||
|
||||
/mob/proc/has_left_hand(check_disabled = TRUE)
|
||||
return TRUE
|
||||
|
||||
|
Before Width: | Height: | Size: 908 KiB After Width: | Height: | Size: 904 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
@@ -1016,6 +1016,7 @@
|
||||
#include "code\datums\components\ai_has_target_timer.dm"
|
||||
#include "code\datums\components\ai_listen_to_weather.dm"
|
||||
#include "code\datums\components\ai_retaliate_advanced.dm"
|
||||
#include "code\datums\components\amputating_limbs.dm"
|
||||
#include "code\datums\components\anti_magic.dm"
|
||||
#include "code\datums\components\appearance_on_aggro.dm"
|
||||
#include "code\datums\components\aquarium_content.dm"
|
||||
@@ -1372,7 +1373,6 @@
|
||||
#include "code\datums\elements\ai_retaliate.dm"
|
||||
#include "code\datums\elements\ai_swap_combat_mode.dm"
|
||||
#include "code\datums\elements\ai_target_damagesource.dm"
|
||||
#include "code\datums\elements\amputating_limbs.dm"
|
||||
#include "code\datums\elements\animal_variety.dm"
|
||||
#include "code\datums\elements\art.dm"
|
||||
#include "code\datums\elements\atmos_requirements.dm"
|
||||
@@ -1458,6 +1458,7 @@
|
||||
#include "code\datums\elements\kneejerk.dm"
|
||||
#include "code\datums\elements\knockback.dm"
|
||||
#include "code\datums\elements\lazy_fishing_spot.dm"
|
||||
#include "code\datums\elements\leeching_walk.dm"
|
||||
#include "code\datums\elements\lifesteal.dm"
|
||||
#include "code\datums\elements\light_blocking.dm"
|
||||
#include "code\datums\elements\light_eaten.dm"
|
||||
@@ -3050,7 +3051,6 @@
|
||||
#include "code\modules\antagonists\clown_ops\clownop.dm"
|
||||
#include "code\modules\antagonists\clown_ops\outfits.dm"
|
||||
#include "code\modules\antagonists\cult\blood_magic.dm"
|
||||
#include "code\modules\antagonists\cult\cult_bastard_sword.dm"
|
||||
#include "code\modules\antagonists\cult\cult_comms.dm"
|
||||
#include "code\modules\antagonists\cult\cult_items.dm"
|
||||
#include "code\modules\antagonists\cult\cult_objectives.dm"
|
||||
@@ -3063,6 +3063,7 @@
|
||||
#include "code\modules\antagonists\cult\cult_turf_overlay.dm"
|
||||
#include "code\modules\antagonists\cult\rune_spawn_action.dm"
|
||||
#include "code\modules\antagonists\cult\runes.dm"
|
||||
#include "code\modules\antagonists\cult\sword_fling.dm"
|
||||
#include "code\modules\antagonists\cult\datums\constructs.dm"
|
||||
#include "code\modules\antagonists\cult\datums\cult_team.dm"
|
||||
#include "code\modules\antagonists\cult\datums\cultist.dm"
|
||||
@@ -3085,6 +3086,7 @@
|
||||
#include "code\modules\antagonists\heretic\knife_effect.dm"
|
||||
#include "code\modules\antagonists\heretic\moon_lunatic.dm"
|
||||
#include "code\modules\antagonists\heretic\rust_effect.dm"
|
||||
#include "code\modules\antagonists\heretic\soultrapped_heretic.dm"
|
||||
#include "code\modules\antagonists\heretic\transmutation_rune.dm"
|
||||
#include "code\modules\antagonists\heretic\items\corrupted_organs.dm"
|
||||
#include "code\modules\antagonists\heretic\items\eldritch_flask.dm"
|
||||
|
||||