diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index b9b7744744..92fd1ac820 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -189,6 +189,12 @@ ..() update_icon() +/obj/machinery/button/vv_edit_var(vname, vval) + . = ..() + if(vname == NAMEOF(src, id)) + var/obj/item/assembly/control/controller = device + if(istype(controller)) + controller.id = vval /obj/machinery/button/door name = "door button" @@ -285,3 +291,17 @@ icon_state = "button" result_path = /obj/machinery/button custom_materials = list(/datum/material/iron = MINERAL_MATERIAL_AMOUNT) + +/obj/machinery/button/elevator + name = "elevator button" + desc = "Go back. Go back. Go back. Can you operate the elevator." + icon_state = "launcher" + skin = "launcher" + device_type = /obj/item/assembly/control/elevator + req_access = list() + id = 1 + +/obj/machinery/button/elevator/examine(mob/user) + . = ..() + . += "There's a small inscription on the button..." + . += "THIS CALLS THE ELEVATOR! IT DOES NOT OPERATE IT! Interact with the elevator itself to use it!" diff --git a/code/game/objects/structures/industrial_lift.dm b/code/game/objects/structures/industrial_lift.dm index 82af7e242a..9f04d1b710 100644 --- a/code/game/objects/structures/industrial_lift.dm +++ b/code/game/objects/structures/industrial_lift.dm @@ -1,3 +1,8 @@ + +//Booleans in arguments are confusing, so I made them defines. +#define LOCKED 1 +#define UNLOCKED 0 + ///Collect and command /datum/lift_master var/list/lift_platforms @@ -43,18 +48,33 @@ possible_expansions |= lift_platform possible_expansions -= borderline -///Move all platforms together +/** + * Moves the lift UP or DOWN, this is what users invoke with their hand. + * This is a SAFE proc, ensuring every part of the lift moves SANELY. + * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. + * Arguments: + * going - UP or DOWN directions, where the lift should go. Keep in mind by this point checks of whether it should go up or down have already been done. + * user - Whomever made the lift movement. + */ /datum/lift_master/proc/MoveLift(going, mob/user) + set_controls(LOCKED) for(var/p in lift_platforms) var/obj/structure/industrial_lift/lift_platform = p lift_platform.travel(going) + set_controls(UNLOCKED) +/** + * Moves the lift, this is what users invoke with their hand. + * This is a SAFE proc, ensuring every part of the lift moves SANELY. + * It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming. + */ /datum/lift_master/proc/MoveLiftHorizontal(going, z) var/max_x = 1 var/max_y = 1 var/min_x = world.maxx var/min_y = world.maxy + set_controls(LOCKED) for(var/p in lift_platforms) var/obj/structure/industrial_lift/lift_platform = p max_x = max(max_x, lift_platform.x) @@ -89,12 +109,14 @@ for(var/y in min_y to max_y) var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z)) lift_platform.travel(going) + set_controls(UNLOCKED) ///Check destination turfs /datum/lift_master/proc/Check_lift_move(check_dir) - for(var/lift_platform in lift_platforms) + for(var/l in lift_platforms) + var/obj/structure/industrial_lift/lift_platform = l var/turf/T = get_step_multiz(lift_platform, check_dir) - if(!T) + if(!T)//the edges of multi-z maps return FALSE if(check_dir == UP && !istype(T, /turf/open/openspace)) // We don't want to go through the ceiling! return FALSE @@ -102,6 +124,15 @@ return FALSE return TRUE +/** + * Sets all lift parts's controls_locked variable. Used to prevent moving mid movement, or cooldowns. + */ +/datum/lift_master/proc/set_controls(state) + for(var/l in lift_platforms) + var/obj/structure/industrial_lift/lift_platform = l + lift_platform.controls_locked = state + +GLOBAL_LIST_EMPTY(lifts) /obj/structure/industrial_lift name = "lift platform" desc = "A lightweight lift platform. It moves up and down." @@ -117,12 +148,18 @@ canSmoothWith = null obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN + var/id = null //ONLY SET THIS TO ONE OF THE LIFT'S PARTS. THEY'RE CONNECTED! ONLY ONE NEEDS THE SIGNAL! + var/pass_through_floors = FALSE //if true, the elevator works through floors + var/controls_locked = FALSE //if true, the lift cannot be manually moved. var/list/atom/movable/lift_load //things to move var/datum/lift_master/lift_master_datum //control from +/obj/structure/industrial_lift/New() + GLOB.lifts.Add(src) + ..() + /obj/structure/industrial_lift/Initialize(mapload) . = ..() - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, PROC_REF(AddItemOnLift)) RegisterSignal(loc, COMSIG_ATOM_CREATED, PROC_REF(AddItemOnLift))//For atoms created on platform RegisterSignal(src, COMSIG_MOVABLE_UNCROSSED, PROC_REF(RemoveItemFromLift)) @@ -133,7 +170,7 @@ /obj/structure/industrial_lift/Move(atom/newloc, direct) UnregisterSignal(loc, COMSIG_ATOM_CREATED) . = ..() - RegisterSignal(newloc, COMSIG_ATOM_CREATED, PROC_REF(AddItemOnLift))//For atoms created on platform + RegisterSignal(loc, COMSIG_ATOM_CREATED, PROC_REF(AddItemOnLift))//For atoms created on platform /obj/structure/industrial_lift/proc/RemoveItemFromLift(datum/source, atom/movable/AM) if(!(AM in lift_load)) @@ -162,51 +199,46 @@ destination = get_step_multiz(src, going) else destination = going + if(going == DOWN)//make sure this stays pre-item moving, or you'll crush anything on the lift under the lift. + for(var/mob/living/crushed in destination.contents) + to_chat(crushed, span_userdanger("You are crushed by [src]!")) + crushed.gib(FALSE,FALSE,FALSE)//the nicest kind of gibbing, keeping everything intact. forceMove(destination) for(var/am in things2move) var/atom/movable/thing = am thing.forceMove(destination) /obj/structure/industrial_lift/proc/use(mob/user, is_ghost=FALSE) - if (is_ghost && !in_range(src, user)) + if(is_ghost && !in_range(src, user)) return - var/static/list/tool_list = list( - "Up" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), - "Down" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH) - ) - - var/turf/can_move_up = lift_master_datum.Check_lift_move(UP) - var/turf/can_move_up_down = lift_master_datum.Check_lift_move(DOWN) - - if (!can_move_up && !can_move_up_down) - to_chat(user, "[src] doesn't seem to able move anywhere!") + var/list/tool_list = list() + if(lift_master_datum.Check_lift_move(UP)) + tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH) + if(lift_master_datum.Check_lift_move(DOWN)) + tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH) + if(!length(tool_list)) + to_chat(user, span_warning("[src] doesn't seem to able to move anywhere!")) + add_fingerprint(user) + return + if(controls_locked) + to_chat(user, span_warning("[src] has its controls locked! It must already be trying to do something!")) add_fingerprint(user) return - var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE) - if (!is_ghost && !in_range(src, user)) + if(!is_ghost && !in_range(src, user)) return // nice try switch(result) if("Up") - if(can_move_up) - lift_master_datum.MoveLift(UP, user) - show_fluff_message(TRUE, user) - use(user) - else - to_chat(user, "[src] doesn't seem to able move up!") - use(user) + lift_master_datum.MoveLift(UP, user) + show_fluff_message(TRUE, user) + use(user) if("Down") - if(can_move_up_down) - lift_master_datum.MoveLift(DOWN, user) - show_fluff_message(FALSE, user) - use(user) - else - to_chat(user, "[src] doesn't seem to able move down!") - use(user) + lift_master_datum.MoveLift(DOWN, user) + show_fluff_message(FALSE, user) + use(user) if("Cancel") return - add_fingerprint(user) /obj/structure/industrial_lift/proc/check_menu(mob/user) @@ -217,6 +249,14 @@ /obj/structure/industrial_lift/on_attack_hand(mob/user) return use(user) +//ai probably shouldn't get to use lifts but they sure are great for admins to crush people with +/obj/structure/industrial_lift/attack_ghost(mob/user) + . = ..() + if(.) + return + if(IsAdminGhost(user)) + use(user) + /obj/structure/industrial_lift/attack_paw(mob/user) return use(user) @@ -229,11 +269,12 @@ /obj/structure/industrial_lift/proc/show_fluff_message(going_up, mob/user) if(going_up) - user.visible_message("[user] move lift up.", "Lift move up.") + user.visible_message(span_notice("[user] moves the lift upwards."), span_notice("You move the lift upwards.")) else - user.visible_message("[user] move lift down.", "Lift move down.") + user.visible_message(span_notice("[user] moves the lift downwards."), span_notice("You move the lift downwards.")) /obj/structure/industrial_lift/Destroy() + GLOB.lifts.Remove(src) QDEL_NULL(lift_master_datum) var/list/border_lift_platforms = lift_platform_expansion() moveToNullspace() diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index 9fefe0c676..7b6d1ff5c6 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -87,6 +87,12 @@ if(AM) user.start_pulling(AM) + //reopening ladder radial menu ahead + T = get_turf(user) + var/obj/structure/ladder/ladder_structure = locate() in T + if (ladder_structure && (up && down)) + ladder_structure.use(user) + /obj/structure/ladder/proc/use(mob/user, is_ghost=FALSE) if (!is_ghost && !in_range(src, user)) return diff --git a/code/modules/assembly/doorcontrol.dm b/code/modules/assembly/doorcontrol.dm index cc4533f118..7e05f73770 100644 --- a/code/modules/assembly/doorcontrol.dm +++ b/code/modules/assembly/doorcontrol.dm @@ -20,9 +20,9 @@ /obj/item/assembly/control/examine(mob/user) . = ..() if(id && show_id) - . += "Its channel ID is '[id]'." + . += span_notice("Its channel ID is '[id]'.") if(can_change_id) - . += "Use in hand to change ID." + . += span_notice("Use in hand to change ID.") /obj/item/assembly/control/attack_self(mob/living/user) . = ..() @@ -165,3 +165,44 @@ /obj/item/assembly/control/electrochromatic/activate() on = !on do_electrochromatic_toggle(on, id) + +//how long it spends on each floor when moving somewhere, so it'd take 4 seconds to reach you if it had to travel up 2 floors +#define FLOOR_TRAVEL_TIME 2 SECONDS +/obj/item/assembly/control/elevator + name = "elevator controller" + desc = "A small device used to call elevators to the current floor." + +/obj/item/assembly/control/elevator/activate() + if(cooldown) + return + cooldown = TRUE + var/obj/structure/industrial_lift/lift + for(var/l in GLOB.lifts) + var/obj/structure/industrial_lift/possible_lift = l + if(possible_lift.id != id || possible_lift.z == z || possible_lift.controls_locked) + continue + lift = possible_lift + break + if(!lift) + addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 2 SECONDS) + return + lift.visible_message(span_notice("[src] clinks and whirrs into automated motion, locking controls.")) + lift.lift_master_datum.set_controls(LOCKED) + ///The z level to which the elevator should travel + var/targetZ = (abs(loc.z)) //The target Z (where the elevator should move to) is not our z level (we are just some assembly in nullspace) but actually the Z level of whatever we are contained in (e.g. elevator button) + ///The amount of z levels between the our and targetZ + var/difference = abs(targetZ - lift.z) + ///Direction (up/down) needed to go to reach targetZ + var/direction = lift.z < targetZ ? UP : DOWN + ///How long it will/should take us to reach the target Z level + var/travel_duration = FLOOR_TRAVEL_TIME * difference //100 / 2 floors up = 50 seconds on every floor, will always reach destination in the same time + addtimer(VARSET_CALLBACK(src, cooldown, FALSE), travel_duration) + for(var/i in 1 to difference) + sleep(FLOOR_TRAVEL_TIME)//hey this should be alright... right? + if(QDELETED(lift) || QDELETED(src))//elevator control or button gone = don't go up anymore + return + lift.lift_master_datum.MoveLift(direction, null) + lift.visible_message(span_notice("[src] clicks, ready to be manually operated again.")) + lift.lift_master_datum.set_controls(UNLOCKED) + +#undef FLOOR_TRAVEL_TIME