Merge pull request #8835 from ShadowLarkens/tgui_inventory

TGUI Drag Inventory
This commit is contained in:
Novacat
2020-08-27 15:52:07 -04:00
committed by GitHub
10 changed files with 366 additions and 101 deletions

View File

@@ -8,6 +8,8 @@
maxHealth = 100
mob_size = 4
inventory_panel_type = null // Disable inventory
var/adult_form
var/dead_icon
var/amount_grown = 0
@@ -47,9 +49,6 @@
/mob/living/carbon/alien/restrained()
return 0
/mob/living/carbon/alien/show_inv(mob/user as mob)
return //Consider adding cuffs and hats to this, for the sake of fun.
/mob/living/carbon/alien/cannot_use_vents()
return

View File

@@ -203,55 +203,6 @@
/mob/living/carbon/human/var/co2overloadtime = null
/mob/living/carbon/human/var/temperature_resistance = T0C+75
/mob/living/carbon/human/show_inv(mob/user as mob)
if(user.incapacitated() || !user.Adjacent(src))
return
var/obj/item/clothing/under/suit = null
if (istype(w_uniform, /obj/item/clothing/under))
suit = w_uniform
user.set_machine(src)
var/dat = "<B><HR><FONT size=3>[name]</FONT></B><BR><HR>"
for(var/entry in species.hud.gear)
var/list/slot_ref = species.hud.gear[entry]
if((slot_ref["slot"] in list(slot_l_store, slot_r_store)))
continue
var/obj/item/thing_in_slot = get_equipped_item(slot_ref["slot"])
dat += "<BR><B>[slot_ref["name"]]:</b> <a href='?src=\ref[src];item=[slot_ref["slot"]]'>[istype(thing_in_slot) ? thing_in_slot : "nothing"]</a>"
dat += "<BR><HR>"
if(species.hud.has_hands)
dat += "<BR><b>Left hand:</b> <A href='?src=\ref[src];item=[slot_l_hand]'>[istype(l_hand) ? l_hand : "nothing"]</A>"
dat += "<BR><b>Right hand:</b> <A href='?src=\ref[src];item=[slot_r_hand]'>[istype(r_hand) ? r_hand : "nothing"]</A>"
// Do they get an option to set internals?
if(istype(wear_mask, /obj/item/clothing/mask) || istype(head, /obj/item/clothing/head/helmet/space))
if(istype(back, /obj/item/weapon/tank) || istype(belt, /obj/item/weapon/tank) || istype(s_store, /obj/item/weapon/tank))
dat += "<BR><A href='?src=\ref[src];item=internals'>Toggle internals.</A>"
// Other incidentals.
if(istype(suit) && suit.has_sensor == 1)
dat += "<BR><A href='?src=\ref[src];item=sensors'>Set sensors</A>"
if(handcuffed)
dat += "<BR><A href='?src=\ref[src];item=[slot_handcuffed]'>Handcuffed</A>"
if(legcuffed)
dat += "<BR><A href='?src=\ref[src];item=[slot_legcuffed]'>Legcuffed</A>"
if(suit && LAZYLEN(suit.accessories))
dat += "<BR><A href='?src=\ref[src];item=tie'>Remove accessory</A>"
dat += "<BR><A href='?src=\ref[src];item=splints'>Remove splints</A>"
dat += "<BR><A href='?src=\ref[src];item=pockets'>Empty pockets</A>"
dat += "<BR><A href='?src=\ref[user];refresh=1'>Refresh</A>"
dat += "<BR><A href='?src=\ref[user];mach_close=mob[name]'>Close</A>"
user << browse(dat, text("window=mob[name];size=340x540"))
onclose(user, "mob[name]")
return
// called when something steps onto a human
// this handles mobs on fire - mulebot and vehicle code has been relocated to /mob/living/Crossed()
/mob/living/carbon/human/Crossed(var/atom/movable/AM)
@@ -371,18 +322,14 @@
/mob/living/carbon/human/Topic(href, href_list)
if (href_list["refresh"])
if((machine)&&(in_range(src, usr)))
show_inv(machine)
if (href_list["mach_close"])
if (href_list["mach_close"]) // This is horrible.
var/t1 = text("window=[]", href_list["mach_close"])
unset_machine()
src << browse(null, t1)
if(href_list["item"])
handle_strip(href_list["item"],usr)
log_runtime(EXCEPTION("Warning: human/Topic was called with item [href_list["item"]], but the item Topic is deprecated!"))
// handle_strip(href_list["item"],usr)
// VOREStation Start
if(href_list["ooc_notes"])

View File

@@ -116,3 +116,5 @@
var/mob/living/carbon/human/vr_link = null
var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently.
inventory_panel_type = /datum/inventory_panel/human

View File

@@ -109,24 +109,6 @@
if(slot_wear_mask) return wear_mask
return null
/mob/living/show_inv(mob/user as mob)
user.set_machine(src)
var/dat = {"
<B><HR><FONT size=3>[name]</FONT></B>
<BR><HR>
<BR><B>Head(Mask):</B> <A href='?src=\ref[src];item=mask'>[(wear_mask ? wear_mask : "Nothing")]</A>
<BR><B>Left Hand:</B> <A href='?src=\ref[src];item=l_hand'>[(l_hand ? l_hand : "Nothing")]</A>
<BR><B>Right Hand:</B> <A href='?src=\ref[src];item=r_hand'>[(r_hand ? r_hand : "Nothing")]</A>
<BR><B>Back:</B> <A href='?src=\ref[src];item=back'>[(back ? back : "Nothing")]</A> [((istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/weapon/tank) && !( internal )) ? text(" <A href='?src=\ref[];item=internal'>Set Internal</A>", src) : "")]
<BR>[(internal ? text("<A href='?src=\ref[src];item=internal'>Remove Internal</A>") : "")]
<BR><A href='?src=\ref[src];item=pockets'>Empty Pockets</A>
<BR><A href='?src=\ref[user];refresh=1'>Refresh</A>
<BR><A href='?src=\ref[user];mach_close=mob[name]'>Close</A>
<BR>"}
user << browse(dat, text("window=mob[];size=325x500", name))
onclose(user, "mob[name]")
return
/mob/living/ret_grab(var/list/L, var/mobchain_limit = 5)
// We're the first!
if(!L)
@@ -180,3 +162,191 @@
if((src.l_hand && !( src.l_hand.abstract )) || (src.r_hand && !( src.r_hand.abstract )))
return 1
return 0
// This handles the drag-open inventory panel.
/mob/living/MouseDrop(atom/over_object)
var/mob/living/L = over_object
if(istype(L) && L != src && L == usr && Adjacent(L))
show_inventory_panel(L)
. = ..()
/mob/living/proc/show_inventory_panel(mob/user, datum/tgui_state/state)
if(!inventory_panel_type)
return FALSE
if(!inventory_panel)
inventory_panel = new inventory_panel_type(src)
inventory_panel.tgui_interact(user, null, state)
return TRUE
// TGUITODO: Don't forget to Destroy() these properly!
/datum/inventory_panel
var/mob/living/host
var/tgui_id = "InventoryPanel"
/datum/inventory_panel/New(mob/living/new_host)
if(!istype(new_host))
qdel(src)
return
host = new_host
. = ..()
/datum/inventory_panel/Destroy()
host = null
. = ..()
/datum/inventory_panel/tgui_host(mob/user)
return host.tgui_host()
/datum/inventory_panel/tgui_state(mob/user)
return GLOB.tgui_physical_state
/datum/inventory_panel/tgui_status(mob/user, datum/tgui_state/state)
if(!host)
return STATUS_CLOSE
if(isAI(user))
return STATUS_CLOSE
return ..()
/datum/inventory_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui_state/custom_state)
if(!host)
qdel(src)
return
// This looks kinda complicated, but it's just making sure that the correct state is definitely set
// before calling open(), so that there isn't any accidental UI closes
var/open = FALSE
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, tgui_id, host.name)
open = TRUE
if(custom_state)
ui.set_state(custom_state)
if(open)
ui.open()
return ui
/datum/inventory_panel/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["slots"] = list()
data["slots"].Add(list(list(
"name" = "Head (Mask)",
"item" = host.wear_mask,
"act" = "mask",
)))
data["slots"].Add(list(list(
"name" = "Left Hand",
"item" = host.l_hand,
"act" = "l_hand",
)))
data["slots"].Add(list(list(
"name" = "Right Hand",
"item" = host.r_hand,
"act" = "r_hand",
)))
data["slots"].Add(list(list(
"name" = "Back",
"item" = host.back,
"act" = "back",
)))
data["slots"].Add(list(list(
"name" = "Pockets",
"item" = "Empty Pockets",
"act" = "pockets",
)))
data["internals"] = host.internals
data["internalsValid"] = istype(host.wear_mask, /obj/item/clothing/mask) && istype(host.back, /obj/item/weapon/tank)
return data
/datum/inventory_panel/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
// If anyone wants the inventory panel to actually work,
// add code to handle actions "mask", "l_hand", "r_hand", "back", "pockets", and "internals" here
// No mobs other than humans actually supported stripping or putting stuff on before the /datum/inventory_panel was
// created, so feature parity demands not adding that and risking breaking stuff
/datum/inventory_panel/human
tgui_id = "InventoryPanelHuman"
/datum/inventory_panel/human/New(mob/living/carbon/human/new_host)
if(!istype(new_host))
qdel(src)
return
return ..() // Let our parent assign the host.
/datum/inventory_panel/human/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
var/mob/living/carbon/human/H = host
switch(action)
if("targetSlot")
H.handle_strip(params["slot"], usr)
return TRUE
/datum/inventory_panel/human/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = list() // We don't inherit TGUI data because humans are soooo different.
var/mob/living/carbon/human/H = host // Not my fault if this runtimes, a human inventory panel should never be created without a human attached.
var/obj/item/clothing/under/suit = null
if(istype(H.w_uniform, /obj/item/clothing/under))
suit = H.w_uniform
data["slots"] = list()
for(var/entry in H.species.hud.gear)
var/list/slot_ref = H.species.hud.gear[entry]
if((slot_ref["slot"] in list(slot_l_store, slot_r_store)))
continue
var/obj/item/thing_in_slot = H.get_equipped_item(slot_ref["slot"])
data["slots"].Add(list(list(
"name" = slot_ref["name"],
"item" = thing_in_slot,
"act" = "targetSlot",
"params" = list("slot" = slot_ref["slot"]),
)))
data["specialSlots"] = list()
if(H.species.hud.has_hands)
data["specialSlots"].Add(list(list(
"name" = "Left Hand",
"item" = H.l_hand,
"act" = "targetSlot",
"params" = list("slot" = slot_l_hand),
)))
data["specialSlots"].Add(list(list(
"name" = "Right Hand",
"item" = H.r_hand,
"act" = "targetSlot",
"params" = list("slot" = slot_r_hand),
)))
data["internals"] = H.internals
data["internalsValid"] = (istype(H.wear_mask, /obj/item/clothing/mask) || istype(H.head, /obj/item/clothing/head/helmet/space)) && (istype(H.back, /obj/item/weapon/tank) || istype(H.belt, /obj/item/weapon/tank) || istype(H.s_store, /obj/item/weapon/tank))
data["sensors"] = FALSE
if(istype(suit) && suit.has_sensor == 1)
data["sensors"] = TRUE
data["handcuffed"] = FALSE
if(H.handcuffed)
data["handcuffed"] = TRUE
data["handcuffedParams"] = list("slot" = slot_handcuffed)
data["legcuffed"] = FALSE
if(H.legcuffed)
data["legcuffed"] = TRUE
data["legcuffedParams"] = list("slot" = slot_legcuffed)
data["accessory"] = FALSE
if(suit && LAZYLEN(suit.accessories))
data["accessory"] = TRUE
return data

View File

@@ -72,3 +72,6 @@
var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs
var/image/selected_image = null // Used for buildmode AI control stuff.
var/inventory_panel_type = /datum/inventory_panel
var/datum/inventory_panel/inventory_panel

View File

@@ -211,10 +211,6 @@
client.eye = loc
return TRUE
/mob/proc/show_inv(mob/user as mob)
return
//mob verbs are faster than object verbs. See http://www.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine()
/mob/verb/examinate(atom/A as mob|obj|turf in view())
set name = "Examine"
@@ -489,16 +485,6 @@
/mob/proc/pull_damage()
return 0
/mob/MouseDrop(mob/M as mob)
..()
if(M != usr) return
if(usr == src) return
if(!Adjacent(usr)) return
if(usr.incapacitated(INCAPACITATION_STUNNED | INCAPACITATION_FORCELYING | INCAPACITATION_KNOCKOUT | INCAPACITATION_RESTRAINED)) return //Incapacitated.
if(istype(M,/mob/living/silicon/ai)) return
show_inv(usr)
/mob/verb/stop_pulling()
set name = "Stop Pulling"

View File

@@ -22,7 +22,7 @@
if(!Adjacent(usr)) return
if(isAI(M)) return
for(var/mob/living/carbon/human/O in contents)
O.show_inv(usr)
O.show_inventory_panel(usr, state = GLOB.tgui_deep_inventory_state)
/obj/item/weapon/holder/micro/attack_self(mob/living/carbon/user) //reworked so it works w/ nonhumans
user.setClickCooldown(user.get_attack_speed())

View File

@@ -0,0 +1,47 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
import { Window } from "../layouts";
export const InventoryPanel = (props, context) => {
const { act, data } = useBackend(context);
const {
slots,
internalsValid,
} = data;
return (
<Window width={400} height={200} resizable>
<Window.Content scrollable>
<Section>
<LabeledList>
{slots && slots.length && slots.map(slot => (
<LabeledList.Item key={slot.name} label={slot.name}>
<Button
mb={-1}
icon={slot.item ? "hand-paper" : "gift"}
onClick={() => act(slot.act)}>
{slot.item || "Nothing"}
</Button>
</LabeledList.Item>
))}
</LabeledList>
</Section>
{internalsValid && /* Remove if more actions are added */ (
<Section title="Actions">
{internalsValid && (
<Button
fluid
icon="lungs"
onClick={() => act("internals")}>
Set Internals
</Button>
) || null}
</Section>
) || null}
</Window.Content>
</Window>
);
};

View File

@@ -0,0 +1,111 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
import { Window } from "../layouts";
export const InventoryPanelHuman = (props, context) => {
const { act, data } = useBackend(context);
const {
slots,
specialSlots,
internals,
internalsValid,
sensors,
handcuffed,
handcuffedParams,
legcuffed,
legcuffedParams,
accessory,
} = data;
return (
<Window width={400} height={600} resizable>
<Window.Content scrollable>
<Section>
<LabeledList>
{slots && slots.length && slots.map(slot => (
<LabeledList.Item key={slot.name} label={slot.name}>
<Button
mb={-1}
icon={slot.item ? "hand-paper" : "gift"}
onClick={() => act(slot.act, slot.params)}>
{slot.item || "Nothing"}
</Button>
</LabeledList.Item>
))}
<LabeledList.Divider />
{specialSlots && specialSlots.length && specialSlots.map(slot => (
<LabeledList.Item key={slot.name} label={slot.name}>
<Button
mb={-1}
icon={slot.item ? "hand-paper" : "gift"}
onClick={() => act(slot.act, slot.params)}>
{slot.item || "Nothing"}
</Button>
</LabeledList.Item>
))}
</LabeledList>
</Section>
<Section title="Actions">
<Button
fluid
icon="running"
onClick={() => act("targetSlot", { slot: "splints" })}>
Remove Splints
</Button>
<Button
fluid
icon="hand-paper"
onClick={() => act("targetSlot", { slot: "pockets" })}>
Empty Pockets
</Button>
{internalsValid && (
<Button
fluid
icon="lungs"
onClick={() => act("targetSlot", { slot: "internals" })}>
Set Internals
</Button>
) || null}
{sensors && (
<Button
fluid
icon="book-medical"
onClick={() => act("targetSlot", { slot: "sensors" })}>
Set Sensors
</Button>
) || null}
{handcuffed && (
<Button
fluid
color="bad"
icon="unlink"
onClick={() => act("targetSlot", handcuffedParams)}>
Handcuffed
</Button>
) || null}
{legcuffed && (
<Button
fluid
color="bad"
icon="unlink"
onClick={() => act("targetSlot", legcuffedParams)}>
Legcuffed
</Button>
) || null}
{accessory && (
<Button
fluid
color="bad"
icon="unlink"
onClick={() => act("targetSlot", { slot: "tie" })}>
Remove Accessory
</Button>
) || null}
</Section>
</Window.Content>
</Window>
);
};

File diff suppressed because one or more lines are too long