mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-26 10:03:45 +00:00
Melee weapons can now potentially attack from farther away. Obstacles are taken into account, so you cannot hit people through windows, but you can attack over specific things such as tables, or other people. Currently only the spear can do this, with a range of two tiles, however the attack speed for the spear was reduced, so it may remain a situational weapon. The intention for this is to make specific weapons feel different to each other besides 'does more damage', and I got ideas on other kinds of weapon adjustments later to make them feel a bit more unique.
653 lines
25 KiB
Plaintext
653 lines
25 KiB
Plaintext
/obj/item
|
|
name = "item"
|
|
icon = 'icons/obj/items.dmi'
|
|
w_class = ITEMSIZE_NORMAL
|
|
|
|
var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
|
|
var/abstract = 0
|
|
var/r_speed = 1.0
|
|
var/health = null
|
|
var/burn_point = null
|
|
var/burning = null
|
|
var/hitsound = null
|
|
var/usesound = null // Like hitsound, but for when used properly and not to kill someone.
|
|
var/storage_cost = null
|
|
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
|
|
var/no_attack_log = 0 //If it's an item we don't want to log attack_logs with, set this to 1
|
|
pass_flags = PASSTABLE
|
|
pressure_resistance = 5
|
|
// causeerrorheresoifixthis
|
|
var/obj/item/master = null
|
|
var/list/origin_tech = null //Used by R&D to determine what research bonuses it grants.
|
|
var/list/attack_verb = list() //Used in attackby() to say how something was attacked "[x] has been [z.attack_verb] by [y] with [z]"
|
|
var/force = 0
|
|
|
|
var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm
|
|
var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, UPPER_TORSO, LOWER_TORSO, 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/datum/action/item_action/action = null
|
|
var/action_button_name //It is also the text which gets displayed on the action button. If not set it defaults to 'Use [name]'. If it's not set, there'll be no button.
|
|
var/action_button_is_hands_free = 0 //If 1, bypass the restrained, lying, and stunned checks action buttons normally test for
|
|
|
|
//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.
|
|
//It should be used purely for appearance. For gameplay effects caused by items covering body parts, use body_parts_covered.
|
|
var/flags_inv = 0
|
|
var/body_parts_covered = 0 //see setup.dm for appropriate bit flags
|
|
|
|
var/item_flags = 0 //Miscellaneous flags pertaining to equippable objects.
|
|
|
|
//var/heat_transfer_coefficient = 1 //0 prevents all transfers, 1 is invisible
|
|
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/permeability_coefficient = 1 // for chemicals/diseases
|
|
var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit)
|
|
var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up
|
|
var/canremove = 1 //Mostly for Ninja code at this point but basically will not allow the item to be removed if set to 0. /N
|
|
var/list/armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
|
|
var/list/armorsoak = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
|
|
var/list/allowed = null //suit storage stuff.
|
|
var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers.
|
|
var/zoomdevicename = null //name used for message when binoculars/scope is used
|
|
var/zoom = 0 //1 if item is actively being used to zoom. For scoped guns and binoculars.
|
|
|
|
var/embed_chance = -1 //0 won't embed, and 100 will always embed
|
|
|
|
var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc.
|
|
|
|
//** These specify item/icon overrides for _slots_
|
|
|
|
var/list/item_state_slots = list() //overrides the default item_state for particular slots.
|
|
|
|
// Used to specify the icon file to be used when the item is worn. If not set the default icon for that slot will be used.
|
|
// If icon_override or sprite_sheets are set they will take precendence over this, assuming they apply to the slot in question.
|
|
// Only slot_l_hand/slot_r_hand are implemented at the moment. Others to be implemented as needed.
|
|
var/list/item_icons = list()
|
|
|
|
//** These specify item/icon overrides for _species_
|
|
|
|
/* Species-specific sprites, concept stolen from Paradise//vg/.
|
|
ex:
|
|
sprite_sheets = list(
|
|
"Tajara" = 'icons/cat/are/bad'
|
|
)
|
|
If index term exists and icon_override is not set, this sprite sheet will be used.
|
|
*/
|
|
var/list/sprite_sheets = list()
|
|
|
|
// Species-specific sprite sheets for inventory sprites
|
|
// Works similarly to worn sprite_sheets, except the alternate sprites are used when the clothing/refit_for_species() proc is called.
|
|
var/list/sprite_sheets_obj = list()
|
|
|
|
var/toolspeed = 1.0 // This is a multipler on how 'fast' a tool works. e.g. setting this to 0.5 will make the tool work twice as fast.
|
|
var/attackspeed = DEFAULT_ATTACK_COOLDOWN // How long click delay will be when using this, in 1/10ths of a second. Checked in the user's get_attack_speed().
|
|
var/reach = 1 // Length of tiles it can reach, 1 is adjacent.
|
|
var/addblends // Icon overlay for ADD highlights when applicable.
|
|
|
|
/obj/item/New()
|
|
..()
|
|
if(embed_chance < 0)
|
|
if(sharp)
|
|
embed_chance = max(5, round(force/w_class))
|
|
else
|
|
embed_chance = max(5, round(force/(w_class*3)))
|
|
|
|
/obj/item/equipped()
|
|
..()
|
|
var/mob/living/M = loc
|
|
if(!istype(M))
|
|
return
|
|
M.update_held_icons()
|
|
|
|
/obj/item/Destroy()
|
|
if(ismob(loc))
|
|
var/mob/m = loc
|
|
m.drop_from_inventory(src)
|
|
m.update_inv_r_hand()
|
|
m.update_inv_l_hand()
|
|
src.loc = null
|
|
return ..()
|
|
|
|
/obj/item/device
|
|
icon = 'icons/obj/device.dmi'
|
|
|
|
//Checks if the item is being held by a mob, and if so, updates the held icons
|
|
/obj/item/proc/update_held_icon()
|
|
if(isliving(src.loc))
|
|
var/mob/living/M = src.loc
|
|
if(M.l_hand == src)
|
|
M.update_inv_l_hand()
|
|
else if(M.r_hand == src)
|
|
M.update_inv_r_hand()
|
|
|
|
/obj/item/ex_act(severity)
|
|
switch(severity)
|
|
if(1.0)
|
|
qdel(src)
|
|
return
|
|
if(2.0)
|
|
if (prob(50))
|
|
qdel(src)
|
|
return
|
|
if(3.0)
|
|
if (prob(5))
|
|
qdel(src)
|
|
return
|
|
else
|
|
return
|
|
|
|
//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/verb/move_to_top()
|
|
set name = "Move To Top"
|
|
set 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, var/distance = -1)
|
|
var/size
|
|
switch(src.w_class)
|
|
if(ITEMSIZE_TINY)
|
|
size = "tiny"
|
|
if(ITEMSIZE_SMALL)
|
|
size = "small"
|
|
if(ITEMSIZE_NORMAL)
|
|
size = "normal-sized"
|
|
if(ITEMSIZE_LARGE)
|
|
size = "bulky"
|
|
if(ITEMSIZE_HUGE)
|
|
size = "huge"
|
|
return ..(user, distance, "", "It is a [size] item.")
|
|
|
|
/obj/item/attack_hand(mob/living/user as mob)
|
|
if (!user) return
|
|
if (hasorgans(user))
|
|
var/mob/living/carbon/human/H = user
|
|
var/obj/item/organ/external/temp = H.organs_by_name["r_hand"]
|
|
if (user.hand)
|
|
temp = H.organs_by_name["l_hand"]
|
|
if(temp && !temp.is_usable())
|
|
user << "<span class='notice'>You try to move your [temp.name], but cannot!</span>"
|
|
return
|
|
if(!temp)
|
|
user << "<span class='notice'>You try to use your hand, but realize it is no longer attached!</span>"
|
|
return
|
|
src.pickup(user)
|
|
if (istype(src.loc, /obj/item/weapon/storage))
|
|
var/obj/item/weapon/storage/S = src.loc
|
|
S.remove_from_storage(src)
|
|
|
|
src.throwing = 0
|
|
if (src.loc == user)
|
|
if(!user.unEquip(src))
|
|
return
|
|
else
|
|
if(isliving(src.loc))
|
|
return
|
|
user.put_in_active_hand(src)
|
|
return
|
|
|
|
/obj/item/attack_ai(mob/user as mob)
|
|
if (istype(src.loc, /obj/item/weapon/robot_module))
|
|
//If the item is part of a cyborg module, equip it
|
|
if(!isrobot(user))
|
|
return
|
|
var/mob/living/silicon/robot/R = user
|
|
R.activate_module(src)
|
|
R.hud_used.update_robot_modules_display()
|
|
|
|
/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
|
if(istype(W, /obj/item/weapon/storage))
|
|
var/obj/item/weapon/storage/S = W
|
|
if(S.use_to_pickup)
|
|
if(S.collection_mode) //Mode is set to collect all items
|
|
if(isturf(src.loc))
|
|
S.gather_all(src.loc, user)
|
|
|
|
else if(S.can_be_inserted(src))
|
|
S.handle_item_insertion(src)
|
|
return
|
|
|
|
/obj/item/proc/talk_into(mob/M as mob, text)
|
|
return
|
|
|
|
/obj/item/proc/moved(mob/user as mob, old_loc as turf)
|
|
return
|
|
|
|
// apparently called whenever an item is removed from a slot, container, or anything else.
|
|
/obj/item/proc/dropped(mob/user as mob)
|
|
..()
|
|
if(zoom) zoom() //binoculars, scope, etc
|
|
|
|
// called just as an item is picked up (loc is not yet changed)
|
|
/obj/item/proc/pickup(mob/user)
|
|
return
|
|
|
|
// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
|
|
/obj/item/proc/on_exit_storage(obj/item/weapon/storage/S as obj)
|
|
return
|
|
|
|
// called when this item is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
|
|
/obj/item/proc/on_enter_storage(obj/item/weapon/storage/S as obj)
|
|
return
|
|
|
|
// called when "found" in pockets and storage items. Returns 1 if the search should end.
|
|
/obj/item/proc/on_found(mob/finder as mob)
|
|
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
|
|
// note this isn't called during the initial dressing of a player
|
|
/obj/item/proc/equipped(var/mob/user, var/slot)
|
|
layer = 20
|
|
if(user.client) user.client.screen |= src
|
|
if(user.pulling == src) user.stop_pulling()
|
|
return
|
|
|
|
//Defines which slots correspond to which slot flags
|
|
var/list/global/slot_flags_enumeration = list(
|
|
"[slot_wear_mask]" = SLOT_MASK,
|
|
"[slot_back]" = SLOT_BACK,
|
|
"[slot_wear_suit]" = SLOT_OCLOTHING,
|
|
"[slot_gloves]" = SLOT_GLOVES,
|
|
"[slot_shoes]" = SLOT_FEET,
|
|
"[slot_belt]" = SLOT_BELT,
|
|
"[slot_glasses]" = SLOT_EYES,
|
|
"[slot_head]" = SLOT_HEAD,
|
|
"[slot_l_ear]" = SLOT_EARS|SLOT_TWOEARS,
|
|
"[slot_r_ear]" = SLOT_EARS|SLOT_TWOEARS,
|
|
"[slot_w_uniform]" = SLOT_ICLOTHING,
|
|
"[slot_wear_id]" = SLOT_ID,
|
|
"[slot_tie]" = SLOT_TIE,
|
|
)
|
|
|
|
//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 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 1 if you wish it to not give you outputs.
|
|
//Should probably move the bulk of this into mob code some time, as most of it is related to the definition of slots and not item-specific
|
|
/obj/item/proc/mob_can_equip(M as mob, slot, disable_warning = 0)
|
|
if(!slot) return 0
|
|
if(!M) return 0
|
|
|
|
if(!ishuman(M)) return 0
|
|
|
|
var/mob/living/carbon/human/H = M
|
|
var/list/mob_equip = list()
|
|
if(H.species.hud && H.species.hud.equip_slots)
|
|
mob_equip = H.species.hud.equip_slots
|
|
|
|
if(H.species && !(slot in mob_equip))
|
|
return 0
|
|
|
|
//First check if the item can be equipped to the desired slot.
|
|
if("[slot]" in slot_flags_enumeration)
|
|
var/req_flags = slot_flags_enumeration["[slot]"]
|
|
if(!(req_flags & slot_flags))
|
|
return 0
|
|
|
|
//Next check that the slot is free
|
|
if(H.get_equipped_item(slot))
|
|
return 0
|
|
|
|
//Next check if the slot is accessible.
|
|
var/mob/_user = disable_warning? null : H
|
|
if(!H.slot_is_accessible(slot, src, _user))
|
|
return 0
|
|
|
|
//Lastly, check special rules for the desired slot.
|
|
switch(slot)
|
|
if(slot_l_ear, slot_r_ear)
|
|
var/slot_other_ear = (slot == slot_l_ear)? slot_r_ear : slot_l_ear
|
|
if( (w_class > ITEMSIZE_TINY) && !(slot_flags & SLOT_EARS) )
|
|
return 0
|
|
if( (slot_flags & SLOT_TWOEARS) && H.get_equipped_item(slot_other_ear) )
|
|
return 0
|
|
if(slot_wear_id)
|
|
if(!H.w_uniform && (slot_w_uniform in mob_equip))
|
|
if(!disable_warning)
|
|
H << "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>"
|
|
return 0
|
|
if(slot_l_store, slot_r_store)
|
|
if(!H.w_uniform && (slot_w_uniform in mob_equip))
|
|
if(!disable_warning)
|
|
H << "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>"
|
|
return 0
|
|
if(slot_flags & SLOT_DENYPOCKET)
|
|
return 0
|
|
if( w_class > ITEMSIZE_SMALL && !(slot_flags & SLOT_POCKET) )
|
|
return 0
|
|
if(slot_s_store)
|
|
if(!H.wear_suit && (slot_wear_suit in mob_equip))
|
|
if(!disable_warning)
|
|
H << "<span class='warning'>You need a suit before you can attach this [name].</span>"
|
|
return 0
|
|
if(!H.wear_suit.allowed)
|
|
if(!disable_warning)
|
|
usr << "<span class='warning'>You somehow have a suit with no defined allowed items for suit storage, stop that.</span>"
|
|
return 0
|
|
if( !(istype(src, /obj/item/device/pda) || istype(src, /obj/item/weapon/pen) || is_type_in_list(src, H.wear_suit.allowed)) )
|
|
return 0
|
|
if(slot_legcuffed) //Going to put this check above the handcuff check because the survival of the universe depends on it.
|
|
if(!istype(src, /obj/item/weapon/handcuffs/legcuffs)) //Putting it here might actually do nothing.
|
|
return 0
|
|
if(slot_handcuffed)
|
|
if(!istype(src, /obj/item/weapon/handcuffs) || istype(src, /obj/item/weapon/handcuffs/legcuffs)) //Legcuffs are a child of handcuffs, but we don't want to use legcuffs as handcuffs...
|
|
return 0 //In theory, this would never happen, but let's just do the legcuff check anyways.
|
|
if(slot_in_backpack) //used entirely for equipping spawned mobs or at round start
|
|
var/allow = 0
|
|
if(H.back && istype(H.back, /obj/item/weapon/storage/backpack))
|
|
var/obj/item/weapon/storage/backpack/B = H.back
|
|
if(B.can_be_inserted(src,1))
|
|
allow = 1
|
|
if(!allow)
|
|
return 0
|
|
if(slot_tie)
|
|
if(!H.w_uniform && (slot_w_uniform in mob_equip))
|
|
if(!disable_warning)
|
|
H << "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>"
|
|
return 0
|
|
var/obj/item/clothing/under/uniform = H.w_uniform
|
|
if(uniform.accessories.len && !uniform.can_attach_accessory(src))
|
|
if (!disable_warning)
|
|
H << "<span class='warning'>You already have an accessory of this type attached to your [uniform].</span>"
|
|
return 0
|
|
return 1
|
|
|
|
/obj/item/proc/mob_can_unequip(mob/M, slot, disable_warning = 0)
|
|
if(!slot) return 0
|
|
if(!M) return 0
|
|
|
|
if(!canremove)
|
|
return 0
|
|
if(!M.slot_is_accessible(slot, src, disable_warning? null : M))
|
|
return 0
|
|
return 1
|
|
|
|
/obj/item/verb/verb_pickup()
|
|
set src in oview(1)
|
|
set category = "Object"
|
|
set name = "Pick up"
|
|
|
|
if(!(usr)) //BS12 EDIT
|
|
return
|
|
if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr))
|
|
return
|
|
if((!istype(usr, /mob/living/carbon)) || (istype(usr, /mob/living/carbon/brain)))//Is humanoid, and is not a brain
|
|
usr << "<span class='warning'>You can't pick things up!</span>"
|
|
return
|
|
var/mob/living/carbon/C = usr
|
|
if( usr.stat || usr.restrained() )//Is not asleep/dead and is not restrained
|
|
usr << "<span class='warning'>You can't pick things up!</span>"
|
|
return
|
|
if(src.anchored) //Object isn't anchored
|
|
usr << "<span class='warning'>You can't pick that up!</span>"
|
|
return
|
|
if(C.get_active_hand()) //Hand is not full
|
|
usr << "<span class='warning'>Your hand is full.</span>"
|
|
return
|
|
if(!istype(src.loc, /turf)) //Object is on a turf
|
|
usr << "<span class='warning'>You can't pick that up!</span>"
|
|
return
|
|
//All checks are done, time to pick it up!
|
|
usr.UnarmedAttack(src)
|
|
return
|
|
|
|
|
|
//This proc is executed when someone clicks the on-screen UI button. To make the UI button show, set the 'icon_action_button' to the icon_state of the image of the button in screen1_action.dmi
|
|
//The default action is attack_self().
|
|
//Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob.
|
|
/obj/item/proc/ui_action_click()
|
|
attack_self(usr)
|
|
|
|
//RETURN VALUES
|
|
//handle_shield should return a positive value to indicate that the attack is blocked and should be prevented.
|
|
//If a negative value is returned, it should be treated as a special return value for bullet_act() and handled appropriately.
|
|
//For non-projectile attacks this usually means the attack is blocked.
|
|
//Otherwise should return 0 to indicate that the attack is not affected in any way.
|
|
/obj/item/proc/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
|
return 0
|
|
|
|
/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_parts_covered & EYES))
|
|
// you can't stab someone in the eyes wearing a mask!
|
|
user << "<span class='warning'>You're going to need to remove the eye covering first.</span>"
|
|
return
|
|
|
|
if(!M.has_eyes())
|
|
user << "<span class='warning'>You cannot locate any eyes on [M]!</span>"
|
|
return
|
|
|
|
if(U.get_accuracy_penalty(U)) //Should only trigger if they're not aiming well
|
|
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("<font color='red'><B>[U] attempts to stab [M] in the eyes, but misses!</B></font>")
|
|
return
|
|
|
|
user.attack_log += "\[[time_stamp()]\]<font color='red'> Attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])</font>"
|
|
M.attack_log += "\[[time_stamp()]\]<font color='orange'> Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])</font>"
|
|
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)") //BS12 EDIT ALG
|
|
|
|
user.setClickCooldown(user.get_attack_speed())
|
|
user.do_attack_animation(M)
|
|
|
|
src.add_fingerprint(user)
|
|
//if((CLUMSY in user.mutations) && prob(50))
|
|
// M = user
|
|
/*
|
|
M << "<span class='warning'>You stab yourself in the eye.</span>"
|
|
M.sdisabilities |= BLIND
|
|
M.weakened += 4
|
|
M.adjustBruteLoss(10)
|
|
*/
|
|
|
|
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("<span class='danger'>[M] has been stabbed in the eye with [src] by [user].</span>", 1)
|
|
M << "<span class='danger'>[user] stabs you in the eye with [src]!</span>"
|
|
user << "<span class='danger'>You stab [M] in the eye with [src]!</span>"
|
|
else
|
|
user.visible_message( \
|
|
"<span class='danger'>[user] has stabbed themself with [src]!</span>", \
|
|
"<span class='danger'>You stab yourself in the eyes with [src]!</span>" \
|
|
)
|
|
|
|
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
|
|
M << "<span class='danger'>Your eyes start to bleed profusely!</span>"
|
|
if(prob(50))
|
|
if(M.stat != 2)
|
|
M << "<span class='warning'>You drop what you're holding and clutch at your eyes!</span>"
|
|
M.drop_item()
|
|
M.eye_blurry += 10
|
|
M.Paralyse(1)
|
|
M.Weaken(4)
|
|
if (eyes.damage >= eyes.min_broken_damage)
|
|
if(M.stat != 2)
|
|
M << "<span class='warning'>You go blind!</span>"
|
|
var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD)
|
|
if(affecting.take_damage(7))
|
|
M:UpdateDamageIcon()
|
|
else
|
|
M.take_organ_damage(7)
|
|
M.eye_blurry += rand(3,4)
|
|
return
|
|
|
|
/obj/item/clean_blood()
|
|
. = ..()
|
|
if(blood_overlay)
|
|
overlays.Remove(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/weapon/melee/energy))
|
|
return
|
|
|
|
//if we haven't made our blood_overlay already
|
|
if( !blood_overlay )
|
|
generate_blood_overlay()
|
|
|
|
//apply the blood-splatter overlay if it isn't already in there
|
|
if(!blood_DNA.len)
|
|
blood_overlay.color = blood_color
|
|
overlays += 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
|
|
|
|
/obj/item/proc/generate_blood_overlay()
|
|
if(blood_overlay)
|
|
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
|
|
|
|
//not sure if this is worth it. It attaches the blood_overlay to every item of the same type if they don't have one already made.
|
|
for(var/obj/item/A in world)
|
|
if(A.type == type && !A.blood_overlay)
|
|
A.blood_overlay = image(I)
|
|
|
|
/obj/item/proc/showoff(mob/user)
|
|
for (var/mob/M in view(user))
|
|
M.show_message("[user] holds up [src]. <a HREF=?src=\ref[M];lookitem=\ref[src]>Take a closer look.</a>",1)
|
|
|
|
/mob/living/carbon/verb/showoff()
|
|
set name = "Show Held Item"
|
|
set category = "Object"
|
|
|
|
var/obj/item/I = get_active_hand()
|
|
if(I && !I.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
|
|
/obj/item/proc/zoom(var/tileoffset = 14,var/viewsize = 9) //tileoffset is client view offset in the direction the user is facing. viewsize is how far out this thing zooms. 7 is normal view
|
|
|
|
var/devicename
|
|
|
|
if(zoomdevicename)
|
|
devicename = zoomdevicename
|
|
else
|
|
devicename = src.name
|
|
|
|
var/cannotzoom
|
|
|
|
if(usr.stat || !(istype(usr,/mob/living/carbon/human)))
|
|
usr << "You are unable to focus through the [devicename]"
|
|
cannotzoom = 1
|
|
else if(!zoom && global_hud.darkMask[1] in usr.client.screen)
|
|
usr << "Your visor gets in the way of looking through the [devicename]"
|
|
cannotzoom = 1
|
|
else if(!zoom && usr.get_active_hand() != src)
|
|
usr << "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better"
|
|
cannotzoom = 1
|
|
|
|
if(!zoom && !cannotzoom)
|
|
if(usr.hud_used.hud_shown)
|
|
usr.toggle_zoom_hud() // If the user has already limited their HUD this avoids them having a HUD when they zoom in
|
|
usr.client.view = viewsize
|
|
zoom = 1
|
|
|
|
var/tilesize = 32
|
|
var/viewoffset = tilesize * tileoffset
|
|
|
|
switch(usr.dir)
|
|
if (NORTH)
|
|
usr.client.pixel_x = 0
|
|
usr.client.pixel_y = viewoffset
|
|
if (SOUTH)
|
|
usr.client.pixel_x = 0
|
|
usr.client.pixel_y = -viewoffset
|
|
if (EAST)
|
|
usr.client.pixel_x = viewoffset
|
|
usr.client.pixel_y = 0
|
|
if (WEST)
|
|
usr.client.pixel_x = -viewoffset
|
|
usr.client.pixel_y = 0
|
|
|
|
usr.visible_message("[usr] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].")
|
|
|
|
else
|
|
usr.client.view = world.view
|
|
if(!usr.hud_used.hud_shown)
|
|
usr.toggle_zoom_hud()
|
|
zoom = 0
|
|
|
|
usr.client.pixel_x = 0
|
|
usr.client.pixel_y = 0
|
|
|
|
if(!cannotzoom)
|
|
usr.visible_message("[zoomdevicename ? "[usr] looks up from the [src.name]" : "[usr] lowers the [src.name]"].")
|
|
|
|
return
|
|
|
|
/obj/item/proc/pwr_drain()
|
|
return 0 // Process Kill
|
|
|
|
// Used for non-adjacent melee attacks with specific weapons capable of reaching more than one tile.
|
|
// This uses changeling range string A* but for this purpose its also applicable.
|
|
/obj/item/proc/attack_can_reach(var/atom/us, var/atom/them, var/range)
|
|
if(us.Adjacent(them))
|
|
return TRUE // Already adjacent.
|
|
if(AStar(get_turf(us), get_turf(them), /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=range))
|
|
return TRUE
|
|
return FALSE
|