mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-27 17:41:50 +00:00
3591 individual conflicts Update build.js Update install_node.sh Update byond.js oh my fucking god hat slow huh holy shit we all fall down 2 more I missed 2900 individual conflicts 2700 Individual conflicts replaces yarn file with tg version, bumping us down to 2200-ish Down to 2000 individual conflicts 140 down mmm aaaaaaaaaaaaaaaaaaa not yt 575 soon 900 individual conflicts 600 individual conflicts, 121 file conflicts im not okay 160 across 19 files 29 in 4 files 0 conflicts, compiletime fix time some minor incap stuff missed ticks weird dupe definition stuff missed ticks 2 incap fixes undefs and pie fix Radio update and some extra minor stuff returns a single override no more dupe definitions, 175 compiletime errors Unticked file fix sound and emote stuff honk and more radio stuff
529 lines
17 KiB
Plaintext
529 lines
17 KiB
Plaintext
/// An element for atoms that, when dragged and dropped onto a mob, opens a strip panel.
|
|
/datum/element/strippable
|
|
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY
|
|
argument_hash_start_idx = 2
|
|
|
|
/// An assoc list of keys to /datum/strippable_item
|
|
var/list/items
|
|
|
|
/// A proc path that returns TRUE/FALSE if we should show the strip panel for this entity.
|
|
/// If it does not exist, the strip menu will always show.
|
|
/// Will be called with (mob/user).
|
|
var/should_strip_proc_path
|
|
|
|
/// An existing strip menus
|
|
var/list/strip_menus
|
|
|
|
/datum/element/strippable/Attach(datum/target, list/items = list(), should_strip_proc_path)
|
|
. = ..()
|
|
if (!isatom(target))
|
|
return ELEMENT_INCOMPATIBLE
|
|
|
|
RegisterSignal(target, COMSIG_MOUSEDROP_ONTO, PROC_REF(mouse_drop_onto))
|
|
|
|
src.items = items
|
|
src.should_strip_proc_path = should_strip_proc_path
|
|
|
|
/datum/element/strippable/Detach(datum/source)
|
|
. = ..()
|
|
|
|
UnregisterSignal(source, COMSIG_MOUSEDROP_ONTO)
|
|
|
|
if (!isnull(strip_menus))
|
|
qdel(strip_menus[source])
|
|
strip_menus -= source
|
|
|
|
/datum/element/strippable/proc/mouse_drop_onto(datum/source, atom/over, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if (user == source)
|
|
return
|
|
if (over != user)
|
|
return
|
|
if(!user.can_perform_action(source, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
|
|
return
|
|
|
|
// Snowflake for cyborgs buckling people by dragging them onto them, unless in combat mode.
|
|
if (iscyborg(user))
|
|
var/mob/living/silicon/robot/cyborg_user = user
|
|
if (!cyborg_user.combat_mode)
|
|
return
|
|
// Snowflake for xeno consumption code
|
|
if (isalienadult(user))
|
|
var/mob/living/carbon/alien/adult/alien = user
|
|
if (alien.grab_state == GRAB_AGGRESSIVE && alien.pulling == source)
|
|
return
|
|
|
|
if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(user))
|
|
return
|
|
|
|
// Snowflake for mob scooping
|
|
if (isliving(source))
|
|
var/mob/living/mob = source
|
|
if (mob.can_be_held && (user.grab_state == GRAB_AGGRESSIVE) && (user.pulling == source))
|
|
return
|
|
|
|
var/datum/strip_menu/strip_menu = LAZYACCESS(strip_menus, source)
|
|
|
|
if (isnull(strip_menu))
|
|
strip_menu = new(source, src)
|
|
LAZYSET(strip_menus, source, strip_menu)
|
|
|
|
INVOKE_ASYNC(strip_menu, TYPE_PROC_REF(/datum/, ui_interact), user)
|
|
return COMPONENT_CANCEL_MOUSEDROP_ONTO
|
|
|
|
/// A representation of an item that can be stripped down
|
|
/datum/strippable_item
|
|
/// The STRIPPABLE_ITEM_* key
|
|
var/key
|
|
|
|
/// Should we give feedback messages?
|
|
var/show_visible_message = TRUE
|
|
|
|
/// Can it be silent?
|
|
var/can_be_silent = FALSE //SKYRAT EDIT ADDITION - THIEVING GLOVES
|
|
|
|
/// Gets the item from the given source.
|
|
/datum/strippable_item/proc/get_item(atom/source)
|
|
|
|
/// Tries to equip the item onto the given source.
|
|
/// Returns TRUE/FALSE depending on if it is allowed.
|
|
/// This should be used for checking if an item CAN be equipped.
|
|
/// It should not perform the equipping itself.
|
|
/datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user)
|
|
if(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, equipping) & COMPONENT_CANT_STRIP)
|
|
return FALSE
|
|
|
|
if (HAS_TRAIT(equipping, TRAIT_NODROP))
|
|
to_chat(user, span_warning("You can't put [equipping] on [source], it's stuck to your hand!"))
|
|
return FALSE
|
|
|
|
if (equipping.item_flags & ABSTRACT)
|
|
return FALSE //I don't know a sane-sounding feedback message for trying to put a slap into someone's hand
|
|
|
|
return TRUE
|
|
|
|
/// Start the equipping process. This is the proc you should yield in.
|
|
/// Returns TRUE/FALSE depending on if it is allowed.
|
|
/datum/strippable_item/proc/start_equip(atom/source, obj/item/equipping, mob/user)
|
|
|
|
equipping.item_start_equip(source, equipping, user, show_visible_message)
|
|
return TRUE
|
|
|
|
/// The proc that places the item on the source. This should not yield.
|
|
/datum/strippable_item/proc/finish_equip(atom/source, obj/item/equipping, mob/user)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
|
|
/// Tries to unequip the item from the given source.
|
|
/// Returns TRUE/FALSE depending on if it is allowed.
|
|
/// This should be used for checking if it CAN be unequipped.
|
|
/// It should not perform the unequipping itself.
|
|
/datum/strippable_item/proc/try_unequip(atom/source, mob/user)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
|
|
var/obj/item/item = get_item(source)
|
|
if (isnull(item))
|
|
return FALSE
|
|
|
|
if (ismob(source))
|
|
if(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, item) & COMPONENT_CANT_STRIP)
|
|
return FALSE
|
|
var/mob/mob_source = source
|
|
if (!item.canStrip(user, mob_source))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/// Start the unequipping process. This is the proc you should yield in.
|
|
/// Returns TRUE/FALSE depending on if it is allowed.
|
|
/datum/strippable_item/proc/start_unequip(atom/source, mob/user)
|
|
var/obj/item/item = get_item(source)
|
|
if (isnull(item))
|
|
return FALSE
|
|
|
|
if (HAS_TRAIT(item, TRAIT_NO_STRIP))
|
|
return FALSE
|
|
|
|
//SKYRAT EDIT CHANGE START - THIEVING GLOVES
|
|
var/is_silent = can_be_silent && HAS_TRAIT(user, TRAIT_STICKY_FINGERS)
|
|
if (!is_silent)
|
|
source.visible_message(
|
|
span_warning("[user] tries to remove [source]'s [item.name]."),
|
|
span_userdanger("[user] tries to remove your [item.name]."),
|
|
blind_message = span_hear("You hear rustling."),
|
|
ignored_mobs = user,
|
|
)
|
|
//SKYRAT EDIT CHANGE END
|
|
|
|
to_chat(user, span_danger("You try to remove [source]'s [item.name]..."))
|
|
user.log_message("is stripping [key_name(source)] of [item].", LOG_ATTACK, color="red", redacted_copy = "is stripping [source] of [item].") // BUBBER EDIT - PUBLIC LOGGING
|
|
source.log_message("is being stripped of [item] by [key_name(user)].", LOG_VICTIM, color="orange", log_globally=FALSE)
|
|
item.add_fingerprint(src)
|
|
|
|
if(ishuman(source) && !is_silent) //SKYRAT EDIT ADDITION - THIEVING GLOVES ORIGINAL if(ishuman(source))
|
|
var/mob/living/carbon/human/victim_human = source
|
|
if(victim_human.key && !victim_human.client) // AKA braindead
|
|
if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
|
|
var/list/new_entry = list(list(user.name, "tried unequipping your [item.name]", world.time))
|
|
LAZYADD(victim_human.afk_thefts, new_entry)
|
|
|
|
else if(victim_human.is_blind())
|
|
to_chat(source, span_userdanger("You feel someone fumble with your belongings."))
|
|
|
|
return TRUE
|
|
|
|
/// The proc that unequips the item from the source. This should not yield.
|
|
/datum/strippable_item/proc/finish_unequip(atom/source, mob/user)
|
|
|
|
/// Returns a STRIPPABLE_OBSCURING_* define to report on whether or not this is obscured.
|
|
/datum/strippable_item/proc/get_obscuring(atom/source)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
return STRIPPABLE_OBSCURING_NONE
|
|
|
|
/**
|
|
* Returns a list of alternate actions that can be performed on this strippable_item.
|
|
* All string keys in the list must be inside tgui\packages\tgui\interfaces\StripMenu.tsx
|
|
* You can also return null if there are no alternate actions.
|
|
*/
|
|
/datum/strippable_item/proc/get_alternate_actions(atom/source, mob/user)
|
|
RETURN_TYPE(/list)
|
|
return null
|
|
|
|
/**
|
|
* Performs an alternate action on this strippable_item.
|
|
* - source: The source of the action.
|
|
* - user: The user performing the action.
|
|
* - action_key: The key of the alternate action to perform.
|
|
* Returns FALSE if unable to perform the action; whether it be due to the signal or some other factor.
|
|
*/
|
|
/datum/strippable_item/proc/perform_alternate_action(atom/source, mob/user, action_key)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(SEND_SIGNAL(user, COMSIG_TRY_ALT_ACTION, source, action_key) & COMPONENT_CANT_ALT_ACTION)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Returns whether or not this item should show.
|
|
/datum/strippable_item/proc/should_show(atom/source, mob/user)
|
|
return TRUE
|
|
|
|
/// A preset for equipping items onto mob slots
|
|
/datum/strippable_item/mob_item_slot
|
|
/// The ITEM_SLOT_* to equip to.
|
|
var/item_slot
|
|
|
|
/datum/strippable_item/mob_item_slot/get_item(atom/source)
|
|
if (!ismob(source))
|
|
return null
|
|
|
|
var/mob/mob_source = source
|
|
return mob_source.get_item_by_slot(item_slot)
|
|
|
|
/datum/strippable_item/mob_item_slot/try_equip(atom/source, obj/item/equipping, mob/user)
|
|
. = ..()
|
|
if (!.)
|
|
return
|
|
|
|
if (!ismob(source))
|
|
return FALSE
|
|
|
|
if (!equipping.mob_can_equip(source, item_slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE))
|
|
to_chat(user, span_warning("\The [equipping] doesn't fit in that place!"))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/datum/strippable_item/mob_item_slot/start_equip(atom/source, obj/item/equipping, mob/user)
|
|
. = ..()
|
|
if (!.)
|
|
return
|
|
|
|
if (!ismob(source))
|
|
return FALSE
|
|
|
|
if (!do_after(user, get_equip_delay(equipping), source))
|
|
return FALSE
|
|
|
|
if (!equipping.mob_can_equip(source, item_slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE))
|
|
return FALSE
|
|
|
|
if (!user.temporarilyRemoveItemFromInventory(equipping))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/datum/strippable_item/mob_item_slot/finish_equip(atom/source, obj/item/equipping, mob/user)
|
|
if (!ismob(source))
|
|
return FALSE
|
|
|
|
var/mob/mob_source = source
|
|
mob_source.equip_to_slot(equipping, item_slot)
|
|
|
|
return finish_equip_mob(equipping, source, user)
|
|
|
|
/datum/strippable_item/mob_item_slot/get_obscuring(atom/source)
|
|
if (iscarbon(source))
|
|
var/mob/living/carbon/carbon_source = source
|
|
return (carbon_source.check_obscured_slots() & item_slot) \
|
|
? STRIPPABLE_OBSCURING_COMPLETELY \
|
|
: STRIPPABLE_OBSCURING_NONE
|
|
|
|
return FALSE
|
|
|
|
/datum/strippable_item/mob_item_slot/start_unequip(atom/source, mob/user)
|
|
. = ..()
|
|
if (!.)
|
|
return
|
|
|
|
return start_unequip_mob(get_item(source), source, user)
|
|
|
|
/datum/strippable_item/mob_item_slot/finish_unequip(atom/source, mob/user)
|
|
var/obj/item/item = get_item(source)
|
|
if (isnull(item))
|
|
return FALSE
|
|
|
|
if (!ismob(source))
|
|
return FALSE
|
|
|
|
return finish_unequip_mob(item, source, user)
|
|
|
|
/// Returns the delay of equipping this item to a mob
|
|
/datum/strippable_item/mob_item_slot/proc/get_equip_delay(obj/item/equipping)
|
|
return equipping.equip_delay_other
|
|
|
|
/// A utility function for `/datum/strippable_item`s to finish equipping an item to a mob.
|
|
/proc/finish_equip_mob(obj/item/item, mob/source, mob/user)
|
|
user.log_message("has put [item] on [key_name(source)].", LOG_ATTACK, color="red", redacted_copy = "has put [item] on [source].") // BUBBER EDIT- PUBLIC LOGS
|
|
source.log_message("had [item] put on them by [key_name(user)].", LOG_VICTIM, color="orange", log_globally=FALSE)
|
|
|
|
/// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob.
|
|
/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay, hidden = FALSE)
|
|
if (!do_after(user, (strip_delay || item.strip_delay) * (HAS_TRAIT(user, TRAIT_STICKY_FINGERS) ? THIEVING_GLOVES_STRIP_SLOWDOWN : NORMAL_STRIP_SLOWDOWN), source, interaction_key = REF(item), hidden = hidden)) // SKYRAT EDIT CHANGE - ORIGINAL: if (!do_after(user, strip_delay || item.strip_delay, source, interaction_key = REF(item), hidden = hidden))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/// A utility function for `/datum/strippable_item`s to finish unequipping an item from a mob.
|
|
/proc/finish_unequip_mob(obj/item/item, mob/source, mob/user)
|
|
if (!item.doStrip(user, source))
|
|
return FALSE
|
|
|
|
user.log_message("has stripped [key_name(source)] of [item].", LOG_ATTACK, color="red", redacted_copy = "has stripped [source] of [item])") // BUBBER EDIT - PUBLIC LOGS
|
|
source.log_message("has been stripped of [item] by [key_name(user)].", LOG_VICTIM, color="orange", log_globally=FALSE)
|
|
|
|
// Updates speed in case stripped speed affecting item
|
|
source.update_equipment_speed_mods()
|
|
|
|
/// A representation of the stripping UI
|
|
/datum/strip_menu
|
|
/// The owner who has the element /datum/element/strippable
|
|
var/atom/movable/owner
|
|
|
|
/// The strippable element itself
|
|
var/datum/element/strippable/strippable
|
|
|
|
/// A lazy list of user mobs to a list of strip menu keys that they're interacting with
|
|
var/list/interactions
|
|
|
|
/datum/strip_menu/New(atom/movable/owner, datum/element/strippable/strippable)
|
|
. = ..()
|
|
src.owner = owner
|
|
src.strippable = strippable
|
|
|
|
/datum/strip_menu/Destroy()
|
|
owner = null
|
|
strippable = null
|
|
|
|
return ..()
|
|
|
|
/datum/strip_menu/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if (!ui)
|
|
ui = new(user, src, "StripMenu")
|
|
ui.open()
|
|
|
|
/datum/strip_menu/ui_assets(mob/user)
|
|
return list(
|
|
get_asset_datum(/datum/asset/simple/inventory),
|
|
)
|
|
|
|
/datum/strip_menu/ui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
var/list/items = list()
|
|
|
|
for (var/strippable_key in strippable.items)
|
|
var/datum/strippable_item/item_data = strippable.items[strippable_key]
|
|
|
|
if (!item_data.should_show(owner, user))
|
|
continue
|
|
|
|
var/list/result
|
|
|
|
if(strippable_key in LAZYACCESS(interactions, user))
|
|
LAZYSET(result, "interacting", TRUE)
|
|
|
|
var/obscuring = item_data.get_obscuring(owner)
|
|
if (obscuring != STRIPPABLE_OBSCURING_NONE)
|
|
LAZYSET(result, "obscured", obscuring)
|
|
items[strippable_key] = result
|
|
continue
|
|
|
|
var/obj/item/item = item_data.get_item(owner)
|
|
if (isnull(item) || (HAS_TRAIT(item, TRAIT_NO_STRIP) || (item.item_flags & EXAMINE_SKIP)))
|
|
items[strippable_key] = result
|
|
continue
|
|
|
|
LAZYINITLIST(result)
|
|
|
|
result["icon"] = icon2base64(icon(item.icon, item.icon_state))
|
|
result["name"] = item.name
|
|
result["alternate"] = item_data.get_alternate_actions(owner, user)
|
|
var/static/list/already_cried = list()
|
|
if(length(result["alternate"]) > 2 && !(type in already_cried))
|
|
stack_trace("Too many alternate actions for [type]! Only two are supported at the moment! This will look bad!")
|
|
already_cried += type
|
|
|
|
items[strippable_key] = result
|
|
|
|
data["items"] = items
|
|
|
|
// While most `\the`s are implicit, this one is not.
|
|
// In this case, `\The` would otherwise be used.
|
|
// This doesn't match with what it's used for, which is to say "Stripping the alien drone",
|
|
// as opposed to "Stripping The alien drone".
|
|
// Human names will still show without "the", as they are proper nouns.
|
|
data["name"] = "\the [owner]"
|
|
|
|
return data
|
|
|
|
/datum/strip_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if (.)
|
|
return
|
|
|
|
. = TRUE
|
|
|
|
var/mob/user = usr
|
|
|
|
switch (action)
|
|
if ("use")
|
|
var/key = params["key"]
|
|
var/datum/strippable_item/strippable_item = strippable.items[key]
|
|
|
|
if (isnull(strippable_item))
|
|
return
|
|
|
|
if (!strippable_item.should_show(owner, user))
|
|
return
|
|
|
|
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
|
|
return
|
|
|
|
var/item = strippable_item.get_item(owner)
|
|
if (isnull(item))
|
|
var/obj/item/held_item = user.get_active_held_item()
|
|
if (isnull(held_item))
|
|
return
|
|
|
|
if (strippable_item.try_equip(owner, held_item, user))
|
|
LAZYORASSOCLIST(interactions, user, key)
|
|
|
|
// Yielding call
|
|
var/should_finish = strippable_item.start_equip(owner, held_item, user)
|
|
|
|
LAZYREMOVEASSOC(interactions, user, key)
|
|
|
|
if (!should_finish)
|
|
return
|
|
|
|
if (QDELETED(src) || QDELETED(owner))
|
|
return
|
|
|
|
// They equipped an item in the meantime
|
|
if (!isnull(strippable_item.get_item(owner)))
|
|
user.put_in_hands(held_item)
|
|
return
|
|
|
|
if (!user.Adjacent(owner))
|
|
user.put_in_hands(held_item)
|
|
return
|
|
|
|
strippable_item.finish_equip(owner, held_item, user)
|
|
else if (strippable_item.try_unequip(owner, user))
|
|
LAZYORASSOCLIST(interactions, user, key)
|
|
|
|
var/should_unequip = strippable_item.start_unequip(owner, user)
|
|
|
|
LAZYREMOVEASSOC(interactions, user, key)
|
|
|
|
// Yielding call
|
|
if (!should_unequip)
|
|
return
|
|
|
|
if (QDELETED(src) || QDELETED(owner))
|
|
return
|
|
|
|
// They changed the item in the meantime
|
|
if (strippable_item.get_item(owner) != item)
|
|
return
|
|
|
|
if (!user.Adjacent(owner))
|
|
return
|
|
|
|
strippable_item.finish_unequip(owner, user)
|
|
if ("alt")
|
|
var/key = params["key"]
|
|
var/alt_action = params["alternate_action"]
|
|
var/datum/strippable_item/strippable_item = strippable.items[key]
|
|
|
|
if (isnull(strippable_item))
|
|
return
|
|
|
|
if (!strippable_item.should_show(owner, user))
|
|
return
|
|
|
|
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
|
|
return
|
|
|
|
var/item = strippable_item.get_item(owner)
|
|
if (isnull(item))
|
|
return
|
|
|
|
if (!(alt_action in strippable_item.get_alternate_actions(owner, user)))
|
|
return
|
|
|
|
LAZYORASSOCLIST(interactions, user, key)
|
|
|
|
// Potentially yielding
|
|
strippable_item.perform_alternate_action(owner, user, alt_action)
|
|
|
|
LAZYREMOVEASSOC(interactions, user, key)
|
|
|
|
/datum/strip_menu/ui_host(mob/user)
|
|
return owner
|
|
|
|
/datum/strip_menu/ui_state(mob/user)
|
|
return GLOB.always_state
|
|
|
|
/datum/strip_menu/ui_status(mob/user, datum/ui_state/state)
|
|
return min(
|
|
ui_status_only_living(user, owner),
|
|
ui_status_user_has_free_hands(user, owner),
|
|
ui_status_user_is_adjacent(user, owner, allow_tk = FALSE),
|
|
HAS_TRAIT(user, TRAIT_CAN_STRIP) ? UI_INTERACTIVE : UI_UPDATE,
|
|
max(
|
|
ui_status_user_is_conscious_and_lying_down(user),
|
|
ui_status_user_is_abled(user, owner),
|
|
),
|
|
)
|
|
|
|
/// Creates an assoc list of keys to /datum/strippable_item
|
|
/proc/create_strippable_list(types)
|
|
var/list/strippable_items = list()
|
|
|
|
for (var/strippable_type in types)
|
|
var/datum/strippable_item/strippable_item = new strippable_type
|
|
strippable_items[strippable_item.key] = strippable_item
|
|
|
|
return strippable_items
|