diff --git a/code/game/objects/structures/alien_props.dm b/code/game/objects/structures/props/alien_props.dm similarity index 90% rename from code/game/objects/structures/alien_props.dm rename to code/game/objects/structures/props/alien_props.dm index eb8ae9d19f..fa11e27b15 100644 --- a/code/game/objects/structures/alien_props.dm +++ b/code/game/objects/structures/props/alien_props.dm @@ -6,15 +6,6 @@ icon = 'icons/obj/abductor.dmi' density = TRUE anchored = TRUE - var/interaction_message = null - -/obj/structure/prop/alien/attack_hand(mob/living/user) // Used to tell the player that this isn't useful for anything. - if(!istype(user)) - return FALSE - if(!interaction_message) - return ..() - else - to_chat(user, interaction_message) /obj/structure/prop/alien/computer name = "alien console" diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm new file mode 100644 index 0000000000..44df2eb68b --- /dev/null +++ b/code/game/objects/structures/props/beam_prism.dm @@ -0,0 +1,215 @@ +//A series(?) of prisms for PoIs. The base one only works for beams. + +/obj/structure/prop/prism + name = "prismatic turret" + desc = "A raised, externally powered 'turret'. It seems to have a massive crystal ring around its base." + description_info = "This device is capable of redirecting any beam projectile." + icon = 'icons/obj/props/prism.dmi' + icon_state = "prism" + density = TRUE + anchored = TRUE + + layer = 3.1 //Layer over projectiles. + plane = -10 //Layer over projectiles. + + var/rotation_lock = 0 // Can you rotate the prism at all? + var/free_rotate = 1 // Does the prism rotate in any direction, or only in the eight standard compass directions? + var/external_control_lock = 0 // Does the prism only rotate from the controls of an external switch? + var/degrees_from_north = 0 // How far is it rotated clockwise? + var/compass_directions = list("North" = 0, "South" = 180, "East" = 90, "West" = 270, "Northwest" = 315, "Northeast" = 45, "Southeast" = 135, "Southwest" = 225) + var/interaction_sound = 'sound/mecha/mechmove04.ogg' + + var/redirect_type = /obj/item/projectile/beam + + var/dialID = null + var/obj/structure/prop/prismcontrol/remote_dial = null + + interaction_message = "The prismatic turret seems to be able to rotate." + +/obj/structure/prop/prism/initialize() + if(degrees_from_north) + animate(src, transform = turn(NORTH, degrees_from_north), time = 3) + +/obj/structure/prop/prism/Destroy() + if(remote_dial) + remote_dial.my_turrets -= src + remote_dial = null + ..() + +/obj/structure/prop/prism/proc/reset_rotation() + var/degrees_to_rotate = -1 * degrees_from_north + animate(src, transform = turn(src.transform, degrees_to_rotate), time = 2) + +/obj/structure/prop/prism/attack_hand(mob/living/user) + ..() + + if(rotation_lock) + to_chat(user, "\The [src] is locked at its current bearing.") + return + if(external_control_lock) + to_chat(user, "\The [src]'s motors resist your efforts to rotate it. You may need to find some form of controller.") + return + + var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No") + if(confirm == "No") + visible_message(\ + "[user.name] decides not to try turning \the [src].",\ + "You decide not to try turning \the [src].") + return + + var/new_bearing + if(free_rotate) + new_bearing = input("What bearing do you want to rotate \the [src] to?", "[name]") as num + new_bearing = round(new_bearing) + if(new_bearing <= -1 || new_bearing > 360) + to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") + return + else + var/choice = input("What point do you want to set \the [src] to?", "[name]") as null|anything in compass_directions + new_bearing = round(compass_directions[choice]) + + var/rotate_degrees = new_bearing - degrees_from_north + + if(new_bearing == 360) // Weird artifact. + new_bearing = 0 + degrees_from_north = new_bearing + + var/two_stage = 0 + if(rotate_degrees == 180 || rotate_degrees == -180) + two_stage = 1 + var/multiplier = pick(-1, 1) + rotate_degrees = multiplier * (rotate_degrees / 2) + + playsound(src, interaction_sound, 50, 1) + if(two_stage) + animate(src, transform = turn(src.transform, rotate_degrees), time = 3) + spawn(3) + animate(src, transform = turn(src.transform, rotate_degrees), time = 3) + else + animate(src, transform = turn(src.transform, rotate_degrees), time = 6) //Can't update transform because it will reset the angle. + +/obj/structure/prop/prism/proc/rotate_auto(var/new_bearing) + if(rotation_lock) + visible_message("\The [src] shudders.") + playsound(src, 'sound/effects/clang.ogg', 50, 1) + return + + visible_message("\The [src] rotates to a bearing of [new_bearing].") + + var/rotate_degrees = new_bearing - degrees_from_north + + if(new_bearing == 360) + new_bearing = 0 + degrees_from_north = new_bearing + + var/two_stage = 0 + if(rotate_degrees == 180 || rotate_degrees == -180) + two_stage = 1 + var/multiplier = pick(-1, 1) + rotate_degrees = multiplier * (rotate_degrees / 2) + + playsound(src, interaction_sound, 50, 1) + if(two_stage) + animate(src, transform = turn(src.transform, rotate_degrees), time = 3) + spawn(3) + animate(src, transform = turn(src.transform, rotate_degrees), time = 3) + else + animate(src, transform = turn(src.transform, rotate_degrees), time = 6) + +/obj/structure/prop/prism/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj, redirect_type)) + visible_message("\The [src] redirects \the [Proj]!") + flick("[initial(icon_state)]+glow", src) + + var/new_x = (1 * round(10 * cos(degrees_from_north - 90))) + x //Vectors vectors vectors. + var/new_y = (-1 * round(10 * sin(degrees_from_north - 90))) + y + var/turf/curloc = get_turf(src) + + Proj.penetrating += 1 // Needed for the beam to get out of the turret. + + Proj.redirect(new_x, new_y, curloc, null) + +/obj/structure/prop/prism/incremental + free_rotate = 0 + description_info = "This device is capable of redirecting any beam projectile, but only locks to specific positions in rotation." + +/obj/structure/prop/prism/incremental/externalcont + external_control_lock = 1 + description_info = "This device is capable of redirecting any beam projectile, but can only be rotated by a control dial to specific positions." + +/obj/structure/prop/prism/externalcont + external_control_lock = 1 + description_info = "This device is capable of redirecting any beam projectile, but can only be rotated by an external control dial." + +/obj/structure/prop/prismcontrol + name = "prismatic dial" + desc = "A large dial with a crystalline ring." + icon = 'icons/obj/props/prism.dmi' + icon_state = "dial" + density = FALSE + anchored = TRUE + + interaction_message = "The dial pulses as your hand nears it." + var/list/my_turrets = list() + var/dialID = null + +/obj/structure/prop/prismcontrol/attack_hand(mob/living/user) + ..() + + var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No") + if(confirm == "No") + visible_message(\ + "[user.name] decides not to try turning \the [src].",\ + "You decide not to try turning \the [src].") + return + + if(!my_turrets || !my_turrets.len) + to_chat(user, "\The [src] doesn't seem to do anything.") + return + + var/free_rotate = 1 + var/list/compass_directions = list() + for(var/obj/structure/prop/prism/P in my_turrets) + if(!P.free_rotate) //Doesn't use bearing, it uses compass points. + free_rotate = 0 + compass_directions |= P.compass_directions + + var/new_bearing + if(free_rotate) + new_bearing = input("What bearing do you want to rotate \the [src] to?", "[name]") as num + new_bearing = round(new_bearing) + if(new_bearing <= -1 || new_bearing > 360) + to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") + return + else + var/choice = input("What point do you want to set \the [src] to?", "[name]") as null|anything in compass_directions + new_bearing = round(compass_directions[choice]) + + confirm = input("Are you certain you want to rotate \the [src]?", "[name]") in list("Yes", "No") + if(confirm == "No") + visible_message(\ + "[user.name] decides not to try turning \the [src].",\ + "You decide not to try turning \the [src].") + return + + to_chat(user, "\The [src] clicks into place.") + for(var/obj/structure/prop/prism/P in my_turrets) + P.rotate_auto(new_bearing) + +/obj/structure/prop/prismcontrol/initialize() + ..() + if(my_turrets.len) //Preset controls. + for(var/obj/structure/prop/prism/P in my_turrets) + P.remote_dial = src + return + spawn() + for(var/obj/structure/prop/prism/P in orange(src, world.view)) //Don't search a huge area. + if(P.dialID == dialID && !P.remote_dial && P.external_control_lock) + my_turrets |= P + P.remote_dial = src + +/obj/structure/prop/prismcontrol/Destroy() + for(var/obj/structure/prop/prism/P in my_turrets) + P.remote_dial = null + my_turrets = list() + ..() diff --git a/code/game/objects/structures/props/projectile_lock.dm b/code/game/objects/structures/props/projectile_lock.dm new file mode 100644 index 0000000000..5c3fcd2ba0 --- /dev/null +++ b/code/game/objects/structures/props/projectile_lock.dm @@ -0,0 +1,53 @@ +//A locking mechanism that pulses when hit by a projectile. The base one responds to high-power lasers. + +/obj/structure/prop/lock + name = "weird lock" + desc = "An esoteric object that responds to.. something." + icon = 'icons/obj/props/prism.dmi' + icon_state = "lock" + + var/enabled = 0 + var/lockID = null + + var/list/linked_objects = list() + +/obj/structure/prop/lock/Destroy() + if(linked_objects.len) + for(var/obj/O in linked_objects) + if(istype(O, /obj/machinery/door/blast/puzzle)) + var/obj/machinery/door/blast/puzzle/P = O + P.locks -= src + linked_objects -= P + ..() + +/obj/structure/prop/lock/proc/toggle_lock() + enabled = !enabled + + if(enabled) + icon_state = "[initial(icon_state)]-active" + else + icon_state = "[initial(icon_state)]" + +/obj/structure/prop/lock/projectile + name = "beam lock" + desc = "An esoteric object that responds to high intensity light." + + var/projectile_key = /obj/item/projectile/beam + var/timed = 0 + var/timing = 0 + var/time_limit = 1500 // In ticks. Ten is one second. + + interaction_message = "The object remains inert to your touch." + +/obj/structure/prop/lock/projectile/bullet_act(var/obj/item/projectile/Proj) + if(!istype(Proj, projectile_key) || timing) + return + + if(istype(Proj, /obj/item/projectile/beam/heavylaser/cannon) || istype(Proj, /obj/item/projectile/beam/emitter) || (Proj.damage >= 80 && Proj.damtype == BURN)) + toggle_lock() + visible_message("\The [src] [enabled ? "disengages" : "engages"] its locking mechanism.") + + if(timed) + timing = 1 + spawn(time_limit) + toggle_lock() diff --git a/code/game/objects/structures/props/prop.dm b/code/game/objects/structures/props/prop.dm new file mode 100644 index 0000000000..fea5815674 --- /dev/null +++ b/code/game/objects/structures/props/prop.dm @@ -0,0 +1,18 @@ +//The base 'prop' for PoIs or other large junk. + +/obj/structure/prop + name = "something" + desc = "My description is broken, bug a developer." + icon = 'icons/obj/structures.dmi' + icon_state = "safe" + density = TRUE + anchored = TRUE + var/interaction_message = null + +/obj/structure/prop/attack_hand(mob/living/user) // Used to tell the player that this isn't useful for anything. + if(!istype(user)) + return FALSE + if(!interaction_message) + return ..() + else + to_chat(user, interaction_message) diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm new file mode 100644 index 0000000000..b9a32fc0dc --- /dev/null +++ b/code/game/objects/structures/props/puzzledoor.dm @@ -0,0 +1,92 @@ +// An indestructible blast door that can only be opened once its puzzle requirements are completed. + +/obj/machinery/door/blast/puzzle + name = "puzzle door" + desc = "A large, virtually indestructible door that will not open unless certain requirements are met." + icon_state_open = "pdoor0" + icon_state_opening = "pdoorc0" + icon_state_closed = "pdoor1" + icon_state_closing = "pdoorc1" + icon_state = "pdoor1" + + explosion_resistance = 100 + + maxhealth = 9999999 //No. + + var/list/locks = list() + var/lockID = null + var/checkrange_mult = 1 + +/obj/machinery/door/blast/puzzle/proc/check_locks() + for(var/obj/structure/prop/lock/L in locks) + if(!L.enabled) + return 0 + return 1 + +/obj/machinery/door/blast/puzzle/bullet_act(var/obj/item/projectile/Proj) + visible_message("\The [src] is completely unaffected by \the [Proj].") + qdel(Proj) //No piercing. No. + +/obj/machinery/door/blast/puzzle/ex_act(severity) + visible_message("\The [src] is completely unaffected by the blast.") + return + +/obj/machinery/door/blast/puzzle/initialize() + . = ..() + implicit_material = get_material_by_name("dungeonium") + if(locks.len) + return + var/check_range = world.view * checkrange_mult + for(var/obj/structure/prop/lock/L in orange(src, check_range)) + if(L.lockID == lockID) + L.linked_objects |= src + locks |= L + +/obj/machinery/door/blast/puzzle/Destroy() + if(locks.len) + for(var/obj/structure/prop/lock/L in locks) + L.linked_objects -= src + locks -= L + ..() + +/obj/machinery/door/blast/puzzle/attack_hand(mob/user as mob) + if(check_locks()) + force_toggle(1, user) + else + to_chat(user, "\The [src] does not respond to your touch.") + +/obj/machinery/door/blast/puzzle/attackby(obj/item/weapon/C as obj, mob/user as mob) + if(istype(C, /obj/item/weapon)) + if(C.pry == 1 && (user.a_intent != I_HURT || (stat & BROKEN))) + if(istype(C,/obj/item/weapon/material/twohanded/fireaxe)) + var/obj/item/weapon/material/twohanded/fireaxe/F = C + if(!F.wielded) + to_chat(user, "You need to be wielding \the [F] to do that.") + return + + if(check_locks()) + force_toggle(1, user) + + else + to_chat(user, "[src]'s arcane workings resist your effort.") + return + + else if(src.density && (user.a_intent == I_HURT)) + var/obj/item/weapon/W = C + user.setClickCooldown(user.get_attack_speed(W)) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") + + else if(istype(C, /obj/item/weapon/plastique)) + to_chat(user, "On contacting \the [src], a flash of light envelops \the [C] as it is turned to ash. Oh.") + qdel(C) + return 0 + +/obj/machinery/door/blast/puzzle/attack_generic(var/mob/user, var/damage) + if(check_locks()) + force_toggle(1, user) + +/obj/machinery/door/blast/puzzle/attack_alien(var/mob/user) + if(check_locks()) + force_toggle(1, user) diff --git a/icons/obj/props/prism.dmi b/icons/obj/props/prism.dmi new file mode 100644 index 0000000000..0b938c7ef0 Binary files /dev/null and b/icons/obj/props/prism.dmi differ diff --git a/icons/obj/props/projectile_lock.dmi b/icons/obj/props/projectile_lock.dmi new file mode 100644 index 0000000000..cf74d73796 Binary files /dev/null and b/icons/obj/props/projectile_lock.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 21345fe0dc..1176f15e95 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1156,7 +1156,6 @@ #include "code\game\objects\random\mob.dm" #include "code\game\objects\random\random_vr.dm" #include "code\game\objects\random\spacesuits.dm" -#include "code\game\objects\structures\alien_props.dm" #include "code\game\objects\structures\barsign.dm" #include "code\game\objects\structures\bedsheet_bin.dm" #include "code\game\objects\structures\bonfire.dm" @@ -1238,6 +1237,11 @@ #include "code\game\objects\structures\flora\trees.dm" #include "code\game\objects\structures\ghost_pods\ghost_pods.dm" #include "code\game\objects\structures\ghost_pods\silicon.dm" +#include "code\game\objects\structures\props\alien_props.dm" +#include "code\game\objects\structures\props\beam_prism.dm" +#include "code\game\objects\structures\props\projectile_lock.dm" +#include "code\game\objects\structures\props\prop.dm" +#include "code\game\objects\structures\props\puzzledoor.dm" #include "code\game\objects\structures\stool_bed_chair_nest\alien_nests.dm" #include "code\game\objects\structures\stool_bed_chair_nest\bed.dm" #include "code\game\objects\structures\stool_bed_chair_nest\chairs.dm"