mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Changes to ghost orbit menu (#9938)
* a * ports Co-authored-by: Emmanuel S <mrdoomboyo@gmail.com>
This commit is contained in:
@@ -287,7 +287,7 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
return .
|
||||
|
||||
//Returns a list of all items of interest with their name
|
||||
/proc/getpois(mobs_only=0,skip_mindless=0)
|
||||
/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE)
|
||||
var/list/mobs = sortmobs()
|
||||
var/list/namecounts = list()
|
||||
var/list/pois = list()
|
||||
@@ -301,7 +301,7 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
|
||||
if(M.real_name && M.real_name != M.name)
|
||||
name += " \[[M.real_name]\]"
|
||||
if(M.stat == DEAD)
|
||||
if(M.stat == DEAD && specify_dead_role)
|
||||
if(isobserver(M))
|
||||
name += " \[ghost\]"
|
||||
else
|
||||
|
||||
@@ -160,10 +160,11 @@
|
||||
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE)
|
||||
if(!istype(A) || !get_turf(A) || A == src)
|
||||
return
|
||||
|
||||
orbit_target = A
|
||||
return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
|
||||
|
||||
/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits)
|
||||
orbit_target = null
|
||||
return // We're just a simple hook
|
||||
|
||||
/atom/proc/transfer_observers_to(atom/target)
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
var/chat_color_darkened // A luminescence-shifted value of the last color calculated for chatmessage overlays
|
||||
|
||||
|
||||
var/atom/orbit_target //Reference to atom being orbited
|
||||
/**
|
||||
* Called when an atom is created in byond (built in engine proc)
|
||||
*
|
||||
@@ -1102,3 +1102,22 @@
|
||||
*/
|
||||
/atom/proc/rust_heretic_act()
|
||||
return
|
||||
|
||||
/**
|
||||
* Recursive getter method to return a list of all ghosts orbitting this atom
|
||||
*
|
||||
* This will work fine without manually passing arguments.
|
||||
*/
|
||||
/atom/proc/get_all_orbiters(list/processed, source = TRUE)
|
||||
var/list/output = list()
|
||||
if (!processed)
|
||||
processed = list()
|
||||
if (src in processed)
|
||||
return output
|
||||
if (!source)
|
||||
output += src
|
||||
processed += src
|
||||
for (var/o in orbiters?.orbiters)
|
||||
var/atom/atom_orbiter = o
|
||||
output += atom_orbiter.get_all_orbiters(processed, source = FALSE)
|
||||
return output
|
||||
|
||||
@@ -479,7 +479,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
var/list/dest = list() //List of possible destinations (mobs)
|
||||
var/target = null //Chosen target.
|
||||
|
||||
dest += getpois(mobs_only=1) //Fill list, prompt user with list
|
||||
dest += getpois(mobs_only = TRUE) //Fill list, prompt user with list
|
||||
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest
|
||||
|
||||
if (!target)//Make sure we actually have a target
|
||||
@@ -820,7 +820,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
if (!eye_name)
|
||||
return
|
||||
|
||||
var/mob/mob_eye = creatures[eye_name]
|
||||
do_observe(creatures[eye_name])
|
||||
|
||||
/mob/dead/observer/proc/do_observe(mob/mob_eye)
|
||||
//Istype so we filter out points of interest that are not mobs
|
||||
if(client && mob_eye && istype(mob_eye))
|
||||
client.eye = mob_eye
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/datum/orbit_menu
|
||||
var/mob/dead/observer/owner
|
||||
var/auto_observe = FALSE
|
||||
|
||||
/datum/orbit_menu/New(mob/dead/observer/new_owner)
|
||||
if(!istype(new_owner))
|
||||
@@ -7,23 +8,44 @@
|
||||
owner = new_owner
|
||||
|
||||
/datum/orbit_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if (..())
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if (action == "orbit")
|
||||
var/list/pois = getpois(skip_mindless = 1)
|
||||
var/atom/movable/poi = pois[params["name"]]
|
||||
if (poi != null)
|
||||
switch(action)
|
||||
if ("orbit")
|
||||
var/ref = params["ref"]
|
||||
var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
|
||||
if (poi == null)
|
||||
. = TRUE
|
||||
return
|
||||
owner.ManualFollow(poi)
|
||||
ui.close()
|
||||
owner.reset_perspective(null)
|
||||
if (auto_observe)
|
||||
owner.do_observe(poi)
|
||||
. = TRUE
|
||||
if ("refresh")
|
||||
update_static_data(owner, ui)
|
||||
. = TRUE
|
||||
if ("toggle_observe")
|
||||
auto_observe = !auto_observe
|
||||
if (auto_observe && owner.orbit_target)
|
||||
owner.do_observe(owner.orbit_target)
|
||||
else
|
||||
owner.reset_perspective(null)
|
||||
|
||||
/datum/orbit_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["auto_observe"] = auto_observe
|
||||
return data
|
||||
|
||||
/datum/orbit_menu/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/alive = list()
|
||||
var/list/antagonists = list()
|
||||
@@ -32,23 +54,28 @@
|
||||
var/list/misc = list()
|
||||
var/list/npcs = list()
|
||||
|
||||
var/list/pois = getpois(skip_mindless = 1)
|
||||
var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE)
|
||||
for (var/name in pois)
|
||||
var/list/serialized = list()
|
||||
serialized["name"] = name
|
||||
|
||||
var/poi = pois[name]
|
||||
|
||||
serialized["ref"] = REF(poi)
|
||||
|
||||
var/mob/M = poi
|
||||
if (istype(M))
|
||||
if (isobserver(M))
|
||||
var/number_of_orbiters = length(M.get_all_orbiters())
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
ghosts += list(serialized)
|
||||
else if (M.stat == DEAD)
|
||||
dead += list(serialized)
|
||||
else if (M.mind == null)
|
||||
npcs += list(serialized)
|
||||
else
|
||||
var/number_of_orbiters = M.orbiters?.orbiters?.len
|
||||
var/number_of_orbiters = length(M.get_all_orbiters())
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
|
||||
@@ -74,5 +101,4 @@
|
||||
data["ghosts"] = ghosts
|
||||
data["misc"] = misc
|
||||
data["npcs"] = npcs
|
||||
|
||||
return data
|
||||
|
||||
95
tgui/packages/tgui/assets.js
Normal file
95
tgui/packages/tgui/assets.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { loadCSS as fgLoadCss } from 'fg-loadcss';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
const logger = createLogger('assets');
|
||||
|
||||
const EXCLUDED_PATTERNS = [/v4shim/i];
|
||||
const RETRY_ATTEMPTS = 5;
|
||||
const RETRY_INTERVAL = 3000;
|
||||
|
||||
const loadedStyleSheetByUrl = {};
|
||||
const loadedMappings = {};
|
||||
|
||||
export const loadStyleSheet = (url, attempt = 1) => {
|
||||
if (loadedStyleSheetByUrl[url]) {
|
||||
return;
|
||||
}
|
||||
loadedStyleSheetByUrl[url] = true;
|
||||
logger.log(`loading stylesheet '${url}'`);
|
||||
/** @type {HTMLLinkElement} */
|
||||
let node = fgLoadCss(url);
|
||||
node.addEventListener('load', () => {
|
||||
if (!isStyleSheetReallyLoaded(node, url)) {
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
loadedStyleSheetByUrl[url] = null;
|
||||
if (attempt >= RETRY_ATTEMPTS) {
|
||||
logger.error(`Error: Failed to load the stylesheet `
|
||||
+ `'${url}' after ${RETRY_ATTEMPTS} attempts.\nIt was either `
|
||||
+ `not found, or you're trying to load an empty stylesheet `
|
||||
+ `that has no CSS rules in it.`);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => loadStyleSheet(url, attempt + 1), RETRY_INTERVAL);
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the stylesheet was registered in the DOM
|
||||
* and is not empty.
|
||||
*/
|
||||
const isStyleSheetReallyLoaded = (node, url) => {
|
||||
// Method #1 (works on IE10+)
|
||||
const styleSheet = node.sheet;
|
||||
if (styleSheet) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
// Method #2
|
||||
const styleSheets = document.styleSheets;
|
||||
const len = styleSheets.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const styleSheet = styleSheets[i];
|
||||
if (styleSheet.href.includes(url)) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
}
|
||||
// All methods failed
|
||||
logger.warn(`Warning: stylesheet '${url}' was not found in the DOM`);
|
||||
return false;
|
||||
};
|
||||
|
||||
export const resolveAsset = name => (
|
||||
loadedMappings[name] || name
|
||||
);
|
||||
|
||||
export const assetMiddleware = store => next => action => {
|
||||
const { type, payload } = action;
|
||||
if (type === 'asset/stylesheet') {
|
||||
loadStyleSheet(payload);
|
||||
return;
|
||||
}
|
||||
if (type === 'asset/mappings') {
|
||||
for (let name of Object.keys(payload)) {
|
||||
// Skip anything that matches excluded patterns
|
||||
if (EXCLUDED_PATTERNS.some(regex => regex.test(name))) {
|
||||
continue;
|
||||
}
|
||||
const url = payload[name];
|
||||
const ext = name.split('.').pop();
|
||||
loadedMappings[name] = url;
|
||||
if (ext === 'css') {
|
||||
loadStyleSheet(url);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
next(action);
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createSearch } from 'common/string';
|
||||
import { Box, Button, Input, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { multiline } from 'common/string';
|
||||
import { resolveAsset } from '../assets';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Divider, Flex, Icon, Input, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const PATTERN_DESCRIPTOR = / \[(?:ghost|dead)\]$/;
|
||||
const PATTERN_NUMBER = / \(([0-9]+)\)$/;
|
||||
|
||||
const searchFor = searchText => createSearch(searchText, thing => thing.name);
|
||||
@@ -42,9 +43,9 @@ const BasicSection = (props, context) => {
|
||||
{things.map(thing => (
|
||||
<Button
|
||||
key={thing.name}
|
||||
content={thing.name.replace(PATTERN_DESCRIPTOR, "")}
|
||||
content={thing.name}
|
||||
onClick={() => act("orbit", {
|
||||
name: thing.name,
|
||||
ref: thing.ref,
|
||||
})} />
|
||||
))}
|
||||
</Section>
|
||||
@@ -59,7 +60,7 @@ const OrbitedButton = (props, context) => {
|
||||
<Button
|
||||
color={color}
|
||||
onClick={() => act("orbit", {
|
||||
name: thing.name,
|
||||
ref: thing.ref,
|
||||
})}>
|
||||
{thing.name}
|
||||
{thing.orbiters && (
|
||||
@@ -67,7 +68,7 @@ const OrbitedButton = (props, context) => {
|
||||
{"("}{thing.orbiters}{" "}
|
||||
<Box
|
||||
as="img"
|
||||
src="ghost.png"
|
||||
src={resolveAsset('ghost.png')}
|
||||
opacity={0.7} />
|
||||
{")"}
|
||||
</Box>
|
||||
@@ -81,6 +82,7 @@ export const Orbit = (props, context) => {
|
||||
const {
|
||||
alive,
|
||||
antagonists,
|
||||
auto_observe,
|
||||
dead,
|
||||
ghosts,
|
||||
misc,
|
||||
@@ -111,24 +113,57 @@ export const Orbit = (props, context) => {
|
||||
.filter(searchFor(searchText))
|
||||
.sort(compareNumberedText)[0];
|
||||
if (member !== undefined) {
|
||||
act("orbit", { name: member.name });
|
||||
act("orbit", { ref: member.ref });
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Window>
|
||||
<Window
|
||||
title="Orbit"
|
||||
width={350}
|
||||
height={700}>
|
||||
<Window.Content scrollable>
|
||||
<Section>
|
||||
<Input
|
||||
autoFocus
|
||||
fluid
|
||||
value={searchText}
|
||||
onInput={(_, value) => setSearchText(value)}
|
||||
onEnter={(_, value) => orbitMostRelevant(value)} />
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Icon
|
||||
name="search"
|
||||
mr={1} />
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1}>
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
autoFocus
|
||||
fluid
|
||||
value={searchText}
|
||||
onInput={(_, value) => setSearchText(value)}
|
||||
onEnter={(_, value) => orbitMostRelevant(value)} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Divider vertical />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
inline
|
||||
color="transparent"
|
||||
tooltip={multiline`Toggle Auto-Observe. When active, you'll
|
||||
see the UI / full inventory of whoever you're orbiting. Neat!`}
|
||||
tooltipPosition="bottom-left"
|
||||
selected={auto_observe}
|
||||
icon={auto_observe ? "toggle-on" : "toggle-off"}
|
||||
onClick={() => act("toggle_observe")} />
|
||||
<Button
|
||||
inline
|
||||
color="transparent"
|
||||
tooltip="Refresh"
|
||||
tooltipPosition="bottom-left"
|
||||
icon="sync-alt"
|
||||
onClick={() => act("refresh")} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
|
||||
{antagonists.length > 0 && (
|
||||
<Section title="Ghost-Visible Antagonists">
|
||||
{sortedAntagonists.map(([name, antags]) => (
|
||||
@@ -148,7 +183,7 @@ export const Orbit = (props, context) => {
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Section title="Alive">
|
||||
<Section title={`Alive - (${alive.length})`}>
|
||||
{alive
|
||||
.filter(searchFor(searchText))
|
||||
.sort(compareNumberedText)
|
||||
@@ -160,11 +195,17 @@ export const Orbit = (props, context) => {
|
||||
))}
|
||||
</Section>
|
||||
|
||||
<BasicSection
|
||||
title="Ghosts"
|
||||
source={ghosts}
|
||||
searchText={searchText}
|
||||
/>
|
||||
<Section title={`Ghosts - (${ghosts.length})`}>
|
||||
{ghosts
|
||||
.filter(searchFor(searchText))
|
||||
.sort(compareNumberedText)
|
||||
.map(thing => (
|
||||
<OrbitedButton
|
||||
key={thing.name}
|
||||
color="grey"
|
||||
thing={thing} />
|
||||
))}
|
||||
</Section>
|
||||
|
||||
<BasicSection
|
||||
title="Dead"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5401,16 +5401,6 @@ send@0.17.1:
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
serialize-javascript@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
|
||||
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
|
||||
|
||||
serialize-javascript@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.0.0.tgz#492e489a2d77b7b804ad391a5f5d97870952548e"
|
||||
integrity sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==
|
||||
|
||||
serialize-javascript@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
|
||||
@@ -5867,7 +5857,7 @@ terser-webpack-plugin@^1.4.3:
|
||||
find-cache-dir "^2.1.0"
|
||||
is-wsl "^1.1.0"
|
||||
schema-utils "^1.0.0"
|
||||
serialize-javascript "^5.0.1"
|
||||
serialize-javascript "^2.1.2"
|
||||
source-map "^0.6.1"
|
||||
terser "^4.1.2"
|
||||
webpack-sources "^1.4.0"
|
||||
@@ -5883,7 +5873,7 @@ terser-webpack-plugin@^2.1.0:
|
||||
jest-worker "^25.4.0"
|
||||
p-limit "^2.3.0"
|
||||
schema-utils "^2.6.6"
|
||||
serialize-javascript "^5.0.1"
|
||||
serialize-javascript "^3.0.0"
|
||||
source-map "^0.6.1"
|
||||
terser "^4.6.12"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
Reference in New Issue
Block a user