diff --git a/aurorastation.dme b/aurorastation.dme index 042fdaff08c..242250b75e7 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -118,6 +118,7 @@ #include "code\_onclick\hud\gun_mode.dm" #include "code\_onclick\hud\hud.dm" #include "code\_onclick\hud\human.dm" +#include "code\_onclick\hud\morph.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\nymph_hud.dm" #include "code\_onclick\hud\other_mobs.dm" @@ -1032,9 +1033,6 @@ #include "code\game\objects\structures\windoor_assembly.dm" #include "code\game\objects\structures\window.dm" #include "code\game\objects\structures\window_spawner.dm" -#include "code\game\objects\structures\alien\alien.dm" -#include "code\game\objects\structures\alien\node.dm" -#include "code\game\objects\structures\alien\resin.dm" #include "code\game\objects\structures\crates_lockers\closets.dm" #include "code\game\objects\structures\crates_lockers\crates.dm" #include "code\game\objects\structures\crates_lockers\largecrate.dm" @@ -1063,7 +1061,10 @@ #include "code\game\objects\structures\crates_lockers\closets\secure\secure_closets.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\security.dm" #include "code\game\objects\structures\crates_lockers\crates\gear_loadout.dm" -#include "code\game\objects\structures\stool_bed_chair_nest\alien_nests.dm" +#include "code\game\objects\structures\gore\core.dm" +#include "code\game\objects\structures\gore\nest.dm" +#include "code\game\objects\structures\gore\tendrils.dm" +#include "code\game\objects\structures\gore\wall.dm" #include "code\game\objects\structures\stool_bed_chair_nest\bed.dm" #include "code\game\objects\structures\stool_bed_chair_nest\chairs.dm" #include "code\game\objects\structures\stool_bed_chair_nest\stools.dm" @@ -2771,6 +2772,8 @@ #include "code\modules\spell_system\spells\spell_list\self\conjure\construct_spells\cultify_area.dm" #include "code\modules\spell_system\spells\spell_list\self\conjure\construct_spells\summon_pylon.dm" #include "code\modules\spell_system\spells\spell_list\self\conjure\construct_spells\summon_soulstone.dm" +#include "code\modules\spell_system\spells\spell_list\self\conjure\morph_spells\create_nest.dm" +#include "code\modules\spell_system\spells\spell_list\self\conjure\morph_spells\create_node.dm" #include "code\modules\spell_system\spells\spell_list\self\equip\holy_relic.dm" #include "code\modules\spell_system\spells\spell_list\self\equip\seed.dm" #include "code\modules\spell_system\spells\spell_list\self\equip\shield.dm" diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 3b88ac7c255..264a2513a87 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -37,6 +37,8 @@ #define LEFT 1 #define RIGHT 2 +#define FIST_ATTACK_ANIMATION -1 + // Pulse levels, very simplified. #define PULSE_NONE 0 // So !M.pulse checks would be possible. #define PULSE_SLOW 1 // <60 bpm diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 3af4962d32f..c31e8e27e0a 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -52,6 +52,7 @@ #define ui_dropbutton "EAST-4:22,SOUTH:5" #define ui_drop_throw "EAST-1:28,SOUTH+1:7" #define ui_pull_resist "EAST-2:26,SOUTH+1:7" +#define ui_morph_resist "EAST-2:26,SOUTH:5" #define ui_acti "EAST-2:26,SOUTH:5" #define ui_movi "EAST-3:24,SOUTH:5" #define ui_zonesel "EAST-1:28,SOUTH:5" diff --git a/code/_onclick/hud/morph.dm b/code/_onclick/hud/morph.dm new file mode 100644 index 00000000000..b83ccedc5ce --- /dev/null +++ b/code/_onclick/hud/morph.dm @@ -0,0 +1,30 @@ +/mob/living/simple_animal/hostile/morph/instantiate_hud(var/datum/hud/HUD) + HUD.morph_hud() + +/datum/hud/proc/morph_hud() + var/obj/screen/mov_intent = new /obj/screen/movement_intent() + mov_intent.set_dir(SOUTHWEST) + mov_intent.icon = 'icons/mob/screen/morph.dmi' + mov_intent.icon_state = (mymob.m_intent == "run" ? "running" : "walking") + move_intent = mov_intent + + mymob.healths = new /obj/screen() + mymob.healths.name = "health" + mymob.healths.icon = 'icons/mob/screen/morph.dmi' + mymob.healths.icon_state = "health0" + mymob.healths.screen_loc = ui_alien_health + + mymob.zone_sel = new /obj/screen/zone_sel() + mymob.zone_sel.icon = 'icons/mob/screen/morph.dmi' + mymob.zone_sel.cut_overlays() + mymob.zone_sel.add_overlay(image('icons/mob/zone_sel.dmi', "[mymob.zone_sel.selecting]")) + + var/obj/screen/resist = new /obj/screen() + resist.name = "resist" + resist.icon = 'icons/mob/screen/morph.dmi' + resist.icon_state = "act_resist" + resist.screen_loc = ui_morph_resist + hotkeybuttons += resist + + mymob.client.screen = null + mymob.client.screen += list(mov_intent, mymob.healths, mymob.zone_sel, resist) \ No newline at end of file diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index f52653cb44b..f56f8114eeb 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -451,8 +451,15 @@ usr.m_intent = "walk" if("walk") usr.m_intent = "run" - - update_move_icon(usr) + else if(istype(usr, /mob/living/simple_animal/hostile/morph)) + var/mob/living/simple_animal/hostile/morph/M = usr + switch(usr.m_intent) + if("run") + usr.m_intent = "walk" + if("walk") + usr.m_intent = "run" + M.update_speed() + update_move_icon(usr) // Hand slots are special to handle the handcuffs overlay /obj/screen/inventory/hand diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 16a13b8c191..f12efa31123 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -20,6 +20,9 @@ var/drytime var/dries = TRUE +/obj/effect/decal/cleanable/blood/no_dry + dries = FALSE + /obj/effect/decal/cleanable/blood/reveal_blood() if(!fluorescent) fluorescent = 1 diff --git a/code/game/objects/structures/alien/alien.dm b/code/game/objects/structures/alien/alien.dm deleted file mode 100644 index b911b997c0a..00000000000 --- a/code/game/objects/structures/alien/alien.dm +++ /dev/null @@ -1,62 +0,0 @@ -/obj/structure/alien - name = "alien thing" - desc = "There's something alien about this." - icon = 'icons/mob/npc/alien.dmi' - layer = 4 - var/health = 50 - -/obj/structure/alien/proc/healthcheck() - if(health <=0) - density = 0 - qdel(src) - return - -/obj/structure/alien/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.damage - ..() - healthcheck() - return - -/obj/structure/alien/ex_act(severity) - switch(severity) - if(1.0) - health-=50 - if(2.0) - health-=50 - if(3.0) - if (prob(50)) - health-=50 - else - health-=25 - healthcheck() - return - -/obj/structure/alien/hitby(AM as mob|obj) - ..() - visible_message("\The [src] was hit by \the [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 10 - else - tforce = AM:throwforce - playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) - health = max(0, health - tforce) - healthcheck() - ..() - return - -/obj/structure/alien/attack_generic() - attack_hand(usr) - -/obj/structure/alien/attackby(var/obj/item/W, var/mob/user) - health = max(0, health - W.force) - playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) - healthcheck() - ..() - return - -/obj/structure/alien/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group) return 0 - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density \ No newline at end of file diff --git a/code/game/objects/structures/alien/node.dm b/code/game/objects/structures/alien/node.dm deleted file mode 100644 index 49357f94c43..00000000000 --- a/code/game/objects/structures/alien/node.dm +++ /dev/null @@ -1,105 +0,0 @@ -#define NODERANGE 3 - -/obj/structure/alien/weeds - name = "weeds" - desc = "Weird purple weeds." - icon_state = "weeds" - anchored = 1 - density = 0 - health = 15 - var/obj/structure/alien/weeds/node/linked_node = null - var/list/dirs_left - -/obj/structure/alien/weeds/node - icon_state = "weednode" - name = "purple sac" - desc = "Weird purple octopus-like thing." - light_range = NODERANGE - var/node_range = NODERANGE - -/obj/structure/alien/weeds/Destroy() - STOP_PROCESSING(SSprocessing, src) - return ..() - -/obj/structure/alien/weeds/Initialize(pos, node) - . = ..() - if(istype(loc, /turf/space)) - return INITIALIZE_HINT_QDEL - linked_node = node - if(icon_state == "weeds")icon_state = pick("weeds", "weeds1", "weeds2") - - START_PROCESSING(SSprocessing, src) - - dirs_left = cardinal.Copy() - -/obj/structure/alien/weeds/process() - var/turf/U = get_turf(src) - - if (istype(U, /turf/space)) - qdel(src) - return PROCESS_KILL - - if (!dirs_left.len || !linked_node || get_dist(linked_node, src) > linked_node.node_range) - return PROCESS_KILL - - for (var/D in dirs_left) - var/turf/T = get_step(src, D) - - if (!isturf(T) || T.is_hole || T.is_space() || locate(/obj/structure/alien/weeds, T)) - dirs_left -= D - continue - - if (T.density) - continue - - var/spawn_new = TRUE - for (var/aa in T) - var/atom/A = aa - if (istype(/obj/structure/alien/weeds, A)) - dirs_left -= D - spawn_new = FALSE - break - - else if (A.density) - spawn_new = FALSE - - if (spawn_new) - new /obj/structure/alien/weeds(T, linked_node) - - -/obj/structure/alien/weeds/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if (prob(50)) - qdel(src) - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/structure/alien/weeds/attackby(var/obj/item/W, var/mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - - visible_message("\The [src] have been attacked with \the [W][(user ? " by [user]." : ".")]") - - var/damage = W.force / 4.0 - - if(W.iswelder()) - var/obj/item/weldingtool/WT = W - - if(WT.remove_fuel(0, user)) - damage = 15 - playsound(loc, 'sound/items/welder.ogg', 100, 1) - - health -= damage - healthcheck() - - -/obj/structure/alien/weeds/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > 300 + T0C) - health -= 5 - healthcheck() - -#undef NODERANGE diff --git a/code/game/objects/structures/alien/resin.dm b/code/game/objects/structures/alien/resin.dm deleted file mode 100644 index 464eacae6e9..00000000000 --- a/code/game/objects/structures/alien/resin.dm +++ /dev/null @@ -1,42 +0,0 @@ -/obj/structure/alien/resin - name = "resin" - desc = "Looks like some kind of slimy growth." - icon_state = "resin" - - density = 1 - opacity = 1 - anchored = 1 - health = 200 - -/obj/structure/alien/resin/wall - name = "resin wall" - desc = "Purple slime solidified into a wall." - icon_state = "resinwall" - -/obj/structure/alien/resin/membrane - name = "resin membrane" - desc = "Purple slime just thin enough to let light pass through." - icon_state = "resinmembrane" - opacity = 0 - health = 120 - -/obj/structure/alien/resin/New() - ..() - var/turf/T = get_turf(src) - T.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - -/obj/structure/alien/resin/Destroy() - var/turf/T = get_turf(src) - T.thermal_conductivity = initial(T.thermal_conductivity) - return ..() - -/obj/structure/alien/resin/attack_hand(var/mob/user) - if (HULK in user.mutations) - visible_message("\The [user] destroys \the [name]!") - health = 0 - else - visible_message("\The [user] claws at \the [src]!") - // Todo check attack datums. - health -= rand(5,10) - healthcheck() - return diff --git a/code/game/objects/structures/gore/core.dm b/code/game/objects/structures/gore/core.dm new file mode 100644 index 00000000000..6d0533d151f --- /dev/null +++ b/code/game/objects/structures/gore/core.dm @@ -0,0 +1,83 @@ +/obj/structure/gore + name = "alien thing" + desc = "There's something alien about this." + icon = 'icons/obj/gore_structures.dmi' + layer = ABOVE_CABLE_LAYER + 0.1 + anchored = TRUE + density = FALSE + var/destroy_message = "THE STRUCTURE collapses in on itself!" + var/maxHealth = 50 + var/health = 50 + +/obj/structure/gore/Initialize(mapload) + . = ..() + health = maxHealth + +/obj/structure/gore/examine(mob/user) + if(..(user, 2)) + var/health_div = health / maxHealth + if(health_div >= 0.9) + to_chat(user, SPAN_NOTICE("\The [src] appears completely intact.")) + else if(health_div >= 0.7) + to_chat(user, SPAN_NOTICE("\The [src] is starting to tear somewhat.")) + else if(health_div >= 0.4) + to_chat(user, SPAN_WARNING("\The [src] is starting to fall apart.")) + else + to_chat(user, SPAN_WARNING("\The [src] is barely holding itself together!")) + +/obj/structure/gore/proc/healthcheck() + if(health <= 0) + var/final_message = replacetext(destroy_message, "THE STRUCTURE", "\The [src]") + visible_message(SPAN_WARNING(final_message)) + qdel(src) + +/obj/structure/gore/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.damage + healthcheck() + return ..() + +/obj/structure/gore/ex_act(severity) + switch(severity) + if(1.0) + health -= 50 + if(2.0) + health -= 50 + if(3.0) + if(prob(50)) + health -= 50 + else + health -= 25 + healthcheck() + +/obj/structure/gore/hitby(atom/movable/AM) + . = ..() + visible_message(SPAN_WARNING("\The [src] was hit by \the [AM].")) + playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE) + var/throw_force = 0 + if(isobj(AM)) + var/obj/O = AM + throw_force = O.throwforce + else if(ismob(AM)) + throw_force = 10 + health -= throw_force + healthcheck() + +/obj/structure/gore/attack_generic() + attack_hand(usr) + +/obj/structure/gore/attackby(obj/item/W, mob/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src, W) + var/force_damage = W.force + if(W.damtype == BURN) + force_damage *= 1.25 + health -= force_damage + playsound(loc, 'sound/effects/attackblob.ogg', 80, TRUE) + healthcheck() + +/obj/structure/gore/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(air_group) + return FALSE + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density \ No newline at end of file diff --git a/code/game/objects/structures/gore/nest.dm b/code/game/objects/structures/gore/nest.dm new file mode 100644 index 00000000000..a08963b2106 --- /dev/null +++ b/code/game/objects/structures/gore/nest.dm @@ -0,0 +1,65 @@ +#define NEST_RESIST_TIME 1200 + +/obj/structure/bed/nest + name = "gore nest" + desc = "It's a gruesome pile of thick, sticky flesh shaped like a nest." + icon = 'icons/obj/gore_structures.dmi' + icon_state = "nest" + var/destroy_message = "THE STRUCTURE collapses in on itself!" + var/maxHealth = 100 + var/health = 100 + +/obj/structure/bed/nest/Initialize(mapload, new_material, new_padding_material) + . = ..() + health = maxHealth + +/obj/structure/bed/nest/examine(mob/user) + if(..(user, 2)) + var/health_div = health / maxHealth + if(health_div >= 0.9) + to_chat(user, SPAN_NOTICE("\The [src] appears completely intact.")) + else if(health_div >= 0.7) + to_chat(user, SPAN_NOTICE("\The [src] is starting to tear somewhat.")) + else if(health_div >= 0.4) + to_chat(user, SPAN_WARNING("\The [src] is starting to fall apart.")) + else + to_chat(user, SPAN_WARNING("\The [src] is barely holding itself together!")) + +/obj/structure/bed/nest/update_icon() + return + +/obj/structure/bed/nest/user_unbuckle_mob(mob/user) + if(buckled_mob?.buckled == src) + add_fingerprint(user) + if(buckled_mob != user) + buckled_mob.visible_message("[user] pulls [buckled_mob] free from the sticky flesh!", SPAN_NOTICE("[user] pulls you free from the gelatinous flesh."), SPAN_WARNING("You hear squelching...")) + unbuckle_buckled_mob(buckled_mob) + else + if(world.time <= buckled_mob.last_special + NEST_RESIST_TIME) + return + buckled_mob.last_special = world.time + buckled_mob.visible_message(SPAN_WARNING("[buckled_mob] starts struggling to break free of the sticky flesh..."), SPAN_WARNING("You struggle to break free from the gelatinous flesh..."), SPAN_WARNING("You hear squelching...")) + if(do_after(buckled_mob, NEST_RESIST_TIME, TRUE)) + unbuckle_buckled_mob(buckled_mob) + +/obj/structure/bed/nest/proc/unbuckle_buckled_mob(var/mob/buck) + if(buckled_mob == buck && buck.buckled == src) + buck.pixel_y = 0 + buck.old_y = 0 + unbuckle_mob() + +/obj/structure/bed/nest/attackby(obj/item/W, mob/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src, W) + var/force_damage = W.force + if(W.damtype == BURN) + force_damage *= 1.25 + health -= force_damage + playsound(loc, 'sound/effects/attackblob.ogg', 80, TRUE) + healthcheck() + +/obj/structure/bed/nest/proc/healthcheck() + if(health <= 0) + var/final_message = replacetext(destroy_message, "THE STRUCTURE", "\The [src]") + visible_message(SPAN_WARNING(final_message)) + qdel(src) \ No newline at end of file diff --git a/code/game/objects/structures/gore/tendrils.dm b/code/game/objects/structures/gore/tendrils.dm new file mode 100644 index 00000000000..22445ee431a --- /dev/null +++ b/code/game/objects/structures/gore/tendrils.dm @@ -0,0 +1,145 @@ +#define NODERANGE 3 + +/obj/structure/gore/tendrils + name = "bloody tendrils" + desc = "Bloody, pulsating tendrils." + icon_state = "tendril" + maxHealth = 40 + var/being_destroyed = FALSE + var/is_node = FALSE + var/obj/structure/gore/tendrils/node/linked_node = null + var/list/dirs_left + var/grow_speed = 5 SECONDS + var/last_grow = 0 + +/obj/structure/gore/tendrils/node + name = "flesh sac" + desc = "Clumped up flesh, pulsating in rhythm with the tendrils that surround it." + icon_state = "tendril_node" + density = TRUE + layer = ABOVE_CABLE_LAYER + 0.2 + maxHealth = 150 + light_range = NODERANGE + light_color = LIGHT_COLOR_EMERGENCY + is_node = TRUE + grow_speed = 3 SECONDS + var/node_range = NODERANGE + +/obj/structure/gore/tendrils/node/CanPass(atom/movable/mover, turf/target, height, air_group) + . = ..() + if(!.) + if(istype(mover, /mob/living/simple_animal/hostile/morph)) + return TRUE + if(ishuman(mover)) + var/mob/living/carbon/human/H = mover + if(H.mind.changeling) + return TRUE + +/obj/structure/gore/tendrils/Destroy() + var/turf/T = get_turf(src) + T.movement_cost = initial(T.movement_cost) + STOP_PROCESSING(SSprocessing, src) + being_destroyed = TRUE + update_neighbours() + return ..() + +/obj/structure/gore/tendrils/Initialize(pos, node) + . = ..() + if(istype(loc, /turf/space) || isopenturf(loc)) + return INITIALIZE_HINT_QDEL + + var/turf/T = get_turf(src) + T.movement_cost += 2 + if(!is_node) + linked_node = node + update_sprite() + update_neighbours() + else + linked_node = src + new /obj/structure/gore/tendrils(loc, linked_node) + + new /obj/effect/decal/cleanable/blood/no_dry(loc) + START_PROCESSING(SSprocessing, src) + dirs_left = cardinal.Copy() + +/obj/structure/gore/tendrils/proc/update_neighbours(turf/U) + if(!U) + U = loc + if(istype(U)) + for(var/dirn in cardinal) + var/turf/T = get_step(U, dirn) + if(!istype(T)) + continue + var/obj/structure/gore/tendrils/GT = locate() in T + if(GT) + GT.update_sprite() + +/obj/structure/gore/tendrils/proc/update_sprite() + var/my_dir = 0 + for(var/check_dir in cardinal) + var/turf/check = get_step(src, check_dir) + if(!istype(check)) + continue + var/obj/structure/gore/tendrils/GT = locate() in check + if(GT && !GT.being_destroyed) + my_dir |= check_dir + + if(my_dir == 15) //weeds in all four directions + icon_state = "tendril[rand(0, 15)]" + else if(my_dir == 0) //no weeds in any direction + icon_state = "tendril" + else + icon_state = "tendril_dir[my_dir]" + +/obj/structure/gore/tendrils/node/update_sprite() + for(var/obj/structure/gore/tendrils/GT in loc) + if(GT == src) + continue + GT.update_sprite() + +/obj/structure/gore/tendrils/process() + if(last_grow + grow_speed > world.time) + return + last_grow = world.time + var/turf/U = get_turf(src) + + if(istype(U, /turf/space) || isopenturf(U)) + qdel(src) + return PROCESS_KILL + + if(!length(dirs_left) || !linked_node || get_dist(linked_node, U) > linked_node.node_range) + return PROCESS_KILL + + var/D = pick(dirs_left) + var/turf/T = get_step(U, D) + + var/obj/machinery/door/MD = locate() in T + if(MD?.density) + return + var/obj/structure/simple_door/SD = locate() in T + if(SD?.density) + return + + if(!isturf(T) || T.is_hole || T.is_space() || T.density || locate(/obj/structure/gore/tendrils, T)) + dirs_left -= D + return + + dirs_left -= D + new /obj/structure/gore/tendrils(T, linked_node) + +/obj/structure/gore/tendrils/ex_act(severity) + switch(severity) + if(1.0) + health = 0 + if(2.0) + health -= maxHealth / 2 + if(3.0) + health -= maxHealth / 5 + healthcheck() + +/obj/structure/gore/tendrils/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300 + T0C) + health -= 5 + healthcheck() + +#undef NODERANGE \ No newline at end of file diff --git a/code/game/objects/structures/gore/wall.dm b/code/game/objects/structures/gore/wall.dm new file mode 100644 index 00000000000..a5d9e6450bc --- /dev/null +++ b/code/game/objects/structures/gore/wall.dm @@ -0,0 +1,40 @@ +/obj/structure/gore/floor + name = "flesh floor" + desc = "Looks like the floor was covered by some fleshlike growth." + icon_state = "flesh_floor" + maxHealth = 100 + +/obj/structure/gore/wall + name = "flesh wall" + desc = "Chunks of flesh sculpted to form an impassable wall." + icon_state = "flesh_wall" + opacity = TRUE + maxHealth = 200 + +/obj/structure/gore/wall/membrane + name = "flesh membrane" + desc = "Skin and muscle stretched just thin enough to let light pass through." + icon_state = "flesh_membrane" + opacity = FALSE + maxHealth = 120 + +/obj/structure/gore/wall/Initialize() + . = ..() + var/turf/T = get_turf(src) + T.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + +/obj/structure/gore/wall/Destroy() + var/turf/T = get_turf(src) + T.thermal_conductivity = initial(T.thermal_conductivity) + return ..() + +/obj/structure/gore/resin/attack_hand(var/mob/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src, FIST_ATTACK_ANIMATION) + if(HULK in user.mutations) + visible_message(SPAN_DANGER("\The [user] destroys \the [src]!")) + health = 0 + else + visible_message(SPAN_DANGER("\The [user] claws at \the [src]!")) + health -= rand(5, 10) + healthcheck() \ No newline at end of file diff --git a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm deleted file mode 100644 index 1f1c884deb6..00000000000 --- a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm +++ /dev/null @@ -1,54 +0,0 @@ -//Alium nests. Essentially beds with an unbuckle delay that only aliums can buckle mobs to. -#define NEST_RESIST_TIME 1200 - -/obj/structure/bed/nest - name = "alien nest" - desc = "It's a gruesome pile of thick, sticky resin shaped like a nest." - icon = 'icons/mob/npc/alien.dmi' - icon_state = "nest" - var/health = 100 - -/obj/structure/bed/nest/update_icon() - return - -/obj/structure/bed/nest/user_unbuckle_mob(mob/user as mob) - if(buckled_mob) - if(buckled_mob.buckled == src) - if(buckled_mob != user) - buckled_mob.visible_message(\ - "[user.name] pulls [buckled_mob.name] free from the sticky nest!",\ - "[user.name] pulls you free from the gelatinous resin.",\ - "You hear squelching...") - buckled_mob.pixel_y = 0 - buckled_mob.old_y = 0 - unbuckle_mob() - else - if(world.time <= buckled_mob.last_special+NEST_RESIST_TIME) - return - buckled_mob.last_special = world.time - buckled_mob.visible_message(\ - "[buckled_mob.name] struggles to break free of the gelatinous resin...",\ - "You struggle to break free from the gelatinous resin...",\ - "You hear squelching...") - spawn(NEST_RESIST_TIME) - if(user && buckled_mob && user.buckled == src) - buckled_mob.last_special = world.time - buckled_mob.pixel_y = 0 - buckled_mob.old_y = 0 - unbuckle_mob() - src.add_fingerprint(user) - return - -/obj/structure/bed/nest/attackby(obj/item/W as obj, mob/user as mob) - var/aforce = W.force - health = max(0, health - aforce) - playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) - for(var/mob/M in viewers(src, 7)) - M.show_message("[user] hits [src] with [W]!", 1) - healthcheck() - -/obj/structure/bed/nest/proc/healthcheck() - if(health <=0) - density = 0 - qdel(src) - return diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index 9e215af5bb4..044f6980b78 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -725,7 +725,7 @@ var/list/worths = list( /obj/structure/transit_tube = 80, /obj/structure/transit_tube_pod = 100, /obj/structure/toilet = 50, - /obj/structure/alien = 300, + /obj/structure/gore = 300, /obj/structure/closet = 15, /obj/structure/bed = 7, /obj/structure/holostool = 0, diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm index 81372bc1a88..767148cf47c 100644 --- a/code/modules/mob/animations.dm +++ b/code/modules/mob/animations.dm @@ -195,6 +195,8 @@ note dizziness decrements automatically in the mob's Life() proc. ..() is_floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + if(attack_item == FIST_ATTACK_ANIMATION) // only play the physical movement + return // What icon do we use for the attack? var/image/I if(attack_item) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index c5c9441721e..f6d6f1c623b 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -87,7 +87,7 @@ tally = max(0, tally-3) var/turf/T = get_turf(src) - if(T) + if(T && !mind.changeling) // changelings don't get movement costs tally += T.movement_cost tally += config.human_delay diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 559c2c1729a..95b61ee21f7 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -794,7 +794,7 @@ default behaviour is: set category = "IC" resting = !resting - to_chat(src, "You are now [resting ? "resting" : "getting up"]") + to_chat(src, "You are now [resting ? "resting" : "getting up"].") update_vision_cone() /mob/living/proc/cannot_use_vents() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 79ba8abb60d..76733c07b95 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -441,7 +441,7 @@ else resting = !resting icon_state = resting ? "[chassis]_rest" : "[chassis]" - to_chat(src, "You are now [resting ? "resting" : "getting up"]") + to_chat(src, "You are now [resting ? "resting" : "getting up"].") canmove = !resting diff --git a/code/modules/mob/living/simple_animal/hostile/morph.dm b/code/modules/mob/living/simple_animal/hostile/morph.dm index 616a5d81ef0..928dd5bc8d7 100644 --- a/code/modules/mob/living/simple_animal/hostile/morph.dm +++ b/code/modules/mob/living/simple_animal/hostile/morph.dm @@ -8,6 +8,7 @@ icon = 'icons/mob/npc/animal.dmi' icon_state = "morph" icon_living = "morph" + icon_rest = "morph_rest" icon_dead = "morph_dead" speed = 2.5 stop_automated_movement = TRUE @@ -28,12 +29,14 @@ maxHealth = 125 health = 125 + max_stamina = -1 melee_damage_lower = 12 melee_damage_upper = 16 see_in_dark = 8 see_invisible = SEE_INVISIBLE_NOLIGHTING + stop_sight_update = TRUE minbodytemp = 0 maxbodytemp = 350 @@ -51,12 +54,51 @@ var/eat_while_disguised = FALSE var/atom/movable/form = null var/morph_time = 0 - var/static/list/blacklist_typecache = typecacheof(list(/obj/screen, /obj/singularity, /mob/living/simple_animal/hostile/morph, /obj/effect)) + var/static/list/blacklist_typecache = typecacheof(list(/obj/screen, /obj/singularity, /mob/living/simple_animal/hostile/morph, /obj/effect, /obj/structure/gore)) /mob/living/simple_animal/hostile/morph/Initialize() . = ..() verbs += /mob/living/proc/ventcrawl + var/list/morph_spells = list(/spell/aoe_turf/conjure/node, /spell/aoe_turf/conjure/nest) + for(var/spell in morph_spells) + add_spell(new spell, "const_spell_ready") + +/mob/living/simple_animal/hostile/morph/Life() + . = ..() + if(stat == DEAD && healths) + healths.icon_state = "health6" + if(.) + if(healths) + switch(health / maxHealth * 100) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + + if((stat == UNCONSCIOUS || resting) && locate(/obj/structure/gore/tendrils) in loc) + health = min(maxHealth, health + 1) + +/mob/living/simple_animal/hostile/morph/verb/toggle_darkview() + set name = "Toggle Darkvision" + set desc = "Toggles whether you see light or not." + set category = "Abilities" + + if(see_invisible == SEE_INVISIBLE_NOLIGHTING) + see_invisible = SEE_INVISIBLE_LIVING + else + see_invisible = SEE_INVISIBLE_NOLIGHTING + /mob/living/simple_animal/hostile/morph/examine(mob/user) if(morphed) . = form.examine(user) @@ -66,6 +108,13 @@ else return ..() +/mob/living/simple_animal/hostile/morph/proc/update_speed() + switch(m_intent) + if("run") + speed = 1.5 + if("walk") + speed = 2.5 + /mob/living/simple_animal/hostile/morph/proc/allowed(atom/movable/A) return !is_type_in_typecache(A, blacklist_typecache) && (isobj(A) || ismob(A)) @@ -201,5 +250,11 @@ return return ..() +/mob/living/simple_animal/hostile/morph/add_spell(var/spell/spell_to_add, var/spell_base = "wiz_spell_ready", var/master_type = /obj/screen/movable/spell_master) + . = ..() + for(var/obj/screen/movable/spell_master/spell_master in spell_masters) + spell_master.open_state = "morph_open" + spell_master.closed_state = "morph_closed" + /mob/living/simple_animal/hostile/morph/do_animate_chat(var/message, var/datum/language/language, var/small, var/list/show_to, var/duration, var/list/message_override) INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, language, small, show_to, duration) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index d6d207ae13f..d028cd278c5 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -696,7 +696,7 @@ mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) else fall_asleep() - to_chat(src, SPAN_NOTICE("You are now [resting ? "resting" : "getting up"]")) + to_chat(src, SPAN_NOTICE("You are now [resting ? "resting" : "getting up"].")) update_icon() diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 0b974663cbf..ddd000af576 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -373,11 +373,7 @@ playsound(F, 'sound/species/diona/gestalt_grow.ogg', 30, TRUE) /datum/reagent/toxin/plantbgone/touch_obj(var/obj/O, var/volume) - if(istype(O, /obj/structure/alien/weeds)) - var/obj/structure/alien/weeds/alien_weeds = O - alien_weeds.health -= rand(15, 35) - alien_weeds.healthcheck() - else if(istype(O, /obj/effect/plant)) + if(istype(O, /obj/effect/plant)) qdel(O) /datum/reagent/toxin/plantbgone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) diff --git a/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_nest.dm b/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_nest.dm new file mode 100644 index 00000000000..2203282b829 --- /dev/null +++ b/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_nest.dm @@ -0,0 +1,14 @@ +/spell/aoe_turf/conjure/nest + name = "Create Nest" + desc = "This spell creates a nest beneath you." + + charge_max = 600 + spell_flags = Z2NOCAST + override_base = "const" + invocation = "none" + invocation_type = SpI_NONE + range = 0 + summon_type = list(/obj/structure/bed/nest) + cast_sound = 'sound/effects/attackblob.ogg' + + hud_state = "nest" \ No newline at end of file diff --git a/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_node.dm b/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_node.dm new file mode 100644 index 00000000000..d394cc2a1a3 --- /dev/null +++ b/code/modules/spell_system/spells/spell_list/self/conjure/morph_spells/create_node.dm @@ -0,0 +1,14 @@ +/spell/aoe_turf/conjure/node + name = "Create Node" + desc = "This spell creates a node beneath you." + + charge_max = 1800 + spell_flags = Z2NOCAST + override_base = "const" + invocation = "none" + invocation_type = SpI_NONE + range = 0 + summon_type = list(/obj/structure/gore/tendrils/node) + cast_sound = 'sound/effects/attackblob.ogg' + + hud_state = "tendril_node" \ No newline at end of file diff --git a/html/changelogs/geeves-gore_pile.yml b/html/changelogs/geeves-gore_pile.yml new file mode 100644 index 00000000000..908513d4ccc --- /dev/null +++ b/html/changelogs/geeves-gore_pile.yml @@ -0,0 +1,9 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "Morphs now have HUDs, move intent, health, zone selection, resist." + - rscadd: "Morphs now have the ability to put down flesh sac tendril nodes, gore nests, and toggle darkvision (the latter is in the abilities tab)." + - rscadd: "Morphs can now rest. Resting on flesh tendrils will heal them, albeit quite slowly." + - rscadd: "Tendrils slow people walking over them down, unless they're a changeling." diff --git a/icons/mob/npc/animal.dmi b/icons/mob/npc/animal.dmi index 102c9556efc..a287acddc44 100644 Binary files a/icons/mob/npc/animal.dmi and b/icons/mob/npc/animal.dmi differ diff --git a/icons/mob/screen/morph.dmi b/icons/mob/screen/morph.dmi new file mode 100644 index 00000000000..02edd70270a Binary files /dev/null and b/icons/mob/screen/morph.dmi differ diff --git a/icons/mob/screen/spells.dmi b/icons/mob/screen/spells.dmi index 84d62c32ca9..41a6afc362e 100644 Binary files a/icons/mob/screen/spells.dmi and b/icons/mob/screen/spells.dmi differ diff --git a/icons/obj/gore_structures.dmi b/icons/obj/gore_structures.dmi new file mode 100644 index 00000000000..2374d2ee960 Binary files /dev/null and b/icons/obj/gore_structures.dmi differ