Changes to ghost orbit menu (#9938)

* a

* ports

Co-authored-by: Emmanuel S <mrdoomboyo@gmail.com>
This commit is contained in:
Redmoogle
2020-10-17 07:52:18 -04:00
committed by GitHub
parent d39970ef44
commit 87ddc76e52
9 changed files with 225 additions and 51 deletions

View File

@@ -287,7 +287,7 @@ Turf and target are separate in case you want to teleport some distance from a t
return . return .
//Returns a list of all items of interest with their name //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/mobs = sortmobs()
var/list/namecounts = list() var/list/namecounts = list()
var/list/pois = 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) if(M.real_name && M.real_name != M.name)
name += " \[[M.real_name]\]" name += " \[[M.real_name]\]"
if(M.stat == DEAD) if(M.stat == DEAD && specify_dead_role)
if(isobserver(M)) if(isobserver(M))
name += " \[ghost\]" name += " \[ghost\]"
else else

View File

@@ -160,10 +160,11 @@
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE) /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) if(!istype(A) || !get_turf(A) || A == src)
return return
orbit_target = A
return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation) return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits) /atom/movable/proc/stop_orbit(datum/component/orbiter/orbits)
orbit_target = null
return // We're just a simple hook return // We're just a simple hook
/atom/proc/transfer_observers_to(atom/target) /atom/proc/transfer_observers_to(atom/target)

View File

@@ -75,7 +75,7 @@
var/chat_color_darkened // A luminescence-shifted value of the last color calculated for chatmessage overlays 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) * Called when an atom is created in byond (built in engine proc)
* *
@@ -1102,3 +1102,22 @@
*/ */
/atom/proc/rust_heretic_act() /atom/proc/rust_heretic_act()
return 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

View File

@@ -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/list/dest = list() //List of possible destinations (mobs)
var/target = null //Chosen target. 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 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 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) if (!eye_name)
return 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 //Istype so we filter out points of interest that are not mobs
if(client && mob_eye && istype(mob_eye)) if(client && mob_eye && istype(mob_eye))
client.eye = mob_eye client.eye = mob_eye

View File

@@ -1,5 +1,6 @@
/datum/orbit_menu /datum/orbit_menu
var/mob/dead/observer/owner var/mob/dead/observer/owner
var/auto_observe = FALSE
/datum/orbit_menu/New(mob/dead/observer/new_owner) /datum/orbit_menu/New(mob/dead/observer/new_owner)
if(!istype(new_owner)) if(!istype(new_owner))
@@ -7,23 +8,44 @@
owner = new_owner 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) /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) if (!ui)
ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state) ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state)
ui.open() ui.open()
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) /datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
if (..()) . = ..()
if(.)
return return
switch(action)
if (action == "orbit") if ("orbit")
var/list/pois = getpois(skip_mindless = 1) var/ref = params["ref"]
var/atom/movable/poi = pois[params["name"]] var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
if (poi != null) if (poi == null)
. = TRUE
return
owner.ManualFollow(poi) 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) /datum/orbit_menu/ui_data(mob/user)
var/list/data = list() 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/alive = list()
var/list/antagonists = list() var/list/antagonists = list()
@@ -32,23 +54,28 @@
var/list/misc = list() var/list/misc = list()
var/list/npcs = 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) for (var/name in pois)
var/list/serialized = list() var/list/serialized = list()
serialized["name"] = name serialized["name"] = name
var/poi = pois[name] var/poi = pois[name]
serialized["ref"] = REF(poi)
var/mob/M = poi var/mob/M = poi
if (istype(M)) if (istype(M))
if (isobserver(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) ghosts += list(serialized)
else if (M.stat == DEAD) else if (M.stat == DEAD)
dead += list(serialized) dead += list(serialized)
else if (M.mind == null) else if (M.mind == null)
npcs += list(serialized) npcs += list(serialized)
else else
var/number_of_orbiters = M.orbiters?.orbiters?.len var/number_of_orbiters = length(M.get_all_orbiters())
if (number_of_orbiters) if (number_of_orbiters)
serialized["orbiters"] = number_of_orbiters serialized["orbiters"] = number_of_orbiters
@@ -74,5 +101,4 @@
data["ghosts"] = ghosts data["ghosts"] = ghosts
data["misc"] = misc data["misc"] = misc
data["npcs"] = npcs data["npcs"] = npcs
return data return data

View 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);
};

View File

@@ -1,9 +1,10 @@
import { createSearch } from 'common/string'; import { createSearch } from 'common/string';
import { Box, Button, Input, Section } from '../components'; import { multiline } from 'common/string';
import { Window } from '../layouts'; import { resolveAsset } from '../assets';
import { useBackend, useLocalState } from '../backend'; 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 PATTERN_NUMBER = / \(([0-9]+)\)$/;
const searchFor = searchText => createSearch(searchText, thing => thing.name); const searchFor = searchText => createSearch(searchText, thing => thing.name);
@@ -42,9 +43,9 @@ const BasicSection = (props, context) => {
{things.map(thing => ( {things.map(thing => (
<Button <Button
key={thing.name} key={thing.name}
content={thing.name.replace(PATTERN_DESCRIPTOR, "")} content={thing.name}
onClick={() => act("orbit", { onClick={() => act("orbit", {
name: thing.name, ref: thing.ref,
})} /> })} />
))} ))}
</Section> </Section>
@@ -59,7 +60,7 @@ const OrbitedButton = (props, context) => {
<Button <Button
color={color} color={color}
onClick={() => act("orbit", { onClick={() => act("orbit", {
name: thing.name, ref: thing.ref,
})}> })}>
{thing.name} {thing.name}
{thing.orbiters && ( {thing.orbiters && (
@@ -67,7 +68,7 @@ const OrbitedButton = (props, context) => {
{"("}{thing.orbiters}{" "} {"("}{thing.orbiters}{" "}
<Box <Box
as="img" as="img"
src="ghost.png" src={resolveAsset('ghost.png')}
opacity={0.7} /> opacity={0.7} />
{")"} {")"}
</Box> </Box>
@@ -81,6 +82,7 @@ export const Orbit = (props, context) => {
const { const {
alive, alive,
antagonists, antagonists,
auto_observe,
dead, dead,
ghosts, ghosts,
misc, misc,
@@ -111,24 +113,57 @@ export const Orbit = (props, context) => {
.filter(searchFor(searchText)) .filter(searchFor(searchText))
.sort(compareNumberedText)[0]; .sort(compareNumberedText)[0];
if (member !== undefined) { if (member !== undefined) {
act("orbit", { name: member.name }); act("orbit", { ref: member.ref });
break; break;
} }
} }
}; };
return ( return (
<Window> <Window
title="Orbit"
width={350}
height={700}>
<Window.Content scrollable> <Window.Content scrollable>
<Section> <Section>
<Input <Flex>
autoFocus <Flex.Item>
fluid <Icon
value={searchText} name="search"
onInput={(_, value) => setSearchText(value)} mr={1} />
onEnter={(_, value) => orbitMostRelevant(value)} /> </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> </Section>
{antagonists.length > 0 && ( {antagonists.length > 0 && (
<Section title="Ghost-Visible Antagonists"> <Section title="Ghost-Visible Antagonists">
{sortedAntagonists.map(([name, antags]) => ( {sortedAntagonists.map(([name, antags]) => (
@@ -148,7 +183,7 @@ export const Orbit = (props, context) => {
</Section> </Section>
)} )}
<Section title="Alive"> <Section title={`Alive - (${alive.length})`}>
{alive {alive
.filter(searchFor(searchText)) .filter(searchFor(searchText))
.sort(compareNumberedText) .sort(compareNumberedText)
@@ -160,11 +195,17 @@ export const Orbit = (props, context) => {
))} ))}
</Section> </Section>
<BasicSection <Section title={`Ghosts - (${ghosts.length})`}>
title="Ghosts" {ghosts
source={ghosts} .filter(searchFor(searchText))
searchText={searchText} .sort(compareNumberedText)
/> .map(thing => (
<OrbitedButton
key={thing.name}
color="grey"
thing={thing} />
))}
</Section>
<BasicSection <BasicSection
title="Dead" title="Dead"

File diff suppressed because one or more lines are too long

View File

@@ -5401,16 +5401,6 @@ send@0.17.1:
range-parser "~1.2.1" range-parser "~1.2.1"
statuses "~1.5.0" 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: serialize-javascript@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" 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" find-cache-dir "^2.1.0"
is-wsl "^1.1.0" is-wsl "^1.1.0"
schema-utils "^1.0.0" schema-utils "^1.0.0"
serialize-javascript "^5.0.1" serialize-javascript "^2.1.2"
source-map "^0.6.1" source-map "^0.6.1"
terser "^4.1.2" terser "^4.1.2"
webpack-sources "^1.4.0" webpack-sources "^1.4.0"
@@ -5883,7 +5873,7 @@ terser-webpack-plugin@^2.1.0:
jest-worker "^25.4.0" jest-worker "^25.4.0"
p-limit "^2.3.0" p-limit "^2.3.0"
schema-utils "^2.6.6" schema-utils "^2.6.6"
serialize-javascript "^5.0.1" serialize-javascript "^3.0.0"
source-map "^0.6.1" source-map "^0.6.1"
terser "^4.6.12" terser "^4.6.12"
webpack-sources "^1.4.3" webpack-sources "^1.4.3"