Skip to content

Commit c979f19

Browse files
authored
Pickpocketing Backpacks (#1146)
* pick pocketing * OVER-REACHING RENAME * make pickpocket code better * fix * qol * idk how this can happen but just in case * fuck * rangedreachcheck * unzip backpack * add controls text * fix
1 parent d26ce21 commit c979f19

File tree

36 files changed

+196
-62
lines changed

36 files changed

+196
-62
lines changed

code/__DEFINES/flags.dm

+9-7
Original file line numberDiff line numberDiff line change
@@ -282,19 +282,21 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
282282

283283
// timed_action_flags parameter for `/proc/do_after_mob`, `/proc/do_mob` and `/proc/do_after`
284284
/// Can do the action even if mob moves location
285-
#define IGNORE_USER_LOC_CHANGE (1<<0)
285+
#define DO_IGNORE_USER_LOC_CHANGE (1<<0)
286286
/// Can do the action even if the target moves location
287-
#define IGNORE_TARGET_LOC_CHANGE (1<<1)
287+
#define DO_IGNORE_TARGET_LOC_CHANGE (1<<1)
288288
/// Can do the action even if the item is no longer being held
289-
#define IGNORE_HELD_ITEM (1<<2)
289+
#define DO_IGNORE_HELD_ITEM (1<<2)
290290
/// Can do the action even if the mob is incapacitated (ex. handcuffed)
291-
#define IGNORE_INCAPACITATED (1<<3)
291+
#define DO_IGNORE_INCAPACITATED (1<<3)
292292
/// Used to prevent important slowdowns from being abused by drugs like kronkaine
293-
#define IGNORE_SLOWDOWNS (1<<4)
293+
#define DO_IGNORE_SLOWDOWNS (1<<4)
294+
/// Used to prevent rotation by the user.
295+
#define DO_RESTRICT_USER_DIR_CHANGE (1<<5)
294296
/// If the user has their next_move value changed (usually by clicking), fail.
295-
#define DO_RESTRICT_CLICKING (1<<5)
297+
#define DO_RESTRICT_CLICKING (1<<6)
296298
/// Shown to all mobs not just the user
297-
#define DO_PUBLIC (1<<6)
299+
#define DO_PUBLIC (1<<7)
298300

299301

300302

code/__DEFINES/traits.dm

+2
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
617617
#define TRAIT_HIDES_VOICE "hides_voice"
618618
/// Wearing this item in the mask slot will make your voice your current ID, or unknown
619619
#define TRAIT_REPLACES_VOICE "replaces_voice"
620+
/// Pickpocketing this item takes no time.
621+
#define TRAIT_INSTANT_PICKPOCKET "instant_pickpocket"
620622

621623
//quirk traits
622624
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"

code/_onclick/click.dm

+18
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,22 @@
256256
/atom/movable/IsContainedAtomAccessible(atom/contained, atom/movable/user)
257257
return !!atom_storage
258258

259+
/mob/living/IsContainedAtomAccessible(atom/contained, atom/movable/user)
260+
. = ..()
261+
if(.)
262+
return
263+
264+
if(!isliving(user))
265+
return
266+
267+
if(!isitem(contained))
268+
return
269+
270+
var/mob/living/living_user = user
271+
var/obj/item/I = contained
272+
if(I.can_pickpocket(user))
273+
return I.atom_storage == living_user.active_storage
274+
259275
/atom/movable/proc/DirectAccess()
260276
return list(src, loc)
261277

@@ -390,6 +406,8 @@
390406
if(!can_interact(user))
391407
return FALSE
392408

409+
return TRUE
410+
393411
/mob/living/CtrlClick(mob/user, list/params)
394412
if(!isliving(user) || !IsReachableBy(user) || user.incapacitated())
395413
return ..()

code/datums/components/cult_ritual_item.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@
311311
scribe_mod *= 0.5
312312

313313
SEND_SOUND(cultist, sound('sound/weapons/slice.ogg', 0, 1, 10))
314-
if(!do_after(cultist, get_turf(cultist), scribe_mod, timed_action_flags = IGNORE_SLOWDOWNS))
314+
if(!do_after(cultist, get_turf(cultist), scribe_mod, timed_action_flags = DO_IGNORE_SLOWDOWNS))
315315
cleanup_shields()
316316
return FALSE
317317
if(!can_scribe_rune(tool, cultist))

code/datums/components/food/edible.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ Behavior that's still missing from this component that original food items had t
310310
var/fullness = eater.get_fullness() + 10 //The theoretical fullness of the person eating if they were to eat this
311311

312312
if(eater == feeder)//If you're eating it yourself.
313-
if(eat_time && !do_after(feeder, eater, eat_time, timed_action_flags = food_flags & FOOD_FINGER_FOOD ? IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE : NONE)) //Gotta pass the minimal eat time
313+
if(eat_time && !do_after(feeder, eater, eat_time, timed_action_flags = food_flags & FOOD_FINGER_FOOD ? DO_IGNORE_USER_LOC_CHANGE | DO_IGNORE_TARGET_LOC_CHANGE : NONE)) //Gotta pass the minimal eat time
314314
return
315315
if(IsFoodGone(owner, feeder))
316316
return

code/datums/elements/strippable.dm

+3-3
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,13 @@
182182
/// Returns the ID of this item's strippable action.
183183
/// Return `null` if there is no alternate action.
184184
/// Any return value of this must be in StripMenu.
185-
/datum/strippable_item/proc/get_alternate_action(atom/source, mob/user)
185+
/datum/strippable_item/proc/get_alternate_action(atom/source, mob/user, action)
186186
return null
187187

188188
/// Performs an alternative action on this strippable_item.
189189
/// `has_alternate_action` needs to be TRUE.
190190
/// Returns FALSE if blocked by signal, TRUE otherwise.
191-
/datum/strippable_item/proc/alternate_action(atom/source, mob/user)
191+
/datum/strippable_item/proc/alternate_action(atom/source, mob/user, action)
192192
SHOULD_CALL_PARENT(TRUE)
193193
if(SEND_SIGNAL(user, COMSIG_TRY_ALT_ACTION, source) & COMPONENT_CANT_ALT_ACTION)
194194
return FALSE
@@ -483,7 +483,7 @@
483483
LAZYORASSOCLIST(interactions, user, key)
484484

485485
// Potentially yielding
486-
strippable_item.alternate_action(owner, user)
486+
strippable_item.alternate_action(owner, user, params["action"])
487487

488488
LAZYREMOVEASSOC(interactions, user, key)
489489

code/datums/storage/storage.dm

+8-5
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(mass_empty))
164164
RegisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_ATTACK_HAND_SECONDARY), PROC_REF(open_storage_on_signal))
165165
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY_SECONDARY, PROC_REF(open_storage_attackby_secondary))
166-
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(close_distance))
166+
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(update_viewability))
167167
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(update_actions))
168168

169169
/datum/storage/proc/on_deconstruct()
@@ -769,6 +769,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
769769

770770
remove_all(dump_loc)
771771

772+
/datum/storage/proc/is_reachable(mob/user)
773+
return parent.IsReachableBy(user)
774+
772775
/// Signal handler for whenever something gets mouse-dropped onto us.
773776
/datum/storage/proc/on_mousedropped_onto(datum/source, obj/item/dropping, mob/user)
774777
SIGNAL_HANDLER
@@ -924,15 +927,15 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
924927
return COMPONENT_NO_AFTERATTACK
925928

926929
/// Opens the storage to the mob, showing them the contents to their UI.
927-
/datum/storage/proc/open_storage(mob/to_show, performing_quickdraw)
930+
/datum/storage/proc/open_storage(mob/to_show, performing_quickdraw, skip_canreach = FALSE)
928931
if(isobserver(to_show))
929932
if(to_show.active_storage == src)
930933
hide_contents(to_show)
931934
else
932935
show_contents(to_show)
933936
return FALSE
934937

935-
if(!can_be_reached_by(to_show))
938+
if(!skip_canreach && !can_be_reached_by(to_show))
936939
to_chat(to_show, span_warning("You cannot reach [parent]."))
937940
return FALSE
938941

@@ -980,8 +983,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
980983
to_chat(toshow, span_notice("You fumble for [toremove] and it falls on the floor."))
981984
return TRUE
982985

983-
/// Signal handler for whenever a mob walks away with us, close if they can't reach us.
984-
/datum/storage/proc/close_distance(datum/source)
986+
/// Close the storage for people who can no longer see it.
987+
/datum/storage/proc/update_viewability(datum/source)
985988
SIGNAL_HANDLER
986989

987990
for(var/mob/user in can_see_contents())

code/game/atoms.dm

+5
Original file line numberDiff line numberDiff line change
@@ -1164,8 +1164,13 @@
11641164
*/
11651165
/atom/proc/setDir(newdir)
11661166
SHOULD_CALL_PARENT(TRUE)
1167+
if(newdir == dir)
1168+
return null
1169+
1170+
. = dir
11671171
SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
11681172
dir = newdir
1173+
return .
11691174

11701175
/**
11711176
* Called when the atom log's in or out

code/game/objects/items.dm

+4
Original file line numberDiff line numberDiff line change
@@ -1894,3 +1894,7 @@ DEFINE_INTERACTABLE(/obj/item)
18941894
center["x"] = text2num(center["x"])
18951895
center["y"] = text2num(center["y"])
18961896
return center
1897+
1898+
/// Returns TRUE if the passed mob can interact with this item's storage via pickpocketing.
1899+
/obj/item/proc/can_pickpocket(mob/living/user)
1900+
return FALSE

code/game/objects/items/bodybag.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
user.last_special = world.time + CLICK_CD_BREAKOUT
8181
to_chat(user, span_notice("You claw at the fabric of [src], trying to tear it open..."))
8282
to_chat(loc, span_warning("Someone starts trying to break free of [src]!"))
83-
if(!do_after(user, src, 12 SECONDS, timed_action_flags = (IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)))
83+
if(!do_after(user, src, 12 SECONDS, timed_action_flags = (DO_IGNORE_TARGET_LOC_CHANGE|DO_IGNORE_HELD_ITEM)))
8484
return
8585
// you are still in the bag? time to go unless you KO'd, honey!
8686
// if they escape during this time and you rebag them the timer is still clocking down and does NOT reset so they can very easily get out.

code/game/objects/items/cardboard_cutouts.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
var/new_appearance = show_radial_menu(user, src, possible_appearances, custom_check = CALLBACK(src, PROC_REF(check_menu), user, crayon), radius = 36, require_near = TRUE)
104104
if(!new_appearance)
105105
return FALSE
106-
if(!do_after(user, src, 1 SECONDS, timed_action_flags = IGNORE_HELD_ITEM))
106+
if(!do_after(user, src, 1 SECONDS, timed_action_flags = DO_IGNORE_HELD_ITEM))
107107
return FALSE
108108
if(!check_menu(user, crayon))
109109
return FALSE

code/game/objects/items/handcuffs.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
to_chat(C, span_userdanger("You feel someone grab your wrists, the cold metal of [name] starting to dig into your skin!"))
6868
playsound(loc, cuffsound, 30, TRUE, -2)
6969
log_combat(user, C, "attempted to handcuff")
70-
if(do_after(user, C, handcuff_time, timed_action_flags = IGNORE_SLOWDOWNS|DO_PUBLIC, display = src) && C.canBeHandcuffed())
70+
if(do_after(user, C, handcuff_time, timed_action_flags = DO_IGNORE_SLOWDOWNS|DO_PUBLIC, display = src) && C.canBeHandcuffed())
7171
if(!apply_cuffs(C, user, iscyborg(user)))
7272
to_chat(user, span_warning("You fail to handcuff [C]!"))
7373
log_combat(user, C, "failed to handcuff")

code/game/objects/items/storage/backpack.dm

+43
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,54 @@
3232
equip_delay_other = EQUIP_DELAY_BACK * 1.5
3333
strip_delay = EQUIP_DELAY_BACK * 1.5
3434

35+
var/zipper_open = FALSE
36+
3537
/obj/item/storage/backpack/Initialize()
3638
. = ..()
3739
atom_storage.max_slots = 21
3840
atom_storage.max_total_storage = 21
3941

42+
/obj/item/storage/backpack/examine(mob/user)
43+
. = ..()
44+
. += span_info("The zipper is [zipper_open ? "open" : "closed"].")
45+
46+
/obj/item/storage/backpack/get_controls_info()
47+
. = ..()
48+
. += "Control Click (while holding) - Toggle zipper."
49+
50+
/obj/item/storage/backpack/can_pickpocket(mob/living/user)
51+
var/mob/wearer = loc
52+
if(!ismob(wearer))
53+
return FALSE
54+
55+
if(wearer.get_item_by_slot(ITEM_SLOT_BACK) != src)
56+
return FALSE
57+
58+
if(!(REVERSE_DIR(wearer.dir) & get_dir(wearer, user)))
59+
return
60+
61+
return wearer.IsReachableBy(user)
62+
63+
/obj/item/storage/backpack/CtrlClick(mob/user, list/params)
64+
. = ..()
65+
if(!.)
66+
return
67+
68+
if(user != loc)
69+
return
70+
71+
toggle_zipper(user)
72+
73+
/obj/item/storage/backpack/proc/toggle_zipper(mob/user)
74+
zipper_open = !zipper_open
75+
user?.changeNext_move(CLICK_CD_RAPID)
76+
user?.visible_message(span_notice("[user] [zipper_open ? "unzips" : "zips"] [user.p_their()] [name]."))
77+
78+
if(zipper_open)
79+
ADD_TRAIT(src, TRAIT_INSTANT_PICKPOCKET, INNATE_TRAIT)
80+
else
81+
REMOVE_TRAIT(src, TRAIT_INSTANT_PICKPOCKET, INNATE_TRAIT)
82+
4083
/*
4184
* Backpack Types
4285
*/

code/game/objects/structures/watercloset.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
if(open)
6262
GM.visible_message(span_danger("[user] starts to give [GM] a swirlie!"), span_userdanger("[user] starts to give you a swirlie..."))
6363
swirlie = GM
64-
if(do_after(user, src, 3 SECONDS, timed_action_flags = IGNORE_HELD_ITEM))
64+
if(do_after(user, src, 3 SECONDS, timed_action_flags = DO_IGNORE_HELD_ITEM))
6565
GM.visible_message(span_danger("[user] gives [GM] a swirlie!"), span_userdanger("[user] gives you a swirlie!"), span_hear("You hear a toilet flushing."))
6666
if(iscarbon(GM))
6767
var/mob/living/carbon/C = GM

code/modules/antagonists/heretic/items/hunter_rifle.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
animate(reticle, fire_time * 0.5, transform = turn(reticle.transform, 180))
7878

7979
currently_aiming = TRUE
80-
. = do_after(user, fire_time, target, IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(check_fire_callback), target, user))
80+
. = do_after(user, fire_time, target, DO_IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(check_fire_callback), target, user))
8181
currently_aiming = FALSE
8282

8383
animate(reticle, 0.5 SECONDS, alpha = 0)

code/modules/antagonists/heretic/magic/mirror_walk.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363

6464
jaunter.Beam(nearby_reflection, icon_state = "light_beam", time = phase_out_time)
6565
nearby_reflection.visible_message(span_warning("[nearby_reflection] begins to shimmer and shake slightly!"))
66-
if(!do_after(jaunter, phase_out_time, nearby_reflection, IGNORE_USER_LOC_CHANGE|IGNORE_INCAPACITATED))
66+
if(!do_after(jaunter, phase_out_time, nearby_reflection, DO_IGNORE_USER_LOC_CHANGE|DO_IGNORE_INCAPACITATED))
6767
return
6868

6969
playsound(jaunter, 'sound/magic/ethereal_enter.ogg', 50, TRUE, -1)

code/modules/antagonists/revenant/revenant_abilities.dm

+4-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
draining = TRUE
4444
essence_drained += rand(15, 20)
4545
to_chat(src, span_revennotice("You search for the soul of [target]."))
46-
if(do_after(src, target, rand(10, 20), timed_action_flags = IGNORE_HELD_ITEM)) //did they get deleted in that second?
46+
if(do_after(src, target, rand(10, 20), timed_action_flags = DO_IGNORE_HELD_ITEM)) //did they get deleted in that second?
4747
if(target.ckey)
4848
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul burns with intelligence."))
4949
essence_drained += rand(20, 30)
@@ -55,7 +55,7 @@
5555
essence_drained = 5
5656
else
5757
to_chat(src, span_revennotice("[target.p_their(TRUE)] soul is weak and faltering."))
58-
if(do_after(src, target, rand(15, 20), timed_action_flags = IGNORE_HELD_ITEM)) //did they get deleted NOW?
58+
if(do_after(src, target, rand(15, 20), timed_action_flags = DO_IGNORE_HELD_ITEM)) //did they get deleted NOW?
5959
switch(essence_drained)
6060
if(1 to 30)
6161
to_chat(src, span_revennotice("[target] will not yield much essence. Still, every bit counts."))
@@ -65,7 +65,7 @@
6565
to_chat(src, span_revenboldnotice("Such a feast! [target] will yield much essence to you."))
6666
if(90 to INFINITY)
6767
to_chat(src, span_revenbignotice("Ah, the perfect soul. [target] will yield massive amounts of essence to you."))
68-
if(do_after(src, target, rand(15, 25), timed_action_flags = IGNORE_HELD_ITEM)) //how about now
68+
if(do_after(src, target, rand(15, 25), timed_action_flags = DO_IGNORE_HELD_ITEM)) //how about now
6969
if(!target.stat)
7070
to_chat(src, span_revenwarning("[target.p_theyre(TRUE)] now powerful enough to fight off your draining."))
7171
to_chat(target, span_boldannounce("You feel something tugging across your body before subsiding."))
@@ -85,7 +85,7 @@
8585
draining = FALSE
8686
return
8787
var/datum/beam/B = Beam(target,icon_state="drain_life")
88-
if(do_after(src, target, 46, timed_action_flags = IGNORE_HELD_ITEM)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
88+
if(do_after(src, target, 46, timed_action_flags = DO_IGNORE_HELD_ITEM)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
8989
change_essence_amount(essence_drained, FALSE, target)
9090
if(essence_drained <= 90 && target.stat != DEAD && !HAS_TRAIT(target, TRAIT_WEAK_SOUL))
9191
essence_regen_cap += 5

code/modules/codex/codex_atom.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
if(!lore && !mechanics && !antag && !controls)
1313
return FALSE
1414

15-
var/datum/codex_entry/entry = new(name, list(type), _lore_text = lore, _mechanics_text = mechanics, _antag_text = antag, _dynamic = TRUE)
15+
var/datum/codex_entry/entry = new(name, list(type), _lore_text = lore, _mechanics_text = mechanics, _antag_text = antag, _controls_text = controls, _dynamic = TRUE)
1616
return entry
1717

1818

0 commit comments

Comments
 (0)