From 858a3990d52eeca1c2aad7d7bc3a982091cab287 Mon Sep 17 00:00:00 2001 From: FeudeyTF Date: Thu, 8 Feb 2024 20:16:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B3=D0=BE=D1=81=D1=82=D0=BE=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- baystation12.dme | 2 + code/_onclick/hud/_defines.dm | 8 + code/_onclick/hud/ghost.dm | 51 ++++++ code/modules/mob/new_player/new_player.dm | 1 + code/modules/mob/observer/following.dm | 15 +- code/modules/mob/observer/ghost/ghost.dm | 40 ++++- code/modules/mob/observer/ghost/orbit.dm | 75 +++++++++ icons/hud/screen_ghost.dmi | Bin 0 -> 25659 bytes tgui/packages/tgui/interfaces/Orbit.js | 181 ++++++++++++++++++++++ 9 files changed, 358 insertions(+), 15 deletions(-) create mode 100644 code/_onclick/hud/ghost.dm create mode 100644 code/modules/mob/observer/ghost/orbit.dm create mode 100644 icons/hud/screen_ghost.dmi create mode 100644 tgui/packages/tgui/interfaces/Orbit.js diff --git a/baystation12.dme b/baystation12.dme index 9f75b7451e..4154609d27 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -172,6 +172,7 @@ #include "code\_onclick\hud\animal.dm" #include "code\_onclick\hud\chorus.dm" #include "code\_onclick\hud\fullscreen.dm" +#include "code\_onclick\hud\ghost.dm" #include "code\_onclick\hud\global_hud.dm" #include "code\_onclick\hud\global_hud_inf.dm" #include "code\_onclick\hud\gun_mode.dm" @@ -2464,6 +2465,7 @@ #include "code\modules\mob\observer\ghost\ghost.dm" #include "code\modules\mob\observer\ghost\login.dm" #include "code\modules\mob\observer\ghost\logout.dm" +#include "code\modules\mob\observer\ghost\orbit.dm" #include "code\modules\mob\observer\ghost\say.dm" #include "code\modules\mob\observer\virtual\_constants.dm" #include "code\modules\mob\observer\virtual\base.dm" diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 543f420e6e..167565f25a 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -133,3 +133,11 @@ #define ui_pai_light "NORTH,WEST+3:6" #define ui_pai_rest "NORTH,WEST+4:6" +// Ghosts +#define ui_ghost_toggle_darkness "SOUTH:6,CENTER-3:16" +#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:16" +#define ui_ghost_orbit "SOUTH:6,CENTER-1:16" +#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:16" +#define ui_ghost_teleport "SOUTH:6,CENTER+1:16" +#define ui_ghost_mafia "SOUTH:6,CENTER+2:16" +#define ui_ghost_spawners_menu "SOUTH:6,CENTER-4:16" diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm new file mode 100644 index 0000000000..d487d6bf77 --- /dev/null +++ b/code/_onclick/hud/ghost.dm @@ -0,0 +1,51 @@ +/obj/screen/ghost + icon = 'icons/hud/screen_ghost.dmi' + +/obj/screen/ghost/MouseExited(location, control, params) + . = ..() + flick(icon_state + "_anim", src) + +/obj/screen/ghost/jumptomob + name = "Jump to mob" + icon_state = "jumptomob" + screen_loc = ui_ghost_jumptomob + +/obj/screen/ghost/jumptomob/Click() + var/mob/observer/ghost/G = usr + G.jumptomob() + +/obj/screen/ghost/orbit + name = "Orbit" + icon_state = "orbit" + screen_loc = ui_ghost_orbit + +/obj/screen/ghost/orbit/Click() + var/mob/observer/ghost/G = usr + G.follow() + +/obj/screen/ghost/reenter_corpse + name = "Reenter corpse" + icon_state = "reenter_corpse" + screen_loc = ui_ghost_reenter_corpse + +/obj/screen/ghost/reenter_corpse/Click() + var/mob/observer/ghost/G = usr + G.reenter_corpse() + +/obj/screen/ghost/teleport + name = "Teleport" + icon_state = "teleport" + screen_loc = ui_ghost_teleport + +/obj/screen/ghost/teleport/Click() + var/mob/observer/ghost/G = usr + G.dead_tele() + +/obj/screen/ghost/toggle_darkness + name = "Toggle Darkness" + icon_state = "toggle_darkness" + screen_loc = ui_ghost_toggle_darkness + +/obj/screen/ghost/toggle_darkness/Click() + var/mob/observer/ghost/G = usr + G.toggle_darkness() diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 047510e4ea..2d353567ca 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -191,6 +191,7 @@ if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. observer.verbs -= /mob/observer/ghost/verb/toggle_antagHUD // Poor guys, don't know what they are missing! observer.key = key + observer.add_ghost_buttons() qdel(src) return 1 diff --git a/code/modules/mob/observer/following.dm b/code/modules/mob/observer/following.dm index 6ef4ae13b3..bf53b5bc67 100644 --- a/code/modules/mob/observer/following.dm +++ b/code/modules/mob/observer/following.dm @@ -14,12 +14,13 @@ following = null /mob/observer/proc/start_following(var/atom/a) - stop_following() - following = a - GLOB.destroyed_event.register(a, src, .proc/stop_following) - GLOB.moved_event.register(a, src, .proc/keep_following) - GLOB.dir_set_event.register(a, src, /atom/proc/recursive_dir_set) - keep_following(new_loc = get_turf(following)) + if(!istype(a, /obj/screen)) + stop_following() + following = a + GLOB.destroyed_event.register(a, src, .proc/stop_following) + GLOB.moved_event.register(a, src, .proc/keep_following) + GLOB.dir_set_event.register(a, src, /atom/proc/recursive_dir_set) + keep_following(new_loc = get_turf(following)) /mob/observer/proc/keep_following(var/atom/movable/moving_instance, var/atom/old_loc, var/atom/new_loc) - forceMove(get_turf(new_loc)) \ No newline at end of file + forceMove(get_turf(new_loc)) diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index 5920ee86e5..fd53203f26 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -32,6 +32,8 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images var/obj/item/device/multitool/ghost_multitool var/list/hud_images // A list of hud images + var/thearea + /mob/observer/ghost/New(mob/body) see_in_dark = 100 verbs += /mob/proc/toggle_antag_pool @@ -71,7 +73,6 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images ghost_multitool = new(src) GLOB.ghost_mob_list += src - ..() /mob/observer/ghost/Destroy() @@ -108,7 +109,6 @@ Works together with spawning an observer, noted above. ..() if(!loc) return if(!client) return 0 - handle_hud_glasses() if(antagHUD) @@ -150,6 +150,7 @@ Works together with spawning an observer, noted above. ghost.key = key if(ghost.client && !ghost.client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. ghost.verbs -= /mob/observer/ghost/verb/toggle_antagHUD // Poor guys, don't know what they are missing! + ghost.add_ghost_buttons() return ghost /mob/observer/ghostize() // Do not create ghosts of ghosts. @@ -233,6 +234,16 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp announce_ghost_joinleave(mind, 0, "They now occupy their body again.") return 1 +/mob/observer/ghost/proc/jumptomob() + var/mob/M = input(usr, "Pick a mob", "Pick a mob") as null|anything in SSmobs.mob_list + log_and_message_admins("jumped to [key_name(M)]") + var/turf/T = get_turf(M) + if(T && isturf(T)) + jumpTo(T) + else + to_chat(usr, "This mob is not located in the game world.") + + /mob/observer/ghost/verb/toggle_medHUD() set category = "Ghost" set name = "Toggle MedicHUD" @@ -273,16 +284,17 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp M.antagHUD = 1 to_chat(src, SPAN_NOTICE("AntagHUD Enabled")) -/mob/observer/ghost/verb/dead_tele(A in area_repository.get_areas_by_z_level()) +/mob/observer/ghost/verb/dead_tele() set category = "Ghost" set name = "Teleport" set desc= "Teleport to a location" + var/A = input(usr, "Pick an area.", "Pick an area") as num|anything in area_repository.get_areas_by_z_level() var/area/thearea = area_repository.get_areas_by_z_level()[A] + if(!thearea) to_chat(src, "No area available.") return - var/list/area_turfs = get_area_turfs(thearea, shall_check_if_holy() ? list(/proc/is_not_holy_turf) : list()) if(!area_turfs.len) to_chat(src, "This area has been entirely made into sacred grounds, you cannot enter it while you are in this plane of existence!") @@ -300,13 +312,13 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghost_to_turf(T) else to_chat(src, "Invalid coordinates.") -/mob/observer/ghost/verb/follow(var/datum/follow_holder/fh in get_follow_targets()) + +/mob/observer/ghost/verb/follow() set category = "Ghost" set name = "Follow" set desc = "Follow and haunt a mob." - if(!fh.show_entry()) return - start_following(fh.followed_instance) + GLOB.orbit_menu.show(src) /mob/observer/ghost/proc/ghost_to_turf(var/turf/target_turf) if(check_is_holy_turf(target_turf)) @@ -326,7 +338,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp verbs -= /mob/observer/ghost/verb/scan_target ..() -/mob/observer/ghost/keep_following(var/atom/movable/am, var/old_loc, var/new_loc) +/mob/observer/ghost/keep_following(var/obj/am, var/old_loc, var/new_loc) var/turf/T = get_turf(new_loc) if(check_is_holy_turf(T)) to_chat(src, "You cannot follow something standing on holy grounds!") @@ -623,3 +635,15 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp M.respawned_time = world.time M.key = key log_and_message_admins("has respawned.", M) + +/mob/observer/ghost/proc/add_ghost_buttons() + var/jumptomob = new /obj/screen/ghost/jumptomob() + var/orbit = new /obj/screen/ghost/orbit() + var/reenter_corpse = new /obj/screen/ghost/reenter_corpse() + var/teleport = new /obj/screen/ghost/teleport() + var/toggle_darkness = new /obj/screen/ghost/toggle_darkness() + client.screen.Add(jumptomob) + client.screen.Add(orbit) + client.screen.Add(reenter_corpse) + client.screen.Add(teleport) + client.screen.Add(toggle_darkness) diff --git a/code/modules/mob/observer/ghost/orbit.dm b/code/modules/mob/observer/ghost/orbit.dm new file mode 100644 index 0000000000..0c211df84d --- /dev/null +++ b/code/modules/mob/observer/ghost/orbit.dm @@ -0,0 +1,75 @@ +GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new) + +/datum/orbit_menu + +/datum/orbit_menu/tgui_state(mob/user) + return GLOB.tgui_observer_state + +/datum/orbit_menu/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "Orbit", "Orbit") + ui.open() + +/datum/orbit_menu/tgui_act(action, list/params) + . = ..() + + if(.) + return + + switch(action) + if("orbit") + var/datum/follow_holder/fh = locate(params["ref"]) in get_follow_targets() + var/atom/movable/a = fh.followed_instance + var/mob/observer/ghost/G = usr + if(a != usr) + G.start_following(a) + return TRUE + if("refresh") + update_tgui_static_data() + return TRUE + +/datum/orbit_menu/tgui_static_data(mob/user) + var/list/data = list() + data["misc"] = list() + data["ghosts"] = list() + data["dead"] = list() + data["npcs"] = list() + data["alive"] = list() + data["antagonists"] = list() + for(var/datum/follow_holder/fh in get_follow_targets()) + var/atom/movable/fi = fh.followed_instance + var/list/serialized = list() + serialized["name"] = fi.name + serialized["ref"] = "\ref[fh]" + + if(!istype(fi, /mob)) + data["misc"] += list(serialized) + continue + var/mob/M = fi + if(isobserver(M)) + data["ghosts"] += list(serialized) + continue + + if(M.stat == DEAD) + data["dead"] += list(serialized) + continue + + if(M.mind == null) + data["npcs"] += list(serialized) + continue + + data["alive"] += list(serialized) + + var/mob/observer/ghost/O = user + if(O.antagHUD && M.get_antag_info()) + var/antag_serialized = serialized.Copy() + for(var/antag_category in M.get_antag_info()) + antag_serialized["antag"] += list(antag_category) + data["antagonists"] += list(antag_serialized) + + return data + +/// Shows the UI to the specified user. +/datum/orbit_menu/proc/show(mob/user) + tgui_interact(user) diff --git a/icons/hud/screen_ghost.dmi b/icons/hud/screen_ghost.dmi new file mode 100644 index 0000000000000000000000000000000000000000..52d72c731fc193942a273701b0e8340de0d9141f GIT binary patch literal 25659 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 NIvV=wHLA8@{|5lnO0@t0 literal 0 HcmV?d00001 diff --git a/tgui/packages/tgui/interfaces/Orbit.js b/tgui/packages/tgui/interfaces/Orbit.js new file mode 100644 index 0000000000..d4c4f60354 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Orbit.js @@ -0,0 +1,181 @@ +import { createSearch } from 'common/string'; +import { useBackend, useLocalState } from '../backend'; +import { Button, Divider, Flex, Icon, Input, Section } from '../components'; +import { Window } from '../layouts'; + +const PATTERN_NUMBER = / \(([0-9]+)\)$/; + +const searchFor = (searchText) => createSearch(searchText, (thing) => thing.name); + +const compareString = (a, b) => (a < b ? -1 : a > b); + +const compareNumberedText = (a, b) => { + const aName = a.name; + const bName = b.name; + + if (!aName || !bName) { + return 0; + } + + // Check if aName and bName are the same except for a number at the end + // e.g. Medibot (2) and Medibot (3) + const aNumberMatch = aName.match(PATTERN_NUMBER); + const bNumberMatch = bName.match(PATTERN_NUMBER); + + if (aNumberMatch && bNumberMatch && aName.replace(PATTERN_NUMBER, '') === bName.replace(PATTERN_NUMBER, '')) { + const aNumber = parseInt(aNumberMatch[1], 10); + const bNumber = parseInt(bNumberMatch[1], 10); + + return aNumber - bNumber; + } + + return compareString(aName, bName); +}; + +const BasicSection = (props, context) => { + const { act } = useBackend(context); + const { searchText, source, title } = props; + const things = source.filter(searchFor(searchText)); + things.sort(compareNumberedText); + return ( + source.length > 0 && ( +
+ {things.map((thing) => ( +
+ ) + ); +}; + +const OrbitedButton = (props, context) => { + const { act } = useBackend(context); + const { color, thing } = props; + + return ( + + ); +}; + +export const Orbit = (props, context) => { + const { act, data } = useBackend(context); + const { alive, antagonists, dead, ghosts, misc, npcs } = data; + + const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); + + const collatedAntagonists = {}; + for (const antagonist of antagonists) { + for (const anta of antagonist.antag) { + if (collatedAntagonists[anta] === undefined) { + collatedAntagonists[anta] = []; + } + collatedAntagonists[anta].push(antagonist); + } + } + + const sortedAntagonists = Object.entries(collatedAntagonists); + sortedAntagonists.sort((a, b) => { + return compareString(a[0], b[0]); + }); + + const orbitMostRelevant = (searchText) => { + for (const source of [sortedAntagonists.map(([_, antags]) => antags), alive, ghosts, dead, npcs, misc]) { + const member = source.filter(searchFor(searchText)).sort(compareNumberedText)[0]; + if (member !== undefined) { + act('orbit', { ref: member.ref }); + break; + } + } + }; + + return ( + + +
+ + + + + + setSearchText(value)} + onEnter={(_, value) => orbitMostRelevant(value)} + /> + + + + + +
+ {antagonists.length > 0 && ( +
+ {sortedAntagonists.map(([name, antags]) => ( +
+ {antags + .filter(searchFor(searchText)) + .sort(compareNumberedText) + .map((antag) => ( + + ))} +
+ ))} +
+ )} + +
+ {alive + .filter(searchFor(searchText)) + .sort(compareNumberedText) + .map((thing) => ( + + ))} +
+ +
+ {ghosts + .filter(searchFor(searchText)) + .sort(compareNumberedText) + .map((thing) => ( + + ))} +
+ + + + + + +
+
+ ); +};