#define PHASEREGEN_FILTER "healing_glow" #define RUIN_QUEUE "the_thing_depleter" /mob/living/basic/boss/thing name = "\improper Thing" icon = 'icons/mob/simple/icemoon/thething.dmi' icon_state = "p1" icon_dead = "dead" gender = NEUTER maxHealth = 1800 //nicely divisible by three health = 1800 armour_penetration = 40 melee_damage_lower = 30 melee_damage_upper = 30 mob_biotypes = MOB_ORGANIC|MOB_SPECIAL|MOB_MINING sharpness = SHARP_EDGED melee_attack_cooldown = CLICK_CD_SLOW attack_verb_continuous = "eviscerates" attack_verb_simple = "eviscerate" attack_sound = 'sound/items/weapons/bladeslice.ogg' attack_vis_effect = ATTACK_EFFECT_SLASH speed = 3.5 //dont make this any faster PLEASE gps_name = "L-4 Biohazard Beacon" ai_controller = /datum/ai_controller/basic_controller/thing_boss crusher_loot = list(/obj/item/crusher_trophy/flesh_glob) mouse_opacity = MOUSE_OPACITY_OPAQUE achievements = list( /datum/award/achievement/boss/boss_killer, /datum/award/achievement/boss/thething_kill, /datum/award/score/boss_score, /datum/award/score/thething_score, ) crusher_achievement_type = /datum/award/achievement/boss/thething_crusher victor_memory_type = /datum/memory/megafauna_slayer /// Current phase of the boss fight var/phase = 1 /// Time the Thing will be invulnerable between phases var/phase_invul_time = 10 SECONDS /// timer of phase invulnerability between phases var/phase_invulnerability_timer // ruin logic /// if true, this boss may only be killed proper in its ruin by the associated machines as part of the bossfight. var/maploaded = TRUE /// where we spawned. not set if not maploaded var/turf/spawn_loc /// return timer var/return_timer /mob/living/basic/boss/thing/Initialize(mapload) . = ..() AddElement(/datum/element/death_drops, /obj/item/keycard/thing_boss, FALSE) var/static/list/innate_actions = list( /datum/action/cooldown/mob_cooldown/the_thing/decimate = BB_THETHING_DECIMATE, /datum/action/cooldown/mob_cooldown/charge/the_thing = BB_THETHING_CHARGE, /datum/action/cooldown/mob_cooldown/the_thing/big_tendrils = BB_THETHING_BIGTENDRILS, /datum/action/cooldown/mob_cooldown/the_thing/shriek = BB_THETHING_SHRIEK, /datum/action/cooldown/mob_cooldown/the_thing/cardinal_tendrils = BB_THETHING_CARDTENDRILS, /datum/action/cooldown/mob_cooldown/the_thing/acid_spit = BB_THETHING_ACIDSPIT, ) grant_actions_by_list(innate_actions) AddElement(/datum/element/relay_attackers) // used to immediately aggro if shot from outside aggro range RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(immediate_aggro)) maploaded = mapload if(!maploaded) return spawn_loc = loc RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(target_gained)) RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(target_lost)) SSqueuelinks.add_to_queue(src, RUIN_QUEUE, 0) return INITIALIZE_HINT_LATELOAD /mob/living/basic/boss/thing/LateInitialize() SSqueuelinks.pop_link(RUIN_QUEUE) /mob/living/basic/boss/thing/update_icon_state() . = ..() if(stat) icon_state = "dead" return icon_state = "p[phase]" icon_living = icon_state /mob/living/basic/boss/thing/adjust_health(amount, updating_health = TRUE, forced = FALSE) if(phase_invulnerability_timer || phase == 3 || stat || amount <= 0) return ..() var/potential_excess = bruteloss + amount - (maxHealth/3)*phase if(potential_excess > 0) amount -= potential_excess . = ..() if(bruteloss >= (maxHealth/3)*phase) phase_health_depleted() /mob/living/basic/boss/thing/proc/phase_health_depleted() if(phase_invulnerability_timer) return //wtf? if(!maploaded || client) phase_successfully_depleted() return if(!client && istype(get_area(src), /area/station)) //retreat to station if AI controlled return_to_spawnloc() return add_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED), MEGAFAUNA_TRAIT) balloon_alert_to_viewers("invulnerable! overload the machines!") visible_message(span_danger("[src] drops to the ground staggered, unable to keep up with injuries!")) phase_invulnerability_timer = addtimer(CALLBACK(src, PROC_REF(phase_too_slow)), phase_invul_time, TIMER_STOPPABLE|TIMER_UNIQUE) add_filter(PHASEREGEN_FILTER, 2, list("type" = "outline", "color" = COLOR_PALE_GREEN, "alpha" = 0, "size" = 1)) var/filter = get_filter(PHASEREGEN_FILTER) animate(filter, alpha = 200, time = 0.5 SECONDS, loop = -1) animate(alpha = 0, time = 0.5 SECONDS) SEND_SIGNAL(src, COMSIG_MEGAFAUNA_THETHING_PHASEUPDATED) /// Delete our return timer when we gain a target if we started premapped /mob/living/basic/boss/thing/proc/target_gained(datum/source) SIGNAL_HANDLER if(!return_timer) return deltimer(return_timer) return_timer = null /// If we started premapped, and we lost our target, start a 3 minute timer to return to spawn turf unless we gain aggro again /mob/living/basic/boss/thing/proc/target_lost(datum/source) SIGNAL_HANDLER if(stat || client || loc == spawn_loc || return_timer) return return_timer = addtimer(CALLBACK(src, PROC_REF(return_to_spawn_check)), 3 MINUTES, TIMER_STOPPABLE | TIMER_DELETE_ME) /// Return us to our spawn loc (ruin boss only) if we are alive and have an ai controller and our loc isnt the spawn loc /mob/living/basic/boss/thing/proc/return_to_spawn_check() if(isnull(ai_controller) || QDELETED(src) || loc == spawn_loc || stat || client) return return_to_spawnloc() /mob/living/basic/boss/thing/proc/return_to_spawnloc() if(isnull(spawn_loc)) CRASH("The Thing tried to return to spawn_loc but it was null! This shouldnt happen") for(var/turf/open/target in RANGE_TURFS(1, loc)) new /obj/effect/temp_visual/mook_dust(target) playsound(loc, 'sound/effects/meteorimpact.ogg', 40, TRUE) visible_message(span_danger("[src] retreats through the ground back to where it came from!")) forceMove(spawn_loc) /// The Thing is successfully hit by incendiary fire while downed by damage (alternatively takes too much damage if not ruin spawned) /mob/living/basic/boss/thing/proc/phase_successfully_depleted() playsound(src, 'sound/effects/pop_expl.ogg', 65) ai_controller?.set_blackboard_key(BB_THETHING_NOAOE, FALSE) remove_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED), MEGAFAUNA_TRAIT) deltimer(phase_invulnerability_timer) phase_invulnerability_timer = null if(phase < 3) //after phase 3 we literally just die phase++ emote("scream") update_appearance() var/filter = get_filter(PHASEREGEN_FILTER) if(!isnull(filter)) animate(filter) remove_filter(PHASEREGEN_FILTER) SEND_SIGNAL(src, COMSIG_MEGAFAUNA_THETHING_PHASEUPDATED) new /obj/effect/gibspawner/human/bodypartless(loc) /mob/living/basic/boss/thing/proc/phase_too_slow() phase_invulnerability_timer = null remove_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED), MEGAFAUNA_TRAIT) balloon_alert_to_viewers("recovers!") visible_message(span_danger("[src] recovers from the damage! Too slow!")) adjust_health(-(maxHealth/3) * 0.5) //half of a phase (which is a third of maxhealth) var/filter = get_filter(PHASEREGEN_FILTER) if(!isnull(filter)) animate(filter) remove_filter(PHASEREGEN_FILTER) emote("roar") SEND_SIGNAL(src, COMSIG_MEGAFAUNA_THETHING_PHASEUPDATED) /// Immediately set out blackboard target key (if empty) to whoever attacks us; this is primarily because it has a lowered aggro range and a high sight range /mob/living/basic/boss/thing/proc/immediate_aggro(datum/source, mob/attacker, flags) SIGNAL_HANDLER if(isnull(ai_controller) || stat || !istype(attacker) || ai_controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) return ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, attacker) /mob/living/basic/boss/thing/vv_edit_var(vname, vval) . = ..() if(vname == NAMEOF(src, phase)) ai_controller?.set_blackboard_key(BB_THETHING_NOAOE, phase > 1 ? FALSE : TRUE) update_appearance() /mob/living/basic/boss/thing/Destroy() spawn_loc = null return ..() /mob/living/basic/boss/thing/with_ruin_loot/Initialize(mapload) . = ..() AddElement(/datum/element/death_drops, /obj/item/organ/brain/cybernetic/ai, FALSE) // special stuff for our ruin to make a cooler bossfight /obj/structure/thing_boss_phase_depleter name = "Molecular Accelerator" desc = "Weird-ass lab equipment." icon_state = "thingdepleter" anchored = TRUE density = TRUE move_resist = INFINITY resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF /// is this not broken yet var/functional = TRUE /// boss weakref var/datum/weakref/boss_weakref /obj/structure/thing_boss_phase_depleter/Initialize(mapload) . = ..() go_in_floor() SSqueuelinks.add_to_queue(src, RUIN_QUEUE, 0) /obj/structure/thing_boss_phase_depleter/MatchedLinks(id, list/partners) if(id != RUIN_QUEUE) return var/mob/living/basic/boss/thing/thing = locate() in partners if(isnull(thing)) qdel(src) return boss_weakref = WEAKREF(thing) RegisterSignal(thing, COMSIG_MEGAFAUNA_THETHING_PHASEUPDATED, PROC_REF(thing_phaseupdated)) /obj/structure/thing_boss_phase_depleter/proc/thing_phaseupdated(mob/living/basic/boss/thing/source) SIGNAL_HANDLER if(!functional) return if(source.phase_invulnerability_timer) go_out_floor() else go_in_floor() /obj/structure/thing_boss_phase_depleter/examine(mob/user) . = ..() . += density ? span_boldnotice("It may be possible to overload this and destroy that things defenses...") : span_bolddanger("The machine is currently being restrained by tendrils.") /obj/structure/thing_boss_phase_depleter/proc/set_circuit_floor(state) for(var/turf/open/floor/circuit/circuit in RANGE_TURFS(1, loc)) circuit.on = state circuit.update_appearance() /obj/structure/thing_boss_phase_depleter/proc/go_in_floor() if(!density) return density = FALSE obj_flags &= ~CAN_BE_HIT set_circuit_floor(FALSE) name = "hatch" icon_state = "thingdepleter_infloor" /obj/structure/thing_boss_phase_depleter/proc/go_out_floor() if(density) return density = TRUE obj_flags |= CAN_BE_HIT set_circuit_floor(TRUE) name = initial(name) icon_state = "thingdepleter" new /obj/effect/temp_visual/mook_dust(loc) /obj/structure/thing_boss_phase_depleter/interact(mob/user, list/modifiers) var/mob/living/basic/boss/thing/the_thing = boss_weakref?.resolve() if(!the_thing || !functional || !density) return if(!user.can_perform_action(src) || !user.can_interact_with(src)) return balloon_alert_to_viewers("overloading...") icon_state = "thingdepleter_overriding" if(!do_after(user, 1 SECONDS, target = src)) if(density) icon_state = "thingdepleter" return new /obj/effect/temp_visual/circle_wave/orange(loc) playsound(src, 'sound/effects/explosion/explosion3.ogg', 100) animate(src, transform = matrix()*1.5, time = 0.2 SECONDS) animate(transform = matrix(), time = 0) the_thing.phase_successfully_depleted() functional = FALSE go_in_floor() icon_state = "thingdepleter_overriding" addtimer(VARSET_CALLBACK(src, icon_state, "thingdepleter_broken"), 0.2 SECONDS) /obj/effect/temp_visual/circle_wave/orange color = COLOR_ORANGE /obj/structure/aggro_gate name = "biohazard gate" desc = "A wall of solid light, only activating when a human is endangered by a biohazard, unfortunately that does little for safety as it locks you in with said biohazard. Virtually indestructible, you must evade (or kill) the threat." icon = 'icons/effects/effects.dmi' icon_state = "wave2" resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF move_resist = MOVE_FORCE_OVERPOWERING opacity = FALSE density = FALSE invisibility = INVISIBILITY_MAXIMUM anchored = TRUE /// queue id var/queue_id = RUIN_QUEUE /// blackboard key for target var/target_bb_key = BB_BASIC_MOB_CURRENT_TARGET /obj/structure/aggro_gate/Initialize(mapload) . = ..() SSqueuelinks.add_to_queue(src, queue_id) /obj/structure/aggro_gate/MatchedLinks(id, list/partners) if(id != queue_id) return for(var/mob/living/partner in partners) RegisterSignal(partner, COMSIG_AI_BLACKBOARD_KEY_SET(target_bb_key), PROC_REF(bar_the_gates)) RegisterSignals(partner, list(COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_bb_key), COMSIG_LIVING_DEATH, COMSIG_MOB_LOGIN), PROC_REF(open_gates)) /obj/structure/aggro_gate/proc/bar_the_gates(mob/living/source) SIGNAL_HANDLER var/atom/target = source.ai_controller?.blackboard[target_bb_key] if (QDELETED(target)) return invisibility = INVISIBILITY_NONE density = TRUE playsound(src, SFX_SPARKS, 100, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) do_sparks(3, cardinal_only = FALSE, source = src) /obj/structure/aggro_gate/proc/open_gates(mob/living/source) playsound(src, SFX_SPARKS, 100, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) do_sparks(3, cardinal_only = FALSE, source = src) density = FALSE invisibility = INVISIBILITY_MAXIMUM #undef PHASEREGEN_FILTER #undef RUIN_QUEUE