Files
Bubberstation/code/datums/components/uplink.dm
SkyratBot dcde1c04ae [MIRROR] Removes a 'unlock failed' message from PDA uplink code [MDB IGNORE] (#13093)
* Removes a 'unlock failed' message from PDA uplink code (#66458)

* Removes a 'unlock failed' message from PDA uplink code

Co-authored-by: RandomGamer123 <31096837+RandomGamer123@users.noreply.github.com>
2022-04-25 14:28:31 +01:00

413 lines
14 KiB
Plaintext

#define PEN_ROTATIONS 2
/**
* Uplinks
*
* All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
* Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
* You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
**/
/datum/component/uplink
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Name of the uplink
var/name = "syndicate uplink"
/// Whether the uplink is currently active or not
var/active = FALSE
/// Whether this uplink can be locked or not
var/lockable = TRUE
/// Whether the uplink is locked or not.
var/locked = TRUE
/// Whether this uplink allows restricted items to be accessed
var/allow_restricted = TRUE
/// Current owner of the uplink
var/owner = null
/// Purchase log, listing all the purchases this uplink has made
var/datum/uplink_purchase_log/purchase_log
/// The current linked uplink handler.
var/datum/uplink_handler/uplink_handler
/// Code to unlock the uplink.
var/unlock_code
/// Used for pen uplink
var/list/previous_attempts
// Not modular variables. These variables should be removed sometime in the future
/// The unlock text that is sent to the traitor with this uplink. This is not modular and not recommended to expand upon
var/unlock_text
/// The unlock note that is sent to the traitor with this uplink. This is not modular and not recommended to expand upon
var/unlock_note
/// The failsafe code that causes this uplink to blow up.
var/failsafe_code
/datum/component/uplink/Initialize(
owner,
lockable = TRUE,
enabled = FALSE,
uplink_flag = UPLINK_TRAITORS,
starting_tc = TELECRYSTALS_DEFAULT,
has_progression = FALSE,
datum/uplink_handler/uplink_handler_override,
)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact)
if(istype(parent, /obj/item/implant))
RegisterSignal(parent, COMSIG_IMPLANT_ACTIVATED, .proc/implant_activation)
RegisterSignal(parent, COMSIG_IMPLANT_IMPLANTING, .proc/implanting)
RegisterSignal(parent, COMSIG_IMPLANT_OTHER, .proc/old_implant)
RegisterSignal(parent, COMSIG_IMPLANT_EXISTING_UPLINK, .proc/new_implant)
else if(istype(parent, /obj/item/modular_computer/tablet))
RegisterSignal(parent, COMSIG_TABLET_CHANGE_ID, .proc/new_ringtone)
RegisterSignal(parent, COMSIG_TABLET_CHECK_DETONATE, .proc/check_detonate)
else if(istype(parent, /obj/item/radio))
RegisterSignal(parent, COMSIG_RADIO_NEW_FREQUENCY, .proc/new_frequency)
else if(istype(parent, /obj/item/pen))
RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation)
if(owner)
src.owner = owner
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
if(GLOB.uplink_purchase_logs_by_key[owner])
purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
else
purchase_log = new(owner, src)
src.lockable = lockable
src.active = enabled
if(!uplink_handler_override)
uplink_handler = new()
uplink_handler.has_objectives = FALSE
uplink_handler.uplink_flag = uplink_flag
uplink_handler.telecrystals = starting_tc
uplink_handler.has_progression = has_progression
uplink_handler.purchase_log = purchase_log
else
uplink_handler = uplink_handler_override
RegisterSignal(uplink_handler, COMSIG_UPLINK_HANDLER_ON_UPDATE, .proc/handle_uplink_handler_update)
if(!lockable)
active = TRUE
locked = FALSE
previous_attempts = list()
/datum/component/uplink/proc/handle_uplink_handler_update()
SIGNAL_HANDLER
SStgui.update_uis(src)
/// Adds telecrystals to the uplink. It is bad practice to use this outside of the component itself.
/datum/component/uplink/proc/add_telecrystals(telecrystals_added)
set_telecrystals(uplink_handler.telecrystals + telecrystals_added)
/// Sets the telecrystals of the uplink. It is bad practice to use this outside of the component itself.
/datum/component/uplink/proc/set_telecrystals(new_telecrystal_amount)
uplink_handler.telecrystals = new_telecrystal_amount
/datum/component/uplink/InheritComponent(datum/component/uplink/uplink)
lockable |= uplink.lockable
active |= uplink.active
uplink_handler.uplink_flag |= uplink.uplink_handler.uplink_flag
/datum/component/uplink/Destroy()
purchase_log = null
return ..()
/datum/component/uplink/proc/load_tc(mob/user, obj/item/stack/telecrystal/telecrystals, silent = FALSE)
if(!silent)
to_chat(user, span_notice("You slot [telecrystals] into [parent] and charge its internal uplink."))
var/amt = telecrystals.amount
uplink_handler.telecrystals += amt
telecrystals.use(amt)
log_uplink("[key_name(user)] loaded [amt] telecrystals into [parent]'s uplink")
/datum/component/uplink/proc/OnAttackBy(datum/source, obj/item/item, mob/user)
SIGNAL_HANDLER
if(!active)
return //no hitting everyone/everything just to try to slot tcs in!
if(istype(item, /obj/item/stack/telecrystal))
load_tc(user, item)
/datum/component/uplink/proc/interact(datum/source, mob/user)
SIGNAL_HANDLER
if(locked)
return
active = TRUE
if(user)
INVOKE_ASYNC(src, .proc/ui_interact, user)
// an unlocked uplink blocks also opening the PDA or headset menu
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/uplink/ui_state(mob/user)
return GLOB.inventory_state
/datum/component/uplink/ui_interact(mob/user, datum/tgui/ui)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Uplink", name)
// This UI is only ever opened by one person,
// and never is updated outside of user input.
ui.set_autoupdate(FALSE)
ui.open()
/datum/component/uplink/ui_data(mob/user)
if(!user.mind)
return
var/list/data = list()
data["telecrystals"] = uplink_handler.telecrystals
data["progression_points"] = uplink_handler.progression_points
data["current_expected_progression"] = SStraitor.current_global_progression
data["maximum_active_objectives"] = uplink_handler.maximum_active_objectives
data["progression_scaling_deviance"] = SStraitor.progression_scaling_deviance
data["current_progression_scaling"] = SStraitor.current_progression_scaling
data["maximum_potential_objectives"] = uplink_handler.maximum_potential_objectives
if(uplink_handler.has_objectives)
var/list/potential_objectives = list()
for(var/index in 1 to uplink_handler.potential_objectives.len)
var/datum/traitor_objective/objective = uplink_handler.potential_objectives[index]
var/list/objective_data = objective.uplink_ui_data(user)
objective_data["id"] = index
potential_objectives += list(objective_data)
var/list/active_objectives = list()
for(var/index in 1 to uplink_handler.active_objectives.len)
var/datum/traitor_objective/objective = uplink_handler.active_objectives[index]
var/list/objective_data = objective.uplink_ui_data(user)
objective_data["id"] = index
active_objectives += list(objective_data)
data["potential_objectives"] = potential_objectives
data["active_objectives"] = active_objectives
var/list/stock_list = uplink_handler.item_stock.Copy()
var/list/extra_purchasable_stock = list()
var/list/extra_purchasable = list()
for(var/datum/uplink_item/item as anything in uplink_handler.extra_purchasable)
if(item in stock_list)
extra_purchasable_stock[REF(item)] = stock_list[item]
stock_list -= item
extra_purchasable += list(list(
"id" = item.type,
"name" = item.name,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category ? initial(item.category.name) : null,
"purchasable_from" = item.purchasable_from,
"restricted" = item.restricted,
"limited_stock" = item.limited_stock,
"restricted_roles" = item.restricted_roles,
"restricted_species" = item.restricted_species,
"progression_minimum" = item.progression_minimum,
"ref" = REF(item),
))
var/list/remaining_stock = list()
for(var/datum/uplink_item/item as anything in stock_list)
remaining_stock[item.type] = stock_list[item]
data["extra_purchasable"] = extra_purchasable
data["extra_purchasable_stock"] = extra_purchasable_stock
data["current_stock"] = remaining_stock
return data
/datum/component/uplink/ui_static_data(mob/user)
var/list/data = list()
data["uplink_flag"] = uplink_handler.uplink_flag
data["has_progression"] = uplink_handler.has_progression
data["has_objectives"] = uplink_handler.has_objectives
data["lockable"] = lockable
data["assigned_role"] = uplink_handler.assigned_role
data["assigned_species"] = uplink_handler.assigned_species
data["debug"] = uplink_handler.debug_mode
return data
/datum/component/uplink/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/json/uplink)
)
/datum/component/uplink/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(!active)
return
switch(action)
if("buy")
var/datum/uplink_item/item
if(params["ref"])
item = locate(params["ref"]) in uplink_handler.extra_purchasable
if(!item)
return
else
var/datum/uplink_item/item_path = text2path(params["path"])
if(!ispath(item_path, /datum/uplink_item))
return
item = SStraitor.uplink_items_by_type[item_path]
uplink_handler.purchase_item(ui.user, item)
if("lock")
if(!lockable)
return TRUE
active = FALSE
locked = TRUE
SStgui.close_uis(src)
if(!uplink_handler.has_objectives)
return TRUE
if(uplink_handler.owner?.current != ui.user || !uplink_handler.can_take_objectives)
return TRUE
switch(action)
if("regenerate_objectives")
uplink_handler.generate_objectives()
return TRUE
var/list/objectives
switch(action)
if("start_objective")
objectives = uplink_handler.potential_objectives
if("objective_act", "finish_objective", "objective_abort")
objectives = uplink_handler.active_objectives
if(!objectives)
return
var/objective_index = round(text2num(params["index"]))
if(objective_index < 1 || objective_index > length(objectives))
return TRUE
var/datum/traitor_objective/objective = objectives[objective_index]
// Objective actions
switch(action)
if("start_objective")
uplink_handler.take_objective(ui.user, objective)
if("objective_act")
uplink_handler.ui_objective_act(ui.user, objective, params["objective_action"])
if("finish_objective")
if(!objective.finish_objective(ui.user))
return
uplink_handler.complete_objective(objective)
if("objective_abort")
uplink_handler.abort_objective(objective)
return TRUE
// Implant signal responses
/datum/component/uplink/proc/implant_activation()
SIGNAL_HANDLER
var/obj/item/implant/implant = parent
locked = FALSE
interact(null, implant.imp_in)
/datum/component/uplink/proc/implanting(datum/source, list/arguments)
SIGNAL_HANDLER
var/mob/user = arguments[2]
owner = user?.key
if(owner && !purchase_log)
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
if(GLOB.uplink_purchase_logs_by_key[owner])
purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
else
purchase_log = new(owner, src)
/datum/component/uplink/proc/old_implant(datum/source, list/arguments, obj/item/implant/new_implant)
SIGNAL_HANDLER
// It kinda has to be weird like this until implants are components
return SEND_SIGNAL(new_implant, COMSIG_IMPLANT_EXISTING_UPLINK, src)
/datum/component/uplink/proc/new_implant(datum/source, datum/component/uplink/uplink)
SIGNAL_HANDLER
return COMPONENT_DELETE_NEW_IMPLANT
// PDA signal responses
/datum/component/uplink/proc/new_ringtone(datum/source, mob/living/user, new_ring_text)
SIGNAL_HANDLER
if(trim(lowertext(new_ring_text)) != trim(lowertext(unlock_code)))
if(trim(lowertext(new_ring_text)) == trim(lowertext(failsafe_code)))
failsafe(user)
return COMPONENT_STOP_RINGTONE_CHANGE
return
locked = FALSE
interact(null, user)
to_chat(user, span_hear("The computer softly beeps."))
return COMPONENT_STOP_RINGTONE_CHANGE
/datum/component/uplink/proc/check_detonate()
SIGNAL_HANDLER
return COMPONENT_TABLET_NO_DETONATE
// Radio signal responses
/datum/component/uplink/proc/new_frequency(datum/source, list/arguments)
SIGNAL_HANDLER
var/obj/item/radio/master = parent
var/frequency = arguments[1]
if(frequency != unlock_code)
if(frequency == failsafe_code)
failsafe(master.loc)
return
locked = FALSE
if(ismob(master.loc))
interact(null, master.loc)
// Pen signal responses
/datum/component/uplink/proc/pen_rotation(datum/source, degrees, mob/living/carbon/user)
SIGNAL_HANDLER
var/obj/item/pen/master = parent
previous_attempts += degrees
if(length(previous_attempts) > PEN_ROTATIONS)
popleft(previous_attempts)
if(compare_list(previous_attempts, unlock_code))
locked = FALSE
previous_attempts.Cut()
master.degrees = 0
interact(null, user)
to_chat(user, span_warning("Your pen makes a clicking noise, before quickly rotating back to 0 degrees!"))
else if(compare_list(previous_attempts, failsafe_code))
failsafe(user)
/datum/component/uplink/proc/setup_unlock_code()
unlock_code = generate_code()
var/obj/item/P = parent
if(istype(parent,/obj/item/modular_computer/tablet))
unlock_note = "<B>Uplink Passcode:</B> [unlock_code] ([P.name])."
else if(istype(parent,/obj/item/radio))
unlock_note = "<B>Radio Frequency:</B> [format_frequency(unlock_code)] ([P.name])."
else if(istype(parent,/obj/item/pen))
unlock_note = "<B>Uplink Degrees:</B> [english_list(unlock_code)] ([P.name])."
/datum/component/uplink/proc/generate_code()
if(istype(parent,/obj/item/modular_computer/tablet))
return "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
else if(istype(parent,/obj/item/radio))
return return_unused_frequency()
else if(istype(parent,/obj/item/pen))
var/list/L = list()
for(var/i in 1 to PEN_ROTATIONS)
L += rand(1, 360)
return L
/datum/component/uplink/proc/failsafe(mob/living/carbon/user)
if(!parent)
return
var/turf/T = get_turf(parent)
if(!T)
return
message_admins("[ADMIN_LOOKUPFLW(user)] has triggered an uplink failsafe explosion at [AREACOORD(T)] The owner of the uplink was [ADMIN_LOOKUPFLW(owner)].")
log_game("[key_name(user)] triggered an uplink failsafe explosion. The owner of the uplink was [key_name(owner)].")
explosion(parent, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3)
qdel(parent) //Alternatively could brick the uplink.