mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 01:51:46 +00:00
## About The Pull Request the dotted color board was missing a color for the 10th digit, and orange and brown were not valid byond colors (damn you ghommie) fake scrubbers and vents use the correct layer and plane you may no longer deconstruct indestructible windows you can no longer push indestructible grilles and robust windows if you have a strong move force step teleporters may not teleport abstract objects or mirage holders (due to init shenanigans this sometimes teleported a mirage holder messing the visuals up) ## Why It's Good For The Game bug bad also i didnt make these bugs ok ## Changelog 🆑 fix: fixed the museum password puzzle (to the cafeteria), and the scrubbers and vents there now look correctly (also fixed a rare visual bug) fix: it is now harder to bypass indestructible windows and grilles (those are placed there for a reason, you know!) /🆑
669 lines
22 KiB
Plaintext
669 lines
22 KiB
Plaintext
//Every time you got lost looking for keycards, increment: 2
|
|
|
|
//**************
|
|
//*****Keys*****
|
|
//**************
|
|
/obj/item/keycard
|
|
name = "security keycard"
|
|
desc = "This feels like it belongs to a door."
|
|
icon = 'icons/obj/fluff/puzzle_small.dmi'
|
|
icon_state = "keycard"
|
|
force = 0
|
|
throwforce = 0
|
|
w_class = WEIGHT_CLASS_TINY
|
|
throw_speed = 1
|
|
throw_range = 7
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
var/puzzle_id = null
|
|
|
|
/obj/item/keycard/get_save_vars()
|
|
return ..() + NAMEOF(src, puzzle_id)
|
|
|
|
//Two test keys for use alongside the two test doors.
|
|
/obj/item/keycard/yellow
|
|
name = "yellow keycard"
|
|
desc = "A yellow keycard. How fantastic. Looks like it belongs to a high security door."
|
|
color = "#f0da12"
|
|
puzzle_id = "yellow"
|
|
|
|
/obj/item/keycard/blue
|
|
name = "blue keycard"
|
|
desc = "A blue keycard. How terrific. Looks like it belongs to a high security door."
|
|
color = "#3bbbdb"
|
|
puzzle_id = "blue"
|
|
|
|
//***************
|
|
//*****Doors*****
|
|
//***************
|
|
|
|
/obj/machinery/door/puzzle
|
|
name = "locked door"
|
|
desc = "This door only opens under certain conditions. It looks virtually indestructible."
|
|
icon = 'icons/obj/doors/puzzledoor/default.dmi'
|
|
icon_state = "door_closed"
|
|
explosion_block = 3
|
|
heat_proof = TRUE
|
|
max_integrity = 600
|
|
armor_type = /datum/armor/door_puzzle
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
move_resist = MOVE_FORCE_OVERPOWERING
|
|
damage_deflection = 70
|
|
can_open_with_hands = FALSE
|
|
/// Make sure that the puzzle has the same puzzle_id as the keycard door! (If this is null, queuelinks dont happen!)
|
|
var/puzzle_id = null
|
|
/// do we use queue_links?
|
|
var/uses_queuelinks = TRUE
|
|
/// Message that occurs when the door is opened
|
|
var/open_message = "The door beeps, and slides opens."
|
|
|
|
/obj/machinery/door/puzzle/get_save_vars()
|
|
return ..() + NAMEOF(src, puzzle_id)
|
|
|
|
//Standard Expressions to make keycard doors basically un-cheeseable
|
|
/datum/armor/door_puzzle
|
|
melee = 100
|
|
bullet = 100
|
|
laser = 100
|
|
energy = 100
|
|
bomb = 100
|
|
bio = 100
|
|
fire = 100
|
|
acid = 100
|
|
|
|
/obj/machinery/door/puzzle/Initialize(mapload)
|
|
. = ..()
|
|
if(!isnull(puzzle_id) && uses_queuelinks)
|
|
SSqueuelinks.add_to_queue(src, puzzle_id)
|
|
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
|
|
|
|
/obj/machinery/door/puzzle/MatchedLinks(id, list/partners)
|
|
for(var/partner in partners)
|
|
RegisterSignal(partner, COMSIG_PUZZLE_COMPLETED, PROC_REF(try_signal))
|
|
|
|
/obj/machinery/door/puzzle/proc/try_signal(datum/source, try_id)
|
|
SIGNAL_HANDLER
|
|
|
|
puzzle_id = null //honestly these cant be closed anyway and im not fucking around with door code anymore
|
|
INVOKE_ASYNC(src, PROC_REF(try_puzzle_open), null)
|
|
|
|
/obj/machinery/door/puzzle/animation_length(animation)
|
|
switch(animation)
|
|
if(DOOR_OPENING_ANIMATION)
|
|
return 1.0 SECONDS
|
|
|
|
/obj/machinery/door/puzzle/animation_segment_delay(animation)
|
|
switch(animation)
|
|
if(DOOR_OPENING_PASSABLE)
|
|
return 0.8 SECONDS
|
|
if(DOOR_OPENING_FINISHED)
|
|
return 1.0 SECONDS
|
|
|
|
/obj/machinery/door/puzzle/Bumped(atom/movable/AM)
|
|
return !density && ..()
|
|
|
|
/obj/machinery/door/puzzle/ex_act(severity, target)
|
|
return FALSE
|
|
|
|
/obj/machinery/door/puzzle/try_to_activate_door(mob/user, access_bypass = FALSE)
|
|
add_fingerprint(user)
|
|
if(operating)
|
|
return
|
|
|
|
/obj/machinery/door/puzzle/proc/try_puzzle_open(try_id)
|
|
if(puzzle_id && puzzle_id != try_id)
|
|
return FALSE
|
|
if(!density)
|
|
visible_message(span_warning("The door can't seem to be closed."))
|
|
return TRUE
|
|
if(open_message)
|
|
visible_message(span_notice(open_message))
|
|
open()
|
|
return TRUE
|
|
|
|
/obj/machinery/door/puzzle/keycard
|
|
desc = "This door only opens when a keycard is swiped. It looks virtually indestructible."
|
|
uses_queuelinks = FALSE
|
|
|
|
/obj/machinery/door/puzzle/keycard/attackby(obj/item/attacking_item, mob/user, params)
|
|
. = ..()
|
|
if(!istype(attacking_item, /obj/item/keycard))
|
|
return
|
|
var/obj/item/keycard/key = attacking_item
|
|
if(!try_puzzle_open(key.puzzle_id))
|
|
to_chat(user, span_notice("[src] buzzes. This must not be the right key."))
|
|
|
|
//Test doors. Gives admins a few doors to use quickly should they so choose for events.
|
|
/obj/machinery/door/puzzle/keycard/yellow_required
|
|
name = "blue airlock"
|
|
desc = "It looks like it requires a yellow keycard."
|
|
puzzle_id = "yellow"
|
|
|
|
/obj/machinery/door/puzzle/keycard/blue_required
|
|
name = "blue airlock"
|
|
desc = "It looks like it requires a blue keycard."
|
|
puzzle_id = "blue"
|
|
|
|
/obj/machinery/door/puzzle/light
|
|
desc = "This door only opens when a linked mechanism is powered. It looks virtually indestructible."
|
|
|
|
//*************************
|
|
//***Box Pushing Puzzles***
|
|
//*************************
|
|
//We're working off a subtype of pressureplates, which should work just a BIT better now.
|
|
/obj/structure/holobox
|
|
name = "holobox"
|
|
desc = "A hard-light box, containing a secure decryption key."
|
|
icon = 'icons/obj/fluff/puzzle_small.dmi'
|
|
icon_state = "laserbox"
|
|
density = TRUE
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
|
|
//Uses the pressure_plate settings for a pretty basic custom pattern that waits for a specific item to trigger. Easy enough to retool for mapping purposes or subtypes.
|
|
/obj/item/pressure_plate/hologrid
|
|
name = "hologrid"
|
|
desc = "A high power, electronic input port for a holobox, which can unlock the hologrid's storage compartment. Safe to stand on."
|
|
icon = 'icons/obj/fluff/puzzle_small.dmi'
|
|
icon_state = "lasergrid"
|
|
anchored = TRUE
|
|
trigger_mob = FALSE
|
|
trigger_item = TRUE
|
|
specific_item = /obj/structure/holobox
|
|
removable_signaller = FALSE //Being a pressure plate subtype, this can also use signals.
|
|
roundstart_signaller_freq = FREQ_HOLOGRID_SOLUTION //Frequency is kept on its own default channel however.
|
|
active = TRUE
|
|
trigger_delay = 10
|
|
protected = TRUE
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
undertile_pressureplate = FALSE
|
|
var/reward = /obj/item/food/cookie
|
|
var/claimed = FALSE
|
|
|
|
/obj/item/pressure_plate/hologrid/get_save_vars()
|
|
return ..() + NAMEOF(src, reward)
|
|
|
|
/obj/item/pressure_plate/hologrid/Initialize(mapload)
|
|
. = ..()
|
|
if(undertile_pressureplate)
|
|
AddElement(/datum/element/undertile, tile_overlay = tile_overlay, use_anchor = FALSE) //we remove use_anchor here, so it ALWAYS stays anchored
|
|
|
|
/obj/item/pressure_plate/hologrid/examine(mob/user)
|
|
. = ..()
|
|
if(claimed)
|
|
. += span_notice("This one appears to be spent already.")
|
|
|
|
/obj/item/pressure_plate/hologrid/trigger()
|
|
if(!claimed)
|
|
new reward(loc)
|
|
flick("lasergrid_a",src)
|
|
icon_state = "lasergrid_full"
|
|
claimed = TRUE
|
|
|
|
/obj/item/pressure_plate/hologrid/on_entered(datum/source, atom/movable/AM)
|
|
. = ..()
|
|
if(trigger_item && istype(AM, specific_item) && !claimed)
|
|
AM.set_anchored(TRUE)
|
|
flick("laserbox_burn", AM)
|
|
trigger()
|
|
QDEL_IN(AM, 15)
|
|
|
|
//Light puzzle
|
|
/obj/structure/light_puzzle
|
|
name = "light mechanism"
|
|
desc = "It's a mechanism that seems to power something when all the lights are lit up. It looks virtually indestructible."
|
|
icon = 'icons/obj/fluff/puzzle_small.dmi'
|
|
icon_state = "light_puzzle"
|
|
anchored = TRUE
|
|
explosion_block = 3
|
|
armor_type = /datum/armor/structure_light_puzzle
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
light_range = MINIMUM_USEFUL_LIGHT_RANGE
|
|
light_power = 3
|
|
light_color = LIGHT_COLOR_ORANGE
|
|
var/powered = FALSE
|
|
var/puzzle_id = null
|
|
var/list/light_list = list(
|
|
0, 0, 0,
|
|
0, 0, 0,
|
|
0, 0, 0
|
|
)
|
|
/// Banned combinations of the list in decimal
|
|
var/static/list/banned_combinations = list(-1, 47, 95, 203, 311, 325, 422, 473, 488, 500, 511)
|
|
/// queue size, must match count of objects this activates!
|
|
var/queue_size = 2
|
|
|
|
/obj/structure/light_puzzle/get_save_vars()
|
|
return ..() + list(NAMEOF(src, queue_size), NAMEOF(src, puzzle_id))
|
|
|
|
/datum/armor/structure_light_puzzle
|
|
melee = 100
|
|
bullet = 100
|
|
laser = 100
|
|
energy = 100
|
|
bomb = 100
|
|
bio = 100
|
|
fire = 100
|
|
acid = 100
|
|
|
|
/obj/structure/light_puzzle/Initialize(mapload)
|
|
AddElement(/datum/element/blocks_explosives)
|
|
. = ..()
|
|
var/generated_board = -1
|
|
while(generated_board in banned_combinations)
|
|
generated_board = rand(0, 510)
|
|
for(var/i in 0 to 8)
|
|
var/position = !!(generated_board & (1<<i))
|
|
light_list[i+1] = position
|
|
update_icon(UPDATE_OVERLAYS)
|
|
if(!isnull(puzzle_id))
|
|
SSqueuelinks.add_to_queue(src, puzzle_id, queue_size)
|
|
|
|
/obj/structure/light_puzzle/update_overlays()
|
|
. = ..()
|
|
for(var/i in 1 to 9)
|
|
if(!light_list[i])
|
|
continue
|
|
var/mutable_appearance/lit_image = mutable_appearance('icons/obj/fluff/puzzle_small.dmi', "light_lit")
|
|
var/mutable_appearance/emissive_image = emissive_appearance('icons/obj/fluff/puzzle_small.dmi', "light_lit", src)
|
|
lit_image.pixel_x = 8 * ((i % 3 || 3 ) - 1)
|
|
lit_image.pixel_y = -8 * (ROUND_UP(i / 3) - 1)
|
|
emissive_image.pixel_x = lit_image.pixel_x
|
|
emissive_image.pixel_y = lit_image.pixel_y
|
|
. += lit_image
|
|
. += emissive_image
|
|
|
|
/obj/structure/light_puzzle/attack_hand(mob/living/user, list/modifiers)
|
|
if(!modifiers || powered)
|
|
return ..()
|
|
var/light_clicked
|
|
var/x_clicked = text2num(modifiers[ICON_X])
|
|
var/y_clicked = text2num(modifiers[ICON_Y])
|
|
if(x_clicked <= 4 || x_clicked >= 29 || y_clicked <= 4 || y_clicked >= 29)
|
|
return ..()
|
|
x_clicked = ROUND_UP((x_clicked - 4) / 8)
|
|
y_clicked = (-(ROUND_UP((y_clicked - 4) / 8) - 4) - 1) * 3
|
|
light_clicked = x_clicked + y_clicked
|
|
switch_light(light_clicked)
|
|
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
|
|
|
|
/obj/structure/light_puzzle/proc/switch_light(light)
|
|
var/list/updating_lights = list()
|
|
updating_lights += light
|
|
if(light % 3 != 0)
|
|
updating_lights += light + 1
|
|
if(light % 3 != 1)
|
|
updating_lights += light - 1
|
|
if(light + 3 <= 9)
|
|
updating_lights += light + 3
|
|
if(light - 3 > 0)
|
|
updating_lights += light - 3
|
|
for(var/updating_light in updating_lights)
|
|
light_list[updating_light] = !light_list[updating_light]
|
|
update_icon(UPDATE_OVERLAYS)
|
|
for(var/checking_light in light_list)
|
|
if(!checking_light)
|
|
return
|
|
visible_message(span_boldnotice("[src] becomes fully charged!"))
|
|
powered = TRUE
|
|
SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED)
|
|
playsound(src, 'sound/machines/synth/synth_yes.ogg', 100, TRUE)
|
|
|
|
//
|
|
// literally just buttons
|
|
//
|
|
|
|
/obj/machinery/puzzle
|
|
name = "abstract puzzle gizmo"
|
|
icon = 'icons/obj/machines/wallmounts.dmi'
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
/// have we been pressed already?
|
|
var/used = FALSE
|
|
/// can we be pressed only once?
|
|
var/single_use = TRUE
|
|
/// puzzle id we send on press
|
|
var/id //null would literally open every puzzle door without an id
|
|
/// queue size, must match count of objects this activates!
|
|
var/queue_size = 2
|
|
/// should the puzzle machinery perform the final step of the queue link on LateInitialize? An alternative to queue size
|
|
var/late_initialize_pop = FALSE
|
|
|
|
/obj/machinery/puzzle/get_save_vars()
|
|
return ..() + list(NAMEOF(src, queue_size), NAMEOF(src, id))
|
|
|
|
/obj/machinery/puzzle/Initialize(mapload)
|
|
. = ..()
|
|
if(!isnull(id))
|
|
SSqueuelinks.add_to_queue(src, id, late_initialize_pop ? 0 : queue_size)
|
|
return late_initialize_pop ? INITIALIZE_HINT_LATELOAD : .
|
|
|
|
/obj/machinery/puzzle/post_machine_initialize()
|
|
. = ..()
|
|
if(late_initialize_pop && id && SSqueuelinks.queues[id])
|
|
SSqueuelinks.pop_link(id)
|
|
|
|
/obj/machinery/puzzle/proc/on_puzzle_complete() //incase someone wants to make this do something else for some reason
|
|
SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED)
|
|
|
|
/obj/machinery/puzzle/update_icon_state()
|
|
icon_state = "[base_icon_state][used]"
|
|
return ..()
|
|
|
|
/obj/machinery/puzzle/button
|
|
name = "control panel"
|
|
desc = "A panel that controls something nearby. I'm sure it being covered in hazard stripes is fine."
|
|
icon = 'icons/obj/machines/wallmounts.dmi'
|
|
icon_state = "lockdown0"
|
|
base_icon_state = "lockdown"
|
|
|
|
/obj/machinery/puzzle/button/attack_hand(mob/user, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(used && single_use)
|
|
return
|
|
used = single_use
|
|
update_icon_state()
|
|
visible_message(span_notice("[user] presses a button on [src]."), span_notice("You press a button on [src]."))
|
|
playsound(src, 'sound/machines/terminal/terminal_button07.ogg', 45, TRUE)
|
|
on_puzzle_complete()
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/button, 32)
|
|
|
|
/obj/machinery/puzzle/keycardpad
|
|
name = "keycard panel"
|
|
desc = "A panel that controls something nearby. Accepts keycards."
|
|
icon_state = "keycardpad0"
|
|
base_icon_state = "keycardpad"
|
|
|
|
/obj/machinery/puzzle/keycardpad/attackby(obj/item/attacking_item, mob/user, params)
|
|
. = ..()
|
|
if(!istype(attacking_item, /obj/item/keycard) || used)
|
|
return
|
|
var/obj/item/keycard/key = attacking_item
|
|
var/correct_card = key.puzzle_id == id
|
|
balloon_alert_to_viewers("[correct_card ? "correct" : "incorrect"] card swiped[correct_card ? "" : "!"]")
|
|
playsound(src, 'sound/machines/card_slide.ogg', 45, TRUE)
|
|
if(!correct_card)
|
|
return
|
|
used = TRUE
|
|
update_icon_state()
|
|
playsound(src, 'sound/machines/beep/beep.ogg', 45, TRUE)
|
|
on_puzzle_complete()
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/keycardpad, 32)
|
|
|
|
/obj/machinery/puzzle/password
|
|
name = "password panel"
|
|
desc = "A panel that controls something nearby. This one requires a (case-sensitive) password, and it's not \"Swordfish\"."
|
|
icon_state = "passpad0"
|
|
base_icon_state = "passpad"
|
|
///The password to this door.
|
|
var/password = ""
|
|
///The text shown in the tgui input popup
|
|
var/tgui_text = "Please enter the password."
|
|
///The title of the tgui input popup
|
|
var/tgui_title = "What's the password?"
|
|
///Decides whether the max length of the input is MAX_NAME_LEN or the length of the password.
|
|
var/input_max_len_is_pass = FALSE
|
|
|
|
/obj/machinery/puzzle/password/get_save_vars()
|
|
return ..() + list(NAMEOF(src, password), NAMEOF(src, tgui_text), NAMEOF(src, tgui_title), NAMEOF(src, input_max_len_is_pass))
|
|
|
|
/obj/machinery/puzzle/password/interact(mob/user, list/modifiers)
|
|
if(used && single_use)
|
|
return
|
|
if(!user.can_perform_action(src, ALLOW_SILICON_REACH) || !user.can_interact_with(src))
|
|
return
|
|
var/pass_input = tgui_input_text(user, tgui_text, tgui_title, max_length = input_max_len_is_pass ? length(password) : MAX_NAME_LEN)
|
|
if(isnull(pass_input) || !user.can_perform_action(src, ALLOW_SILICON_REACH) || !user.can_interact_with(src))
|
|
return
|
|
var/correct = pass_input == password
|
|
balloon_alert_to_viewers("[correct ? "correct" : "wrong"] password[correct ? "" : "!"]")
|
|
if(!correct)
|
|
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 45, TRUE)
|
|
return
|
|
used = single_use
|
|
update_icon_state()
|
|
playsound(src, 'sound/machines/terminal/terminal_button07.ogg', 45, TRUE)
|
|
on_puzzle_complete()
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/password, 32)
|
|
|
|
/obj/machinery/puzzle/password/pin
|
|
desc = "A panel that controls something nearby. This one requires a PIN password, so let's start by typing in 1234..."
|
|
tgui_text = "Please enter the PIN code."
|
|
tgui_title = "What's the PIN code?"
|
|
input_max_len_is_pass = TRUE
|
|
///The length of the PIN. Suggestion: something between 4 and 12.
|
|
var/pin_length = 6
|
|
///associate a color to each digit that may be found in the password.
|
|
var/list/digit_to_color = list()
|
|
|
|
/obj/machinery/puzzle/password/pin/get_save_vars()
|
|
return ..() + NAMEOF(src, pin_length)
|
|
|
|
/obj/machinery/puzzle/password/pin/Initialize(mapload)
|
|
. = ..()
|
|
|
|
for(var/iteration in 1 to pin_length)
|
|
password += "[rand(1, 9)]"
|
|
|
|
var/list/possible_colors = list(
|
|
"white",
|
|
"black",
|
|
"red",
|
|
"green",
|
|
"blue",
|
|
"yellow",
|
|
COLOR_ORANGE, // orange is also not valid
|
|
COLOR_BROWN, // brown is NOT a valid byond color
|
|
"gray",
|
|
"purple",
|
|
)
|
|
for(var/digit in 0 to 9)
|
|
digit_to_color["[digit]"] = pick_n_take(possible_colors)
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/password/pin, 32)
|
|
|
|
//
|
|
// blockade
|
|
//
|
|
|
|
///blockades destroy themselves if they receive COMSIG_GLOB_PUZZLE_COMPLETED with their ID
|
|
/obj/structure/puzzle_blockade
|
|
name = "shield gate"
|
|
desc = "A wall of solid light, likely defending something important. Virtually indestructible, must be a way around, or to disable it."
|
|
icon = 'icons/effects/effects.dmi'
|
|
icon_state = "wave2"
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF
|
|
move_resist = MOVE_FORCE_OVERPOWERING
|
|
opacity = FALSE
|
|
density = TRUE
|
|
anchored = TRUE
|
|
/// if we receive a puzzle signal with this id we get destroyed
|
|
var/id
|
|
|
|
/obj/structure/puzzle_blockade/get_save_vars()
|
|
return ..() + NAMEOF(src, id)
|
|
|
|
/obj/structure/puzzle_blockade/Initialize(mapload)
|
|
. = ..()
|
|
if(!isnull(id))
|
|
SSqueuelinks.add_to_queue(src, id)
|
|
|
|
/obj/structure/puzzle_blockade/MatchedLinks(id, list/partners)
|
|
for(var/partner in partners)
|
|
RegisterSignal(partner, COMSIG_PUZZLE_COMPLETED, PROC_REF(try_signal))
|
|
|
|
/obj/structure/puzzle_blockade/proc/try_signal(datum/source)
|
|
SIGNAL_HANDLER
|
|
playsound(src, SFX_SPARKS, 100, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
|
|
do_sparks(3, cardinal_only = FALSE, source = src)
|
|
qdel(src)
|
|
|
|
/obj/structure/puzzle_blockade/oneway
|
|
name = "one-way gate"
|
|
desc = "A wall of solid light, likely defending something important. Virtually indestructible."
|
|
icon = 'icons/obj/structures.dmi'
|
|
icon_state = "oneway"
|
|
base_icon_state = "oneway"
|
|
light_color = COLOR_BIOLUMINESCENCE_BLUE
|
|
light_range = 1
|
|
density = FALSE
|
|
|
|
/obj/structure/puzzle_blockade/oneway/update_icon_state()
|
|
icon_state = "[base_icon_state][density ? "" : "-off"]"
|
|
return ..()
|
|
|
|
/obj/structure/puzzle_blockade/oneway/CanAllowThrough(atom/movable/mover, border_dir)
|
|
return ..() && (REVERSE_DIR(border_dir) == dir || get_turf(mover) == get_turf(src))
|
|
|
|
/obj/structure/puzzle_blockade/oneway/CanAStarPass(border_dir, datum/can_pass_info/pass_info)
|
|
return REVERSE_DIR(border_dir) == dir
|
|
|
|
/obj/structure/puzzle_blockade/oneway/try_signal(datum/source)
|
|
density = FALSE
|
|
update_appearance(UPDATE_ICON)
|
|
|
|
/obj/effect/puzzle_poddoor_open
|
|
name = "puzzle-poddoor relay"
|
|
desc = "activates poddoors if activated with a puzzle signal."
|
|
icon = 'icons/effects/mapping_helpers.dmi'
|
|
icon_state = ""
|
|
anchored = TRUE
|
|
invisibility = INVISIBILITY_MAXIMUM
|
|
/// if we receive a puzzle signal with this we do our thing
|
|
var/queue_id
|
|
/// door id
|
|
var/id
|
|
|
|
/obj/effect/puzzle_poddoor_open/get_save_vars()
|
|
return ..() + list(NAMEOF(src, queue_id), NAMEOF(src, id))
|
|
|
|
/obj/effect/puzzle_poddoor_open/Initialize(mapload)
|
|
. = ..()
|
|
if(isnull(id) || isnull(queue_id))
|
|
log_mapping("[src] id:[id] has no id or door id and has been deleted")
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
SSqueuelinks.add_to_queue(src, queue_id)
|
|
|
|
/obj/effect/puzzle_poddoor_open/MatchedLinks(id, list/partners)
|
|
for(var/partner in partners)
|
|
RegisterSignal(partner, COMSIG_PUZZLE_COMPLETED, PROC_REF(try_signal))
|
|
|
|
/obj/effect/puzzle_poddoor_open/proc/try_signal(datum/source)
|
|
SIGNAL_HANDLER
|
|
var/openclose
|
|
for(var/obj/machinery/door/poddoor/door as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/poddoor))
|
|
if(door.id != id)
|
|
continue
|
|
if(isnull(openclose))
|
|
openclose = door.density
|
|
INVOKE_ASYNC(door, openclose ? TYPE_PROC_REF(/obj/machinery/door/poddoor, open) : TYPE_PROC_REF(/obj/machinery/door/poddoor, close))
|
|
|
|
#define MAX_PUZZLE_DOTS_PER_ROW 4
|
|
#define PUZZLE_DOTS_VERTICAL_OFFSET 7
|
|
#define PUZZLE_DOTS_HORIZONTAL_OFFSET 7
|
|
|
|
///A dotted board that can be used as clue for PIN puzzle machinery
|
|
/obj/effect/decal/puzzle_dots
|
|
name = "dotted board"
|
|
desc = "A board filled with colored dots. What could this mean?"
|
|
icon = 'icons/obj/fluff/puzzle_small.dmi'
|
|
icon_state = "puzzle_dots"
|
|
layer = ABOVE_NORMAL_TURF_LAYER
|
|
plane = GAME_PLANE //visible over walls
|
|
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | UNACIDABLE | LAVA_PROOF
|
|
flags_1 = UNPAINTABLE_1
|
|
///The id of the puzzle we're linked to.
|
|
var/id
|
|
|
|
/obj/effect/decal/puzzle_dots/get_save_vars()
|
|
return ..() + NAMEOF(src, id)
|
|
|
|
/obj/effect/decal/puzzle_dots/Initialize(mapload)
|
|
. = ..()
|
|
if(id)
|
|
SSqueuelinks.add_to_queue(src, id)
|
|
|
|
/obj/effect/decal/puzzle_dots/MatchedLinks(id, partners)
|
|
var/obj/machinery/puzzle/password/pin/pad = locate() in partners
|
|
var/list/pass_digits = splittext(pad.password, "")
|
|
var/pass_len = length(pass_digits)
|
|
var/extra_rows = CEILING((pass_len/MAX_PUZZLE_DOTS_PER_ROW)-1, 1)
|
|
if(extra_rows)
|
|
pixel_y += round(extra_rows*(PUZZLE_DOTS_VERTICAL_OFFSET*0.5))
|
|
for(var/i in 1 to extra_rows)
|
|
var/mutable_appearance/row = mutable_appearance(icon, icon_state)
|
|
row.pixel_y = -i*PUZZLE_DOTS_VERTICAL_OFFSET
|
|
add_overlay(row)
|
|
for(var/i in 1 to pass_len)
|
|
var/mutable_appearance/colored_dot = mutable_appearance(icon, "puzzle_dot_single")
|
|
colored_dot.color = pad.digit_to_color[pass_digits[i]]
|
|
colored_dot.pixel_x = PUZZLE_DOTS_HORIZONTAL_OFFSET * ((i-1)%MAX_PUZZLE_DOTS_PER_ROW)
|
|
colored_dot.pixel_y -= CEILING((i/MAX_PUZZLE_DOTS_PER_ROW)-1, 1)*PUZZLE_DOTS_VERTICAL_OFFSET
|
|
add_overlay(colored_dot)
|
|
|
|
#undef MAX_PUZZLE_DOTS_PER_ROW
|
|
#undef PUZZLE_DOTS_VERTICAL_OFFSET
|
|
#undef PUZZLE_DOTS_HORIZONTAL_OFFSET
|
|
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle
|
|
name = "Password character"
|
|
icon_state = "0"
|
|
///The id of the puzzle we're linked to.
|
|
var/puzzle_id
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle/get_save_vars()
|
|
return ..() + NAMEOF(src, puzzle_id)
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
|
. = ..()
|
|
name = "number"
|
|
if(puzzle_id)
|
|
SSqueuelinks.add_to_queue(src, puzzle_id)
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle/MatchedLinks(id, partners)
|
|
var/obj/machinery/puzzle/password/pad = locate() in partners
|
|
var/list/pass_character = splittext(pad.password, "")
|
|
var/chosen_character = icon_state
|
|
if(!findtext(chosen_character, GLOB.is_alphanumeric))
|
|
qdel(src)
|
|
return FALSE
|
|
icon_state = pick(pass_character)
|
|
if(!text2num(icon_state))
|
|
name = "letter"
|
|
desc = "A letter vandalizing the station."
|
|
return TRUE
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle/pin
|
|
name = "PIN number"
|
|
|
|
/obj/effect/decal/cleanable/crayon/puzzle/pin/MatchedLinks(id, partners)
|
|
. = ..()
|
|
var/obj/machinery/puzzle/password/pin/pad = locate() in partners
|
|
add_atom_colour(pad.digit_to_color[icon_state], FIXED_COLOUR_PRIORITY)
|
|
|
|
/obj/item/paper/fluff/scrambled_pass
|
|
name = "gibberish note"
|
|
icon_state = "scrap"
|
|
///The ID associated to the puzzle we're part of.
|
|
var/puzzle_id
|
|
|
|
/obj/item/paper/fluff/scrambled_pass/get_save_vars()
|
|
return ..() + NAMEOF(src, puzzle_id)
|
|
|
|
/obj/item/paper/fluff/scrambled_pass/Initialize(mapload)
|
|
. = ..()
|
|
if(mapload && puzzle_id)
|
|
SSqueuelinks.add_to_queue(src, puzzle_id)
|
|
|
|
/obj/item/paper/fluff/scrambled_pass/MatchedLinks(id, partners)
|
|
var/obj/machinery/puzzle/password/pad = locate() in partners
|
|
var/scrambled_text = ""
|
|
var/list/pass_characters = splittext(pad.password, "")
|
|
for(var/i in 1 to rand(200, 300))
|
|
scrambled_text += pick(pass_characters)
|
|
add_raw_text(scrambled_text)
|