mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-23 03:57:48 +01:00
TGUI Uplink
This commit is contained in:
@@ -1,13 +1,23 @@
|
||||
GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink)
|
||||
|
||||
// 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(hidden_uplink)
|
||||
if(hidden_uplink.active)
|
||||
hidden_uplink.trigger(user)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/device/uplink
|
||||
var/welcome = "Welcome, Operative" // Welcoming menu message
|
||||
var/uses // Numbers of crystals
|
||||
var/list/ItemsCategory // List of categories with lists of items
|
||||
var/list/ItemsReference // List of references with an associated item
|
||||
var/list/nanoui_items // List of items for NanoUI use
|
||||
var/nanoui_menu = 0 // The current menu we are in
|
||||
var/list/nanoui_data = new // Additional data for NanoUI use
|
||||
var/faction = "" //Antag faction holder.
|
||||
|
||||
var/list/purchase_log = new
|
||||
@@ -17,9 +27,7 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink)
|
||||
var/next_offer_time
|
||||
var/datum/uplink_item/discount_item //The item to be discounted
|
||||
var/discount_amount //The amount as a percent the item will be discounted by
|
||||
|
||||
/obj/item/device/uplink/nano_host()
|
||||
return loc
|
||||
var/compact_mode = FALSE
|
||||
|
||||
/obj/item/device/uplink/Initialize(var/mapload, var/datum/mind/owner = null, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT)
|
||||
. = ..()
|
||||
@@ -53,23 +61,20 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink)
|
||||
name = "hidden uplink"
|
||||
desc = "There is something wrong if you're examining this."
|
||||
var/active = 0
|
||||
var/datum/uplink_category/category = 0 // The current category we are in
|
||||
var/exploit_id // Id of the current exploit record we are viewing
|
||||
var/selected_cat
|
||||
|
||||
// The hidden uplink MUST be inside an obj/item's contents.
|
||||
/obj/item/device/uplink/hidden/Initialize()
|
||||
. = ..()
|
||||
if(!isitem(loc))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
nanoui_data = list()
|
||||
update_nano_data()
|
||||
|
||||
/obj/item/device/uplink/hidden/next_offer()
|
||||
discount_item = default_uplink_selection.get_random_item(INFINITY)
|
||||
discount_amount = pick(90;0.9, 80;0.8, 70;0.7, 60;0.6, 50;0.5, 40;0.4, 30;0.3, 20;0.2, 10;0.1)
|
||||
update_nano_data()
|
||||
SSnanoui.update_uis(src)
|
||||
next_offer_time = world.time + offer_time
|
||||
SStgui.update_uis(src)
|
||||
addtimer(CALLBACK(src, .proc/next_offer), offer_time)
|
||||
|
||||
// Toggles the uplink on and off. Normally this will bypass the item's normal functions and go to the uplink menu, if activated.
|
||||
@@ -91,136 +96,137 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
NANO UI FOR UPLINK WOOP WOOP
|
||||
*/
|
||||
/obj/item/device/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
var/title = "Remote Uplink"
|
||||
var/data[0]
|
||||
uses = user.mind.tcrystals
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
faction = H.antag_faction
|
||||
// Legacy
|
||||
/obj/item/device/uplink/hidden/interact(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
data["welcome"] = welcome
|
||||
data["crystals"] = uses
|
||||
data["menu"] = nanoui_menu
|
||||
data += nanoui_data
|
||||
/*****************
|
||||
* Uplink TGUI
|
||||
*****************/
|
||||
/obj/item/device/uplink/tgui_host()
|
||||
return loc
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui) // No auto-refresh
|
||||
ui = new(user, src, ui_key, "uplink.tmpl", title, 630, 700, state = inventory_state)
|
||||
data["menu"] = 0
|
||||
ui.set_initial_data(data)
|
||||
/obj/item/device/uplink/hidden/tgui_state(mob/user)
|
||||
return GLOB.tgui_inventory_state
|
||||
|
||||
/obj/item/device/uplink/hidden/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
|
||||
if(!active)
|
||||
toggle()
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Uplink", "Remote Uplink")
|
||||
// This UI is only ever opened by one person,
|
||||
// and never is updated outside of user input.
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
/obj/item/device/uplink/hidden/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(!user.mind)
|
||||
return
|
||||
|
||||
// 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/hidden/interact(mob/user)
|
||||
ui_interact(user)
|
||||
var/list/data = ..()
|
||||
|
||||
/obj/item/device/uplink/hidden/CanUseTopic()
|
||||
data["telecrystals"] = uses
|
||||
data["lockable"] = TRUE
|
||||
data["compactMode"] = compact_mode
|
||||
|
||||
data["discount_name"] = discount_item ? discount_item.name : ""
|
||||
data["discount_amount"] = (1-discount_amount)*100
|
||||
data["offer_expiry"] = worldtime2stationtime(next_offer_time)
|
||||
|
||||
data["exploit"] = null
|
||||
data["locked_records"] = null
|
||||
|
||||
if(exploit_id)
|
||||
for(var/datum/data/record/L in data_core.locked)
|
||||
if(L.fields["id"] == exploit_id)
|
||||
data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value.
|
||||
// We trade off being able to automatically add shit for more control over what gets passed to json
|
||||
// and if it's sanitized for html.
|
||||
data["exploit"]["nanoui_exploit_record"] = html_encode(L.fields["exploit_record"]) // Change stuff into html
|
||||
data["exploit"]["nanoui_exploit_record"] = replacetext(data["exploit"]["nanoui_exploit_record"], "\n", "<br>") // change line breaks into <br>
|
||||
data["exploit"]["name"] = html_encode(L.fields["name"])
|
||||
data["exploit"]["sex"] = html_encode(L.fields["sex"])
|
||||
data["exploit"]["age"] = html_encode(L.fields["age"])
|
||||
data["exploit"]["species"] = html_encode(L.fields["species"])
|
||||
data["exploit"]["rank"] = html_encode(L.fields["rank"])
|
||||
data["exploit"]["home_system"] = html_encode(L.fields["home_system"])
|
||||
data["exploit"]["citizenship"] = html_encode(L.fields["citizenship"])
|
||||
data["exploit"]["faction"] = html_encode(L.fields["faction"])
|
||||
data["exploit"]["religion"] = html_encode(L.fields["religion"])
|
||||
data["exploit"]["fingerprint"] = html_encode(L.fields["fingerprint"])
|
||||
if(L.fields["antagvis"] == ANTAG_KNOWN || (faction == L.fields["antagfac"] && (L.fields["antagvis"] == ANTAG_SHARED)))
|
||||
data["exploit"]["antagfaction"] = html_encode(L.fields["antagfac"])
|
||||
else
|
||||
data["exploit"]["antagfaction"] = html_encode("None")
|
||||
break
|
||||
else
|
||||
var/list/permanentData = list()
|
||||
for(var/datum/data/record/L in sortRecord(data_core.locked))
|
||||
permanentData.Add(list(list(
|
||||
"name" = L.fields["name"],
|
||||
"id" = L.fields["id"]
|
||||
)))
|
||||
data["locked_records"] = permanentData
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/device/uplink/hidden/tgui_static_data(mob/user)
|
||||
var/list/data = ..()
|
||||
|
||||
data["categories"] = list()
|
||||
for(var/datum/uplink_category/category in uplink.categories)
|
||||
if(category.can_view(src))
|
||||
var/list/cat = list(
|
||||
"name" = category.name,
|
||||
"items" = (category == selected_cat ? list() : null)
|
||||
)
|
||||
for(var/datum/uplink_item/item in category.items)
|
||||
if(!item.can_view(src))
|
||||
continue
|
||||
var/cost = item.cost(uses, src) || "???"
|
||||
cat["items"] += list(list(
|
||||
"name" = item.name,
|
||||
"cost" = cost,
|
||||
"desc" = item.description(),
|
||||
"ref" = REF(item),
|
||||
))
|
||||
data["categories"] += list(cat)
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/device/uplink/hidden/tgui_status(mob/user, datum/tgui_state/state)
|
||||
if(!active)
|
||||
return STATUS_CLOSE
|
||||
return ..()
|
||||
|
||||
// The purchasing code.
|
||||
/obj/item/device/uplink/hidden/Topic(href, href_list)
|
||||
/obj/item/device/uplink/hidden/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
var/mob/user = usr
|
||||
if(href_list["buy_item"])
|
||||
var/datum/uplink_item/UI = (locate(href_list["buy_item"]) in uplink.items)
|
||||
UI.buy(src, usr)
|
||||
else if(href_list["lock"])
|
||||
toggle()
|
||||
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
|
||||
ui.close()
|
||||
else if(href_list["return"])
|
||||
nanoui_menu = round(nanoui_menu/10)
|
||||
else if(href_list["menu"])
|
||||
nanoui_menu = text2num(href_list["menu"])
|
||||
if(href_list["id"])
|
||||
exploit_id = href_list["id"]
|
||||
else if(href_list["category"])
|
||||
category = locate(href_list["category"]) in uplink.categories
|
||||
|
||||
update_nano_data()
|
||||
return 1
|
||||
|
||||
/obj/item/device/uplink/hidden/proc/update_nano_data()
|
||||
if(nanoui_menu == 0)
|
||||
var/categories[0]
|
||||
for(var/datum/uplink_category/category in uplink.categories)
|
||||
if(category.can_view(src))
|
||||
categories[++categories.len] = list("name" = category.name, "ref" = "\ref[category]")
|
||||
nanoui_data["categories"] = categories
|
||||
nanoui_data["discount_name"] = discount_item ? discount_item.name : ""
|
||||
nanoui_data["discount_amount"] = (1-discount_amount)*100
|
||||
nanoui_data["offer_expiry"] = worldtime2stationtime(next_offer_time)
|
||||
|
||||
if(category)
|
||||
nanoui_data["current_category"] = category.name
|
||||
var/items[0]
|
||||
for(var/datum/uplink_item/item in category.items)
|
||||
if(item.can_view(src))
|
||||
var/cost = item.cost(uses, src)
|
||||
if(!cost) cost = "???"
|
||||
items[++items.len] = list("name" = item.name, "description" = replacetext(item.description(), "\n", "<br>"), "can_buy" = item.can_buy(src), "cost" = cost, "ref" = "\ref[item]")
|
||||
nanoui_data["items"] = items
|
||||
|
||||
else if(nanoui_menu == 2)
|
||||
var/permanentData[0]
|
||||
for(var/datum/data/record/L in sortRecord(data_core.locked))
|
||||
permanentData[++permanentData.len] = list(Name = L.fields["name"],"id" = L.fields["id"])
|
||||
nanoui_data["exploit_records"] = permanentData
|
||||
else if(nanoui_menu == 21)
|
||||
nanoui_data["exploit_exists"] = 0
|
||||
|
||||
for(var/datum/data/record/L in data_core.locked)
|
||||
if(L.fields["id"] == exploit_id)
|
||||
nanoui_data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value.
|
||||
// We trade off being able to automatically add shit for more control over what gets passed to json
|
||||
// and if it's sanitized for html.
|
||||
nanoui_data["exploit"]["nanoui_exploit_record"] = html_encode(L.fields["exploit_record"]) // Change stuff into html
|
||||
nanoui_data["exploit"]["nanoui_exploit_record"] = replacetext(nanoui_data["exploit"]["nanoui_exploit_record"], "\n", "<br>") // change line breaks into <br>
|
||||
nanoui_data["exploit"]["name"] = html_encode(L.fields["name"])
|
||||
nanoui_data["exploit"]["sex"] = html_encode(L.fields["sex"])
|
||||
nanoui_data["exploit"]["age"] = html_encode(L.fields["age"])
|
||||
nanoui_data["exploit"]["species"] = html_encode(L.fields["species"])
|
||||
nanoui_data["exploit"]["rank"] = html_encode(L.fields["rank"])
|
||||
nanoui_data["exploit"]["home_system"] = html_encode(L.fields["home_system"])
|
||||
nanoui_data["exploit"]["citizenship"] = html_encode(L.fields["citizenship"])
|
||||
nanoui_data["exploit"]["faction"] = html_encode(L.fields["faction"])
|
||||
nanoui_data["exploit"]["religion"] = html_encode(L.fields["religion"])
|
||||
nanoui_data["exploit"]["fingerprint"] = html_encode(L.fields["fingerprint"])
|
||||
if(L.fields["antagvis"] == ANTAG_KNOWN || (faction == L.fields["antagfac"] && (L.fields["antagvis"] == ANTAG_SHARED)))
|
||||
nanoui_data["exploit"]["antagfaction"] = html_encode(L.fields["antagfac"])
|
||||
else
|
||||
nanoui_data["exploit"]["antagfaction"] = html_encode("None")
|
||||
nanoui_data["exploit_exists"] = 1
|
||||
break
|
||||
|
||||
// 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
|
||||
switch(action)
|
||||
if("buy")
|
||||
var/datum/uplink_item/UI = (locate(params["ref"]) in uplink.items)
|
||||
UI.buy(src, usr)
|
||||
return TRUE
|
||||
if("lock")
|
||||
toggle()
|
||||
SStgui.close_uis(src)
|
||||
if("select")
|
||||
selected_cat = params["category"]
|
||||
return TRUE
|
||||
if("compact_toggle")
|
||||
compact_mode = !compact_mode
|
||||
return TRUE
|
||||
if("view_exploits")
|
||||
exploit_id = params["id"]
|
||||
return TRUE
|
||||
|
||||
// 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(atom/loc, datum/mind/target_mind, telecrystals)
|
||||
..(loc)
|
||||
hidden_uplink = new(src, target_mind, telecrystals)
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
|
||||
<!--
|
||||
Title: Syndicate Uplink, uses some javascript to change nanoUI up a bit.
|
||||
Used In File(s): \code\game\objects\items\devices\uplinks.dm
|
||||
-->
|
||||
{{:helper.syndicateMode()}}
|
||||
|
||||
<style type="text/css">
|
||||
.fixedLengthLink .link {
|
||||
float: left;
|
||||
width: 270px;
|
||||
height: auto;
|
||||
white-space: normal;
|
||||
background-color: #8f1414;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
background: none;
|
||||
border: none;
|
||||
white-space: normal;
|
||||
height: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fixedLengthLink .link:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
a:hover, .zoomLink:hover, .linkActive:hover {
|
||||
cursor: pointer;
|
||||
background-color: #ff1414;
|
||||
}
|
||||
|
||||
.category {
|
||||
width: 150px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.category .uiIcon16 {
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.categoryActive {
|
||||
background-color: #ff1414;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-base-color: #8f1414;
|
||||
scrollbar-arrow-color: #ffffff;
|
||||
height: 374px;
|
||||
}
|
||||
|
||||
.statusDisplay {
|
||||
border: 1px solid #8f1414;
|
||||
}
|
||||
</style>
|
||||
|
||||
<H2><span class="white">{{:data.welcome}}</span></H2>
|
||||
<br>
|
||||
<div class="item">
|
||||
<div class="itemLabelNarrow">
|
||||
<b>Functions</b>:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
<div class="line">
|
||||
{{:helper.link('Request Items', 'gear', {'menu' : 0}, null, 'fixedLeftWider')}}
|
||||
{{:helper.link('Exploitable Information', 'gear', {'menu' : 2}, null, 'fixedLeftWider')}}
|
||||
</div>
|
||||
<div class="line">
|
||||
{{:helper.link('Return', 'arrowreturn-1-w', {'return' : 1}, null, 'fixedLeft')}}
|
||||
{{:helper.link('Close', 'gear', {'lock' : "1"}, null, 'fixedLeft')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
<b>Tele-Crystals</b>:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.crystals}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{if data.menu == 0}}
|
||||
{{if data.discount_amount < 100}}
|
||||
<div class="item" style="margin-bottom: 10px;">
|
||||
<div class="itemLabel">
|
||||
<b>Currently discounted</b>:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.discount_name}} - {{:data.discount_amount}}% off. Offer will expire at: {{:data.offer_expiry}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div style="float: left; width: 30%;">
|
||||
<div class="itemLabel">Categories</div>
|
||||
{{for data.categories}}
|
||||
<div class="line">
|
||||
{{:helper.link(value.name, 'gear', {'category' : value.ref}, (value.name == data.current_category) ? 'categoryActive' : '', 'category')}}
|
||||
</div>
|
||||
{{/for}}
|
||||
</div>
|
||||
|
||||
<div style="float: right; width: 70%">
|
||||
<div class="itemLabel">
|
||||
Request Items
|
||||
</div>
|
||||
<div class="itemContentWide" style="width: 95%;">
|
||||
<div class="statusDisplay">
|
||||
<div class="line scroll">
|
||||
{{for data.items}}
|
||||
<div class="line">
|
||||
<div class="statusValue">
|
||||
<div class="fixedLengthLink">
|
||||
{{:helper.link(value.name, 'gear', {'buy_item' : value.ref}, value.can_buy ? null : 'disabled')}}
|
||||
</div>
|
||||
<div style="float: right; margin-right: 20px;">{{:value.cost}} points</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/for}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{else data.menu == 2}}
|
||||
<H2><span class="white">Information Record List:</span></H2>
|
||||
<br>
|
||||
<div class="item">
|
||||
Select a Record
|
||||
</div>
|
||||
<br>
|
||||
{{for data.exploit_records}}
|
||||
<div class="item">
|
||||
{{:helper.link(value.Name, 'gear', {'menu' : 21, 'id' : value.id}, null, null)}}
|
||||
</div>
|
||||
{{/for}}
|
||||
{{else data.menu == 21}}
|
||||
<H2><span class="white">Information Record:</span></H2>
|
||||
<br>
|
||||
<div class="statusDisplayRecords">
|
||||
<div class="item">
|
||||
<div class="itemContent" style="width: 100%;">
|
||||
{{if data.exploit_exists == 1}}
|
||||
<span class="good">Name: </span> <span class="average">{{:data.exploit.name}} </span><br>
|
||||
<span class="good">Sex: </span> <span class="average">{{:data.exploit.sex}} </span><br>
|
||||
<span class="good">Species: </span> <span class="average">{{:data.exploit.species}} </span><br>
|
||||
<span class="good">Age: </span> <span class="average">{{:data.exploit.age}} </span><br>
|
||||
<span class="good">Rank: </span> <span class="average">{{:data.exploit.rank}} </span><br>
|
||||
<span class="good">Home System: </span> <span class="average">{{:data.exploit.home_system}} </span><br>
|
||||
<span class="good">Citizenship: </span> <span class="average">{{:data.exploit.citizenship}} </span><br>
|
||||
<span class="good">Faction: </span> <span class="average">{{:data.exploit.faction}} </span><br>
|
||||
<span class="good">Religion: </span> <span class="average">{{:data.exploit.religion}} </span><br>
|
||||
<span class="good">Fingerprint: </span> <span class="average">{{:data.exploit.fingerprint}} </span><br>
|
||||
<span class="good">Other Affiliation: </span> <span class="average">{{:data.exploit.antagfaction}} </span><br>
|
||||
|
||||
<br>Acquired Information:<br>
|
||||
<span class="good">Notes:<br> </span> <span class="average">{{:data.exploit.nanoui_exploit_record}}</span><br><br>
|
||||
{{else}}
|
||||
<span class="bad">
|
||||
No exploitative information acquired!
|
||||
<br>
|
||||
<br>
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -0,0 +1,322 @@
|
||||
import { createSearch, decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Flex, Input, Section, Table, Tabs, NoticeBox, LabeledList } 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 [screen, setScreen] = useLocalState(context, 'screen', 0);
|
||||
|
||||
const { telecrystals } = data;
|
||||
return (
|
||||
<Window
|
||||
width={620}
|
||||
height={580}
|
||||
theme="syndicate"
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<UplinkHeader screen={screen} setScreen={setScreen} />
|
||||
{screen === 0 && (
|
||||
<GenericUplink
|
||||
currencyAmount={telecrystals}
|
||||
currencySymbol="TC" />
|
||||
) || screen === 1 && (
|
||||
<ExploitableInformation />
|
||||
) || (
|
||||
<Section color="bad">
|
||||
Error
|
||||
</Section>
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const UplinkHeader = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
screen,
|
||||
setScreen,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
discount_name,
|
||||
discount_amount,
|
||||
offer_expiry,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Tabs style={{
|
||||
"border-bottom": "none",
|
||||
"margin-bottom": "0",
|
||||
}}>
|
||||
<Tabs.Tab
|
||||
selected={screen === 0}
|
||||
onClick={() => setScreen(0)}>
|
||||
Request Items
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={screen === 1}
|
||||
onClick={() => setScreen(1)}>
|
||||
Exploitable Information
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Section title="Item Discount" level={2}>
|
||||
{discount_amount < 100 && (
|
||||
<Box>
|
||||
{discount_name} - {discount_amount}% off. Offer expires at: {offer_expiry}
|
||||
</Box>
|
||||
) || (
|
||||
<Box>
|
||||
No items currently discounted.
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ExploitableInformation = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
exploit,
|
||||
locked_records,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Exploitable Information" buttons={exploit && (
|
||||
<Button
|
||||
icon="undo"
|
||||
content="Back"
|
||||
onClick={() => act("view_exploits", { id: 0 })} />
|
||||
)}>
|
||||
{exploit && (
|
||||
<Box>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{exploit.name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Sex">
|
||||
{exploit.sex}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Species">
|
||||
{exploit.species}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Age">
|
||||
{exploit.age}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Rank">
|
||||
{exploit.rank}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Home System">
|
||||
{exploit.home_system}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Citizenship">
|
||||
{exploit.citizenship}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Faction">
|
||||
{exploit.faction}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Religion">
|
||||
{exploit.religion}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Fingerprint">
|
||||
{exploit.fingerprint}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Other Affiliations">
|
||||
{exploit.antagfaction}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item>Acquired Information</LabeledList.Item>
|
||||
<LabeledList.Item label="Notes">
|
||||
{exploit.nanoui_exploit_record}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Box>
|
||||
) || locked_records.map(record => (
|
||||
<Button
|
||||
key={record.id}
|
||||
icon="eye"
|
||||
fluid
|
||||
content={record.name}
|
||||
onClick={() => act("view_exploits", { id: record.id })} />
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const GenericUplink = (props, context) => {
|
||||
const {
|
||||
currencyAmount = 0,
|
||||
currencySymbol = '₮',
|
||||
} = 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={(
|
||||
<Fragment>
|
||||
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')} />
|
||||
{!!lockable && (
|
||||
<Button
|
||||
icon="lock"
|
||||
content="Lock"
|
||||
onClick={() => act('lock')} />
|
||||
)}
|
||||
</Fragment>
|
||||
)}>
|
||||
<Flex>
|
||||
{searchText.length === 0 && (
|
||||
<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>
|
||||
)}
|
||||
<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', {
|
||||
ref: item.ref,
|
||||
})} />
|
||||
</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', {
|
||||
ref: item.ref,
|
||||
})} />
|
||||
)}>
|
||||
{decodeHtmlEntities(item.desc)}
|
||||
</Section>
|
||||
));
|
||||
};
|
||||
Reference in New Issue
Block a user