Files
Bubberstation/code/modules/admin/spawn_menu.dm
SmArtKar fd06311361 TGUI-fies the Spawn verb (#94094)
## About The Pull Request

This PR converts the native text/list input that pops up when the Spawn
verb is used into a TGUI panel with a dynamic search function. It now
displays atom names, as well if they are abstract (and probably
shouldn't be spawned).
You can still input a typepath after the spawn verb in the tab bar which
will open the menu with said path already in the input box. If only one
typepath matches the input, it will be spawned immediately without
opening the panel.

<img width="400" height="500" alt="Qs0t8xkGj7"
src="https://github.com/user-attachments/assets/d54a57d5-fc22-4f59-af18-45b4fab0b256"
/>

It features RegEx support which activates when the input starts with
``re:``, as well as toggles for searching in atom names and fancy
typepath display. They can be toggled via buttons, or Alt+R/N/F for
regex/names/fancy paths respectively. Invalid regex-es will highlight
the search bar red.
The list itself can be navigated using arrow keys, enter/double click
will spawn the atom, and escape will close the menu.

<img width="400" height="500" alt="xgK2vCd1ck"
src="https://github.com/user-attachments/assets/e621c6ba-ad6b-4a30-922f-eb863797c65a"
/>

<img width="400" height="500" alt="2DeOPBRqoC"
src="https://github.com/user-attachments/assets/18fbba32-17d7-4351-bbc2-bc8e66eb24ae"
/>

Old functionality of spawning multiple objects by leaving a colon +
number after the typepath has been preserved. Using ``*`` and``!`` to
signify final path/end of a path respectively also still works when
regex mode isn't active.

## Why It's Good For The Game

Spawn isn't a critical tool, and using TGUI allows it to have dynamic
search and shifts searching from serverside to clientside, making it
significantly more responsive. Because we don't need to build a fancy
list of atom types, using it the first time won't cause a lag spike on
weaker machines anymore, which should make debugging locally a bit less
annoying.

## Changelog
🆑
admin: Converted the Spawn verb into a TGUI input, featuring for dynamic
search and RegEx support
/🆑
2025-11-28 15:52:28 -07:00

93 lines
2.5 KiB
Plaintext

/datum/spawn_menu
/// Does the menu default to a regex prefix?
var/regex_search = FALSE
/// Does the search include atom names?
var/name_search = TRUE
/// Should we display full typepaths or the condensed versions?
var/fancy_types = TRUE
/// Should abstract types be included in the search?
var/include_abstracts = FALSE
/// Initial search value from the latest command
var/init_value = null
/datum/spawn_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "SpawnSearch")
ui.open()
/datum/spawn_menu/ui_state(mob/user)
return ADMIN_STATE(R_SPAWN)
/datum/spawn_menu/ui_act(action, params, datum/tgui/ui)
if (..() || !check_rights_for(ui.user.client, R_SPAWN))
return FALSE
switch (action)
if ("setRegexSearch")
regex_search = params["regexSearch"]
return TRUE
if ("setNameSearch")
name_search = params["searchNames"]
return TRUE
if ("setFancyTypes")
fancy_types = params["fancyTypes"]
return TRUE
if ("setIncludeAbstracts")
include_abstracts = params["includeAbstracts"]
return TRUE
if ("spawn")
var/path = text2path(params["type"])
if (!path)
return TRUE
var/amount = clamp(text2num(params["amount"]) || 1, 1, ADMIN_SPAWN_CAP)
var/turf/target_turf = get_turf(ui.user)
if(ispath(path, /turf))
target_turf.ChangeTurf(path)
else
for(var/i in 1 to amount)
var/atom/spawned = new path(target_turf)
spawned.flags_1 |= ADMIN_SPAWNED_1
log_admin("[key_name(ui.user)] spawned [amount] x [path] at [AREACOORD(ui.user)]")
SStgui.close_uis(src)
return TRUE
if ("cancel")
SStgui.close_uis(src)
return TRUE
/datum/spawn_menu/ui_data(mob/user)
var/list/data = list()
data["initValue"] = init_value
data["searchNames"] = name_search
data["regexSearch"] = regex_search
data["fancyTypes"] = fancy_types
data["includeAbstracts"] = include_abstracts
return data
/datum/spawn_menu/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/json/spawn_menu),
)
/datum/asset/json/spawn_menu
name = "spawn_menu_atom_data"
/datum/asset/json/spawn_menu/generate()
var/list/data = list()
var/static/list/types_list
if (isnull(types_list))
var/list/local_types = list()
for (var/atom/atom_type as anything in subtypesof(/atom))
local_types[atom_type] = atom_type::name || ""
types_list = local_types
data["types"] = types_list
data["abstractTypes"] = get_abstract_types()
data["fancyTypes"] = GLOB.fancy_type_replacements
return data