Uplink tgui (#17336)

* add uplink.tsx

* a

* a

* a

* a

* a

* exploitables

* close gui

* a

* delete uplink.tmpl

* 1

* ooooooooooooooooooooooooo

* lint

* c

---------

Co-authored-by: DreamySkrell <>
This commit is contained in:
DreamySkrell
2023-10-06 22:15:38 +00:00
committed by GitHub
co-authored by DreamySkrell <>
parent eddb686c0d
commit e381a5fa88
5 changed files with 481 additions and 223 deletions
+1 -1
View File
@@ -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)
..()
+32 -46
View File
@@ -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
@@ -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."
-176
View File
@@ -1,176 +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()}}
<H2><span class="white">{{:data.welcome}}</span></H2>
<br>
<div class="item">
<div class="itemLabelNarrow">
<b>Functions</b>:
</div>
<div class="itemContent">
{{: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')}}
</div>
</div>
<br>
<div class="item">
<div class="itemLabel">
<b>Telecrystals</b>:
</div>
<div class="itemContent">
{{:data.telecrystals}}
</div>
<div class="itemLabel">
<b>Bluecrystals</b>:
</div>
<div class="itemContent">
{{:data.bluecrystals}}
</div>
</div>
{{if data.menu == 0}}
<H2>Categories:</H2>
{{for data.categories}}
<div class="item">
{{:helper.link(value.name, 'gear', {'menu' : 1, 'category' : value.ref})}}
</div>
{{/for}}
{{else data.menu == 1}}
<H2><span class="white">Request items:</span></H2>
<span class="white"><i>Each item costs a number of telecrystals or bluecrystals as indicated by the numbers following their name.</i></span><br>
<span class="white"><b>Note that when buying items, bluecrystals are prioritised over telecrystals.</b></span>
{{for data.items}}
<div class="item">
{{: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}}
- <span class="white">{{:value.left}} left in the uplink</span>
{{/if}}
</div>
<div class="item">
{{:value.description}}
</div>
{{/for}}
{{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">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>
<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>
{{else data.menu == 3}}
<H2><span class="white">Available Contracts:</span></H2>
<br>
{{if data.contracts_found == 1}}
<div class="item">
<center><table class="pmon"><tbody>
<tr><th>ID</th><th>Contractor</th><th colspan="2">Title</th>
{{if data.contracts_view == 1}}
<tr><th colspan="4" class="med">Available Contracts</th></tr>
{{else}}
<tr><th colspan="4" class="sec">Closed Contracts</th></tr>
{{/if}}
{{for data.contracts}}
<tr><td><span class="white">{{:value.id}}</span></td><td><span class="white">{{:value.contractee}}</span></td><td><span class="white">{{:value.title}}</span></td><td>{{:helper.link('View', null, {'menu' : 31, 'id' : value.id}, null, 'fixedLeft')}}</td></tr>
{{/for}}
</tbody></table></center>
<br>
<div class="item" style="text-align:center">
{{for data.contracts_pages}}
{{:helper.link(value, null, {'contract_page' : value}, null, null)}}
{{/for}}
<br><br>
{{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}}
</div>
<br>
<br>
</div>
{{else}}
<div class="item">
<center><span class="bad">No Contracts Available.</span></center>
</div>
{{/if}}
{{else data.menu == 31}}
<H2><span class="white">Viewing Contract:</span></H2>
<br>
<div class="statusDisplayRecords">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{if data.contracts_found == 1}}
<span class="good">ID: </span> <span class="average">#{{:data.contract.id}} </span><br>
<span class="good">Contractee: </span> <span class="average">{{:data.contract.contractee}} </span><br>
<span class="good">Status: </span>
{{if data.contract.status == 1}}
<span class="good">Open</span><br>
{{else}}
<span class="bad">Closed</span><br>
{{/if}}
<span class="good">Title: </span> <span class="average">{{:data.contract.title}} </span><br>
<br>
<span class="good">Description: </span> <span class="average">{{:data.contract.description}} </span><br>
<span class="good">Reward: </span> <span class="average">{{:data.contract.reward_other}} </span><br>
{{else}}
<span class="bad">Failed to retreive contract information!</span>
{{/if}}
</div>
</div>
</div>
<br><br>
{{if data.contracts_found == 1}}
{{:helper.link('View Reports And Updates', 'extlink', {'contract_interact' : data.contract.id}, null, null)}}
{{/if}}
{{/if}}
+407
View File
@@ -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<UplinkData>(context);
return (
<Window resizable theme="syndicate">
<Window.Content scrollable>
<Section title="Functions">
{data.welcome}
<br />
<br />
<LabeledList>
{[
['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]) => (
<LabeledList.Item key={text}>
<Button
content={text}
icon={icon}
color={'transparent'}
onClick={action}
/>
</LabeledList.Item>
))}
</LabeledList>
<br />
<Table width="50%">
<Table.Row>
<Table.Cell>Telecrystals</Table.Cell>
<Table.Cell>{data.telecrystals}</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Bluecrystals</Table.Cell>
<Table.Cell>{data.bluecrystals}</Table.Cell>
</Table.Row>
</Table>
</Section>
{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) : ''}
</Window.Content>
</Window>
);
};
const ItemCategoriesSection = function (act: any, data: UplinkData) {
return (
<Section title="Gear categories">
<LabeledList>
{data.categories?.map((category) => (
<LabeledList.Item key={category.name}>
<Button
content={category.name}
color={'purple'}
onClick={() => act('menu', { menu: 1, category: category.ref })}
/>
</LabeledList.Item>
))}
</LabeledList>
</Section>
);
};
const ItemSection = function (context: any, act: any, data: UplinkData) {
const [sortDesc, setSortDesc] = useLocalState<boolean>(
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 (
<Section title="Request Gear">
<span class="white">
<i>
Each item costs a number of telecrystals or bluecrystals as indicated
by the numbers following their name.
</i>
</span>
<br />
<span class="white">
<b>
Note that when buying items, bluecrystals are prioritised over
telecrystals.
</b>
</span>
<br />
<Box textAlign="right">
<Button
icon={sortDesc ? 'sort-amount-asc' : 'sort-amount-desc'}
color={'purple'}
onClick={() => {
setSortDesc(!sortDesc);
}}
/>
</Box>
<Table>
{data.items?.map((item: ItemData) => (
<>
<Table.Row color={item.can_buy ? null : 'gray'}>
<Table.Cell>
<Button
content={item.name}
color={'purple'}
disabled={!item.can_buy}
onClick={() => act('buy_item', { buy_item: item.ref })}
/>
</Table.Cell>
<Table.Cell>
{item.bc_cost ? item.bc_cost + ' BC' : ''}{' '}
</Table.Cell>
<Table.Cell>
{item.tc_cost ? item.tc_cost + ' TC' : ''}{' '}
</Table.Cell>
<Table.Cell>
{item.left < 42 ? item.left + ' LEFT' : ''}{' '}
</Table.Cell>
</Table.Row>
<Table.Row color={item.can_buy ? null : 'gray'}>
<Table.Cell colspan={4}>
<Box width="75%">
{item.description}
<br />
<br />
</Box>
</Table.Cell>
</Table.Row>
</>
))}
</Table>
</Section>
);
};
const ExploitSection = function (act: any, data: UplinkData) {
const exploit_sort = (a: ExploitData, b: ExploitData) => {
return a.name.localeCompare(b.name);
};
return (
<Section title="Information Record List">
Select a Record
<br />
<br />
<Box>
{data.exploit_records
?.sort(exploit_sort)
.map((exploit: ExploitData) => (
<>
<Button
content={exploit.name}
color={'purple'}
icon={exploit.has_exploitables ? 'warning' : null}
onClick={() => act('menu', { menu: 21, id: exploit.id })}
/>
<br />
</>
))}
</Box>
</Section>
);
};
const ExploitRecordSection = function (act: any, data: UplinkData) {
if (data.exploit_exists && data.exploit) {
const exploit = data.exploit;
return (
<Section title="Information Record">
<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="Citizenship">
{exploit.citizenship}
</LabeledList.Item>
<LabeledList.Item label="Faction">
{exploit.employer}
</LabeledList.Item>
<LabeledList.Item label="Religion">
{exploit.religion}
</LabeledList.Item>
<LabeledList.Item label="Fingerprint">
{exploit.fingerprint}
</LabeledList.Item>
<LabeledList.Item label="Acquired Information" />
</LabeledList>
{exploit.nanoui_exploit_record
? exploit.nanoui_exploit_record
: 'No additional information acquired.'}
</Section>
);
} else {
return (
<Section title="Information Record">
No exploitative information acquired!
</Section>
);
}
};
const ContractsSection = function (act: any, data: UplinkData) {
return (
<Section title="Available Contracts">
{!data.contracts_found ? (
<Box>No Contracts Available.</Box>
) : (
<Box>
<Table>
<Table.Row>
<Table.Cell>ID</Table.Cell>
<Table.Cell>Contractor</Table.Cell>
<Table.Cell>Title</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell colspan={999} textAlign="center">
<Box
backgroundColor={data.contracts_view === 1 ? 'good' : 'bad'}>
{data.contracts_view === 1
? 'Available Contracts'
: 'Closed Contracts'}
</Box>
</Table.Cell>
</Table.Row>
{data.contracts.map((contract: ContractData) => (
<Table.Row key={contract.id}>
<Table.Cell>{contract.id}</Table.Cell>
<Table.Cell>{contract.contractee}</Table.Cell>
<Table.Cell>{contract.title}</Table.Cell>
<Table.Cell>
<Button
content="View"
color={'purple'}
onClick={() => act('menu', { menu: 31, id: contract.id })}
/>
</Table.Cell>
</Table.Row>
))}
</Table>
<Box>
{data.contracts_pages.map((page_n: Number) => (
<Button
key={page_n}
content={page_n}
color={'purple'}
onClick={() => act('contract_page', { contract_page: page_n })}
/>
))}
</Box>
</Box>
)}
<Box>
{data.contracts_view === 1 ? (
<Button
content="View Expired Contracts"
color={'purple'}
onClick={() => act('contract_view', { contract_view: 2 })}
/>
) : (
<Button
content="View Open Contracts"
color={'purple'}
onClick={() => act('contract_view', { contract_view: 1 })}
/>
)}
</Box>
</Section>
);
};
const ContractDetailsSection = function (act: any, data: UplinkData) {
return (
<Section title="Viewing Contract">
{data.contracts_found === 1 ? (
<LabeledList>
<LabeledList.Item label="ID">{data.contract.id}</LabeledList.Item>
<LabeledList.Item label="Contractee">
{data.contract.contractee}
</LabeledList.Item>
<LabeledList.Item label="Status">
<Box
backgroundColor={data.contract.status === true ? 'good' : 'bad'}>
{data.contract.status === true ? 'Open' : 'Closed'}
</Box>
</LabeledList.Item>
<LabeledList.Item label="Title">
{data.contract.title}
</LabeledList.Item>
<LabeledList.Item label="Description">
{data.contract.description}
</LabeledList.Item>
<LabeledList.Item label="Reward">
{data.contract.reward_other}
</LabeledList.Item>
</LabeledList>
) : (
<Box>{'Failed to retrieve contract information!'}</Box>
)}
<br />
{data.contracts_found === 1 ? (
<Button
content="View Reports And Updates"
color={'purple'}
onClick={() =>
act('contract_interact', { contract_interact: data.contract.id })
}
/>
) : (
''
)}
</Section>
);
};