diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm index 56cc0fbd32a..5f3cc8f298d 100644 --- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm +++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm @@ -39,19 +39,25 @@ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended") flags_2 = RANDOM_BLOCKER_2 var/summon_cooldown = 0 - var/list/mob/dead/observer/spirits + /// List of wisps we have active, for cleanup purposes in case a ghost gets randomly deleted. + var/list/obj/effect/wisp/ghost/orbs + /// List of ghosts currently orbiting us. + var/list/mob/dead/observer/ghosts /obj/item/melee/ghost_sword/New() ..() - spirits = list() + ghosts = list() + orbs = list() register_signals(src) RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) GLOB.poi_list |= src /obj/item/melee/ghost_sword/Destroy() - for(var/mob/dead/observer/G in spirits) + for(var/mob/dead/observer/G in ghosts) remove_ghost(G) - spirits.Cut() + // if there are any orbs left (possibly detached from ghosts) ensure they don't stick around + for(var/spirit as anything in orbs) + qdel(spirit) remove_signals(src) UnregisterSignal(src, COMSIG_MOVABLE_MOVED) GLOB.poi_list -= src @@ -59,8 +65,8 @@ /obj/item/melee/ghost_sword/examine() . = ..() - if(length(spirits)) - . += "It appears to pulse with the power of [length(spirits)] vengeful spirits!" + if(length(orbs)) + . += "It appears to pulse with the power of [length(orbs)] vengeful spirit\s!" else . += "It glows weakly." @@ -83,7 +89,7 @@ /obj/item/melee/ghost_sword/proc/add_ghost(atom/movable/orbited, atom/orbiter) SIGNAL_HANDLER // COMSIG_ATOM_ORBIT_BEGIN var/mob/dead/observer/ghost = orbiter - if(!istype(ghost) || !isobserver(orbiter) || (ghost in spirits)) + if(!istype(ghost) || !isobserver(orbiter) || (ghost in ghosts)) return if(!ismob(loc)) @@ -91,23 +97,34 @@ // they'll get added to spirits (and turned visible) when the sword enters a mob's hand then return - register_signals(ghost) // Pull in any ghosts that may be orbiting other ghosts TODO THIS MIGHT BE THE FUCKIN PROBLEM + register_signals(ghost) // Pull in any ghosts that may be orbiting other ghosts - spirits |= ghost - ghost.invisibility = 0 + + var/obj/effect/wisp/ghost/orb = new(src) + orb.color = ghost.get_runechat_color() + orb.alpha = 128 + orb.orbit(src, clockwise = FALSE) + ghosts[ghost] = orb + orbs.Add(orb) + + // if a ghost gets deleted, the orb cleans itself up + // which then passes the torch to us to clean ourselves up + RegisterSignal(orb, COMSIG_PARENT_QDELETING, PROC_REF(on_orb_qdel)) /obj/item/melee/ghost_sword/proc/remove_ghost(atom/movable/orbited, atom/orbiter) SIGNAL_HANDLER // COMSIG_ATOM_ORBIT_STOP var/mob/dead/observer/ghost = orbiter - if(!istype(ghost) || !isobserver(ghost) || !(ghost in spirits)) + if(!istype(ghost) || !(ghost in ghosts)) return remove_signals(ghost) - spirits -= ghost - ghost.invisibility = initial(ghost.invisibility) + var/obj/effect/wisp/ghost/attached_orb = ghosts[ghost] + attached_orb.stop_orbit() + qdel(attached_orb) + ghosts -= ghost /obj/item/melee/ghost_sword/proc/remove_signals(atom/A) UnregisterSignal(A, COMSIG_ATOM_ORBIT_STOP) @@ -126,7 +143,7 @@ if(ismob(old_loc)) remove_signals(old_loc) - for(var/mob/dead/observer/orbiter in spirits) + for(var/mob/dead/observer/orbiter in ghosts) remove_ghost(src, orbiter) if(ismob(loc)) @@ -135,20 +152,48 @@ for(var/mob/dead/observer/orbiter in get_orbiters_up_hierarchy(recursive = TRUE)) add_ghost(src, orbiter) +// clean up wisps +/obj/item/melee/ghost_sword/proc/on_orb_qdel(obj/effect/wisp/ghost/orb) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + orbs -= orb + for(var/ghost in ghosts) + if(ghosts[ghost] == orb) + ghosts -= ghost + break + + /obj/item/melee/ghost_sword/attack(mob/living/target, mob/living/carbon/human/user) force = 0 - var/ghost_counter = length(spirits) + var/ghost_counter = length(orbs) force = clamp((ghost_counter * 4), 0, 75) - user.visible_message("[user] strikes with the force of [ghost_counter] vengeful spirits!") + user.visible_message("[user] strikes with the force of [ghost_counter] vengeful spirit\s!") ..() /obj/item/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - var/ghost_counter = length(spirits) + var/ghost_counter = length(orbs) final_block_chance += clamp((ghost_counter * 5), 0, 75) - owner.visible_message("[owner] is protected by a ring of [ghost_counter] ghosts!") + owner.visible_message("[owner] is protected by a ring of [ghost_counter] ghost\s!") return ..() + +/obj/effect/wisp/ghost + name = "mischievous wisp" + desc = "A wisp that seems to want to get up to shenanigans. It often seems disappointed, for some reason." + light_range = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/wisp/ghost/Initialize(mapload, mob/dead/observer/ghost) + . = ..() + RegisterSignal(ghost, COMSIG_PARENT_QDELETING, PROC_REF(on_ghost_qdel)) + +/obj/effect/wisp/ghost/proc/on_ghost_qdel(mob/dead/observer/ghost) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + stop_orbit() + // we only live as long as our attached ghost + qdel(src) + + // Blood /obj/item/dragons_blood diff --git a/code/modules/mob/dead/observer/observer_base.dm b/code/modules/mob/dead/observer/observer_base.dm index e832baa543e..f766af39a43 100644 --- a/code/modules/mob/dead/observer/observer_base.dm +++ b/code/modules/mob/dead/observer/observer_base.dm @@ -35,6 +35,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) ///toggle for ghost gas analyzer var/gas_analyzer = FALSE var/datum/orbit_menu/orbit_menu + /// The "color" their runechat would have had + var/alive_runechat_color = "#FFFFFF" /mob/dead/observer/New(mob/body=null, flags=1) set_invisibility(GLOB.observer_default_invisibility) @@ -59,6 +61,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) if(ismob(body)) T = get_turf(body) //Where is the body located? attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost + alive_runechat_color = body.get_runechat_color() var/mutable_appearance/MA = copy_appearance(body) if(body.mind && body.mind.name) @@ -846,3 +849,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return allow_roundstart_observers return FALSE + +/mob/dead/observer/get_runechat_color() + return alive_runechat_color