TG spawn panel port (#19293)

* TG spawn panel port

* .

* .

* click intercept

* .

* diagonals

* .

* .

* fix path search

* reduce that

* Fixes ghost click

* lol

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
Kashargul
2026-03-30 01:01:27 +02:00
committed by GitHub
parent c38c0d0698
commit 7c7aa0d237
38 changed files with 2236 additions and 662 deletions
@@ -0,0 +1,9 @@
// Open "View Variables" menu for target
/mob/observer/dead/CtrlShiftClickOn(atom/target)
if(check_rights(R_DEBUG))
SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/debug_variables, target)
// Open "Show Player Panel" menu for target mob
/mob/observer/dead/CtrlClickOn(atom/target)
if(check_rights(R_ADMIN) && ismob(target))
SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/show_player_panel, target)
+51
View File
@@ -0,0 +1,51 @@
/datum/asset/json/spawnpanel
name = "spawnpanel_atom_data"
/datum/asset/json/spawnpanel/generate()
var/list/data = list()
var/static/list/mapping_objects = typecacheof(list(
/obj/effect/landmark,
/obj/effect/spawner,
/obj/effect/shuttle_landmark,
/obj/effect/floor_decal,
/obj/effect/decal,
/obj/effect/overlay,
/obj/effect/step_trigger,
/obj/effect/overmap,
/obj/effect/zone_divider,
))
data["atoms"] = list()
for(var/obj/each_object as anything in typesof(/obj))
data["atoms"][each_object] = list(
"icon" = /* each_object?.icon_preview ||*/ each_object?.icon || "none",
"icon_state" = /* each_object?.icon_state_preview ||*/ each_object?.icon_state || "none",
"name" = each_object.name,
"description" = each_object.desc,
"mapping" = is_type_in_typecache(each_object, mapping_objects),
"type" = "Objects"
)
for(var/turf/each_turf as anything in typesof(/turf))
data["atoms"][each_turf] = list(
"icon" = each_turf?.icon || "noneturf",
"icon_state" = each_turf?.icon_state || "noneturf",
"name" = each_turf.name,
"description" = each_turf.desc,
"mapping" = is_type_in_typecache(each_turf, mapping_objects),
"type" = "Turfs"
)
for(var/mob/each_mob as anything in typesof(/mob))
data["atoms"][each_mob] = list(
"icon" = each_mob?.icon || "nonemob",
"icon_state" = each_mob?.icon_state || "nonemob",
"name" = each_mob.name,
"description" = each_mob.desc,
"mapping" = is_type_in_typecache(each_mob, mapping_objects),
"type" = "Mobs"
)
return data
@@ -0,0 +1,161 @@
#define WHERE_FLOOR_BELOW_MOB "Current location"
#define WHERE_SUPPLY_BELOW_MOB "Current location (droppod)"
#define WHERE_MOB_HAND "In own mob's hand"
#define WHERE_MARKED_OBJECT "At a marked object"
#define WHERE_IN_MARKED_OBJECT "In the marked object"
#define WHERE_TARGETED_LOCATION "Targeted location"
#define WHERE_TARGETED_LOCATION_POD "Targeted location (droppod)"
#define WHERE_TARGETED_MOB_HAND "In targeted mob's hand"
#define OFFSET_ABSOLUTE "Absolute offset"
#define OFFSET_RELATIVE "Relative offset"
/*
Handles spawning an atom. See the call examples for the proper spawn parameters fetching.
*/
/datum/spawnpanel/proc/spawn_atom(list/spawn_params, mob/user)
if(!check_rights_for(user.client, R_SPAWN) || !spawn_params)
return
var/atom/atom_to_spawn = spawn_params["selected_atom"]
if(!atom_to_spawn || (!ispath(atom_to_spawn, /obj) && !ispath(atom_to_spawn, /turf) && !ispath(atom_to_spawn, /mob)))
return
var/amount = clamp(text2num(spawn_params["atom_amount"]), 1, ADMIN_SPAWN_CAP)
var/X = offset["X"] || 0
var/Y = offset["Y"] || 0
var/Z = offset["Z"] || 0
var/atom_dir = text2num(spawn_params["atom_dir"]) || 1
var/atom_name = sanitize(spawn_params["atom_name"])
var/where_target_type = spawn_params["where_target_type"]
var/atom/target = null
if(where_target_type == WHERE_MOB_HAND || where_target_type == WHERE_TARGETED_MOB_HAND)
target = (where_target_type == WHERE_TARGETED_MOB_HAND ? spawn_params["target"] : user)
if(!target)
to_chat(user, span_warning("No target specified."))
return
if(!ismob(target))
to_chat(user, span_warning("The targeted atom is not a mob."))
return
if(!iscarbon(target) && !isrobot(target))
to_chat(user, span_warning("Can only spawn in hand when the target is a carbon mob or a cyborg."))
where_target_type = WHERE_FLOOR_BELOW_MOB
else if(where_target_type == WHERE_MARKED_OBJECT || where_target_type == WHERE_IN_MARKED_OBJECT)
if(!user.client.holder.marked_datum)
to_chat(user, span_warning("You don't have any object marked."))
return
else if(!istype(user.client.holder.marked_datum, /atom))
to_chat(user, span_warning("The object you have marked cannot be used as a target. Target must be of type /atom."))
return
else
target = (where_target_type == WHERE_MARKED_OBJECT ? get_turf(user.client.holder.marked_datum) : user.client.holder.marked_datum)
else if(where_target_type == WHERE_TARGETED_LOCATION || where_target_type == WHERE_TARGETED_LOCATION_POD)
target = spawn_params["target"]
else
switch(spawn_params["offset_type"])
if(OFFSET_ABSOLUTE)
target = locate(X, Y, Z)
if(OFFSET_RELATIVE)
var/turf/relative_turf
var/atom/user_loc = user.loc
if (user_loc)
relative_turf = get_turf(user_loc)
if (!relative_turf)
if(isobserver(user))
var/mob/observer/dead/user_observer = user
relative_turf = get_turf(user_observer.client?.eye) || get_turf(user_observer)
if (!relative_turf)
relative_turf = locate(1, 1, 1)
if (!relative_turf)
to_chat(user, span_warning("Could not determine a valid relative location."))
return
target = locate(relative_turf.x + X, relative_turf.y + Y, relative_turf.z + Z)
if(!target)
return
var/use_droppod = where_target_type == WHERE_SUPPLY_BELOW_MOB || where_target_type == WHERE_TARGETED_LOCATION_POD
var/obj/structure/drop_pod/polite/pod
if(use_droppod)
pod = new(target)
for(var/i in 1 to amount)
if(istype(atom_to_spawn, /turf))
var/turf/original_turf = target
var/turf/created_turf = original_turf.ChangeTurf(atom_to_spawn.type)
if(created_turf && atom_name)
created_turf.name = atom_name
continue
var/atom/created_atom = new atom_to_spawn(use_droppod ? pod : target)
if(QDELETED(created_atom))
return
created_atom.flags |= ADMIN_SPAWNED
if(spawn_params["apply_icon_override"])
if(spawn_params["selected_atom_icon"])
created_atom.icon = file(spawn_params["selected_atom_icon"])
if(spawn_params["selected_atom_icon_state"])
created_atom.icon_state = spawn_params["selected_atom_icon_state"]
if(spawn_params["atom_icon_size"])
if(ismob(created_atom))
var/mob/living/created_mob = created_atom
created_mob.size_multiplier = spawn_params["atom_icon_size"] / 100
if(atom_dir)
created_atom.set_dir(atom_dir)
if(atom_name)
created_atom.name = atom_name
if(ismob(created_atom))
var/mob/created_mob = created_atom
created_mob.real_name = atom_name
if((where_target_type == WHERE_MOB_HAND || where_target_type == WHERE_TARGETED_MOB_HAND) && isliving(target) && isitem(created_atom))
var/mob/living/living_target = target
var/obj/item/created_item = created_atom
living_target.put_in_hands(created_item)
if(isrobot(living_target))
var/mob/living/silicon/robot/target_robot = living_target
if(target_robot.module)
target_robot.module.add_item(created_item, target_robot)
target_robot.activate_module(created_item)
if(pod)
pod.podfall()
log_admin("[key_name(user)] created [amount == 1 ? "an instance" : "[amount] instances"] of [atom_to_spawn.type]")
if(istype(atom_to_spawn, /mob))
message_admins("[key_name_admin(user)] created [amount == 1 ? "an instance" : "[amount] instances"] of [atom_to_spawn.type]")
#undef WHERE_FLOOR_BELOW_MOB
#undef WHERE_SUPPLY_BELOW_MOB
#undef WHERE_MOB_HAND
#undef WHERE_MARKED_OBJECT
#undef WHERE_IN_MARKED_OBJECT
#undef WHERE_TARGETED_LOCATION
#undef WHERE_TARGETED_LOCATION_POD
#undef WHERE_TARGETED_MOB_HAND
#undef OFFSET_ABSOLUTE
#undef OFFSET_RELATIVE
@@ -0,0 +1,304 @@
#define WHERE_FLOOR_BELOW_MOB "Current location"
#define WHERE_SUPPLY_BELOW_MOB "Current location (droppod)"
#define WHERE_MOB_HAND "In own mob's hand"
#define WHERE_MARKED_OBJECT "At a marked object"
#define WHERE_IN_MARKED_OBJECT "In the marked object"
#define WHERE_TARGETED_LOCATION "Targeted location"
#define WHERE_TARGETED_LOCATION_POD "Targeted location (droppod)"
#define WHERE_TARGETED_MOB_HAND "In targeted mob's hand"
#define PRECISE_MODE_OFF "Off"
#define PRECISE_MODE_TARGET "Target"
#define PRECISE_MODE_MARK "Mark"
#define PRECISE_MODE_COPY "Copy"
#define OFFSET_ABSOLUTE "Absolute offset"
#define OFFSET_RELATIVE "Relative offset"
/*
An instance of a /tg/UI™ Spawn Panel. Stores preferences, spawns things, controls the UI. Unique for each user (their ckey).
*/
/datum/spawnpanel
/// Where and how the atom should be spawned.
var/where_target_type = WHERE_FLOOR_BELOW_MOB
/// The atom selected from the panel.
var/atom/selected_atom = null
/// The icon selected for the atom from the panel.
var/selected_atom_icon = null
/// The icon state selected for the atom from the panel.
var/selected_atom_icon_state = null
/// Should selected icon/icon state override the initial ones? Added as an edge case to not replace animated GAGS icons.
var/apply_icon_override = FALSE
/// A list of icon states to display in preview panels.
var/list/available_icon_states = null
/// Override for the icon size of the spawned mob.
var/atom_icon_size = 100
/// How many atoms will be spawned at once.
var/atom_amount = 1
/// Custom atom name (leave `null` for initial).
var/atom_name = null
/// Custom atom description (leave `null` for initial).
var/atom_desc = null
/// Custom atom dir (leave `null` for `2`).
var/atom_dir = 1
/// An associative list of x-y-z offsets.
var/offset = list()
/// The pivot point for offsetting — relative or absolute.
var/offset_type = OFFSET_RELATIVE
/// Precise mode toggle. Used for build-mode-like spawning experience and targeting datums.
var/precise_mode = PRECISE_MODE_OFF
/datum/spawnpanel/New()
. = ..()
offset = list("X" = 0, "Y" = 0, "Z" = 0)
/datum/spawnpanel/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SpawnPanel")
ui.open()
/datum/spawnpanel/tgui_close(mob/user)
. = ..()
if (precise_mode && precise_mode != PRECISE_MODE_OFF)
toggle_precise_mode(PRECISE_MODE_OFF, user)
/datum/spawnpanel/tgui_state(mob/user)
return ADMIN_STATE(R_SPAWN)
/datum/spawnpanel/tgui_act(action, params, datum/tgui/ui)
if(..() || !check_rights_for(ui.user.client, R_SPAWN))
return FALSE
switch(action)
if("select-new-DMI")
var/icon/new_icon = input("Select a new icon file:", "Icon") as null|icon
if(new_icon)
selected_atom_icon = new_icon
available_icon_states = icon_states(selected_atom_icon)
if(!(selected_atom_icon_state in available_icon_states))
selected_atom_icon_state = available_icon_states[1]
return TRUE
if("set-apply-icon-override")
apply_icon_override = !!params["value"]
return TRUE
if("reset-DMI-icon")
selected_atom_icon = null
selected_atom_icon_state = null
if(selected_atom)
selected_atom_icon = initial(selected_atom.icon)
selected_atom_icon_state = initial(selected_atom.icon_state)
available_icon_states = icon_states(selected_atom_icon)
else
available_icon_states = list()
return TRUE
if("select-new-icon-state")
selected_atom_icon_state = params["new_state"]
return TRUE
if("reset-icon-state")
selected_atom_icon_state = null
if(selected_atom)
selected_atom_icon_state = initial(selected_atom.icon_state)
return TRUE
if("set-icon-size")
atom_icon_size = params["size"]
return TRUE
if("reset-icon-size")
atom_icon_size = 100
return TRUE
if("get-icon-states")
available_icon_states = icon_states(selected_atom_icon)
return TRUE
if("selected-atom-changed")
var/path = text2path(params["newObj"])
if(path)
var/atom/temp_atom = path
selected_atom_icon = initial(temp_atom.icon)
selected_atom_icon_state = initial(temp_atom.icon_state)
available_icon_states = icon_states(selected_atom_icon)
selected_atom = temp_atom
return TRUE
if("create-atom-action")
var/list/spawn_params = list(
"selected_atom" = selected_atom,
"offset" = params["offset"],
"atom_dir" = text2num(params["dir"]) || 1,
"atom_amount" = text2num(params["atom_amount"]) || 1,
"atom_name" = params["atom_name"],
"where_target_type" = params["where_target_type"] || WHERE_FLOOR_BELOW_MOB,
"atom_icon_size" = params["atom_icon_size"],
"offset_type" = params["offset_type"] || OFFSET_RELATIVE,
"apply_icon_override" = apply_icon_override,
)
if(apply_icon_override)
spawn_params["selected_atom_icon"] = selected_atom_icon
spawn_params["selected_atom_icon_state"] = selected_atom_icon_state
spawn_atom(spawn_params, ui.user)
return TRUE
if("toggle-precise-mode")
var/precise_type = params["newPreciseType"]
if(precise_type == PRECISE_MODE_TARGET && params["where_target_type"])
where_target_type = params["where_target_type"]
toggle_precise_mode(precise_type, ui.user)
return TRUE
if("update-settings")
if(params["atom_amount"])
atom_amount = text2num(params["atom_amount"])
if(params["atom_dir"])
atom_dir = text2num(params["atom_dir"])
if(params["offset"])
var/list/temp_offset = params["offset"]
offset["X"] = temp_offset[1]
offset["Y"] = temp_offset[2]
offset["Z"] = temp_offset[3]
if(params["atom_name"])
atom_name = params["atom_name"]
if(params["where_target_type"])
where_target_type = params["where_target_type"]
if(params["offset_type"])
offset_type = params["offset_type"]
if(params["atom_icon_size"])
atom_icon_size = text2num(params["atom_icon_size"])
if(params["selected_atom_icon"])
selected_atom_icon = params["selected_atom_icon"]
if(params["selected_atom_icon_state"])
selected_atom_icon_state = params["selected_atom_icon_state"]
return TRUE
/datum/spawnpanel/proc/toggle_precise_mode(precise_type, mob/user)
precise_mode = precise_type
var/client/admin_client = user.client
if (!admin_client)
return
/* Unimplemented
admin_client.mouse_up_icon = null
admin_client.mouse_down_icon = null
admin_client.mouse_override_icon = null
*/
admin_client.click_intercept = null
if (precise_mode != PRECISE_MODE_OFF)
/* Unimplemented
admin_client.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi'
admin_client.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi'
admin_client.mouse_override_icon = admin_client.mouse_up_icon
admin_client.mouse_pointer_icon = admin_client.mouse_override_icon
*/
admin_client.click_intercept = src
winset(admin_client, "mapwindow.map", "right-click=true")
else
winset(admin_client, "mapwindow.map", "right-click=false")
/* Unimplemented
var/mob/holder_mob = admin_client.mob
holder_mob?.update_mouse_pointer()
*/
/datum/spawnpanel/proc/InterceptClickOn(mob/user, params, atom/target)
var/list/modifiers = params2list(params)
var/left_click = LAZYACCESS(modifiers, LEFT_CLICK)
var/right_click = LAZYACCESS(modifiers, RIGHT_CLICK)
if(right_click)
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
return TRUE
if(left_click)
if(istype(target,/atom/movable/screen))
return FALSE
var/turf/clicked_turf = get_turf(target)
if(!clicked_turf)
return FALSE
switch(precise_mode)
if(PRECISE_MODE_TARGET)
var/list/spawn_params = list(
"selected_atom" = selected_atom,
"atom_amount" = atom_amount,
"offset" = "0,0,0",
"atom_dir" = atom_dir,
"atom_name" = atom_name,
"atom_desc" = atom_desc,
"offset_type" = OFFSET_ABSOLUTE,
"where_target_type" = where_target_type,
"target" = target,
"atom_icon_size" = atom_icon_size,
"apply_icon_override" = apply_icon_override,
)
if(apply_icon_override)
spawn_params["selected_atom_icon"] = selected_atom_icon
spawn_params["selected_atom_icon_state"] = selected_atom_icon_state
if(where_target_type == WHERE_TARGETED_LOCATION || where_target_type == WHERE_TARGETED_LOCATION_POD)
spawn_params["X"] = clicked_turf.x
spawn_params["Y"] = clicked_turf.y
spawn_params["Z"] = clicked_turf.z
spawn_atom(spawn_params, user)
if(PRECISE_MODE_MARK)
var/client/admin_client = user.client
admin_client.mark_datum(target)
to_chat(user, span_notice("Marked object: [icon2html(target, user)] [span_bold("[target]")]"))
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
if(PRECISE_MODE_COPY)
to_chat(user, span_notice("Picked object: [icon2html(target, user)] [span_bold("[target]")]"))
selected_atom = target
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
return TRUE
/datum/spawnpanel/tgui_data(mob/user)
var/data = list()
data["icon"] = selected_atom_icon
data["iconState"] = selected_atom_icon_state
data["iconSize"] = atom_icon_size
data["apply_icon_override"] = apply_icon_override
var/list/states = list()
if(available_icon_states)
for(var/state in available_icon_states)
states += state
data["iconStates"] = states
data["precise_mode"] = precise_mode
data["selected_object"] = selected_atom ? "[selected_atom.type]" : ""
return data
/datum/spawnpanel/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/json/spawnpanel),
)
#undef WHERE_FLOOR_BELOW_MOB
#undef WHERE_SUPPLY_BELOW_MOB
#undef WHERE_MOB_HAND
#undef WHERE_MARKED_OBJECT
#undef WHERE_IN_MARKED_OBJECT
#undef WHERE_TARGETED_LOCATION
#undef WHERE_TARGETED_LOCATION_POD
#undef WHERE_TARGETED_MOB_HAND
#undef PRECISE_MODE_OFF
#undef PRECISE_MODE_TARGET
#undef PRECISE_MODE_MARK
#undef PRECISE_MODE_COPY
#undef OFFSET_ABSOLUTE
#undef OFFSET_RELATIVE