mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-27 03:46:27 +01:00
311 lines
10 KiB
Plaintext
311 lines
10 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2024 Citadel Station Developers *//
|
|
|
|
// todo: this should be just mob-interaction-panel and be a tgui interaction panel lol
|
|
|
|
/**
|
|
* Stripping system
|
|
* Procs can be called and overridden as needed
|
|
* Please be careful when doing so and understand what you are overriding.
|
|
*/
|
|
|
|
// todo: tgui
|
|
// todo: ui state handles prechecks? interesting to deal with.
|
|
/mob/proc/mouse_drop_strip_interaction(mob/user)
|
|
if(user.a_intent == INTENT_GRAB)
|
|
return NONE // riding code
|
|
if(user == src)
|
|
return NONE // why would we?
|
|
. = CLICKCHAIN_DO_NOT_PROPAGATE
|
|
request_strip_menu(user)
|
|
|
|
/mob/proc/request_strip_menu(mob/user)
|
|
if(!strip_interaction_prechecks(user, FALSE))
|
|
return FALSE
|
|
return open_strip_menu(user)
|
|
|
|
/mob/proc/open_strip_menu(mob/user)
|
|
var/datum/browser/B = new(user, "strip_window_[REF(src)]", "[name] (stripping)", 340, 540)
|
|
var/list/content = render_strip_menu(user)
|
|
B.set_content(content.Join(""))
|
|
B.open()
|
|
return TRUE
|
|
|
|
/mob/proc/close_strip_menu(mob/user)
|
|
user << browse(null, "window=strip_window_[REF(src)]")
|
|
|
|
/mob/proc/render_strip_menu(mob/user)
|
|
RETURN_TYPE(/list)
|
|
. = list()
|
|
. += "<h1>Inventory</h1>"
|
|
// inv slots
|
|
. += "<hr>"
|
|
var/list/slot_ids = get_inventory_slot_ids(semantic = TRUE, sorted = TRUE)
|
|
var/last_order
|
|
for(var/id in slot_ids)
|
|
// todo: optimize
|
|
var/datum/inventory_slot/meta = resolve_inventory_slot(id)
|
|
var/remove_only = meta.inventory_slot_flags & INV_SLOT_STRIP_ONLY_REMOVES
|
|
var/obj/item/I = _item_by_slot(id)
|
|
if(remove_only && !I)
|
|
continue
|
|
var/simple = meta.inventory_slot_flags & INV_SLOT_STRIP_SIMPLE_LINK
|
|
var/obfuscations = meta.strip_obfuscation_check(I, src, user)
|
|
if(obfuscations & INV_VIEW_OBFUSCATE_HIDE_SLOT)
|
|
continue
|
|
if(last_order > 0 && meta.sort_order < 0)
|
|
. += "<hr>"
|
|
last_order = meta.sort_order
|
|
if(simple)
|
|
. += "<a href='?src=[REF(src)];strip=slot;id=[id]'>[capitalize(meta.name)]</a><br>"
|
|
else
|
|
var/item_known = !(obfuscations & (INV_VIEW_OBFUSCATE_HIDE_ITEM_EXISTENCE | INV_VIEW_OBFUSCATE_HIDE_ITEM_NAME))
|
|
var/slot_text
|
|
if(obfuscations & INV_VIEW_OBFUSCATE_HIDE_ITEM_EXISTENCE)
|
|
slot_text = "<a href='?src=[REF(src)];strip=slot;id=[id]'>obscured</a><br>"
|
|
else if(obfuscations & INV_VIEW_OBFUSCATE_HIDE_ITEM_NAME)
|
|
slot_text = "<a href='?src=[REF(src)];strip=slot;id=[id]'>[I? "something" : "nothing"]</a><br>"
|
|
else
|
|
slot_text = "<a href='?src=[REF(src)];strip=slot;id=[id]'>[(I && I.name) || "nothing"]</a><br>"
|
|
. += "[capitalize(meta.name)]: "
|
|
. += slot_text
|
|
if(I && item_known)
|
|
var/list/options = I.strip_menu_options(user)
|
|
if(LAZYLEN(options))
|
|
// generate hrefs for the options
|
|
for(var/key in options)
|
|
var/name = options[key]
|
|
. += " <a href='?src=[REF(src)];strip=opti;item=[REF(I)];act=[key]'>[name]</a><br>"
|
|
. += "<hr>"
|
|
|
|
// now for hands
|
|
if(has_hands())
|
|
for(var/i in 1 to inventory.get_hand_count())
|
|
switch(i)
|
|
if(1)
|
|
. += "Left hand: "
|
|
if(2)
|
|
. += "Right hand: "
|
|
else
|
|
. += "Hand [i]: "
|
|
var/obj/item/holding = inventory.held_items[i]
|
|
. += "<a href='?src=[REF(src)];strip=hand;id=[i]'>[holding? holding.name : "nothing"]</a><br>"
|
|
. += "<hr>"
|
|
|
|
// now for options
|
|
var/list/options = strip_menu_options(user)
|
|
if(LAZYLEN(options))
|
|
// generate hrefs for the options
|
|
for(var/key in options)
|
|
var/name = options[key]
|
|
. += "<a href='?src=[REF(src)];strip=optm;act=[key]'>[name]</a><br>"
|
|
. += "<hr>"
|
|
|
|
// now for misc
|
|
. += "<a href='?src=[REF(src)];strip=refresh'>Refresh</a><br>"
|
|
|
|
/mob/proc/attempt_slot_strip(mob/user, slot_id, delay_mod = 1)
|
|
if(!strip_interaction_prechecks(user))
|
|
return FALSE
|
|
|
|
var/datum/inventory_slot/slot_meta = resolve_inventory_slot(slot_id)
|
|
if(!slot_meta)
|
|
return FALSE
|
|
|
|
var/obj/item/ours = item_by_slot_id(slot_id)
|
|
var/obj/item/theirs = user.get_active_held_item()
|
|
if(!ours && !theirs)
|
|
to_chat(user, SPAN_WARNING("They're not wearing anything in that slot!"))
|
|
return FALSE
|
|
|
|
if(!attempt_strip_common(ours, theirs, user, slot_id))
|
|
return FALSE
|
|
|
|
if(ours)
|
|
if(temporarily_remove_from_inventory(ours, user = user))
|
|
add_attack_logs(user, src, "Removed [ours] from slot [slot_id]")
|
|
user.put_in_hands_or_drop(ours)
|
|
else
|
|
add_attack_logs(user, src, "Failed to remove [ours] from slot [slot_id]")
|
|
else
|
|
if(equip_to_slot_if_possible(theirs, slot_id))
|
|
add_attack_logs(user, src, "Put [theirs] in slot [slot_id]")
|
|
else
|
|
add_attack_logs(user, src, "Failed to put [theirs] in slot [slot_id]")
|
|
return TRUE
|
|
|
|
/mob/proc/attempt_hand_strip(mob/user, index, delay_mod = 1)
|
|
if(!strip_interaction_prechecks(user))
|
|
return FALSE
|
|
|
|
if((index < 1) || (index > inventory.get_hand_count()))
|
|
return FALSE
|
|
|
|
var/obj/item/ours = inventory.get_hand_single(index)
|
|
var/obj/item/theirs = user.get_active_held_item()
|
|
|
|
if(!ours && !theirs)
|
|
to_chat(user, SPAN_WARNING("They're not holding anything in that hand!"))
|
|
return FALSE
|
|
|
|
if(!attempt_strip_common(ours, theirs, user, index))
|
|
return FALSE
|
|
|
|
if(ours)
|
|
if(temporarily_remove_from_inventory(ours, user = user))
|
|
add_attack_logs(user, src, "Removed [ours] from hand index [index]")
|
|
user.put_in_hands_or_drop(ours)
|
|
else
|
|
add_attack_logs(user, src, "Failed to remove [ours] from hand index [index]")
|
|
else
|
|
if(put_in_hand(theirs, index))
|
|
add_attack_logs(user, src, "Put [theirs] in hand index [index]")
|
|
else
|
|
add_attack_logs(user, src, "Failed to put [theirs] in hand index [index]")
|
|
return TRUE
|
|
|
|
/mob/proc/attempt_strip_common(obj/item/ours, obj/item/theirs, mob/user, slot_id_or_index)
|
|
var/removing = !!ours
|
|
|
|
var/view_flags = NONE
|
|
|
|
var/datum/inventory_slot/slot_meta = resolve_inventory_slot(slot_id_or_index)
|
|
if(!isnum(slot_id_or_index))
|
|
slot_meta = resolve_inventory_slot(slot_id_or_index)
|
|
view_flags = slot_meta.strip_obfuscation_check(ours, src, user)
|
|
if(view_flags & (INV_VIEW_OBFUSCATE_HIDE_SLOT))
|
|
return FALSE // how are you seeing this
|
|
|
|
var/hide_item = view_flags & (INV_VIEW_OBFUSCATE_HIDE_ITEM_NAME | INV_VIEW_OBFUSCATE_HIDE_ITEM_EXISTENCE)
|
|
|
|
if(removing)
|
|
if(!can_unequip(ours, user = user))
|
|
to_chat(user, SPAN_WARNING("[ours] is stuck!"))
|
|
return FALSE
|
|
if(!(view_flags & INV_VIEW_STRIP_IS_SILENT))
|
|
if(hide_item)
|
|
visible_message(
|
|
SPAN_DANGER("[user] is trying to remove something from [src]!"),
|
|
SPAN_DANGER("[user] is trying to remove your [ours.name]!")
|
|
)
|
|
else
|
|
visible_message(
|
|
SPAN_DANGER("[user] is trying to remove [src]'s [ours.name]!"),
|
|
SPAN_DANGER("[user] is trying to remove your [ours.name]!")
|
|
)
|
|
else
|
|
to_chat(user, SPAN_WARNING("You start trying to sneakily remove [hide_item? "something" : ours.name] from [src]!"))
|
|
else
|
|
if(!user.can_unequip(theirs))
|
|
to_chat(user, SPAN_WARNING("[theirs] is stuck to your hand!"))
|
|
return FALSE
|
|
|
|
// if it isn't a hand index, check semantic conflicts first so they don't waste time.
|
|
if(!isnum(slot_id_or_index) && !inventory_slot_semantic_conflict(theirs, slot_meta, user))
|
|
to_chat(user, SPAN_WARNING("[theirs] doesn't go there!"))
|
|
return FALSE
|
|
|
|
if(!(view_flags & INV_VIEW_STRIP_IS_SILENT))
|
|
switch(slot_id_or_index)
|
|
if(SLOT_ID_MASK)
|
|
visible_message(
|
|
SPAN_DANGER("[user] is trying to put \a [theirs] in [src]'s mouth!"),
|
|
SPAN_DANGER("[user] is trying to put \a [theirs] in your mouth!")
|
|
)
|
|
else
|
|
visible_message(
|
|
SPAN_DANGER("[user] is trying to put \a [theirs] on [src]!"),
|
|
SPAN_DANGER("[user] is trying to put \a [theirs] on you!")
|
|
)
|
|
else
|
|
to_chat(user, SPAN_WARNING("You start trying to sneakily put \a [theirs] on [src]!"))
|
|
|
|
if(!do_after(user, HUMAN_STRIP_DELAY, src, FALSE))
|
|
if(view_flags & INV_VIEW_STRIP_FUMBLE_ON_FAILURE)
|
|
// slot_meta must not be null if view_flags isn't NONE, so.
|
|
to_chat(src, SPAN_WARNING("You feel something being fumbled with near your [slot_meta.name]!"))
|
|
return FALSE
|
|
|
|
if(removing)
|
|
if(isnum(slot_id_or_index))
|
|
if(get_held_index(ours) != slot_id_or_index)
|
|
return FALSE
|
|
else
|
|
if(slot_id_by_item(ours) != slot_id_or_index)
|
|
return FALSE
|
|
else
|
|
if(!user.is_holding(theirs))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/proc/handle_strip_topic(mob/user, list/href_list, operation = href_list["strip"])
|
|
// do checks
|
|
if(!strip_interaction_prechecks(user))
|
|
return
|
|
// act
|
|
switch(operation)
|
|
if("slot")
|
|
var/slot = href_list["id"]
|
|
. = attempt_slot_strip(user, slot)
|
|
if("hand")
|
|
var/index = text2num(href_list["id"])
|
|
if(!index || (index < 1) || (index > get_nominal_hand_count()))
|
|
return
|
|
. = attempt_hand_strip(user, index)
|
|
// option mob
|
|
if("optm")
|
|
var/action = href_list["act"]
|
|
. = strip_menu_act(user, action)
|
|
// option item
|
|
if("opti")
|
|
var/obj/item/I = locate(href_list["item"])
|
|
if(!istype(I) || !is_in_inventory(I))
|
|
return
|
|
var/slot = I.worn_slot
|
|
if(slot != SLOT_ID_HANDS)
|
|
var/datum/inventory_slot/slot_meta = resolve_inventory_slot(slot)
|
|
var/view_flags = slot_meta.strip_obfuscation_check(I, src, user)
|
|
if(view_flags & (INV_VIEW_OBFUSCATE_DISALLOW_INTERACT | INV_VIEW_OBFUSCATE_HIDE_ITEM_EXISTENCE | INV_VIEW_OBFUSCATE_HIDE_SLOT))
|
|
return // how tf are you gonna interact with it huh
|
|
var/action = href_list["act"]
|
|
. = I.strip_menu_act(user, action)
|
|
if("refresh")
|
|
// we do that later
|
|
. = TRUE
|
|
|
|
// refresh
|
|
if(.)
|
|
open_strip_menu(user)
|
|
|
|
/mob/proc/strip_interaction_prechecks(mob/user, autoclose = TRUE, allow_loc = FALSE)
|
|
if(!isliving(user))
|
|
// no ghost fuckery
|
|
return FALSE
|
|
if(user.incapacitated())
|
|
to_chat(user, SPAN_WARNING("You are incapacitated!"))
|
|
close_strip_menu(user)
|
|
return FALSE
|
|
if(user.restrained())
|
|
to_chat(user, SPAN_WARNING("You are restrained!"))
|
|
return FALSE
|
|
if(!user.Adjacent(src) && (!allow_loc || !user.Adjacent(loc)))
|
|
to_chat(user, SPAN_WARNING("You are too far away!"))
|
|
close_strip_menu(user)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* return a list of action = name. action should be short, for hrefs! same for name!
|
|
*/
|
|
/mob/proc/strip_menu_options(mob/user)
|
|
RETURN_TYPE(/list)
|
|
return list()
|
|
|
|
/**
|
|
* use for strip menu options
|
|
* adjacency/can act is checked already
|
|
* return TRUE to refresh
|
|
*/
|
|
/mob/proc/strip_menu_act(mob/user, action)
|
|
return FALSE
|