From 1fc53f679b31948cc20c4e71805da08fe3fab8ae Mon Sep 17 00:00:00 2001 From: san7890 Date: Fri, 27 Jun 2025 21:29:45 -0600 Subject: [PATCH] Soulscythe Refactor to Basic Mob (#91667) 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 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 :cl: refactor: Soulscythes have undergone a mild refactor, but everything should work as it should. Report any issues. /:cl: (cherry picked from commit f74c9d7aacb4c6a4a3193aff9fec5c5de63fc62b) --- code/_globalvars/lists/mobs.dm | 28 +- code/_onclick/hud/soulscythe.dm | 2 +- code/game/objects/items/soulscythe.dm | 289 ++++++++++++++++++ .../mining_loot/megafauna/bubblegum.dm | 285 ----------------- .../unit_tests/simple_animal_freeze.dm | 1 - tgstation.dme | 1 + 6 files changed, 305 insertions(+), 301 deletions(-) create mode 100644 code/game/objects/items/soulscythe.dm diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 4948fac4ba7..498b497f1d5 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -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, )) diff --git a/code/_onclick/hud/soulscythe.dm b/code/_onclick/hud/soulscythe.dm index 7a98b416731..8c7a3359c8a 100644 --- a/code/_onclick/hud/soulscythe.dm +++ b/code/_onclick/hud/soulscythe.dm @@ -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 diff --git a/code/game/objects/items/soulscythe.dm b/code/game/objects/items/soulscythe.dm new file mode 100644 index 00000000000..d58ae34de50 --- /dev/null +++ b/code/game/objects/items/soulscythe.dm @@ -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 diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm b/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm index 2068bfc4d27..32684a83ac9 100644 --- a/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm +++ b/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm @@ -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 diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 96ceb2876fa..f746a29b172 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -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 diff --git a/tgstation.dme b/tgstation.dme index 9b00620dbcb..3a8c68715d3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"