Soulscythe Refactor to Basic Mob (#91667)

<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

I was planning on doing this a few years ago but didn't to let other
people do it; no one did it so I just did it since it's still on the
docket. Even easier in a post #90578 world

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

Lot more lightweight as it were. This mob also doesn't really need to
"live" either, so we can bypass the whole life chain as well to
alleviate any struggles. The item does 95% of the work anyways lol.

also damn you guys laxed up on the alphabetization requirements. touched
all that up while in the areas i was fixing. soulscythes are in their
own file now too instead of the bubblegum one or whatever

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and its effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

🆑
refactor: Soulscythes have undergone a mild refactor, but everything
should work as it should. Report any issues.
/🆑

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

(cherry picked from commit f74c9d7aac)
This commit is contained in:
san7890
2025-06-28 20:38:32 -04:00
committed by Roxy
parent fd2228cd28
commit 1fc53f679b
6 changed files with 305 additions and 301 deletions
+14 -14
View File
@@ -8,31 +8,31 @@ GLOBAL_LIST_EMPTY(stealthminID) //reference list with IDs that store ckeys, for
/// List of types of abstract mob which shouldn't usually exist in the world on its own if we're spawning random mobs
GLOBAL_LIST_INIT(abstract_mob_types, list(
/mob/living/basic,
/mob/living/basic/blob_minion,
/mob/living/basic/bot,
/mob/living/basic/construct,
/mob/living/basic/guardian,
/mob/living/basic/heretic_summon,
/mob/living/basic/mimic, // Cannot exist if spawned without being passed an item reference
/mob/living/basic/mining,
/mob/living/basic/pet,
/mob/living/basic,
/mob/living/basic/soulscythe, // This is just a way for players to control the soulscythe item
/mob/living/basic/spider,
/mob/living/carbon/alien/adult,
/mob/living/carbon/alien,
/mob/living/carbon/human/consistent,
/mob/living/carbon/human/dummy/consistent,
/mob/living/carbon/human/dummy,
/mob/living/carbon/human/species,
/mob/living/carbon,
/mob/living/carbon/alien,
/mob/living/carbon/alien/adult,
/mob/living/carbon/human/consistent,
/mob/living/carbon/human/dummy,
/mob/living/carbon/human/dummy/consistent,
/mob/living/carbon/human/species,
/mob/living/silicon,
/mob/living/simple_animal/bot,
/mob/living/simple_animal/hostile/asteroid/elite,
/mob/living/simple_animal/hostile/asteroid,
/mob/living/simple_animal/hostile/megafauna,
/mob/living/basic/mimic, // Cannot exist if spawned without being passed an item reference
/mob/living/simple_animal/hostile,
/mob/living/simple_animal/soulscythe, // As mimic, can't exist if spawned outside an item
/mob/living/simple_animal,
/mob/living/simple_animal/bot,
/mob/living/simple_animal/hostile,
/mob/living/simple_animal/hostile/asteroid,
/mob/living/simple_animal/hostile/asteroid/elite,
/mob/living/simple_animal/hostile/megafauna,
))
+1 -1
View File
@@ -1,4 +1,4 @@
/datum/hud/soulscythe/New(mob/living/simple_animal/soulscythe/owner)
/datum/hud/soulscythe/New(mob/living/basic/soulscythe/owner)
. = ..()
var/atom/movable/screen/using = new /atom/movable/screen/blood_level(null, src)
static_inventory += using
+289
View File
@@ -0,0 +1,289 @@
#define MAX_BLOOD_LEVEL 100
/obj/item/soulscythe
name = "soulscythe"
desc = "An old relic of hell created by devils to establish themselves as the leadership of hell over the demons. It grows stronger while it possesses a powerful soul."
icon = 'icons/obj/mining_zones/artefacts.dmi'
icon_state = "soulscythe"
inhand_icon_state = "soulscythe"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
attack_verb_continuous = list("chops", "slices", "cuts", "reaps")
attack_verb_simple = list("chop", "slice", "cut", "reap")
hitsound = 'sound/items/weapons/bladeslice.ogg'
inhand_x_dimension = 64
inhand_y_dimension = 64
force = 20
throwforce = 17
armour_penetration = 50
sharpness = SHARP_EDGED
exposed_wound_bonus = 10
layer = MOB_LAYER
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/// Soulscythe mob in the scythe
var/mob/living/basic/soulscythe/soul
/// Are we grabbing a spirit?
var/using = FALSE
/// Currently charging?
var/charging = FALSE
/// Cooldown between moves
COOLDOWN_DECLARE(move_cooldown)
/// Cooldown between attacks
COOLDOWN_DECLARE(attack_cooldown)
/obj/item/soulscythe/Initialize(mapload)
. = ..()
soul = new(src)
RegisterSignal(soul, COMSIG_LIVING_RESIST, PROC_REF(on_resist))
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED, PROC_REF(on_attack))
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED_SECONDARY, PROC_REF(on_secondary_attack))
RegisterSignal(src, COMSIG_ATOM_INTEGRITY_CHANGED, PROC_REF(on_integrity_change))
AddElement(/datum/element/bane, mob_biotypes = MOB_PLANT, damage_multiplier = 0.5, requires_combat_mode = FALSE)
/obj/item/soulscythe/examine(mob/user)
. = ..()
. += soul.ckey ? span_nicegreen("There is a soul inhabiting it.") : span_danger("It's dormant.")
/obj/item/soulscythe/attack(mob/living/attacked, mob/living/user, list/modifiers, list/attack_modifiers)
. = ..()
if(attacked.stat != DEAD)
give_blood(10)
/obj/item/soulscythe/attack_hand(mob/user, list/modifiers)
if(soul.ckey && !soul.faction_check_atom(user))
to_chat(user, span_warning("You can't pick up [src]!"))
return
return ..()
/obj/item/soulscythe/pickup(mob/user)
. = ..()
if(soul.ckey)
animate(src) //stop spinnage
/obj/item/soulscythe/dropped(mob/user, silent)
. = ..()
if(soul.ckey)
reset_spin() //resume spinnage
/obj/item/soulscythe/attack_self(mob/user, modifiers)
if(using || soul.ckey || soul.stat)
return
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
balloon_alert(user, "you can't awaken the scythe!")
return
using = TRUE
balloon_alert(user, "you hold the scythe up...")
ADD_TRAIT(src, TRAIT_NODROP, type)
var/mob/chosen_one = SSpolling.poll_ghosts_for_target(
check_jobban = ROLE_PAI,
poll_time = 20 SECONDS,
checked_target = src,
ignore_category = POLL_IGNORE_POSSESSED_BLADE,
alert_pic = src,
role_name_text = "soulscythe soul",
chat_text_border_icon = src,
)
on_poll_concluded(user, chosen_one)
/// Ghost poll has concluded and a candidate has been chosen.
/obj/item/soulscythe/proc/on_poll_concluded(mob/living/master, mob/dead/observer/ghost)
if(isnull(ghost))
balloon_alert(master, "the scythe is dormant!")
REMOVE_TRAIT(src, TRAIT_NODROP, type)
using = FALSE
return
soul.PossessByPlayer(ghost.ckey)
soul.copy_languages(master, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the master.
soul.faction = list("[REF(master)]")
balloon_alert(master, "the scythe glows")
add_overlay("soulscythe_gem")
density = TRUE
if(!ismob(loc))
reset_spin()
REMOVE_TRAIT(src, TRAIT_NODROP, type)
using = FALSE
/obj/item/soulscythe/relaymove(mob/living/user, direction)
if(!COOLDOWN_FINISHED(src, move_cooldown) || charging)
return
if(!isturf(loc))
balloon_alert(user, "resist out!")
COOLDOWN_START(src, move_cooldown, 1 SECONDS)
return
if(!use_blood(1, FALSE))
return
if(pixel_x != base_pixel_x || pixel_y != base_pixel_y)
animate(src, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y, flags = ANIMATION_PARALLEL)
try_step_multiz(direction)
COOLDOWN_START(src, move_cooldown, (direction in GLOB.cardinals) ? 0.1 SECONDS : 0.2 SECONDS)
/obj/item/soulscythe/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!charging)
return
charging = FALSE
throwforce *= 0.5
reset_spin()
if(ismineralturf(hit_atom))
var/turf/closed/mineral/hit_rock = hit_atom
hit_rock.gets_drilled()
if(isliving(hit_atom))
var/mob/living/hit_mob = hit_atom
if(hit_mob.stat != DEAD)
give_blood(15)
/obj/item/soulscythe/AllowClick()
return TRUE
/obj/item/soulscythe/proc/use_blood(amount = 0, message = TRUE)
if(amount > soul.blood_volume)
if(message)
to_chat(soul, span_warning("Not enough blood!"))
return FALSE
soul.blood_volume -= amount
return TRUE
/obj/item/soulscythe/proc/give_blood(amount)
soul.blood_volume = min(MAX_BLOOD_LEVEL, soul.blood_volume + amount)
/obj/item/soulscythe/proc/on_resist(mob/living/user)
SIGNAL_HANDLER
if(isturf(loc))
return
INVOKE_ASYNC(src, PROC_REF(break_out))
/obj/item/soulscythe/proc/break_out()
if(!use_blood(10))
return
balloon_alert(soul, "you resist...")
if(!do_after(soul, 5 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
balloon_alert(soul, "interrupted!")
return
balloon_alert(soul, "you break out")
if(ismob(loc))
var/mob/holder = loc
holder.temporarilyRemoveItemFromInventory(src)
forceMove(drop_location())
/obj/item/soulscythe/proc/on_integrity_change(datum/source, old_value, new_value)
SIGNAL_HANDLER
soul.set_health(new_value)
/obj/item/soulscythe/proc/on_attack(mob/living/source, atom/attacked_atom, modifiers)
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
return
if(get_dist(source, attacked_atom) > 1)
INVOKE_ASYNC(src, PROC_REF(shoot_target), attacked_atom)
else
INVOKE_ASYNC(src, PROC_REF(slash_target), attacked_atom)
/obj/item/soulscythe/proc/on_secondary_attack(mob/living/source, atom/attacked_atom, modifiers)
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
return
INVOKE_ASYNC(src, PROC_REF(charge_target), attacked_atom)
/obj/item/soulscythe/proc/shoot_target(atom/attacked_atom)
if(!use_blood(15))
return
COOLDOWN_START(src, attack_cooldown, 3 SECONDS)
var/obj/projectile/projectile = new /obj/projectile/soulscythe(get_turf(src))
projectile.aim_projectile(attacked_atom, src)
projectile.firer = src
projectile.fire(null, attacked_atom)
visible_message(span_danger("[src] fires at [attacked_atom]!"), span_notice("You fire at [attacked_atom]!"))
playsound(src, 'sound/effects/magic/fireball.ogg', 50, TRUE)
/obj/item/soulscythe/proc/slash_target(atom/attacked_atom)
if(isliving(attacked_atom) && use_blood(10))
var/mob/living/attacked_mob = attacked_atom
if(attacked_mob.stat != DEAD)
give_blood(15)
attacked_mob.apply_damage(damage = force * (ismining(attacked_mob) ? 2 : 1), sharpness = SHARP_EDGED, exposed_wound_bonus = 5)
to_chat(attacked_mob, span_userdanger("You're slashed by [src]!"))
else if((ismachinery(attacked_atom) || isstructure(attacked_atom)) && use_blood(5))
var/obj/attacked_obj = attacked_atom
attacked_obj.take_damage(force, BRUTE, MELEE, FALSE)
else
return
COOLDOWN_START(src, attack_cooldown, 1 SECONDS)
animate(src)
SpinAnimation(5)
addtimer(CALLBACK(src, PROC_REF(reset_spin)), 1 SECONDS)
visible_message(span_danger("[src] slashes [attacked_atom]!"), span_notice("You slash [attacked_atom]!"))
playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
do_attack_animation(attacked_atom, ATTACK_EFFECT_SLASH)
/obj/item/soulscythe/proc/charge_target(atom/attacked_atom)
if(charging || !use_blood(30))
return
COOLDOWN_START(src, attack_cooldown, 5 SECONDS)
animate(src)
charging = TRUE
visible_message(span_danger("[src] starts charging..."))
balloon_alert(soul, "you start charging...")
if(!do_after(soul, 2 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
balloon_alert(soul, "interrupted!")
return
visible_message(span_danger("[src] charges at [attacked_atom]!"), span_notice("You charge at [attacked_atom]!"))
new /obj/effect/temp_visual/mook_dust(get_turf(src))
playsound(src, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE)
SpinAnimation(1)
throwforce *= 2
throw_at(attacked_atom, 10, 3, soul, FALSE)
/obj/item/soulscythe/proc/reset_spin()
animate(src)
SpinAnimation(15)
/obj/item/soulscythe/Destroy(force)
soul.ghostize()
QDEL_NULL(soul)
return ..()
/// Soulscythe mob, just a way for players to control the scythe.
/mob/living/basic/soulscythe
name = "mysterious spirit"
maxHealth = 200
health = 200
gender = NEUTER
mob_biotypes = MOB_SPIRIT
faction = list()
blood_volume = MAX_BLOOD_LEVEL
hud_type = /datum/hud/soulscythe
/mob/living/basic/soulscythe/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_LIVING_LIFE, PROC_REF(on_life))
/mob/living/basic/soulscythe/proc/on_life(datum/source, seconds_per_tick, times_fired) // done like this because there's no need to go through all of life since the item does the work anyways
if(stat == CONSCIOUS)
blood_volume = min(MAX_BLOOD_LEVEL, blood_volume + round(1 * seconds_per_tick))
return COMPONENT_LIVING_CANCEL_LIFE_PROCESSING
/// Special projectile for the soulscythe.
/obj/projectile/soulscythe
name = "soulslash"
icon_state = "soulslash"
armor_flag = MELEE //jokair
damage = 15
light_range = 1
light_power = 1
light_color = LIGHT_COLOR_BLOOD_MAGIC
/obj/projectile/soulscythe/on_hit(atom/target, blocked = 0, pierce_hit)
if (isliving(target))
var/mob/living/as_living = target
if (ismining(as_living))
damage *= 2
return ..()
#undef MAX_BLOOD_LEVEL
@@ -129,288 +129,3 @@
var/datum/client_colour/color_effect = user.add_client_colour(/datum/client_colour/bloodlust, HELMET_TRAIT)
QDEL_IN(color_effect, 1 SECONDS)
// Soulscythe
#define MAX_BLOOD_LEVEL 100
/obj/item/soulscythe
name = "soulscythe"
desc = "An old relic of hell created by devils to establish themselves as the leadership of hell over the demons. It grows stronger while it possesses a powerful soul."
icon = 'icons/obj/mining_zones/artefacts.dmi'
icon_state = "soulscythe"
inhand_icon_state = "soulscythe"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
attack_verb_continuous = list("chops", "slices", "cuts", "reaps")
attack_verb_simple = list("chop", "slice", "cut", "reap")
hitsound = 'sound/items/weapons/bladeslice.ogg'
inhand_x_dimension = 64
inhand_y_dimension = 64
force = 20
throwforce = 17
armour_penetration = 50
sharpness = SHARP_EDGED
exposed_wound_bonus = 10
layer = MOB_LAYER
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/// Soulscythe mob in the scythe
var/mob/living/simple_animal/soulscythe/soul
/// Are we grabbing a spirit?
var/using = FALSE
/// Currently charging?
var/charging = FALSE
/// Cooldown between moves
COOLDOWN_DECLARE(move_cooldown)
/// Cooldown between attacks
COOLDOWN_DECLARE(attack_cooldown)
/obj/item/soulscythe/Initialize(mapload)
. = ..()
soul = new(src)
RegisterSignal(soul, COMSIG_LIVING_RESIST, PROC_REF(on_resist))
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED, PROC_REF(on_attack))
RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED_SECONDARY, PROC_REF(on_secondary_attack))
RegisterSignal(src, COMSIG_ATOM_INTEGRITY_CHANGED, PROC_REF(on_integrity_change))
AddElement(/datum/element/bane, mob_biotypes = MOB_PLANT, damage_multiplier = 0.5, requires_combat_mode = FALSE)
/obj/item/soulscythe/examine(mob/user)
. = ..()
. += soul.ckey ? span_nicegreen("There is a soul inhabiting it.") : span_danger("It's dormant.")
/obj/item/soulscythe/attack(mob/living/attacked, mob/living/user, list/modifiers, list/attack_modifiers)
. = ..()
if(attacked.stat != DEAD)
give_blood(10)
/obj/item/soulscythe/attack_hand(mob/user, list/modifiers)
if(soul.ckey && !soul.faction_check_atom(user))
to_chat(user, span_warning("You can't pick up [src]!"))
return
return ..()
/obj/item/soulscythe/pickup(mob/user)
. = ..()
if(soul.ckey)
animate(src) //stop spinnage
/obj/item/soulscythe/dropped(mob/user, silent)
. = ..()
if(soul.ckey)
reset_spin() //resume spinnage
/obj/item/soulscythe/attack_self(mob/user, modifiers)
if(using || soul.ckey || soul.stat)
return
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
balloon_alert(user, "you can't awaken the scythe!")
return
using = TRUE
balloon_alert(user, "you hold the scythe up...")
ADD_TRAIT(src, TRAIT_NODROP, type)
var/mob/chosen_one = SSpolling.poll_ghosts_for_target(
check_jobban = ROLE_PAI,
poll_time = 20 SECONDS,
checked_target = src,
ignore_category = POLL_IGNORE_POSSESSED_BLADE,
alert_pic = src,
role_name_text = "soulscythe soul",
chat_text_border_icon = src,
)
on_poll_concluded(user, chosen_one)
/// Ghost poll has concluded and a candidate has been chosen.
/obj/item/soulscythe/proc/on_poll_concluded(mob/living/master, mob/dead/observer/ghost)
if(isnull(ghost))
balloon_alert(master, "the scythe is dormant!")
REMOVE_TRAIT(src, TRAIT_NODROP, type)
using = FALSE
return
soul.PossessByPlayer(ghost.ckey)
soul.copy_languages(master, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the master.
soul.faction = list("[REF(master)]")
balloon_alert(master, "the scythe glows")
add_overlay("soulscythe_gem")
density = TRUE
if(!ismob(loc))
reset_spin()
REMOVE_TRAIT(src, TRAIT_NODROP, type)
using = FALSE
/obj/item/soulscythe/relaymove(mob/living/user, direction)
if(!COOLDOWN_FINISHED(src, move_cooldown) || charging)
return
if(!isturf(loc))
balloon_alert(user, "resist out!")
COOLDOWN_START(src, move_cooldown, 1 SECONDS)
return
if(!use_blood(1, FALSE))
return
if(pixel_x != base_pixel_x || pixel_y != base_pixel_y)
animate(src, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y, flags = ANIMATION_PARALLEL)
try_step_multiz(direction)
COOLDOWN_START(src, move_cooldown, (direction in GLOB.cardinals) ? 0.1 SECONDS : 0.2 SECONDS)
/obj/item/soulscythe/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!charging)
return
charging = FALSE
throwforce *= 0.5
reset_spin()
if(ismineralturf(hit_atom))
var/turf/closed/mineral/hit_rock = hit_atom
hit_rock.gets_drilled()
if(isliving(hit_atom))
var/mob/living/hit_mob = hit_atom
if(hit_mob.stat != DEAD)
give_blood(15)
/obj/item/soulscythe/AllowClick()
return TRUE
/obj/item/soulscythe/proc/use_blood(amount = 0, message = TRUE)
if(amount > soul.blood_volume)
if(message)
to_chat(soul, span_warning("Not enough blood!"))
return FALSE
soul.blood_volume -= amount
return TRUE
/obj/item/soulscythe/proc/give_blood(amount)
soul.blood_volume = min(MAX_BLOOD_LEVEL, soul.blood_volume + amount)
/obj/item/soulscythe/proc/on_resist(mob/living/user)
SIGNAL_HANDLER
if(isturf(loc))
return
INVOKE_ASYNC(src, PROC_REF(break_out))
/obj/item/soulscythe/proc/break_out()
if(!use_blood(10))
return
balloon_alert(soul, "you resist...")
if(!do_after(soul, 5 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
balloon_alert(soul, "interrupted!")
return
balloon_alert(soul, "you break out")
if(ismob(loc))
var/mob/holder = loc
holder.temporarilyRemoveItemFromInventory(src)
forceMove(drop_location())
/obj/item/soulscythe/proc/on_integrity_change(datum/source, old_value, new_value)
SIGNAL_HANDLER
soul.set_health(new_value)
/obj/item/soulscythe/proc/on_attack(mob/living/source, atom/attacked_atom, modifiers)
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
return
if(get_dist(source, attacked_atom) > 1)
INVOKE_ASYNC(src, PROC_REF(shoot_target), attacked_atom)
else
INVOKE_ASYNC(src, PROC_REF(slash_target), attacked_atom)
/obj/item/soulscythe/proc/on_secondary_attack(mob/living/source, atom/attacked_atom, modifiers)
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc))
return
INVOKE_ASYNC(src, PROC_REF(charge_target), attacked_atom)
/obj/item/soulscythe/proc/shoot_target(atom/attacked_atom)
if(!use_blood(15))
return
COOLDOWN_START(src, attack_cooldown, 3 SECONDS)
var/obj/projectile/projectile = new /obj/projectile/soulscythe(get_turf(src))
projectile.aim_projectile(attacked_atom, src)
projectile.firer = src
projectile.fire(null, attacked_atom)
visible_message(span_danger("[src] fires at [attacked_atom]!"), span_notice("You fire at [attacked_atom]!"))
playsound(src, 'sound/effects/magic/fireball.ogg', 50, TRUE)
/obj/item/soulscythe/proc/slash_target(atom/attacked_atom)
if(isliving(attacked_atom) && use_blood(10))
var/mob/living/attacked_mob = attacked_atom
if(attacked_mob.stat != DEAD)
give_blood(15)
attacked_mob.apply_damage(damage = force * (ismining(attacked_mob) ? 2 : 1), sharpness = SHARP_EDGED, exposed_wound_bonus = 5)
to_chat(attacked_mob, span_userdanger("You're slashed by [src]!"))
else if((ismachinery(attacked_atom) || isstructure(attacked_atom)) && use_blood(5))
var/obj/attacked_obj = attacked_atom
attacked_obj.take_damage(force, BRUTE, MELEE, FALSE)
else
return
COOLDOWN_START(src, attack_cooldown, 1 SECONDS)
animate(src)
SpinAnimation(5)
addtimer(CALLBACK(src, PROC_REF(reset_spin)), 1 SECONDS)
visible_message(span_danger("[src] slashes [attacked_atom]!"), span_notice("You slash [attacked_atom]!"))
playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
do_attack_animation(attacked_atom, ATTACK_EFFECT_SLASH)
/obj/item/soulscythe/proc/charge_target(atom/attacked_atom)
if(charging || !use_blood(30))
return
COOLDOWN_START(src, attack_cooldown, 5 SECONDS)
animate(src)
charging = TRUE
visible_message(span_danger("[src] starts charging..."))
balloon_alert(soul, "you start charging...")
if(!do_after(soul, 2 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE))
balloon_alert(soul, "interrupted!")
return
visible_message(span_danger("[src] charges at [attacked_atom]!"), span_notice("You charge at [attacked_atom]!"))
new /obj/effect/temp_visual/mook_dust(get_turf(src))
playsound(src, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE)
SpinAnimation(1)
throwforce *= 2
throw_at(attacked_atom, 10, 3, soul, FALSE)
/obj/item/soulscythe/proc/reset_spin()
animate(src)
SpinAnimation(15)
/obj/item/soulscythe/Destroy(force)
soul.ghostize()
QDEL_NULL(soul)
. = ..()
/mob/living/simple_animal/soulscythe
name = "mysterious spirit"
maxHealth = 200
health = 200
gender = NEUTER
mob_biotypes = MOB_SPIRIT
faction = list()
weather_immunities = list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE)
blood_volume = MAX_BLOOD_LEVEL
hud_type = /datum/hud/soulscythe
/mob/living/simple_animal/soulscythe/Life(seconds_per_tick, times_fired)
. = ..()
if(!stat)
blood_volume = min(MAX_BLOOD_LEVEL, blood_volume + round(1 * seconds_per_tick))
/obj/projectile/soulscythe
name = "soulslash"
icon_state = "soulslash"
armor_flag = MELEE //jokair
damage = 15
light_range = 1
light_power = 1
light_color = LIGHT_COLOR_BLOOD_MAGIC
/obj/projectile/soulscythe/on_hit(atom/target, blocked = 0, pierce_hit)
if (isliving(target))
var/mob/living/as_living = target
if (ismining(as_living))
damage *= 2
return ..()
#undef MAX_BLOOD_LEVEL
@@ -58,7 +58,6 @@
/mob/living/simple_animal/hostile/ooze,
/mob/living/simple_animal/hostile/ooze/gelatinous,
/mob/living/simple_animal/hostile/ooze/grapes,
/mob/living/simple_animal/soulscythe,
// MODULAR SKYRAT ENTRIES
// PLEASE REFACTOR THESE AS YOU CAN
+1
View File
@@ -2660,6 +2660,7 @@
#include "code\game\objects\items\shrapnel.dm"
#include "code\game\objects\items\signs.dm"
#include "code\game\objects\items\skub.dm"
#include "code\game\objects\items\soulscythe.dm"
#include "code\game\objects\items\spear.dm"
#include "code\game\objects\items\stickers.dm"
#include "code\game\objects\items\surgery_tray.dm"