mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
* it begins * Update gun.dm * misc fixes * Update gun.dm * more fixes * Update lightning_flow.dm * i may be stupid * Update suicide.dm * fix mech strafing * Update mecha.dm * let there be qol * ghost stuff * Update screen_clockwork.dmi * does the stuff * stuff * Update worldbreaker.dm * moltial arts * Update worldbreaker.dm * CRITICAL FIX * mech stuff * Update tables_racks.dm * stuff * fix seismic arm * buster/seismic arm fix 2 * saber + lockers * stuff * hand tele and pre_attack_secondary * more right click acts * Update closets.dm * who did this * heck * Update mob.dm * Update items.dm * darkspawn fix * fixes wound healing * Update item_attack.dm * minor qol stuff * Update kinetic_crusher.dm * Update kinetic_crusher.dm * runtime fix * Update kinetic_crusher.dm * Update screen_plasmafire.dmi * stuff * syringes * i am very silly * death to /obj/item/toolset_handler * Update assembly.dm * surgery fix + hypo stuff * mantis fix * gas harpoon * atmos machines --------- Co-authored-by: Molti <gamingjoelouis@gmail.com>
1207 lines
44 KiB
Plaintext
1207 lines
44 KiB
Plaintext
GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/effects/fire.dmi', FIRE))
|
|
|
|
GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
|
// if true, everyone item when created will have its name changed to be
|
|
// more... RPG-like.
|
|
|
|
/obj/item
|
|
name = "item"
|
|
icon = 'icons/obj/misc.dmi'
|
|
blocks_emissive = EMISSIVE_BLOCK_GENERIC
|
|
|
|
/* !!!!!!!!!!!!!!! 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 name for inhand overlays
|
|
var/item_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
|
|
|
|
//Dimensions of the icon file used when this item is worn, eg: hats.dmi
|
|
//eg: 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
|
|
var/worn_y_dimension = 32
|
|
//Same as above but for inhands, uses the lefthand_ and righthand_ file vars
|
|
var/inhand_x_dimension = 32
|
|
var/inhand_y_dimension = 32
|
|
|
|
max_integrity = 200
|
|
|
|
obj_flags = NONE
|
|
var/item_flags = NONE
|
|
|
|
var/hitsound
|
|
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
|
|
|
|
var/w_class = WEIGHT_CLASS_NORMAL
|
|
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
|
|
pass_flags = PASSTABLE
|
|
pressure_resistance = 4
|
|
var/obj/item/master = null
|
|
|
|
var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, CHEST, GROIN, etc. flags. See setup.dm
|
|
var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, CHEST, GROIN, etc. flags. See setup.dm
|
|
var/max_heat_protection_temperature //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/min_cold_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/list/actions //list of /datum/action's that this item has.
|
|
var/list/actions_types //list of paths of action datums to give to the item on New().
|
|
|
|
//Since any item can now be a piece of clothing, this has to be put here so all items share it.
|
|
var/flags_inv //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_prot //This flag acts like flags_inv except it allows the items to still be rendered.
|
|
var/transparent_protection = NONE //you can see someone's mask through their transparent visor, but you can't reach it
|
|
|
|
var/interaction_flags_item = INTERACT_ITEM_ATTACK_HAND_PICKUP
|
|
|
|
var/body_parts_covered = 0 //see setup.dm for appropriate bit flags
|
|
var/body_parts_partial_covered = 0 //same bit flags as above, only applies half armor to these body parts
|
|
|
|
var/gas_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/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up
|
|
var/armour_penetration = 0 //percentage of armour effectiveness to remove
|
|
var/list/allowed = null //suit storage stuff.
|
|
var/equip_delay_self = 0 //In deciseconds, how long an item takes to equip; counts only for normal clothing slots, not pockets etc.
|
|
var/equip_delay_other = 20 //In deciseconds, how long an item takes to put on another person
|
|
var/strip_delay = 40 //In deciseconds, how long an item takes to remove from another person
|
|
var/breakouttime = 0
|
|
var/list/materials //materials in this object, and the amount
|
|
|
|
var/list/attack_verb //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
|
|
var/list/species_exception = null // 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/mob/thrownby = null
|
|
|
|
mouse_drag_pointer = MOUSE_ACTIVE_POINTER //the icon to indicate this object is being dragged
|
|
|
|
var/datum/embedding_behavior/embedding
|
|
|
|
var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES
|
|
var/heat = 0
|
|
/// A list of statistics used when a weapon hits someone, swing speed = multiplier for melee attack cd, encumbrance = slowdown, encumbrance_time = slowdown length, reach = reach, embed chance = chance for applicable weapons to embed on hit, damage_low/high = range of damage the weapon takes on hitting a mob
|
|
var/list/weapon_stats = list(SWING_SPEED = 1, ENCUMBRANCE = 0, ENCUMBRANCE_TIME = 0, REACH = 1, DAMAGE_LOW = 0, DAMAGE_HIGH = 0)
|
|
/// multiplier to increase/decrease effects of range on attack cooldown, 0 to ignore range
|
|
var/range_cooldown_mod = 1
|
|
var/break_message = "%SRC crumbles into scraps under hard use."
|
|
///All items with sharpness of SHARP_EDGED or higher will automatically get the butchering component.
|
|
var/sharpness = SHARP_NONE
|
|
|
|
var/tool_behaviour = NONE
|
|
var/toolspeed = 1
|
|
|
|
var/block_chance = 0
|
|
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
|
|
|
|
//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.
|
|
var/list/slot_equipment_priority = null // for default list, see /mob/proc/equip_to_appropriate_slot()
|
|
|
|
// 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
|
|
var/force_string //string form of an item's force. Edit this var only to set a custom force string
|
|
var/last_force_string_check = 0
|
|
var/tip_timer
|
|
|
|
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 vars
|
|
var/list/grind_results //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/juice_results //A reagent list containing blah blah... but when JUICED in a grinder!
|
|
|
|
//Tape vars
|
|
var/taped = FALSE
|
|
|
|
/// Should the cryo console preserve this item
|
|
var/cryo_preserve = FALSE
|
|
|
|
/// Is this item fryable without a syndicate frying pan
|
|
var/fryable = FALSE
|
|
|
|
var/printed = FALSE
|
|
|
|
var/canMouseDown = FALSE
|
|
/// Does this item have syndicate only functionality via hud buttons? Needs to be in this scope to encompass all Chameleon items - Hopek
|
|
var/syndicate = FALSE
|
|
/// item hover FX
|
|
var/outline_filter
|
|
|
|
/obj/item/Initialize(mapload)
|
|
|
|
materials = typelist("materials", materials)
|
|
|
|
if(materials) //Otherwise, use the instances already provided.
|
|
var/list/temp_list = list()
|
|
for(var/i in materials) //Go through all of our materials, get the subsystem instance, and then replace the list.
|
|
var/amount = materials[i]
|
|
var/datum/material/M = getmaterialref(i)
|
|
temp_list[M] = amount
|
|
materials = temp_list
|
|
|
|
if (attack_verb)
|
|
attack_verb = typelist("attack_verb", attack_verb)
|
|
|
|
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(GLOB.rpg_loot_items)
|
|
AddComponent(/datum/component/fantasy)
|
|
|
|
if(sharpness && force > 5) //give sharp objects butchering functionality, for consistency
|
|
AddComponent(/datum/component/butchering, 80 * toolspeed)
|
|
|
|
if(force_string)
|
|
item_flags |= FORCE_STRING_OVERRIDE
|
|
|
|
if(!hitsound)
|
|
if(damtype == BURN)
|
|
hitsound = 'sound/items/welder.ogg'
|
|
if(damtype == "brute")
|
|
hitsound = "swing_hit"
|
|
|
|
if (!embedding)
|
|
embedding = getEmbeddingBehavior()
|
|
else if (islist(embedding))
|
|
embedding = getEmbeddingBehavior(arglist(embedding))
|
|
else if (!istype(embedding, /datum/embedding_behavior))
|
|
stack_trace("Invalid type [embedding.type] found in .embedding during /obj/item Initialize(mapload)")
|
|
|
|
/obj/item/Destroy(force=FALSE)
|
|
item_flags &= ~DROPDEL //prevent reqdels
|
|
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 ..()
|
|
|
|
/// 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))
|
|
if(ismob(loc))
|
|
// We're being held or are equipped by someone while adding an action?
|
|
// Then they should also probably be granted the action, given it's in a correct slot
|
|
var/mob/holder = loc
|
|
give_item_action(action, holder, holder.get_slot_by_item(src))
|
|
|
|
return action
|
|
|
|
/// 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)
|
|
|
|
|
|
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
|
|
if(((src in target) && !target_self) || (!isturf(target.loc) && !isturf(target) && not_inside))
|
|
return 0
|
|
else
|
|
return 1
|
|
|
|
/obj/item/blob_act(obj/structure/blob/B)
|
|
if(B && B.loc == loc)
|
|
qdel(src)
|
|
|
|
//user: The mob that is suiciding
|
|
//damagetype: The type of damage the item will inflict on the user
|
|
//BRUTELOSS = 1
|
|
//FIRELOSS = 2
|
|
//TOXLOSS = 4
|
|
//OXYLOSS = 8
|
|
//Output a creative message and then return the damagetype done
|
|
/obj/item/proc/suicide_act(mob/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/proc/get_sharpness()
|
|
return sharpness
|
|
|
|
/obj/item/verb/move_to_top()
|
|
set name = "Move To Top"
|
|
set category = "Object"
|
|
set src in oview(1)
|
|
|
|
if(!isturf(loc) || usr.stat || usr.restrained())
|
|
return
|
|
|
|
if(isliving(usr))
|
|
var/mob/living/L = usr
|
|
if(!(L.mobility_flags & MOBILITY_PICKUP))
|
|
return
|
|
|
|
var/turf/T = loc
|
|
loc = null
|
|
loc = T
|
|
|
|
/obj/item/examine(mob/user) //This might be spammy. Remove?
|
|
. = ..()
|
|
|
|
. += "[gender == PLURAL ? "They are" : "It is"] a [weightclass2text(w_class)] item."
|
|
|
|
if(HAS_TRAIT(src, TRAIT_NO_STORAGE))
|
|
. += "[gender == PLURAL ? "They are" : "It is"] too bulky, fragile, or cumbersome to fit in a container."
|
|
|
|
if(demolition_mod > 1)
|
|
. += "[src] seems exceptionally good at breaking things!"
|
|
else if(demolition_mod < 1)
|
|
. += "[src] seems exceptionally bad at breaking things."
|
|
|
|
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."
|
|
if(taped)
|
|
. += "[src] seems to be covered in tape."
|
|
if(!user.research_scanner)
|
|
return
|
|
|
|
// 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> ")
|
|
var/sep = ""
|
|
var/list/boostable_nodes = techweb_item_boost_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 (materials.len)
|
|
sep = ""
|
|
for(var/mat in materials)
|
|
research_msg += sep
|
|
research_msg += CallMaterialName(mat)
|
|
sep = ", "
|
|
else
|
|
research_msg += "None"
|
|
research_msg += "."
|
|
. += research_msg.Join()
|
|
|
|
/obj/item/interact(mob/user)
|
|
add_fingerprint(user)
|
|
ui_interact(user)
|
|
|
|
/obj/item/ui_act(action, list/params)
|
|
add_fingerprint(usr)
|
|
return ..()
|
|
|
|
/obj/item/attack_hand(mob/user, modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(!user)
|
|
return
|
|
if(anchored)
|
|
return
|
|
|
|
if(resistance_flags & ON_FIRE)
|
|
var/mob/living/carbon/C = user
|
|
var/can_handle_hot = FALSE
|
|
if(!istype(C))
|
|
can_handle_hot = TRUE
|
|
else if(C.gloves && (C.gloves.max_heat_protection_temperature > 360))
|
|
can_handle_hot = TRUE
|
|
else if(HAS_TRAIT(C, TRAIT_RESISTHEAT) || HAS_TRAIT(C, TRAIT_RESISTHEATHANDS))
|
|
can_handle_hot = TRUE
|
|
|
|
if(can_handle_hot)
|
|
extinguish()
|
|
to_chat(user, span_notice("You put out the fire on [src]."))
|
|
else
|
|
to_chat(user, span_warning("You burn your hand on [src]!"))
|
|
var/obj/item/bodypart/affecting = C.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
|
|
if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage
|
|
C.update_damage_overlays()
|
|
return
|
|
|
|
if(acid_level > 20 && !ismob(loc))// so we can still remove the clothes on us that have acid.
|
|
var/mob/living/carbon/C = user
|
|
if(istype(C))
|
|
if(!C.gloves || (!(C.gloves.resistance_flags & (UNACIDABLE|ACID_PROOF))))
|
|
to_chat(user, span_warning("The acid on [src] burns your hand!"))
|
|
var/obj/item/bodypart/affecting = C.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
|
|
if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage
|
|
C.update_damage_overlays()
|
|
|
|
if(!(interaction_flags_item & INTERACT_ITEM_ATTACK_HAND_PICKUP)) //See if we're supposed to auto pickup.
|
|
return
|
|
|
|
//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
|
|
SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
|
|
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
|
|
return
|
|
|
|
if(throwing)
|
|
throwing.finalize(FALSE)
|
|
if(loc == user)
|
|
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
|
|
return
|
|
|
|
pickup(user)
|
|
add_fingerprint(user)
|
|
if(!user.put_in_active_hand(src, FALSE, FALSE))
|
|
user.dropItemToGround(src)
|
|
|
|
/obj/item/proc/allow_attack_hand_drop(mob/user)
|
|
return TRUE
|
|
|
|
/obj/item/attack_paw(mob/user)
|
|
if(!user)
|
|
return
|
|
if(anchored)
|
|
return
|
|
|
|
SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
|
|
|
|
if(throwing)
|
|
throwing.finalize(FALSE)
|
|
if(loc == user)
|
|
if(!user.temporarilyRemoveItemFromInventory(src))
|
|
return
|
|
|
|
pickup(user)
|
|
add_fingerprint(user)
|
|
if(!user.put_in_active_hand(src, FALSE, FALSE))
|
|
user.dropItemToGround(src)
|
|
|
|
/obj/item/attack_alien(mob/user)
|
|
var/mob/living/carbon/alien/A = user
|
|
|
|
if(!A.has_fine_manipulation)
|
|
if(src in A.contents) // To stop Aliens having items stuck in their pockets
|
|
A.dropItemToGround(src)
|
|
to_chat(user, span_warning("Your claws aren't capable of such fine manipulation!"))
|
|
return
|
|
attack_paw(A)
|
|
|
|
/obj/item/attack_ai(mob/user)
|
|
if(istype(src.loc, /obj/item/robot_module))
|
|
//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()
|
|
|
|
/obj/item/proc/GetDeconstructableContents()
|
|
return get_all_contents() - src
|
|
|
|
// 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)
|
|
var/intercept = SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args)
|
|
if(intercept & COMPONENT_HIT_REACTION_BLOCK)
|
|
return 1
|
|
if(prob(final_block_chance))
|
|
owner.visible_message(span_danger("[owner] blocks [attack_text] with [src]!"))
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language, list/message_mods)
|
|
return ITALICS | REDUCE_RANGE
|
|
|
|
/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)
|
|
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)
|
|
|
|
|
|
// 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)
|
|
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
|
|
// 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)
|
|
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 && !initial &&(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)
|
|
|
|
/// 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))
|
|
// 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)
|
|
if(slot == ITEM_SLOT_BACKPACK || slot == 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.
|
|
//Set disable_warning to TRUE if you wish it to not give you outputs.
|
|
/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
|
|
if(!M)
|
|
return FALSE
|
|
|
|
return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self)
|
|
|
|
/obj/item/verb/verb_pickup()
|
|
set src in oview(1)
|
|
set category = "Object"
|
|
set name = "Pick up"
|
|
|
|
var/mob/user_mob = usr
|
|
|
|
if(HAS_TRAIT(src, TRAIT_NODROP))
|
|
return
|
|
|
|
if(user_mob.incapacitated() || !Adjacent(user_mob))
|
|
return
|
|
|
|
if(isliving(user_mob))
|
|
var/mob/living/L = user_mob
|
|
if(!(L.mobility_flags & MOBILITY_PICKUP))
|
|
return
|
|
|
|
if(issilicon(user_mob))
|
|
var/obj/item/borg/gripper/gripper = user_mob.get_active_held_item(TRUE)
|
|
if(istype(gripper))
|
|
gripper.pre_attack(src, user_mob, get_dist(src, user_mob))
|
|
return
|
|
|
|
if(user_mob.get_active_held_item() == null) // Let me know if this has any problems -Yota
|
|
user_mob.UnarmedAttack(src, TRUE, list()) // no modifiers, just normal pickup
|
|
|
|
//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)
|
|
attack_self(user)
|
|
|
|
/obj/item/proc/IsReflect(def_zone) //This proc determines if and at what% an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit
|
|
return 0
|
|
|
|
/obj/item/proc/eyestab(mob/living/carbon/M, mob/living/carbon/user)
|
|
|
|
var/is_human_victim
|
|
var/obj/item/bodypart/affecting = M.get_bodypart(BODY_ZONE_HEAD)
|
|
if(ishuman(M))
|
|
if(!affecting) //no head!
|
|
return
|
|
is_human_victim = TRUE
|
|
|
|
if(M.is_eyes_covered())
|
|
// you can't stab someone in the eyes wearing a mask!
|
|
to_chat(user, span_danger("You're going to need to remove [M.p_their()] eye protection first!"))
|
|
return
|
|
|
|
if(isalien(M))//Aliens don't have eyes./N slimes also don't have eyes!
|
|
to_chat(user, span_warning("You cannot locate any eyes on this creature!"))
|
|
return
|
|
|
|
if(isbrain(M))
|
|
to_chat(user, span_danger("You cannot locate any organic eyes on this brain!"))
|
|
return
|
|
|
|
src.add_fingerprint(user)
|
|
|
|
playsound(loc, src.hitsound, 30, 1, -1)
|
|
|
|
user.do_attack_animation(M)
|
|
|
|
if(M != user)
|
|
M.visible_message(span_danger("[user] has stabbed [M] in the eye with [src]!"), \
|
|
span_userdanger("[user] stabs you in the eye with [src]!"))
|
|
else
|
|
user.visible_message( \
|
|
span_danger("[user] has stabbed [user.p_them()]self in the eyes with [src]!"), \
|
|
span_userdanger("You stab yourself in the eyes with [src]!") \
|
|
)
|
|
if(is_human_victim)
|
|
var/mob/living/carbon/human/U = M
|
|
U.apply_damage(7, BRUTE, affecting)
|
|
|
|
else
|
|
M.take_bodypart_damage(7)
|
|
|
|
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "eye_stab", /datum/mood_event/eye_stab)
|
|
|
|
log_combat(user, M, "attacked", "[src.name]", "(COMBAT_MODE: [user.combat_mode ? "ON" : "OFF"])")
|
|
|
|
var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES)
|
|
if (!eyes)
|
|
return
|
|
M.adjust_eye_blur(3)
|
|
eyes.applyOrganDamage(rand(2,4))
|
|
if(eyes.damage >= 10)
|
|
M.adjust_eye_blur(15)
|
|
if(M.stat != DEAD)
|
|
to_chat(M, span_danger("Your eyes start to bleed profusely!"))
|
|
if(!(HAS_TRAIT(M, TRAIT_BLIND) || HAS_TRAIT(M, TRAIT_NEARSIGHT)))
|
|
to_chat(M, span_danger("You become nearsighted!"))
|
|
M.become_nearsighted(EYE_DAMAGE)
|
|
if(prob(50))
|
|
if(M.stat != DEAD)
|
|
if(M.drop_all_held_items())
|
|
to_chat(M, span_danger("You drop what you're holding and clutch at your eyes!"))
|
|
M.adjust_eye_blur(10)
|
|
M.Unconscious(20)
|
|
M.Paralyze(40)
|
|
if (prob(eyes.damage - 10 + 1))
|
|
M.become_blind(EYE_DAMAGE)
|
|
to_chat(M, span_danger("You go blind!"))
|
|
|
|
/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/component/storage/concrete/master_storage)
|
|
. = ..()
|
|
var/atom/location = master_storage.real_location()
|
|
do_drop_animation(location)
|
|
|
|
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
if(hit_atom && !QDELETED(hit_atom))
|
|
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
|
|
if(is_hot() && isliving(hit_atom))
|
|
var/mob/living/L = hit_atom
|
|
L.ignite_mob()
|
|
var/itempush = 1
|
|
if(w_class < 4)
|
|
itempush = 0 //too light to push anything
|
|
if(istype(hit_atom, /mob/living)) //Living mobs handle hit sounds differently.
|
|
var/volume = get_volume_by_throwforce_and_or_w_class()
|
|
if (throwforce > 0)
|
|
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, quickstart = TRUE)
|
|
if(HAS_TRAIT(src, TRAIT_NODROP))
|
|
return
|
|
thrownby = thrower
|
|
callback = CALLBACK(src, PROC_REF(after_throw), callback) //replace their callback with our own
|
|
. = ..(target, range, speed, thrower, spin, diagonals_first, callback, force, quickstart = quickstart)
|
|
|
|
|
|
/obj/item/proc/after_throw(datum/callback/callback)
|
|
if (callback) //call the original callback
|
|
. = callback.Invoke()
|
|
item_flags &= ~IN_INVENTORY
|
|
var/matrix/M = matrix(transform)
|
|
M.Turn(rand(-170, 170))
|
|
transform = M
|
|
pixel_x = rand(-12, 12)
|
|
pixel_y = rand(-12, 12)
|
|
|
|
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage
|
|
if(!newLoc)
|
|
return FALSE
|
|
if(SEND_SIGNAL(loc, COMSIG_CONTAINS_STORAGE))
|
|
return SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, newLoc, TRUE)
|
|
return FALSE
|
|
|
|
/obj/item/proc/get_belt_overlay() //Returns the icon used for overlaying the object on a belt
|
|
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)
|
|
|
|
/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_inv_wear_suit()
|
|
if(flags & ITEM_SLOT_ICLOTHING)
|
|
owner.update_inv_w_uniform()
|
|
if(flags & ITEM_SLOT_GLOVES)
|
|
owner.update_inv_gloves()
|
|
if(flags & ITEM_SLOT_EYES)
|
|
owner.update_inv_glasses()
|
|
if(flags & ITEM_SLOT_EARS)
|
|
owner.update_inv_ears()
|
|
if(flags & ITEM_SLOT_MASK)
|
|
owner.update_inv_wear_mask()
|
|
if(flags & ITEM_SLOT_HEAD)
|
|
owner.update_inv_head()
|
|
if(flags & ITEM_SLOT_FEET)
|
|
owner.update_inv_shoes()
|
|
if(flags & ITEM_SLOT_ID)
|
|
owner.update_inv_wear_id()
|
|
if(flags & ITEM_SLOT_BELT)
|
|
owner.update_inv_belt()
|
|
if(flags & ITEM_SLOT_BACK)
|
|
owner.update_inv_back()
|
|
if(flags & ITEM_SLOT_NECK)
|
|
owner.update_inv_neck()
|
|
|
|
/obj/item/proc/is_hot()
|
|
return heat
|
|
|
|
/obj/item/proc/is_sharp()
|
|
return sharpness
|
|
|
|
/obj/item/proc/get_dismember_sound()
|
|
if(damtype == BURN)
|
|
. = 'sound/weapons/sear.ogg'
|
|
else
|
|
. = pick('sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg')
|
|
|
|
/obj/item/proc/open_flame(flame_heat=700)
|
|
var/turf/location = loc
|
|
if(ismob(location))
|
|
var/mob/M = location
|
|
var/success = FALSE
|
|
if(src == M.get_item_by_slot(ITEM_SLOT_MASK) || (src in M.held_items))
|
|
success = TRUE
|
|
if(success)
|
|
location = get_turf(M)
|
|
if(isturf(location))
|
|
location.hotspot_expose(flame_heat, 5)
|
|
|
|
/obj/item/proc/ignition_effect(atom/A, mob/user)
|
|
if(is_hot())
|
|
. = span_notice("[user] lights [A] with [src].")
|
|
else
|
|
. = ""
|
|
|
|
/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
|
|
return
|
|
|
|
/obj/item/attack_hulk(mob/living/carbon/human/user)
|
|
return 0
|
|
|
|
/obj/item/attack_animal(mob/living/simple_animal/M)
|
|
if (obj_flags & CAN_BE_HIT)
|
|
return ..()
|
|
return 0
|
|
|
|
/obj/item/mech_melee_attack(obj/mecha/M, punch_force, equip_allowed = TRUE)
|
|
return 0
|
|
|
|
/obj/item/deconstruct(disassembled = TRUE)
|
|
var/turf/T = get_turf(src)
|
|
var/msg = replacetext(break_message, "%SRC", "[src]")
|
|
T.visible_message(span_danger(msg))
|
|
..()
|
|
|
|
/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/M)
|
|
if(istype(M) && M.dirty < 100)
|
|
M.dirty++
|
|
|
|
/obj/item/proc/on_mob_death(mob/living/L, gibbed)
|
|
|
|
/obj/item/proc/grind_requirements(obj/machinery/reagentgrinder/R) //Used to check for extra requirements for grinding an object
|
|
return TRUE
|
|
|
|
//Called BEFORE the object is ground up - use this to change grind results based on conditions
|
|
//Use "return -1" to prevent the grinding from occurring
|
|
/obj/item/proc/on_grind()
|
|
|
|
/obj/item/proc/on_juice()
|
|
|
|
/obj/item/proc/set_force_string()
|
|
switch(force)
|
|
if(0 to 4)
|
|
force_string = "very low"
|
|
if(4 to 7)
|
|
force_string = "low"
|
|
if(7 to 10)
|
|
force_string = "medium"
|
|
if(10 to 14)
|
|
force_string = "high"
|
|
if(14 to 20) //15 is the force of a toolbox
|
|
force_string = "robust"
|
|
if(20 to 25)
|
|
force_string = "very robust"
|
|
else
|
|
force_string = "exceptionally robust"
|
|
last_force_string_check = force
|
|
|
|
/obj/item/proc/openTip(location, control, params, user)
|
|
if(last_force_string_check != force && !(item_flags & FORCE_STRING_OVERRIDE))
|
|
set_force_string()
|
|
if(!(item_flags & FORCE_STRING_OVERRIDE))
|
|
openToolTip(user,src,params,title = name,content = "[desc]<br>[force ? "<b>Force:</b> [force_string]" : ""]",theme = "")
|
|
else
|
|
openToolTip(user,src,params,title = name,content = "[desc]<br><b>Force:</b> [force_string]",theme = "")
|
|
|
|
/obj/item/MouseEntered(location, control, params)
|
|
. = ..()
|
|
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && !QDELETED(src))
|
|
var/mob/living/L = usr
|
|
if(usr.client.prefs.read_preference(/datum/preference/toggle/enable_tooltips))
|
|
var/timedelay = usr.client.prefs.read_preference(/datum/preference/numeric/tooltip_delay) / 100
|
|
tip_timer = addtimer(CALLBACK(src, PROC_REF(openTip), location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
|
|
if(usr.client.prefs.read_preference(/datum/preference/toggle/item_outlines))
|
|
if(istype(L) && L.incapacitated())
|
|
apply_outline(COLOR_RED_GRAY) //if they're dead or handcuffed, let's show the outline as red to indicate that they can't interact with that right now
|
|
else
|
|
apply_outline() //if the player's alive and well we send the command with no color set, so it uses the theme's color
|
|
|
|
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
|
|
. = ..()
|
|
remove_filter(HOVER_OUTLINE_FILTER) //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
|
|
|
|
/obj/item/MouseExited()
|
|
deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
|
|
closeToolTip(usr)
|
|
remove_filter(HOVER_OUTLINE_FILTER)
|
|
|
|
/obj/item/proc/apply_outline(outline_color = null)
|
|
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
|
|
return
|
|
var/theme = lowertext(usr.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
|
|
if(!outline_color) //if we weren't provided with a color, take the theme's color
|
|
switch(theme) //yeah it kinda has to be this way
|
|
if("midnight")
|
|
outline_color = COLOR_THEME_MIDNIGHT
|
|
if("plasmafire")
|
|
outline_color = COLOR_THEME_PLASMAFIRE
|
|
if("retro")
|
|
outline_color = COLOR_THEME_RETRO //just as garish as the rest of this theme
|
|
if("slimecore")
|
|
outline_color = COLOR_THEME_SLIMECORE
|
|
if("operative")
|
|
outline_color = COLOR_THEME_OPERATIVE
|
|
if("clockwork")
|
|
outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
|
|
if("glass")
|
|
outline_color = COLOR_THEME_GLASS
|
|
if("detective")
|
|
outline_color = COLOR_THEME_DETECTIVE
|
|
else //this should never happen, hopefully
|
|
outline_color = COLOR_WHITE
|
|
if(color)
|
|
outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
|
|
|
|
add_filter(HOVER_OUTLINE_FILTER, 1, list("type" = "outline", "size" = 1, "color" = outline_color))
|
|
|
|
// Called when a mob tries to use the item as a tool.
|
|
// Handles most checks.
|
|
/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, robo_check)
|
|
// No delay means there is no start message, and no reason to call tool_start_check before use_tool.
|
|
// Run the start check here so we wouldn't have to call it manually.
|
|
if(!delay && !tool_start_check(user, amount))
|
|
return
|
|
delay *= toolspeed
|
|
|
|
if(((IS_ENGINEERING(user) || (robo_check && IS_JOB(user, "Roboticist"))) && (tool_behaviour in MECHANICAL_TOOLS)) || (IS_MEDICAL(user) && (tool_behaviour in MEDICAL_TOOLS)))
|
|
delay *= 0.8 // engineers and doctors use their own tools faster
|
|
|
|
if(volume) // Play tool sound at the beginning of tool usage.
|
|
play_tool_sound(target, volume)
|
|
|
|
if(delay)
|
|
// Create a callback with checks that would be called every tick by do_after.
|
|
var/datum/callback/tool_check = CALLBACK(src, PROC_REF(tool_check_callback), user, amount, extra_checks)
|
|
|
|
if(!do_after(user, delay, target, extra_checks=tool_check))
|
|
return
|
|
else
|
|
// Invoke the extra checks once, just in case.
|
|
if(extra_checks && !extra_checks.Invoke())
|
|
return
|
|
|
|
// Use tool's fuel, stack sheets or charges if amount is set.
|
|
if(amount && !use(amount))
|
|
return
|
|
|
|
// Play tool sound at the end of tool usage,
|
|
// but only if the delay between the beginning and the end is not too small
|
|
if(delay >= MIN_TOOL_SOUND_DELAY)
|
|
play_tool_sound(target, volume)
|
|
|
|
return TRUE
|
|
|
|
// Called before use_tool if there is a delay, or by use_tool if there isn't.
|
|
// Only ever used by welding tools and stacks, so it's not added on any other use_tool checks.
|
|
/obj/item/proc/tool_start_check(mob/living/user, amount=0)
|
|
return tool_use_check(user, amount)
|
|
|
|
// A check called by tool_start_check once, and by use_tool on every tick of delay.
|
|
/obj/item/proc/tool_use_check(mob/living/user, amount)
|
|
return !amount
|
|
|
|
// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc.
|
|
// Returns TRUE on success, FALSE on failure.
|
|
/obj/item/proc/use(used)
|
|
return !used
|
|
|
|
// Plays item's usesound, if any.
|
|
/obj/item/proc/play_tool_sound(atom/target, volume=50)
|
|
if(target && usesound && volume)
|
|
var/played_sound = usesound
|
|
|
|
if(islist(usesound))
|
|
played_sound = pick(usesound)
|
|
|
|
playsound(target, played_sound, volume, 1)
|
|
|
|
// Used in a callback that is passed by use_tool into do_after call. Do not override, do not call manually.
|
|
/obj/item/proc/tool_check_callback(mob/living/user, amount, datum/callback/extra_checks)
|
|
return tool_use_check(user, amount) && (!extra_checks || extra_checks.Invoke())
|
|
|
|
// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped
|
|
/obj/item/proc/get_part_rating()
|
|
return 0
|
|
|
|
/obj/item/doMove(atom/destination)
|
|
if (ismob(loc))
|
|
var/mob/M = loc
|
|
var/hand_index = M.get_held_index_of_item(src)
|
|
if(hand_index)
|
|
M.held_items[hand_index] = null
|
|
M.update_inv_hands()
|
|
if(M.client)
|
|
M.client.screen -= src
|
|
layer = initial(layer)
|
|
SET_PLANE_IMPLICIT(src, initial(plane))
|
|
appearance_flags &= ~NO_CLIENT_COLOR
|
|
dropped(M, FALSE)
|
|
return ..()
|
|
|
|
/obj/item/proc/canStrip(mob/stripper, mob/owner)
|
|
SHOULD_BE_PURE(TRUE)
|
|
return !HAS_TRAIT(src, TRAIT_NODROP)
|
|
|
|
/obj/item/proc/doStrip(mob/stripper, mob/owner)
|
|
return owner.dropItemToGround(src)
|
|
|
|
///Returns the temperature of src. If you want to know if an item is hot use this proc.
|
|
/obj/item/proc/get_temperature()
|
|
return heat
|
|
|
|
// Update icons if this is being carried by a mob
|
|
/obj/item/wash(clean_types)
|
|
. = ..()
|
|
|
|
if(ismob(loc))
|
|
var/mob/mob_loc = loc
|
|
mob_loc.regenerate_icons()
|
|
|
|
/obj/item/proc/do_pickup_animation(atom/target, turf/source)
|
|
if(!source)
|
|
if(!istype(loc, /turf))
|
|
return
|
|
source = loc
|
|
var/image/pickup_animation = image(icon = src, loc = loc, layer = layer + 0.1)
|
|
SET_PLANE(pickup_animation, GAME_PLANE, source)
|
|
pickup_animation.transform.Scale(0.75)
|
|
|
|
var/turf/current_turf = get_turf(src)
|
|
var/direction = get_dir(current_turf, target)
|
|
var/to_x = target.pixel_x
|
|
var/to_y = target.pixel_y
|
|
|
|
if(direction & NORTH)
|
|
to_y += 32
|
|
else if(direction & SOUTH)
|
|
to_y -= 32
|
|
if(direction & EAST)
|
|
to_x += 32
|
|
else if(direction & WEST)
|
|
to_x -= 32
|
|
if(!direction)
|
|
to_y += 10
|
|
pickup_animation.pixel_x += 6 * (prob(50) ? 1 : -1) //6 to the right or left, helps break up the straight upward move
|
|
|
|
flick_overlay_global(pickup_animation, GLOB.clients, 4)
|
|
var/matrix/animation_matrix = new
|
|
animation_matrix.Turn(pick(-30, 30))
|
|
animation_matrix.Scale(0.65)
|
|
|
|
animate(pickup_animation, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = animation_matrix, easing = CUBIC_EASING)
|
|
animate(alpha = 0, transform = matrix().Scale(0.7), time = 1)
|
|
|
|
/obj/item/proc/do_drop_animation(atom/moving_from)
|
|
if(!istype(loc, /turf))
|
|
return
|
|
|
|
var/turf/current_turf = get_turf(src)
|
|
var/direction = get_dir(moving_from, current_turf)
|
|
var/from_x = moving_from.pixel_x
|
|
var/from_y = moving_from.pixel_y
|
|
|
|
if(direction & NORTH)
|
|
from_y -= 32
|
|
else if(direction & SOUTH)
|
|
from_y += 32
|
|
if(direction & EAST)
|
|
from_x -= 32
|
|
else if(direction & WEST)
|
|
from_x += 32
|
|
if(!direction)
|
|
from_y += 10
|
|
from_x += 6 * (prob(50) ? 1 : -1) //6 to the right or left, helps break up the straight upward move
|
|
|
|
//We're moving from these chords to our current ones
|
|
var/old_x = pixel_x
|
|
var/old_y = pixel_y
|
|
var/old_alpha = alpha
|
|
var/matrix/animation_matrix = new
|
|
animation_matrix.Turn(pick(-30, 30))
|
|
animation_matrix.Scale(0.7) // Shrink to start, end up normal sized
|
|
|
|
pixel_x = from_x
|
|
pixel_y = from_y
|
|
alpha = 0
|
|
transform = animation_matrix
|
|
|
|
// This is instant on byond's end, but to our clients this looks like a quick drop
|
|
animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = matrix(), time = 3, easing = CUBIC_EASING)
|
|
|
|
/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item)
|
|
var/image/attack_image
|
|
if(visual_effect_icon)
|
|
attack_image = image('icons/effects/effects.dmi', attacked_atom, visual_effect_icon, attacked_atom.layer + 0.1)
|
|
else if(used_item)
|
|
attack_image = image(icon = used_item, loc = attacked_atom, layer = attacked_atom.layer + 0.1)
|
|
attack_image.plane = attacked_atom.plane
|
|
|
|
// Scale the icon.
|
|
attack_image.transform *= 0.4
|
|
|
|
// Set the direction of the icon animation.
|
|
var/direction = get_dir(src, attacked_atom)
|
|
if(direction & NORTH)
|
|
attack_image.pixel_y = -12
|
|
else if(direction & SOUTH)
|
|
attack_image.pixel_y = 12
|
|
|
|
if(direction & EAST)
|
|
attack_image.pixel_x = -14
|
|
else if(direction & WEST)
|
|
attack_image.pixel_x = 14
|
|
|
|
if(!direction) // Attacked self?!
|
|
attack_image.pixel_y = 12
|
|
attack_image.pixel_x = 5 * (prob(50) ? 1 : -1)
|
|
|
|
if(!attack_image)
|
|
return
|
|
|
|
flick_overlay_global(attack_image, GLOB.clients, 10)
|
|
// And animate the attack!
|
|
var/t_color = "#ffffff" //yogs start
|
|
if(ismob(src) && ismob(attacked_atom) && (!used_item))
|
|
var/mob/living/M = src
|
|
t_color = M.combat_mode ? "#ff0000" : "#ffffff"
|
|
animate(attack_image, alpha = 175, transform = matrix() * 0.75, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color)
|
|
animate(time = 1)
|
|
animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT) //yogs end
|
|
|
|
//specifically for "suture" type robotic healing items
|
|
//amount is either the fuel of a welding tool, or the number of wires consumed
|
|
//volume is how loud the sound of the item is
|
|
|
|
#define ROBO_LIMB_HEAL_SELF 3 SECONDS
|
|
#define ROBO_LIMB_HEAL_OTHER 1 SECONDS
|
|
|
|
/obj/item/proc/heal_robo_limb(obj/item/I, mob/living/carbon/human/H, mob/user, brute_heal = 0, burn_heal = 0, amount = 0, volume = 0)
|
|
if(I.use_tool(H, user, (H == user) ? ROBO_LIMB_HEAL_SELF : ROBO_LIMB_HEAL_OTHER, amount, volume, null, TRUE))
|
|
if(item_heal_robotic(H, user, brute_heal, burn_heal))
|
|
return heal_robo_limb(I, H, user, brute_heal, burn_heal, amount, volume)
|
|
return TRUE
|
|
|
|
/**
|
|
* Updates all action buttons associated with this item
|
|
*
|
|
* Arguments:
|
|
* * update_flags - Which flags of the action should we update
|
|
* * force - Force buttons update even if the given button icon state has not changed
|
|
*/
|
|
/obj/item/proc/update_item_action_buttons(update_flags = ALL, force = FALSE)
|
|
for(var/datum/action/current_action as anything in actions)
|
|
current_action.build_all_button_icons(update_flags, force)
|