From b176296719bccc683da0cbeea13cba30ee8d8fc2 Mon Sep 17 00:00:00 2001 From: Razgriz Date: Sat, 9 Feb 2019 04:30:37 -0700 Subject: [PATCH] Secret --- .../living/simple_animal/animals/sculpture.dm | 276 ++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 code/modules/mob/living/simple_animal/animals/sculpture.dm diff --git a/code/modules/mob/living/simple_animal/animals/sculpture.dm b/code/modules/mob/living/simple_animal/animals/sculpture.dm new file mode 100644 index 0000000000..1acf703a89 --- /dev/null +++ b/code/modules/mob/living/simple_animal/animals/sculpture.dm @@ -0,0 +1,276 @@ +//sculpture +//SCP-173, nothing more need be said +/mob/living/simple_animal/sculpture + name = "\improper sculpture" + desc = "It's some kind of hastily-painted human-size stone sculpture. Just looking at it makes you feel nervous." + icon_state = "sculpture" + icon_living = "sculpture" + icon_dead = "sculpture" + emote_hear = list("makes a faint scraping sound") + emote_see = list("twitches slightly", "shivers") + response_help = "touches the" + response_disarm = "pushes the" + response_harm = "hits the" + see_in_dark = 8 //Needs to see in darkness to snap in darkness + pass_flags = PASSTABLE + var/response_snap = "snapped the neck of" //Past tense because it "happened before you could see it" + var/response_snap_target = "In the blink of an eye, something grabs you and snaps your neck!" + var/snap_sound = list('sound/scp/firstpersonsnap.ogg','sound/scp/firstpersonsnap2.ogg','sound/scp/firstpersonsnap3.ogg') + var/scare_sound = list('sound/scp/scare1.ogg','sound/scp/scare2.ogg','sound/scp/scare3.ogg','sound/scp/scare4.ogg') //Boo + var/hibernate = 0 //Disables SCP until toggled back to 0 + var/scare_played = 0 //Did we rape everyone's ears yet ? + var/obj/machinery/atmospherics/unary/vent_pump/entry_vent //Graciously stolen from spider code + +/mob/living/simple_animal/sculpture/Life() + + //If we are hibernating, just don't do anything + if(hibernate) + return + + if(!check_los()) + return + + //See if we're able to strangle anyone + for(var/mob/living/carbon/M in get_turf(src)) + if(M.stat == CONSCIOUS && check_los()) + snap_neck(M) + break + + //Find out what mobs we can see for targetting purposes + var/list/conscious = list() + for(var/mob/living/carbon/H in view(7, src)) + if(H.stat == CONSCIOUS) //He's up and running + conscious.Add(H) + + //Pick the nearest valid conscious target + var/mob/living/carbon/target + for(var/mob/living/carbon/H in conscious) + if(!target || get_dist(src, H) < get_dist(src, target)) + target = H + + if(target) + handle_target(target) + else + handle_idle() + + +//Check if any mob can see SPC-173 +/mob/living/simple_animal/sculpture/proc/check_los() + + var/observed = 0 + + //Humans can observe SCP-173. If a single human observes him, he's observed for everyone + //Note that humans have a 180 degrees field of vision for the purposes of this proc + for(var/mob/living/carbon/H in viewers(src, null)) + if(H.stat) + continue + var/x_diff = H.x - src.x + var/y_diff = H.y - src.y + if(y_diff != 0) //If we are not on the same vertical plane (up/down), mob is either above or below src + if(y_diff < 0 && H.dir == NORTH) //Mob is below src and looking up + observed = 1 + break + else if(y_diff > 0 && H.dir == SOUTH) //Mob is above src and looking down + observed = 1 + break + if(x_diff != 0) //If we are not on the same horizontal plane (left/right), mob is either left or right of src + if(x_diff < 0 && H.dir == EAST) //Mob is left of src and looking right + observed = 1 + break + else if(x_diff > 0 && H.dir == WEST) //Mob is right of src and looking left + observed = 1 + break + + if(observed) //Is someone looking at us, given that they can see us ? + return 0 //Try again when we get a chance + return 1 //Success, let's move + +/mob/living/simple_animal/sculpture/proc/handle_target(var/mob/living/carbon/target) + + if(!target) //Sanity + return + + if(!check_los()) + return + + var/turf/target_turf + + //Send the warning that SPC is homing in + target_turf = get_turf(target) + if(!scare_played) //Let's minimize the spam + playsound(get_turf(src), pick(scare_sound), 50, 1) + scare_played = 1 + spawn(50) + scare_played = 0 + + //Rampage along a path to get to them, in the blink of an eye + var/turf/next_turf = get_step_towards(src, target) + var/num_turfs = get_dist(src,target) + spawn() + while(get_turf(src) != target_turf && num_turfs > 0) + if(!check_los()) //Something is looking at us now + break + if(!next_turf.CanPass(src, next_turf)) //We can't pass through our planned path + break + for(var/obj/structure/table/O in next_turf) + O.ex_act(1) + sleep(5) + for(var/obj/structure/closet/C in next_turf) + C.ex_act(1) + sleep(5) + for(var/obj/structure/grille/G in next_turf) + G.ex_act(1) + sleep(5) + for(var/obj/machinery/door/airlock/A in next_turf) + if(A.welded || A.locked) //Snowflakey code to take in account bolts and welding + break + A.open() + sleep(5) + for(var/obj/machinery/door/D in next_turf) + D.open() + sleep(5) + if(!next_turf.CanPass(src, next_turf)) //Once we cleared everything we could, check one last time if we can pass + break + forceMove(next_turf) + dir = get_dir(src, target) + next_turf = get_step(src, get_dir(next_turf,target)) + num_turfs-- + +/mob/living/simple_animal/sculpture/proc/handle_idle() + + if(!check_los()) + return + + //If we're not strangling anyone, take a stroll + if(prob(25)) //1 in 4 chance of checking out something new + var/list/turfs = new/list() + for(var/turf/T in view(7, src)) + if(!istype(T, /turf/simulated/floor)) + continue + turfs += T + var/turf/target_turf = safepick(turfs) + if(!target_turf) + return + //Move to our new resting point with celerity + var/turf/next_turf = get_step_towards(src, target_turf) + var/num_turfs = get_dist(src, target_turf) + spawn() + while(get_turf(src) != target_turf && num_turfs > 0) + if(!check_los()) //Something is looking at us now + break + if(!next_turf.CanPass(src, next_turf)) //We can't pass through our planned path + break + for(var/obj/structure/table/O in next_turf) + O.ex_act(1) + sleep(5) + for(var/obj/structure/closet/C in next_turf) + C.ex_act(1) + sleep(5) + for(var/obj/structure/grille/G in next_turf) + G.ex_act(1) + sleep(5) + for(var/obj/machinery/door/airlock/A in next_turf) + if(A.welded || A.locked) //Snowflakey code to take in account bolts and welding + break + A.open() + sleep(5) + for(var/obj/machinery/door/D in next_turf) + D.open() + sleep(5) + if(!next_turf.CanPass(src, next_turf)) //Once we cleared everything we could, check one last time if we can pass + break + forceMove(next_turf) + dir = get_dir(src, target_turf) + next_turf = get_step(src, get_dir(next_turf,target_turf)) + num_turfs-- + //Coding note : This is known to allow SCP to end up on tiles that contain obstructing structures (doors, machinery, etc) + //Although he CAN'T pass through them during normal movement. Will look into a fix soon + + //Do we have a vent ? Good, let's take a look + for(entry_vent in view(1, src)) + if(prob(90)) //10 % chance to consider a vent, to try and avoid constant vent switching + return + visible_message("\The [src] starts trying to slide itself into the vent!") + sleep(50) //Let's stop SCP-173 for five seconds to do his parking job + ..() + if(entry_vent.network && entry_vent.network.normal_members.len) + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.network.normal_members) + vents.Add(temp_vent) + if(!vents.len) + entry_vent = null + return + if(!check_los()) //Someone started looking at us + return + var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents) + spawn() + visible_message("\The [src] suddenly disappears into the vent!") + loc = exit_vent + var/travel_time = round(get_dist(loc, exit_vent.loc)/2) + spawn(travel_time) + if(!exit_vent || exit_vent.welded) + forceMove(get_turf(entry_vent)) + entry_vent = null + visible_message("\The [src] suddenly appears from the vent!") + return + + forceMove(get_turf(exit_vent)) + entry_vent = null + visible_message("\The [src] suddenly appears from the vent!") + else + entry_vent = null + +//This performs an immediate neck snap check, meant to avoid people cheesing SCP-173 by just running faster than Life() refresh +/mob/living/simple_animal/sculpture/proc/check_snap_neck() + + //See if we're able to strangle anyone + for(var/mob/living/carbon/M in get_turf(src)) + if(M.stat == CONSCIOUS && check_los()) + snap_neck(M) + break + +/mob/living/simple_animal/sculpture/forceMove(atom/destination, var/no_tp = 0) + + ..() + check_snap_neck() + +/mob/living/simple_animal/sculpture/proc/snap_neck(var/mob/living/target) + + if(!check_los()) + return + + if(target) + //To prevent movement cheese, SCP snaps necks the second it ends up on the same turf as someone + //Or in other terms, if SCP decides it had a clean shot for a neck snap at the moment this proc fired, you're good as dead + target.apply_damage(rand(120, 150), BRUTE, "head") + target.apply_damage(100, OXY) + playsound(target.loc, pick(snap_sound), 50, 1) + + //Warn everyone + visible_message("[src] [response_snap] [target]!") + target << "[response_snap_target] Your vision fades away..." + + target.death() //Immediately trigger death to avoid doublesnap during lag + + //Logging stuff + target.attack_log += text("\[[time_stamp()]\] Has had his neck snapped by [src]!") + log_admin("[target] ([target.ckey]) has had his neck snapped by an active [src].") + message_admins("ALERT: [target.real_name] has had his neck snapped by an active [src].") + +/mob/living/simple_animal/sculpture/attackby(var/obj/item/O as obj, var/mob/user as mob) + ..() + +/mob/living/simple_animal/sculpture/Topic(href, href_list) + ..() + +/mob/living/simple_animal/sculpture/Bump(atom/movable/AM as mob) + if(!check_los()) + snap_neck(AM) + ..() + +/mob/living/simple_animal/sculpture/Bumped(atom/movable/AM as mob, yes) + if(!check_los()) + snap_neck(AM) + +//You cannot destroy SCP-173, fool! +/mob/living/simple_animal/sculpture/ex_act(var/severity)