mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-31 00:58:26 +01:00
new space ruin, the biological research outpost (#79149)
## About The Pull Request  adds this ruin to space ruin pool this is a shady (as NT always is) bioresearch outpost that got fucked up by an experiment this has like some puzzle aspect to it since you gotta find keycards and shit and press buttons to unlock shield gates this ends with you fighting a heart which if you defeat, destroys the blockade that prevents you from entering the outpost vault also you can no longer literally just cut indestructible grilles or unanchor indestructible windows ### new puzzle elements or something idk variant of pressure plate that you cannot remove and it sends a puzzle signal cooler red puzzle doors that look very foreboding or something idk theyre for this ruin also puzzle blockades, which are indestructible dense objects that are destroyed if they receive a puzzle signal and also buttons and keycard pads for puzzles https://github.com/tgstation/tgstation/assets/70376633/c98807ec-1e7b-49c4-a757-cdbb76a1b566 https://github.com/tgstation/tgstation/assets/70376633/9d5d9dd1-5868-44e6-a978-5ea57b30c298 stuff that throws electric shocks in a pattern, ignores insuls and only knocks down, and no you cannot just run past https://github.com/tgstation/tgstation/assets/70376633/5772917c-a963-48a4-a743-b0f610801d25 ### enemies living floor, it can only attack stuff on top of it and it attacks until the victim is dead it is invincible to all but a crowbar, and it cannot move, and it remains hidden until a victim is in range https://github.com/tgstation/tgstation/assets/70376633/aa1d54f6-b259-4e58-9d44-e393d2131acf living flesh, it can replace your limbs with itself the conditions for that are; the limb must have 20 or more brute, victim must be alive and dismemberable, the limb may not be torso or head, or the limb may not be living flesh alternatively it can replace a missing limb these are all checked with every attack they have 20 hp the limbs in question will sometimes act up, while passively draining nutrition, arms will randomly start pulling nearby stuff, legs may step randomly limbs when detached, turn into mobs and reactivate AI 2 seconds later. if the host is shocked, all living flesh limbs will detach, or if the host dies they will also do that https://github.com/tgstation/tgstation/assets/70376633/765cc99e-c800-4efb-aabe-d68817bbd7ae ## Why It's Good For The Game ruin variety is cool i think also the other things i added should be useful for other mappers for bitrunning or whatever also bug bad for that one fix ## Changelog 🆑 add: living floor, living flesh, and other stuff for the bioresearch outpost ruin add: bioresearch outpost ruin fix: you may not defeat indestructible grilles and windows with mere tools /🆑 --------- Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
/datum/ai_controller/basic_controller/living_limb_flesh
|
||||
blackboard = list(
|
||||
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic,
|
||||
BB_TARGET_MINIMUM_STAT = HARD_CRIT,
|
||||
)
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree
|
||||
)
|
||||
|
||||
/mob/living/basic/living_limb_flesh
|
||||
name = "living flesh"
|
||||
desc = "A vaguely leg or arm shaped flesh abomination. It pulses, like a heart."
|
||||
icon = 'icons/mob/simple/animal.dmi'
|
||||
icon_state = "limb"
|
||||
icon_living = "limb"
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
faction = list(FACTION_HOSTILE)
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
attack_vis_effect = ATTACK_EFFECT_BITE
|
||||
attack_verb_continuous = "tries desperately to attach to"
|
||||
attack_verb_simple = "try to attach to"
|
||||
mob_biotypes = MOB_ORGANIC | MOB_SPECIAL
|
||||
ai_controller = /datum/ai_controller/basic_controller/living_limb_flesh
|
||||
/// the meat bodypart we are currently inside, used to like drain nutrition and dismember and shit
|
||||
var/obj/item/bodypart/current_bodypart
|
||||
|
||||
/mob/living/basic/living_limb_flesh/Initialize(mapload, obj/item/bodypart/limb)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/swarming, max_x = 8, max_y = 8)
|
||||
AddElement(/datum/element/death_drops, string_list(list(/obj/effect/gibspawner/generic)))
|
||||
if(!isnull(limb))
|
||||
register_to_limb(limb)
|
||||
|
||||
/mob/living/basic/living_limb_flesh/Destroy(force)
|
||||
. = ..()
|
||||
QDEL_NULL(current_bodypart)
|
||||
|
||||
/mob/living/basic/living_limb_flesh/Life(seconds_per_tick = SSMOBS_DT, times_fired)
|
||||
. = ..()
|
||||
if(stat == DEAD)
|
||||
return
|
||||
if(isnull(current_bodypart) || isnull(current_bodypart.owner))
|
||||
return
|
||||
var/mob/living/carbon/human/victim = current_bodypart.owner
|
||||
if(prob(SPT_PROB(3, SSMOBS_DT)))
|
||||
to_chat(victim, span_warning("The thing posing as your limb makes you feel funny...")) //warn em
|
||||
//firstly as a sideeffect we drain nutrition from our host
|
||||
victim.adjust_nutrition(-1.5)
|
||||
|
||||
if(!prob(SPT_PROB(1.5, SSMOBS_DT)))
|
||||
return
|
||||
|
||||
if(istype(current_bodypart, /obj/item/bodypart/arm))
|
||||
var/list/candidates = list()
|
||||
for(var/atom/movable/movable in orange(victim, 1))
|
||||
if(movable.anchored)
|
||||
continue
|
||||
if(movable == victim)
|
||||
continue
|
||||
if(!victim.CanReach(movable))
|
||||
continue
|
||||
candidates += movable
|
||||
var/atom/movable/candidate = pick(candidates)
|
||||
if(isnull(candidate))
|
||||
return
|
||||
victim.start_pulling(candidate, supress_message = TRUE)
|
||||
victim.visible_message(span_warning("[victim][victim.p_s()] [current_bodypart] instinctually starts feeling [candidate]!"))
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(victim, TRAIT_IMMOBILIZED))
|
||||
return
|
||||
step(victim, pick(GLOB.cardinals))
|
||||
to_chat(victim, span_warning("Your [current_bodypart] moves on its own!"))
|
||||
|
||||
|
||||
/mob/living/basic/living_limb_flesh/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown)
|
||||
. = ..()
|
||||
if (!ishuman(target) || target.stat == DEAD || HAS_TRAIT(target, TRAIT_NODISMEMBER))
|
||||
return
|
||||
|
||||
var/list/zone_candidates = target.get_missing_limbs()
|
||||
for(var/obj/item/bodypart/bodypart in target.bodyparts)
|
||||
if(bodypart.body_zone == BODY_ZONE_HEAD || bodypart.body_zone == BODY_ZONE_CHEST)
|
||||
continue
|
||||
if(HAS_TRAIT(bodypart, TRAIT_IGNORED_BY_LIVING_FLESH))
|
||||
continue
|
||||
if(bodypart.bodypart_flags & BODYPART_UNREMOVABLE)
|
||||
continue
|
||||
if(bodypart.brute_dam < 20)
|
||||
continue
|
||||
zone_candidates += bodypart.body_zone
|
||||
|
||||
if(!length(zone_candidates))
|
||||
return
|
||||
|
||||
var/target_zone = pick(zone_candidates)
|
||||
var/obj/item/bodypart/target_part = target.get_bodypart(target_zone)
|
||||
if(isnull(target_part))
|
||||
target.emote("scream") // dismember already makes them scream so only do this if we aren't doing that
|
||||
else
|
||||
target_part.dismember()
|
||||
|
||||
var/part_type
|
||||
switch(target_zone)
|
||||
if(BODY_ZONE_L_ARM)
|
||||
part_type = /obj/item/bodypart/arm/left/flesh
|
||||
if(BODY_ZONE_R_ARM)
|
||||
part_type = /obj/item/bodypart/arm/right/flesh
|
||||
if(BODY_ZONE_L_LEG)
|
||||
part_type = /obj/item/bodypart/leg/left/flesh
|
||||
if(BODY_ZONE_R_LEG)
|
||||
part_type = /obj/item/bodypart/leg/right/flesh
|
||||
|
||||
target.visible_message(span_danger("[src] [target_part ? "tears off and attaches itself" : "attaches itself"] to where [target][target.p_s()] limb used to be!"))
|
||||
current_bodypart = new part_type(TRUE) //dont_spawn_flesh, we cant use named arguments here
|
||||
current_bodypart.replace_limb(target, TRUE)
|
||||
forceMove(current_bodypart)
|
||||
register_to_limb(current_bodypart)
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/register_to_limb(obj/item/bodypart/part)
|
||||
ai_controller.set_ai_status(AI_STATUS_OFF)
|
||||
RegisterSignal(part, COMSIG_BODYPART_REMOVED, PROC_REF(on_limb_lost))
|
||||
RegisterSignal(part.owner, COMSIG_LIVING_DEATH, PROC_REF(owner_died))
|
||||
RegisterSignal(part.owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(owner_shocked)) //detach if we are shocked, not beneficial for the host but hey its a sideeffect
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/owner_shocked(datum/source, shock_damage, source, siemens_coeff, flags)
|
||||
SIGNAL_HANDLER
|
||||
if(shock_damage < 10)
|
||||
return
|
||||
var/mob/living/carbon/human/part_owner = current_bodypart.owner
|
||||
if(!detach_self())
|
||||
return
|
||||
var/turf/our_location = get_turf(src)
|
||||
our_location.visible_message(span_warning("[part_owner][part_owner.p_s()] [current_bodypart] begins to convulse wildly!"))
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/owner_died(datum/source, gibbed)
|
||||
SIGNAL_HANDLER
|
||||
if(gibbed)
|
||||
return
|
||||
addtimer(CALLBACK(src, PROC_REF(detach_self)), 1 SECONDS) //we need new hosts, dead people suck!
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/detach_self()
|
||||
if(isnull(current_bodypart))
|
||||
return FALSE
|
||||
current_bodypart.dismember()
|
||||
return TRUE//on_limb_lost should be called after that
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/on_limb_lost(atom/movable/source, mob/living/carbon/old_owner, dismembered)
|
||||
SIGNAL_HANDLER
|
||||
UnregisterSignal(source, COMSIG_BODYPART_REMOVED)
|
||||
UnregisterSignal(old_owner, COMSIG_LIVING_ELECTROCUTE_ACT)
|
||||
UnregisterSignal(old_owner, COMSIG_LIVING_DEATH)
|
||||
addtimer(CALLBACK(src, PROC_REF(wake_up), source), 2 SECONDS)
|
||||
|
||||
/mob/living/basic/living_limb_flesh/proc/wake_up(atom/limb)
|
||||
ai_controller.set_ai_status(AI_STATUS_ON)
|
||||
forceMove(limb.drop_location())
|
||||
current_bodypart = null
|
||||
qdel(limb)
|
||||
visible_message(span_warning("[src] begins flailing around!"))
|
||||
Shake(6, 6, 0.5 SECONDS)
|
||||
@@ -0,0 +1,96 @@
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/on_top/SelectBehaviors(datum/ai_controller/controller, delta_time)
|
||||
var/mob/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
|
||||
if(!target || QDELETED(target))
|
||||
return
|
||||
if(target.loc != controller.pawn.loc)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/datum/ai_controller/basic_controller/living_floor
|
||||
max_target_distance = 2
|
||||
blackboard = list(
|
||||
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic,
|
||||
BB_TARGET_MINIMUM_STAT = HARD_CRIT,
|
||||
)
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/on_top
|
||||
)
|
||||
|
||||
/mob/living/basic/living_floor
|
||||
name = "floor"
|
||||
desc = "The floor you walk on. It looks near-impervious to damage."
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "floor"
|
||||
icon_living = "floor"
|
||||
mob_size = MOB_SIZE_HUGE
|
||||
mob_biotypes = MOB_SPECIAL
|
||||
status_flags = GODMODE //nothing but crowbars may kill us
|
||||
death_message = ""
|
||||
unsuitable_atmos_damage = 0
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = INFINITY
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
move_resist = INFINITY
|
||||
density = FALSE
|
||||
combat_mode = TRUE
|
||||
layer = TURF_LAYER
|
||||
plane = FLOOR_PLANE
|
||||
faction = list(FACTION_HOSTILE)
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 40 //pranked.....
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
attack_vis_effect = ATTACK_EFFECT_BITE
|
||||
attack_verb_continuous = "bites"
|
||||
attack_verb_simple = "bite"
|
||||
ai_controller = /datum/ai_controller/basic_controller/living_floor
|
||||
melee_attack_cooldown = 0.5 SECONDS // get real
|
||||
|
||||
var/icon_aggro = "floor-hostile"
|
||||
var/desc_aggro = "This flooring is alive and filled with teeth, better not step on that. Being covered in plating, it is immune to damage. Seems vulnerable to prying though."
|
||||
|
||||
/mob/living/basic/living_floor/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_IMMOBILIZED, INNATE_TRAIT)
|
||||
var/static/list/connections = list(COMSIG_ATOM_ENTERED = PROC_REF(look_aggro), COMSIG_ATOM_EXITED = PROC_REF(look_deaggro))
|
||||
AddComponent(/datum/component/connect_range, tracked = src, connections = connections, range = 1, works_in_containers = FALSE)
|
||||
|
||||
/mob/living/basic/living_floor/proc/look_aggro(datum/source, mob/living/victim)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(victim) || istype(victim, /mob/living/basic/living_floor) || victim.stat == DEAD)
|
||||
return
|
||||
if(victim.loc == loc) //guaranteed bite
|
||||
var/datum/targetting_datum/basic/targetting = ai_controller.blackboard[BB_TARGETTING_DATUM]
|
||||
if(targetting.can_attack(src, victim))
|
||||
melee_attack(victim)
|
||||
icon_state = icon_aggro
|
||||
desc = desc_aggro
|
||||
|
||||
/mob/living/basic/living_floor/proc/look_deaggro(datum/source, mob/living/victim)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(victim) && !istype(victim, /mob/living/basic/living_floor))
|
||||
return
|
||||
icon_state = initial(icon_state)
|
||||
desc = initial(desc_aggro)
|
||||
|
||||
/mob/living/basic/living_floor/med_hud_set_health()
|
||||
return
|
||||
|
||||
/mob/living/basic/living_floor/med_hud_set_status()
|
||||
return
|
||||
|
||||
/mob/living/basic/living_floor/attackby(obj/item/weapon, mob/user, params)
|
||||
if(weapon.tool_behaviour != TOOL_CROWBAR)
|
||||
return ..()
|
||||
balloon_alert(user, "prying...")
|
||||
playsound(src, 'sound/items/crowbar.ogg', 45, TRUE)
|
||||
if(!do_after(user, 5 SECONDS, src))
|
||||
return
|
||||
new /obj/effect/gibspawner/generic(loc)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/basic/living_floor/white
|
||||
icon_state = "white"
|
||||
icon_living = "white"
|
||||
icon_aggro = "whitefloor-hostile"
|
||||
Reference in New Issue
Block a user