mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
Components! TGUI uplinks! Oh god! (#30641)
* components * tgui uplink * Components! TGUI uplinks! Oh god! * yeah * this too
This commit is contained in:
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
79
code/datums/components/component.dm
Normal file
79
code/datums/components/component.dm
Normal 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))
|
||||
180
code/datums/components/uplink.dm
Normal file
180
code/datums/components/uplink.dm
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>→ 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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/ ))))
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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("'" = "", "\"" = "", ">" = "", "<" = "", "(" = "", ")" = "")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
var/enabled=1
|
||||
|
||||
/datum/component/New(var/datum/component_container/CC)
|
||||
..()
|
||||
container=CC
|
||||
|
||||
// Override to receive signals.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
198
tgui/packages/tgui/interfaces/Uplink.js
Normal file
198
tgui/packages/tgui/interfaces/Uplink.js
Normal 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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user