Components! TGUI uplinks! Oh god! (#30641)

* components

* tgui uplink

* Components! TGUI uplinks! Oh god!

* yeah

* this too
This commit is contained in:
DamianX
2021-09-09 14:44:13 +02:00
committed by GitHub
parent 46a32d8b45
commit 95cb0d4d23
34 changed files with 634 additions and 476 deletions

View File

@@ -1,5 +1,10 @@
/datum
var/list/datum_components
/datum/proc/initialize()
return
//Called when a variable is edited by admin powers
//Return 1 to block the varedit!
//Why is this here?
/datum/proc/variable_edited(variable_name, old_value, new_value)
return

View File

@@ -213,12 +213,35 @@
/event/ui_act
//Called when living calls a life() tick
// Called when living calls a life() tick
// Arguments:
// mob/living/L: thing that ticker
// life_ticks: the amounts of lifetick processed
/lazy_event/on_life
// Called by attack_self
// Arguments:
// mob/living/user
/event/item_attack_self
// Called when a PDA's ringtone is about to be changed.
// Arguments:
// mob/user: who's changing the ringtone
// new_ringtone: the new ringtone, string
/event/pda_change_ringtone
// Called when a radio's frequency is about to be changed
// Arguments:
// mob/user: who's changing the frequency
// new_frequency: the new frequency, number
/event/radio_new_frequency
// Called when a mob performs an emote
// Arguments:
// emote: the name of the emote being performed
// mob/source: the mob performing the emote
/event/emote
/datum
/// Associative list of type path -> list(),
/// where the type path is a descendant of /event_type.

View File

@@ -3,6 +3,7 @@
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
. = invoke_event(/event/item_attack_self, list("user" = user))
if(flags & TWOHANDABLE)
if(!(flags & MUSTTWOHAND))
if(wielded)

View File

@@ -0,0 +1,79 @@
/datum/component
var/datum/parent
/datum/component/New(datum/parent, ...)
src.parent = parent
var/list/arguments = args.Copy(2)
if(!initialize(arglist(arguments)))
stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
qdel(src)
return
_join_parent(parent)
/datum/component/Destroy()
if(parent)
_remove_from_parent()
parent = null
..()
/datum/component/proc/_join_parent()
var/datum/P = parent
var/list/dc = P.datum_components
if(!dc)
P.datum_components = dc = list()
dc[type] = src
register_with_parent()
/datum/component/proc/_remove_from_parent()
var/datum/P = parent
var/list/dc = P.datum_components
dc -= type
if(!dc.len)
P.datum_components = null
unregister_from_parent()
/datum/component/proc/register_with_parent()
return
/datum/component/proc/unregister_from_parent()
return
/datum/component/proc/remove()
if(!parent)
return
_remove_from_parent()
parent = null
/datum/proc/get_component(datum/component/c_type)
RETURN_TYPE(c_type)
var/list/dc = datum_components
if(!dc)
return null
return dc[c_type]
/datum/proc/add_component(...)
var/datum/component/new_type = args[1]
if(!ispath(new_type))
CRASH("add_component called with non-path first argument: [new_type]")
if(!isnull(get_component(new_type)))
CRASH("add_component called but [new_type] already exists")
args[1] = src
var/datum/component/new_component = new new_type(arglist(args))
if(!new_component || new_component.gcDestroyed)
CRASH("add_component tried to create new [new_type] but it was deleted")
return new_component
/datum/proc/load_component(datum/component/c_type, ...)
. = get_component(c_type)
if(!.)
return add_component(arglist(args))

View File

@@ -0,0 +1,180 @@
/datum/component/uplink
var/locked = TRUE
var/lockable = TRUE
var/list/purchase_log = list()
var/unlock_code
var/unlock_frequency
var/compact_mode
var/telecrystals = 20
var/selected_category
var/job
var/nuke_ops_inventory = FALSE
/datum/component/uplink/initialize()
if(!isitem(parent))
return FALSE
parent.register_event(/event/attackby, src, .proc/on_attackby)
parent.register_event(/event/item_attack_self, src, .proc/on_attack_self)
if(istype(parent, /obj/item/device/pda))
generate_unlock_code()
parent.register_event(/event/pda_change_ringtone, src, .proc/on_pda_change_ringtone)
if(istype(parent, /obj/item/device/radio))
generate_frequency()
parent.register_event(/event/radio_new_frequency, src, .proc/on_radio_new_frequency)
return TRUE
/datum/component/uplink/Destroy()
parent.unregister_event(/event/attackby, src, .proc/on_attackby)
parent.unregister_event(/event/item_attack_self, src, .proc/on_attack_self)
parent.unregister_event(/event/pda_change_ringtone, src, .proc/on_pda_change_ringtone)
parent.unregister_event(/event/radio_new_frequency, src, .proc/on_radio_new_frequency)
..()
/datum/component/uplink/ui_host(mob/user)
return parent
/datum/component/uplink/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Uplink")
ui.set_autoupdate(FALSE)
ui.open()
/datum/component/uplink/ui_data(mob/user)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compactMode"] = compact_mode
data["selectedCategory"] = selected_category
return data
/datum/component/uplink/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
for(var/category in get_uplink_items(uplink_items))
var/list/cat = list(
"name" = category,
"items" = list()
)
for(var/datum/uplink_item/I in uplink_items[category])
if(!I.available_for_job(job) || (!I.available_for_nuke_ops && nuke_ops_inventory))
continue
cat["items"] += list(list(
"name" = I.name,
"cost" = I.get_cost(job),
"desc" = I.desc,
"discounted" = I.gives_discount(job) || length(I.jobs_exclusive),
"refundable" = I.refundable,
))
if(!length(cat["items"]))
continue
data["categories"] += list(cat)
return data
/datum/component/uplink/ui_state(mob/user)
return global.deep_inventory_state
/datum/component/uplink/ui_act(action, params)
. = ..()
if(.)
return
if(locked)
return
switch(action)
if("buy")
var/item_name = params["name"]
var/list/buyable_items = get_uplink_items(job)
var/datum/uplink_item/item
for(var/category in buyable_items)
for(var/datum/uplink_item/category_item in buyable_items[category])
if(category_item.name == item_name)
item = category_item
break
if(!item)
CRASH("unknown uplink_item [item_name]")
item.buy(src, usr)
return TRUE
if("lock")
locked = TRUE
SStgui.close_uis(src)
if("select")
selected_category = params["category"]
return TRUE
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
if("get_tc")
var/amount = clamp(text2num(params["amount"]), 0, telecrystals)
if(amount == 0)
return
telecrystals -= amount
var/obj/item/stack/telecrystal/R = new(get_turf(usr), amount)
to_chat(usr, "<span class='notice'>You withdraw [amount] telecrystal[amount > 1 ? "s" : ""] from the uplink.</span>")
usr.put_in_hands(R)
return TRUE
// Dumb oldcode
/datum/component/uplink/proc/generate_frequency()
var/freq = 1441
var/static/list/freqlist
if(!freqlist)
freqlist = list()
while (freq <= 1489)
if (freq < 1451 || freq > 1459)
freqlist += freq
freq += 2
if ((freq % 2) == 0)
freq += 1
unlock_frequency = pick(freqlist)
/datum/component/uplink/proc/generate_unlock_code()
unlock_code = "[rand(100,999)] [pick("Alpha","Bravo","Delta","Omega")]"
/datum/component/uplink/proc/on_attackby(mob/living/attacker, obj/item/item)
if(locked)
return
if(istype(item, /obj/item/stack/telecrystal))
var/obj/item/stack/telecrystal/crystals = item
telecrystals += crystals.amount
crystals.use(crystals.amount)
to_chat(attacker, "<span class='notice'>You insert [crystals.amount] telecrystal[crystals.amount > 1 ? "s" : ""] into the uplink.</span>")
return
var/list/items = get_uplink_items()
for(var/category in items)
for(var/category_item in items[category])
var/datum/uplink_item/uplink_item = category_item
if(!uplink_item.refundable)
continue
var/path = uplink_item.refund_path || uplink_item.item
if(!istype(uplink_item, path) || item.check_uplink_validity())
continue
var/cost = uplink_item.refund_amount || uplink_item.cost
telecrystals += cost
to_chat(attacker, "<span class='notice'>[item] refunded.</span>")
qdel(item)
/datum/component/uplink/proc/on_attack_self(mob/user)
if(locked)
return
tgui_interact(user)
return TRUE
/datum/component/uplink/proc/on_pda_change_ringtone(mob/user, new_ringtone)
if(trim(lowertext(new_ringtone)) != trim(lowertext(unlock_code)))
return
locked = FALSE
tgui_interact(user)
return TRUE
/datum/component/uplink/proc/on_radio_new_frequency(mob/user, new_frequency)
if(new_frequency != unlock_frequency)
return
locked = FALSE
tgui_interact(user)
return TRUE

View File

@@ -47,9 +47,7 @@
msg = replace_pronoun(user, msg)
if(isliving(user))
var/mob/living/L = user
for(var/obj/item/weapon/implant/I in L)
I.trigger(key, L)
user.invoke_event(/event/emote, list("emote" = key, "source" = user))
if(!msg)
return

View File

@@ -310,73 +310,16 @@
roles += player.mind.assigned_role
return roles
/proc/equip_traitor(mob/living/carbon/human/traitor_mob, var/uses = 20, var/datum/role/traitor/role)
if (!istype(traitor_mob))
return
. = 1
// find a radio! toolbox(es), backpack, belt, headset
var/loc = ""
var/list/contents = recursive_type_check(traitor_mob, /obj/item/device)
var/obj/item/R = locate(/obj/item/device/pda) in contents //Hide the uplink in a PDA if available, otherwise radio
if(!R)
R = locate(/obj/item/device/radio) in contents
if (!R)
to_chat(traitor_mob, "Unfortunately, the Syndicate wasn't able to get you a radio.")
. = 0
else
var/obj/item/device/uplink/hidden/T
if (istype(R, /obj/item/device/radio))
// generate list of radio freqs
var/obj/item/device/radio/target_radio = R
var/freq = 1441
var/list/freqlist = list()
while (freq <= 1489)
if (freq < 1451 || freq > 1459)
freqlist += freq
freq += 2
if ((freq % 2) == 0)
freq += 1
freq = freqlist[rand(1, freqlist.len)]
T = new(R)
T.uses = uses
target_radio.hidden_uplink = T
target_radio.traitor_frequency = freq
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name] [loc]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features.")
traitor_mob.mind.store_memory("<B>Radio Freq:</B> [format_frequency(freq)] ([R.name] [loc]).")
traitor_mob.mind.total_TC += target_radio.hidden_uplink.uses
else if (istype(R, /obj/item/device/pda))
// generate a passcode if the uplink is hidden in a PDA
var/pda_pass = "[rand(100,999)] [pick("Alpha","Bravo","Delta","Omega")]"
T = new(R)
R.hidden_uplink = T
var/obj/item/device/pda/P = R
P.lock_code = pda_pass
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name] [loc]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features.")
traitor_mob.mind.store_memory("<B>Uplink Passcode:</B> [pda_pass] ([R.name] [loc]).")
traitor_mob.mind.total_TC += R.hidden_uplink.uses
if (role && T)
role.uplink = T
/datum/mind/proc/find_syndicate_uplink(var/obj/item/device/uplink/true_uplink)
var/uplink = null
/datum/mind/proc/find_syndicate_uplink(datum/component/uplink/true_uplink)
for (var/obj/item/I in get_contents_in_object(current, /obj/item))
if (I && I.hidden_uplink)
uplink = I.hidden_uplink
break
var/datum/component/uplink/uplink_comp = I.get_component(/datum/component/uplink)
if(uplink_comp)
return uplink_comp
if (!uplink && true_uplink)
return true_uplink//returns the uplink they spawned with rather than the one they are currently carrying
return uplink
return true_uplink // returns the uplink they spawned with rather than the one they are currently carrying, or null
/datum/mind/proc/take_uplink()
var/obj/item/device/uplink/hidden/H = find_syndicate_uplink()
var/datum/component/uplink/H = find_syndicate_uplink()
if(H)
message_admins("Found and deleted [H] for [src].")
qdel(H)

View File

@@ -95,8 +95,8 @@ var/list/assassination_objectives = list()
if (A.syndicate_checked)
continue
var/obj/item/device/uplink/hidden/owner_uplink = owner.find_syndicate_uplink()
var/obj/item/device/uplink/hidden/enemy_uplink = target.find_syndicate_uplink(enemy.uplink)
var/datum/component/uplink/owner_uplink = owner.find_syndicate_uplink()
var/datum/component/uplink/enemy_uplink = target.find_syndicate_uplink(enemy.uplink)
//chances are the target's uplink is no longer on their mind.current especially if they got decapitated or such.
//by associating the uplink with the role we can at least try and get the TCs out of it.
@@ -104,10 +104,10 @@ var/list/assassination_objectives = list()
to_chat(owner.current, "<span class='notice'>The Syndicate congratulates you on your Victory. Look forward to be assigned on higher risk operations another day.</span>")
else
if (owner_uplink)
owner_uplink.uses += DOUBLE_AGENT_TC_REWARD
owner_uplink.telecrystals += DOUBLE_AGENT_TC_REWARD
if (enemy_uplink)
owner_uplink.uses += enemy_uplink.uses
enemy_uplink.uses = 0
owner_uplink.telecrystals += enemy_uplink.telecrystals
enemy_uplink.telecrystals = 0
to_chat(owner.current, "<span class='notice'>Good work agent. [DOUBLE_AGENT_TC_REWARD] additional tele-crystals have been sent to your uplink.</span>")
else
to_chat(owner.current, "<span class='notice'>Good work agent. Unfortunately we couldn't find your uplink on your person, so no additional tele-crystals could be distributed.</span>")
@@ -117,8 +117,8 @@ var/list/assassination_objectives = list()
to_chat(owner.current, "<b>New Objective</b>: [new_kill_target.explanation_text]<br>")
if (owner_uplink && enemy_uplink)
owner_uplink.uses += enemy_uplink.uses
enemy_uplink.uses = 0
owner_uplink.telecrystals += enemy_uplink.telecrystals
enemy_uplink.telecrystals = 0
A.syndicate_checked = SYNDICATE_CANCELED
to_chat(target.current, "<span class='warning'>The Syndicate has taken note of your demise. You are therefore ineligible for victory this time around. Better luck next time!</span>")

View File

@@ -7,7 +7,7 @@
default_admin_voice = "The Syndicate"
admin_voice_style = "syndradio"
var/can_be_smooth = TRUE //Survivors can't be smooth because they get nothing.
var/obj/item/device/uplink/hidden/uplink//so we keep track of where the uplink they spawn with ends up
var/datum/component/uplink/uplink //so we keep track of where the uplink they spawn with ends up
/datum/role/traitor/OnPostSetup()
..()
@@ -81,9 +81,9 @@
/datum/role/traitor/extraPanelButtons()
var/dat = ""
var/obj/item/device/uplink/hidden/guplink = antag.find_syndicate_uplink()
var/datum/component/uplink/guplink = antag.find_syndicate_uplink()
if(guplink)
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];telecrystalsSet=1;'>Telecrystals: [guplink.uses](Set telecrystals)</a><br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];telecrystalsSet=1;'>Telecrystals: [guplink.telecrystals](Set telecrystals)</a><br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];removeuplink=1;'>(Remove uplink)</a><br>"
else
dat = " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];giveuplink=1;'>(Give uplink)</a><br>"
@@ -94,11 +94,11 @@
if(href_list["giveuplink"])
equip_traitor(antag.current, 20, src)
if(href_list["telecrystalsSet"])
var/obj/item/device/uplink/hidden/guplink = M.find_syndicate_uplink()
var/amount = input("What would you like to set their crystal count to?", "Their current count is [guplink.uses]") as null|num
var/datum/component/uplink/guplink = M.find_syndicate_uplink()
var/amount = input("What would you like to set their crystal count to?", "Their current count is [guplink.telecrystals]") as null|num
if(isnum(amount) && amount >= 0)
to_chat(usr, "<span class = 'notice'>You have set [M]'s uplink telecrystals to [amount].</span>")
guplink.uses = amount
guplink.telecrystals = amount
if(href_list["removeuplink"])
M.take_uplink()
@@ -133,6 +133,37 @@
else
. += "The traitor was a smooth operator this round.<BR>"
/datum/role/traitor/proc/equip_traitor(mob/living/carbon/human/traitor_mob, var/uses = 20)
. = FALSE
if (!istype(traitor_mob))
return
var/list/contents = recursive_type_check(traitor_mob, /obj/item/device)
var/datum/component/uplink/new_uplink
// Hide the uplink in a PDA if available, otherwise radio
var/obj/item/device/pda/found_pda = locate() in contents
if(found_pda)
new_uplink = found_pda.add_component(/datum/component/uplink)
traitor_mob.mind.store_memory("<B>Uplink Passcode:</B> [new_uplink.unlock_code] ([found_pda.name]).")
traitor_mob.mind.total_TC += new_uplink.telecrystals
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [found_pda.name]. Simply enter the code \"[new_uplink.unlock_code]\" as its ringtone to unlock its hidden features.")
. = TRUE
else
var/obj/item/device/radio/found_radio = locate() in contents
if(found_radio)
new_uplink = found_radio.add_component(/datum/component/uplink)
traitor_mob.mind.store_memory("<B>Uplink frequency:</B> [format_frequency(new_uplink.unlock_frequency)] ([found_radio.name]).")
traitor_mob.mind.total_TC += new_uplink.telecrystals
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [found_radio.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_frequency)] to unlock its hidden features.")
. = TRUE
if (new_uplink)
uplink = new_uplink
new_uplink.job = traitor_mob.mind.assigned_role
else
to_chat(traitor_mob, "Unfortunately, the Syndicate wasn't able to get you an uplink.")
//________________________________________________
@@ -239,4 +270,4 @@
if (confirm == "Yes" && M.stat == CONSCIOUS)
ticker.StartThematic(linkedfaction.playlist)
command_alert(/datum/command_alert/nuclear_operatives)
qdel(src)
qdel(src)

View File

@@ -26,7 +26,7 @@ var/list/datum/map_element/map_elements = list()
return FALSE
return TRUE
/datum/map_element/proc/initialize(list/objects) //Called after loading the element. The "objects" list contains all spawned atoms
/datum/map_element/initialize(list/objects) //Called after loading the element. The "objects" list contains all spawned atoms
map_elements.Add(src)
if(!location && objects.len)

View File

@@ -106,7 +106,7 @@
//initialize() proc - called automatically in proc/setup_shuttles() below.
//Returns INIT_SUCCESS, INIT_NO_AREA, INIT_NO_START or INIT_NO_PORT, depending on whether there were any errors
/datum/shuttle/proc/initialize()
/datum/shuttle/initialize()
. = INIT_SUCCESS
src.docking_ports = list()
src.docking_ports_aboard = list()

View File

@@ -41,8 +41,8 @@ var/list/uplink_items = list()
var/list/jobs_exclusive = list() //If empty, does nothing. If not empty, ONLY jobs in this list can buy this item.
var/list/jobs_excluded = list() //Jobs in this list cannot buy this item at all.
var/list/roles_exclusive = list() //If empty, does nothing. If not empty, ONLY roles in this list can buy this item.
var/list/roles_excluded = list() //Roles in this list cannot buy this item at all.
var/available_for_traitors = TRUE
var/available_for_nuke_ops = TRUE
var/only_on_month //two-digit month as string
var/only_on_day //two-digit day as string
var/num_in_stock = 0 // Number of times this can be bought, globally. 0 is infinite
@@ -62,19 +62,9 @@ var/list/uplink_items = list()
return user_job && jobs_with_discount.len && jobs_with_discount.Find(user_job)
/datum/uplink_item/proc/available_for_job(var/user_job)
return user_job && !(jobs_exclusive.len && !jobs_exclusive.Find(user_job)) && !(jobs_excluded.len && jobs_excluded.Find(user_job))
/datum/uplink_item/proc/available_for_role(var/list/roles)
if (roles_exclusive.len)
for (var/role in roles_exclusive)
if (role in roles)
return TRUE
return FALSE
else
for (var/role in roles_excluded)
if (role in roles)
return FALSE
if(!user_job)
return TRUE
return !(jobs_exclusive.len && !jobs_exclusive.Find(user_job)) && !(jobs_excluded.len && jobs_excluded.Find(user_job))
//This will get called that is essentially a New() by default.
//Use this to make New()s that have extra conditions, such as bundles
@@ -82,23 +72,18 @@ var/list/uplink_items = list()
/datum/uplink_item/proc/new_uplink_item(var/new_item, var/turf/location, mob/user)
return new new_item(location)
/datum/uplink_item/proc/spawn_item(var/turf/loc, var/obj/item/device/uplink/U, mob/user)
/datum/uplink_item/proc/spawn_item(var/turf/loc, datum/component/uplink/U, mob/user)
if(!available_for_job(U.job))
message_admins("[key_name(user)] tried to purchase \the [src.name] from their uplink despite not being available to their job! (Job: [U.job]) ([formatJumpTo(get_turf(U))])")
return
if(!available_for_role(U.roles))
var/dat = ""
for (var/role in roles_exclusive)
if (dat)
dat+= ", "
dat += role
message_admins("[key_name(user)] tried to purchase \the [src.name] from their uplink despite not being available to their role! (Role: [dat]) ([formatJumpTo(get_turf(U))])")
if(U.nuke_ops_inventory && !available_for_nuke_ops)
message_admins("[key_name(user)] tried to purchase \the [src.name] from their uplink despite being a nuclear operative")
return
U.uses -= max(get_cost(U.job), 0)
U.telecrystals -= max(get_cost(U.job), 0)
feedback_add_details("traitor_uplink_items_bought", name)
return new_uplink_item(item, loc, user)
/datum/uplink_item/proc/buy(var/obj/item/device/uplink/hidden/U, var/mob/user)
/datum/uplink_item/proc/buy(datum/component/uplink/U, var/mob/user)
if(!istype(U))
return 0
@@ -113,9 +98,10 @@ var/list/uplink_items = list()
return 0
// If the uplink's holder is in the user's contents
if ((U.loc in user.contents || (in_range(U.loc, user) && istype(U.loc.loc, /turf))))
var/obj/item/holder = U.parent
if ((holder in user.contents || (in_range(holder, user) && istype(holder.loc, /turf))))
user.set_machine(U)
if(get_cost(U.job) > U.uses)
if(get_cost(U.job) > U.telecrystals)
return 0
var/obj/I = spawn_item(get_turf(user), U, user)
@@ -154,8 +140,6 @@ var/list/uplink_items = list()
R = user.mind.GetRole(CHALLENGER)
if(R)
R.uplink_items_bought += {"<img class='icon' src='data:image/png;base64,[iconsouth2base64(tempimage)]'> [bundlename] for [get_cost(U.job)] TC<BR>"}
U.interact(user)
return 1
return 0
@@ -610,8 +594,7 @@ var/list/uplink_items = list()
item = /obj/item/weapon/storage/box/syndicate
cost = 0
/datum/uplink_item/badass/random/spawn_item(var/turf/loc, var/obj/item/device/uplink/U, user)
/datum/uplink_item/badass/random/spawn_item(var/turf/loc, var/datum/component/uplink/U, user)
var/list/buyable_items = get_uplink_items()
var/list/possible_items = list()
@@ -621,15 +604,15 @@ var/list/uplink_items = list()
continue
if(!I.available_for_job(U.job))
continue
if(!I.available_for_role(U.roles))
if(!I.available_for_nuke_ops && U.nuke_ops_inventory)
continue
if(I.get_cost(U.job, 0.5) > U.uses)
if(I.get_cost(U.job, 0.5) > U.telecrystals)
continue
possible_items += I
if(possible_items.len)
var/datum/uplink_item/I = pick(possible_items)
U.uses -= max(0, I.get_cost(U.job, 0.5))
U.telecrystals -= max(0, I.get_cost(U.job, 0.5))
feedback_add_details("traitor_uplink_items_bought","RN")
return new_uplink_item(I.item, loc, user)
@@ -1128,7 +1111,7 @@ var/list/uplink_items = list()
/datum/uplink_item/syndie_coop
category = "Cooperative Cell"
roles_exclusive = list(TRAITOR)
available_for_nuke_ops = FALSE
/datum/uplink_item/syndie_coop/elite_bundle
name = "Elite Syndicate Bundle"

View File

@@ -17,25 +17,6 @@
to_chat(viewers(user), "<span class='danger'>[user] wraps the cord of the [src.name] around \his neck! It looks like \he's trying to commit suicide.</span>")
return(SUICIDE_ACT_OXYLOSS)
/*/obj/item/weapon/syndicate_uplink
name = "station bounced radio"
desc = "Remain silent about this..."
icon = 'icons/obj/radio.dmi'
icon_state = "radio"
var/temp = null
var/uses = 10.0
var/selfdestruct = 0.0
var/traitor_frequency = 0.0
var/mob/currentUser = null
var/obj/item/device/radio/origradio = null
flags = FPRINT | CONDUCT | ONBELT
w_class = W_CLASS_SMALL
item_state = "radio"
throw_speed = 4
throw_range = 20
m_amt = 100
origin_tech = Tc_MAGNETS + "=2;" + Tc_SYNDICATE + "=3"*/
/obj/item/weapon/rsp
name = "\improper Rapid-Seed-Producer (RSP)"
desc = "A device used to rapidly deploy seeds."

View File

@@ -872,7 +872,7 @@ its easier to just keep the beam vertical.
if(uppertext(C.ckey) == uppertext(fingerprintslast))
return C.mob
/atom/proc/initialize()
/atom/initialize()
flags |= ATOM_INITIALIZED
/atom/proc/get_cell()

View File

@@ -47,7 +47,6 @@
var/armor_absorb = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
var/list/allowed = null //suit storage stuff.
var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers.
var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc.
var/list/species_fit = null //This object has a different appearance when worn by these species
var/nonplant_seed_type
@@ -281,11 +280,6 @@
/obj/item/attack_paw(var/mob/user)
attack_hand(user)
// Due to storage type consolidation this should get used more now.
// I have cleaned it up a little, but it could probably use more. -Sayu
/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob)
return ..()
/obj/item/proc/talk_into(var/datum/speech/speech, var/channel=null)
return

View File

@@ -612,7 +612,7 @@ var/global/msg_id = 0
aiPDA.photo.pixel_y = q.fields["pixel_y"]
aiPDA.photo.blueprints = q.fields["blueprints"]
break
aiPDA.create_message(src, selected)
aiPDA.photo = null
@@ -658,7 +658,7 @@ var/global/msg_id = 0
photo.pixel_y = q.fields["pixel_y"]
photo.blueprints = q.fields["blueprints"]
break
create_message(usr, selected)
photo = null
@@ -777,7 +777,8 @@ var/global/msg_id = 0
if (map_app && map_app.holomap)
map_app.holomap.stopWatching()
if(active_uplink_check(user))
. = ..()
if(.)
return
if(user.client)
@@ -2111,13 +2112,14 @@ var/global/msg_id = 0
var/t = input(U, "Please enter new ringtone", name, ttone) as text
if (in_range(U, src) && loc == U)
if (t)
if(src.hidden_uplink && hidden_uplink.check_trigger(U, trim(lowertext(t)), trim(lowertext(lock_code))))
if(invoke_event(/event/pda_change_ringtone, list("user" = U, "new_ringtone" = t)))
to_chat(U, "The PDA softly beeps.")
U << browse(null, "window=pda")
src.mode = 0
else
t = copytext(sanitize(t), 1, 20)
ttone = t
return
else
U << browse(null, "window=pda")
return
@@ -2285,7 +2287,7 @@ var/global/msg_id = 0
else
difficulty += 2
if(P.hidden_uplink)
if(P.get_component(/datum/component/uplink))
U.show_message("<span class='warning'>An error flashes on your [src]; [pick(syndicate_code_response)]</span>", 1)
U << browse(null, "window=pda")
create_message(null, P, null, null, pick(syndicate_code_phrase)) //friendly fire
@@ -2492,7 +2494,7 @@ var/global/msg_id = 0
return
var/obj/item/weapon/photo/current_photo = null
if(photo)
current_photo = photo
@@ -2504,7 +2506,7 @@ var/global/msg_id = 0
if(current_photo)
imglist["[msg_id]"] = current_photo.img
P.imglist["[msg_id]"] = current_photo.img
useMS.send_pda_message("[P.owner]","[owner]","[t]",imglist["[msg_id]"])
tnote["[msg_id]"] = "<i><b>&rarr; To [P.owner]:</b></i><br>[t]<br>"
@@ -2623,8 +2625,8 @@ obj/item/device/pda/AltClick()
// access to status display signals
/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob)
..()
if(hidden_uplink && hidden_uplink.active && hidden_uplink.refund(user, C))
. = ..()
if(.)
return
if(istype(C, /obj/item/weapon/cartridge) && !cartridge)
if(user.drop_item(C, src))

View File

@@ -38,6 +38,9 @@
return TRUE
/obj/item/device/multitool/attack_self(var/mob/user)
. = ..()
if(.)
return
if(!buffer?.get() && !clone) // Can't enable cloning without buffer.
return

View File

@@ -243,7 +243,7 @@
/obj/item/device/radio/headset/attackby(obj/item/weapon/W, mob/user)
// ..()
if(hidden_uplink && hidden_uplink.active && hidden_uplink.refund(user, W))
if(invoke_event(/event/attackby, list("attacker" = user, "item" = W)))
return
user.set_machine(src)
if (!( W.is_screwdriver(user) || (istype(W, /obj/item/device/encryptionkey/ ))))

View File

@@ -7,7 +7,6 @@
var/on = 1 // 0 for off
var/last_transmission
var/frequency = 1459
var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies
var/canhear_range = 3 // the range which mobs can hear this radio from
var/obj/item/device/radio/patch_link = null
var/datum/wires/radio/wires = null
@@ -59,7 +58,7 @@
radio_list -= src
remove_radio_all(src) //Just to be sure
..()
/obj/item/device/radio/initialize()
. = ..()
frequency = COMMON_FREQ //common chat
@@ -86,9 +85,6 @@
if(!on)
return
if(active_uplink_check(user))
return
var/dat = "<html><head><title>[src]</title></head><body><TT>"
dat += "Microphone: [broadcasting ? "<A href='byond://?src=\ref[src];talk=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];talk=1'>Disengaged</A>"]<BR>"
@@ -122,12 +118,6 @@
<B>[chan_name]</B>: <A href='byond://?src=\ref[src];ch_name=[chan_name];listen=[!list]'>[list ? "Engaged" : "Disengaged"]</A><BR>
"}
/obj/item/device/radio/proc/check_traitor_uplink(frequency)
if(hidden_uplink)
if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency))
usr << browse(null, "window=radio")
return 1
/obj/item/device/radio/Topic(href, href_list)
if (!isAdminGhost(usr) && (usr.stat || !on))
return
@@ -147,17 +137,15 @@
var/new_frequency
new_frequency = input(usr, "Set a new frequency (1200-1600 kHz).", src, frequency) as null|num
new_frequency = sanitize_frequency(new_frequency, maxf)
set_frequency(new_frequency)
if (check_traitor_uplink(frequency))
return
if(!invoke_event(/event/radio_new_frequency, list("user" = usr, "new_frequency" = new_frequency)))
set_frequency(new_frequency)
else if (href_list["freq"])
var/new_frequency
new_frequency = (frequency + text2num(href_list["freq"]))
new_frequency = sanitize_frequency(new_frequency, maxf)
set_frequency(new_frequency)
if (check_traitor_uplink(frequency))
return
if(!invoke_event(/event/radio_new_frequency, list("user" = usr, "new_frequency" = new_frequency)))
set_frequency(new_frequency)
else if (href_list["talk"])
broadcasting = text2num(href_list["talk"])
@@ -508,8 +496,8 @@
interact(user)
/obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob)
..()
if(hidden_uplink && hidden_uplink.active && hidden_uplink.refund(user, W))
. = ..()
if(.)
return
user.set_machine(src)
if (!(W.is_screwdriver(user)))

View File

@@ -6,296 +6,29 @@ A list of items and costs is stored under the datum of every game mode, alongsid
*/
/obj/item/device/uplink
var/welcome // Welcoming menu message
var/uses // Number of crystals
// List of items not to shove in their hands.
var/list/purchase_log = list()
var/show_description = null
var/active = 0
var/job = null
var/list/roles = list()
/obj/item/device/uplink/New()
..()
if(ticker)
initialize()
return
/obj/item/device/uplink/initialize()
if(ticker.mode)
welcome = "Syndicate Uplink Console"
uses = 20
else
welcome = "THANKS FOR MAPPING IN THIS THING AND NOT CHECKING FOR RUNTIMES BUDDY"
uses = 90 // Because this is only happening on centcomm's snowflake uplink
/obj/item/device/uplink/proc/refund(mob/living/carbon/human/user, obj/item/I)
if(!user || !I)
return
if (istype(I, /obj/item/stack/telecrystal))
var/obj/item/stack/telecrystal/S = I
uses += S.amount
user.drop_item(S, src)
to_chat(user, "<span class='notice'>You insert [S.amount] telecrystal[S.amount > 1 ? "s" : ""] into the uplink.</span>")
qdel(S)
if(!uplink_items)
get_uplink_items()
for(var/category in uplink_items)
for(var/item in uplink_items[category])
var/datum/uplink_item/UI = item
var/path = UI.refund_path || UI.item
var/cost = UI.refund_amount || UI.cost
if(istype(I, path) && UI.refundable && I.check_uplink_validity())
uses += cost
to_chat(user, "<span class='notice'>[I] refunded.</span>")
qdel(I)
return TRUE
return FALSE
//Let's build a menu!
/obj/item/device/uplink/proc/generate_menu(var/mob/user)
if(!job)
job = user.mind.assigned_role
roles = list()
for (var/role in user.mind.antag_roles)
var/datum/role/R = user.mind.antag_roles[role]
roles += R.id
var/dat = list()
dat += "<B>[src.welcome]</B><BR>"
dat += {"Tele-Crystals left: [src.uses]<BR>
<HR>
<B>Request item:</B><BR>
<I>Each item costs a number of tele-crystals as indicated by the number following their name.</I><br><BR>"}
var/list/buyable_items = get_uplink_items()
// Loop through categories
var/index = 0
for(var/category in buyable_items)
index++
dat += "<b>[category]</b><br>"
var/discounted_list = list() //These go on top.
var/jobexclusive_list = list()
var/nondiscounted_list = list() //These go on the bottom.
var/i = 0
// Loop through items in category
for(var/datum/uplink_item/item in buyable_items[category])
i++
if(!item.available_for_job(job))
continue
var/itemcost = item.get_cost(job)
var/cost_text = ""
var/desc = "[item.desc]"
var/final_text = ""
if(itemcost > 0)
if(item.gives_discount(job) || item.jobs_exclusive.len)
cost_text = "<span style='color: yellow; font-weight: bold;'>([itemcost]!)</span>"
else
cost_text = "([itemcost])"
if(itemcost <= uses)
final_text += "<A href='byond://?src=\ref[src];buy_item=[url_encode(category)]:[i];'>[item.name]</A> [cost_text] "
else
final_text += "<font color='grey'><i>[item.name] [cost_text] </i></font>"
if(item.refundable)
final_text += "<span style='color: yellow;'>\[R\]</span>"
if(item.desc)
if(show_description == 2)
final_text += "<A href='byond://?src=\ref[src];show_desc=1'><font size=2>\[-\]</font></A><BR><font size=2>[desc][item.refundable ? " Use this item on your uplink to refund it for [item.refund_amount || item.cost] TC.":""]</font>"
else
final_text += "<A href='byond://?src=\ref[src];show_desc=2' title='[html_encode(desc)]'><font size=2>\[?\]</font></A>"
final_text += "<BR>"
if(item.gives_discount(job))
discounted_list += final_text
else if(item.jobs_exclusive.len) //If we don't match this thing's job, we already exited out, so we don't need to check again
jobexclusive_list += final_text
else
nondiscounted_list += final_text
for(var/text in discounted_list|jobexclusive_list|nondiscounted_list) //Discounted first, nondiscounted later.
dat += text
// Break up the categories, if it isn't the last.
if(buyable_items.len != index)
dat += "<br>"
dat += "<HR>"
if (uses)
dat += "<a href='byond://?src=\ref[src];get_tc=1'>Extract telecrystals</a><br/>"
else
dat += "<font color='grey'><i>Extract telecrystals</i>/</font><br/>"
dat = jointext(dat,"") //Optimize BYOND's shittiness by making "dat" actually a list of strings and join it all together afterwards! Yes, I'm serious, this is actually a big deal
return dat
// Interaction code. Gathers a list of items purchasable from the paren't uplink and displays it. It also adds a lock button.
/obj/item/device/uplink/interact(mob/user as mob)
var/dat = "<body link='yellow' alink='white' bgcolor='#601414'><font color='white'>"
dat += src.generate_menu(user)
dat += {"<A href='byond://?src=\ref[src];lock=1'>Lock</a>
</font></body>"}
user << browse(dat, "window=hidden")
onclose(user, "hidden")
return
/obj/item/device/uplink/Topic(href, href_list)
..()
if (!is_holder_of(usr, src))
message_admins("[key_name(usr)] tried to access [src], an unlocked PDA, despite not being its holder. ([formatJumpTo(get_turf(src))])")
return FALSE
if(!active)
return
if (href_list["buy_item"])
var/item = href_list["buy_item"]
var/list/split = splittext(item, ":") // throw away variable
if(split.len == 2)
// Collect category and number
var/category = split[1]
var/number = text2num(split[2])
if(!job) //Should never happen unless the user somehow sends out a Topic() call before opening their uplink, but just in case.
job = usr.mind.assigned_role
var/list/buyable_items = get_uplink_items(job)
var/list/uplink = buyable_items[category]
if(uplink && uplink.len >= number)
var/datum/uplink_item/I = uplink[number]
if(I)
I.buy(src, usr)
else
var/text = "[key_name(usr)] tried to purchase an uplink item that doesn't exist"
var/textalt = "[key_name(usr)] tried to purchase an uplink item that doesn't exist [item]"
message_admins(text)
log_game(textalt)
admin_log.Add(textalt)
else if(href_list["show_desc"])
show_description = text2num(href_list["show_desc"])
interact(usr)
else if(href_list["get_tc"])
if (uses <= 0)
return
var/amount = input("How many telecrystals do you wish to withdraw?:", "Extract telecrystals", null) as num
if(!usr.Adjacent(src))
return
amount = clamp(amount, 0, uses)
if (amount <= 0)
return
uses -= amount
var/obj/item/stack/telecrystal/R = new /obj/item/stack/telecrystal(usr, amount)
var/mob/living/carbon/human/H = usr
to_chat(usr, "<span class='notice'>You withdraw [amount] telecrystal[amount > 1 ? "s" : ""] from your uplink.</span>")
H.put_in_hands(R)
// HIDDEN UPLINK - Can be stored in anything but the host item has to have a trigger for it.
/* How to create an uplink in 3 easy steps!
1. 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. Feel free to add "uses".
2. Code in the triggers. Use check_trigger for this, I recommend closing the item's menu with "usr << browse(null, "window=windowname") if it returns true.
The var/value is the value that will be compared with the var/target. If they are equal it will activate the menu.
3. If you want the menu to stay until the users locks his uplink, add an active_uplink_check(mob/user as mob) in your interact/attack_hand proc.
Then check if it's true, if true return. This will stop the normal menu appearing and will instead show the uplink menu.
*/
/obj/item/device/uplink/hidden
name = "Hidden Uplink."
desc = "There is something wrong if you're examining this."
var/datum/role/traitor/associated_role
/obj/item/device/uplink/hidden/Destroy()
var/obj/item/I = loc
I.hidden_uplink = null
if (associated_role)
associated_role.uplink = null
associated_role = null
..()
/obj/item/device/uplink/hidden/Topic(href, href_list)
..()
if(href_list["lock"])
toggle()
usr << browse(null, "window=hidden")
return 1
// Toggles the uplink on and off. Normally this will bypass the item's normal functions and go to the uplink menu, if activated.
/obj/item/device/uplink/hidden/proc/toggle()
active = !active
// Directly trigger the uplink. Turn on if it isn't already.
/obj/item/device/uplink/hidden/proc/trigger(mob/user as mob)
if(!active)
toggle()
interact(user)
// Checks to see if the value meets the target. Like a frequency being a traitor_frequency, in order to unlock a headset.
// If true, it accesses trigger() and returns 1. If it fails, it returns false. Use this to see if you need to close the
// current item's menu.
/obj/item/device/uplink/hidden/proc/check_trigger(mob/user as mob, var/value, var/target)
if(value == target)
trigger(user)
return 1
return 0
// I placed this here because of how relevant it is.
// You place this in your uplinkable item to check if an uplink is active or not.
// If it is, it will display the uplink menu and return 1, else it'll return false.
// If it returns true, I recommend closing the item's normal menu with "user << browse(null, "window=name")"
/obj/item/proc/active_uplink_check(mob/user as mob)
// Activates the uplink if it's active
if(src.hidden_uplink)
if(src.hidden_uplink.active)
src.hidden_uplink.trigger(user)
return 1
return 0
// PRESET UPLINKS
// A collection of preset uplinks.
//
// Includes normal radio uplink, multitool uplink,
// implant uplink (not the implant tool) and a preset headset uplink.
/obj/item/device/radio/uplink/New()
hidden_uplink = new(src)
icon_state = "radio"
/obj/item/device/radio/uplink/attack_self(mob/user as mob)
if(hidden_uplink)
hidden_uplink.trigger(user)
..()
add_component(/datum/component/uplink)
/obj/item/device/radio/uplink/nukeops/New()
..()
hidden_uplink.uses = 80 //haha fuck OOP
hidden_uplink.job = "Nuclear Operative"
var/datum/component/uplink/uplink_component = get_component(/datum/component/uplink)
uplink_component.telecrystals = 80
uplink_component.locked = FALSE
uplink_component.lockable = FALSE
uplink_component.nuke_ops_inventory = TRUE
/obj/item/device/multitool/uplink/New()
hidden_uplink = new(src)
/obj/item/device/multitool/uplink/attack_self(mob/user as mob)
if(hidden_uplink)
hidden_uplink.trigger(user)
/obj/item/device/radio/headset/uplink
traitor_frequency = 1445
..()
var/datum/component/uplink/uplink_comp = add_component(/datum/component/uplink)
uplink_comp.lockable = FALSE
uplink_comp.locked = FALSE
/obj/item/device/radio/headset/uplink/New()
..()
hidden_uplink = new(src)
hidden_uplink.uses = 20
add_component(/datum/component/uplink)

View File

@@ -28,6 +28,7 @@
return dat
/obj/item/weapon/implant/exile/implanted(mob/source, mob/user)
..()
theExile = source
disablePhrase = stripped_input(user, "Choose a phrase that disables the implant:")
var/list/replacechars = list("'" = "", "\"" = "", ">" = "", "<" = "", "(" = "", ")" = "")

View File

@@ -92,7 +92,7 @@
if(findtext(msg, phrase))
activate()
/obj/item/weapon/implant/explosive/trigger(emote, source as mob)
/obj/item/weapon/implant/explosive/trigger(emote, mob/source)
if(emote == "deathgasp")
activate()
@@ -122,6 +122,7 @@
usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', <B>say [src.phrase]</B> to attempt to activate.", 0, 0)
to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', <B>say [src.phrase]</B> to attempt to activate.")
addHear()
source.register_event(/event/emote, src, .proc/trigger)
return 1
/obj/item/weapon/implant/explosive/emp_act(severity)
@@ -149,6 +150,7 @@
return 0
/obj/item/weapon/implant/explosive/handle_removal(var/mob/remover)
imp_in?.unregister_event(/event/emote, src, .proc/trigger)
makeunusable(75)
/obj/item/weapon/implant/explosive/proc/small_boom()
@@ -263,6 +265,13 @@ the implant may become unstable and either pre-maturely inject the subject or si
src.activate(src.reagents.total_volume)
return
/obj/item/weapon/implant/chem/implanted(mob/source)
source.register_event(/event/emote, src, .proc/trigger)
return TRUE
/obj/item/weapon/implant/explosive/handle_removal(mob/remover)
imp_in?.unregister_event(/event/emote, src, .proc/trigger)
makeunusable(75)
/obj/item/weapon/implant/chem/activate(var/cause)
if(malfunction == IMPLANT_MALFUNCTION_PERMANENT)
@@ -446,11 +455,13 @@ the implant may become unstable and either pre-maturely inject the subject or si
return
/obj/item/weapon/implant/adrenalin/implanted(mob/source)
source.mind.store_memory("A implant can be activated by using the pale emote, <B>say *pale</B> to attempt to activate.", 0, 0)
to_chat(source, "The implanted freedom implant can be activated by using the pale emote, <B>say *pale</B> to attempt to activate.")
return 1
source.register_event(/event/emote, src, .proc/trigger)
source.mind.store_memory("A implant can be activated by using the pale emote, <B>say *pale</B> to attempt to activate.", 0, 0)
to_chat(source, "The implanted freedom implant can be activated by using the pale emote, <B>say *pale</B> to attempt to activate.")
return 1
/obj/item/weapon/implant/adrenalin/handle_removal(var/mob/remover)
imp_in?.unregister_event(/event/emote, src, .proc/trigger)
makeunusable(75)
/obj/item/weapon/implant/death_alarm
@@ -579,6 +590,7 @@ the implant may become unstable and either pre-maturely inject the subject or si
qdel (src)
/obj/item/weapon/implant/compressed/implanted(mob/source as mob)
source.register_event(/event/emote, src, .proc/trigger)
src.activation_emote = input("Choose activation emote:") in list("blink", "blink_r", "eyebrow", "chuckle", "twitch_s", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink")
usr.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, <B>say *[src.activation_emote]</B> to attempt to activate.", 0, 0)
to_chat(usr, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, <B>say *[src.activation_emote]</B> to attempt to activate.")
@@ -588,6 +600,7 @@ the implant may become unstable and either pre-maturely inject the subject or si
return 0
/obj/item/weapon/implant/compressed/handle_removal(var/mob/remover)
imp_in?.unregister_event(/event/emote, src, .proc/trigger)
makeunusable(75)
/obj/item/weapon/implant/cortical

View File

@@ -1,25 +1,27 @@
/obj/item/weapon/implant/uplink
name = "uplink"
desc = "Summon things."
var/activation_emote = "chuckle"
var/activation_emote
/obj/item/weapon/implant/uplink/New()
activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch_s", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink")
hidden_uplink = new(src)
hidden_uplink.uses = 10
..()
return
var/datum/component/uplink/uplink_comp = add_component(/datum/component/uplink)
uplink_comp.telecrystals = 10
/obj/item/weapon/implant/uplink/implanted(mob/source)
source.register_event(/event/emote, src, .proc/trigger)
activation_emote = input("Choose activation emote:") in list("blink", "blink_r", "eyebrow", "chuckle", "twitch_s", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink")
source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, <B>say *[src.activation_emote]</B> to attempt to activate.", 0, 0)
to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, <B>say *[src.activation_emote]</B> to attempt to activate.")
return 1
/obj/item/weapon/implant/uplink/handle_removal(var/mob/remover)
imp_in?.register_event(/event/emote, src, .proc/trigger)
makeunusable(75)
/obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob)
if(hidden_uplink && usr == source) // Let's not have another people activate our uplink
hidden_uplink.check_trigger(source, emote, activation_emote)
return
/obj/item/weapon/implant/uplink/trigger(emote, mob/source)
if(emote != activation_emote)
return
var/datum/component/uplink/uplink_comp = get_component(/datum/component/uplink)
uplink_comp.locked = FALSE
uplink_comp.tgui_interact(source)

View File

@@ -85,6 +85,7 @@ var/global/list/reagents_to_log = list(FUEL, PLASMA, PACID, SACID, AMUTATIONTOXI
verbs += /obj/proc/remove_pai
/obj/attackby(obj/item/weapon/W, mob/user)
invoke_event(/event/attackby, list("attacker" = user, "item" = W))
if(can_take_pai && istype(W, /obj/item/device/paicard))
if(integratedpai)
to_chat(user, "<span class = 'notice'>There's already a Personal AI inserted.</span>")

View File

@@ -195,8 +195,6 @@
return 0 // no updates, completely disabled (red visibility)
else if (nano.user.restrained() || nano.user.lying)
return 1 // update only (orange visibility)
else if (istype(instrumentObj, /obj/item/device/uplink/hidden)) // You know what if they have the uplink open let them use the UI
return 2 // Will build in distance checks on the topics for sanity.
else if (!(instrumentObj in view(4, nano.user))) // If the src object is not in visable, set status to 0
return 0 // no updates, completely disabled (red visibility)
else if (dist <= 1)

View File

@@ -5,6 +5,7 @@
var/enabled=1
/datum/component/New(var/datum/component_container/CC)
..()
container=CC
// Override to receive signals.

View File

@@ -66,7 +66,7 @@
/datum/library_catalog
var/list/cached_books = list()
/datum/library_catalog/proc/initialize()
/datum/library_catalog/initialize()
var/newid=1
for(var/typepath in typesof(/obj/item/weapon/book/manual)-/obj/item/weapon/book/manual)
var/obj/item/weapon/book/B = new typepath(null)

View File

@@ -194,8 +194,6 @@ nanoui is used to open and update nano browser uis
return STATUS_DISABLED // no updates, completely disabled (red visibility)
else if (nano.user.restrained() || nano.user.lying)
return STATUS_UPDATE // update only (orange visibility)
else if (istype(nano.src_object, /obj/item/device/uplink/hidden)) // You know what if they have the uplink open let them use the UI
return STATUS_INTERACTIVE // Will build in distance checks on the topics for sanity.
else if (!(nano.src_object in view(4, nano.user))) // If the src object is not in visable, set status to 0
return STATUS_DISABLED // no updates, completely disabled (red visibility)
else if (dist <= 1)

View File

@@ -153,7 +153,7 @@
if(!C || !C.get_powernet())
return 0
C.powernet.add_component(src)
C.powernet.add_connection(src)
connected=1
return 1
@@ -306,7 +306,7 @@
if(!cable || !cable.get_powernet())
return 0
cable.powernet.add_component(src)
cable.powernet.add_connection(src)
connected=1
return 1

View File

@@ -81,7 +81,7 @@
M.powernet = src
nodes += M
/datum/powernet/proc/add_component(var/datum/power_connection/C)
/datum/powernet/proc/add_connection(var/datum/power_connection/C)
if(C.powernet) // if M already has a powernet...
if(C.powernet == src)
return

View File

@@ -48,7 +48,7 @@
* optional inline_html string Custom HTML to inject.
* optional fancy bool If TRUE, will hide the window titlebar.
*/
/datum/tgui_window/proc/initialize(
/datum/tgui_window/initialize(
inline_assets = list(),
inline_html = "",
fancy = FALSE)

View File

@@ -0,0 +1,198 @@
import { createSearch, decodeHtmlEntities } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Flex, Input, Section, Table, Tabs, NoticeBox, Icon } from '../components';
import { formatMoney } from '../format';
import { Window } from '../layouts';
const MAX_SEARCH_RESULTS = 25;
export const Uplink = (props, context) => {
const { data } = useBackend(context);
const { telecrystals } = data;
return (
<Window
width={620}
height={600}
theme="syndicate">
<Window.Content scrollable>
<GenericUplink
currencyAmount={telecrystals}
currencySymbol="TC" />
</Window.Content>
</Window>
);
};
export const GenericUplink = (props, context) => {
const {
currencyAmount = 0,
currencySymbol = 'cr',
} = props;
const { act, data } = useBackend(context);
const {
compactMode,
lockable,
categories = [],
} = data;
const [
searchText,
setSearchText,
] = useLocalState(context, 'searchText', '');
const [
selectedCategory,
setSelectedCategory,
] = useLocalState(context, 'category', categories[0]?.name);
const testSearch = createSearch(searchText, item => {
return item.name + item.desc;
});
const items = searchText.length > 0
// Flatten all categories and apply search to it
&& categories
.flatMap(category => category.items || [])
.filter(testSearch)
.filter((item, i) => i < MAX_SEARCH_RESULTS)
// Select a category and show all items in it
|| categories
.find(category => category.name === selectedCategory)
?.items
// If none of that results in a list, return an empty list
|| [];
return (
<Section
title={(
<Box
inline
color={currencyAmount > 0 ? 'good' : 'bad'}>
{formatMoney(currencyAmount)} {currencySymbol}
</Box>
)}
buttons={(
<>
Search
<Input
autoFocus
value={searchText}
onInput={(e, value) => setSearchText(value)}
mx={1} />
<Button
icon={compactMode ? 'list' : 'info'}
content={compactMode ? 'Compact' : 'Detailed'}
onClick={() => act('compact_toggle')} />
<Button.Input
content={<Box><Icon name="coins"/> Withdraw</Box>}
onCommit={(e, value) => act('get_tc', { 'amount': value })}/>
{!!lockable && (
<Button
icon="lock"
content="Lock"
onClick={() => act('lock')} />
)}
</>
)}>
<Flex>
{searchText.length === 0 && (
<Box pr={2}>
<Flex.Item>
<Tabs vertical>
{categories.map(category => (
<Tabs.Tab
key={category.name}
selected={category.name === selectedCategory}
onClick={() => setSelectedCategory(category.name)}>
{category.name} ({category.items?.length || 0})
</Tabs.Tab>
))}
</Tabs>
</Flex.Item>
</Box>
)}
<Flex.Item grow={1} basis={0}>
{items.length === 0 && (
<NoticeBox>
{searchText.length === 0
? 'No items in this category.'
: 'No results found.'}
</NoticeBox>
)}
<ItemList
compactMode={searchText.length > 0 || compactMode}
currencyAmount={currencyAmount}
currencySymbol={currencySymbol}
items={items} />
</Flex.Item>
</Flex>
</Section>
);
};
const ItemList = (props, context) => {
const {
compactMode,
currencyAmount,
currencySymbol,
} = props;
const { act } = useBackend(context);
const [
hoveredItem,
setHoveredItem,
] = useLocalState(context, 'hoveredItem', {});
const hoveredCost = hoveredItem && hoveredItem.cost || 0;
// Append extra hover data to items
const items = props.items.map(item => {
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
const notEnoughHovered = currencyAmount - hoveredCost < item.cost;
const disabledDueToHovered = notSameItem && notEnoughHovered;
const disabled = currencyAmount < item.cost || disabledDueToHovered;
return {
...item,
disabled,
};
});
if (compactMode) {
return (
<Table>
{items.map(item => (
<Table.Row
key={item.name}
className="candystripe">
<Table.Cell bold>
{decodeHtmlEntities(item.name)}
</Table.Cell>
<Table.Cell collapsing textAlign="right">
<Button
fluid
content={formatMoney(item.cost) + ' ' + currencySymbol}
disabled={item.disabled}
tooltip={item.desc}
tooltipPosition="left"
onmouseover={() => setHoveredItem(item)}
onmouseout={() => setHoveredItem({})}
onClick={() => act('buy', {
name: item.name,
})} />
</Table.Cell>
</Table.Row>
))}
</Table>
);
}
return items.map(item => (
<Section
key={item.name}
title={item.name}
level={2}
buttons={(
<Button
content={item.cost + ' ' + currencySymbol}
disabled={item.disabled}
onmouseover={() => setHoveredItem(item)}
onmouseout={() => setHoveredItem({})}
onClick={() => act('buy', {
name: item.name,
})} />
)}>
{decodeHtmlEntities(item.desc)}
</Section>
));
};

File diff suppressed because one or more lines are too long

View File

@@ -88,7 +88,7 @@
#include "code\__HELPERS\clockwork.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\constants.dm"
#include "code\__HELPERS\datumpool.dm"
#include "code\__HELPERS\datum.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
#include "code\__HELPERS\gender.dm"
@@ -292,6 +292,8 @@
#include "code\datums\supplypacks.dm"
#include "code\datums\tether.dm"
#include "code\datums\uplink_item.dm"
#include "code\datums\components\component.dm"
#include "code\datums\components\uplink.dm"
#include "code\datums\diseases\appendicitis.dm"
#include "code\datums\diseases\brainrot.dm"
#include "code\datums\diseases\cold.dm"