Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/modules/mob/dead/observer/observer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
// of the mob
var/deadchat_name
var/datum/orbit_menu/orbit_menu
var/datum/orbit_menu/mobs/mob_orbit_menu
var/datum/spawners_menu/spawners_menu

// The POI we're orbiting (orbit menu)
Expand Down Expand Up @@ -177,6 +178,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
updateallghostimages()

QDEL_NULL(orbit_menu)
QDEL_NULL(mob_orbit_menu)
QDEL_NULL(spawners_menu)

remove_data_huds()
Expand Down
65 changes: 59 additions & 6 deletions code/modules/mob/dead/observer/orbit.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@
if ("orbit")
var/ref = params["ref"]
var/auto_observe = params["auto_observe"]

// PENTEST ADDITION - Check if this is a planet (datum) that can't be orbited
var/datum/overmap/dynamic/planet = locate(ref)
if(istype(planet))
// Teleport to the overmap marker
var/turf/overmap_turf = OVERMAP_TOKEN_TURF(planet.x, planet.y, planet.current_overmap)
if(overmap_turf)
owner.forceMove(overmap_turf)
to_chat(usr, span_notice("Teleported to overmap coordinates ([planet.x], [planet.y])."))
else
to_chat(usr, span_warning("Unable to find overmap location for this planet."))
return TRUE
// END PENTEST ADDITION

var/atom/movable/poi = SSpoints_of_interest.get_poi_atom_by_ref(ref)

if((ismob(poi) && !SSpoints_of_interest.is_valid_poi(poi, CALLBACK(src, PROC_REF(validate_mob_poi)))) \
Expand All @@ -42,6 +56,13 @@
if (auto_observe)
owner.do_observe(poi)
. = TRUE
if ("switch_mode")
var/mob/dead/observer/user = usr
// Switch to mob orbit menu
if(!user.mob_orbit_menu)
user.mob_orbit_menu = new /datum/orbit_menu/mobs(user)
user.mob_orbit_menu.ui_interact(user)
. = TRUE
if ("refresh")
update_static_data(owner, ui)
. = TRUE
Expand All @@ -67,6 +88,7 @@
var/list/ships = list()
var/list/misc = list()
var/list/npcs = list()
var/list/planets = list() //PENTEST ADDITION

for(var/name in new_mob_pois)
var/list/serialized = list()
Expand All @@ -77,7 +99,7 @@
continue

serialized["ref"] = REF(mob_poi)
serialized["full_name"] = mob_poi.name
serialized["full_name"] = mob_poi.mind ? mob_poi.real_name : mob_poi.name
serialized["job"] = mob_poi.job
if(number_of_orbiters)
serialized["orbiters"] = number_of_orbiters
Expand Down Expand Up @@ -116,15 +138,33 @@
var/atom/atom_poi = new_other_pois[name]

var/list/other_data = get_misc_data(atom_poi)
var/misc_data = list(other_data[1])
var/misc_data = other_data[1]

if(istype(atom_poi, /obj/machinery/computer/helm))
ships += misc_data
ships += list(misc_data)
else
misc += misc_data
misc += list(misc_data)

if(other_data[2]) // Critical = TRUE
critical += misc_data
critical += list(misc_data)

//PENTEST ADDITION - Add loaded planets to their own category
for(var/datum/overmap/dynamic/planet in SSovermap.overmap_objects)
if(!istype(planet))
continue
// Only show planets that have been loaded (have a mapzone)
if(!planet.mapzone)
continue

var/list/planet_data = list()
planet_data["ref"] = REF(planet)
var/planet_name = planet.planet_name || planet.name
var/planet_type_name = planet.planet ? planet.planet.name : "Unknown"
planet_data["full_name"] = "[planet_name] - [planet_type_name]"
planet_data["extra"] = "Coordinates: ([planet.x], [planet.y])"

planets += list(planet_data)
//END PENTEST ADDITION

return list(
"alive" = alive,
Expand All @@ -135,6 +175,7 @@
"ships" = ships,
"misc" = misc,
"npcs" = npcs,
"planets" = planets, //PENTEST ADDITION
)

/datum/orbit_menu/ui_assets()
Expand Down Expand Up @@ -180,7 +221,7 @@
return serialized

var/mob/mob_poi = poi
serialized["full_name"] = mob_poi.name
serialized["full_name"] = mob_poi.mind ? mob_poi.real_name : mob_poi.name
serialized["ref"] = REF(poi)

if(mob_poi.mind)
Expand Down Expand Up @@ -209,6 +250,18 @@
misc["ref"] = REF(atom_poi)
misc["full_name"] = atom_poi.name

// Determine group
var/group = "Other"
if(istype(atom_poi, /obj/overmap))
var/obj/overmap/O = atom_poi
if(istype(O.parent, /datum/overmap/star))
group = "Sun"
else if(istype(O.parent, /datum/overmap/outpost))
group = "Outposts"
else if(istype(atom_poi, /obj/mecha))
group = "Mecha"
misc["group"] = group

// Display the nuke timer
if(istype(atom_poi, /obj/machinery/nuclearbomb))
var/obj/machinery/nuclearbomb/bomb = atom_poi
Expand Down
203 changes: 203 additions & 0 deletions modular_pentest/master_files/code/modules/mob/dead/observer/orbit.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/datum/orbit_menu/mobs
// Inherits owner from parent

/// Types of dead mobs that should still be included in the orbit menu (like pets)
var/static/list/dead_mob_exceptions

// Initialize the dead mob exceptions list
/datum/orbit_menu/mobs/New()
if(!dead_mob_exceptions)
dead_mob_exceptions = typecacheof(list(
/mob/living/simple_animal/pet
))
return ..()

// Inherits New, ui_state from parent

// Inherits ui_interact from parent, but uses same TGUI interface

// Override ui_act to handle mob orbiting without POI system
/datum/orbit_menu/mobs/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
switch(action)
if ("orbit")
var/ref = params["ref"]
var/auto_observe = params["auto_observe"]
var/mob/poi = locate(ref)

if(!ismob(poi) || QDELETED(poi))
to_chat(usr, span_notice("That mob is no longer available."))
return TRUE

// Basic validation - make sure it's still a valid mob to orbit
if(isobserver(poi) || poi.mind || poi.ckey)
to_chat(usr, span_notice("You cannot orbit that."))
return TRUE

var/mob/dead/observer/user = usr
owner.ManualFollow(poi)
owner.reset_perspective(null)
user.orbiting_ref = ref
if (auto_observe)
owner.do_observe(poi)
. = TRUE
if ("switch_mode")
var/mob/dead/observer/user = usr
// Switch to regular orbit menu
if(!user.orbit_menu)
user.orbit_menu = new(user)
SStgui.close_user_uis(user, src) // Close current UI
user.orbit_menu.ui_interact(user)
. = TRUE
if ("refresh")
update_static_data(owner, ui)
. = TRUE

. = ..()
if(.)
return

// Inherits ui_data from parent

/datum/orbit_menu/mobs/ui_static_data(mob/user)
var/list/mobs_by_location = list()

// Collect all living mobs that are NPCs (no mind/ckey) and interesting to orbit
for(var/mob/living/mob_poi in world)
// Skip players and observers
if(isobserver(mob_poi) || mob_poi.mind || mob_poi.ckey)
continue

// Skip newplayers
if(isnewplayer(mob_poi))
continue

// Skip dead mobs - dead NPCs are not interesting to orbit, except for special cases like pets
if(mob_poi.stat == DEAD && !is_type_in_typecache(mob_poi, dead_mob_exceptions))
continue

// Get location info
var/location_name = get_mob_location_name(mob_poi)
if(!location_name)
location_name = "Unknown"

if(!mobs_by_location[location_name])
mobs_by_location[location_name] = list()

var/list/mob_data = list()
mob_data["ref"] = REF(mob_poi)
mob_data["full_name"] = mob_poi.name
mob_data["job"] = mob_poi.job || "Unknown"

if(isliving(mob_poi))
var/mob/living/living_mob = mob_poi
mob_data["health"] = FLOOR((living_mob.health / living_mob.maxHealth * 100), 1)

mobs_by_location[location_name] += list(mob_data)

// Convert to the format expected by TGUI
var/list/location_groups = list()
for(var/location_name in mobs_by_location)
location_groups += list(list(location_name, mobs_by_location[location_name]))

return list("mobs" = location_groups, "critical" = list())

/datum/orbit_menu/mobs/proc/get_mob_location_name(mob/mob_poi)
var/turf/T = get_turf(mob_poi)
if(!T)
return "Unknown"

var/area/A = get_area(mob_poi)
if(!A)
return "Unknown"

// Check if mob is on Centcom z-level (z-level 1)
if(is_centcom_level(mob_poi))
return "Centcom"

// Try to get overmap object name first (with planet name)
var/datum/overmap/O = SSovermap.get_overmap_object_by_location(mob_poi, exclude_ship = TRUE)
if(O)
if(istype(O, /datum/overmap/star))
return "Sun & Outposts"
else if(istype(O, /datum/overmap/outpost))
return O.name
else if(istype(O, /datum/overmap/dynamic))
var/datum/overmap/dynamic/D = O
if(D.planet_name)
return D.planet_name
// Dynamic encounters without planet names - use overmap coordinates
if(D.x && D.y)
return "Grid [D.x]/[D.y]"
else if(istype(O, /datum/overmap/static_object))
var/datum/overmap/static_object/S = O
if(S.planet_name)
return S.planet_name
// Static objects (like ruins) without planet names - use overmap coordinates
if(S.x && S.y)
return "Grid [S.x]/[S.y]"

// Check if mob is on a player ship (don't exclude ships this time)
var/datum/overmap/ship_check = SSovermap.get_overmap_object_by_location(mob_poi, exclude_ship = FALSE)
if(ship_check && istype(ship_check, /datum/overmap/ship))
var/datum/overmap/ship/player_ship = ship_check
return player_ship.name

// Fallback: try to categorize by area type
if(istype(A, /area/space))
// If in space without overmap object, try to get coordinates from any nearby overmap object
var/datum/overmap/space_O = SSovermap.get_overmap_object_by_location(mob_poi, exclude_ship = TRUE)
if(space_O && space_O.x && space_O.y)
return "Grid [space_O.x]/[space_O.y]"
return "Space"
else if(istype(A, /area/ship))
return "Shuttles"
else if(A.name)
// For station areas, group by general location
if(findtext(A.name, "Bridge") || findtext(A.name, "Command") || findtext(A.name, "Captain"))
return "Command"
else if(findtext(A.name, "Engineering") || findtext(A.name, "Atmos") || findtext(A.name, "Power"))
return "Engineering"
else if(findtext(A.name, "Medbay") || findtext(A.name, "Medical") || findtext(A.name, "Surgery"))
return "Medical"
else if(findtext(A.name, "Security") || findtext(A.name, "Brig") || findtext(A.name, "Armory"))
return "Security"
else if(findtext(A.name, "Cargo") || findtext(A.name, "Mining") || findtext(A.name, "Quartermaster"))
return "Cargo"
else if(findtext(A.name, "Science") || findtext(A.name, "Research") || findtext(A.name, "Xenobiology"))
return "Science"
else if(findtext(A.name, "Chapel") || findtext(A.name, "Library") || findtext(A.name, "Hydroponics"))
return "Service"
else
return "Station"

return "Unknown"

/datum/orbit_menu/mobs/get_currently_orbiting(mob/dead/observer/user)
if(isnull(user.orbiting_ref))
return

var/mob/poi = locate(user.orbiting_ref)
if(isnull(poi) || !ismob(poi))
user.orbiting_ref = null
return

// Basic validation
if(isobserver(poi) || poi.mind || poi.ckey)
user.orbiting_ref = null
return

var/list/serialized = list()
serialized["full_name"] = poi.mind ? poi.real_name : poi.name
serialized["ref"] = REF(poi)

if(ismob(poi))
var/mob/mob_poi = poi
if(mob_poi.mind)
serialized["client"] = !!mob_poi.client
serialized["name"] = mob_poi.real_name

if(isliving(poi))
var/mob/living/living_mob = poi
serialized["health"] = FLOOR((living_mob.health / living_mob.maxHealth * 100), 1)

return serialized
1 change: 1 addition & 0 deletions modular_pentest/~pentest.dme
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
#include "master_files\code\modules\mining\lavaland\necropolis_chests.dm"
#include "master_files\code\modules\mob\mob.dm"
#include "master_files\code\modules\mob\dead\new_player\ship_select.dm"
#include "master_files\code\modules\mob\dead\observer\orbit.dm"
#include "master_files\code\modules\mob\living\emote.dm"
#include "master_files\code\modules\mob\living\living.dm"
#include "master_files\code\modules\mob\living\carbon\carbon.dm"
Expand Down
Loading
Loading