-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
items.dm
1705 lines (1437 loc) · 68.2 KB
/
items.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// Anything you can pick up and hold.
/obj/item
name = "item"
icon = 'icons/obj/anomaly.dmi'
blocks_emissive = EMISSIVE_BLOCK_GENERIC
burning_particles = /particles/smoke/burning/small
pass_flags_self = PASSITEM
/* !!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
IF YOU ADD MORE ICON CRAP TO THIS
ENSURE YOU ALSO ADD THE NEW VARS TO CHAMELEON ITEM_ACTION'S update_item() PROC (/datum/action/item_action/chameleon/change/proc/update_item())
WASHING MASHINE'S dye_item() PROC (/obj/item/proc/dye_item())
AND ALSO TO THE CHANGELING PROFILE DISGUISE SYSTEMS (/datum/changeling_profile / /datum/antagonist/changeling/proc/create_profile() / /proc/changeling_transform())
!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! */
///icon state for inhand overlays.
var/inhand_icon_state = null
///Icon file for left hand inhand overlays
var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
///Icon file for right inhand overlays
var/righthand_file = 'icons/mob/inhands/items_righthand.dmi'
///Icon file for mob worn overlays.
var/icon/worn_icon
///Icon state for mob worn overlays, if null the normal icon_state will be used.
var/worn_icon_state
///Icon state for the belt overlay, if null the normal icon_state will be used.
var/belt_icon_state
///Forced mob worn layer instead of the standard preferred size.
var/alternate_worn_layer
///The config type to use for greyscaled worn sprites. Both this and greyscale_colors must be assigned to work.
var/greyscale_config_worn
///The config type to use for greyscaled left inhand sprites. Both this and greyscale_colors must be assigned to work.
var/greyscale_config_inhand_left
///The config type to use for greyscaled right inhand sprites. Both this and greyscale_colors must be assigned to work.
var/greyscale_config_inhand_right
///The config type to use for greyscaled belt overlays. Both this and greyscale_colors must be assigned to work.
var/greyscale_config_belt
/* !!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!
IF YOU ADD MORE ICON CRAP TO THIS
ENSURE YOU ALSO ADD THE NEW VARS TO CHAMELEON ITEM_ACTION'S update_item() PROC (/datum/action/item_action/chameleon/change/proc/update_item())
WASHING MASHINE'S dye_item() PROC (/obj/item/proc/dye_item())
AND ALSO TO THE CHANGELING PROFILE DISGUISE SYSTEMS (/datum/changeling_profile / /datum/antagonist/changeling/proc/create_profile() / /proc/changeling_transform())
!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! */
///Dimensions of the icon file used when this item is worn, eg: hats.dmi (32x32 sprite, 64x64 sprite, etc.). Allows inhands/worn sprites to be of any size, but still centered on a mob properly
var/worn_x_dimension = 32
///Dimensions of the icon file used when this item is worn, eg: hats.dmi (32x32 sprite, 64x64 sprite, etc.). Allows inhands/worn sprites to be of any size, but still centered on a mob properly
var/worn_y_dimension = 32
///Same as for [worn_x_dimension][/obj/item/var/worn_x_dimension] but for inhands, uses the lefthand_ and righthand_ file vars
var/inhand_x_dimension = 32
///Same as for [worn_y_dimension][/obj/item/var/worn_y_dimension] but for inhands, uses the lefthand_ and righthand_ file vars
var/inhand_y_dimension = 32
/// Worn overlay will be shifted by this along y axis
var/worn_y_offset = 0
max_integrity = 200
obj_flags = NONE
///Item flags for the item
var/item_flags = NONE
///Sound played when you hit something with the item
var/hitsound
///Played when the item is used, for example tools
var/usesound
///Used when yate into a mob
var/mob_throw_hit_sound
///Sound used when equipping the item into a valid slot
var/equip_sound
///Sound uses when picking the item up (into your hands)
var/pickup_sound
///Sound uses when dropping the item, or when its thrown.
var/drop_sound
///Whether or not we use stealthy audio levels for this item's attack sounds
var/stealthy_audio = FALSE
///Sound which is produced when blocking an attack
var/block_sound
///How large is the object, used for stuff like whether it can fit in backpacks or not
var/w_class = WEIGHT_CLASS_NORMAL
///This is used to determine on which slots an item can fit.
var/slot_flags = 0
pass_flags = PASSTABLE
pressure_resistance = 4
/// This var exists as a weird proxy "owner" ref
/// It's used in a few places. Stop using it, and optimially replace all uses please
var/obj/item/master = null
///Price of an item in a vending machine, overriding the base vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
var/custom_price
///Price of an item in a vending machine, overriding the premium vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
var/custom_premium_price
///Whether spessmen with an ID with an age below AGE_MINOR (20 by default) can buy this item
var/age_restricted = FALSE
///flags which determine which body parts are protected from heat. [See here][HEAD]
var/heat_protection = 0
///flags which determine which body parts are protected from cold. [See here][HEAD]
var/cold_protection = 0
///Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags
var/max_heat_protection_temperature
///Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags
var/min_cold_protection_temperature
///list of /datum/action's that this item has.
var/list/datum/action/actions
///list of paths of action datums to give to the item on New().
var/list/actions_types
//Since any item can now be a piece of clothing, this has to be put here so all items share it.
///This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc.
var/flags_inv
///you can see someone's mask through their transparent visor, but you can't reach it
var/transparent_protection = NONE
///flags for what should be done when you click on the item, default is picking it up
var/interaction_flags_item = INTERACT_ITEM_ATTACK_HAND_PICKUP
///What body parts are covered by the clothing when you wear it
var/body_parts_covered = 0
/// for electrical admittance/conductance (electrocution checks and shit)
var/siemens_coefficient = 1
/// How much clothing is slowing you down. Negative values speeds you up
var/slowdown = 0
///percentage of armour effectiveness to remove
var/armour_penetration = 0
///Whether or not our object is easily hindered by the presence of armor
var/weak_against_armour = FALSE
/// The click cooldown given after attacking. Lower numbers means faster attacks
var/attack_speed = CLICK_CD_MELEE
/// The click cooldown on secondary attacks. Lower numbers mean faster attacks. Will use attack_speed if undefined.
var/secondary_attack_speed
///In deciseconds, how long an item takes to equip; counts only for normal clothing slots, not pockets etc.
var/equip_delay_self = 0
///In deciseconds, how long an item takes to put on another person
var/equip_delay_other = 20
///In deciseconds, how long an item takes to remove from another person
var/strip_delay = 40
///How long it takes to resist out of the item (cuffs and such)
var/breakouttime = 0
///Used in [atom/proc/attackby] to say how something was attacked `"[x] has been [z.attack_verb] by [y] with [z]"`
var/list/attack_verb_continuous
var/list/attack_verb_simple
///list() of species types, if a species cannot put items in a certain slot, but species type is in list, it will be able to wear that item
var/list/species_exception = null
///This is a bitfield that defines what variations exist for bodyparts like Digi legs. See: code\_DEFINES\inventory.dm
var/supports_variations_flags = NONE
///A weakref to the mob who threw the item
var/datum/weakref/thrownby = null //I cannot verbally describe how much I hate this var
///Items can by default thrown up to 10 tiles by TK users
tk_throw_range = 10
///the icon to indicate this object is being dragged
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
///Does it embed and if yes, what kind of embed
var/list/embedding
///for flags such as [GLASSESCOVERSEYES]
var/flags_cover = 0
var/heat = 0
///All items with sharpness of SHARP_EDGED or higher will automatically get the butchering component.
var/sharpness = NONE
///How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power
var/tool_behaviour = null
///How fast does the tool work
var/toolspeed = 1
///Chance of blocking incoming attack
var/block_chance = 0
///Effect of blocking
var/block_effect = /obj/effect/temp_visual/block
var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom
///In tiles, how far this weapon can reach; 1 for adjacent, which is default
var/reach = 1
///The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. For default list, see [/mob/proc/equip_to_appropriate_slot]
var/list/slot_equipment_priority = null
///Reference to the datum that determines whether dogs can wear the item: Needs to be in /obj/item because corgis can wear a lot of non-clothing items
var/datum/dog_fashion/dog_fashion = null
//Tooltip vars
///string form of an item's force. Edit this var only to set a custom force string
var/force_string
var/last_force_string_check = 0
var/tip_timer
///Determines who can shoot this
var/trigger_guard = TRIGGER_GUARD_NONE
///Used as the dye color source in the washing machine only (at the moment). Can be a hex color or a key corresponding to a registry entry, see washing_machine.dm
var/dye_color
///Whether the item is unaffected by standard dying.
var/undyeable = FALSE
///What dye registry should be looked at when dying this item; see washing_machine.dm
var/dying_key
///Grinder var:A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only
var/list/grind_results
///A reagent the nutriments are converted into when the item is juiced.
var/datum/reagent/consumable/juice_typepath
/// Used in obj/item/examine to give additional notes on what the weapon does, separate from the predetermined output variables
var/offensive_notes
/// Used in obj/item/examine to determines whether or not to detail an item's statistics even if it does not meet the force requirements
var/override_notes = FALSE
/// Used if we want to have a custom verb text for throwing. "John Spaceman flicks the ciggerate" for example.
var/throw_verb
/// A lazylist used for applying fantasy values, contains the actual modification applied to a variable.
var/list/fantasy_modifications = null
/// Has the item been reskinned?
var/current_skin
///// List of options to reskin.
var/list/unique_reskin
/// Do we apply a click cooldown when resisting this object if it is restraining them?
var/resist_cooldown = CLICK_CD_BREAKOUT
/obj/item/Initialize(mapload)
if(attack_verb_continuous)
attack_verb_continuous = string_list(attack_verb_continuous)
if(attack_verb_simple)
attack_verb_simple = string_list(attack_verb_simple)
if(species_exception)
species_exception = string_list(species_exception)
if(sharpness && force > 5) //give sharp objects butchering functionality, for consistency
AddComponent(/datum/component/butchering, speed = 8 SECONDS * toolspeed)
if(!greyscale_config && greyscale_colors && (greyscale_config_worn || greyscale_config_belt || greyscale_config_inhand_right || greyscale_config_inhand_left))
update_greyscale()
. = ..()
// Handle adding item associated actions
for(var/path in actions_types)
add_item_action(path)
actions_types = null
if(force_string)
item_flags |= FORCE_STRING_OVERRIDE
if(!hitsound)
if(damtype == BURN)
hitsound = 'sound/items/welder.ogg'
if(damtype == BRUTE)
hitsound = SFX_SWING_HIT
add_weapon_description()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_ITEM, src)
if(LAZYLEN(embedding))
updateEmbedding()
if(unique_reskin)
RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
register_context()
/obj/item/Destroy(force)
// This var exists as a weird proxy "owner" ref
// It's used in a few places. Stop using it, and optimially replace all uses please
master = null
if(ismob(loc))
var/mob/m = loc
m.temporarilyRemoveItemFromInventory(src, TRUE)
// Handle cleaning up our actions list
for(var/datum/action/action as anything in actions)
remove_item_action(action)
return ..()
/obj/item/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(!unique_reskin)
return
if(current_skin && !(item_flags & INFINITE_RESKIN))
return
context[SCREENTIP_CONTEXT_ALT_LMB] = "Reskin"
return CONTEXTUAL_SCREENTIP_SET
/// Called when an action associated with our item is deleted
/obj/item/proc/on_action_deleted(datum/source)
SIGNAL_HANDLER
if(!(source in actions))
CRASH("An action ([source.type]) was deleted that was associated with an item ([src]), but was not found in the item's actions list.")
LAZYREMOVE(actions, source)
/// Adds an item action to our list of item actions.
/// Item actions are actions linked to our item, that are granted to mobs who equip us.
/// This also ensures that the actions are properly tracked in the actions list and removed if they're deleted.
/// Can be be passed a typepath of an action or an instance of an action.
/obj/item/proc/add_item_action(action_or_action_type)
var/datum/action/action
if(ispath(action_or_action_type, /datum/action))
action = new action_or_action_type(src)
else if(istype(action_or_action_type, /datum/action))
action = action_or_action_type
else
CRASH("item add_item_action got a type or instance of something that wasn't an action.")
LAZYADD(actions, action)
RegisterSignal(action, COMSIG_QDELETING, PROC_REF(on_action_deleted))
grant_action_to_bearer(action)
return action
/// Grant the action to anyone who has this item equipped to an appropriate slot
/obj/item/proc/grant_action_to_bearer(datum/action/action)
if(!ismob(loc))
return
var/mob/holder = loc
give_item_action(action, holder, holder.get_slot_by_item(src))
/// Removes an instance of an action from our list of item actions.
/obj/item/proc/remove_item_action(datum/action/action)
if(!action)
return
UnregisterSignal(action, COMSIG_QDELETING)
LAZYREMOVE(actions, action)
qdel(action)
/// Called if this item is supposed to be a steal objective item objective.
/obj/item/proc/add_stealing_item_objective()
return
/// Adds the weapon_description element, which shows the 'warning label' for especially dangerous objects. Override this for item types with special notes.
/obj/item/proc/add_weapon_description()
AddElement(/datum/element/weapon_description)
/**
* Checks if an item is allowed to be used on an atom/target
* Returns TRUE if allowed.
*
* Args:
* target_self - Whether we will check if we (src) are in target, preventing people from using items on themselves.
* not_inside - Whether target (or target's loc) has to be a turf.
*/
/obj/item/proc/check_allowed_items(atom/target, not_inside = FALSE, target_self = FALSE)
if(!target_self && (src in target))
return FALSE
if(not_inside && !isturf(target.loc) && !isturf(target))
return FALSE
return TRUE
/obj/item/blob_act(obj/structure/blob/B)
if(B && B.loc == loc)
atom_destruction(MELEE)
/**Makes cool stuff happen when you suicide with an item
*
*Outputs a creative message and then return the damagetype done
* Arguments:
* * user: The mob that is suiciding
*/
/obj/item/proc/suicide_act(mob/living/user)
return
/obj/item/set_greyscale(list/colors, new_config, new_worn_config, new_inhand_left, new_inhand_right)
if(new_worn_config)
greyscale_config_worn = new_worn_config
if(new_inhand_left)
greyscale_config_inhand_left = new_inhand_left
if(new_inhand_right)
greyscale_config_inhand_right = new_inhand_right
return ..()
/// Checks if this atom uses the GAGS system and if so updates the worn and inhand icons
/obj/item/update_greyscale()
. = ..()
if(!greyscale_colors)
return
if(greyscale_config_worn)
worn_icon = SSgreyscale.GetColoredIconByType(greyscale_config_worn, greyscale_colors)
if(greyscale_config_inhand_left)
lefthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_left, greyscale_colors)
if(greyscale_config_inhand_right)
righthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_right, greyscale_colors)
/obj/item/verb/move_to_top()
set name = "Move To Top"
set category = "Object"
set src in oview(1)
if(!isturf(loc) || usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED))
return
if(isliving(usr))
var/mob/living/L = usr
if(!(L.mobility_flags & MOBILITY_PICKUP))
return
var/turf/T = loc
abstract_move(null)
forceMove(T)
/obj/item/examine(mob/user) //This might be spammy. Remove?
. = ..()
. += "[gender == PLURAL ? "They are" : "It is"] a [weight_class_to_text(w_class)] item."
if(item_flags & CRUEL_IMPLEMENT)
. += "[src] seems quite practical for particularly <font color='red'>morbid</font> procedures and experiments."
if(resistance_flags & INDESTRUCTIBLE)
. += "[src] seems extremely robust! It'll probably withstand anything that could happen to it!"
else
if(resistance_flags & LAVA_PROOF)
. += "[src] is made of an extremely heat-resistant material, it'd probably be able to withstand lava!"
if(resistance_flags & (ACID_PROOF | UNACIDABLE))
. += "[src] looks pretty robust! It'd probably be able to withstand acid!"
if(resistance_flags & FREEZE_PROOF)
. += "[src] is made of cold-resistant materials."
if(resistance_flags & FIRE_PROOF)
. += "[src] is made of fire-retardant materials."
return
/obj/item/examine_more(mob/user)
. = ..()
if(HAS_TRAIT(user, TRAIT_RESEARCH_SCANNER))
. += research_scan(user)
/obj/item/proc/research_scan(mob/user)
/// Research prospects, including boostable nodes and point values. Deliver to a console to know whether the boosts have already been used.
var/list/research_msg = list("<font color='purple'>Research prospects:</font> ")
///Separator between the items on the list
var/sep = ""
///Nodes that can be boosted
var/list/boostable_nodes = techweb_item_unlock_check(src)
if (boostable_nodes)
for(var/id in boostable_nodes)
var/datum/techweb_node/node = SSresearch.techweb_node_by_id(id)
if(!node)
continue
research_msg += sep
research_msg += node.display_name
sep = ", "
var/list/points = techweb_item_point_check(src)
if (length(points))
sep = ", "
research_msg += techweb_point_display_generic(points)
if (!sep) // nothing was shown
research_msg += "None"
// Extractable materials. Only shows the names, not the amounts.
research_msg += ".<br><font color='purple'>Extractable materials:</font> "
if (length(custom_materials))
sep = ""
for(var/mat in custom_materials)
research_msg += sep
research_msg += CallMaterialName(mat)
sep = ", "
else
research_msg += "None"
research_msg += "."
return research_msg.Join()
/obj/item/interact(mob/user)
add_fingerprint(user)
ui_interact(user)
/obj/item/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
add_fingerprint(usr)
return ..()
/obj/item/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION(VV_HK_ADD_FANTASY_AFFIX, "Add Fantasy Affix")
/obj/item/vv_do_topic(list/href_list)
. = ..()
if(!.)
return
if(href_list[VV_HK_ADD_FANTASY_AFFIX])
if(!check_rights(R_FUN))
return
//gathering all affixes that make sense for this item
var/list/prefixes = list()
var/list/suffixes = list()
for(var/datum/fantasy_affix/affix_choice as anything in subtypesof(/datum/fantasy_affix))
affix_choice = new affix_choice()
if(!affix_choice.validate(src))
qdel(affix_choice)
else
if(affix_choice.placement & AFFIX_PREFIX)
prefixes[affix_choice.name] = affix_choice
else
suffixes[affix_choice.name] = affix_choice
//making it more presentable here
var/list/affixes = list("---PREFIXES---")
affixes.Add(prefixes)
affixes.Add("---SUFFIXES---")
affixes.Add(suffixes)
//admin picks, cleanup the ones we didn't do and handle chosen
var/picked_affix_name = tgui_input_list(usr, "Affix to add to [src]", "Enchant [src]", affixes)
if(isnull(picked_affix_name))
return
if(!affixes[picked_affix_name] || QDELETED(src))
return
var/datum/fantasy_affix/affix = affixes[picked_affix_name]
affixes.Remove(affix)
var/fantasy_quality = 0
if(affix.alignment & AFFIX_GOOD)
fantasy_quality++
else
fantasy_quality--
//name gets changed by the component so i want to store it for feedback later
var/before_name = name
//naming these vars that i'm putting into the fantasy component to make it more readable
var/canFail = FALSE
var/announce = FALSE
//Apply fantasy with affix. failing this should never happen, but if it does it should not be silent.
if(AddComponent(/datum/component/fantasy, fantasy_quality, list(affix), canFail, announce) == COMPONENT_INCOMPATIBLE)
to_chat(usr, span_warning("Fantasy component not compatible with [src]."))
CRASH("fantasy component incompatible with object of type: [type]")
to_chat(usr, span_notice("[before_name] now has [picked_affix_name]!"))
log_admin("[key_name(usr)] has added [picked_affix_name] fantasy affix to [before_name]")
message_admins(span_notice("[key_name(usr)] has added [picked_affix_name] fantasy affix to [before_name]"))
/obj/item/attack_hand(mob/user, list/modifiers)
. = ..()
if(. || !user || anchored)
return
return attempt_pickup(user)
/obj/item/proc/attempt_pickup(mob/user, skip_grav = FALSE)
. = TRUE
if(!(interaction_flags_item & INTERACT_ITEM_ATTACK_HAND_PICKUP)) //See if we're supposed to auto pickup.
return
if(!skip_grav)
//Heavy gravity makes picking up things very slow.
var/grav = user.has_gravity()
if(grav > STANDARD_GRAVITY)
var/grav_power = min(3,grav - STANDARD_GRAVITY)
to_chat(user,span_notice("You start picking up [src]..."))
if(!do_after(user, 30 * grav_power, src))
return
//If the item is in a storage item, take it out
var/outside_storage = !loc.atom_storage
var/turf/storage_turf
if(loc.atom_storage)
//We want the pickup animation to play even if we're moving the item between movables. Unless the mob is not located on a turf.
if(isturf(user.loc))
storage_turf = get_turf(loc)
if(!loc.atom_storage.remove_single(user, src, user, silent = TRUE))
return
if(QDELETED(src)) //moving it out of the storage destroyed it.
return
if(storage_turf)
do_pickup_animation(user, storage_turf)
if(throwing)
throwing.finalize(FALSE)
if(loc == user && outside_storage)
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
return
. = FALSE
pickup(user)
add_fingerprint(user)
if(!user.put_in_active_hand(src, ignore_animation = !outside_storage))
user.dropItemToGround(src)
return TRUE
/obj/item/proc/allow_attack_hand_drop(mob/user)
return TRUE
/obj/item/attack_paw(mob/user, list/modifiers)
. = ..()
if(. || !user || anchored)
return
return attempt_pickup(user)
/obj/item/attack_alien(mob/user, list/modifiers)
var/mob/living/carbon/alien/ayy = user
if(!user.can_hold_items(src))
if(src in ayy.contents) // To stop Aliens having items stuck in their pockets
ayy.dropItemToGround(src)
to_chat(user, span_warning("Your claws aren't capable of such fine manipulation!"))
return
attack_paw(ayy, modifiers)
/obj/item/attack_ai(mob/user)
if(istype(src.loc, /obj/item/robot_model))
//If the item is part of a cyborg module, equip it
if(!iscyborg(user))
return
var/mob/living/silicon/robot/R = user
if(!R.low_power_mode) //can't equip modules with an empty cell.
R.activate_module(src)
R.hud_used.update_robot_modules_display()
// afterattack() and attack() prototypes moved to _onclick/item_attack.dm for consistency
/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, final_block_chance, damage, attack_type, damage_type) & COMPONENT_HIT_REACTION_BLOCK)
return TRUE
if(prob(final_block_chance))
owner.visible_message(span_danger("[owner] blocks [attack_text] with [src]!"))
var/owner_turf = get_turf(owner)
new block_effect(owner_turf, COLOR_YELLOW)
playsound(src, block_sound, BLOCK_SOUND_VOLUME, vary = TRUE)
return TRUE
/**
* Handles someone talking INTO an item
*
* Commonly used by someone holding it and using .r or .l
* Also used by radios
*
* * speaker - the atom that is doing the talking
* * message - the message being spoken
* * channel - the channel the message is being spoken on, only really used for radios
* * spans - the spans of the message
* * language - the language the message is in
* * message_mods - any message mods that should be applied to the message
*
* Return a flag that modifies the original message
*/
/obj/item/proc/talk_into(atom/movable/speaker, message, channel, list/spans, datum/language/language, list/message_mods)
return SEND_SIGNAL(src, COMSIG_ITEM_TALK_INTO, speaker, message, channel, spans, language, message_mods) || (ITALICS|REDUCE_RANGE)
/// Called when a mob drops an item.
/obj/item/proc/dropped(mob/user, silent = FALSE)
SHOULD_CALL_PARENT(TRUE)
// Remove any item actions we temporary gave out.
for(var/datum/action/action_item_has as anything in actions)
action_item_has.Remove(user)
if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
item_flags &= ~IN_INVENTORY
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED, user)
if(!silent)
playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
user?.update_equipment_speed_mods()
/// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
SEND_SIGNAL(user, COMSIG_LIVING_PICKED_UP_ITEM, src)
item_flags |= IN_INVENTORY
/// called when "found" in pockets and storage items. Returns 1 if the search should end.
/obj/item/proc/on_found(mob/finder)
return
/**
* Called after an item is placed in an equipment slot. Runs equipped(), then sends a signal.
* This should be called last or near-to-last, after all other inventory code stuff is handled.
*
* Arguments:
* * user is mob that equipped it
* * slot uses the slot_X defines found in setup.dm for items that can be placed in multiple slots
* * initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
*/
/obj/item/proc/on_equipped(mob/user, slot, initial = FALSE)
SHOULD_NOT_OVERRIDE(TRUE)
equipped(user, slot, initial)
if(SEND_SIGNAL(src, COMSIG_ITEM_POST_EQUIPPED, user, slot) && COMPONENT_EQUIPPED_FAILED)
return FALSE
return TRUE
/**
* To be overwritten to only perform visual tasks;
* this is directly called instead of `equipped` on visual-only features like human dummies equipping outfits.
*
* This separation exists to prevent things like the monkey sentience helmet from
* polling ghosts while it's just being equipped as a visual preview for a dummy.
*/
/obj/item/proc/visual_equipped(mob/user, slot, initial = FALSE)
return
/**
* Called by on_equipped. Don't call this directly, we want the ITEM_POST_EQUIPPED signal to be sent after everything else.
*
* Note that hands count as slots.
*
* Arguments:
* * user is mob that equipped it
* * slot uses the slot_X defines found in setup.dm for items that can be placed in multiple slots
* * initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
*/
/obj/item/proc/equipped(mob/user, slot, initial = FALSE)
SHOULD_CALL_PARENT(TRUE)
PROTECTED_PROC(TRUE)
visual_equipped(user, slot, initial)
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
SEND_SIGNAL(user, COMSIG_MOB_EQUIPPED_ITEM, src, slot)
// Give out actions our item has to people who equip it.
for(var/datum/action/action as anything in actions)
give_item_action(action, user, slot)
item_flags |= IN_INVENTORY
if(!initial)
if(equip_sound && (slot_flags & slot))
playsound(src, equip_sound, EQUIP_SOUND_VOLUME, TRUE, ignore_walls = FALSE)
else if(slot & ITEM_SLOT_HANDS)
playsound(src, pickup_sound, PICKUP_SOUND_VOLUME, ignore_walls = FALSE)
user.update_equipment_speed_mods()
/// Gives one of our item actions to a mob, when equipped to a certain slot
/obj/item/proc/give_item_action(datum/action/action, mob/to_who, slot)
// Some items only give their actions buttons when in a specific slot.
if(!item_action_slot_check(slot, to_who, action) || SEND_SIGNAL(src, COMSIG_ITEM_UI_ACTION_SLOT_CHECKED, to_who, action, slot) & COMPONENT_ITEM_ACTION_SLOT_INVALID)
// There is a chance we still have our item action currently,
// and are moving it from a "valid slot" to an "invalid slot".
// So call Remove() here regardless, even if excessive.
action.Remove(to_who)
return
action.Grant(to_who)
/// Sometimes we only want to grant the item's action if it's equipped in a specific slot.
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/action)
if(slot & (ITEM_SLOT_BACKPACK|ITEM_SLOT_LEGCUFFED)) //these aren't true slots, so avoid granting actions there
return FALSE
return TRUE
/**
*the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't.
*if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise.
*If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen.
* Arguments:
* * disable_warning to TRUE if you wish it to not give you text outputs.
* * slot is the slot we are trying to equip to
* * bypass_equip_delay_self for whether we want to bypass the equip delay
* * ignore_equipped ignores any already equipped items in that slot
* * indirect_action allows inserting into "soft locked" bags, things that can be easily opened by the owner
*/
/obj/item/proc/mob_can_equip(mob/living/M, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action = FALSE)
if(!M)
return FALSE
return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self, ignore_equipped, indirect_action = indirect_action)
/obj/item/verb/verb_pickup()
set src in oview(1)
set category = "Object"
set name = "Pick up"
if(usr.incapacitated() || !Adjacent(usr))
return
if(isliving(usr))
var/mob/living/L = usr
if(!(L.mobility_flags & MOBILITY_PICKUP))
return
if(usr.get_active_held_item() == null) // Let me know if this has any problems -Yota
usr.UnarmedAttack(src)
/**
*This proc is executed when someone clicks the on-screen UI button.
*The default action is attack_self().
*Checks before we get to here are: mob is alive, mob is not restrained, stunned, asleep, resting, laying, item is on the mob.
*/
/obj/item/proc/ui_action_click(mob/user, actiontype)
if(SEND_SIGNAL(src, COMSIG_ITEM_UI_ACTION_CLICK, user, actiontype) & COMPONENT_ACTION_HANDLED)
return
attack_self(user)
///This proc determines if and at what an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit
/obj/item/proc/IsReflect(def_zone)
return FALSE
/obj/item/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FOUR)
throw_at(S,14,3, spin=0)
else
return
/obj/item/on_exit_storage(datum/storage/master_storage)
. = ..()
do_drop_animation(master_storage.parent)
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(QDELETED(hit_atom))
return
if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum) & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
return
if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
return
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(get_temperature() && isliving(hit_atom))
var/mob/living/L = hit_atom
L.ignite_mob()
var/itempush = 1
if(w_class < WEIGHT_CLASS_BULKY)
itempush = 0 //too light to push anything
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0 || HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
if (mob_throw_hit_sound)
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/genhit.ogg',volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
else
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
if(HAS_TRAIT(src, TRAIT_NODROP))
return
thrownby = WEAKREF(thrower)
callback = CALLBACK(src, PROC_REF(after_throw), callback) //replace their callback with our own
. = ..(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle, quickstart = quickstart)
/obj/item/proc/after_throw(datum/callback/callback)
if (callback) //call the original callback
. = callback.Invoke()
item_flags &= ~IN_INVENTORY
if(!pixel_y && !pixel_x && !(item_flags & NO_PIXEL_RANDOM_DROP))
pixel_x = rand(-8,8)
pixel_y = rand(-8,8)
/// Takes the location to move the item to, and optionally the mob doing the removing
/// If no mob is provided, we'll pass in the location, assuming it is a mob
/// Please use this if you're going to snowflake an item out of a obj/item/storage
/obj/item/proc/remove_item_from_storage(atom/newLoc, mob/removing)
if(!newLoc)
return FALSE
if(!removing)
if(ismob(newLoc))
removing = newLoc
else
stack_trace("Tried to remove an item and place it into [newLoc] without implicitly or explicitly passing in a mob doing the removing")
return
if(loc.atom_storage)
return loc.atom_storage.remove_single(removing, src, newLoc, silent = TRUE)
return FALSE
/// Returns the icon used for overlaying the object on a belt
/obj/item/proc/get_belt_overlay()
var/icon_state_to_use = belt_icon_state || icon_state
if(greyscale_config_belt && greyscale_colors)
return mutable_appearance(SSgreyscale.GetColoredIconByType(greyscale_config_belt, greyscale_colors), icon_state_to_use)
return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', icon_state_to_use)
/**
* Extend this to give the item an appearance when placed in a surgical tray. Uses an icon state in `medicart.dmi`.
* * tray_extended - If true, the surgical tray the item is placed on is in "table mode"
*/
/obj/item/proc/get_surgery_tool_overlay(tray_extended)
return null
/obj/item/proc/update_slot_icon()
if(!ismob(loc))
return
var/mob/owner = loc
var/flags = slot_flags
if(flags & ITEM_SLOT_OCLOTHING)
owner.update_worn_oversuit()
if(flags & ITEM_SLOT_ICLOTHING)
owner.update_worn_undersuit()
if(flags & ITEM_SLOT_GLOVES)
owner.update_worn_gloves()
if(flags & ITEM_SLOT_EYES)
owner.update_worn_glasses()
if(flags & ITEM_SLOT_EARS)
owner.update_worn_ears()
if(flags & ITEM_SLOT_MASK)
owner.update_worn_mask()
if(flags & ITEM_SLOT_HEAD)
owner.update_worn_head()
if(flags & ITEM_SLOT_FEET)
owner.update_worn_shoes()
if(flags & ITEM_SLOT_ID)
owner.update_worn_id()
if(flags & ITEM_SLOT_BELT)
owner.update_worn_belt()
if(flags & ITEM_SLOT_BACK)
owner.update_worn_back()
if(flags & ITEM_SLOT_NECK)
owner.update_worn_neck()
///Returns the temperature of src. If you want to know if an item is hot use this proc.
/obj/item/proc/get_temperature()
if(resistance_flags & ON_FIRE)
return max(heat, BURNING_ITEM_MINIMUM_TEMPERATURE)
return heat
///Returns the sharpness of src. If you want to get the sharpness of an item use this.
/obj/item/proc/get_sharpness()
return sharpness
/obj/item/proc/get_dismember_sound()
if(damtype == BURN)
. = SFX_SEAR
else
. = SFX_DESECRATION
/// Creates an ignition hotspot if item is lit and located on turf, in mask, or in hand
/obj/item/proc/open_flame(flame_heat=700)
var/turf/location = loc
if(ismob(location))
var/mob/pyromanic = location
var/success = FALSE
if(src == pyromanic.get_item_by_slot(ITEM_SLOT_MASK) || (src in pyromanic.held_items))
success = TRUE
if(success)
location = get_turf(pyromanic)
if(isturf(location))
location.hotspot_expose(flame_heat, 5)
/// If an object can successfully be used as a fire starter it will return a message
/obj/item/proc/ignition_effect(atom/A, mob/user)
if(get_temperature())
. = span_notice("[user] lights [A] with [src].")
else
. = ""
/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return SEND_SIGNAL(src, COMSIG_ATOM_HITBY, AM, skipcatch, hitpush, blocked, throwingdatum)
/obj/item/attack_hulk(mob/living/carbon/human/user)
return FALSE
/obj/item/attack_animal(mob/living/simple_animal/user, list/modifiers)
if (obj_flags & CAN_BE_HIT)
return ..()
return 0
/obj/item/burn()
if(!QDELETED(src))
var/turf/T = get_turf(src)
var/ash_type = /obj/effect/decal/cleanable/ash
if(w_class == WEIGHT_CLASS_HUGE || w_class == WEIGHT_CLASS_GIGANTIC)
ash_type = /obj/effect/decal/cleanable/ash/large
var/obj/effect/decal/cleanable/ash/A = new ash_type(T)
A.desc += "\nLooks like this used to be \an [name] some time ago."
..()
/obj/item/acid_melt()
if(!QDELETED(src))
var/turf/T = get_turf(src)
var/obj/effect/decal/cleanable/molten_object/MO = new(T)
MO.pixel_x = rand(-16,16)
MO.pixel_y = rand(-16,16)
MO.desc = "Looks like this was \an [src] some time ago."
..()
/obj/item/proc/microwave_act(obj/machinery/microwave/microwave_source, mob/microwaver, randomize_pixel_offset)
SHOULD_CALL_PARENT(TRUE)
return SEND_SIGNAL(src, COMSIG_ITEM_MICROWAVE_ACT, microwave_source, microwaver, randomize_pixel_offset)
//Used to check for extra requirements for blending(grinding or juicing) an object
/obj/item/proc/blend_requirements(obj/machinery/reagentgrinder/R)
return TRUE
///Called BEFORE the object is ground up - use this to change grind results based on conditions. Return "-1" to prevent the grinding from occurring
/obj/item/proc/on_grind()
return SEND_SIGNAL(src, COMSIG_ITEM_ON_GRIND)
///Grind item, adding grind_results to item's reagents and transfering to target_holder if specified
/obj/item/proc/grind(datum/reagents/target_holder, mob/user)
if(on_grind() == -1)
return FALSE