diff --git a/code/datums/records.dm b/code/datums/records.dm index 2be5d60a252..5593c88c01d 100644 --- a/code/datums/records.dm +++ b/code/datums/records.dm @@ -165,7 +165,7 @@ var/nid = "" var/enzymes var/identity - var/exploit_record = "No additional information acquired." + var/exploit_record /datum/record/general/locked/New(var/mob/living/carbon/human/H) ..() diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index a6ea8bdb878..9c00dea07ca 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -98,69 +98,55 @@ Then check if it's true, if true return. This will stop the normal menu appearin return 0 /* - NANO UI FOR UPLINK WOOP WOOP + TGUI 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] +/obj/item/device/uplink/hidden/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Uplink", capitalize_first_letters(name)) + ui.open() +/obj/item/device/uplink/hidden/ui_data(mob/user) + var/list/data = list() data["welcome"] = welcome data["telecrystals"] = telecrystals data["bluecrystals"] = bluecrystals data["menu"] = nanoui_menu + update_nano_data() data += nanoui_data - - // 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, 450, 600, state = inventory_state) - ui.set_initial_data(data) - ui.open() - + return data // 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) + ui_interact(user, null) -/obj/item/device/uplink/hidden/CanUseTopic() - if(!active) - return STATUS_CLOSE - return ..() - -// The purchasing code. -/obj/item/device/uplink/hidden/Topic(href, href_list) +/obj/item/device/uplink/hidden/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) if(..()) return 1 - var/mob/user = usr - if(href_list["buy_item"]) - var/datum/uplink_item/UI = (locate(href_list["buy_item"]) in uplink.items) + if(action == "buy_item") + var/datum/uplink_item/UI = (locate(params["buy_item"]) in uplink.items) UI.buy(src, usr) - else if(href_list["lock"]) + else if(action == "lock") toggle() - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - ui.close() - else if(href_list["return"]) + SStgui.close_uis(src) + else if(action == "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"] - if(href_list["category"]) - category = locate(href_list["category"]) in uplink.categories - // #TODO-MERGE: Check NanoUI on PDAs - if(href_list["contract_interact"]) - var/list/params = list("location" = "contract_details", "contract" = href_list["contract_interact"]) - usr.client.process_webint_link("interface/login/sso_server", list2params(params)) - if(href_list["contract_page"]) - nanoui_data["contracts_current_page"] = text2num(href_list["contract_page"]) - update_nano_data() - if(href_list["contract_view"]) - nanoui_data["contracts_view"] = text2num(href_list["contract_view"]) + else if(action == "menu") + nanoui_menu = text2num(params["menu"]) + if(params["id"]) + exploit_id = params["id"] + if(params["category"]) + category = locate(params["category"]) in uplink.categories + if(action == "contract_interact") + var/list/params_webint = list("location" = "contract_details", "contract" = params["contract_interact"]) + usr.client.process_webint_link("interface/login/sso_server", list2params(params_webint)) + if(action == "contract_page") + nanoui_data["contracts_current_page"] = text2num(params["contract_page"]) + if(action == "contract_view") + nanoui_data["contracts_view"] = text2num(params["contract_view"]) nanoui_data["contracts_current_page"] = 1 - update_nano_data() - update_nano_data() return 1 /obj/item/device/uplink/hidden/proc/update_nano_data() @@ -189,8 +175,8 @@ Then check if it's true, if true return. This will stop the normal menu appearin nanoui_data["items"] = items else if(nanoui_menu == 2) var/permanentData[0] - for(var/datum/record/general/locked/L in sortRecord(SSrecords.records_locked)) - permanentData[++permanentData.len] = list(Name = L.name,"id" = L.id) + for(var/datum/record/general/locked/record in SSrecords.records_locked) + permanentData[++permanentData.len] = list("name" = record.name,"id" = record.id, "has_exploitables" = !!record.exploit_record) nanoui_data["exploit_records"] = permanentData else if(nanoui_menu == 21) nanoui_data["exploit_exists"] = 0 diff --git a/html/changelogs/DreamySkrell-uplink-tgui.yml b/html/changelogs/DreamySkrell-uplink-tgui.yml new file mode 100644 index 00000000000..ec4f2d28544 --- /dev/null +++ b/html/changelogs/DreamySkrell-uplink-tgui.yml @@ -0,0 +1,41 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: DreamySkrell + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Uplink rewritten from nanoui to tgui." diff --git a/nano/templates/uplink.tmpl b/nano/templates/uplink.tmpl deleted file mode 100644 index 3e5db5563f2..00000000000 --- a/nano/templates/uplink.tmpl +++ /dev/null @@ -1,176 +0,0 @@ - - -{{:helper.syndicateMode()}} -

{{:data.welcome}}

-
-
-
- Functions: -
-
- {{:helper.link('Request Items', 'gear', {'menu' : 0}, null, 'fixedLeftWider')}} - {{:helper.link('Exploitable Information', 'gear', {'menu' : 2}, null, 'fixedLeftWider')}} - {{:helper.link('Extranet Contract Database', 'gear', {'menu' : 3}, null, 'fixedLeftWider')}} - {{:helper.link('Return', 'arrowreturn-1-w', {'return' : 1}, null, 'fixedLeft')}} - {{:helper.link('Close', 'gear', {'lock' : "1"}, null, 'fixedLeft')}} -
-
-
- -
-
- Telecrystals: -
-
- {{:data.telecrystals}} -
-
- Bluecrystals: -
-
- {{:data.bluecrystals}} -
-
- -{{if data.menu == 0}} -

Categories:

- {{for data.categories}} -
- {{:helper.link(value.name, 'gear', {'menu' : 1, 'category' : value.ref})}} -
- {{/for}} -{{else data.menu == 1}} -

Request items:

- Each item costs a number of telecrystals or bluecrystals as indicated by the numbers following their name.
- Note that when buying items, bluecrystals are prioritised over telecrystals. - {{for data.items}} -
- {{:helper.link(value.name, 'gear', {'buy_item' : value.ref}, value.can_buy ? null : 'disabled')}} - {{if value.bc_cost}} - - {{:value.bc_cost}} BC - {{/if}} - {{if value.tc_cost}} - - {{:value.tc_cost}} TC - {{/if}} - {{if value.left <= 10}} - - {{:value.left}} left in the uplink - {{/if}} -
-
- {{:value.description}} -
- {{/for}} -{{else data.menu == 2}} -

Information Record List:

-
-
- Select a Record -
-
- {{for data.exploit_records}} -
- {{:helper.link(value.Name, 'gear', {'menu' : 21, 'id' : value.id}, null, null)}} -
- {{/for}} -{{else data.menu == 21}} -

Information Record:

-
-
-
-
- {{if data.exploit_exists == 1}} - Name: {{:data.exploit.name}}
- Sex: {{:data.exploit.sex}}
- Species: {{:data.exploit.species}}
- Age: {{:data.exploit.age}}
- Rank: {{:data.exploit.rank}}
- Citizenship: {{:data.exploit.citizenship}}
- Faction: {{:data.exploit.faction}}
- Religion: {{:data.exploit.religion}}
- Fingerprint: {{:data.exploit.fingerprint}}
- -
Acquired Information:
- Notes:
{{:data.exploit.nanoui_exploit_record}}

- {{else}} - - No exploitative information acquired! -
-
-
- {{/if}} -
-
-
- -{{else data.menu == 3}} -

Available Contracts:

-
- {{if data.contracts_found == 1}} -
-
- - {{if data.contracts_view == 1}} - - {{else}} - - {{/if}} - {{for data.contracts}} - - {{/for}} -
IDContractorTitle
Available Contracts
Closed Contracts
{{:value.id}}{{:value.contractee}}{{:value.title}}{{:helper.link('View', null, {'menu' : 31, 'id' : value.id}, null, 'fixedLeft')}}
-
-
- {{for data.contracts_pages}} - {{:helper.link(value, null, {'contract_page' : value}, null, null)}} - {{/for}} -

- {{if data.contracts_view == 1}} - {{:helper.link('View Expired Contracts', 'gear', {'contract_view' : 2}, null, null)}} - {{else}} - {{:helper.link('View Open Contracts', 'gear', {'contract_view' : 1}, null, null)}} - {{/if}} -
-
-
-
- {{else}} -
-
No Contracts Available.
-
- {{/if}} - -{{else data.menu == 31}} -

Viewing Contract:

-
-
-
-
- {{if data.contracts_found == 1}} - ID: #{{:data.contract.id}}
- Contractee: {{:data.contract.contractee}}
- Status: - {{if data.contract.status == 1}} - Open
- {{else}} - Closed
- {{/if}} - Title: {{:data.contract.title}}
-
- Description: {{:data.contract.description}}
- Reward: {{:data.contract.reward_other}}
- {{else}} - Failed to retreive contract information! - {{/if}} -
- -
-
-

- {{if data.contracts_found == 1}} - {{:helper.link('View Reports And Updates', 'extlink', {'contract_interact' : data.contract.id}, null, null)}} - {{/if}} - -{{/if}} diff --git a/tgui/packages/tgui/interfaces/Uplink.tsx b/tgui/packages/tgui/interfaces/Uplink.tsx new file mode 100644 index 00000000000..29784712d93 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Uplink.tsx @@ -0,0 +1,407 @@ +import { BooleanLike } from '../../common/react'; +import { useBackend, useLocalState } from '../backend'; +import { Box, Button, Section, LabeledList, Table } from '../components'; +import { Window } from '../layouts'; + +export type UplinkData = { + menu: Number; + welcome: string; + telecrystals: Number; + bluecrystals: Number; + + categories: { name: string; ref: string }[]; + items: ItemData[]; + + exploit_records: ExploitData[]; + exploit: ExploitData; + exploit_exists: BooleanLike; + + contracts_found: Number; + contracts_view: Number; + contracts: ContractData[]; + contracts_pages: Number[]; + contract: ContractData; +}; + +type ItemData = { + name: string; + description: string; + can_buy: BooleanLike; + tc_cost: number; + bc_cost: number; + left: number; + ref: string; +}; + +type ExploitData = { + id: number; + nanoui_exploit_record: string; + name: string; + sex: string; + age: string; + species: string; + rank: string; + citizenship: string; + employer: string; + religion: string; + fingerprint: string; + has_exploitables: BooleanLike; +}; + +type ContractData = { + id: number; + contractee: String; + title: String; + status: BooleanLike; + description: String; + reward_other: String; +}; + +export const Uplink = (props, context) => { + const { act, data } = useBackend(context); + + return ( + + +
+ {data.welcome} +
+
+ + {[ + ['shopping-cart', 'Request Gear', () => act('menu', { menu: 0 })], + [ + 'file-lines', + 'Exploitable Information', + () => act('menu', { menu: 2 }), + ], + [ + 'network-wired', + 'Extranet Contract Database', + () => act('menu', { menu: 3 }), + ], + ['arrow-left', 'Return', () => act('return')], + ['close', 'Close', () => act('lock')], + ].map(([icon, text, action]: [string, string, any]) => ( + +
+ {data.menu === 0 ? ItemCategoriesSection(act, data) : ''} + {data.menu === 1 ? ItemSection(context, act, data) : ''} + {data.menu === 2 ? ExploitSection(act, data) : ''} + {data.menu === 21 ? ExploitRecordSection(act, data) : ''} + {data.menu === 3 ? ContractsSection(act, data) : ''} + {data.menu === 31 ? ContractDetailsSection(act, data) : ''} +
+
+ ); +}; + +const ItemCategoriesSection = function (act: any, data: UplinkData) { + return ( +
+ + {data.categories?.map((category) => ( + +
+ ); +}; + +const ItemSection = function (context: any, act: any, data: UplinkData) { + const [sortDesc, setSortDesc] = useLocalState( + context, + `sortDesc`, + true + ); + + // sort by item cost first + data.items?.sort((a, b) => { + const a_cost = Math.max(a.bc_cost, a.tc_cost, 0); + const b_cost = Math.max(b.bc_cost, b.tc_cost, 0); + if (sortDesc) { + return b_cost - a_cost; + } else { + return a_cost - b_cost; + } + }); + // and then sort to put unavailable items at the end + data.items?.sort((a, b) => { + if (!a.can_buy && b.can_buy) { + return +1; + } else if (a.can_buy && !b.can_buy) { + return -1; + } else { + return 0; + } + }); + + return ( +
+ + + Each item costs a number of telecrystals or bluecrystals as indicated + by the numbers following their name. + + +
+ + + Note that when buying items, bluecrystals are prioritised over + telecrystals. + + +
+ +
+ ); +}; + +const ExploitSection = function (act: any, data: UplinkData) { + const exploit_sort = (a: ExploitData, b: ExploitData) => { + return a.name.localeCompare(b.name); + }; + + return ( +
+ Select a Record +
+
+ + {data.exploit_records + ?.sort(exploit_sort) + .map((exploit: ExploitData) => ( + <> +
+ ); +}; + +const ExploitRecordSection = function (act: any, data: UplinkData) { + if (data.exploit_exists && data.exploit) { + const exploit = data.exploit; + return ( +
+ + {exploit.name} + {exploit.sex} + {exploit.species} + {exploit.age} + {exploit.rank} + + {exploit.citizenship} + + + {exploit.employer} + + + {exploit.religion} + + + {exploit.fingerprint} + + + + {exploit.nanoui_exploit_record + ? exploit.nanoui_exploit_record + : 'No additional information acquired.'} +
+ ); + } else { + return ( +
+ No exploitative information acquired! +
+ ); + } +}; + +const ContractsSection = function (act: any, data: UplinkData) { + return ( +
+ {!data.contracts_found ? ( + No Contracts Available. + ) : ( + + + + ID + Contractor + Title + + + + + {data.contracts_view === 1 + ? 'Available Contracts' + : 'Closed Contracts'} + + + + {data.contracts.map((contract: ContractData) => ( + + {contract.id} + {contract.contractee} + {contract.title} + +
+ + {data.contracts_pages.map((page_n: Number) => ( +
+ ); +}; + +const ContractDetailsSection = function (act: any, data: UplinkData) { + return ( +
+ {data.contracts_found === 1 ? ( + + {data.contract.id} + + {data.contract.contractee} + + + + {data.contract.status === true ? 'Open' : 'Closed'} + + + + {data.contract.title} + + + {data.contract.description} + + + {data.contract.reward_other} + + + ) : ( + {'Failed to retrieve contract information!'} + )} +
+ {data.contracts_found === 1 ? ( +
+ ); +};