mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-19 03:55:11 +01:00
RPG Loot: Revisited & READY (#82533)
## About The Pull Request Revival of #72881 A new alt click window with a tarkov-y loading spinner. Replaces the object item window in stat panel. ## Videos <details> <summary>vids</summary> toggleable grouping:  now lists the floor as first obj:  in action:  </details> ## features: - search by name - 515 image generator is much faster than alt click menu - opening a gargantuan amount of items shouldnt freeze your screen - groups similar items together in stacks by default, toggleable - shows tile as first item - <kbd>Shift</kbd> and <kbd>Ctrl</kbd> compatible with LMB 🖱️ - RMB points points at items (sry i could not get MMB working) - key <kbd>Esc</kbd> to exit the window. For devs: - A new image generation tech. - An error refetch mechanic to the Image component - It does not "smart track" the items being added to the pile, just reopen or refresh. This was a design decision. ## Why It's Good For The Game Honestly I just dislike the stat panel Fixes #53824 Fixes  ## Changelog 🆑 add: Added a loot window for alt-clicking tiles. del: Removed the item browser from the stat panel. /🆑 --------- Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: AnturK <AnturK@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* ## Loot panel
|
||||
* A datum that stores info containing the contents of a turf.
|
||||
* Handles opening the lootpanel UI and searching the turf for items.
|
||||
*/
|
||||
/datum/lootpanel
|
||||
/// The owner of the panel
|
||||
var/client/owner
|
||||
/// The list of all search objects indexed.
|
||||
var/list/datum/search_object/contents = list()
|
||||
/// The list of search_objects needing processed
|
||||
var/list/datum/search_object/to_image = list()
|
||||
/// We've been notified about client version
|
||||
var/notified = FALSE
|
||||
/// The turf being searched
|
||||
var/turf/source_turf
|
||||
|
||||
|
||||
/datum/lootpanel/New(client/owner)
|
||||
. = ..()
|
||||
|
||||
src.owner = owner
|
||||
|
||||
|
||||
/datum/lootpanel/Destroy(force)
|
||||
reset_contents()
|
||||
owner = null
|
||||
source_turf = null
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/lootpanel/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "LootPanel")
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
|
||||
/datum/lootpanel/ui_close(mob/user)
|
||||
. = ..()
|
||||
|
||||
source_turf = null
|
||||
reset_contents()
|
||||
|
||||
|
||||
/datum/lootpanel/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["contents"] = get_contents()
|
||||
data["searching"] = length(to_image)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
/datum/lootpanel/ui_status(mob/user, datum/ui_state/state)
|
||||
if(!source_turf.Adjacent(user))
|
||||
return UI_CLOSE
|
||||
|
||||
if(user.incapacitated())
|
||||
return UI_DISABLED
|
||||
|
||||
return UI_INTERACTIVE
|
||||
|
||||
|
||||
/datum/lootpanel/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("grab")
|
||||
return grab(usr, params)
|
||||
if("refresh")
|
||||
return populate_contents()
|
||||
|
||||
return FALSE
|
||||
@@ -0,0 +1,49 @@
|
||||
/// Adds the item to contents and to_image (if needed)
|
||||
/datum/lootpanel/proc/add_to_index(datum/search_object/index)
|
||||
RegisterSignal(index, COMSIG_QDELETING, PROC_REF(on_searchable_deleted))
|
||||
if(isnull(index.icon))
|
||||
to_image += index
|
||||
|
||||
contents += index
|
||||
|
||||
|
||||
/// Used to populate contents and start generating if needed
|
||||
/datum/lootpanel/proc/populate_contents()
|
||||
if(length(contents))
|
||||
reset_contents()
|
||||
|
||||
// Add source turf first
|
||||
var/datum/search_object/source = new(owner, source_turf)
|
||||
add_to_index(source)
|
||||
|
||||
for(var/atom/thing as anything in source_turf.contents)
|
||||
// validate
|
||||
if(thing.mouse_opacity == MOUSE_OPACITY_TRANSPARENT)
|
||||
continue
|
||||
if(thing.IsObscured())
|
||||
continue
|
||||
if(thing.invisibility > owner.mob.see_invisible)
|
||||
continue
|
||||
|
||||
// convert
|
||||
var/datum/search_object/index = new(owner, thing)
|
||||
add_to_index(index)
|
||||
|
||||
var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src)
|
||||
window?.send_update()
|
||||
|
||||
if(length(to_image))
|
||||
SSlooting.backlog += src
|
||||
|
||||
|
||||
/// For: Resetting to empty. Ignores the searchable qdel event
|
||||
/datum/lootpanel/proc/reset_contents()
|
||||
for(var/datum/search_object/index as anything in contents)
|
||||
contents -= index
|
||||
to_image -= index
|
||||
|
||||
if(QDELETED(index))
|
||||
continue
|
||||
|
||||
UnregisterSignal(index, COMSIG_QDELETING)
|
||||
qdel(index)
|
||||
@@ -0,0 +1,19 @@
|
||||
/// On contents change, either reset or update
|
||||
/datum/lootpanel/proc/on_searchable_deleted(datum/search_object/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
contents -= source
|
||||
to_image -= source
|
||||
|
||||
var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src)
|
||||
#if !defined(UNIT_TESTS) // we dont want to delete contents if we're testing
|
||||
if(isnull(window))
|
||||
reset_contents()
|
||||
return
|
||||
#endif
|
||||
|
||||
if(isturf(source.item))
|
||||
populate_contents()
|
||||
return
|
||||
|
||||
window?.send_update()
|
||||
@@ -0,0 +1,48 @@
|
||||
/// Helper to open the panel
|
||||
/datum/lootpanel/proc/open(turf/tile)
|
||||
source_turf = tile
|
||||
|
||||
#if !defined(OPENDREAM) && !defined(UNIT_TESTS)
|
||||
if(!notified)
|
||||
var/build = owner.byond_build
|
||||
var/version = owner.byond_version
|
||||
if(build < 515 || (build == 515 && version < 1635))
|
||||
to_chat(owner.mob, examine_block(span_info("\
|
||||
<span class='bolddanger'>Your version of Byond doesn't support fast image loading.</span>\n\
|
||||
Detected: [version].[build]\n\
|
||||
Required version for this feature: <b>515.1635</b> or later.\n\
|
||||
Visit <a href=\"https://secure.byond.com/download\">BYOND's website</a> to get the latest version of BYOND.\n\
|
||||
")))
|
||||
|
||||
notified = TRUE
|
||||
#endif
|
||||
|
||||
populate_contents()
|
||||
ui_interact(owner.mob)
|
||||
|
||||
|
||||
/**
|
||||
* Called by SSlooting whenever this datum is added to its backlog.
|
||||
* Iterates over to_image list to create icons, then removes them.
|
||||
* Returns boolean - whether this proc has finished the queue or not.
|
||||
*/
|
||||
/datum/lootpanel/proc/process_images()
|
||||
for(var/datum/search_object/index as anything in to_image)
|
||||
to_image -= index
|
||||
|
||||
if(QDELETED(index) || index.icon)
|
||||
continue
|
||||
|
||||
index.generate_icon(owner)
|
||||
|
||||
if(TICK_CHECK)
|
||||
break
|
||||
|
||||
var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src)
|
||||
if(isnull(window))
|
||||
reset_contents()
|
||||
return TRUE
|
||||
|
||||
window.send_update()
|
||||
|
||||
return !length(to_image)
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* ## Search Object
|
||||
* An object for content lists. Compacted item data.
|
||||
*/
|
||||
/datum/search_object
|
||||
/// Item we're indexing
|
||||
var/atom/item
|
||||
/// Url to the image of the object
|
||||
var/icon
|
||||
/// Icon state, for inexpensive icons
|
||||
var/icon_state
|
||||
/// Name of the original object
|
||||
var/name
|
||||
/// Typepath of the original object for ui grouping
|
||||
var/path
|
||||
|
||||
|
||||
/datum/search_object/New(client/owner, atom/item)
|
||||
. = ..()
|
||||
|
||||
src.item = item
|
||||
name = item.name
|
||||
if(isobj(item))
|
||||
path = item.type
|
||||
|
||||
if(isturf(item))
|
||||
RegisterSignal(item, COMSIG_TURF_CHANGE, PROC_REF(on_turf_change))
|
||||
else
|
||||
RegisterSignals(item, list(
|
||||
COMSIG_ITEM_PICKUP,
|
||||
COMSIG_MOVABLE_MOVED,
|
||||
COMSIG_QDELETING,
|
||||
), PROC_REF(on_item_moved))
|
||||
|
||||
// Icon generation conditions //////////////
|
||||
// Condition 1: Icon is complex
|
||||
if(ismob(item) || length(item.overlays) > 2)
|
||||
return
|
||||
|
||||
// Condition 2: Can't get icon path
|
||||
if(!isfile(item.icon) || !length("[item.icon]"))
|
||||
return
|
||||
|
||||
// Condition 3: Using opendream
|
||||
#if defined(OPENDREAM) || defined(UNIT_TESTS)
|
||||
return
|
||||
#endif
|
||||
|
||||
// Condition 4: Using older byond version
|
||||
var/build = owner.byond_build
|
||||
var/version = owner.byond_version
|
||||
if(build < 515 || (build == 515 && version < 1635))
|
||||
return
|
||||
|
||||
icon = "[item.icon]"
|
||||
icon_state = item.icon_state
|
||||
|
||||
|
||||
/datum/search_object/Destroy(force)
|
||||
item = null
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/// Generates the icon for the search object. This is the expensive part.
|
||||
/datum/search_object/proc/generate_icon(client/owner)
|
||||
if(ismob(item) || length(item.overlays) > 2)
|
||||
icon = costly_icon2html(item, owner, sourceonly = TRUE)
|
||||
else // our pre 515.1635 fallback for normal items
|
||||
icon = icon2html(item, owner, sourceonly = TRUE)
|
||||
|
||||
|
||||
/// Parent item has been altered, search object no longer valid
|
||||
/datum/search_object/proc/on_item_moved(atom/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
qdel(src)
|
||||
|
||||
|
||||
/// Parent tile has been altered, entire search needs reset
|
||||
/datum/search_object/proc/on_turf_change(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
post_change_callbacks += CALLBACK(src, GLOBAL_PROC_REF(qdel), src)
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
/// Queues image generation for search objects without icons
|
||||
SUBSYSTEM_DEF(looting)
|
||||
name = "Loot Icon Generation"
|
||||
init_order = INIT_ORDER_LOOT
|
||||
priority = FIRE_PRIORITY_PROCESS
|
||||
wait = 0.5 SECONDS
|
||||
/// Backlog of items. Gets put into processing
|
||||
var/list/datum/lootpanel/backlog = list()
|
||||
/// Actively processing items
|
||||
var/list/datum/lootpanel/processing = list()
|
||||
|
||||
|
||||
/datum/controller/subsystem/looting/stat_entry(msg)
|
||||
msg = "P:[length(backlog)]"
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/controller/subsystem/looting/fire(resumed)
|
||||
if(!length(backlog))
|
||||
return
|
||||
|
||||
if(!resumed)
|
||||
processing = backlog
|
||||
backlog = list()
|
||||
|
||||
while(length(processing))
|
||||
var/datum/lootpanel/panel = processing[length(processing)]
|
||||
if(QDELETED(panel) || !length(panel.to_image))
|
||||
processing.len--
|
||||
continue
|
||||
|
||||
if(!panel.process_images())
|
||||
backlog += panel
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
processing.len--
|
||||
@@ -0,0 +1,46 @@
|
||||
/// UI helper for converting the associative list to a list of lists
|
||||
/datum/lootpanel/proc/get_contents()
|
||||
var/list/items = list()
|
||||
|
||||
for(var/datum/search_object/index as anything in contents)
|
||||
UNTYPED_LIST_ADD(items, list(
|
||||
"icon_state" = index.icon_state,
|
||||
"icon" = index.icon,
|
||||
"name" = index.name,
|
||||
"path" = index.path,
|
||||
"ref" = REF(index),
|
||||
))
|
||||
|
||||
return items
|
||||
|
||||
|
||||
/// Clicks an object from the contents. Validates the object and the user
|
||||
/datum/lootpanel/proc/grab(mob/user, list/params)
|
||||
var/ref = params["ref"]
|
||||
if(isnull(ref))
|
||||
return FALSE
|
||||
|
||||
if(!source_turf.Adjacent(user)) // Source tile is no longer valid
|
||||
reset_contents()
|
||||
return FALSE
|
||||
|
||||
var/datum/search_object/index = locate(ref) in contents
|
||||
var/atom/thing = index?.item
|
||||
if(QDELETED(index) || QDELETED(thing)) // Obj is gone
|
||||
return FALSE
|
||||
|
||||
if(thing != source_turf && !(locate(thing) in source_turf.contents))
|
||||
qdel(index) // Item has moved
|
||||
return TRUE
|
||||
|
||||
var/modifiers = ""
|
||||
if(params["ctrl"])
|
||||
modifiers += "ctrl=1;"
|
||||
if(params["middle"])
|
||||
modifiers += "middle=1;"
|
||||
if(params["shift"])
|
||||
modifiers += "shift=1;"
|
||||
|
||||
user.ClickOn(thing, modifiers)
|
||||
|
||||
return TRUE
|
||||
Reference in New Issue
Block a user