/obj/item name = "item" icon = 'icons/obj/items.dmi' w_class = WEIGHT_CLASS_NORMAL // todo: better way, for now, block all rad contamination to interior rad_flags = RAD_BLOCK_CONTENTS obj_flags = OBJ_IGNORE_MOB_DEPTH | OBJ_RANGE_TARGETABLE depth_level = 0 climb_allowed = FALSE //* Actions *// /// cached action descriptors /// /// this can be: /// * a /datum/action instance /// * a /datum/action typepath /// * a list of /datum/action instancse /// * a list of /datum/action typepaths /// /// typepaths get instanced on us entering inventory var/item_actions /// if [item_actions] is not set, and this is, we make a single action rendering ourselves /// and set its name to this var/item_action_name /// if [item_actions] is not set, and [action_name] is set, this is the mobility flags /// the action will check for var/item_action_mobility_flags = MOBILITY_CAN_HOLD | MOBILITY_CAN_USE //* Combat *// /// Amount of damage we do on melee. var/damage_force = 0 /// armor flag for melee attacks var/damage_flag = ARMOR_MELEE /// damage tier var/damage_tier = 3 /// damage_mode bitfield - see [code/__DEFINES/combat/damage.dm] var/damage_mode = NONE /// DAMAGE_TYPE_* enum /// /// * This is the primary damage type this object does on usage as a melee / thrown weapon. var/damage_type = DAMAGE_TYPE_BRUTE /// passive parry data / frame /// /// * anonymous typepath is allowed /// * typepath is allowed /// * instance is allowed /// /// note that the component will not be modified while held; /// if this is changed, the component needs to be remade. var/passive_parry /// base melee click cooldown var/melee_click_cd_base = 0.8 SECONDS /// base melee click cooldown multiplier var/melee_click_cd_multiply = 1 //* Economy /// economic category for items var/economic_category_item = ECONOMIC_CATEGORY_ITEM_DEFAULT //* Flags *// /// Item flags. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/item_flags = ITEM_ENCUMBERS_WHILE_HELD /// Miscellaneous flags pertaining to equippable objects. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/clothing_flags = NONE /// Flags for items (or in some cases mutant parts) hidden by this item when worn. /// As of right now, some flags only work in some slots. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/inv_hide_flags = NONE /// Flags for the bodyparts this item covers when worn. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. /// /// * Do not set these directly, use set_body_cover_flags()! var/body_cover_flags = NONE /// Flags which determine which body parts are protected from heat. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/heat_protection_cover = NONE /// Flags which determine which body parts are protected from cold. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/cold_protection_cover = NONE /// This is used to determine on which slots an item can fit, for inventory slots that use flags to determine this. /// These flags are listed in [code/__DEFINES/inventory/slots.dm]. var/slot_flags = NONE /// This is used to determine how we persist, in addition to potentially atom_persist_flags and obj_persist_flags (not yet made) /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/item_persist_flags = NONE /// This is used to determine how default item-level interaction hooks are handled. /// These flags are listed in [code/__DEFINES/_flags/interaction_flags.dm] var/interaction_flags_item = INTERACT_ITEM_ATTACK_SELF //* Inventory *// /// Currently equipped slot ID or hand index if held in hand var/inv_slot_or_index /// The inventory datum we're in. /// /// * This also doubles as an 'is in inventory' check, as this will always be set if we are in inventory. /// * This also doubles as 'get worn mob' by doing `inv_inside?.owner`. var/datum/inventory/inv_inside /// currently equipped slot id /// /// todo: `worn_slot_or_index` var/worn_slot /// current hand index, if held in hand /// /// todo: `worn_slot_or_index` var/held_index /** * current item we fitted over * ! DANGER: While this is more or less bug-free for "won't lose the item when you unequip/won't get stuck", we * ! do not promise anything for functionality - this is a SNOWFLAKE SYSTEM. */ var/obj/item/worn_over /** * current item we're fitted in. */ var/obj/item/worn_inside /// suppress auto inventory hooks in forceMove var/worn_hook_suppressed = FALSE //* Environmentals *// /// 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 /// Set this variable if the item protects its wearer against high pressures below an upper bound. Keep at null to disable protection. var/max_pressure_protection /// Set this variable if the item protects its wearer against low pressures above a lower bound. Keep at null to disable protection. 0 represents protection against hard vacuum. var/min_pressure_protection //* Carry Weight *// /// encumberance. /// calculated as max() of all encumbrance /// result is calculated into slowdown value /// and then max()'d with carry weight for the final slowdown used. var/encumbrance = ITEM_ENCUMBRANCE_BASELINE /// registered encumbrance - null if not in inventory var/encumbrance_registered /// carry weight in kgs. this might be generalized later so KEEP IT REALISTIC. var/weight = ITEM_WEIGHT_BASELINE /// registered carry weight - null if not in inventory. var/weight_registered /// flat encumbrance - while worn, you are treated as at **least** this encumbered /// e.g. if someone is wearing a flat 50 encumbrance item, but their regular encumbrance tally is only 45, they still have 50 total. var/flat_encumbrance = 0 /// Hard slowdown. Applied before carry weight. /// This affects multiplicative movespeed. var/slowdown = 0 //* Mounting *// /// Base level item API for item mounts. /// * This is manipulated from the mount side, not from the item side for now. /// * Item side does have a Destroy() hook. var/datum/item_mount/item_mount //* Storage *// /// storage cost for volumetric storage /// null to default to weight class var/weight_volume //* Storage - Belts *// /// Belt storage class /// * IT IS NOT SAFE TO MODIFY THIS WITHOUT USING GETTER / SETTER PATTERN /// TODO: getter / setter var/belt_storage_class = BELT_CLASS_INVALID /// Belt storage size /// * IT IS NOT SAFE TO MODIFY THIS WITHOUT USING GETTER / SETTER PATTERN /// TODO: getter / setter var/belt_storage_size = BELT_SIZE_DEFAULT //* Storage - Suit Storage *// /// Suit storage classes var/suit_storage_class = NONE /// Suit storage classes to allow var/suit_storage_class_allow = NONE /// Suit storage classes to disallow var/suit_storage_class_disallow = NONE /// Suit storage override type-list /// * only for adminbus really VAR_PRIVATE/list/suit_storage_types_allow_override /// Suit storage override type-list /// * only for adminbus really VAR_PRIVATE/list/suit_storage_types_disallow_override //? unsorted / legacy /// This saves our blood splatter overlay, which will be processed not to go over the edges of the sprite var/image/blood_overlay = null pass_flags = ATOM_PASS_TABLE pressure_resistance = 5 var/obj/item/master = null /// Used by R&D to determine what research bonuses it grants. var/list/origin_tech = null /** * Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]" * Either a list() with equal chances or a single verb. */ var/list/attack_verb = "attacked" /// 0 prevents all transfers, 1 is invisible //var/heat_transfer_coefficient = 1 /// For leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets) var/gas_transfer_coefficient = 1 /// For chemicals/diseases var/permeability_coefficient = 1 /// For electrical admittance/conductance (electrocution checks and shit) var/siemens_coefficient = 1 // todo: kill with fire /// All items can have an uplink hidden inside, just remember to add the triggers. var/obj/item/uplink/hidden/hidden_uplink = null /// Name used for message when binoculars/scope is used // todo: kill with fire var/zoomdevicename = null /// TRUE if item is actively being used to zoom. For scoped guns and binoculars. // todo: kill with fire var/zoom = FALSE /// 0 won't embed, and 100 will always embed var/embed_chance = EMBED_CHANCE_UNSET /// Length of tiles it can reach, 1 is adjacent. var/reach = 1 /// Icon overlay for ADD highlights when applicable. var/addblends //? Sounds /// sound used when used in melee attacks. null for default for our damage tpye. var/attack_sound /// Used when thrown into a mob. var/mob_throw_hit_sound /// Sound used when equipping the item into a valid slot from hands or ground var/equip_sound /// Sound used when uneqiupping the item from a valid slot to hands or ground var/unequip_sound /// Pickup sound - played when picking something up off the floor. var/pickup_sound = 'sound/items/pickup/device.ogg' /// Drop sound - played when dropping something onto the floor. var/drop_sound = 'sound/items/drop/device.ogg' /// Whether or not we are heavy. Used for some species to determine if they can two-hand it. var/heavy = FALSE /// If true, a 'cleaving' attack will occur. var/can_cleave = FALSE /// Used to avoid infinite cleaving. var/cleaving = FALSE /obj/item/Initialize(mapload) . = ..() loc?.on_contents_item_new(src) if(islist(origin_tech)) origin_tech = typelist(NAMEOF(src, origin_tech), origin_tech) //Potential memory optimization: Making embed chance a getter if unset. if(embed_chance == EMBED_CHANCE_UNSET) if(damage_mode & DAMAGE_MODE_SHARP) embed_chance = max(5, round(damage_force/w_class)) else embed_chance = max(5, round(damage_force/(w_class*3))) /obj/item/Destroy() // run inventory hooks if(worn_slot && !worn_hook_suppressed) var/mob/M = get_worn_mob() if(!ismob(M)) stack_trace("invalid current equipped slot [worn_slot] on an item not on a mob.") return ..() M.temporarily_remove_from_inventory(src, INV_OP_FORCE | INV_OP_DELETING) // inform mount so it can unmount us item_mount?.on_item_del(src) return ..() /// Check if target is reasonable for us to operate on. /obj/item/proc/check_allowed_items(atom/target, not_inside, target_self) if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside))) return FALSE else return TRUE /obj/item/verb/move_to_top() set name = "Move To Top" set category = VERB_CATEGORY_OBJECT set src in oview(1) if(!istype(src.loc, /turf) || usr.stat || usr.restrained() ) return var/turf/T = src.loc src.loc = null src.loc = T /// See inventory_sizes.dm for the defines. /obj/item/examine(mob/user, dist) . = ..() . += "[gender == PLURAL ? "They are" : "It is"] a [weightclass2text(w_class)] item." switch(get_encumbrance()) if(-INFINITY to 0.1) . += "It looks effortless to carry around and wear." if(0.1 to 0.75) . += "It looks very easy to carry around and wear." if(0.75 to 2) . += "It looks decently able to be carried around and worn." if(2 to 5) . += "It looks somewhat unwieldly." if(5 to 10) . += "It looks quite unwieldly." if(10 to 20) . += "It looks very unwieldly. It would take a good effort to run around with it." if(20 to 40) . += "It looks extremely unwieldly. You probably will have a hard time running with it." if(40 to INFINITY) . += "It's so unwieldly that it's a surprise you can hold it at all. You really won't be doing much running with it." switch(get_weight()) if(-INFINITY to 0.1) // todo: put this in when we actually get weight // . += "It looks like it weighs practically nothing." if(0.1 to 0.75) . += "It looks like it weighs very little." if(0.75 to 2) . += "It looks like it's decently lightweight." if(2 to 5) . += "It looks like it weighs a bit." if(5 to 10) . += "It looks like it weighs a good amount." if(10 to 20) . += "It looks like it is heavy. It would take a good effort to run around with it." if(20 to 40) . += "It looks like it weighs a lot. You probably will have a hard time running with it." if(40 to INFINITY) . += "It looks like it weighs a ton. You really won't be doing much running with it." if(belt_storage_class != BELT_CLASS_INVALID) . += "It looks like it can fit into a belt, and would use [belt_storage_size] [lowertext(global.belt_class_names[belt_storage_class])]-sized loop(s)." // if(item_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY)) // var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data) // . += "[src] has the capacity to be used to block and/or parry. \[Show Stats\]" /proc/weightclass2text(w_class) switch(w_class) if(WEIGHT_CLASS_TINY, WEIGHT_CLASS_TINY) . = "tiny" if(WEIGHT_CLASS_SMALL, WEIGHT_CLASS_SMALL) . = "small" if(WEIGHT_CLASS_NORMAL, WEIGHT_CLASS_NORMAL) . = "normal-sized" if(WEIGHT_CLASS_BULKY, WEIGHT_CLASS_BULKY) . = "bulky" if(WEIGHT_CLASS_HUGE, WEIGHT_CLASS_HUGE) . = "huge" if(WEIGHT_CLASS_GIGANTIC) . = "gigantic" else . = "" /obj/item/proc/talk_into(mob/M as mob, text) return /obj/item/proc/get_volume_by_throwforce_and_or_w_class() // This is used for figuring out how loud our sounds are for throwing. if(throw_force && w_class) return clamp((throw_force + w_class) * 5, 30, 100)// Add the item's throw_force to its weight class and multiply by 5, then clamp the value between 30 and 100 else if(w_class) return clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 else return 0 /obj/item/throw_impact(atom/A, datum/thrownthing/TT) . = ..() if(QDELETED(A)) return if(isliving(A)) //Living mobs handle hit sounds differently. var/volume = get_volume_by_throwforce_and_or_w_class() if (throw_force > 0) if (mob_throw_hit_sound) playsound(A, mob_throw_hit_sound, volume, TRUE, -1) else if(attack_sound) playsound(A, attack_sound, volume, TRUE, -1) else playsound(A, 'sound/weapons/genhit.ogg', volume, TRUE, -1) else playsound(A, 'sound/weapons/throwtap.ogg', 1, volume, -1) /obj/item/throw_land(atom/A, datum/thrownthing/TT) . = ..() // if we're landing from the impact we don't play a sound as we already played hitsound if(drop_sound && (A != TT.landing_from_impact)) playsound(src, drop_sound, 50, TRUE) if(TT.throw_flags & THROW_AT_IS_NEAT) return var/matrix/M = matrix(transform) M.Turn(rand(-170, 170)) transform = M set_pixel_offsets(rand(-8, 8), rand(-8, 8)) /obj/item/verb/verb_pickup() set src in oview(1) set category = VERB_CATEGORY_OBJECT set name = "Pick up" if(!(usr)) //BS12 EDIT return if(!CHECK_MOBILITY(usr, MOBILITY_CAN_PICKUP) || !Adjacent(usr)) return if((!istype(usr, /mob/living/carbon)) || (istype(usr, /mob/living/carbon/brain)))//Is humanoid, and is not a brain to_chat(usr, "You can't pick things up!") return var/mob/living/carbon/C = usr if( usr.stat || usr.restrained() )//Is not asleep/dead and is not restrained to_chat(usr, "You can't pick things up!") return if(src.anchored) //Object isn't anchored to_chat(usr, "You can't pick that up!") return if(C.get_active_held_item()) //Hand is not full to_chat(usr, "Your hand is full.") return if(!istype(src.loc, /turf)) //Object is on a turf to_chat(usr, "You can't pick that up!") return attempt_pickup(usr) /obj/item/ui_action_click(datum/action/action, datum/event_args/actor/actor) attack_self(usr) return TRUE /obj/item/proc/get_loc_turf() var/atom/L = loc while(L && !istype(L, /turf/)) L = L.loc return loc /obj/item/proc/eyestab(mob/living/carbon/M as mob, mob/living/carbon/user as mob) var/mob/living/carbon/human/H = M var/mob/living/carbon/human/U = user if(istype(H)) for(var/obj/item/protection in list(H.head, H.wear_mask, H.glasses)) if(protection && (protection.body_cover_flags & EYES)) // you can't stab someone in the eyes wearing a mask! to_chat(user, "You're going to need to remove the eye covering first.") return if(!M.has_eyes()) to_chat(user, "You cannot locate any eyes on [M]!") return //this should absolutely trigger even if not aim-impaired in some way var/hit_zone = get_zone_with_miss_chance(U.zone_sel.selecting, M, U.get_accuracy_penalty(U)) if(!hit_zone) U.do_attack_animation(M) playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) //visible_message("[U] attempts to stab [M] in the eyes, but misses!") for(var/mob/V in viewers(M)) V.show_message("[U] attempts to stab [M] in the eyes, but misses!") return add_attack_logs(user,M,"Attack eyes with [name]") user.setClickCooldownLegacy(user.get_attack_speed_legacy()) user.do_attack_animation(M) src.add_fingerprint(user) if(istype(H)) var/obj/item/organ/internal/eyes/eyes = H.internal_organs_by_name[O_EYES] if(H != user) for(var/mob/O in (viewers(M) - user - M)) O.show_message("[M] has been stabbed in the eye with [src] by [user].", 1) to_chat(M, "[user] stabs you in the eye with [src]!") to_chat(user, "You stab [M] in the eye with [src]!") else user.visible_message( \ "[user] has stabbed themself with [src]!", \ "You stab yourself in the eyes with [src]!" \ ) eyes.damage += rand(3,4) if(eyes.damage >= eyes.min_bruised_damage) if(M.stat != 2) if(!(eyes.robotic >= ORGAN_ROBOT)) //robot eyes bleeding might be a bit silly to_chat(M, "Your eyes start to bleed profusely!") if(prob(50)) if(M.stat != 2) to_chat(M, "You drop what you're holding and clutch at your eyes!") M.drop_active_held_item() M.eye_blurry += 10 M.afflict_unconscious(20 * 1) M.afflict_paralyze(20 * 4) if (eyes.damage >= eyes.min_broken_damage) if(M.stat != 2) to_chat(M, "You go blind!") var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) affecting.inflict_bodypart_damage( brute = 7, ) else M.take_random_targeted_damage(brute = 7) M.eye_blurry += rand(3,4) return /obj/item/clean_blood() . = ..() if(blood_overlay) cut_overlay(blood_overlay) if(istype(src, /obj/item/clothing/gloves)) var/obj/item/clothing/gloves/G = src G.transfer_blood = 0 /obj/item/reveal_blood() if(was_bloodied && !fluorescent) fluorescent = 1 blood_color = COLOR_LUMINOL blood_overlay.color = COLOR_LUMINOL update_icon() /obj/item/add_blood(mob/living/carbon/human/M as mob) if (!..()) return 0 if(istype(src, /obj/item/melee/transforming/energy)) return //if we haven't made our blood_overlay already if( !blood_overlay ) generate_blood_overlay() //Make the blood_overlay have the proper color then apply it. blood_overlay.color = blood_color add_overlay(blood_overlay) //if this blood isn't already in the list, add it if(istype(M)) if(blood_DNA[M.dna.unique_enzymes]) return 0 //already bloodied with this blood. Cannot add more. blood_DNA[M.dna.unique_enzymes] = M.dna.b_type return 1 //we applied blood to the item // Protip: don't use world scans to implement caching. Yes, that is how this used to work. GLOBAL_LIST_EMPTY(blood_overlay_cache) /obj/item/proc/generate_blood_overlay() if(blood_overlay) return if (GLOB.blood_overlay_cache[type]) blood_overlay = GLOB.blood_overlay_cache[type] return var/icon/I = new /icon(icon, icon_state) I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)), ICON_ADD) //fills the icon_state with white (except where it's transparent) I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant blood_overlay = GLOB.blood_overlay_cache[type] = image(I) /obj/item/proc/showoff(mob/user) for (var/mob/M in view(user)) M.show_message("[user] holds up [src]. Take a closer look.",1) /mob/living/carbon/verb/showoff() set name = "Show Held Item" set category = VERB_CATEGORY_OBJECT var/obj/item/I = get_active_held_item() if(I && !(I.atom_flags & ATOM_ABSTRACT)) I.showoff(src) /* For zooming with scope or binoculars. This is called from modules/mob/mob_movement.dm if you move you will be zoomed out modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. */ //Looking through a scope or binoculars should /not/ improve your periphereal vision. Still, increase viewsize a tiny bit so that sniping isn't as restricted to NSEW // this is shitcode holy crap /obj/item/proc/zoom(tileoffset = 14, viewsize = 9, mob/user = usr, wornslot = FALSE) //tileoffset is client view offset in the direction the user is facing. viewsize is how far out this thing zooms. 7 is normal view. slot determines whether the item needs to be held in-hand (by being set to FALSE) OR worn on a specific slot to look through var/devicename if(zoomdevicename) devicename = zoomdevicename else devicename = src.name var/cannotzoom if((user.stat && !zoom) || !(istype(user,/mob/living/carbon/human))) to_chat(user, "You are unable to focus through the [devicename]") cannotzoom = 1 else if(!zoom && (GLOB.global_hud.darkMask[1] in user.client.screen)) to_chat(user, "Your visor gets in the way of looking through the [devicename]") cannotzoom = 1 else if(!zoom && user.get_active_held_item() != src && wornslot == FALSE) to_chat(user, "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better") cannotzoom = 1 else if(!zoom && user.item_by_slot_id(wornslot) != src && wornslot != FALSE) to_chat(user, "You need to wear the [devicename] to look through it properly") cannotzoom = 1 //We checked above if they are a human and returned already if they weren't. var/mob/living/carbon/human/H = user if(!zoom && !cannotzoom) H.ensure_self_perspective() H.self_perspective.set_augmented_view(viewsize - 2, viewsize - 2) zoom = 1 var/tilesize = 32 var/viewoffset = tilesize * tileoffset switch(H.dir) if (NORTH) H.client.pixel_x = 0 H.client.pixel_y = viewoffset if (SOUTH) H.client.pixel_x = 0 H.client.pixel_y = -viewoffset if (EAST) H.client.pixel_x = viewoffset H.client.pixel_y = 0 if (WEST) H.client.pixel_x = -viewoffset H.client.pixel_y = 0 H.visible_message("[user] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].") H.looking_elsewhere = TRUE H.handle_vision() else H.ensure_self_perspective() H.self_perspective.set_augmented_view(0, 0) zoom = 0 H.client.pixel_x = 0 H.client.pixel_y = 0 H.looking_elsewhere = FALSE H.handle_vision() if(!cannotzoom) user.visible_message("[zoomdevicename ? "[user] looks up from the [src.name]" : "[user] lowers the [src.name]"].") return /obj/item/proc/pwr_drain() return 0 // Process Kill /// Check if an object should ignite others, like a lit lighter or candle. /obj/item/proc/is_hot() return FALSE /// These procs are for RPEDs and part ratings. The concept for this was borrowed from /vg/station. /// Gets the rating of the item, used in stuff like machine construction. /// return null for don't use as part /obj/item/proc/get_rating() return null /// These procs are for RPEDs and part ratings, but used for RPED sorting of parts. /obj/item/proc/rped_rating() return get_rating() // todo: WHAT? /obj/item/interact(mob/user) add_fingerprint(user) ui_interact(user) //* Actions *// /** * instructs all our action buttons to re-render */ /obj/item/proc/update_action_buttons() if(islist(item_actions)) for(var/datum/action/action in item_actions) action.update_buttons() else if(istype(item_actions, /datum/action)) var/datum/action/action = item_actions action.update_buttons() /** * ensures our [item_actions] variable is set to: * * * null * * a list of actions * * an action instance */ /obj/item/proc/ensure_item_actions_loaded() if(item_actions) if(islist(item_actions)) var/requires_init = FALSE for(var/i in 1 to length(item_actions)) if(ispath(item_actions[i])) requires_init = TRUE break if(requires_init) set_actions_to(item_actions) else if(ispath(item_actions)) set_actions_to(item_actions) else if(item_action_name) var/datum/action/item_action/created = new(src) created.name = item_action_name created.check_mobility_flags = item_action_mobility_flags set_actions_to(created) /** * setter for [item_actions] * * accepts: * * * an instance of /datum/action * * a typepath of /datum/action * * a list of /datum/action instances and typepaths * * null */ /obj/item/proc/set_actions_to(descriptor) var/mob/get_worn_mob = get_worn_mob() if(get_worn_mob) unregister_item_actions(get_worn_mob) if(ispath(descriptor, /datum/action)) descriptor = new descriptor(src) else if(islist(descriptor)) var/list/transforming = descriptor:Copy() for(var/i in 1 to length(transforming)) if(ispath(transforming[i], /datum/action)) var/path = transforming[i] transforming[i] = new path(src) descriptor = transforming else item_actions = descriptor if(get_worn_mob) register_item_actions(get_worn_mob) /** * handles action granting */ /obj/item/proc/register_item_actions(mob/user) ensure_item_actions_loaded() if(islist(item_actions)) for(var/datum/action/action in item_actions) action.grant(user.inventory.actions) else if(istype(item_actions, /datum/action)) var/datum/action/action = item_actions action.grant(user.inventory.actions) /** * handles action revoking */ /obj/item/proc/unregister_item_actions(mob/user) if(islist(item_actions)) for(var/datum/action/action in item_actions) action.revoke(user.inventory.actions) else if(istype(item_actions, /datum/action)) var/datum/action/action = item_actions action.revoke(user.inventory.actions) //* Attack *// /** * grabs an attack verb to use * * @params * * target - thing being attacked * * user - person attacking * * @return attack verb */ /obj/item/proc/get_attack_verb(atom/target, mob/user) return length(attack_verb)? pick(attack_verb) : attack_verb /** * can be sharp; even if not being used as such * * @params * * strict - require us to be toggled to sharp mode if there's multiple modes of attacking. */ /obj/item/proc/is_sharp(strict) return (damage_mode & DAMAGE_MODE_SHARP) /** * can be edged; even if not being used as such * * @params * * strict - require us to be toggled to sharp mode if there's multiple modes of attacking. */ /obj/item/proc/is_edge(strict) return (damage_mode & DAMAGE_MODE_EDGE) /** * can be piercing; even if not being used as such * * @params * * strict - require us to be toggled to sharp mode if there's multiple modes of attacking. */ /obj/item/proc/is_pierce(strict) return (damage_mode & DAMAGE_MODE_PIERCE) /** * can be shredding; even if not being used as such * * @params * * strict - require us to be toggled to sharp mode if there's multiple modes of attacking. */ /obj/item/proc/is_shred(strict) return (damage_mode & DAMAGE_MODE_SHRED) //* Combat *// /obj/item/proc/load_passive_parry() if(!passive_parry) return passive_parry = resolve_passive_parry_data(passive_parry) var/datum/component/passive_parry/loaded = GetComponent(/datum/component/passive_parry) if(loaded) loaded.parry_data = passive_parry /obj/item/proc/reload_passive_parry() load_passive_parry() //* Flags *// /obj/item/proc/set_body_cover_flags(new_body_cover_flags) body_cover_flags = new_body_cover_flags inv_inside.invalidate_coverage_cache() //* Interactions *// /obj/item/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) if(isturf(loc) && I.obj_storage?.allow_mass_gather && I.obj_storage.allow_mass_gather_via_click) I.obj_storage.auto_handle_interacted_mass_pickup(new /datum/event_args/actor(user), src) return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING return ..() /** * Hitsound override when successfully melee attacking someone for melee_hit() * * We get final say by returning a sound here. */ /obj/item/proc/attacksound_override(atom/target, attack_type) return //* Inventory *// /** * Called when someone clisk us on a storage, before the storage handler's * 'put item in' runs. Return FALSE to deny. */ /obj/item/proc/allow_auto_storage_insert(datum/event_args/actor/actor, datum/object_system/storage/storage) return TRUE /obj/item/proc/on_exit_storage(datum/object_system/storage/storage) SEND_SIGNAL(src, COMSIG_STORAGE_EXITED, storage) /obj/item/proc/on_enter_storage(datum/object_system/storage/storage) SEND_SIGNAL(src, COMSIG_STORAGE_ENTERED, storage) //* Materials *// /obj/item/material_trait_brittle_shatter() var/datum/prototype/material/material = get_primary_material() var/turf/T = get_turf(src) T.visible_message("\The [src] [material.destruction_desc]!") // if(istype(loc, /mob/living)) // var/mob/living/M = loc // if(material.shard_type == SHARD_SHARD) // Wearing glass armor is a bad idea. // var/obj/item/material/shard/S = material.place_shard(T) // M.embed(S) playsound(src, "shatter", 70, 1) qdel(src) //* Mount *// /** * Mounts to an item mount */ /obj/item/proc/mount(datum/item_mount/mount) return mount.mount(src) /** * Unmounts us if we're mounted onto an item mount */ /obj/item/proc/unmount() return item_mount?.unmount(src) /** * Returns if we're mounted (true / false) */ /obj/item/proc/is_mounted() as num return !!item_mount /** * Returns our mount, if any */ /obj/item/proc/get_mount() as /datum/item_mount return item_mount //* Mouse *// /obj/item/MouseEntered(location, control, params) ..() SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTERED, usr) /obj/item/MouseExited(location, control, params) ..() SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXITED, usr) //* Rendering *// /** * invokes * * * update_icon() * * update_worn_icon() * * update_action_buttons() * * please be very delicate with this, this is expensive */ /obj/item/proc/update_full_icon() update_icon() update_worn_icon() update_action_buttons() //* Storage *// /obj/item/proc/get_weight_class() return w_class /obj/item/proc/get_weight_volume() return isnull(weight_volume)? global.w_class_to_volume[w_class || WEIGHT_CLASS_GIGANTIC] : weight_volume /obj/item/proc/set_weight_class(weight_class) var/old = w_class w_class = weight_class loc?.on_contents_weight_class_change(src, old, weight_class) /obj/item/proc/set_weight_volume(volume) var/old = weight_volume weight_volume = volume loc?.on_contents_weight_volume_change(src, old, volume) /** * called when someone is opening a storage with us in it * * @return TRUE to stop the storage from opening */ /obj/item/proc/on_containing_storage_opening(datum/event_args/actor/actor, datum/object_system/storage/storage) return FALSE //* VV *// /obj/item/vv_get_var(var_name, resolve) switch(var_name) if(NAMEOF(src, passive_parry)) if(resolve) load_passive_parry() return ..() /obj/item/vv_edit_var(var_name, var_value, mass_edit, raw_edit) switch(var_name) if(NAMEOF(src, passive_parry)) . = ..() reload_passive_parry() if(NAMEOF(src, item_flags)) var/requires_update = (item_flags & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) != (var_value & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) . = ..() if(. && requires_update) var/mob/living/L = get_worn_mob() // check, as get_worn_mob() returns /mob, not /living if(istype(L)) L.recalculate_carry() L.update_carry() if(NAMEOF(src, weight), NAMEOF(src, encumbrance), NAMEOF(src, flat_encumbrance)) // todo: introspection system update - this should be 'handled', as opposed to hooked. . = ..() if(. ) var/mob/living/L = get_worn_mob() // check, as get_worn_mob() returns /mob, not /living if(istype(L)) L.update_carry_slowdown() if(NAMEOF(src, slowdown)) . = ..() if(.) var/mob/living/L = get_worn_mob() // check, as get_worn_mob() returns /mob, not /living if(istype(L)) L.update_item_slowdown() if(NAMEOF(src, w_class)) if(!isnum(var_value) && !raw_edit) return FALSE if((var_value < WEIGHT_CLASS_MIN) || (var_value > WEIGHT_CLASS_MAX)) return FALSE set_weight_class(var_value) return TRUE if(NAMEOF(src, weight_volume)) if(!isnum(var_value) && !raw_edit) return FALSE if(var_value < 0) return FALSE set_weight_volume(var_value) return TRUE else return ..()