diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index cd7cf7f0478c..dd23bde986cd 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -155,6 +155,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isconstruct(A) (istype(A, /mob/living/simple_animal/hostile/construct)) +#define isfauna(A) (istype(A, /mob/living/simple_animal/hostile/asteroid)) + #define ismegafauna(A) (istype(A, /mob/living/simple_animal/hostile/megafauna)) #define isclown(A) (istype(A, /mob/living/simple_animal/hostile/retaliate/clown)) diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 03c257d94d1d..aa8e0a39220c 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -2,6 +2,7 @@ #define INVESTIGATE_ATMOS "atmos" #define INVESTIGATE_BOTANY "botany" #define INVESTIGATE_CARGO "cargo" +#define INVESTIGATE_DEATHS "deaths" #define INVESTIGATE_EXPERIMENTOR "experimentor" #define INVESTIGATE_GRAVITY "gravity" #define INVESTIGATE_RECORDS "records" diff --git a/code/datums/elements/content_barfer.dm b/code/datums/elements/content_barfer.dm new file mode 100644 index 000000000000..0a9d05796631 --- /dev/null +++ b/code/datums/elements/content_barfer.dm @@ -0,0 +1,27 @@ +/** + * Content Barfer; which expels the contents of a mob when it dies, or is transformed + * + * Used for morphs! + */ +/datum/element/content_barfer + argument_hash_start_idx = 2 + +/datum/element/content_barfer/Attach(datum/target, tally_string) + . = ..() + + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignals(target, list(COMSIG_LIVING_DEATH, COMSIG_LIVING_ON_WABBAJACKED), PROC_REF(barf_contents)) + +/datum/element/content_barfer/Detach(datum/target) + UnregisterSignal(target, list(COMSIG_LIVING_DEATH, COMSIG_LIVING_ON_WABBAJACKED)) + return ..() + +/datum/element/content_barfer/proc/barf_contents(mob/living/target) +// SIGNAL_HANDLER + + for(var/atom/movable/barfed_out in target) + barfed_out.forceMove(target.loc) + if(prob(90)) + step(barfed_out, pick(GLOB.alldirs)) diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index 5ba81ca9faa7..3c1f275ccb94 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -18,7 +18,7 @@ var/preview_outfit_behind = /datum/outfit/nuclear_operative /datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override) - add_team_hud(mob_override || owner.current) + add_team_hud(mob_override || owner.current, /datum/antagonist/nukeop) ADD_TRAIT(owner, TRAIT_DISK_VERIFIER, NUKEOP_TRAIT) /datum/antagonist/nukeop/remove_innate_effects(mob/living/mob_override) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 31cb70d3e407..07ffce414661 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -78,11 +78,9 @@ ..() initialize_outfits() -/datum/action/chameleon_outfit/IsAvailable(feedback = FALSE) +/datum/action/chameleon_outfit/Grant(mob/user) if(syndicate) - if(!is_syndicate(owner)) - HideFrom(owner) - return is_syndicate(owner) + owner_has_control = is_syndicate(user) return ..() /datum/action/chameleon_outfit/proc/initialize_outfits() @@ -157,10 +155,13 @@ if(!M.chameleon_item_actions) M.chameleon_item_actions = list(src) var/datum/action/chameleon_outfit/O = new /datum/action/chameleon_outfit() + O.syndicate = syndicate O.Grant(M) else M.chameleon_item_actions |= src - ..() + if(syndicate) + owner_has_control = is_syndicate(M) + return ..() /datum/action/item_action/chameleon/change/Remove(mob/M) if(M && (M == owner)) @@ -168,7 +169,7 @@ if(!LAZYLEN(M.chameleon_item_actions)) var/datum/action/chameleon_outfit/O = locate(/datum/action/chameleon_outfit) in M.actions qdel(O) - ..() + return ..() /datum/action/item_action/chameleon/change/proc/initialize_disguises() name = "Change [chameleon_name] Appearance" @@ -183,14 +184,11 @@ var/chameleon_item_name = "[initial(I.name)] ([initial(I.icon_state)])" chameleon_list[chameleon_item_name] = I - /datum/action/item_action/chameleon/change/proc/select_look(mob/user) - var/obj/item/picked_item - var/picked_name - picked_name = input("Select [chameleon_name] to change into", "Chameleon [chameleon_name]", picked_name) as null|anything in chameleon_list + var/picked_name = tgui_input_list(user, "Select [chameleon_name] to change into", "Chameleon [chameleon_name]", chameleon_list) if(!picked_name) return - picked_item = chameleon_list[picked_name] + var/obj/item/picked_item = chameleon_list[picked_name] if(!picked_item) return update_look(user, picked_item) diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index bb096851f20b..da20086e3925 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -139,8 +139,7 @@ /obj/item/clothing/gloves/bracer/cuffs/equipped(mob/living/user, slot) . = ..() - if(ishuman(user) && slot == ITEM_SLOT_GLOVES) - swipe_ability = new(user) + if(ishuman(user) && (slot & ITEM_SLOT_GLOVES)) swipe_ability.Grant(user) /obj/item/clothing/gloves/bracer/cuffs/dropped(mob/living/user) @@ -149,65 +148,61 @@ /datum/action/cooldown/swipe //you stupid name = "Swipe" - desc = "Swipe at a target area, dealing damage to heal yourself. Creatures take 60 damage while people and cyborgs take 20 damage. Living creatures hit with this ability will heal the user for 13 brute/burn/poison while dead ones heal for 20 and get butchered, while killing a creature with a swipe will heal the user for 33. People and cyborgs hit will heal for 5." + desc = "Swipe at a target area, dealing damage to heal yourself. \ + Creatures take 60 damage while people and cyborgs take 20 damage. \ + Living creatures hit with this ability will heal the user for 13 brute/burn/poison while dead ones heal for 20 and get butchered, \ + while killing a creature with a swipe will heal the user for 33. \ + People and cyborgs hit will heal for 5." background_icon_state = "bg_demon" button_icon = 'icons/mob/actions/actions_items.dmi' button_icon_state = "cuff" ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_target.dmi' + click_to_activate = TRUE + check_flags = AB_CHECK_HANDS_BLOCKED | AB_CHECK_CONSCIOUS + cooldown_time = 10 SECONDS /datum/action/cooldown/swipe/Remove(mob/living/user) unset_click_ability(user) return ..() -/datum/action/cooldown/swipe/Trigger(mob/living/carbon/user) - if(!isliving(owner)) - return FALSE - if(user.handcuffed) - to_chat(user, span_danger("You can't attack while handcuffed!")) +/datum/action/cooldown/swipe/IsAvailable(feedback = FALSE) + if(!iscarbon(owner)) return FALSE return ..() -/datum/action/cooldown/swipe/InterceptClickOn(mob/living/caller, params, atom/target) +/datum/action/cooldown/swipe/Activate(mob/living/target) . = ..() - var/turf/open/T = get_turf(target) - var/mob/living/L = target - if(!.) - return - if(owner.stat) - unset_click_ability(caller) - return - if(!COOLDOWN_FINISHED(src, next_use_time)) - to_chat(owner, span_warning("Your cuffs aren't ready to do that yet. Give them some time to recharge!")) - return - if(!istype(T)) - return - if(!(T in range(9, caller))) - to_chat(caller, warning("The target is too far!")) - return - new /obj/effect/temp_visual/bubblegum_hands/rightpaw(T) - new /obj/effect/temp_visual/bubblegum_hands/rightthumb(T) - to_chat(L, span_userdanger("Claws reach out from the floor and maul you!")) - to_chat(owner, "You summon claws at [L]'s location!") - L.visible_message(span_warning("[caller] rends [L]!")) - for(L in range(0,T)) - playsound(T, 'sound/magic/demon_attack1.ogg', 80, 5, -1) - if(isanimal(L)) - if(L.stat != DEAD) - L.adjustBruteLoss(60) + var/turf/open/target_turf = get_turf(target) + var/mob/living/carbon/caller = owner + if(!istype(target_turf)) + return FALSE + if(!(target_turf in range(9, owner))) + to_chat(owner, warning("The target is too far!")) + return FALSE + new /obj/effect/temp_visual/bubblegum_hands/rightpaw(target_turf) + new /obj/effect/temp_visual/bubblegum_hands/rightthumb(target_turf) + to_chat(target, span_userdanger("Claws reach out from the floor and maul you!")) + to_chat(owner, "You summon claws at [target]'s location!") + target.visible_message(span_warning("[owner] rends [target]!")) + for(target in range(0, target_turf)) + playsound(target_turf, 'sound/magic/demon_attack1.ogg', 80, TRUE, -1) + if(isanimal(target)) + if(target.stat != DEAD) + target.adjustBruteLoss(60) caller.adjustBruteLoss(-13) caller.adjustFireLoss(-13) caller.adjustToxLoss(-13) - if(L.stat == DEAD) - to_chat(caller, span_notice("You kill [L], healing yourself more!")) - if(L.stat == DEAD) - L.gib() + if(target.stat == DEAD) + to_chat(caller, span_notice("You kill [target], healing yourself more!")) + if(target.stat == DEAD) + target.gib() to_chat(caller, span_notice("You're able to consume the body entirely!")) caller.adjustBruteLoss(-20) caller.adjustFireLoss(-20) caller.adjustToxLoss(-20) - if(iscarbon(L)) - L.adjustBruteLoss(20) + if(iscarbon(target)) + target.adjustBruteLoss(20) caller.adjustBruteLoss(-5) caller.adjustFireLoss(-5) caller.adjustToxLoss(-5) @@ -216,7 +211,7 @@ return TRUE /datum/action/cooldown/swipe/proc/cooldown_over() - to_chat(usr, (span_notice("You're ready to swipe again!"))) + owner.balloon_alert(owner, "ready to swipe!") /obj/item/clothing/gloves/gauntlets name = "concussive gauntlets" diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 9608d7e0e78d..734a62134211 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1288,10 +1288,10 @@ GLOBAL_LIST_EMPTY(aide_list) log_combat(user, L, "took out a blood contract on", src) qdel(src) -#define COOLDOWN 150 -#define COOLDOWN_HUMAN 100 -#define COOLDOWN_ANIMAL 60 -#define COOLDOWN_SPLASH 100 +#define COOLDOWN_ATTACK_HUMAN (10 SECONDS) +#define COOLDOWN_ATTACK_ANIMAL (6 SECONDS) +#define COOLDOWN_SPLASH (10 SECONDS) +#define COOLDOWN_REACH (15 SECONDS) /datum/action/item_action/visegrip name = "Vise Grip" @@ -1316,9 +1316,9 @@ GLOBAL_LIST_EMPTY(aide_list) item_state = "knuckles" w_class = WEIGHT_CLASS_SMALL force = 18 - var/next_reach = 0 - var/next_splash = 0 - var/next_knuckle = 0 + COOLDOWN_DECLARE(next_reach) + COOLDOWN_DECLARE(next_splash) + COOLDOWN_DECLARE(next_knuckle) var/splash_range = 9 var/fauna_damage_bonus = 32 var/fauna_damage_type = BRUTE @@ -1326,66 +1326,65 @@ GLOBAL_LIST_EMPTY(aide_list) actions_types = list(/datum/action/item_action/reach, /datum/action/item_action/visegrip) /obj/item/melee/knuckles/afterattack(mob/living/target, mob/living/user, proximity) - var/mob/living/L = target - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) - L.apply_damage(fauna_damage_bonus,fauna_damage_type) + if(!istype(target)) + return + if(ismegafauna(target) || isfauna(target)) + target.apply_damage(fauna_damage_bonus, fauna_damage_type) if(proximity) - if(L.has_status_effect(STATUS_EFFECT_KNUCKLED)) - L.apply_status_effect(/datum/status_effect/roots) + if(target.has_status_effect(STATUS_EFFECT_KNUCKLED)) + target.apply_status_effect(/datum/status_effect/roots) return - if(next_knuckle > world.time) + if(!COOLDOWN_FINISHED(src, next_knuckle)) to_chat(user, span_warning("The knuckles aren't ready to mark yet.")) return else - L.apply_status_effect(STATUS_EFFECT_KNUCKLED) - if(ishuman(L)) - next_knuckle = world.time + COOLDOWN_HUMAN - return - next_knuckle = world.time + COOLDOWN_ANIMAL + target.apply_status_effect(STATUS_EFFECT_KNUCKLED) + COOLDOWN_START(src, next_knuckle, (ishuman(target) ? COOLDOWN_ATTACK_HUMAN : COOLDOWN_ATTACK_ANIMAL)) /obj/item/melee/knuckles/attack_self(mob/user) - var/turf/T = get_turf(user) - if(next_splash > world.time) + var/turf/user_turf = get_turf(user) + if(!COOLDOWN_FINISHED(src, next_splash)) to_chat(user, span_warning("You can't do that yet!")) return user.visible_message(span_warning("[user] splashes blood from [user.p_their()] knuckles!")) - playsound(T, 'sound/effects/splat.ogg', 80, 5, -1) + playsound(user_turf, 'sound/effects/splat.ogg', 80, TRUE, -1) for(var/i = 0 to splash_range) - if(T) - new /obj/effect/decal/cleanable/blood(T) - T = get_step(T,user.dir) - next_splash = world.time + COOLDOWN_SPLASH + if(user_turf) + new /obj/effect/decal/cleanable/blood(user_turf) + user_turf = get_step(user_turf, user.dir) + COOLDOWN_START(src, next_splash, COOLDOWN_SPLASH) /obj/item/melee/knuckles/ui_action_click(mob/living/user, action) - var/mob/living/U = user if(istype(action, /datum/action/item_action/reach)) - if(next_reach > world.time) - to_chat(U, span_warning("You can't do that yet!")) + if(!COOLDOWN_FINISHED(src, next_reach)) + to_chat(user, span_warning("You can't do that yet!")) return var/valid_reaching = FALSE - for(var/mob/living/L in view(7, U)) - if(L == U) + for(var/mob/living/target in view(7, user)) + if(target == user) continue - for(var/obj/effect/decal/cleanable/B in range(0,L)) - if(istype(B, /obj/effect/decal/cleanable/blood )|| istype(B, /obj/effect/decal/cleanable/trail_holder)) + for(var/obj/effect/decal/cleanable/decal in range(0, target)) + if(istype(decal, /obj/effect/decal/cleanable/blood )|| istype(decal, /obj/effect/decal/cleanable/trail_holder)) valid_reaching = TRUE - L.apply_status_effect(STATUS_EFFECT_KNUCKLED) + target.apply_status_effect(STATUS_EFFECT_KNUCKLED) if(!valid_reaching) - to_chat(U, span_warning("There's nobody to use this on!")) + to_chat(user, span_warning("There's nobody to use this on!")) return - next_reach = world.time + COOLDOWN - else if(istype(action, /datum/action/item_action/visegrip)) + COOLDOWN_START(src, next_reach, COOLDOWN_REACH) + if(istype(action, /datum/action/item_action/visegrip)) var/valid_casting = FALSE - for(var/mob/living/L in view(8, U)) - if(L.has_status_effect(STATUS_EFFECT_KNUCKLED)) + for(var/mob/living/target in view(8, user)) + if(target.has_status_effect(STATUS_EFFECT_KNUCKLED)) valid_casting = TRUE - L.apply_status_effect(/datum/status_effect/roots) + target.apply_status_effect(/datum/status_effect/roots) if(!valid_casting) - to_chat(U, span_warning("There's nobody to use this on!")) + to_chat(user, span_warning("There's nobody to use this on!")) return - #undef COOLDOWN - #undef COOLDOWN_HUMAN - #undef COOLDOWN_ANIMAL + +#undef COOLDOWN_ATTACK_HUMAN +#undef COOLDOWN_ATTACK_ANIMAL +#undef COOLDOWN_SPLASH +#undef COOLDOWN_REACH //Colossus /obj/structure/closet/crate/necropolis/colossus name = "colossus chest" diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm index cf5920623c94..901e938c2f60 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -269,30 +269,26 @@ Doesn't work on other aliens/AI.*/ build_all_button_icons() on_who.update_icons() +// We do this in InterceptClickOn() instead of Activate() +// because we use the click parameters for aiming the projectile +// (or something like that) /datum/action/cooldown/alien/acid/neurotoxin/InterceptClickOn(mob/living/caller, params, atom/target) . = ..() if(!.) unset_click_ability(caller, refund_cooldown = FALSE) return FALSE - // We do this in InterceptClickOn() instead of Activate() - // because we use the click parameters for aiming the projectile - // (or something like that) - var/turf/user_turf = caller.loc - var/turf/target_turf = get_step(caller, target.dir) // Get the tile infront of the move, based on their direction - if(!isturf(target_turf)) - return FALSE - - var/modifiers = params2list(params) +// var/modifiers = params2list(params) caller.visible_message( span_danger("[caller] spits neurotoxin!"), span_alertalien("You spit neurotoxin."), ) + var/obj/item/projectile/bullet/neurotoxin/neurotoxin = new /obj/item/projectile/bullet/neurotoxin(caller.loc) - neurotoxin.preparePixelProjectile(target, caller, modifiers) + neurotoxin.preparePixelProjectile(target, caller, params) neurotoxin.firer = caller neurotoxin.fire() - caller.newtonian_move(get_dir(target_turf, user_turf)) + caller.newtonian_move(get_dir(target, caller)) return TRUE // Has to return TRUE, otherwise is skipped. diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 94be5b849179..cac8016d1bd2 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -1349,18 +1349,18 @@ deactive_msg = span_notice("You stop channeling your telecrystal core.") spell_requirements = NONE -/datum/action/cooldown/spell/pointed/phase_jump/InterceptClickOn(atom/target, params, mob/living/user) +/datum/action/cooldown/spell/pointed/phase_jump/InterceptClickOn(mob/living/user, params, atom/target) . = ..() if(!.) return FALSE - var/turf/T = get_turf(target) + var/turf/target_turf = get_turf(target) var/phasein = /obj/effect/temp_visual/dir_setting/cult/phase var/phaseout = /obj/effect/temp_visual/dir_setting/cult/phase/out var/obj/spot1 = new phaseout(get_turf(user), user.dir) - user.forceMove(T) + owner.forceMove(target_turf) var/obj/spot2 = new phasein(get_turf(user), user.dir) - spot1.Beam(spot2,"tentacle",time=20) - user.visible_message("[user] phase shifts away!", span_warning("You shift around the space around you.")) + spot1.Beam(spot2, "tentacle", time=2 SECONDS) + user.visible_message(span_danger("[user] phase shifts away!"), span_warning("You shift around the space around you.")) return TRUE /datum/action/cooldown/spell/pointed/phase_jump/is_valid_target(atom/target) @@ -1371,7 +1371,7 @@ var/area/AU = get_area(owner) var/area/AT = get_area(T) if(AT.noteleport || AU.noteleport) - owner.balloon_alert(owner, "something omnious prevents your teleport!") + owner.balloon_alert(owner, "can't teleport there!") return FALSE return TRUE diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 20b2af928b14..c8c7ba3c8dfc 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -14,6 +14,7 @@ GLOBAL_VAR_INIT(permadeath, FALSE) spread_bodyparts(no_brain, no_organs) spawn_gibs(no_bodyparts) + SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, no_brain, no_organs, no_bodyparts) qdel(src) /mob/living/proc/gib_animation() @@ -28,6 +29,15 @@ GLOBAL_VAR_INIT(permadeath, FALSE) /mob/living/proc/spread_bodyparts() return +/** + * This is the proc for turning a mob into ash. + * Dusting robots does not eject the MMI, so it's a bit more powerful than gib() + * + * Arguments: + * * just_ash - If TRUE, ash will spawn where the mob was, as opposed to remains + * * drop_items - Should the mob drop their items before dusting? + * * force - Should this mob be FORCABLY dusted? +*/ /mob/living/dust(just_ash, drop_items, force) death(TRUE) @@ -49,6 +59,9 @@ GLOBAL_VAR_INIT(permadeath, FALSE) /mob/living/death(gibbed) + if(stat == DEAD) + return FALSE + set_stat(DEAD) unset_machine() timeofdeath = world.time diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 741bdda17dee..ef2e6f163446 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -67,7 +67,6 @@ . = ..() if(!.) //dead walk(src, 0) //stops walking - return 0 /mob/living/simple_animal/hostile/handle_automated_action() if(AIStatus == AI_OFF) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 2b9a43d6662e..98929ffadfff 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -857,18 +857,22 @@ Difficulty: Very Hard escape.Grant(holder_animal) remove_verb(holder_animal, /mob/living/verb/pulled) -/obj/structure/closet/stasis/dump_contents(kill = 1) +/obj/structure/closet/stasis/dump_contents(kill = TRUE) STOP_PROCESSING(SSobj, src) - for(var/mob/living/L in src) - REMOVE_TRAIT(L, TRAIT_MUTE, STASIS_MUTE) - L.status_flags &= ~GODMODE - L.notransform = 0 + for(var/mob/living/possessor in src) + REMOVE_TRAIT(possessor, TRAIT_MUTE, STASIS_MUTE) + possessor.status_flags &= ~GODMODE + possessor.notransform = FALSE + if(kill || !isanimal(loc)) + possessor.investigate_log("has died from [src].", INVESTIGATE_DEATHS) + possessor.death(FALSE) if(holder_animal) - holder_animal.mind.transfer_to(L) + possessor.forceMove(get_turf(holder_animal)) + holder_animal.mind.transfer_to(possessor) + possessor.mind.grab_ghost(force = TRUE) holder_animal.gib() - if(kill || !isanimal(loc)) - L.death(0) - ..() + return ..() + return ..() /obj/structure/closet/stasis/emp_act() return diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index 7643131c38b4..63386e08f8c5 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -38,7 +38,7 @@ icon_dead = "spacedragon_dead" health_doll_icon = "spacedragon" obj_damage = 50 - see_in_dark = 7 //Yogs + see_in_dark = 7 //yogs lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE //yogs environment_smash = ENVIRONMENT_SMASH_NONE flags_1 = PREVENT_CONTENTS_EXPLOSION_1 @@ -60,6 +60,7 @@ minbodytemp = 0 maxbodytemp = 1500 faction = list("carp") + speak_emote = list("gnashes") pressure_resistance = 200 /// How much endlag using Wing Gust should apply. Each use of wing gust increments this, and it decreases over time. var/tiredness = 0 @@ -84,6 +85,7 @@ /mob/living/simple_animal/hostile/space_dragon/Initialize(mapload) . = ..() + AddElement(/datum/element/content_barfer) small_sprite = new small_sprite.Grant(src) RegisterSignal(small_sprite, COMSIG_ACTION_TRIGGER, PROC_REF(add_dragon_overlay)) @@ -100,7 +102,7 @@ /mob/living/simple_animal/hostile/space_dragon/Life(seconds_per_tick = SSMOBS_DT, times_fired) . = ..() - tiredness = max(tiredness - (0.5), 0) + tiredness = max(tiredness - (0.5 * seconds_per_tick), 0) for(var/mob/living/consumed_mob in src) if(consumed_mob.stat == DEAD) continue @@ -187,7 +189,7 @@ * If the name is invalid, will re-prompt the dragon until a proper name is chosen. */ /mob/living/simple_animal/hostile/space_dragon/proc/dragon_name() - var/chosen_name = stripped_input(src, "What would you like your name to be?", "Choose Your Name", real_name, MAX_NAME_LEN) //CHANGE THIS WHEN PROC HOLDERS GET MERGED + var/chosen_name = sanitize_name(reject_bad_text(tgui_input_text(src, "What would you like your name to be?", "Choose Your Name", real_name, MAX_NAME_LEN))) if(!chosen_name) to_chat(src, span_warning("Not a valid name, please try again.")) dragon_name() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 7e56049b2801..f86f94a1fd51 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -289,9 +289,9 @@ new /obj/effect/temp_visual/gib_animation/animal(loc, icon_gib) /mob/living/simple_animal/say_mod(input, list/message_mods = list()) - if(speak_emote && speak_emote.len) + if(length(speak_emote)) verb_say = pick(speak_emote) - . = ..() + return ..() /mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE, is_keybind = FALSE) if(stat) diff --git a/code/modules/spells/spell_types/shapeshift/_shapeshift.dm b/code/modules/spells/spell_types/shapeshift/_shapeshift.dm index 6d26ad8f8591..51267a0a1653 100644 --- a/code/modules/spells/spell_types/shapeshift/_shapeshift.dm +++ b/code/modules/spells/spell_types/shapeshift/_shapeshift.dm @@ -127,7 +127,7 @@ priority_announce("We detected a pipe blockage around [get_area(get_turf(cast_on))], please dispatch someone to investigate.", "Central Command") // Gib our caster, and make sure to leave nothing behind // (If we leave something behind, it'll drop on the turf of the pipe, which is kinda wrong.) -// cast_on.investigate_log("has been gibbed by shapeshifting while ventcrawling.", INVESTIGATE_DEATHS) + cast_on.investigate_log("has been gibbed by shapeshifting while ventcrawling.", INVESTIGATE_DEATHS) cast_on.gib(TRUE, TRUE, TRUE) /// Callback for the radial that allows the user to choose their species. diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index c17e8a987ccd..c458f00a3f15 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -84,6 +84,7 @@ #include "anchored_mobs.dm" #include "component_tests.dm" +#include "dragon_expiration.dm" #include "dynamic_ruleset_sanity.dm" #include "focus_only_tests.dm" #include "map_landmarks.dm" diff --git a/code/modules/unit_tests/dragon_expiration.dm b/code/modules/unit_tests/dragon_expiration.dm new file mode 100644 index 000000000000..7b36b5762911 --- /dev/null +++ b/code/modules/unit_tests/dragon_expiration.dm @@ -0,0 +1,28 @@ +/// Unit test for the contents barfer element +/datum/unit_test/contents_barfer + +/datum/unit_test/contents_barfer/Run() + var/mob/living/simple_animal/hostile/space_dragon/dragon_time = allocate(/mob/living/simple_animal/hostile/space_dragon) + var/mob/living/carbon/human/to_be_consumed = allocate(/mob/living/carbon/human/consistent) + TEST_ASSERT(dragon_time.eat(to_be_consumed), "The space dragon failed to consume the dummy!") + TEST_ASSERT_EQUAL(to_be_consumed.loc, dragon_time, "The dummy's location, after being successfuly consumed, was not within the space dragon's contents!") + dragon_time.death() + TEST_ASSERT(isturf(to_be_consumed.loc), "After dying, the space dragon did not eject the consumed dummy content barfer element.") + +/// Unit tests that the space dragon - when its rift expires and it gets qdel'd - doesn't delete all the mobs it has eaten +/datum/unit_test/space_dragon_expiration + +/datum/unit_test/space_dragon_expiration/Run() + var/mob/living/simple_animal/hostile/space_dragon/dragon_time = allocate(/mob/living/simple_animal/hostile/space_dragon) + var/mob/living/carbon/human/to_be_consumed = allocate(/mob/living/carbon/human/consistent) + + dragon_time.mind_initialize() + var/datum/antagonist/space_dragon/dragon_antag_datum = dragon_time.mind.add_antag_datum(/datum/antagonist/space_dragon) + dragon_time.eat(to_be_consumed) + + dragon_antag_datum.riftTimer = dragon_antag_datum.maxRiftTimer + 1 + dragon_antag_datum.rift_checks() + + TEST_ASSERT(QDELETED(dragon_time), "The space dragon wasn't deleted after having its rift timer exceeded!") + TEST_ASSERT(!QDELETED(to_be_consumed), "After having its rift timer exceeded, the dragon deleted the dummy instead! The dragon should be dead prior to being deleted!") + TEST_ASSERT(isturf(to_be_consumed.loc), "After having its rift timer exceeded, the dragon did not eject the dummy! (Dummy's loc: [to_be_consumed.loc])") diff --git a/yogstation.dme b/yogstation.dme index 3271026aa276..0043c3551267 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -638,6 +638,7 @@ #include "code\datums\elements\_element.dm" #include "code\datums\elements\cleaning.dm" #include "code\datums\elements\connect_loc.dm" +#include "code\datums\elements\content_barfer.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\firestacker.dm" #include "code\datums\elements\squish.dm" diff --git a/yogstation/code/modules/guardian/abilities/major/predator.dm b/yogstation/code/modules/guardian/abilities/major/predator.dm index 033d9f2e9f11..b3c643337757 100644 --- a/yogstation/code/modules/guardian/abilities/major/predator.dm +++ b/yogstation/code/modules/guardian/abilities/major/predator.dm @@ -62,8 +62,6 @@ /datum/action/cooldown/spell/predator/cast(mob/living/user) . = ..() - if(!.) - return FALSE if (!isguardian(user)) return var/mob/living/simple_animal/hostile/guardian/G = user @@ -78,13 +76,11 @@ to_chat(G, span_notice("You didn't select anyone to track!")) return to_chat(G, span_notice("We begin to track [span_bold(prey.real_name)].[get_final_z(prey) == get_final_z(G) ? "" : " They are far away from here[G.stats.potential >= 4 ? ", on z-level [get_final_z(prey)]." : "."]"]")) - log_game("[key_name(G)] began to track [key_name(prey)] using Predator.") // why log this? Simple. Some idiot will eventually cry metacomms because someone used this ability to track them to their autistic maint base or random-ass locker. + log_game("[key_name(G)] began to track [key_name(prey)] using Predator.") // why log this? Simple. Some idiot will eventually cry metacomms because someone used this ability to track them to their autistic maint base or random-ass locker. //this post was fact-checked by real byond experts: TRUE for (var/datum/status_effect/agent_pinpointer/predator/status in G.status_effects) status.scan_target = prey status.point_to_target() - return TRUE - /atom/movable/screen/alert/status_effect/agent_pinpointer/predator name = "Predator's All-Seeing Eyes" diff --git a/yogstation/code/modules/guardian/abilities/minor/snares.dm b/yogstation/code/modules/guardian/abilities/minor/snares.dm index cd8b64ac6c7e..5bf47764d208 100644 --- a/yogstation/code/modules/guardian/abilities/minor/snares.dm +++ b/yogstation/code/modules/guardian/abilities/minor/snares.dm @@ -22,9 +22,9 @@ S.spawner = user S.name = "[get_area(snare_loc)] snare ([rand(1, 1000)])" ability.snares |= S - to_chat(user, span_bolddanger("Surveillance snare deployed!")) + user.balloon_alert(user, "snare deployed!") else - to_chat(user, span_bolddanger("You have too many snares deployed. Remove some first.")) + user.balloon_alert(user, "at snare limit!") /datum/action/guardian/disarm_snare name = "Disarm Snare" @@ -33,11 +33,11 @@ /datum/action/guardian/disarm_snare/on_use(mob/living/simple_animal/hostile/guardian/user) var/datum/guardian_ability/minor/snare/ability = user.has_ability(/datum/guardian_ability/minor/snare) - var/picked_snare = input(user, "Pick which snare to remove", "Remove Snare") as null|anything in ability.snares + var/picked_snare = tgui_input_list(user, "Pick which snare to remove", "Remove Snare", ability.snares) //kids, read your docs if (picked_snare) ability.snares -= picked_snare qdel(picked_snare) - to_chat(ability, span_bolddanger("Snare disarmed.")) + user.balloon_alert(user, "snare disarmed") // the snare