diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index a6ea566113..830a25fb0a 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -73,6 +73,7 @@ #define UI_DAMAGE_LAYER 18.2 #define BLIND_LAYER 18.3 #define CRIT_LAYER 18.4 +#define CURSE_LAYER 18.5 #define HUD_PLANE 19 #define HUD_LAYER 19 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 102653f1eb..758ff6f496 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -51,6 +51,12 @@ #define DEVIL_BODYPART "devil" /*see __DEFINES/inventory.dm for bodypart bitflag defines*/ +//Health hud screws for carbon mobs +#define SCREWYHUD_NONE 0 +#define SCREWYHUD_CRIT 1 +#define SCREWYHUD_DEAD 2 +#define SCREWYHUD_HEALTHY 3 + //Nutrition levels for humans #define NUTRITION_LEVEL_FAT 600 #define NUTRITION_LEVEL_FULL 550 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 918809c381..cc8104f98b 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -54,6 +54,12 @@ #define STATUS_EFFECT_SAWBLEED /datum/status_effect/saw_bleed //if the bleed builds up enough, takes a ton of damage +#define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse +#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured +#define CURSE_SPAWNING 2 //spawns creatures that attack the target only +#define CURSE_WASTING 4 //causes gradual damage +#define CURSE_GRASPING 8 //hands reach out from the sides of the screen, doing damage and stunning if they hit the target + ///////////// // NEUTRAL // ///////////// diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index e9bfc36b55..9031983875 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -100,6 +100,11 @@ layer = BLIND_LAYER plane = FULLSCREEN_PLANE +/obj/screen/fullscreen/curse + icon_state = "curse" + layer = CURSE_LAYER + plane = FULLSCREEN_PLANE + /obj/screen/fullscreen/impaired icon_state = "impairedoverlay" @@ -163,4 +168,4 @@ plane = LIGHTING_PLANE layer = LIGHTING_LAYER blend_mode = BLEND_ADD - show_when_dead = TRUE + show_when_dead = TRUE diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 7f70432b7a..bd46778c19 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -341,3 +341,96 @@ owner.adjustBruteLoss(bleed_damage) else new /obj/effect/temp_visual/bleed(get_turf(owner)) + +/mob/living/proc/apply_necropolis_curse(set_curse) + var/datum/status_effect/necropolis_curse/C = has_status_effect(STATUS_EFFECT_NECROPOLIS_CURSE) + if(!set_curse) + set_curse = pick(CURSE_BLINDING, CURSE_SPAWNING, CURSE_WASTING, CURSE_GRASPING) + if(QDELETED(C)) + apply_status_effect(STATUS_EFFECT_NECROPOLIS_CURSE, set_curse) + else + C.apply_curse(set_curse) + C.duration += 3000 //additional curses add 5 minutes + +/datum/status_effect/necropolis_curse + id = "necrocurse" + duration = 6000 //you're cursed for 10 minutes have fun + tick_interval = 50 + alert_type = null + var/curse_flags = NONE + var/effect_last_activation = 0 + var/effect_cooldown = 100 + var/obj/effect/temp_visual/curse/wasting_effect = new + +/datum/status_effect/necropolis_curse/on_creation(mob/living/new_owner, set_curse) + . = ..() + if(.) + apply_curse(set_curse) + +/datum/status_effect/necropolis_curse/Destroy() + if(!QDELETED(wasting_effect)) + qdel(wasting_effect) + wasting_effect = null + return ..() + +/datum/status_effect/necropolis_curse/on_remove() + remove_curse(curse_flags) + +/datum/status_effect/necropolis_curse/proc/apply_curse(set_curse) + curse_flags |= set_curse + if(curse_flags & CURSE_BLINDING) + owner.overlay_fullscreen("curse", /obj/screen/fullscreen/curse, 1) + +/datum/status_effect/necropolis_curse/proc/remove_curse(remove_curse) + if(remove_curse & CURSE_BLINDING) + owner.clear_fullscreen("curse", 50) + curse_flags &= ~remove_curse + +/datum/status_effect/necropolis_curse/tick() + if(owner.stat == DEAD) + return + if(curse_flags & CURSE_WASTING) + wasting_effect.forceMove(owner.loc) + wasting_effect.setDir(owner.dir) + wasting_effect.transform = owner.transform //if the owner has been stunned the overlay should inherit that position + wasting_effect.alpha = 255 + animate(wasting_effect, alpha = 0, time = 32) + playsound(owner, 'sound/effects/curse5.ogg', 20, 1, -1) + owner.adjustFireLoss(0.75) + if(effect_last_activation <= world.time) + effect_last_activation = world.time + effect_cooldown + if(curse_flags & CURSE_SPAWNING) + var/turf/spawn_turf + var/sanity = 10 + while(!spawn_turf && sanity) + spawn_turf = locate(owner.x + pick(rand(10, 15), rand(-10, -15)), owner.y + pick(rand(10, 15), rand(-10, -15)), owner.z) + sanity-- + if(spawn_turf) + var/mob/living/simple_animal/hostile/asteroid/curseblob/C = new (spawn_turf) + C.set_target = owner + C.GiveTarget() + if(curse_flags & CURSE_GRASPING) + var/grab_dir = turn(owner.dir, pick(-90, 90, 180, 180)) //grab them from a random direction other than the one faced, favoring grabbing from behind + var/turf/spawn_turf = get_ranged_target_turf(owner, grab_dir, 5) + if(spawn_turf) + grasp(spawn_turf) + +/datum/status_effect/necropolis_curse/proc/grasp(turf/spawn_turf) + set waitfor = FALSE + new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, owner.dir) + playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, 1, -1) + var/turf/ownerloc = get_turf(owner) + var/obj/item/projectile/curse_hand/C = new (spawn_turf) + C.current = spawn_turf + C.starting = spawn_turf + C.yo = ownerloc.y - spawn_turf.y + C.xo = ownerloc.x - spawn_turf.x + C.original = owner + C.fire() + +/obj/effect/temp_visual/curse + icon_state = "curse" + +/obj/effect/temp_visual/curse/Initialize() + . = ..() + deltimer(timerid) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index c9569a92ee..1e671bbe51 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -109,6 +109,41 @@ icon_state = "tailsweep" duration = 4 +/obj/effect/temp_visual/dir_setting/curse + icon_state = "curse" + duration = 32 + var/fades = TRUE + +/obj/effect/temp_visual/dir_setting/curse/Initialize(mapload, set_dir) + . = ..() + if(fades) + animate(src, alpha = 0, time = 32) + +/obj/effect/temp_visual/dir_setting/curse/blob + icon_state = "curseblob" + +/obj/effect/temp_visual/dir_setting/curse/grasp_portal + icon = 'icons/effects/64x64.dmi' + layer = LARGE_MOB_LAYER + pixel_y = -16 + pixel_x = -16 + duration = 32 + fades = FALSE + +/obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading + duration = 32 + fades = TRUE + +/obj/effect/temp_visual/dir_setting/curse/hand + icon_state = "cursehand" + +/obj/effect/temp_visual/dir_setting/curse/hand/Initialize(mapload, set_dir, handedness) + . = ..() + update_icon() + +/obj/item/projectile/curse_hand/update_icon() + icon_state = "[icon_state][handedness]" + /obj/effect/temp_visual/wizard name = "water" icon = 'icons/mob/mob.dmi' diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 4b92497a26..750695d2e5 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -11,11 +11,6 @@ Gunshots/explosions/opening doors/less rare audio (done) */ -#define SCREWYHUD_NONE 0 -#define SCREWYHUD_CRIT 1 -#define SCREWYHUD_DEAD 2 -#define SCREWYHUD_HEALTHY 3 - /mob/living/carbon var/image/halimage var/image/halbody diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm new file mode 100644 index 0000000000..3003d93402 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -0,0 +1,107 @@ +/mob/living/simple_animal/hostile/asteroid/curseblob + name = "curse mass" + desc = "A mass of purple... smoke?" + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "curseblob" + icon_living = "curseblob" + icon_aggro = "curseblob" + move_to_delay = 5 + vision_range = 20 + aggro_vision_range = 20 + idle_vision_range = 20 + maxHealth = 40 //easy to kill, but oh, will you be seeing a lot of them. + health = 40 + melee_damage_lower = 10 + melee_damage_upper = 10 + melee_damage_type = BURN + attacktext = "slashes" + attack_sound = 'sound/effects/curseattack.ogg' + throw_message = "passes through the smokey body of" + obj_damage = 0 + environment_smash = ENVIRONMENT_SMASH_NONE + sentience_type = SENTIENCE_BOSS + layer = LARGE_MOB_LAYER + var/doing_move_loop = FALSE + var/mob/living/set_target + var/timerid + +/mob/living/simple_animal/hostile/asteroid/curseblob/Initialize(mapload) + . = ..() + timerid = QDEL_IN(src, 600) + playsound(src, 'sound/effects/curse1.ogg', 100, 1, -1) + +/mob/living/simple_animal/hostile/asteroid/curseblob/Destroy() + new /obj/effect/temp_visual/dir_setting/curse/blob(loc, dir) + doing_move_loop = FALSE + return ..() + +/mob/living/simple_animal/hostile/asteroid/curseblob/Goto(move_target, delay, minimum_distance) + move_loop(target, delay) + +/mob/living/simple_animal/hostile/asteroid/curseblob/proc/move_loop(move_target, delay) + set waitfor = FALSE + if(doing_move_loop) + return + doing_move_loop = TRUE + if(check_for_target()) + return + while(!QDELETED(src) && doing_move_loop && isturf(loc) && !check_for_target()) + var/step_turf = get_step(src, get_dir(src, set_target)) + if(step_turf != get_turf(set_target)) + forceMove(step_turf) + sleep(delay) + doing_move_loop = FALSE + +/mob/living/simple_animal/hostile/asteroid/curseblob/proc/check_for_target() + if(QDELETED(set_target) || set_target.stat != CONSCIOUS || z != set_target.z) + qdel(src) + return TRUE + +/mob/living/simple_animal/hostile/asteroid/curseblob/GiveTarget(new_target) + if(check_for_target()) + return + new_target = set_target + . = ..() + Goto(target, move_to_delay) + +/mob/living/simple_animal/hostile/asteroid/curseblob/LoseTarget() //we can't lose our target! + if(check_for_target()) + return + +//if it's not our target, we ignore it +/mob/living/simple_animal/hostile/asteroid/curseblob/CanPass(atom/movable/mover, turf/target, height = 0) + if(mover == set_target) + return FALSE + if(istype(mover, /obj/item/projectile)) + var/obj/item/projectile/P = mover + if(P.firer == set_target) + return FALSE + return TRUE + +#define IGNORE_PROC_IF_NOT_TARGET(X) /mob/living/simple_animal/hostile/asteroid/curseblob/##X(AM) { if (AM == set_target) return ..(); } + +IGNORE_PROC_IF_NOT_TARGET(attack_hand) + +IGNORE_PROC_IF_NOT_TARGET(attack_hulk) + +IGNORE_PROC_IF_NOT_TARGET(attack_paw) + +IGNORE_PROC_IF_NOT_TARGET(attack_alien) + +IGNORE_PROC_IF_NOT_TARGET(attack_larva) + +IGNORE_PROC_IF_NOT_TARGET(attack_animal) + +IGNORE_PROC_IF_NOT_TARGET(attack_slime) + +/mob/living/simple_animal/hostile/asteroid/curseblob/bullet_act(obj/item/projectile/Proj) + if(Proj.firer != set_target) + return + return ..() + +/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L) + if(L != set_target) + return + return ..() + +#undef IGNORE_PROC_IF_NOT_TARGET diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index b7435001e4..aa0f7ffc11 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -161,6 +161,8 @@ var/turf/target_turf = get_turf(A) if(!prehit(A)) + if(forcedodge) + loc = target_turf return FALSE var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null if(permutation == -1 || forcedodge)// the bullet passes through a dense object! @@ -264,7 +266,7 @@ old_pixel_x = pixel_x_offset old_pixel_y = pixel_y_offset - if(original && (original.layer>=2.75) || ismob(original)) + if(original && (original.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER) || ismob(original)) if(loc == get_turf(original)) if(!(original in permutated)) Bump(original, 1) @@ -279,7 +281,7 @@ if((!( current ) || loc == current)) current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) step_towards(src, current) - if(original && (original.layer>=2.75) || ismob(original)) + if(original && (original.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER) || ismob(original)) if(loc == get_turf(original)) if(!(original in permutated)) Bump(original, 1) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index e6ee7af2c6..5c77baf689 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -334,3 +334,57 @@ for(var/turf/Z in range(T,power)) new /obj/effect/temp_visual/gravpush(Z) +/obj/effect/ebeam/curse_arm + name = "curse arm" + layer = LARGE_MOB_LAYER + +/obj/item/projectile/curse_hand + name = "curse hand" + icon_state = "cursehand" + hitsound = 'sound/effects/curse4.ogg' + layer = LARGE_MOB_LAYER + damage_type = BURN + damage = 10 + knockdown = 20 + speed = 2 + range = 16 + forcedodge = TRUE + var/datum/beam/arm + var/handedness = 0 + +/obj/item/projectile/curse_hand/Initialize(mapload) + . = ..() + handedness = prob(50) + update_icon() + +/obj/item/projectile/curse_hand/update_icon() + icon_state = "[icon_state][handedness]" + +/obj/item/projectile/curse_hand/fire(setAngle) + if(starting) + arm = starting.Beam(src, icon_state = "curse[handedness]", time = INFINITY, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm) + ..() + +/obj/item/projectile/curse_hand/prehit(atom/target) + if(target == original) + forcedodge = FALSE + else if(!isturf(target)) + return FALSE + return ..() + +/obj/item/projectile/curse_hand/Destroy() + if(arm) + arm.End() + arm = null + if(forcedodge) + playsound(src, 'sound/effects/curse3.ogg', 25, 1, -1) + var/turf/T = get_step(src, dir) + new/obj/effect/temp_visual/dir_setting/curse/hand(T, dir, handedness) + for(var/obj/effect/temp_visual/dir_setting/curse/grasp_portal/G in starting) + qdel(G) + new /obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading(starting, dir) + var/datum/beam/D = starting.Beam(T, icon_state = "curse[handedness]", time = 32, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm, beam_sleep_time = 1) + for(var/b in D.elements) + var/obj/effect/ebeam/B = b + animate(B, alpha = 0, time = 32) + return ..() diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi index d80b077988..6b0cf1efc9 100644 Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index 2ae375b927..71fdb96a85 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 701f2ffb19..971eb849f6 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/mob/lavaland/lavaland_monsters.dmi b/icons/mob/lavaland/lavaland_monsters.dmi index 7c41752024..3e8ecaf1ce 100644 Binary files a/icons/mob/lavaland/lavaland_monsters.dmi and b/icons/mob/lavaland/lavaland_monsters.dmi differ diff --git a/icons/mob/screen_full.dmi b/icons/mob/screen_full.dmi index 595b870a17..207f2d3e91 100644 Binary files a/icons/mob/screen_full.dmi and b/icons/mob/screen_full.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 58eed2bbf4..98ac40f0c3 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/sound/effects/curse1.ogg b/sound/effects/curse1.ogg new file mode 100644 index 0000000000..b5482db037 Binary files /dev/null and b/sound/effects/curse1.ogg differ diff --git a/sound/effects/curse2.ogg b/sound/effects/curse2.ogg new file mode 100644 index 0000000000..ab0d82246d Binary files /dev/null and b/sound/effects/curse2.ogg differ diff --git a/sound/effects/curse3.ogg b/sound/effects/curse3.ogg new file mode 100644 index 0000000000..8ba46199b9 Binary files /dev/null and b/sound/effects/curse3.ogg differ diff --git a/sound/effects/curse4.ogg b/sound/effects/curse4.ogg new file mode 100644 index 0000000000..8d4e13e3dc Binary files /dev/null and b/sound/effects/curse4.ogg differ diff --git a/sound/effects/curse5.ogg b/sound/effects/curse5.ogg new file mode 100644 index 0000000000..3525280298 Binary files /dev/null and b/sound/effects/curse5.ogg differ diff --git a/sound/effects/curse6.ogg b/sound/effects/curse6.ogg new file mode 100644 index 0000000000..ae36eff039 Binary files /dev/null and b/sound/effects/curse6.ogg differ diff --git a/sound/effects/curseattack.ogg b/sound/effects/curseattack.ogg new file mode 100644 index 0000000000..0726582f7b Binary files /dev/null and b/sound/effects/curseattack.ogg differ diff --git a/tgstation.dme b/tgstation.dme index b217beecf0..3fe775f30b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1781,6 +1781,7 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\swarmer.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\basilisk.dm" +#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\goldgrub.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\goliath.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm"