diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 8947a87be90..a650d6fd978 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -43,9 +43,9 @@ #define isslime(A) (istype((A), /mob/living/simple_animal/slime)) -#define ispulsedemon(A) (istype(A, /mob/living/simple_animal/demon/pulse_demon)) +#define ispulsedemon(A) (istype(A, /mob/living/basic/demon/pulse_demon)) -#define isshadowdemon(A) (istype(A, /mob/living/simple_animal/demon/shadow)) +#define isshadowdemon(A) (istype(A, /mob/living/basic/demon/shadow)) // Basic mobs diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index 39e0016bb4c..dfb55686bcc 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -256,8 +256,8 @@ #define ishostile(A) (istype((A), /mob/living/simple_animal/hostile)) #define isretaliate(A) (istype((A), /mob/living/simple_animal/hostile/retaliate)) #define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider) || istype((A), /mob/living/basic/spiderling/terror_spiderling)) -#define isslaughterdemon(A) (istype((A), /mob/living/simple_animal/demon/slaughter)) -#define isdemon(A) (istype((A), /mob/living/simple_animal/demon)) +#define isslaughterdemon(A) (istype((A), /mob/living/basic/demon/slaughter)) +#define isdemon(A) (istype((A), /mob/living/basic/demon)) #define iscat(A) (istype((A), /mob/living/simple_animal/pet/cat)) #define isgorilla(A) (istype((A), /mob/living/basic/gorilla)) #define ismorph(A) (istype((A), /mob/living/simple_animal/hostile/morph)) diff --git a/code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm b/code/datums/components/cross_shock_component.dm similarity index 100% rename from code/game/gamemodes/miniantags/pulsedemon/cross_shock_component.dm rename to code/datums/components/cross_shock_component.dm diff --git a/code/datums/spells/bloodcrawl.dm b/code/datums/spells/bloodcrawl.dm index 425a1721f8b..1314f938e32 100644 --- a/code/datums/spells/bloodcrawl.dm +++ b/code/datums/spells/bloodcrawl.dm @@ -81,7 +81,7 @@ GM.set_nitrogen(MOLES_N2STANDARD) GM.set_temperature(T20C) return GM - + /datum/spell/bloodcrawl/proc/block_hands(mob/living/carbon/C) if(C.l_hand || C.r_hand) to_chat(C, "You may not hold items while blood crawling!") @@ -141,7 +141,7 @@ ) var/sound if(isslaughterdemon(L)) - var/mob/living/simple_animal/demon/slaughter/SD = L + var/mob/living/basic/demon/slaughter/SD = L sound = SD.feast_sound else sound = 'sound/misc/demon_consume.ogg' @@ -171,7 +171,7 @@ L.adjustFireLoss(-25) if(isslaughterdemon(L)) - var/mob/living/simple_animal/demon/slaughter/demon = L + var/mob/living/basic/demon/slaughter/demon = L demon.devoured++ to_chat(victim, "You feel teeth sink into your flesh, and the--") var/obj/item/organ/internal/regenerative_core/legion/core = victim.get_int_organ(/obj/item/organ/internal/regenerative_core/legion) @@ -236,7 +236,7 @@ /datum/spell/bloodcrawl/proc/post_phase_out(atom/A, mob/living/L) if(isslaughterdemon(L)) - var/mob/living/simple_animal/demon/slaughter/S = L + var/mob/living/basic/demon/slaughter/S = L S.speed = 0 S.boost = world.time + 6 SECONDS var/old_color = L.color @@ -275,7 +275,7 @@ action_icon_state = "shadow_crawl" /datum/spell/bloodcrawl/shadow_crawl/can_cast(mob/user, charge_check, show_message) - var/mob/living/simple_animal/demon/shadow/current_demon = user + var/mob/living/basic/demon/shadow/current_demon = user if(!istype(current_demon)) return ..() @@ -303,10 +303,10 @@ /datum/spell/bloodcrawl/shadow_crawl/post_phase_in(mob/living/L, obj/effect/dummy/slaughter/holder) ..() - if(!istype(L, /mob/living/simple_animal/demon/shadow)) + if(!istype(L, /mob/living/basic/demon/shadow)) return - var/mob/living/simple_animal/demon/shadow/S = L - S.RegisterSignal(holder, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, check_darkness)) + var/mob/living/basic/demon/shadow/S = L + S.RegisterSignal(holder, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/basic/demon/shadow, check_darkness)) #undef BLOODCRAWL #undef BLOODCRAWL_EAT diff --git a/code/game/gamemodes/cult/cult_actions.dm b/code/game/gamemodes/cult/cult_actions.dm index a43b78fba66..4329d6eb08f 100644 --- a/code/game/gamemodes/cult/cult_actions.dm +++ b/code/game/gamemodes/cult/cult_actions.dm @@ -47,7 +47,7 @@ var/title var/large = FALSE var/living_message - if(istype(user, /mob/living/simple_animal/demon/slaughter/cult)) //Harbringers of the Slaughter + if(istype(user, /mob/living/basic/demon/slaughter/cult)) //Harbringers of the Slaughter title = "Harbringer of the Slaughter" large = TRUE else diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index bcb138e8cfe..eeafdbba7f2 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -281,7 +281,7 @@ if(curselimit > 1) to_chat(user, "We have exhausted our ability to curse the shuttle.") return - if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/demon/slaughter/cult) in GLOB.mob_list) + if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/basic/demon/slaughter/cult) in GLOB.mob_list) to_chat(user, "Nar'Sie or her avatars are already on this plane, there is no delaying the end of all things.") return diff --git a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm b/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm deleted file mode 100644 index 621b56c4676..00000000000 --- a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm +++ /dev/null @@ -1,357 +0,0 @@ -//////////////////The Monster - -/mob/living/simple_animal/demon/slaughter - name = "slaughter demon" - real_name = "slaughter demon" - desc = "A large, menacing creature covered in armored black scales. You should run." - maxHealth = 240 - health = 240 - speak = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq") - icon = 'icons/mob/mob.dmi' - icon_state = "daemon" - icon_living = "daemon" - var/boost = 0 - var/feast_sound = 'sound/misc/demon_consume.ogg' - var/devoured = 0 - var/list/consumed_mobs = list() - - var/list/nearby_mortals = list() - var/cooldown = 0 - var/gorecooldown = 0 - var/vialspawned = FALSE - loot = list(/obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic, /obj/item/organ/internal/heart/demon/slaughter) - var/playstyle_string = "You are the Slaughter Demon, a terrible creature from another existence. You have a single desire: to kill. \ - You may use the blood crawl icon when on blood pools to travel through them, appearing and dissapearing from the station at will. \ - Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to feast. \ - You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. " - deathmessage = "screams in anger as it collapses into a puddle of viscera!" - - -/mob/living/simple_animal/demon/slaughter/New() - ..() - remove_from_all_data_huds() - ADD_TRAIT(src, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") - var/datum/spell/bloodcrawl/bloodspell = new - AddSpell(bloodspell) - if(istype(loc, /obj/effect/dummy/slaughter)) - bloodspell.phased = TRUE - addtimer(CALLBACK(src, PROC_REF(attempt_objectives)), 5 SECONDS) - - -/mob/living/simple_animal/demon/slaughter/Life(seconds, times_fired) - ..() - if(boost < world.time) - speed = 1 - else - speed = 0 - -/mob/living/simple_animal/demon/slaughter/proc/attempt_objectives() - if(mind) - var/list/messages = list() - messages.Add(playstyle_string) - messages.Add("You are not currently in the same plane of existence as the station. Use the blood crawl action at a blood pool to manifest.") - SEND_SOUND(src, sound('sound/misc/demon_dies.ogg')) - if(!vialspawned) - SSticker.mode.traitors |= mind - mind.add_mind_objective(/datum/objective/slaughter) - mind.add_mind_objective(/datum/objective/demon_fluff) - messages.Add(mind.prepare_announce_objectives(FALSE)) - - messages.Add("For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Slaughter_Demon)") - to_chat(src, chat_box_red(messages.Join("
"))) - - -/obj/effect/decal/cleanable/blood/innards - icon = 'icons/obj/surgery.dmi' - icon_state = "innards" - base_icon = 'icons/obj/surgery.dmi' - icon_state = "innards" - random_icon_states = list("innards") - name = "pile of viscera" - desc = "A repulsive pile of guts and gore." - weightless_icon = 'icons/obj/surgery.dmi' - -/mob/living/simple_animal/demon/slaughter/Destroy() - // Only execute the below if we successfully died - for(var/mob/living/M in consumed_mobs) - REMOVE_TRAIT(M, TRAIT_UNREVIVABLE, "demon") - release_consumed(M) - . = ..() - -/mob/living/simple_animal/demon/slaughter/proc/release_consumed(mob/living/M) - M.forceMove(get_turf(src)) - - -// Midround slaughter demon, less tanky - -/mob/living/simple_animal/demon/slaughter/lesser - maxHealth = 170 - health = 170 - -// Cult slaughter demon -/// Summoned as part of the cult objective "Bring the Slaughter" -/mob/living/simple_animal/demon/slaughter/cult - name = "harbinger of the slaughter" - real_name = "harbinger of the Slaughter" - desc = "An awful creature from beyond the realms of madness." - maxHealth = 540 - health = 540 - melee_damage_upper = 60 - melee_damage_lower = 60 - environment_smash = ENVIRONMENT_SMASH_RWALLS //Smashes through EVERYTHING - r-walls included - faction = list("cult") - playstyle_string = "You are a Harbinger of the Slaughter. Brought forth by the servants of Nar'Sie, you have a single purpose: slaughter the heretics \ - who do not worship your master. You may use the ability 'Blood Crawl' near a pool of blood to enter it and become incorporeal. Using the ability again near a blood pool will allow you \ - to emerge from it. You are fast, powerful, and almost invincible. By dragging a dead or unconscious body into a blood pool with you, you will consume it after a time and fully regain \ - your health. You may use the ability 'Sense Victims' in your Cultist tab to locate a random, living heretic." - -/datum/spell/sense_victims - name = "Sense Victims" - desc = "Sense the location of heretics." - base_cooldown = 0 - clothes_req = FALSE - overlay = null - action_icon_state = "bloodcrawl" - action_background_icon_state = "bg_cult" - -/datum/spell/sense_victims/create_new_targeting() - return new /datum/spell_targeting/alive_mob_list - -/datum/spell/sense_victims/valid_target(mob/living/target, user) - return target.stat == CONSCIOUS && target.key && !IS_CULTIST(target) // Only conscious, non cultist players - -/datum/spell/sense_victims/cast(list/targets, mob/user) - var/mob/living/victim = targets[1] - to_chat(victim, "You feel an awful sense of being watched...") - victim.Stun(6 SECONDS) //HUE - var/area/A = get_area(victim) - if(!A) - to_chat(user, "You could not locate any sapient heretics for the Slaughter.") - return 0 - to_chat(user, "You sense a terrified soul at [A]. Show [A.p_them()] the error of [A.p_their()] ways.") - -/mob/living/simple_animal/demon/slaughter/cult/New() - ..() - spawn(5) - var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/demon/slaughter/cult) - if(!length(demon_candidates)) - visible_message("[src] disappears in a flash of red light!") - qdel(src) - return - if(QDELETED(src)) // Just in case - return - var/mob/M = pick(demon_candidates) - var/mob/living/simple_animal/demon/slaughter/cult/S = src - if(!M || !M.client) - visible_message("[src] disappears in a flash of red light!") - qdel(src) - return - var/client/C = M.client - - S.key = C.key - dust_if_respawnable(M) - S.mind.assigned_role = "Harbinger of the Slaughter" - S.mind.special_role = "Harbinger of the Slaughter" - to_chat(S, playstyle_string) - S.mind.add_antag_datum(/datum/antagonist/cultist) - var/datum/spell/sense_victims/SV = new - AddSpell(SV) - - S.mind.add_mind_objective(/datum/objective/cult_slaughter) - var/list/messages = S.mind.prepare_announce_objectives(FALSE) - to_chat(S, chat_box_red(messages.Join("
"))) - -////////////////////The Powers - -//Paradise Port: I added this because..SPOOPY DEMON IN YOUR BRAIN - - -/datum/action/innate/demon_whisper - name = "Demonic Whisper" - button_icon_state = "demon_comms" - background_icon_state = "bg_demon" - -/datum/action/innate/demon_whisper/proc/choose_targets(mob/user = usr)//yes i am copying from telepathy..hush... - var/list/validtargets = list() - for(var/mob/living/M in view(user.client.maxview(), get_turf(user))) - if(M && M.mind && M.stat != DEAD) - if(M == user) - continue - - validtargets += M - - if(!length(validtargets)) - to_chat(usr, "There are no valid targets!") - return - - var/mob/living/target = tgui_input_list(user, "Choose the target to talk to", "Targeting", validtargets) - return target - -/datum/action/innate/demon_whisper/Activate() - var/mob/living/choice = choose_targets() - if(!choice) - return - - var/msg = tgui_input_text(usr, "What do you wish to tell [choice]?", null, "") - if(!msg) - return - log_say("(SLAUGHTER to [key_name(choice)]) [msg]", usr) - to_chat(usr, "You whisper to [choice]: [msg]") - to_chat(choice, "Suddenly a strange, demonic voice resonates in your head... [msg]") - for(var/mob/dead/observer/G in GLOB.player_list) - G.show_message("Demonic message from [usr] ([ghost_follow_link(usr, ghost=G)]) to [choice] ([ghost_follow_link(choice, ghost=G)]): [msg]") - - -//////////The Loot - -// Demon heart base type -/obj/item/organ/internal/heart/demon - name = "demon heart" - desc = "Still it beats furiously, emanating an aura of utter hate." - icon_state = "demon_heart" - origin_tech = "combat=5;biotech=7" - organ_datums = list(/datum/organ/heart/always_beating, /datum/organ/battery) - -/obj/item/organ/internal/heart/demon/update_icon_state() - return //always beating visually - -/obj/item/organ/internal/heart/demon/prepare_eat() - return // Just so people don't accidentally waste it - -/obj/item/organ/internal/heart/demon/attack_self__legacy__attackchain(mob/living/user) - user.visible_message("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!", \ - "An unnatural hunger consumes you. You raise [src] to your mouth and devour it!") - playsound(user, 'sound/misc/demon_consume.ogg', 50, 1) - -//////////The Loot - -//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl -/// SLAUGHTER DEMON HEART - -/obj/item/organ/internal/heart/demon/slaughter/attack_self__legacy__attackchain(mob/living/user) - ..() - - // Eating the heart for the first time. Gives basic bloodcrawling. This is the only time we need to insert the heart. - if(!HAS_TRAIT(user, TRAIT_BLOODCRAWL)) - user.visible_message("[user]'s eyes flare a deep crimson!", \ - "You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!") - ADD_TRAIT(user, TRAIT_BLOODCRAWL, "bloodcrawl") - user.drop_item() - insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E. - return TRUE - - // Eating a 2nd heart. Gives the ability to drag people into blood and eat them. - if(HAS_TRAIT(user, TRAIT_BLOODCRAWL)) - to_chat(user, "You feel differ- CONSUME THEM!") - ADD_TRAIT(user, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") - qdel(src) // Replacing their demon heart with another demon heart is pointless, just delete this one and return. - return TRUE - - // Eating any more than 2 demon hearts does nothing. - to_chat(user, "...and you don't feel any different.") - qdel(src) - -/obj/item/organ/internal/heart/demon/slaughter/insert(mob/living/carbon/M, special = 0) - . = ..() - if(M.mind) - M.mind.AddSpell(new /datum/spell/bloodcrawl(null)) - -/obj/item/organ/internal/heart/demon/slaughter/remove(mob/living/carbon/M, special = 0) - . = ..() - if(M.mind) - REMOVE_TRAIT(M, TRAIT_BLOODCRAWL, "bloodcrawl") - REMOVE_TRAIT(M, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") - M.mind.RemoveSpell(/datum/spell/bloodcrawl) - -/mob/living/simple_animal/demon/slaughter/laughter - // The laughter demon! It's everyone's best friend! It just wants to hug - // them so much, it wants to hug everyone at once! - name = "laughter demon" - real_name = "laughter demon" - desc = "A large, adorable creature covered in armor with pink bows." - speak_emote = list("giggles", "titters", "chuckles") - emote_hear = list("gaffaws", "laughs") - response_help = "hugs" - attacktext = "wildly tickles" - maxHealth = 215 - health = 215 - melee_damage_lower = 25 - melee_damage_upper = 25 - playstyle_string = "You are the Laughter Demon, an adorable creature from another existence. You have a single desire: to hug and tickle. \ - You may use the blood crawl icon when on blood pools to travel through them, appearing and dissapearing from the station at will. \ - Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to hug them. \ - You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. \ - (You should be attacking people on harm intent, and not nuzzling them.)" - - attack_sound = 'sound/items/bikehorn.ogg' - feast_sound = 'sound/spookoween/scary_horn2.ogg' - death_sound = 'sound/misc/sadtrombone.ogg' - - icon_state = "bowmon" - icon_living = "bowmon" - deathmessage = "fades out, as all of its friends are released from its prison of hugs." - loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"}) - -/mob/living/simple_animal/demon/slaughter/laughter/release_consumed(mob/living/M) - if(M.revive()) - M.grab_ghost(force = TRUE) - playsound(get_turf(src), feast_sound, 50, TRUE, -1) - to_chat(M, "You leave [src]'s warm embrace, and feel ready to take on the world.") - ..(M) - - -//Objectives and helpers. - -//Objective info, Based on Reverent mini Atang -/datum/objective/slaughter - needs_target = FALSE - var/targetKill = 10 - -/datum/objective/slaughter/New() - targetKill = rand(10,20) - explanation_text = "Devour [targetKill] mortals." - ..() - -/datum/objective/slaughter/check_completion() - var/kill_count = 0 - for(var/datum/mind/M in get_owners()) - if(!isslaughterdemon(M.current) || QDELETED(M.current)) - continue - var/mob/living/simple_animal/demon/slaughter/R = M.current - kill_count += R.devoured - if(kill_count >= targetKill) - return TRUE - return FALSE - -/datum/objective/demon_fluff - name = "Spread blood" - needs_target = FALSE - -/datum/objective/demon_fluff/New() - find_target() - var/targetname = "someone" - if(target && target.current) - targetname = target.current.real_name - var/list/explanation_texts = list( - "Spread blood all over the bridge.", - "Spread blood all over the brig.", - "Spread blood all over the chapel.", - "Kill or Destroy all Janitors or Sanitation bots.", - "Spare a few after striking them... make them bleed before the harvest.", - "Hunt those that try to hunt you first.", - "Hunt those that run away from you in fear", - "Show [targetname] the power of blood.", - "Drive [targetname] insane with demonic whispering." - ) - // As this is a fluff objective, we don't need a target, so we want to null it out. - // We don't want the demon getting a "Time for Plan B" message if the target cryos. - target = null - explanation_text = pick(explanation_texts) - ..() - -/datum/objective/demon_fluff/check_completion() - return TRUE - -/datum/objective/cult_slaughter - explanation_text = "Bring forth the Slaughter to the nonbelievers." - needs_target = FALSE diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_objectives.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_objectives.dm deleted file mode 100644 index 64d9b8af4e0..00000000000 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_objectives.dm +++ /dev/null @@ -1,57 +0,0 @@ -/datum/objective/pulse_demon/infest - name = "Hijack APCs" - /// Amount of APCs we need to hijack, can be 15, 20, or 25 - var/amount = 0 - -/datum/objective/pulse_demon/infest/New() - . = ..() - amount = rand(3, 5) * 5 - explanation_text = "Hijack [amount] APCs." - -/datum/objective/pulse_demon/infest/check_completion() - if(..()) - return TRUE - var/hijacked = 0 - for(var/datum/mind/M in get_owners()) - if(!ispulsedemon(M.current) || QDELETED(M.current)) - continue - var/mob/living/simple_animal/demon/pulse_demon/demon = M.current - hijacked += length(demon.hijacked_apcs) - return hijacked >= amount - -/datum/objective/pulse_demon/drain - name = "Drain Power" - /// Amount of power we need to drain, ranges from 500 KW to 5 MW - var/amount = 0 - -/datum/objective/pulse_demon/drain/New() - . = ..() - amount = rand(1, 10) * 500000 - explanation_text = "Drain [format_si_suffix(amount)]W of power." - -/datum/objective/pulse_demon/drain/check_completion() - if(..()) - return TRUE - var/drained = 0 - for(var/datum/mind/M in get_owners()) - if(!ispulsedemon(M.current) || QDELETED(M.current)) - continue - var/mob/living/simple_animal/demon/pulse_demon/demon = M.current - drained += demon.charge_drained - return drained >= amount - -// Requires 1 APC to be hacked and not destroyed to complete -/datum/objective/pulse_demon/tamper - name = "Tamper Machinery" - explanation_text = "Cause mischief amongst the machines in rooms with APCs you've hijacked, and defend yourself from anyone trying to stop you." - -/datum/objective/pulse_demon/tamper/check_completion() - if(..()) - return TRUE - for(var/datum/mind/M in get_owners()) - if(!ispulsedemon(M.current) || QDELETED(M.current)) - continue - var/mob/living/simple_animal/demon/pulse_demon/demon = M.current - if(!length(demon.hijacked_apcs) || !M.active || demon.stat == DEAD) - return FALSE - return TRUE diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 5bfc0d76f83..74b2b554987 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -184,7 +184,7 @@ var/veil_msg = "You sense a dark presence lurking \ just beyond the veil..." var/objective_verb = "Kill" - var/mob/living/demon_type = /mob/living/simple_animal/demon/slaughter + var/mob/living/demon_type = /mob/living/basic/demon/slaughter /obj/item/antag_spawner/slaughter_demon/attack_self__legacy__attackchain(mob/user) if(level_blocks_magic(user.z)) //this is to make sure the wizard does NOT summon a demon from the Den.. @@ -213,9 +213,9 @@ /obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user) var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T) - var/mob/living/simple_animal/demon/D = new demon_type(holder) - if(istype(D, /mob/living/simple_animal/demon/slaughter)) - var/mob/living/simple_animal/demon/slaughter/S = D + var/mob/living/basic/demon/D = new demon_type(holder) + if(istype(D, /mob/living/basic/demon/slaughter)) + var/mob/living/basic/demon/slaughter/S = D S.vialspawned = TRUE D.key = C.key @@ -246,7 +246,7 @@ veil_msg = "You sense an adorable presence \ lurking just beyond the veil..." objective_verb = "Hug and tickle" - demon_type = /mob/living/simple_animal/demon/slaughter/laughter + demon_type = /mob/living/basic/demon/slaughter/laughter /obj/item/antag_spawner/slaughter_demon/shadow name = "vial of shadow" @@ -256,7 +256,7 @@ icon_state = "vialshadows" veil_msg = "You sense a dark presence \ lurking in the shadows..." - demon_type = /mob/living/simple_animal/demon/shadow + demon_type = /mob/living/basic/demon/shadow ///////////MORPH @@ -397,7 +397,7 @@ var/shatter_msg = "You shatter the bulb, no turning back now!" var/veil_msg = "The creature sparks energetically and zips away..." var/objective_verb = "Electrocute" - var/mob/living/demon_type = /mob/living/simple_animal/demon/pulse_demon/wizard + var/mob/living/demon_type = /mob/living/basic/demon/pulse_demon/wizard /obj/item/antag_spawner/pulse_demon/attack_self__legacy__attackchain(mob/user) if(level_blocks_magic(user.z)) @@ -436,7 +436,7 @@ var/datum/mind/player_mind = new /datum/mind(C.key) player_mind.active = TRUE - var/mob/living/simple_animal/demon/pulse_demon/wizard/demon = new(T) + var/mob/living/basic/demon/pulse_demon/wizard/demon = new(T) player_mind.transfer_to(demon) player_mind.assigned_role = SPECIAL_ROLE_DEMON player_mind.special_role = SPECIAL_ROLE_DEMON diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_event.dm b/code/modules/events/pulsedemon_event.dm similarity index 94% rename from code/game/gamemodes/miniantags/pulsedemon/pulsedemon_event.dm rename to code/modules/events/pulsedemon_event.dm index 2fb91852d07..594e946f98e 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_event.dm +++ b/code/modules/events/pulsedemon_event.dm @@ -5,7 +5,7 @@ var/key_of_pulsedemon /datum/event/spawn_pulsedemon/proc/get_pulsedemon() - var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a pulse demon?", ROLE_DEMON, FALSE, source = /mob/living/simple_animal/demon/pulse_demon) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a pulse demon?", ROLE_DEMON, FALSE, source = /mob/living/basic/demon/pulse_demon) if(!length(candidates)) message_admins("no candidates were found for the pulse demon event.") kill() @@ -23,7 +23,7 @@ player_mind.active = TRUE var/turf/spawn_loc = get_spawn_loc(player_mind.current) - var/mob/living/simple_animal/demon/pulse_demon/demon = new(spawn_loc) + var/mob/living/basic/demon/pulse_demon/demon = new(spawn_loc) player_mind.transfer_to(demon) message_admins("[key_name_admin(demon)] has been made into a [initial(demon.name)] by an event.") log_game("[key_name_admin(demon)] was spawned as a [initial(demon.name)] by an event.") diff --git a/code/modules/events/slaughterevent.dm b/code/modules/events/slaughterevent.dm index f26fa697139..c1508c4cd8d 100644 --- a/code/modules/events/slaughterevent.dm +++ b/code/modules/events/slaughterevent.dm @@ -2,7 +2,7 @@ name = "Slaughter Demon" noAutoEnd = TRUE var/key_of_slaughter - var/mob/living/simple_animal/demon/demon = /mob/living/simple_animal/demon/slaughter/lesser + var/mob/living/basic/demon/demon = /mob/living/basic/demon/slaughter/lesser nominal_severity = EVENT_LEVEL_MAJOR role_weights = list(ASSIGNMENT_SECURITY = 5, ASSIGNMENT_JANITOR = 5, ASSIGNMENT_MEDICAL = 3, ASSIGNMENT_CREW = 0.7) role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_JANITOR = 1, ASSIGNMENT_MEDICAL = 2, ASSIGNMENT_CREW = 25) @@ -29,7 +29,7 @@ dust_if_respawnable(C) var/turf/spawn_loc = get_spawn_loc(player_mind.current) var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(spawn_loc) - var/mob/living/simple_animal/demon/S = new demon(holder) + var/mob/living/basic/demon/S = new demon(holder) player_mind.transfer_to(S) player_mind.assigned_role = "Demon" player_mind.special_role = SPECIAL_ROLE_DEMON @@ -54,11 +54,11 @@ /datum/event/spawn_slaughter/greater name = "Greater Slaughter Demon" - demon = /mob/living/simple_animal/demon/slaughter + demon = /mob/living/basic/demon/slaughter /datum/event/spawn_slaughter/shadow name = "Shadow Demon" - demon = /mob/living/simple_animal/demon/shadow + demon = /mob/living/basic/demon/shadow // Same as slaughter but without Jani role_weights = list(ASSIGNMENT_SECURITY = 5, ASSIGNMENT_MEDICAL = 3, ASSIGNMENT_CREW = 0.7) role_requirements = list(ASSIGNMENT_SECURITY = 4, ASSIGNMENT_MEDICAL = 2, ASSIGNMENT_CREW = 25) diff --git a/code/game/gamemodes/miniantags/demons/demon.dm b/code/modules/mob/living/basic/hostile/demons/demon.dm similarity index 53% rename from code/game/gamemodes/miniantags/demons/demon.dm rename to code/modules/mob/living/basic/hostile/demons/demon.dm index 5c12637d637..a6e8c90fe86 100644 --- a/code/game/gamemodes/miniantags/demons/demon.dm +++ b/code/modules/mob/living/basic/hostile/demons/demon.dm @@ -1,22 +1,24 @@ -/mob/living/simple_animal/demon +/mob/living/basic/demon name = "a generic demon" desc = "you shouldnt be reading this, file a github report." - speak_emote = list("gurgles") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" + speak_emote = list("gurgles", "wails", "screeches") + response_help_continuous = "thinks better of touching" + response_help_simple = "thinks better of touching" + response_disarm_continuous = "flails at" + response_disarm_simple = "flail at" + response_harm_continuous = "punches" + response_harm_simple = "punches" a_intent = INTENT_HARM mob_biotypes = MOB_ORGANIC | MOB_HUMANOID - stop_automated_movement = TRUE attack_sound = 'sound/misc/demon_attack1.ogg' death_sound = 'sound/misc/demon_dies.ogg' atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 10 - minbodytemp = 0 - maxbodytemp = INFINITY + minimum_survivable_temperature = 0 + maximum_survivable_temperature = INFINITY faction = list("demon") - attacktext = "wildly tears into" + attack_verb_continuous = "wildly tears into" + attack_verb_simple = "wildly tears into" maxHealth = 200 health = 200 environment_smash = ENVIRONMENT_SMASH_STRUCTURES @@ -25,14 +27,16 @@ melee_damage_upper = 30 see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - del_on_death = TRUE + basic_mob_flags = DEL_ON_DEATH var/datum/action/innate/demon_whisper/whisper_action -/mob/living/simple_animal/demon/Initialize(mapload) +/mob/living/basic/demon/Initialize(mapload) . = ..() whisper_action = new() whisper_action.Grant(src) + add_language("Galactic Common") + set_default_language(GLOB.all_languages["Galactic Common"]) -/mob/living/simple_animal/demon/Destroy() +/mob/living/basic/demon/Destroy() QDEL_NULL(whisper_action) return ..() diff --git a/code/modules/mob/living/basic/hostile/demons/demon_loot.dm b/code/modules/mob/living/basic/hostile/demons/demon_loot.dm new file mode 100644 index 00000000000..dc94d550397 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/demons/demon_loot.dm @@ -0,0 +1,114 @@ +// Demon heart base type +/obj/item/organ/internal/heart/demon + name = "demon heart" + desc = "Still it beats furiously, emanating an aura of utter hate." + icon_state = "demon_heart" + origin_tech = "combat=5;biotech=7" + organ_datums = list(/datum/organ/heart/always_beating, /datum/organ/battery) + +/obj/item/organ/internal/heart/demon/update_icon_state() + return //always beating visually + +/obj/item/organ/internal/heart/demon/prepare_eat() + return // Just so people don't accidentally waste it + +/obj/item/organ/internal/heart/demon/attack_self__legacy__attackchain(mob/living/user) + user.visible_message("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!", \ + "An unnatural hunger consumes you. You raise [src] to your mouth and devour it!") + playsound(user, 'sound/misc/demon_consume.ogg', 50, 1) + +/// SLAUGHTER DEMON HEART + +/obj/item/organ/internal/heart/demon/slaughter/attack_self__legacy__attackchain(mob/living/user) + ..() + + // Eating the heart for the first time. Gives basic bloodcrawling. This is the only time we need to insert the heart. + if(!HAS_TRAIT(user, TRAIT_BLOODCRAWL)) + user.visible_message("[user]'s eyes flare a deep crimson!", \ + "You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!") + ADD_TRAIT(user, TRAIT_BLOODCRAWL, "bloodcrawl") + user.drop_item() + insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E. + return TRUE + + // Eating a 2nd heart. Gives the ability to drag people into blood and eat them. + if(HAS_TRAIT(user, TRAIT_BLOODCRAWL)) + to_chat(user, "You feel differ- CONSUME THEM!") + ADD_TRAIT(user, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") + qdel(src) // Replacing their demon heart with another demon heart is pointless, just delete this one and return. + return TRUE + + // Eating any more than 2 demon hearts does nothing. + to_chat(user, "...and you don't feel any different.") + qdel(src) + +/obj/item/organ/internal/heart/demon/slaughter/insert(mob/living/carbon/M, special = 0) + . = ..() + if(M.mind) + M.mind.AddSpell(new /datum/spell/bloodcrawl(null)) + +/obj/item/organ/internal/heart/demon/slaughter/remove(mob/living/carbon/M, special = 0) + . = ..() + if(M.mind) + REMOVE_TRAIT(M, TRAIT_BLOODCRAWL, "bloodcrawl") + REMOVE_TRAIT(M, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") + M.mind.RemoveSpell(/datum/spell/bloodcrawl) + +/// SHADOW DEMON HEART +/obj/item/organ/internal/heart/demon/shadow + name = "heart of darkness" + desc = "It still beats furiously, emitting an aura of fear." + color = COLOR_BLACK + +/obj/item/organ/internal/heart/demon/shadow/attack_self__legacy__attackchain(mob/living/user) + . = ..() + user.drop_item() + insert(user) + +/obj/item/organ/internal/heart/demon/shadow/insert(mob/living/carbon/M, special = 0) + . = ..() + if(M.mind) + M.mind.AddSpell(new /datum/spell/fireball/shadow_grapple) + +/obj/item/organ/internal/heart/demon/shadow/remove(mob/living/carbon/M, special = 0) + . = ..() + if(M.mind) + M.mind.RemoveSpell(/datum/spell/fireball/shadow_grapple) + +/// PULSE DEMON HEART +/obj/item/organ/internal/heart/demon/pulse + name = "perpetual pacemaker" + desc = "It still beats furiously, thousands of bright lights shine within it." + color = COLOR_YELLOW + +/obj/item/organ/internal/heart/demon/pulse/Initialize(mapload) + . = ..() + set_light(13, 2, "#bbbb00") + +/obj/item/organ/internal/heart/demon/pulse/attack_self__legacy__attackchain(mob/living/user) + . = ..() + user.drop_item() + insert(user) + +/obj/item/organ/internal/heart/demon/pulse/insert(mob/living/carbon/M, special, dont_remove_slot) + . = ..() + M.AddComponent(/datum/component/cross_shock, 30, 500, 2 SECONDS) + ADD_TRAIT(M, TRAIT_SHOCKIMMUNE, UNIQUE_TRAIT_SOURCE(src)) + M.set_light(3, 2, "#bbbb00") + +/obj/item/organ/internal/heart/demon/pulse/remove(mob/living/carbon/M, special) + . = ..() + REMOVE_TRAIT(M, TRAIT_SHOCKIMMUNE, UNIQUE_TRAIT_SOURCE(src)) + M.remove_light() + +/obj/item/organ/internal/heart/demon/pulse/on_life() + if(!owner) + return + for(var/obj/item/stock_parts/cell/cell_to_charge in owner.GetAllContents()) + var/newcharge = min(0.05 * cell_to_charge.maxcharge + cell_to_charge.charge, cell_to_charge.maxcharge) + if(cell_to_charge.charge < newcharge) + cell_to_charge.charge = newcharge + if(isobj(cell_to_charge.loc)) + var/obj/cell_location = cell_to_charge.loc + cell_location.update_icon() //update power meters and such + cell_to_charge.update_icon() diff --git a/code/modules/mob/living/basic/hostile/demons/demon_objectives.dm b/code/modules/mob/living/basic/hostile/demons/demon_objectives.dm new file mode 100644 index 00000000000..7354cf3bd70 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/demons/demon_objectives.dm @@ -0,0 +1,111 @@ +// Objective info, Based on Reverent mini Atang +/datum/objective/slaughter + needs_target = FALSE + var/targetKill = 10 + +/datum/objective/slaughter/New() + targetKill = rand(10,20) + explanation_text = "Devour [targetKill] mortals." + ..() + +/datum/objective/slaughter/check_completion() + var/kill_count = 0 + for(var/datum/mind/M in get_owners()) + if(!isslaughterdemon(M.current) || QDELETED(M.current)) + continue + var/mob/living/basic/demon/slaughter/R = M.current + kill_count += R.devoured + if(kill_count >= targetKill) + return TRUE + return FALSE + +/datum/objective/demon_fluff + name = "Spread blood" + needs_target = FALSE + +/datum/objective/demon_fluff/New() + find_target() + var/targetname = "someone" + if(target && target.current) + targetname = target.current.real_name + var/list/explanation_texts = list( + "Spread blood all over the bridge.", + "Spread blood all over the brig.", + "Spread blood all over the chapel.", + "Kill or Destroy all Janitors or Sanitation bots.", + "Spare a few after striking them... make them bleed before the harvest.", + "Hunt those that try to hunt you first.", + "Hunt those that run away from you in fear", + "Show [targetname] the power of blood.", + "Drive [targetname] insane with demonic whispering." + ) + // As this is a fluff objective, we don't need a target, so we want to null it out. + // We don't want the demon getting a "Time for Plan B" message if the target cryos. + target = null + explanation_text = pick(explanation_texts) + ..() + +/datum/objective/demon_fluff/check_completion() + return TRUE + +/datum/objective/cult_slaughter + explanation_text = "Bring forth the Slaughter to the nonbelievers." + needs_target = FALSE + +/datum/objective/pulse_demon/infest + name = "Hijack APCs" + /// Amount of APCs we need to hijack, can be 15, 20, or 25 + var/amount = 0 + +/datum/objective/pulse_demon/infest/New() + . = ..() + amount = rand(3, 5) * 5 + explanation_text = "Hijack [amount] APCs." + +/datum/objective/pulse_demon/infest/check_completion() + if(..()) + return TRUE + var/hijacked = 0 + for(var/datum/mind/M in get_owners()) + if(!ispulsedemon(M.current) || QDELETED(M.current)) + continue + var/mob/living/basic/demon/pulse_demon/demon = M.current + hijacked += length(demon.hijacked_apcs) + return hijacked >= amount + +/datum/objective/pulse_demon/drain + name = "Drain Power" + /// Amount of power we need to drain, ranges from 500 KW to 5 MW + var/amount = 0 + +/datum/objective/pulse_demon/drain/New() + . = ..() + amount = rand(1, 10) * 500000 + explanation_text = "Drain [format_si_suffix(amount)]W of power." + +/datum/objective/pulse_demon/drain/check_completion() + if(..()) + return TRUE + var/drained = 0 + for(var/datum/mind/M in get_owners()) + if(!ispulsedemon(M.current) || QDELETED(M.current)) + continue + var/mob/living/basic/demon/pulse_demon/demon = M.current + drained += demon.charge_drained + return drained >= amount + +// Requires 1 APC to be hacked and not destroyed to complete +/datum/objective/pulse_demon/tamper + name = "Tamper Machinery" + explanation_text = "Cause mischief amongst the machines in rooms with APCs you've hijacked, and defend yourself from anyone trying to stop you." + +/datum/objective/pulse_demon/tamper/check_completion() + if(..()) + return TRUE + for(var/datum/mind/M in get_owners()) + if(!ispulsedemon(M.current) || QDELETED(M.current)) + continue + var/mob/living/basic/demon/pulse_demon/demon = M.current + if(!length(demon.hijacked_apcs) || !M.active || demon.stat == DEAD) + return FALSE + return TRUE diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm b/code/modules/mob/living/basic/hostile/demons/demon_powers.dm similarity index 73% rename from code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm rename to code/modules/mob/living/basic/hostile/demons/demon_powers.dm index 6b637f7e70b..cb4da56daa9 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm +++ b/code/modules/mob/living/basic/hostile/demons/demon_powers.dm @@ -1,3 +1,149 @@ +////////////////////////////// +// MARK: DEMONIC WHISPER +////////////////////////////// +/datum/action/innate/demon_whisper + name = "Demonic Whisper" + button_icon_state = "demon_comms" + background_icon_state = "bg_demon" + +/datum/action/innate/demon_whisper/proc/choose_targets(mob/user = usr) + var/list/validtargets = list() + for(var/mob/living/M in view(user.client.maxview(), get_turf(user))) + if(M && M.mind && M.stat != DEAD) + if(M == user) + continue + + validtargets += M + + if(!length(validtargets)) + to_chat(usr, "There are no valid targets!") + return + + var/mob/living/target = tgui_input_list(user, "Choose the target to talk to", "Targeting", validtargets) + return target + +/datum/action/innate/demon_whisper/Activate() + var/mob/living/choice = choose_targets() + if(!choice) + return + + var/msg = tgui_input_text(usr, "What do you wish to tell [choice]?", null, "") + if(!msg) + return + log_say("(SLAUGHTER to [key_name(choice)]) [msg]", usr) + to_chat(usr, "You whisper to [choice]: [msg]") + to_chat(choice, "Suddenly a strange, demonic voice resonates in your head... [msg]") + for(var/mob/dead/observer/G in GLOB.player_list) + G.show_message("Demonic message from [usr] ([ghost_follow_link(usr, ghost=G)]) to [choice] ([ghost_follow_link(choice, ghost=G)]): [msg]") + +////////////////////////////// +// MARK: CULT DEMON +////////////////////////////// +/datum/spell/sense_victims + name = "Sense Victims" + desc = "Sense the location of heretics." + base_cooldown = 0 + clothes_req = FALSE + overlay = null + action_icon_state = "bloodcrawl" + action_background_icon_state = "bg_cult" + +/datum/spell/sense_victims/create_new_targeting() + return new /datum/spell_targeting/alive_mob_list + +/datum/spell/sense_victims/valid_target(mob/living/target, user) + return target.stat == CONSCIOUS && target.key && !IS_CULTIST(target) // Only conscious, non cultist players + +/datum/spell/sense_victims/cast(list/targets, mob/user) + var/mob/living/victim = targets[1] + to_chat(victim, "You feel an awful sense of being watched...") + victim.Stun(6 SECONDS) //HUE + var/area/A = get_area(victim) + if(!A) + to_chat(user, "You could not locate any sapient heretics for the Slaughter.") + return 0 + to_chat(user, "You sense a terrified soul at [A]. Show [A.p_them()] the error of [A.p_their()] ways.") + +////////////////////////////// +// MARK: SHADOW DEMON +////////////////////////////// +/datum/spell/fireball/shadow_grapple + name = "Shadow Grapple" + desc = "Fire one of your hands, if it hits a person it pulls them in. If you hit a structure you get pulled to the structure. Any light source hit with this will be disabled in a two tile radius." + base_cooldown = 10 SECONDS + fireball_type = /obj/projectile/magic/shadow_hand + + selection_activated_message = "You raise your hand, full of demonic energy! Left-click to cast at a target!" + selection_deactivated_message = "You re-absorb the energy...for now." + + action_background_icon_state = "shadow_demon_bg" + action_icon_state = "shadow_grapple" + sound = null + invocation_type = "none" + invocation = null + +/datum/spell/fireball/shadow_grapple/update_spell_icon() + return + +/obj/projectile/magic/shadow_hand + name = "shadow hand" + icon_state = "shadow_hand" + plane = FLOOR_PLANE + hitsound = 'sound/shadowdemon/shadowattack1.ogg' // Plays when hitting something living or a light + var/hit = FALSE + +/obj/projectile/magic/shadow_hand/pixel_move(trajectory_multiplier, hitscanning) + . = ..() + var/obj/machinery/light/floor/floor_light = locate(/obj/machinery/light/floor) in get_turf(src) + if(floor_light) + Bump(floor_light) + +/obj/projectile/magic/shadow_hand/fire(setAngle) + if(firer) + var/mob/living/basic/demon/shadow/current_demon = firer + if(istype(current_demon)) + current_demon.block_shadow_crawl() + firer.Beam(src, icon_state = "grabber_beam", time = INFINITY, maxdistance = INFINITY, beam_type = /obj/effect/ebeam/floor) + return ..() + +/obj/projectile/magic/shadow_hand/on_hit(atom/target, blocked, hit_zone) + if(hit) + return + hit = TRUE // to prevent double hits from the pull + . = ..() + for(var/atom/extinguish_target in range(2, src)) + extinguish_target.extinguish_light(TRUE) + if(!isliving(target)) + if(isshadowdemon(firer)) + firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10, callback = CALLBACK(firer, TYPE_PROC_REF(/mob/living/basic/demon/shadow, unblock_shadow_crawl))) + else + firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10) + else + unblock_shadowdemon_crawl() + if(!.) + return + else + var/mob/living/L = target + L.Immobilize(2 SECONDS) + L.apply_damage(40, BRUTE, BODY_ZONE_CHEST) + L.throw_at(get_step(firer, get_dir(firer, target)), 50, 10) + +/obj/projectile/magic/shadow_hand/Destroy() + if(!hit) + unblock_shadowdemon_crawl() + return ..() + +/obj/projectile/magic/shadow_hand/proc/unblock_shadowdemon_crawl() + var/mob/living/basic/demon/shadow/current_demon = firer + if(istype(current_demon)) + current_demon.unblock_shadow_crawl() + +/obj/effect/ebeam/floor + plane = FLOOR_PLANE + +////////////////////////////// +// MARK: PULSE DEMON +////////////////////////////// #define PULSEDEMON_REMOTE_DRAIN_MULTIPLIER 5 #define PD_UPGRADE_HIJACK_SPEED "Speed" @@ -36,7 +182,7 @@ action.desc = desc action.build_all_button_icons() -/datum/spell/pulse_demon/can_cast(mob/living/simple_animal/demon/pulse_demon/user, charge_check, show_message) +/datum/spell/pulse_demon/can_cast(mob/living/basic/demon/pulse_demon/user, charge_check, show_message) if(!..()) return FALSE if(!istype(user)) @@ -57,7 +203,7 @@ return FALSE return TRUE -/datum/spell/pulse_demon/cast(list/targets, mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/cast(list/targets, mob/living/basic/demon/pulse_demon/user) if(!istype(user) || locked || user.charge < cast_cost || !length(targets)) return FALSE if(requires_area && !user.controlling_area) @@ -77,7 +223,7 @@ /datum/spell/pulse_demon/create_new_targeting() return new /datum/spell_targeting/clicked_atom -/datum/spell/pulse_demon/proc/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/proc/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) return FALSE /datum/spell/pulse_demon/proc/reveal_demon(mob/user, reveal_time) @@ -87,7 +233,7 @@ user.layer = ABOVE_PLATING_LAYER // handles purchasing and upgrading abilities -/datum/spell/pulse_demon/AltClick(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/AltClick(mob/living/basic/demon/pulse_demon/user) if(!istype(user)) return @@ -122,7 +268,7 @@ else to_chat(user, "You cannot afford to upgrade this ability! It costs [format_si_suffix(upgrade_cost)] APC\s to upgrade.") -/datum/spell/pulse_demon/proc/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/proc/do_upgrade(mob/living/basic/demon/pulse_demon/user) cooldown_handler.recharge_duration = round(base_cooldown / (1.5 ** spell_level)) to_chat(user, "You have upgraded [initial(name)] to level [spell_level + 1], it now takes [cooldown_handler.recharge_duration / 10] seconds to recharge.") @@ -136,7 +282,7 @@ revealing = TRUE reveal_time = 5 SECONDS -/datum/spell/pulse_demon/cablehop/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/cablehop/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) var/turf/O = get_turf(user) var/turf/T = get_turf(target) var/obj/structure/cable/C = locate(/obj/structure/cable) in T @@ -167,7 +313,7 @@ requires_area = TRUE revealing = TRUE -/datum/spell/pulse_demon/emagtamper/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/emagtamper/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) to_chat(user, "You attempt to tamper with [target]!") target.emag_act(user) return TRUE @@ -182,7 +328,7 @@ requires_area = TRUE revealing = TRUE -/datum/spell/pulse_demon/emp/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/emp/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) to_chat(user, "You attempt to EMP [target]!") empulse(get_turf(target), 1, 1) return TRUE @@ -199,7 +345,7 @@ base_cooldown = 60 SECONDS reveal_time = 20 SECONDS -/datum/spell/pulse_demon/overload/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/overload/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) var/obj/machinery/M = target if(!istype(M)) to_chat(user, "That is not a machine.") @@ -211,7 +357,7 @@ addtimer(CALLBACK(src, PROC_REF(detonate), user, M), 5 SECONDS) return TRUE -/datum/spell/pulse_demon/overload/proc/detonate(mob/living/simple_animal/demon/pulse_demon/user, obj/machinery/target) +/datum/spell/pulse_demon/overload/proc/detonate(mob/living/basic/demon/pulse_demon/user, obj/machinery/target) if(!QDELETED(target)) if(spell_level == level_max) explosion(get_turf(target), 0, 1, 2, 2, smoke = TRUE, cause = "Pulse Demon: [name]") @@ -229,7 +375,7 @@ level_max = 0 base_cooldown = 3 SECONDS // you have to wait for the regular hijack time anyway -/datum/spell/pulse_demon/remotehijack/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/remotehijack/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) var/obj/machinery/power/apc/A = target if(!istype(A)) to_chat(user, "That is not an APC.") @@ -246,7 +392,7 @@ cast_cost = 50 KJ upgrade_cost = 2 -/datum/spell/pulse_demon/remotedrain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/remotedrain/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) if(isapc(target)) var/drained = user.drain_APC(target, PULSEDEMON_REMOTE_DRAIN_MULTIPLIER) if(drained == PULSEDEMON_SOURCE_DRAIN_INVALID) @@ -290,11 +436,11 @@ locked = FALSE level_max = 0 -/datum/spell/pulse_demon/toggle/do_drain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/toggle/do_drain/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) user.do_drain = do_toggle(!user.do_drain, user) return TRUE -/datum/spell/pulse_demon/toggle/do_drain/AltClick(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/toggle/do_drain/AltClick(mob/living/basic/demon/pulse_demon/user) if(!istype(user)) return @@ -317,14 +463,14 @@ level_max = 3 locked = FALSE -/datum/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) if(user.can_exit_cable && !(user.current_cable || user.current_power)) to_chat(user, "Enter a cable or power source first!") return FALSE user.can_exit_cable = do_toggle(!user.can_exit_cable, user) return TRUE -/datum/spell/pulse_demon/toggle/can_exit_cable/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/toggle/can_exit_cable/do_upgrade(mob/living/basic/demon/pulse_demon/user) user.outside_cable_speed = max(initial(user.outside_cable_speed) - spell_level, 1) to_chat(user, "You have upgraded [initial(name)] to level [spell_level + 1], you will now move faster outside of cables.") @@ -343,7 +489,7 @@ /datum/spell/pulse_demon/cycle_camera/create_new_targeting() return new /datum/spell_targeting/self -/datum/spell/pulse_demon/cycle_camera/AltClick(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/cycle_camera/AltClick(mob/living/basic/demon/pulse_demon/user) if(!istype(user)) return current_camera = 0 @@ -354,7 +500,7 @@ return user.forceMove(user.current_power) -/datum/spell/pulse_demon/cycle_camera/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/cycle_camera/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) if(!length(user.controlling_area.cameras)) return FALSE @@ -383,7 +529,7 @@ /datum/spell/pulse_demon/toggle/penetrating_shock/create_new_targeting() return new /datum/spell_targeting/self -/datum/spell/pulse_demon/toggle/penetrating_shock/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/toggle/penetrating_shock/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) user.strong_shocks = do_toggle(!user.strong_shocks, user) /datum/spell/pulse_demon/open_upgrades @@ -417,7 +563,7 @@ /datum/spell/pulse_demon/open_upgrades/create_new_targeting() return new /datum/spell_targeting/self -/datum/spell/pulse_demon/open_upgrades/proc/calc_cost(mob/living/simple_animal/demon/pulse_demon/user, upgrade) +/datum/spell/pulse_demon/open_upgrades/proc/calc_cost(mob/living/basic/demon/pulse_demon/user, upgrade) var/cost switch(upgrade) if(PD_UPGRADE_HIJACK_SPEED) @@ -459,7 +605,7 @@ return -1 return cost -/datum/spell/pulse_demon/open_upgrades/proc/get_upgrades(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/open_upgrades/proc/get_upgrades(mob/living/basic/demon/pulse_demon/user) var/upgrades = list() for(var/upgrade in upgrade_icons) var/cost = calc_cost(user, upgrade) @@ -468,7 +614,7 @@ upgrades["[upgrade] ([format_si_suffix(cost)] APC\s)"] = upgrade_icons[upgrade] return upgrades -/datum/spell/pulse_demon/open_upgrades/AltClick(mob/living/simple_animal/demon/pulse_demon/user) +/datum/spell/pulse_demon/open_upgrades/AltClick(mob/living/basic/demon/pulse_demon/user) if(!istype(user)) return @@ -477,7 +623,7 @@ var/cost = calc_cost(user, upgrade) to_chat(user, "[upgrade] ([cost == -1 ? "Fully Upgraded" : "[format_si_suffix(cost)]J"]) - [upgrade_descs[upgrade]]") -/datum/spell/pulse_demon/open_upgrades/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target) +/datum/spell/pulse_demon/open_upgrades/try_cast_action(mob/living/basic/demon/pulse_demon/user, atom/target) var/upgrades = get_upgrades(user) if(!length(upgrades)) to_chat(user, "You have already fully upgraded everything available!") diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm b/code/modules/mob/living/basic/hostile/demons/pulse_demon.dm similarity index 79% rename from code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm rename to code/modules/mob/living/basic/hostile/demons/pulse_demon.dm index 19b69942a0d..4ae2c524d92 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm +++ b/code/modules/mob/living/basic/hostile/demons/pulse_demon.dm @@ -9,25 +9,25 @@ #define ALERT_CATEGORY_NOREGEN "pulse_noregen" /// Conversion ratio from Watt ticks to joules. -/mob/living/simple_animal/demon/pulse_demon +/mob/living/basic/demon/pulse_demon name = "pulse demon" real_name = "pulse demon" desc = "A strange electrical apparition that lives in wires." - gender = NEUTER - speak_chance = 20 damage_coeff = list(BRUTE = 0.25, BURN = 0.5, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) // Pulse demons take reduced damage from most sources. Use ions - emote_hear = list("vibrates", "sizzles") - speak_emote = list("modulates") + speak_emote = list("modulates", "vibrates", "sizzles") icon_state = "pulsedem" icon_living = "pulsedem" icon_dead = "pulsedem" - response_help = "reaches their hand into" - response_disarm = "pushes their hand through" - response_harm = "punches their fist through" - deathmessage = "fizzles out into faint sparks, leaving only a slight trail of smoke..." + response_help_continuous = "reaches their hand into" + response_help_simple = "reach their hand into" + response_disarm_continuous = "pushes their hand through" + response_disarm_simple = "push their hand through" + response_harm_continuous = "punches their fist through" + response_harm_simple = "punches their fist through" + death_message = "fizzles out into faint sparks, leaving only a slight trail of smoke..." atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) // "I don't need air, just MORE POWER!" unsuitable_atmos_damage = 0 level = 1 @@ -40,7 +40,8 @@ mob_size = MOB_SIZE_TINY density = FALSE - attacktext = "electrocutes" + attack_verb_continuous = "electrocutes" + attack_verb_simple = "electrocute" attack_sound = "sparks" harm_intent_damage = 0 melee_damage_lower = 0 @@ -125,18 +126,18 @@ /// Reference to the APC currently being hijacked. var/obj/machinery/power/apc/apc_being_hijacked -/mob/living/simple_animal/demon/pulse_demon/wizard +/mob/living/basic/demon/pulse_demon/wizard name = "Empowered Pulse Demon" real_name = "Empowered Pulse Demon" desc = "A strange electrical apparition that lives in wires. This one appears more charged than usual." -/mob/living/simple_animal/demon/pulse_demon/wizard/Initialize(mapload) +/mob/living/basic/demon/pulse_demon/wizard/Initialize(mapload) . = ..() src.apcs_remaining = 20 src.maxcharge = 500 KJ src.charge = 100 KJ -/mob/living/simple_animal/demon/pulse_demon/Initialize(mapload) +/mob/living/basic/demon/pulse_demon/Initialize(mapload) . = ..() if(!mapload) name += " ([rand(100, 999)])" @@ -174,18 +175,17 @@ whisper_action.button_icon_state = "pulse_whisper" whisper_action.background_icon_state = "bg_pulsedemon" -/mob/living/simple_animal/demon/pulse_demon/proc/deleted_handler(our_demon, force) +/mob/living/basic/demon/pulse_demon/proc/deleted_handler(our_demon, force) SIGNAL_HANDLER // assume normal deletion if we're on a turf, otherwise deletion could be inherited from loc if(force || isnull(loc) || isturf(loc)) return FALSE - // if we did actually die, simple_animal/death will set del_on_death to FALSE before calling qdel - if(!del_on_death) + if(!(basic_mob_flags & DEL_ON_DEATH)) return FALSE exit_to_turf() return TRUE -/mob/living/simple_animal/demon/pulse_demon/proc/cable_updated_handler(SSdcs, turf/T) +/mob/living/basic/demon/pulse_demon/proc/cable_updated_handler(SSdcs, turf/T) SIGNAL_HANDLER if(cable_images[T]) var/list/turf_images = cable_images[T] @@ -201,11 +201,11 @@ cable_images[T] += cable_image client?.images += cable_image -/mob/living/simple_animal/demon/pulse_demon/proc/apc_deleted_handler(obj/machinery/power/apc/A, force) +/mob/living/basic/demon/pulse_demon/proc/apc_deleted_handler(obj/machinery/power/apc/A, force) SIGNAL_HANDLER hijacked_apcs -= A -/mob/living/simple_animal/demon/pulse_demon/Destroy() +/mob/living/basic/demon/pulse_demon/Destroy() cable_images.Cut() apc_images.Cut() @@ -221,17 +221,17 @@ return ..() -/mob/living/simple_animal/demon/pulse_demon/Login() +/mob/living/basic/demon/pulse_demon/Login() . = ..() update_cableview() -/mob/living/simple_animal/demon/pulse_demon/proc/make_pulse_antagonist(demon) +/mob/living/basic/demon/pulse_demon/proc/make_pulse_antagonist(demon) SIGNAL_HANDLER mind.assigned_role = SPECIAL_ROLE_DEMON mind.special_role = SPECIAL_ROLE_DEMON give_objectives() -/mob/living/simple_animal/demon/pulse_demon/vv_edit_var(var_name, var_value) +/mob/living/basic/demon/pulse_demon/vv_edit_var(var_name, var_value) switch(var_name) if("glow_color") update_glow() @@ -241,7 +241,7 @@ return TRUE return ..() -/mob/living/simple_animal/demon/pulse_demon/forceMove(atom/destination) +/mob/living/basic/demon/pulse_demon/forceMove(atom/destination) var/old_location = loc . = ..() current_weapon = null @@ -252,12 +252,12 @@ if(istype(old_location, /obj/item/stock_parts/cell)) var/obj/item/stock_parts/cell/C = old_location // only set rigged if there are no remaining demons in the cell - C.rigged = !(locate(/mob/living/simple_animal/demon/pulse_demon) in old_location) + C.rigged = !(locate(/mob/living/basic/demon/pulse_demon) in old_location) if(istype(loc, /obj/item/stock_parts/cell)) var/obj/item/stock_parts/cell/C = loc C.rigged = FALSE -/mob/living/simple_animal/demon/pulse_demon/proc/give_objectives() +/mob/living/basic/demon/pulse_demon/proc/give_objectives() if(!mind) return mind.wipe_memory() @@ -274,7 +274,7 @@ to_chat(src, chat_box_red(greeting.Join("
"))) SSticker.mode.traitors |= mind -/mob/living/simple_animal/demon/pulse_demon/proc/give_spells() +/mob/living/basic/demon/pulse_demon/proc/give_spells() AddSpell(new /datum/spell/pulse_demon/open_upgrades) AddSpell(new /datum/spell/pulse_demon/cycle_camera) AddSpell(new /datum/spell/pulse_demon/toggle/penetrating_shock(strong_shocks)) @@ -287,7 +287,7 @@ AddSpell(new /datum/spell/pulse_demon/remotehijack) AddSpell(new /datum/spell/pulse_demon/remotedrain) -/mob/living/simple_animal/demon/pulse_demon/get_status_tab_items() +/mob/living/basic/demon/pulse_demon/get_status_tab_items() var/list/status_tab_data = ..() . = status_tab_data status_tab_data[++status_tab_data.len] = list("Energy:", "[format_si_suffix(charge)]J") @@ -298,13 +298,13 @@ status_tab_data[++status_tab_data.len] = list("Drain Rate:", "[format_si_suffix(power_drain_rate)]W") status_tab_data[++status_tab_data.len] = list("Hijack Time:", "[hijack_time / 10] seconds") -/mob/living/simple_animal/demon/pulse_demon/dust() +/mob/living/basic/demon/pulse_demon/dust() return death() -/mob/living/simple_animal/demon/pulse_demon/gib() +/mob/living/basic/demon/pulse_demon/gib() return death() -/mob/living/simple_animal/demon/pulse_demon/death() +/mob/living/basic/demon/pulse_demon/death() var/turf/T = get_turf(src) do_sparks(rand(2, 4), FALSE, src) . = ..() @@ -315,7 +315,7 @@ playsound(T, pick(hurt_sounds), 30, TRUE) SEND_SIGNAL(src, COMSIG_MOB_DEATH) -/mob/living/simple_animal/demon/pulse_demon/proc/exit_to_turf() +/mob/living/basic/demon/pulse_demon/proc/exit_to_turf() var/turf/T = get_turf(src) current_power = null update_controlling_area() @@ -329,7 +329,7 @@ S.do_toggle(can_exit_cable) to_chat(src, "Your self-sustaining ability has automatically enabled itself to prevent death from having no connection!") -/mob/living/simple_animal/demon/pulse_demon/proc/update_controlling_area(reset = FALSE) +/mob/living/basic/demon/pulse_demon/proc/update_controlling_area(reset = FALSE) var/area/prev = controlling_area if(reset || current_power == null) controlling_area = null @@ -349,10 +349,10 @@ S.action.build_all_button_icons() // can enter an apc at all? -/mob/living/simple_animal/demon/pulse_demon/proc/is_valid_apc(obj/machinery/power/apc/A) +/mob/living/basic/demon/pulse_demon/proc/is_valid_apc(obj/machinery/power/apc/A) return istype(A) && !(A.stat & BROKEN) && !A.shorted -/mob/living/simple_animal/demon/pulse_demon/Move(newloc) +/mob/living/basic/demon/pulse_demon/Move(newloc) var/obj/machinery/power/new_power = locate(/obj/machinery/power) in newloc var/obj/structure/cable/new_cable = locate(/obj/structure/cable) in newloc @@ -429,7 +429,7 @@ if(!ispulsedemon(user)) return ..() - var/mob/living/simple_animal/demon/pulse_demon/demon = user + var/mob/living/basic/demon/pulse_demon/demon = user var/turf/T = get_turf(src) var/turf/T2 = get_step(T, dir) if(demon.can_exit_cable || locate(/obj/structure/cable) in T2) @@ -439,7 +439,7 @@ if(isapc(src)) demon.update_controlling_area(TRUE) -/mob/living/simple_animal/demon/pulse_demon/proc/adjust_charge(amount, adjust_max = FALSE) +/mob/living/basic/demon/pulse_demon/proc/adjust_charge(amount, adjust_max = FALSE) if(!amount) return FALSE if(adjust_max) @@ -468,27 +468,27 @@ // 2.5 at 50000 // 3 at 600000 etc -/mob/living/simple_animal/demon/pulse_demon/proc/update_glow() +/mob/living/basic/demon/pulse_demon/proc/update_glow() var/range = charge / 200000 range = clamp(range, 1.5, 5) set_light(range, 2, glow_color) -/mob/living/simple_animal/demon/pulse_demon/proc/drain_APC(obj/machinery/power/apc/A, multiplier = 1) +/mob/living/basic/demon/pulse_demon/proc/drain_APC(obj/machinery/power/apc/A, multiplier = 1) if(A.being_hijacked) return PULSEDEMON_SOURCE_DRAIN_INVALID - //CELLRATE is the conversion ratio between a watt tick and powercell energy storage units + // CELLRATE is the conversion ratio between a watt tick and powercell energy storage units var/amount_to_drain = clamp(A.cell.charge / GLOB.CELLRATE, 0, power_drain_rate * WATT_TICK_TO_JOULE * multiplier) A.cell.use(min(amount_to_drain * GLOB.CELLRATE, maxcharge - charge)) // calculated seperately because the apc charge multiplier shouldn't affect the actual consumption return adjust_charge(amount_to_drain * PULSEDEMON_APC_CHARGE_MULTIPLIER) -/mob/living/simple_animal/demon/pulse_demon/proc/drain_SMES(obj/machinery/power/smes/S, multiplier = 1) - //CELLRATE is the conversion ratio between a watt tick and powercell energy storage units. +/mob/living/basic/demon/pulse_demon/proc/drain_SMES(obj/machinery/power/smes/S, multiplier = 1) + // CELLRATE is the conversion ratio between a watt tick and powercell energy storage units. var/amount_to_drain = clamp(S.charge / GLOB.CELLRATE, 0, power_drain_rate * WATT_TICK_TO_JOULE * multiplier * PULSEDEMON_SMES_DRAIN_MULTIPLIER) var/drained = adjust_charge(amount_to_drain) S.charge -= drained * GLOB.CELLRATE return drained -/mob/living/simple_animal/demon/pulse_demon/Life(seconds, times_fired) +/mob/living/basic/demon/pulse_demon/Life(seconds, times_fired) . = ..() var/got_power = FALSE @@ -532,12 +532,12 @@ if(--regen_lock == 0) clear_alert(ALERT_CATEGORY_NOREGEN) -/mob/living/simple_animal/demon/pulse_demon/proc/gen_speech_name() +/mob/living/basic/demon/pulse_demon/proc/gen_speech_name() . = "" for(var/i = 1 to 10) . += pick("!", "@", "#", "$", "%", "^", "&", "*") -/mob/living/simple_animal/demon/pulse_demon/say(message, verb, sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE) +/mob/living/basic/demon/pulse_demon/say(message, verb, sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE) if(client && check_mute(client.ckey, MUTE_IC)) to_chat(src, "You cannot speak in IC (Muted).") return FALSE @@ -602,16 +602,15 @@ name = real_name return TRUE - emote("me", message = "[pick(emote_hear)]") return TRUE -/mob/living/simple_animal/demon/pulse_demon/update_runechat_msg_location() +/mob/living/basic/demon/pulse_demon/update_runechat_msg_location() if(istype(loc, /obj/machinery/hologram/holopad)) runechat_msg_location = loc.UID() else return ..() -/mob/living/simple_animal/demon/pulse_demon/visible_message(message, self_message, blind_message, chat_message_type) +/mob/living/basic/demon/pulse_demon/visible_message(message, self_message, blind_message, chat_message_type) // overriden because pulse demon is quite often in non-turf locs, and /mob/visible_message acts differently there for(var/mob/M as anything in get_mobs_in_view(7, src, ai_eyes = AI_EYE_INCLUDE)) if(M.see_invisible < invisibility) @@ -621,10 +620,10 @@ msg = self_message M.show_message(msg, EMOTE_VISIBLE, blind_message, EMOTE_AUDIBLE, chat_message_type = MESSAGE_TYPE_LOCALCHAT) -/mob/living/simple_animal/demon/pulse_demon/has_internal_radio_channel_access(mob/user, list/req_one_accesses) +/mob/living/basic/demon/pulse_demon/has_internal_radio_channel_access(mob/user, list/req_one_accesses) return has_access(list(), req_one_accesses, get_all_accesses()) -/mob/living/simple_animal/demon/pulse_demon/proc/try_hijack_apc(obj/machinery/power/apc/A, remote = FALSE) +/mob/living/basic/demon/pulse_demon/proc/try_hijack_apc(obj/machinery/power/apc/A, remote = FALSE) // one APC per pulse demon, one pulse demon per APC, no duplicate APCs if(!is_valid_apc(A) || (A in hijacked_apcs) || apc_being_hijacked || A.being_hijacked) return FALSE @@ -644,12 +643,12 @@ // Basically this proc gives you more max charge per apc you have hijacked // Looks weird but it gets the job done -/mob/living/simple_animal/demon/pulse_demon/proc/calc_maxcharge(hijacked_apcs) +/mob/living/basic/demon/pulse_demon/proc/calc_maxcharge(hijacked_apcs) if(!hijacked_apcs) // No APCs hijacked? No extra charge return 100000 return 100000 + (hijacked_apcs * 25000) -/mob/living/simple_animal/demon/pulse_demon/proc/finish_hijack_apc(obj/machinery/power/apc/A, remote = FALSE) +/mob/living/basic/demon/pulse_demon/proc/finish_hijack_apc(obj/machinery/power/apc/A, remote = FALSE) var/image/apc_image = image('icons/obj/power.dmi', A, "apcemag", ABOVE_LIGHTING_LAYER, A.dir) apc_image.plane = ABOVE_LIGHTING_PLANE LAZYADD(apc_images[get_turf(A)], apc_image) @@ -666,7 +665,7 @@ var/distance = 0 strengthen_cables(T, distance) -/mob/living/simple_animal/demon/pulse_demon/proc/strengthen_cables(turf/T, distance) +/mob/living/basic/demon/pulse_demon/proc/strengthen_cables(turf/T, distance) distance += 1 for(var/obj/structure/cable/cable in T.contents) if(cable.strengthened == TRUE) @@ -683,24 +682,24 @@ var/turf/next_turf = get_turf(cable) strengthen_cables(next_turf, distance) -/mob/living/simple_animal/demon/pulse_demon/proc/on_atom_entered(datum/source, atom/movable/entered) +/mob/living/basic/demon/pulse_demon/proc/on_atom_entered(datum/source, atom/movable/entered) SIGNAL_HANDLER // COMSIG_ATOM_ENTERED try_cross_shock(entered) -/mob/living/simple_animal/demon/pulse_demon/proc/on_movable_moved(datum/source, old_location, direction, forced) +/mob/living/basic/demon/pulse_demon/proc/on_movable_moved(datum/source, old_location, direction, forced) SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED if(is_under_tile()) return for(var/mob/living/mob in loc) try_shock_mob(mob) -/mob/living/simple_animal/demon/pulse_demon/proc/try_cross_shock(atom/movable/A) +/mob/living/basic/demon/pulse_demon/proc/try_cross_shock(atom/movable/A) if(!isliving(A) || is_under_tile()) return var/mob/living/L = A try_shock_mob(L) -/mob/living/simple_animal/demon/pulse_demon/proc/try_shock_mob(mob/living/L, siemens_coeff = 1) +/mob/living/basic/demon/pulse_demon/proc/try_shock_mob(mob/living/L, siemens_coeff = 1) var/dealt = 0 if(current_cable && current_cable.powernet && current_cable.powernet.available_power) // returns used energy, not damage dealt, but ez conversion with /20 @@ -719,12 +718,12 @@ add_attack_logs(src, L, "shocked ([dealt] damage)") -/mob/living/simple_animal/demon/pulse_demon/proc/is_under_tile() +/mob/living/basic/demon/pulse_demon/proc/is_under_tile() var/turf/T = get_turf(src) return T.intact || HAS_TRAIT(T, TRAIT_TURF_COVERED) // cable (and hijacked APC) view helper -/mob/living/simple_animal/demon/pulse_demon/proc/update_cableview() +/mob/living/basic/demon/pulse_demon/proc/update_cableview() if(!client) return @@ -760,7 +759,7 @@ LAZYADD(apc_images[apc_turf], apc_image) client.images += apc_image -/mob/living/simple_animal/demon/pulse_demon/proc/handle_emp(datum/source, severity) +/mob/living/basic/demon/pulse_demon/proc/handle_emp(datum/source, severity) SIGNAL_HANDLER if(emp_debounce) return @@ -777,12 +776,12 @@ emp_debounce = TRUE addtimer(VARSET_CALLBACK(src, emp_debounce, FALSE), 0.1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) -/mob/living/simple_animal/demon/pulse_demon/proc/try_attack_mob(mob/living/L) +/mob/living/basic/demon/pulse_demon/proc/try_attack_mob(mob/living/L) if(!is_under_tile() && L != src) do_attack_animation(L) try_shock_mob(L) -/mob/living/simple_animal/demon/pulse_demon/UnarmedAttack(atom/A) +/mob/living/basic/demon/pulse_demon/UnarmedAttack(atom/A) if(isliving(A)) try_attack_mob(A) else if(isitem(A) && !is_under_tile()) @@ -793,20 +792,20 @@ adjust_charge(min(C.charge, power_drain_rate)) visible_message("[src] touches [O] and drains its power!", "You touch [O] and drain it's power!") -/mob/living/simple_animal/demon/pulse_demon/attack_hand(mob/living/carbon/human/M) +/mob/living/basic/demon/pulse_demon/attack_hand(mob/living/carbon/human/M) if(is_under_tile()) to_chat(M, "You can't interact with something that's under the floor!") return switch(M.intent) if(INTENT_HELP) - visible_message("[M] [response_help] [src].") + visible_message("[M] [response_help_continuous] [src].") if(INTENT_DISARM, INTENT_GRAB) - visible_message("[M] [response_disarm] [src].") + visible_message("[M] [response_disarm_continuous] [src].") if(INTENT_HELP) - visible_message("[M] [response_harm] [src].") + visible_message("[M] [response_harm_continuous] [src].") try_attack_mob(M) -/mob/living/simple_animal/demon/pulse_demon/attack_by(obj/item/O, mob/living/user, params) +/mob/living/basic/demon/pulse_demon/attack_by(obj/item/O, mob/living/user, params) if(..()) return FINISH_ATTACK @@ -824,63 +823,63 @@ try_shock_mob(user, O.siemens_coefficient) return FINISH_ATTACK -/mob/living/simple_animal/demon/pulse_demon/ex_act() +/mob/living/basic/demon/pulse_demon/ex_act() return -/mob/living/simple_animal/demon/pulse_demon/CanPass(atom/movable/mover, border_dir) +/mob/living/basic/demon/pulse_demon/CanPass(atom/movable/mover, border_dir) . = ..() if(istype(mover, /obj/projectile/ion)) return FALSE -/mob/living/simple_animal/demon/pulse_demon/bullet_act(obj/projectile/proj) +/mob/living/basic/demon/pulse_demon/bullet_act(obj/projectile/proj) if(proj.damage_type == BURN) regen_lock = max(regen_lock, 1) return ..() else visible_message("[proj] goes right through [src]!") -/mob/living/simple_animal/demon/pulse_demon/electrocute_act(shock_damage, source, siemens_coeff, flags) +/mob/living/basic/demon/pulse_demon/electrocute_act(shock_damage, source, siemens_coeff, flags) return -/mob/living/simple_animal/demon/pulse_demon/blob_act(obj/structure/blob/B) +/mob/living/basic/demon/pulse_demon/blob_act(obj/structure/blob/B) return // will likely end up dying if the blob cuts its wires anyway -/mob/living/simple_animal/demon/pulse_demon/narsie_act() +/mob/living/basic/demon/pulse_demon/narsie_act() return // you can't turn electricity into a harvester -/mob/living/simple_animal/demon/pulse_demon/get_access() +/mob/living/basic/demon/pulse_demon/get_access() return get_all_accesses() -/mob/living/simple_animal/demon/pulse_demon/IsAdvancedToolUser() +/mob/living/basic/demon/pulse_demon/IsAdvancedToolUser() return TRUE // interacting with machines -/mob/living/simple_animal/demon/pulse_demon/can_be_pulled() +/mob/living/basic/demon/pulse_demon/can_be_pulled() return FALSE -/mob/living/simple_animal/demon/pulse_demon/can_buckle() +/mob/living/basic/demon/pulse_demon/can_buckle() return FALSE -/mob/living/simple_animal/demon/pulse_demon/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) +/mob/living/basic/demon/pulse_demon/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) return -/mob/living/simple_animal/demon/pulse_demon/experience_pressure_difference(flow_x, flow_y) +/mob/living/basic/demon/pulse_demon/experience_pressure_difference(flow_x, flow_y) return // Immune to gas flow. -/mob/living/simple_animal/demon/pulse_demon/singularity_pull() +/mob/living/basic/demon/pulse_demon/singularity_pull() return -/mob/living/simple_animal/demon/pulse_demon/mob_negates_gravity() +/mob/living/basic/demon/pulse_demon/mob_negates_gravity() return TRUE -/mob/living/simple_animal/demon/pulse_demon/mob_has_gravity() +/mob/living/basic/demon/pulse_demon/mob_has_gravity() return TRUE -/mob/living/simple_animal/demon/pulse_demon/can_remote_apc_interface(obj/machinery/power/apc/ourapc) +/mob/living/basic/demon/pulse_demon/can_remote_apc_interface(obj/machinery/power/apc/ourapc) if(ourapc.hacked_by_ruin_AI || ourapc.malfai || ourapc.malfhack) return FALSE return TRUE -/mob/living/simple_animal/demon/pulse_demon/adjustHealth(amount, updating_health) +/mob/living/basic/demon/pulse_demon/adjustHealth(amount, updating_health) if(amount > 0) // This damages the pulse demon return ..() @@ -892,43 +891,6 @@ amount = round(amount, 1) return ..() -/obj/item/organ/internal/heart/demon/pulse - name = "perpetual pacemaker" - desc = "It still beats furiously, thousands of bright lights shine within it." - color = COLOR_YELLOW - -/obj/item/organ/internal/heart/demon/pulse/Initialize(mapload) - . = ..() - set_light(13, 2, "#bbbb00") - -/obj/item/organ/internal/heart/demon/pulse/attack_self__legacy__attackchain(mob/living/user) - . = ..() - user.drop_item() - insert(user) - -/obj/item/organ/internal/heart/demon/pulse/insert(mob/living/carbon/M, special, dont_remove_slot) - . = ..() - M.AddComponent(/datum/component/cross_shock, 30, 500, 2 SECONDS) - ADD_TRAIT(M, TRAIT_SHOCKIMMUNE, UNIQUE_TRAIT_SOURCE(src)) - M.set_light(3, 2, "#bbbb00") - -/obj/item/organ/internal/heart/demon/pulse/remove(mob/living/carbon/M, special) - . = ..() - REMOVE_TRAIT(M, TRAIT_SHOCKIMMUNE, UNIQUE_TRAIT_SOURCE(src)) - M.remove_light() - -/obj/item/organ/internal/heart/demon/pulse/on_life() - if(!owner) - return - for(var/obj/item/stock_parts/cell/cell_to_charge in owner.GetAllContents()) - var/newcharge = min(0.05 * cell_to_charge.maxcharge + cell_to_charge.charge, cell_to_charge.maxcharge) - if(cell_to_charge.charge < newcharge) - cell_to_charge.charge = newcharge - if(isobj(cell_to_charge.loc)) - var/obj/cell_location = cell_to_charge.loc - cell_location.update_icon() //update power meters and such - cell_to_charge.update_icon() - /atom/movable/screen/alert/pulse_nopower name = "No Power" desc = "You are not connected to a cable or machine and are losing health!" diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_interactions.dm b/code/modules/mob/living/basic/hostile/demons/pulse_demon_interactions.dm similarity index 80% rename from code/game/gamemodes/miniantags/pulsedemon/pulsedemon_interactions.dm rename to code/modules/mob/living/basic/hostile/demons/pulse_demon_interactions.dm index e1b106efac9..a43c39c4fbd 100644 --- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_interactions.dm +++ b/code/modules/mob/living/basic/hostile/demons/pulse_demon_interactions.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/demon/pulse_demon/ClickOn(atom/A, params) +/mob/living/basic/demon/pulse_demon/ClickOn(atom/A, params) if(client?.click_intercept) client.click_intercept.InterceptClickOn(src, params, A) return @@ -42,7 +42,7 @@ changeNext_click(0.1 SECONDS) // returns TRUE if any [modifier]ClickOn was called -/mob/living/simple_animal/demon/pulse_demon/proc/try_modified_click(atom/A, params) +/mob/living/basic/demon/pulse_demon/proc/try_modified_click(atom/A, params) var/list/modifiers = params2list(params) if(modifiers["middle"]) if(modifiers["shift"]) @@ -62,43 +62,43 @@ return FALSE // check area for all of these, then do AI actions -/mob/living/simple_animal/demon/pulse_demon/MiddleShiftClickOn(atom/A) +/mob/living/basic/demon/pulse_demon/MiddleShiftClickOn(atom/A) if(get_area(A) == controlling_area) A.AIShiftMiddleClick(src) -/mob/living/simple_animal/demon/pulse_demon/ShiftClickOn(atom/A) +/mob/living/basic/demon/pulse_demon/ShiftClickOn(atom/A) if(get_area(A) == controlling_area) A.AIShiftClick(src) else examinate(A) -/mob/living/simple_animal/demon/pulse_demon/AltClickOn(atom/A) +/mob/living/basic/demon/pulse_demon/AltClickOn(atom/A) if(get_area(A) == controlling_area) A.AIAltClick(src) else AltClickNoInteract(src, A) -/mob/living/simple_animal/demon/pulse_demon/CtrlClickOn(atom/A) +/mob/living/basic/demon/pulse_demon/CtrlClickOn(atom/A) if(get_area(A) == controlling_area) A.AICtrlClick(src) // for alt-click status tab -/mob/living/simple_animal/demon/pulse_demon/TurfAdjacent(turf/T) +/mob/living/basic/demon/pulse_demon/TurfAdjacent(turf/T) return get_area(T) == controlling_area || ..() // for overrides in general -/atom/proc/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/atom/proc/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) return -/obj/machinery/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) return attack_ai(user) // ai not allowed to use cams consoles -/obj/machinery/computer/security/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/computer/security/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) return attack_hand(user) // jump back into our apc -/obj/machinery/power/apc/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/power/apc/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) if(user.loc != src) user.forceMove(src) user.current_power = src @@ -106,7 +106,7 @@ else attack_ai(user) -/mob/living/simple_animal/bot/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/mob/living/simple_animal/bot/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) if(user.loc == src) return to_chat(user, "You are now inside [src]. If it is destroyed, you will be dropped onto the ground, and may die if there is no cable under you.") @@ -121,12 +121,12 @@ to_chat(user, "[src] isn't turned on!") return if(ispulsedemon(user)) - var/mob/living/simple_animal/demon/pulse_demon/demon = user + var/mob/living/basic/demon/pulse_demon/demon = user if(demon.bot_movedelay <= world.time && dir) Move(get_step(get_turf(src), dir)) demon.bot_movedelay = world.time + (BOT_STEP_DELAY * (base_speed - 1)) * ((dir in GLOB.diagonals) ? SQRT_2 : 1) -/obj/machinery/recharger/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/recharger/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) user.forceMove(src) if(!charging) to_chat(user, "There is no weapon charging. Click again to retry.") @@ -141,7 +141,7 @@ user.forceMove(charging) user.current_weapon = charging -/obj/machinery/cell_charger/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/cell_charger/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) user.forceMove(src) if(!charging) to_chat(user, "There is no cell charging. Click again to retry.") @@ -159,7 +159,7 @@ to_chat(user, "You are now inside [charging]. Click on a hijacked APC to return.") user.forceMove(charging) -/obj/machinery/recharge_station/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/recharge_station/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) user.forceMove(src) if(!isrobot(occupant)) to_chat(user, "There is no silicon-based occupant inside. Click again to retry.") @@ -177,7 +177,7 @@ return to_chat(src, "Failed to hijack [src].") -/mob/living/simple_animal/demon/pulse_demon/proc/do_hijack_robot(mob/living/silicon/robot/R) +/mob/living/basic/demon/pulse_demon/proc/do_hijack_robot(mob/living/silicon/robot/R) to_chat(src, "You are now inside [R]. Click on a hijacked APC to return.") forceMove(R) current_robot = R @@ -185,25 +185,25 @@ hijacked_robots += R to_chat(R, "TARGETING SYSTEMS HIJACKED, REPORT ALL UNWANTED ACTIVITY") -/obj/machinery/camera/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/camera/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) if(user.loc != src) user.forceMove(src) to_chat(user, "You jump towards [src]. Click on a hijacked APC to return.") // see pulse_demon/say -/obj/machinery/hologram/holopad/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/machinery/hologram/holopad/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) if(user.loc != src) user.forceMove(src) to_chat(user, "You jump towards [src]. You can now communicate via the holopad's speaker. Click on a hijacked APC to return.") -/obj/item/radio/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) +/obj/item/radio/attack_pulsedemon(mob/living/basic/demon/pulse_demon/user) if(user.loc != src) user.forceMove(src) to_chat(user, "You jump towards [src]. You can now communicate via radio. Click on a hijacked APC to return.") else attack_ai(user) -/mob/living/simple_animal/bot/proc/attack_integrated_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user, atom/A) +/mob/living/simple_animal/bot/proc/attack_integrated_pulsedemon(mob/living/basic/demon/pulse_demon/user, atom/A) if(!on) return if(Adjacent(A)) @@ -211,7 +211,7 @@ else RangedAttack(A) -/mob/living/simple_animal/bot/secbot/attack_integrated_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user, atom/A) +/mob/living/simple_animal/bot/secbot/attack_integrated_pulsedemon(mob/living/basic/demon/pulse_demon/user, atom/A) if(!on) return if(Adjacent(A)) @@ -221,7 +221,7 @@ playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50) visible_message("[src] points at [A.name]!") -/mob/living/simple_animal/bot/floorbot/attack_integrated_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user, atom/A) +/mob/living/simple_animal/bot/floorbot/attack_integrated_pulsedemon(mob/living/basic/demon/pulse_demon/user, atom/A) if(!on) return if(isfloorturf(A) && Adjacent(A)) @@ -230,7 +230,7 @@ F.break_tile_to_plating() audible_message("[src] makes an excited booping sound.") -/mob/living/simple_animal/bot/cleanbot/attack_integrated_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user, atom/A) +/mob/living/simple_animal/bot/cleanbot/attack_integrated_pulsedemon(mob/living/basic/demon/pulse_demon/user, atom/A) if(!on) return if(isfloorturf(A) && Adjacent(A)) @@ -243,7 +243,7 @@ new /obj/effect/decal/cleanable/blood/gibs(F) playsound(F, 'sound/effects/blobattack.ogg', 40, TRUE) -/mob/living/simple_animal/bot/mulebot/attack_integrated_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user, atom/A) +/mob/living/simple_animal/bot/mulebot/attack_integrated_pulsedemon(mob/living/basic/demon/pulse_demon/user, atom/A) if(!on) return if(istype(A) && Adjacent(A) && ismovable(A)) diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/modules/mob/living/basic/hostile/demons/shadow_demon.dm similarity index 65% rename from code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm rename to code/modules/mob/living/basic/hostile/demons/shadow_demon.dm index 5a75c97211a..494d138ea79 100644 --- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm +++ b/code/modules/mob/living/basic/hostile/demons/shadow_demon.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/demon/shadow +/mob/living/basic/demon/shadow name = "shadow demon" desc = "A creature that's barely tangible, you can feel its gaze piercing you." icon = 'icons/mob/mob.dmi' @@ -17,7 +17,7 @@ /// Used for fault tolerance. If you set it manually, it may fuck up shooting_grapple var. var/last_block_shadow_crawl = 0 -/mob/living/simple_animal/demon/shadow/Login() +/mob/living/basic/demon/shadow/Login() ..() var/list/L = list( "You are a shadow demon!", @@ -30,7 +30,7 @@ ) to_chat(src, chat_box_red(L.Join("
"))) -/mob/living/simple_animal/demon/shadow/Life(seconds, times_fired) +/mob/living/basic/demon/shadow/Life(seconds, times_fired) . = ..() var/lum_count = check_darkness() var/damage_mod = istype(loc, /obj/effect/dummy/slaughter) ? 0.5 : 1 @@ -41,7 +41,7 @@ else adjustBruteLoss(-20) -/mob/living/simple_animal/demon/shadow/UnarmedAttack(atom/A) +/mob/living/basic/demon/shadow/UnarmedAttack(atom/A) // Pick a random attack sound for each attack attack_sound = pick('sound/shadowdemon/shadowattack2.ogg', 'sound/shadowdemon/shadowattack3.ogg', 'sound/shadowdemon/shadowattack4.ogg') if(!ishuman(A)) @@ -52,7 +52,7 @@ if(target.stat != DEAD) return ..() - if(isLivingSSD(target) && client.send_ssd_warning(target)) //Similar to revenants, only wrap SSD targets if you've accepted the SSD warning + if(isLivingSSD(target) && client.send_ssd_warning(target)) // Similar to revenants, only wrap SSD targets if you've accepted the SSD warning return if(wrapping) @@ -71,16 +71,16 @@ target.forceMove(C) wrapping = FALSE -/mob/living/simple_animal/demon/shadow/proc/block_shadow_crawl() +/mob/living/basic/demon/shadow/proc/block_shadow_crawl() last_block_shadow_crawl = world.time block_shadow_crawl = TRUE addtimer(CALLBACK(src, PROC_REF(check_block_shadow_crawl), last_block_shadow_crawl), 10 SECONDS) -/mob/living/simple_animal/demon/shadow/proc/unblock_shadow_crawl() +/mob/living/basic/demon/shadow/proc/unblock_shadow_crawl() last_block_shadow_crawl = 0 block_shadow_crawl = FALSE -/mob/living/simple_animal/demon/shadow/proc/check_block_shadow_crawl(block_time) +/mob/living/basic/demon/shadow/proc/check_block_shadow_crawl(block_time) if(block_time == last_block_shadow_crawl) /// it means 10 seconds passed from last shadow grapple shot and it didnt unset block_shadow_crawl to_chat(src, "You feel good enough to use Shadow Crawl again.") @@ -109,7 +109,7 @@ /obj/structure/shadowcocoon/examine(mob/user) . = ..() - if(istype(user, /mob/living/simple_animal/demon/shadow)) + if(istype(user, /mob/living/basic/demon/shadow)) . += silent ? "The tendrils are idle and will not produce noise." : "The tendrils are agitated and will occasionally produce noise to lure in more prey." . += "Alt+Click to toggle whether [src] should produce noise to lure in victims." @@ -161,12 +161,12 @@ A.forceMove(loc) return..() -/mob/living/simple_animal/demon/shadow/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) +/mob/living/basic/demon/shadow/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(isliving(AM)) // when a living creature is thrown at it, dont knock it back return ..() -/mob/living/simple_animal/demon/shadow/Initialize(mapload) +/mob/living/basic/demon/shadow/Initialize(mapload) . = ..() AddSpell(new /datum/spell/fireball/shadow_grapple) var/datum/spell/bloodcrawl/shadow_crawl/S = new @@ -175,11 +175,11 @@ whisper_action.background_icon_state = "shadow_demon_bg" if(istype(loc, /obj/effect/dummy/slaughter)) S.phased = TRUE - RegisterSignal(loc, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, check_darkness)) + RegisterSignal(loc, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/living/basic/demon/shadow, check_darkness)) RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(check_darkness)) add_overlay(emissive_appearance(icon, "shadow_demon_eye_glow_overlay")) -/mob/living/simple_animal/demon/shadow/proc/check_darkness() +/mob/living/basic/demon/shadow/proc/check_darkness() var/turf/T = get_turf(src) var/lum_count = T.get_lumcount() if(lum_count > 0.2) @@ -195,98 +195,3 @@ alpha = 125 speed = 0.5 return lum_count - - -/datum/spell/fireball/shadow_grapple - name = "Shadow Grapple" - desc = "Fire one of your hands, if it hits a person it pulls them in. If you hit a structure you get pulled to the structure. Any light source hit with this will be disabled in a two tile radius." - base_cooldown = 10 SECONDS - fireball_type = /obj/projectile/magic/shadow_hand - - selection_activated_message = "You raise your hand, full of demonic energy! Left-click to cast at a target!" - selection_deactivated_message = "You re-absorb the energy...for now." - - action_background_icon_state = "shadow_demon_bg" - action_icon_state = "shadow_grapple" - sound = null - invocation_type = "none" - invocation = null - -/datum/spell/fireball/shadow_grapple/update_spell_icon() - return - -/obj/projectile/magic/shadow_hand - name = "shadow hand" - icon_state = "shadow_hand" - plane = FLOOR_PLANE - hitsound = 'sound/shadowdemon/shadowattack1.ogg' // Plays when hitting something living or a light - var/hit = FALSE - -/obj/projectile/magic/shadow_hand/pixel_move(trajectory_multiplier, hitscanning) - . = ..() - var/obj/machinery/light/floor/floor_light = locate(/obj/machinery/light/floor) in get_turf(src) - if(floor_light) - Bump(floor_light) - -/obj/projectile/magic/shadow_hand/fire(setAngle) - if(firer) - var/mob/living/simple_animal/demon/shadow/current_demon = firer - if(istype(current_demon)) - current_demon.block_shadow_crawl() - firer.Beam(src, icon_state = "grabber_beam", time = INFINITY, maxdistance = INFINITY, beam_type = /obj/effect/ebeam/floor) - return ..() - -/obj/projectile/magic/shadow_hand/on_hit(atom/target, blocked, hit_zone) - if(hit) - return - hit = TRUE // to prevent double hits from the pull - . = ..() - for(var/atom/extinguish_target in range(2, src)) - extinguish_target.extinguish_light(TRUE) - if(!isliving(target)) - if(isshadowdemon(firer)) - firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10, callback = CALLBACK(firer, TYPE_PROC_REF(/mob/living/simple_animal/demon/shadow, unblock_shadow_crawl))) - else - firer.throw_at(get_step(target, get_dir(target, firer)), 50, 10) - else - unblock_shadowdemon_crawl() - if(!.) - return - else - var/mob/living/L = target - L.Immobilize(2 SECONDS) - L.apply_damage(40, BRUTE, BODY_ZONE_CHEST) - L.throw_at(get_step(firer, get_dir(firer, target)), 50, 10) - -/obj/projectile/magic/shadow_hand/Destroy() - if(!hit) - unblock_shadowdemon_crawl() - return ..() - -/obj/projectile/magic/shadow_hand/proc/unblock_shadowdemon_crawl() - var/mob/living/simple_animal/demon/shadow/current_demon = firer - if(istype(current_demon)) - current_demon.unblock_shadow_crawl() - -/obj/effect/ebeam/floor - plane = FLOOR_PLANE - -/obj/item/organ/internal/heart/demon/shadow - name = "heart of darkness" - desc = "It still beats furiously, emitting an aura of fear." - color = COLOR_BLACK - -/obj/item/organ/internal/heart/demon/shadow/attack_self__legacy__attackchain(mob/living/user) - . = ..() - user.drop_item() - insert(user) - -/obj/item/organ/internal/heart/demon/shadow/insert(mob/living/carbon/M, special = 0) - . = ..() - if(M.mind) - M.mind.AddSpell(new /datum/spell/fireball/shadow_grapple) - -/obj/item/organ/internal/heart/demon/shadow/remove(mob/living/carbon/M, special = 0) - . = ..() - if(M.mind) - M.mind.RemoveSpell(/datum/spell/fireball/shadow_grapple) diff --git a/code/modules/mob/living/basic/hostile/demons/slaughter_demon.dm b/code/modules/mob/living/basic/hostile/demons/slaughter_demon.dm new file mode 100644 index 00000000000..71bb4706047 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/demons/slaughter_demon.dm @@ -0,0 +1,168 @@ +/// The Monster + +/mob/living/basic/demon/slaughter + name = "slaughter demon" + real_name = "slaughter demon" + desc = "A large, menacing creature covered in armored black scales. You should run." + maxHealth = 240 + health = 240 + icon = 'icons/mob/mob.dmi' + icon_state = "daemon" + icon_living = "daemon" + var/boost = 0 + var/feast_sound = 'sound/misc/demon_consume.ogg' + var/devoured = 0 + var/list/consumed_mobs = list() + + var/list/nearby_mortals = list() + var/cooldown = 0 + var/gorecooldown = 0 + var/vialspawned = FALSE + loot = list(/obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic, /obj/item/organ/internal/heart/demon/slaughter) + var/playstyle_string = "You are the Slaughter Demon, a terrible creature from another existence. You have a single desire: to kill. \ + You may use the blood crawl icon when on blood pools to travel through them, appearing and dissapearing from the station at will. \ + Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to feast. \ + You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. " + death_message = "screams in anger as it collapses into a puddle of viscera!" + +/mob/living/basic/demon/slaughter/New() + ..() + remove_from_all_data_huds() + ADD_TRAIT(src, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat") + var/datum/spell/bloodcrawl/bloodspell = new + AddSpell(bloodspell) + if(istype(loc, /obj/effect/dummy/slaughter)) + bloodspell.phased = TRUE + addtimer(CALLBACK(src, PROC_REF(attempt_objectives)), 5 SECONDS) + +/mob/living/basic/demon/slaughter/Life(seconds, times_fired) + ..() + if(boost < world.time) + speed = 1 + else + speed = 0 + +/mob/living/basic/demon/slaughter/proc/attempt_objectives() + if(mind) + var/list/messages = list() + messages.Add(playstyle_string) + messages.Add("You are not currently in the same plane of existence as the station. Use the blood crawl action at a blood pool to manifest.") + SEND_SOUND(src, sound('sound/misc/demon_dies.ogg')) + if(!vialspawned) + SSticker.mode.traitors |= mind + mind.add_mind_objective(/datum/objective/slaughter) + mind.add_mind_objective(/datum/objective/demon_fluff) + messages.Add(mind.prepare_announce_objectives(FALSE)) + + messages.Add("For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Slaughter_Demon)") + to_chat(src, chat_box_red(messages.Join("
"))) + +/obj/effect/decal/cleanable/blood/innards + icon = 'icons/obj/surgery.dmi' + icon_state = "innards" + base_icon = 'icons/obj/surgery.dmi' + icon_state = "innards" + random_icon_states = list("innards") + name = "pile of viscera" + desc = "A repulsive pile of guts and gore." + weightless_icon = 'icons/obj/surgery.dmi' + +/mob/living/basic/demon/slaughter/Destroy() + // Only execute the below if we successfully died + for(var/mob/living/M in consumed_mobs) + REMOVE_TRAIT(M, TRAIT_UNREVIVABLE, "demon") + release_consumed(M) + . = ..() + +/mob/living/basic/demon/slaughter/proc/release_consumed(mob/living/M) + M.forceMove(get_turf(src)) + +// Midround slaughter demon, less tanky +/mob/living/basic/demon/slaughter/lesser + maxHealth = 170 + health = 170 + +// Cult slaughter demon +/// Summoned as part of the cult objective "Bring the Slaughter" +/mob/living/basic/demon/slaughter/cult + name = "harbinger of the slaughter" + real_name = "harbinger of the Slaughter" + desc = "An awful creature from beyond the realms of madness." + maxHealth = 540 + health = 540 + melee_damage_upper = 60 + melee_damage_lower = 60 + environment_smash = ENVIRONMENT_SMASH_RWALLS // Smashes through EVERYTHING - r-walls included + faction = list("cult") + playstyle_string = "You are a Harbinger of the Slaughter. Brought forth by the servants of Nar'Sie, you have a single purpose: slaughter the heretics \ + who do not worship your master. You may use the ability 'Blood Crawl' near a pool of blood to enter it and become incorporeal. Using the ability again near a blood pool will allow you \ + to emerge from it. You are fast, powerful, and almost invincible. By dragging a dead or unconscious body into a blood pool with you, you will consume it after a time and fully regain \ + your health. You may use the ability 'Sense Victims' in your Cultist tab to locate a random, living heretic." + +/mob/living/basic/demon/slaughter/cult/New() + ..() + spawn(5) + var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/basic/demon/slaughter/cult) + if(!length(demon_candidates)) + visible_message("[src] disappears in a flash of red light!") + qdel(src) + return + if(QDELETED(src)) // Just in case + return + var/mob/M = pick(demon_candidates) + var/mob/living/basic/demon/slaughter/cult/S = src + if(!M || !M.client) + visible_message("[src] disappears in a flash of red light!") + qdel(src) + return + var/client/C = M.client + + S.key = C.key + dust_if_respawnable(M) + S.mind.assigned_role = "Harbinger of the Slaughter" + S.mind.special_role = "Harbinger of the Slaughter" + to_chat(S, playstyle_string) + S.mind.add_antag_datum(/datum/antagonist/cultist) + var/datum/spell/sense_victims/SV = new + AddSpell(SV) + + S.mind.add_mind_objective(/datum/objective/cult_slaughter) + var/list/messages = S.mind.prepare_announce_objectives(FALSE) + to_chat(S, chat_box_red(messages.Join("
"))) + +/mob/living/basic/demon/slaughter/laughter + // The laughter demon! It's everyone's best friend! It just wants to hug + // them so much, it wants to hug everyone at once! + name = "laughter demon" + real_name = "laughter demon" + desc = "A large, adorable creature covered in armor with pink bows." + speak_emote = list("giggles", "titters", "chuckles", "gaffaws", "laughs") + response_help_continuous = "hugs" + response_help_simple = "hug" + attack_verb_continuous = "wildly tickles" + attack_verb_simple = "wildly tickles" + maxHealth = 215 + health = 215 + melee_damage_lower = 25 + melee_damage_upper = 25 + playstyle_string = "You are the Laughter Demon, an adorable creature from another existence. You have a single desire: to hug and tickle. \ + You may use the blood crawl icon when on blood pools to travel through them, appearing and dissapearing from the station at will. \ + Pulling a dead or critical mob while you enter a pool will pull them in with you, allowing you to hug them. \ + You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. \ + (You should be attacking people on harm intent, and not nuzzling them.)" + + attack_sound = 'sound/items/bikehorn.ogg' + feast_sound = 'sound/spookoween/scary_horn2.ogg' + death_sound = 'sound/misc/sadtrombone.ogg' + + icon_state = "bowmon" + icon_living = "bowmon" + death_message = "fades out, as all of its friends are released from its prison of hugs." + loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"}) + +/mob/living/basic/demon/slaughter/laughter/release_consumed(mob/living/M) + if(M.revive()) + M.grab_ghost(force = TRUE) + playsound(get_turf(src), feast_sound, 50, TRUE, -1) + to_chat(M, "You leave [src]'s warm embrace, and feel ready to take on the world.") + ..(M) diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm index 82ff3460ea1..407d0cf2b6a 100644 --- a/code/modules/mob/living/silicon/robot/robot_mob.dm +++ b/code/modules/mob/living/silicon/robot/robot_mob.dm @@ -1007,7 +1007,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( C.install(W) var/been_hijacked = FALSE - for(var/mob/living/simple_animal/demon/pulse_demon/demon in cell) + for(var/mob/living/basic/demon/pulse_demon/demon in cell) if(!been_hijacked) demon.do_hijack_robot(src) been_hijacked = TRUE diff --git a/code/modules/power/apc/apc.dm b/code/modules/power/apc/apc.dm index ef8460035db..f46a15a871c 100644 --- a/code/modules/power/apc/apc.dm +++ b/code/modules/power/apc/apc.dm @@ -253,7 +253,7 @@ "[user] inserts [used] into [src].", "You insert [used] into [src]." ) - for(var/mob/living/simple_animal/demon/pulse_demon/demon in cell) + for(var/mob/living/basic/demon/pulse_demon/demon in cell) demon.forceMove(src) demon.current_power = src if(!being_hijacked) // First come, first serve! diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm index 27cc3fdfda5..b1e4b312359 100644 --- a/code/modules/tgui/states/default.dm +++ b/code/modules/tgui/states/default.dm @@ -52,7 +52,7 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new) /mob/living/basic/revenant/default_can_use_topic(src_object) return UI_UPDATE -/mob/living/simple_animal/demon/pulse_demon/default_can_use_topic(src_object) +/mob/living/basic/demon/pulse_demon/default_can_use_topic(src_object) . = shared_ui_interaction(src_object) if(. < UI_INTERACTIVE) return diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm index ea16eaf3d84..8fbf00542ab 100644 --- a/code/modules/tgui/states/physical.dm +++ b/code/modules/tgui/states/physical.dm @@ -19,7 +19,7 @@ GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new) /mob/proc/physical_can_use_topic(src_object) return UI_CLOSE -/mob/living/simple_animal/demon/pulse_demon/physical_can_use_topic(src_object) +/mob/living/basic/demon/pulse_demon/physical_can_use_topic(src_object) return UI_UPDATE /mob/living/physical_can_use_topic(src_object) diff --git a/paradise.dme b/paradise.dme index 4530a2b0d5d..0bbf9e40a70 100644 --- a/paradise.dme +++ b/paradise.dme @@ -542,6 +542,7 @@ #include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\connect_range.dm" #include "code\datums\components\corpse_description.dm" +#include "code\datums\components\cross_shock_component.dm" #include "code\datums\components\cult_held_body.dm" #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\debris.dm" @@ -923,9 +924,6 @@ #include "code\game\gamemodes\miniantags\abduction\machinery\dispenser.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\experiment.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\pad.dm" -#include "code\game\gamemodes\miniantags\demons\demon.dm" -#include "code\game\gamemodes\miniantags\demons\shadow_demon\shadow_demon.dm" -#include "code\game\gamemodes\miniantags\demons\slaughter_demon\slaughter.dm" #include "code\game\gamemodes\miniantags\guardian\guardian.dm" #include "code\game\gamemodes\miniantags\guardian\host_actions.dm" #include "code\game\gamemodes\miniantags\guardian\types\assassin.dm" @@ -944,12 +942,6 @@ #include "code\game\gamemodes\miniantags\morph\spells\open_vent.dm" #include "code\game\gamemodes\miniantags\morph\spells\pass_airlock.dm" #include "code\game\gamemodes\miniantags\morph\spells\reproduce.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\cross_shock_component.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\pulsedemon.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\pulsedemon_abilities.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\pulsedemon_event.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\pulsedemon_interactions.dm" -#include "code\game\gamemodes\miniantags\pulsedemon\pulsedemon_objectives.dm" #include "code\game\gamemodes\miniantags\tourist\tourist_arrivals.dm" #include "code\game\gamemodes\miniantags\tourist\tourist_objectives.dm" #include "code\game\gamemodes\nuclear\nuclear.dm" @@ -2135,6 +2127,7 @@ #include "code\modules\events\meteors_event.dm" #include "code\modules\events\money_spam.dm" #include "code\modules\events\prison_break.dm" +#include "code\modules\events\pulsedemon_event.dm" #include "code\modules\events\radiation_storm_event.dm" #include "code\modules\events\revenant_spawn_event.dm" #include "code\modules\events\rogue_drones.dm" @@ -2523,6 +2516,14 @@ #include "code\modules\mob\living\basic\hostile\alien\alien_mob_maid.dm" #include "code\modules\mob\living\basic\hostile\alien\alien_mob_queen.dm" #include "code\modules\mob\living\basic\hostile\alien\alien_mob_sentinel.dm" +#include "code\modules\mob\living\basic\hostile\demons\demon.dm" +#include "code\modules\mob\living\basic\hostile\demons\demon_loot.dm" +#include "code\modules\mob\living\basic\hostile\demons\demon_objectives.dm" +#include "code\modules\mob\living\basic\hostile\demons\demon_powers.dm" +#include "code\modules\mob\living\basic\hostile\demons\pulse_demon.dm" +#include "code\modules\mob\living\basic\hostile\demons\pulse_demon_interactions.dm" +#include "code\modules\mob\living\basic\hostile\demons\shadow_demon.dm" +#include "code\modules\mob\living\basic\hostile\demons\slaughter_demon.dm" #include "code\modules\mob\living\basic\hostile\bees\bee_ai_behaviors.dm" #include "code\modules\mob\living\basic\hostile\bees\bee_ai_subtree.dm" #include "code\modules\mob\living\basic\hostile\bees\bees.dm" diff --git a/tools/ci/check_simplemob_additions.py b/tools/ci/check_simplemob_additions.py index 41c193f93ea..2b3aa743c8e 100644 --- a/tools/ci/check_simplemob_additions.py +++ b/tools/ci/check_simplemob_additions.py @@ -45,14 +45,6 @@ BURNDOWN_LIST = { "/mob/living/simple_animal/bot/secbot/griefsky/toy", "/mob/living/simple_animal/bot/secbot/ofitser", "/mob/living/simple_animal/bot/secbot/pingsky", - "/mob/living/simple_animal/demon", - "/mob/living/simple_animal/demon/pulse_demon", - "/mob/living/simple_animal/demon/pulse_demon/wizard", - "/mob/living/simple_animal/demon/shadow", - "/mob/living/simple_animal/demon/slaughter", - "/mob/living/simple_animal/demon/slaughter/cult", - "/mob/living/simple_animal/demon/slaughter/laughter", - "/mob/living/simple_animal/demon/slaughter/lesser", "/mob/living/simple_animal/drone", "/mob/living/simple_animal/hostile", "/mob/living/simple_animal/hostile/ancient_robot_leg",